Skip to content

Commit

Permalink
fix: enhanced otp handeling taxpayer apis
Browse files Browse the repository at this point in the history
(cherry picked from commit 7afa94c)

# Conflicts:
#	india_compliance/__init__.py
  • Loading branch information
vorasmit authored and mergify[bot] committed Sep 11, 2024
1 parent f5ab72f commit fcb8ae5
Show file tree
Hide file tree
Showing 7 changed files with 184 additions and 100 deletions.
6 changes: 6 additions & 0 deletions india_compliance/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
<<<<<<< HEAD
__version__ = "15.0.2"
=======
from .exceptions import *

__version__ = "16.0.0-dev"
>>>>>>> 7afa94c7 (fix: enhanced otp handeling taxpayer apis)
12 changes: 12 additions & 0 deletions india_compliance/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,15 @@ def __init__(self, message="GSP/GST server is down", *args, **kwargs):
class GatewayTimeoutError(GSPServerError):
def __init__(self, message="The server took too long to respond", *args, **kwargs):
super().__init__(message, *args, **kwargs)


class OTPRequestedError(Exception):
def __init__(self, message="OTP has been requested", *args, **kwargs):
self.response = kwargs.pop("response", None)
super().__init__(message, *args, **kwargs)


class InvalidOTPError(Exception):
def __init__(self, message="Invalid OTP", *args, **kwargs):
self.response = kwargs.pop("response", None)
super().__init__(message, *args, **kwargs)
34 changes: 31 additions & 3 deletions india_compliance/gst_india/api_classes/taxpayer_base.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import json
from base64 import b64decode, b64encode
from functools import wraps

from cryptography import x509
from cryptography.hazmat.backends import default_backend
Expand All @@ -8,6 +9,7 @@
from frappe import _
from frappe.utils import add_to_date, cint, now_datetime

import india_compliance
from india_compliance.gst_india.api_classes.base import BaseAPI, get_public_ip
from india_compliance.gst_india.utils import merge_dicts, tar_gz_bytes_to_data
from india_compliance.gst_india.utils.cryptography import (
Expand All @@ -19,6 +21,25 @@
)


def otp_handler(func):

@wraps(func)
def wrapper(*args, **kwargs):
try:
return func(*args, **kwargs)

except india_compliance.OTPRequestedError as e:
return e.response

except india_compliance.InvalidOTPError as e:
return e.response

except Exception as e:
raise e

return wrapper


class PublicCertificate(BaseAPI):
BASE_PATH = "static"

Expand Down Expand Up @@ -104,9 +125,9 @@ def request_otp(self):
if response.status_cd != 1:
return

return response.update(
{"error_type": "otp_requested", "gstin": self.company_gstin}
)
response.update({"error_type": "otp_requested", "gstin": self.company_gstin})

raise india_compliance.OTPRequestedError(response=response)

def autheticate_with_otp(self, otp=None):
if not otp:
Expand Down Expand Up @@ -347,6 +368,13 @@ def is_ignored_error(self, response):
if error_code in self.IGNORED_ERROR_CODES:
response.error_type = self.IGNORED_ERROR_CODES[error_code]
response.gstin = self.company_gstin

if response.error_type == "otp_requested":
raise india_compliance.OTPRequestedError(response=response)

if response.error_type == "invalid_otp":
raise india_compliance.InvalidOTPError(response=response)

return True

def generate_app_key(self):
Expand Down
9 changes: 5 additions & 4 deletions india_compliance/gst_india/utils/gstr_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@
from frappe import _
from frappe.utils import add_to_date, now_datetime

from india_compliance.gst_india.api_classes.taxpayer_base import TaxpayerBaseAPI
from india_compliance.gst_india.api_classes.taxpayer_base import (
TaxpayerBaseAPI,
otp_handler,
)
from india_compliance.gst_india.api_classes.taxpayer_returns import ReturnsAPI
from india_compliance.gst_india.doctype.gstr_import_log.gstr_import_log import (
create_import_log,
Expand Down Expand Up @@ -97,6 +100,7 @@ def request_otp(company_gstin):


@frappe.whitelist()
@otp_handler
def authenticate_otp(company_gstin, otp):
frappe.has_permission("GST Settings", throw=True)

Expand Down Expand Up @@ -149,9 +153,6 @@ def _download_queued_request(doc):
frappe.db.delete("GSTR Import Log", doc.name)
raise e

if response.error_type in ["otp_requested", "invalid_otp"]:
return toggle_scheduled_jobs(stopped=True)

if response.error_type == "no_docs_found":
return create_import_log(
doc.gstin,
Expand Down
129 changes: 129 additions & 0 deletions india_compliance/public/js/gst_api_handler.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
frappe.provide("taxpayer_api");
frappe.provide("india_compliance");

taxpayer_api.call = async function (...args) {
const response = await frappe.call(...args);
const { message } = response;
if (!["otp_requested", "invalid_otp"].includes(message?.error_type)) return response;

await india_compliance.authenticate_otp(message.gstin, message.error_type);
return taxpayer_api.call(...args);
}

Object.assign(india_compliance, {
get_gstin_otp(company_gstin, error_type) {
let description = `An OTP has been sent to the registered mobile/email for GSTIN ${company_gstin} for further authentication. Please provide OTP.`;
if (error_type === "invalid_otp")
description = `Invalid OTP was provided for GSTIN ${company_gstin}. Please try again.`;

return new Promise(resolve => {
const prompt = new frappe.ui.Dialog({
title: __("Enter OTP"),
fields: [
{
fieldtype: "Data",
label: __("One Time Password"),
fieldname: "otp",
reqd: 1,
description: description,
},
],
primary_action_label: __("Submit"),
primary_action(values) {
resolve(values.otp);
prompt.hide();
},
secondary_action_label: __("Resend OTP"),
secondary_action() {
frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.request_otp",
args: { company_gstin },
callback: function () {
frappe.show_alert({
message: __("OTP has been resent."),
indicator: "green",
});
prompt.get_secondary_btn().addClass("disabled");
},
});
},
});
prompt.show();
});
},

async authenticate_company_gstins(company, company_gstin) {
const { message: gstin_authentication_status } = await frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.validate_company_gstins",
args: { company: company, company_gstin: company_gstin },
});

for (let gstin of Object.keys(gstin_authentication_status)) {
if (gstin_authentication_status[gstin]) continue;

gstin_authentication_status[gstin] =
await this.request_and_authenticate_otp(gstin);
}

return Object.keys(gstin_authentication_status);
},

async request_and_authenticate_otp(gstin) {
await frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.request_otp",
args: { company_gstin: gstin },
});

// wait for OTP to be authenticated to proceed
await this.authenticate_otp(gstin);
},

async authenticate_otp(gstin, error_type = null) {
console.log(gstin, error_type);
if (!error_type) error_type = "otp_requested";

let is_authenticated = false;

while (!is_authenticated) {
const otp = await this.get_gstin_otp(gstin, error_type);

const { message } = await frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.authenticate_otp",
args: { company_gstin: gstin, otp: otp },
});

if (
message &&
["otp_requested", "invalid_otp"].includes(message.error_type)
) {
error_type = message.error_type;
continue;
}

is_authenticated = true;
return true;
}
},
});

class IndiaComplianceForm extends frappe.ui.form.Form {
taxpayer_api_call(method, args, callback) {
// similar to frappe.ui.form.Form.prototype.call
const opts = {
method: method,
doc: this.doc,
args: args,
callback: callback
}

opts.original_callback = opts.callback;
opts.callback = (r) => {
if (!r.exc) this.refresh_fields();
opts.original_callback && opts.original_callback(r);
}

return taxpayer_api.call(opts);
}
}

frappe.ui.form.Form = IndiaComplianceForm;
1 change: 1 addition & 0 deletions india_compliance/public/js/india_compliance.bundle.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import "./utils";
import "./gst_api_handler";
import "./quick_entry";
import "./transaction";
import "./audit_trail_notification";
Expand Down
93 changes: 0 additions & 93 deletions india_compliance/public/js/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -240,47 +240,6 @@ Object.assign(india_compliance, {
}
},

get_gstin_otp(company_gstin, error_type) {
let description = `An OTP has been sent to the registered mobile/email for GSTIN ${company_gstin} for further authentication. Please provide OTP.`;
if (error_type === "invalid_otp")
description = `Invalid OTP was provided for GSTIN ${company_gstin}. Please try again.`;

return new Promise(resolve => {
const prompt = new frappe.ui.Dialog({
title: __("Enter OTP"),
fields: [
{
fieldtype: "Data",
label: __("One Time Password"),
fieldname: "otp",
reqd: 1,
description: description,
},
],
primary_action_label: __("Submit"),
primary_action(values) {
resolve(values.otp);
prompt.hide();
},
secondary_action_label: __("Resend OTP"),
secondary_action() {
frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.request_otp",
args: { company_gstin },
callback: function () {
frappe.show_alert({
message: __("OTP has been resent."),
indicator: "green",
});
prompt.get_secondary_btn().addClass("disabled");
},
});
},
});
prompt.show();
});
},

guess_gst_category(gstin, country) {
if (!gstin) {
if (country && country !== "India") return "Overseas";
Expand Down Expand Up @@ -429,58 +388,6 @@ Object.assign(india_compliance, {
.addClass("text-danger");
},

async authenticate_company_gstins(company, company_gstin) {
const { message: gstin_authentication_status } = await frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.validate_company_gstins",
args: { company: company, company_gstin: company_gstin },
});

for (let gstin of Object.keys(gstin_authentication_status)) {
if (gstin_authentication_status[gstin]) continue;

gstin_authentication_status[gstin] =
await this.request_and_authenticate_otp(gstin);
}

return Object.keys(gstin_authentication_status);
},

async request_and_authenticate_otp(gstin) {
await frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.request_otp",
args: { company_gstin: gstin },
});

// wait for OTP to be authenticated to proceed
await this.authenticate_otp(gstin);
},

async authenticate_otp(gstin, error_type = null) {
if (!error_type) error_type = "otp_requested";

let is_authenticated = false;

while (!is_authenticated) {
const otp = await this.get_gstin_otp(gstin, error_type);

const { message } = await frappe.call({
method: "india_compliance.gst_india.utils.gstr_utils.authenticate_otp",
args: { company_gstin: gstin, otp: otp },
});

if (
message &&
["otp_requested", "invalid_otp"].includes(message.error_type)
) {
error_type = message.error_type;
continue;
}

is_authenticated = true;
return true;
}
},

show_dismissable_alert(wrapper, message, alert_type = "primary", on_close = null) {
const alert = $(`
<div class="container">
Expand Down

0 comments on commit fcb8ae5

Please sign in to comment.