From a7f7e68242acf501354eaae829b673b821a92b03 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Wed, 29 May 2024 15:06:03 +0530 Subject: [PATCH 1/9] fix: gst-transporter-id refactor (cherry picked from commit a87d2cbca753f6f71d8719f979d5b605de75695a) # Conflicts: # india_compliance/gst_india/doctype/gstin/gstin.json # india_compliance/gst_india/doctype/gstin/gstin.py --- .../client_scripts/e_waybill_actions.js | 4 +- .../gst_india/client_scripts/supplier.js | 2 +- .../gst_india/doctype/gstin/gstin.js | 6 +- .../gst_india/doctype/gstin/gstin.json | 19 ++- .../gst_india/doctype/gstin/gstin.py | 110 ++++++++++++++---- .../gst_india/doctype/gstin/test_gstin.py | 70 ++++++----- .../gst_india/overrides/transaction.py | 11 +- india_compliance/gst_india/utils/__init__.py | 45 ++++--- .../gst_india/utils/gstin_info.py | 7 ++ india_compliance/public/js/transaction.js | 2 +- india_compliance/public/js/utils.js | 9 ++ 11 files changed, 193 insertions(+), 92 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/e_waybill_actions.js b/india_compliance/gst_india/client_scripts/e_waybill_actions.js index 473581f98..7140264d7 100644 --- a/india_compliance/gst_india/client_scripts/e_waybill_actions.js +++ b/india_compliance/gst_india/client_scripts/e_waybill_actions.js @@ -1162,9 +1162,7 @@ async function update_gst_tranporter_id(dialog) { } function set_gst_transporter_id_status(dialog) { - const gst_transporter_id_field = dialog.get_field("gst_transporter_id"); - - india_compliance.set_gstin_status(gst_transporter_id_field); + india_compliance.validate_gst_transporter_id(dialog.get_value("gst_transporter_id")); } function update_generation_dialog(dialog, doc) { diff --git a/india_compliance/gst_india/client_scripts/supplier.js b/india_compliance/gst_india/client_scripts/supplier.js index 05a0bfa11..2b36b5ac6 100644 --- a/india_compliance/gst_india/client_scripts/supplier.js +++ b/india_compliance/gst_india/client_scripts/supplier.js @@ -30,6 +30,6 @@ frappe.ui.form.on(DOCTYPE, { return; gst_transporter_id_field = frm.get_field("gst_transporter_id"); - india_compliance.set_gstin_status(gst_transporter_id_field); + india_compliance.validate_gst_transporter_id(gst_transporter_id_field.value); }, }); diff --git a/india_compliance/gst_india/doctype/gstin/gstin.js b/india_compliance/gst_india/doctype/gstin/gstin.js index 2abed01b8..b5c68b1b1 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.js +++ b/india_compliance/gst_india/doctype/gstin/gstin.js @@ -5,8 +5,12 @@ frappe.ui.form.on("GSTIN", { refresh(frm) { frm.disable_form(); - frm.add_custom_button(__("Refresh Now"), () => { + frm.add_custom_button(__("Refresh GSTIN Status"), () => { frm.call("update_gstin_status"); }); + + frm.add_custom_button(__("Refresh Transporter ID Status"), () => { + frm.call("update_transporter_id_status"); + }); }, }); diff --git a/india_compliance/gst_india/doctype/gstin/gstin.json b/india_compliance/gst_india/doctype/gstin/gstin.json index 234b1382c..0be1c14e1 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.json +++ b/india_compliance/gst_india/doctype/gstin/gstin.json @@ -13,8 +13,12 @@ "column_break_nrjd", "last_updated_on", "cancelled_date", +<<<<<<< HEAD "section_break_ttzc", "gstr_1_filed_upto" +======= + "transporter_id_status" +>>>>>>> a87d2cbc (fix: gst-transporter-id refactor) ], "fields": [ { @@ -30,8 +34,7 @@ "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, - "label": "Status", - "reqd": 1 + "label": "Status" }, { "fieldname": "registration_date", @@ -63,6 +66,7 @@ "label": "Is Blocked" }, { +<<<<<<< HEAD "fieldname": "section_break_ttzc", "fieldtype": "Section Break" }, @@ -71,12 +75,23 @@ "fieldtype": "Date", "hidden": 1, "label": "GSTR-1 Filed Upto" +======= + "fieldname": "transporter_id_status", + "fieldtype": "Data", + "in_list_view": 1, + "in_standard_filter": 1, + "label": "Transporter ID Status" +>>>>>>> a87d2cbc (fix: gst-transporter-id refactor) } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], +<<<<<<< HEAD "modified": "2024-05-17 15:38:05.867522", +======= + "modified": "2024-05-21 12:59:58.322776", +>>>>>>> a87d2cbc (fix: gst-transporter-id refactor) "modified_by": "Administrator", "module": "GST India", "name": "GSTIN", diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index 96a5f7160..487b3fc86 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -6,6 +6,7 @@ from frappe.model.document import Document from frappe.utils import date_diff, format_date, get_datetime +from india_compliance.exceptions import GSPServerError from india_compliance.gst_india.api_classes.e_invoice import EInvoiceAPI from india_compliance.gst_india.api_classes.e_waybill import EWaybillAPI from india_compliance.gst_india.api_classes.public import PublicAPI @@ -40,7 +41,15 @@ def update_gstin_status(self): """ Permission check not required as GSTIN details are public and user has access to doc. """ - create_or_update_gstin_status(self.gstin) + # hard refresh will always use public API + create_or_update_gstin_status(self.gstin, use_public_api=True) + + @frappe.whitelist() + def update_transporter_id_status(self): + """ + Permission check not required as GSTIN details are public and user has access to doc. + """ + create_or_update_gstin_status(self.gstin, is_transporter_id=True) @frappe.whitelist() @@ -61,17 +70,15 @@ def get_gstin_status( return frappe.get_doc("GSTIN", gstin) - return get_updated_gstin(gstin, transaction_date, is_request_from_ui) + return get_updated_gstin(gstin, transaction_date, is_request_from_ui, force_update) -def get_updated_gstin(gstin, transaction_date=None, is_request_from_ui=0): +def get_updated_gstin( + gstin, transaction_date=None, is_request_from_ui=0, force_update=0 +): if is_request_from_ui: - return create_or_update_gstin_status(gstin) - - if gstin[:2] == "88": - callback = _validate_gst_transporter_id_info - else: - callback = _validate_gstin_info + # hard refresh from UI will always use public API + return create_or_update_gstin_status(gstin, use_public_api=force_update) frappe.enqueue( create_or_update_gstin_status, @@ -79,7 +86,7 @@ def get_updated_gstin(gstin, transaction_date=None, is_request_from_ui=0): queue="short", gstin=gstin, transaction_date=transaction_date, - callback=callback, + callback=_validate_gstin_info, ) @@ -88,13 +95,17 @@ def create_or_update_gstin_status( response=None, transaction_date=None, callback=None, + use_public_api=False, + is_transporter_id=False, ): doctype = "GSTIN" - if gstin and gstin[:2] == "88": + if is_transporter_id: response = get_transporter_id_info(gstin) else: - response = _get_gstin_info(gstin=gstin, response=response) + response = _get_gstin_info( + gstin=gstin, response=response, use_public_api=use_public_api + ) if not response: return @@ -113,16 +124,19 @@ def create_or_update_gstin_status( return doc -def _get_gstin_info(*, gstin=None, response=None): +def _get_gstin_info(*, gstin=None, response=None, use_public_api=False): if response: return get_formatted_response(response) validate_gstin(gstin) try: + if frappe.cache.get_value("gst_server_error"): + return + company_gstin = get_company_gstin() - if not company_gstin: + if use_public_api or not company_gstin: response = PublicAPI().get_gstin_info(gstin) return get_formatted_response(response) @@ -137,7 +151,10 @@ def _get_gstin_info(*, gstin=None, response=None): } ) - except Exception: + except Exception as e: + if isinstance(e, GSPServerError): + frappe.cache.set_value("gst_server_error", True, expires_in_sec=60) + frappe.log_error( title=_("Error fetching GSTIN status"), message=frappe.get_traceback(), @@ -197,7 +214,7 @@ def _throw(message): ) -def _validate_gst_transporter_id_info(transporter_id_info, **kwargs): +def _validate_gst_transporter_id_info(transporter_id_info, *args, **kwargs): if not transporter_id_info: return @@ -213,7 +230,7 @@ def _throw(message): message=message, ) - if transporter_id_info.status != "Active": + if transporter_id_info.transporter_id_status != "Active": return _throw( _( "Transporter ID {0} is not Active. Please make sure that transporter ID is valid." @@ -250,11 +267,6 @@ def is_status_refresh_required(gstin, transaction_date): if not doc: return True - # Transporter ID status is never cancelled - is_transporter_id = gstin[:2] == "88" - if is_transporter_id: - return False - if not transaction_date: # not from transactions return False @@ -301,13 +313,65 @@ def get_transporter_id_info(transporter_id): return frappe._dict( { "gstin": transporter_id, - "status": "Active" if response.transin else "Invalid", + "transporter_id_status": "Active" if response.transin else "Invalid", } ) +<<<<<<< HEAD def get_gstr_1_filed_upto(gstin): if not gstin: return return frappe.db.get_value("GSTIN", gstin, "gstr_1_filed_upto") +======= +@frappe.whitelist() +def validate_gst_transporter_id(transporter_id): + """ + Validates GST Transporter ID and warns user if transporter_id is not Active + + Args: + transporter_id (str): GST Transporter ID + """ + if not transporter_id: + return + + gstin = None + + # Check if GSTIN doc exists + if frappe.db.exists("GSTIN", transporter_id): + gstin = frappe.get_doc("GSTIN", transporter_id) + + # Check if transporter_id starts with 88 or is not valid GSTIN and use Transporter ID API + elif transporter_id[:2] == "88" or not validate_gstin(transporter_id, throw=False): + gstin = create_or_update_gstin_status( + transporter_id, + is_transporter_id=True, + callback=_validate_gst_transporter_id_info, + ) + + # Use GSTIN API + else: + gstin = create_or_update_gstin_status( + transporter_id, callback=_validate_gstin_info + ) + + # If GSTIN status is not Active and transporter_id_status is None, use Transporter ID API + if gstin.status and gstin.status != "Active" and not gstin.transporter_id_status: + gstin = create_or_update_gstin_status( + transporter_id, + is_transporter_id=True, + callback=_validate_gst_transporter_id_info, + ) + + # Return if GSTIN or transporter_id_status is Active + if gstin and (gstin.status == "Active" or gstin.transporter_id_status == "Active"): + return + + frappe.msgprint( + _("Transporter ID {0} seems to be {1}").format( + transporter_id, gstin.transporter_id_status if gstin else "Inactive" + ), + indicator="orange", + ) +>>>>>>> a87d2cbc (fix: gst-transporter-id refactor) diff --git a/india_compliance/gst_india/doctype/gstin/test_gstin.py b/india_compliance/gst_india/doctype/gstin/test_gstin.py index e948c983e..d9b495641 100644 --- a/india_compliance/gst_india/doctype/gstin/test_gstin.py +++ b/india_compliance/gst_india/doctype/gstin/test_gstin.py @@ -1,48 +1,44 @@ # Copyright (c) 2023, Resilient Tech and Contributors # See license.txt -import re +import responses +from responses import matchers -import frappe from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import now -from india_compliance.gst_india.utils.tests import create_transaction +from india_compliance.gst_india.doctype.gstin.gstin import validate_gst_transporter_id + +TEST_GSTIN = "24AANFA2641L1ZK" + +TRANSPORTER_ID_API_RESPONSE = { + "success": True, + "message": "Transporter details are fetched successfully", + "result": { + "transin": TEST_GSTIN, + "tradeName": "_Test Transporter ID Comapany", + "legalName": "_Test Transporter ID Comapany", + "address1": "address 1", + "address2": "address 2", + "stateCode": "24", + "pinCode": "390020", + }, +} class TestGSTIN(FrappeTestCase): + @responses.activate @change_settings("GST Settings", {"validate_gstin_status": 1, "sandbox_mode": 0}) - def test_validate_gst_transporter_id_info(self): - # customer gstin - frappe.get_doc( - { - "doctype": "GSTIN", - "gstin": "24AANFA2641L1ZF", - "registration_date": "2021-01-01", - "status": "Active", - "last_updated_on": now(), - } - ).insert(ignore_if_duplicate=True) - - # gst transporter id - frappe.get_doc( - { - "doctype": "GSTIN", - "gstin": "88AABCM9407D1ZS", - "status": "Invalid", - "last_updated_on": now(), - } - ).insert(ignore_if_duplicate=True) - - si = create_transaction( - doctype="Sales Invoice", - gst_transporter_id="88AABCM9407D1ZS", - do_not_save=True, - ) + def test_validate_gst_transporter_id(self): + self.mock_get_transporter_details_response() + + validate_gst_transporter_id(TEST_GSTIN) + + def mock_get_transporter_details_response(self): + url = "https://asp.resilient.tech/ewb/Master/GetTransporterDetails" - self.assertRaisesRegex( - frappe.ValidationError, - re.compile( - r"^(.*is not Active. Please make sure that transporter ID is valid.*)$" - ), - si.save, + responses.add( + responses.GET, + url, + json=TRANSPORTER_ID_API_RESPONSE, + match=[matchers.query_param_matcher({"trn_no": TEST_GSTIN})], + status=200, ) diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 1a810efd6..69b840c36 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -21,10 +21,12 @@ restrict_gstr_1_transaction_for, ) from india_compliance.gst_india.doctype.gstin.gstin import ( - _validate_gst_transporter_id_info, _validate_gstin_info, get_gstin_status, ) +from india_compliance.gst_india.doctype.gstin.gstin import ( + validate_gst_transporter_id as _validate_gst_transporter_id, +) from india_compliance.gst_india.utils import ( get_all_gst_accounts, get_gst_accounts_by_tax_type, @@ -1447,12 +1449,7 @@ def validate_gst_transporter_id(doc): doc.gst_transporter_id, label="GST Transporter ID", is_transporter_id=True ) - gstin_doc = get_gstin_status(doc.gst_transporter_id) - - if not gstin_doc: - return - - _validate_gst_transporter_id_info(gstin_doc, throw=True) + _validate_gst_transporter_id(doc.gst_transporter_id) def validate_company_address_field(doc): diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index 0609e897a..a9dcd09e7 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -152,6 +152,7 @@ def validate_gstin( *, is_tcs_gstin=False, is_transporter_id=False, + throw=True, ): """ Validate GSTIN with following checks: @@ -166,21 +167,27 @@ def validate_gstin( gstin = gstin.upper().strip() if len(gstin) != 15: - frappe.throw( - _("{0} {1} must have 15 characters").format(label, frappe.bold(gstin)), - title=_("Invalid {0}").format(label), - ) + if throw: + frappe.throw( + _("{0} {1} must have 15 characters").format(label, frappe.bold(gstin)), + title=_("Invalid {0}").format(label), + ) + return False - if not is_transporter_id: - validate_gstin_check_digit(gstin, label) + is_valid = True + + if not (is_transporter_id and gstin.startswith("88")): + is_valid = validate_gstin_check_digit(gstin, label=label, throw=throw) if is_tcs_gstin and not TCS.match(gstin): - frappe.throw( - _("Invalid format for e-Commerce Operator (TCS) GSTIN"), - title=_("Invalid GSTIN"), - ) + if throw: + frappe.throw( + _("Invalid format for e-Commerce Operator (TCS) GSTIN"), + title=_("Invalid GSTIN"), + ) + return False - return gstin + return gstin if is_valid else is_valid def validate_gst_category(gst_category, gstin): @@ -323,7 +330,7 @@ def get_data_file_path(file_name): return frappe.get_app_path("india_compliance", "gst_india", "data", file_name) -def validate_gstin_check_digit(gstin, label="GSTIN"): +def validate_gstin_check_digit(gstin, label="GSTIN", throw=True): """ Function to validate the check digit of the GSTIN. """ @@ -338,11 +345,15 @@ def validate_gstin_check_digit(gstin, label="GSTIN"): total += digit factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: - frappe.throw( - _( - """Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""" - ).format(label) - ) + if throw: + frappe.throw( + _( + """Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""" + ).format(label) + ) + return False + + return True def is_overseas_doc(doc): diff --git a/india_compliance/gst_india/utils/gstin_info.py b/india_compliance/gst_india/utils/gstin_info.py index 5c9ffdc4e..a22cf4f61 100644 --- a/india_compliance/gst_india/utils/gstin_info.py +++ b/india_compliance/gst_india/utils/gstin_info.py @@ -6,6 +6,7 @@ from frappe import _ from frappe.utils import getdate +from india_compliance.exceptions import GSPServerError from india_compliance.gst_india.api_classes.base import BASE_URL from india_compliance.gst_india.api_classes.public import PublicAPI from india_compliance.gst_india.doctype.gstr_1_log.gstr_1_log import ( @@ -46,6 +47,9 @@ def _get_gstin_info(gstin, *, throw_error=True): if not response: try: + if frappe.cache.get_value("gst_server_error"): + return + response = PublicAPI().get_gstin_info(gstin) frappe.enqueue( "india_compliance.gst_india.doctype.gstin.gstin.create_or_update_gstin_status", @@ -53,6 +57,9 @@ def _get_gstin_info(gstin, *, throw_error=True): response=response, ) except Exception as exc: + if isinstance(exc, GSPServerError): + frappe.cache.set_value("gst_server_error", True, expires_in_sec=60) + if throw_error: raise exc diff --git a/india_compliance/public/js/transaction.js b/india_compliance/public/js/transaction.js index 580c1b7f9..324e01d44 100644 --- a/india_compliance/public/js/transaction.js +++ b/india_compliance/public/js/transaction.js @@ -181,7 +181,7 @@ function set_and_validate_gstin_status(doctype) { }, gst_transporter_id(frm) { - _set_and_validate_gstin_status(frm, "gst_transporter_id"); + india_compliance.validate_gst_transporter_id(frm.get_field("gst_transporter_id").value); }, posting_date(frm) { diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index 7f1dc236a..387d05b88 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -115,6 +115,15 @@ Object.assign(india_compliance, { return message; }, + async validate_gst_transporter_id(transporter_id) { + if (!transporter_id || transporter_id.length !== 15) return; + + await frappe.call({ + method: "india_compliance.gst_india.doctype.gstin.gstin.validate_gst_transporter_id", + args: { transporter_id }, + }); + }, + get_gstin_status_desc(status, datetime) { if (!status) return; const user_date = frappe.datetime.str_to_user(datetime); From ef43aad64884e7e223d6fc6a5d327f74413ff6b0 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Thu, 30 May 2024 12:00:38 +0530 Subject: [PATCH 2/9] fix: failing test case fixed (cherry picked from commit 0fc37d4318dd1c1d812fda9758a5c5caa3a5e640) --- india_compliance/gst_india/doctype/gstin/gstin.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index 487b3fc86..a3fcc0f6c 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -357,7 +357,7 @@ def validate_gst_transporter_id(transporter_id): ) # If GSTIN status is not Active and transporter_id_status is None, use Transporter ID API - if gstin.status and gstin.status != "Active" and not gstin.transporter_id_status: + if gstin and gstin.status != "Active" and not gstin.transporter_id_status: gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, From cd374d870f8be810189d9bfd846d7c05f96addc4 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Thu, 30 May 2024 12:28:27 +0530 Subject: [PATCH 3/9] chore: refactor (cherry picked from commit f7feff80226df01488e00268bf9fd79b3a7d5c6b) --- india_compliance/gst_india/doctype/gstin/gstin.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index a3fcc0f6c..888be0f46 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -356,8 +356,11 @@ def validate_gst_transporter_id(transporter_id): transporter_id, callback=_validate_gstin_info ) + if not gstin: + return + # If GSTIN status is not Active and transporter_id_status is None, use Transporter ID API - if gstin and gstin.status != "Active" and not gstin.transporter_id_status: + if gstin.status != "Active" and not gstin.transporter_id_status: gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, @@ -365,13 +368,11 @@ def validate_gst_transporter_id(transporter_id): ) # Return if GSTIN or transporter_id_status is Active - if gstin and (gstin.status == "Active" or gstin.transporter_id_status == "Active"): + if gstin.status == "Active" or gstin.transporter_id_status == "Active": return frappe.msgprint( - _("Transporter ID {0} seems to be {1}").format( - transporter_id, gstin.transporter_id_status if gstin else "Inactive" - ), + _("Transporter ID {0} seems to be Inactive").format(transporter_id), indicator="orange", ) >>>>>>> a87d2cbc (fix: gst-transporter-id refactor) From bedde72926c926a1c2d0c220af89408d54fa4421 Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Thu, 30 May 2024 12:48:14 +0530 Subject: [PATCH 4/9] fix: use validate_gstin_check_digit instead of validate_gstin in validate_gst_transporter_id (cherry picked from commit 3f2a83b42d8909241444e6695bada2865a67b1a9) --- .../gst_india/doctype/gstin/gstin.py | 5 ++- india_compliance/gst_india/utils/__init__.py | 42 ++++++++----------- 2 files changed, 22 insertions(+), 25 deletions(-) diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index 888be0f46..471ed53e7 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -14,6 +14,7 @@ is_api_enabled, parse_datetime, validate_gstin, + validate_gstin_check_digit, ) GSTIN_STATUS = { @@ -343,7 +344,9 @@ def validate_gst_transporter_id(transporter_id): gstin = frappe.get_doc("GSTIN", transporter_id) # Check if transporter_id starts with 88 or is not valid GSTIN and use Transporter ID API - elif transporter_id[:2] == "88" or not validate_gstin(transporter_id, throw=False): + elif transporter_id[:2] == "88" or not validate_gstin_check_digit( + transporter_id, throw=False + ): gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index a9dcd09e7..7600a91f4 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -152,7 +152,6 @@ def validate_gstin( *, is_tcs_gstin=False, is_transporter_id=False, - throw=True, ): """ Validate GSTIN with following checks: @@ -167,27 +166,21 @@ def validate_gstin( gstin = gstin.upper().strip() if len(gstin) != 15: - if throw: - frappe.throw( - _("{0} {1} must have 15 characters").format(label, frappe.bold(gstin)), - title=_("Invalid {0}").format(label), - ) - return False - - is_valid = True + frappe.throw( + _("{0} {1} must have 15 characters").format(label, frappe.bold(gstin)), + title=_("Invalid {0}").format(label), + ) if not (is_transporter_id and gstin.startswith("88")): - is_valid = validate_gstin_check_digit(gstin, label=label, throw=throw) + validate_gstin_check_digit(gstin, label=label) if is_tcs_gstin and not TCS.match(gstin): - if throw: - frappe.throw( - _("Invalid format for e-Commerce Operator (TCS) GSTIN"), - title=_("Invalid GSTIN"), - ) - return False + frappe.throw( + _("Invalid format for e-Commerce Operator (TCS) GSTIN"), + title=_("Invalid GSTIN"), + ) - return gstin if is_valid else is_valid + return gstin def validate_gst_category(gst_category, gstin): @@ -345,13 +338,14 @@ def validate_gstin_check_digit(gstin, label="GSTIN", throw=True): total += digit factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: - if throw: - frappe.throw( - _( - """Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""" - ).format(label) - ) - return False + if not throw: + return False + + frappe.throw( + _( + """Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""" + ).format(label) + ) return True From 44455401c0b715a357912359e867eb9658b7c18c Mon Sep 17 00:00:00 2001 From: Vishakh Desai Date: Thu, 30 May 2024 15:34:43 +0530 Subject: [PATCH 5/9] chore: refactor (cherry picked from commit dc8db1813cbb01783d43a1c3b22a60b328a929bc) --- india_compliance/gst_india/client_scripts/supplier.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/supplier.js b/india_compliance/gst_india/client_scripts/supplier.js index 2b36b5ac6..c42d12cb3 100644 --- a/india_compliance/gst_india/client_scripts/supplier.js +++ b/india_compliance/gst_india/client_scripts/supplier.js @@ -29,7 +29,6 @@ frappe.ui.form.on(DOCTYPE, { ) return; - gst_transporter_id_field = frm.get_field("gst_transporter_id"); - india_compliance.validate_gst_transporter_id(gst_transporter_id_field.value); + india_compliance.validate_gst_transporter_id(frm.doc.gst_transporter_id); }, }); From 60a22a63c9e9e7ea3d5195391dd321299fa323b0 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Fri, 14 Jun 2024 11:28:01 +0530 Subject: [PATCH 6/9] refactor: naming, usage etc (cherry picked from commit 6f3f5e84b95ccea3262fdc35fef5c87e5ab174c3) # Conflicts: # india_compliance/gst_india/doctype/gstin/gstin.py --- .../client_scripts/e_waybill_actions.js | 10 ++--- .../gst_india/client_scripts/supplier.js | 6 --- .../gst_india/doctype/gstin/gstin.py | 37 ++++++++++++------- .../gst_india/overrides/transaction.py | 4 +- india_compliance/gst_india/utils/__init__.py | 10 ++--- india_compliance/public/js/transaction.js | 2 +- india_compliance/public/js/utils.js | 4 +- 7 files changed, 37 insertions(+), 36 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/e_waybill_actions.js b/india_compliance/gst_india/client_scripts/e_waybill_actions.js index 7140264d7..166bb2757 100644 --- a/india_compliance/gst_india/client_scripts/e_waybill_actions.js +++ b/india_compliance/gst_india/client_scripts/e_waybill_actions.js @@ -295,7 +295,7 @@ function show_generate_e_waybill_dialog(frm) { ); d.show(); - set_gst_transporter_id_status(d); + validate_gst_transporter_id(d); //Alert if E-waybill cannot be generated using api if (!is_e_waybill_generatable(frm)) { @@ -374,7 +374,7 @@ function get_generate_e_waybill_dialog(opts, frm) { frm.doc.gst_transporter_id?.length == 15 ? frm.doc.gst_transporter_id : "", - onchange: () => set_gst_transporter_id_status(d), + onchange: () => validate_gst_transporter_id(d), }, // Sub Supply Type will be visible here for Delivery Note @@ -814,7 +814,7 @@ function show_update_transporter_dialog(frm) { frm.doc.gst_transporter_id.length == 15 ? frm.doc.gst_transporter_id : "", - onchange: () => set_gst_transporter_id_status(d), + onchange: () => validate_gst_transporter_id(d), }, { label: "Update e-Waybill Print/Data", @@ -841,7 +841,7 @@ function show_update_transporter_dialog(frm) { // To prevent triggering of change event on input twice frappe.ui.form.ControlData.trigger_change_on_input_event = true; d.show(); - set_gst_transporter_id_status(d); + validate_gst_transporter_id(d); } async function show_extend_validity_dialog(frm) { @@ -1161,7 +1161,7 @@ async function update_gst_tranporter_id(dialog) { dialog.set_value("gst_transporter_id", response.gst_transporter_id); } -function set_gst_transporter_id_status(dialog) { +function validate_gst_transporter_id(dialog) { india_compliance.validate_gst_transporter_id(dialog.get_value("gst_transporter_id")); } diff --git a/india_compliance/gst_india/client_scripts/supplier.js b/india_compliance/gst_india/client_scripts/supplier.js index c42d12cb3..750423c8e 100644 --- a/india_compliance/gst_india/client_scripts/supplier.js +++ b/india_compliance/gst_india/client_scripts/supplier.js @@ -23,12 +23,6 @@ frappe.ui.form.on(DOCTYPE, { }, gst_transporter_id(frm) { - if ( - !frm.doc.gst_transporter_id || - frm.doc.gst_transporter_id.length < 15 - ) - return; - india_compliance.validate_gst_transporter_id(frm.doc.gst_transporter_id); }, }); diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index 471ed53e7..ece07c874 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -87,7 +87,7 @@ def get_updated_gstin( queue="short", gstin=gstin, transaction_date=transaction_date, - callback=_validate_gstin_info, + callback=_validate_gstin_status, ) @@ -102,9 +102,9 @@ def create_or_update_gstin_status( doctype = "GSTIN" if is_transporter_id: - response = get_transporter_id_info(gstin) + response = get_transporter_id_status(gstin) else: - response = _get_gstin_info( + response = _get_gstin_status( gstin=gstin, response=response, use_public_api=use_public_api ) @@ -125,7 +125,7 @@ def create_or_update_gstin_status( return doc -def _get_gstin_info(*, gstin=None, response=None, use_public_api=False): +def _get_gstin_status(*, gstin=None, response=None, use_public_api=False): if response: return get_formatted_response(response) @@ -166,7 +166,7 @@ def _get_gstin_info(*, gstin=None, response=None, use_public_api=False): frappe.cache().set_value(gstin, True, expires_in_sec=180) -def _validate_gstin_info(gstin_doc, transaction_date=None, throw=False): +def _validate_gstin_status(gstin_doc, transaction_date=None, throw=False): if not (gstin_doc and transaction_date): return @@ -215,7 +215,7 @@ def _throw(message): ) -def _validate_gst_transporter_id_info(transporter_id_info, *args, **kwargs): +def _validate_gst_transporter_id_status(transporter_id_info, *args, **kwargs): if not transporter_id_info: return @@ -296,7 +296,7 @@ def get_formatted_response(response): ) -def get_transporter_id_info(transporter_id): +def get_transporter_id_status(transporter_id): if not frappe.get_cached_value("GST Settings", None, "enable_e_waybill"): return @@ -344,19 +344,17 @@ def validate_gst_transporter_id(transporter_id): gstin = frappe.get_doc("GSTIN", transporter_id) # Check if transporter_id starts with 88 or is not valid GSTIN and use Transporter ID API - elif transporter_id[:2] == "88" or not validate_gstin_check_digit( - transporter_id, throw=False - ): + elif transporter_id[:2] == "88" or has_gstin_check_digit_failed(transporter_id): gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, - callback=_validate_gst_transporter_id_info, + callback=_validate_gst_transporter_id_status, ) # Use GSTIN API else: gstin = create_or_update_gstin_status( - transporter_id, callback=_validate_gstin_info + transporter_id, callback=_validate_gstin_status ) if not gstin: @@ -367,7 +365,7 @@ def validate_gst_transporter_id(transporter_id): gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, - callback=_validate_gst_transporter_id_info, + callback=_validate_gst_transporter_id_status, ) # Return if GSTIN or transporter_id_status is Active @@ -378,4 +376,17 @@ def validate_gst_transporter_id(transporter_id): _("Transporter ID {0} seems to be Inactive").format(transporter_id), indicator="orange", ) +<<<<<<< HEAD >>>>>>> a87d2cbc (fix: gst-transporter-id refactor) +======= + + +def has_gstin_check_digit_failed(gstin): + try: + validate_gstin_check_digit(gstin) + + except frappe.ValidationError: + return True + + return False +>>>>>>> 6f3f5e84 (refactor: naming, usage etc) diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 69b840c36..853f57d00 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -21,7 +21,7 @@ restrict_gstr_1_transaction_for, ) from india_compliance.gst_india.doctype.gstin.gstin import ( - _validate_gstin_info, + _validate_gstin_status, get_gstin_status, ) from india_compliance.gst_india.doctype.gstin.gstin import ( @@ -1434,7 +1434,7 @@ def validate_gstin_status(gstin, transaction_date): if not gstin_doc: return - _validate_gstin_info(gstin_doc, transaction_date, throw=True) + _validate_gstin_status(gstin_doc, transaction_date, throw=True) def validate_gst_transporter_id(doc): diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index 7600a91f4..3f41c8bdd 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -171,7 +171,8 @@ def validate_gstin( title=_("Invalid {0}").format(label), ) - if not (is_transporter_id and gstin.startswith("88")): + # eg: 29AAFCA7488L1Z0 invalid check digit for valid transporter id + if not is_transporter_id: validate_gstin_check_digit(gstin, label=label) if is_tcs_gstin and not TCS.match(gstin): @@ -323,7 +324,7 @@ def get_data_file_path(file_name): return frappe.get_app_path("india_compliance", "gst_india", "data", file_name) -def validate_gstin_check_digit(gstin, label="GSTIN", throw=True): +def validate_gstin_check_digit(gstin, label="GSTIN"): """ Function to validate the check digit of the GSTIN. """ @@ -338,17 +339,12 @@ def validate_gstin_check_digit(gstin, label="GSTIN", throw=True): total += digit factor = 2 if factor == 1 else 1 if gstin[-1] != code_point_chars[((mod - (total % mod)) % mod)]: - if not throw: - return False - frappe.throw( _( """Invalid {0}! The check digit validation has failed. Please ensure you've typed the {0} correctly.""" ).format(label) ) - return True - def is_overseas_doc(doc): return is_overseas_transaction(doc.doctype, doc.gst_category, doc.place_of_supply) diff --git a/india_compliance/public/js/transaction.js b/india_compliance/public/js/transaction.js index 324e01d44..57909384a 100644 --- a/india_compliance/public/js/transaction.js +++ b/india_compliance/public/js/transaction.js @@ -181,7 +181,7 @@ function set_and_validate_gstin_status(doctype) { }, gst_transporter_id(frm) { - india_compliance.validate_gst_transporter_id(frm.get_field("gst_transporter_id").value); + india_compliance.validate_gst_transporter_id(frm.doc.gst_transporter_id); }, posting_date(frm) { diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index 387d05b88..5e74c20cc 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -115,10 +115,10 @@ Object.assign(india_compliance, { return message; }, - async validate_gst_transporter_id(transporter_id) { + validate_gst_transporter_id(transporter_id) { if (!transporter_id || transporter_id.length !== 15) return; - await frappe.call({ + frappe.call({ method: "india_compliance.gst_india.doctype.gstin.gstin.validate_gst_transporter_id", args: { transporter_id }, }); From 70988902795f52af822d038d466ed36a5175f2c9 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Sat, 15 Jun 2024 08:29:40 +0530 Subject: [PATCH 7/9] fix: changes as per reivew (cherry picked from commit 574b01605da4716a05b1c506498d391dbe247959) # Conflicts: # india_compliance/gst_india/doctype/gstin/gstin.json # india_compliance/gst_india/doctype/gstin/gstin.py # india_compliance/gst_india/overrides/transaction.py # india_compliance/gst_india/utils/gstin_info.py --- .../doctype/gst_settings/gst_settings.py | 20 ++ .../gst_india/doctype/gstin/gstin.json | 19 +- .../gst_india/doctype/gstin/gstin.py | 233 +++++------------- .../gst_india/overrides/transaction.py | 17 +- india_compliance/gst_india/utils/__init__.py | 2 +- .../gst_india/utils/gstin_info.py | 113 ++++++++- india_compliance/public/js/utils.js | 11 +- 7 files changed, 222 insertions(+), 193 deletions(-) diff --git a/india_compliance/gst_india/doctype/gst_settings/gst_settings.py b/india_compliance/gst_india/doctype/gst_settings/gst_settings.py index a25bf4864..70fe08289 100644 --- a/india_compliance/gst_india/doctype/gst_settings/gst_settings.py +++ b/india_compliance/gst_india/doctype/gst_settings/gst_settings.py @@ -90,6 +90,26 @@ def update_retry_e_invoice_e_waybill_scheduled_job(self): not self.enable_retry_einv_ewb_generation, ) + def get_gstin_with_credentials(self, service=None): + if not service: + return + + if service == "Returns" and not self: + return + + if service == "e-Waybill" and not self.enable_e_waybill: + return + + if service == "e-Invoice" and not self.enable_e_invoice: + return + + if service in ["e-Invoice", "e-Waybill"]: + service = "e-Waybill / e-Invoice" + + for row in self.credentials: + if row.service == service: + return row.gstin + def validate_gst_accounts(self): account_list = [] company_wise_account_types = {} diff --git a/india_compliance/gst_india/doctype/gstin/gstin.json b/india_compliance/gst_india/doctype/gstin/gstin.json index 0be1c14e1..845329c49 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.json +++ b/india_compliance/gst_india/doctype/gstin/gstin.json @@ -9,16 +9,20 @@ "gstin", "registration_date", "status", - "is_blocked", + "transporter_id_status", "column_break_nrjd", "last_updated_on", "cancelled_date", +<<<<<<< HEAD <<<<<<< HEAD "section_break_ttzc", "gstr_1_filed_upto" ======= "transporter_id_status" >>>>>>> a87d2cbc (fix: gst-transporter-id refactor) +======= + "is_blocked" +>>>>>>> 574b0160 (fix: changes as per reivew) ], "fields": [ { @@ -34,7 +38,7 @@ "fieldtype": "Data", "in_list_view": 1, "in_standard_filter": 1, - "label": "Status" + "label": "GSTIN Status" }, { "fieldname": "registration_date", @@ -79,7 +83,6 @@ "fieldname": "transporter_id_status", "fieldtype": "Data", "in_list_view": 1, - "in_standard_filter": 1, "label": "Transporter ID Status" >>>>>>> a87d2cbc (fix: gst-transporter-id refactor) } @@ -87,11 +90,15 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], +<<<<<<< HEAD <<<<<<< HEAD "modified": "2024-05-17 15:38:05.867522", ======= "modified": "2024-05-21 12:59:58.322776", >>>>>>> a87d2cbc (fix: gst-transporter-id refactor) +======= + "modified": "2024-06-14 13:59:53.998262", +>>>>>>> 574b0160 (fix: changes as per reivew) "modified_by": "Administrator", "module": "GST India", "name": "GSTIN", @@ -115,6 +122,12 @@ "report": 1, "role": "Accounts Manager", "share": 1 + }, + { + "export": 1, + "read": 1, + "report": 1, + "role": "Accounts User" } ], "sort_field": "modified", diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index ece07c874..c79c13f54 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -6,15 +6,10 @@ from frappe.model.document import Document from frappe.utils import date_diff, format_date, get_datetime -from india_compliance.exceptions import GSPServerError -from india_compliance.gst_india.api_classes.e_invoice import EInvoiceAPI -from india_compliance.gst_india.api_classes.e_waybill import EWaybillAPI -from india_compliance.gst_india.api_classes.public import PublicAPI -from india_compliance.gst_india.utils import ( - is_api_enabled, - parse_datetime, - validate_gstin, - validate_gstin_check_digit, +from india_compliance.gst_india.utils import is_api_enabled, validate_gstin_check_digit +from india_compliance.gst_india.utils.gstin_info import ( + fetch_gstin_status, + fetch_transporter_id_status, ) GSTIN_STATUS = { @@ -43,7 +38,7 @@ def update_gstin_status(self): Permission check not required as GSTIN details are public and user has access to doc. """ # hard refresh will always use public API - create_or_update_gstin_status(self.gstin, use_public_api=True) + create_or_update_gstin_status(self.gstin, throw=True) @frappe.whitelist() def update_transporter_id_status(self): @@ -53,63 +48,24 @@ def update_transporter_id_status(self): create_or_update_gstin_status(self.gstin, is_transporter_id=True) -@frappe.whitelist() -def get_gstin_status( - gstin, transaction_date=None, is_request_from_ui=0, force_update=0 -): - """ - Permission check not required as GSTIN details are public where GSTIN is known. - """ - if not gstin: - return - - if not int(force_update) and not is_status_refresh_required( - gstin, transaction_date - ): - if not frappe.db.exists("GSTIN", gstin): - return - - return frappe.get_doc("GSTIN", gstin) - - return get_updated_gstin(gstin, transaction_date, is_request_from_ui, force_update) - - -def get_updated_gstin( - gstin, transaction_date=None, is_request_from_ui=0, force_update=0 -): - if is_request_from_ui: - # hard refresh from UI will always use public API - return create_or_update_gstin_status(gstin, use_public_api=force_update) - - frappe.enqueue( - create_or_update_gstin_status, - enqueue_after_commit=True, - queue="short", - gstin=gstin, - transaction_date=transaction_date, - callback=_validate_gstin_status, - ) - - def create_or_update_gstin_status( gstin=None, response=None, transaction_date=None, callback=None, - use_public_api=False, is_transporter_id=False, + throw=False, ): doctype = "GSTIN" - if is_transporter_id: - response = get_transporter_id_status(gstin) - else: - response = _get_gstin_status( - gstin=gstin, response=response, use_public_api=use_public_api - ) - if not response: - return + if is_transporter_id: + response = fetch_transporter_id_status(gstin, throw=throw) + else: + response = fetch_gstin_status(gstin=gstin, throw=throw) + + if not response: + return if frappe.db.exists(doctype, response.get("gstin")): doc = frappe.get_doc(doctype, response.pop("gstin")) @@ -125,48 +81,60 @@ def create_or_update_gstin_status( return doc -def _get_gstin_status(*, gstin=None, response=None, use_public_api=False): - if response: - return get_formatted_response(response) +### GSTIN Status Validation ### - validate_gstin(gstin) - try: - if frappe.cache.get_value("gst_server_error"): +def get_and_validate_gstin_status(gstin, transaction_date): + """ + Get and validate GSTIN status. + Enqueues fetching GSTIN status if required and hence best suited for Backend use. + """ + if not gstin: + return + + if not is_status_refresh_required(gstin, transaction_date): + if not frappe.db.exists("GSTIN", gstin): return - company_gstin = get_company_gstin() - - if use_public_api or not company_gstin: - response = PublicAPI().get_gstin_info(gstin) - return get_formatted_response(response) - - response = EInvoiceAPI(company_gstin=company_gstin).get_gstin_info(gstin) - return frappe._dict( - { - "gstin": gstin, - "registration_date": parse_datetime(response.DtReg, throw=False), - "cancelled_date": parse_datetime(response.DtDReg, throw=False), - "status": response.Status, - "is_blocked": response.BlkStatus, - } + doc = frappe.get_doc("GSTIN", gstin) + validate_gstin_status(doc, transaction_date, throw=True) + + else: + # Don't delay the response if API is required + frappe.enqueue( + create_or_update_gstin_status, + enqueue_after_commit=True, + queue="short", + gstin=gstin, + transaction_date=transaction_date, + callback=validate_gstin_status, ) - except Exception as e: - if isinstance(e, GSPServerError): - frappe.cache.set_value("gst_server_error", True, expires_in_sec=60) - frappe.log_error( - title=_("Error fetching GSTIN status"), - message=frappe.get_traceback(), - ) - frappe.clear_last_message() +@frappe.whitelist() +def get_gstin_status(gstin, transaction_date=None, force_update=False): + """ + Get GSTIN status. Responds immediately, and best suited for Frontend use. + Permission check not required as GSTIN details are public where GSTIN is known. + """ + if not gstin: + return +<<<<<<< HEAD finally: frappe.cache().set_value(gstin, True, expires_in_sec=180) +======= + if not force_update and not is_status_refresh_required(gstin, transaction_date): + if not frappe.db.exists("GSTIN", gstin): + return + + return frappe.get_doc("GSTIN", gstin) + return create_or_update_gstin_status(gstin, throw=force_update) +>>>>>>> 574b0160 (fix: changes as per reivew) -def _validate_gstin_status(gstin_doc, transaction_date=None, throw=False): + +def validate_gstin_status(gstin_doc, transaction_date=None, throw=False): if not (gstin_doc and transaction_date): return @@ -215,41 +183,6 @@ def _throw(message): ) -def _validate_gst_transporter_id_status(transporter_id_info, *args, **kwargs): - if not transporter_id_info: - return - - throw = kwargs.get("throw", False) - - def _throw(message): - if throw: - frappe.throw(message) - - else: - frappe.log_error( - title=_("Invalid Transporter ID"), - message=message, - ) - - if transporter_id_info.transporter_id_status != "Active": - return _throw( - _( - "Transporter ID {0} is not Active. Please make sure that transporter ID is valid." - ).format(transporter_id_info.gstin) - ) - - -def get_company_gstin(): - gst_settings = frappe.get_cached_doc("GST Settings") - - if not gst_settings.enable_e_invoice: - return - - for row in gst_settings.credentials: - if row.service == "e-Waybill / e-Invoice": - return row.gstin - - def is_status_refresh_required(gstin, transaction_date): settings = frappe.get_cached_doc("GST Settings") @@ -257,7 +190,10 @@ def is_status_refresh_required(gstin, transaction_date): not settings.validate_gstin_status or not is_api_enabled(settings) or settings.sandbox_mode +<<<<<<< HEAD or frappe.cache().get_value(gstin) +======= +>>>>>>> 574b0160 (fix: changes as per reivew) ): return @@ -278,45 +214,7 @@ def is_status_refresh_required(gstin, transaction_date): return days_since_last_update >= settings.gstin_status_refresh_interval -def get_formatted_response(response): - """ - Format response from Public API - """ - return frappe._dict( - { - "gstin": response.gstin, - "registration_date": parse_datetime( - response.rgdt, day_first=True, throw=False - ), - "cancelled_date": parse_datetime( - response.cxdt, day_first=True, throw=False - ), - "status": response.sts, - } - ) - - -def get_transporter_id_status(transporter_id): - if not frappe.get_cached_value("GST Settings", None, "enable_e_waybill"): - return - - company_gstin = get_company_gstin() - if not company_gstin: - return - - response = EWaybillAPI(company_gstin=company_gstin).get_transporter_details( - transporter_id - ) - - if not response: - return - - return frappe._dict( - { - "gstin": transporter_id, - "transporter_id_status": "Active" if response.transin else "Invalid", - } - ) +### GST Transporter ID Validation ### <<<<<<< HEAD @@ -329,7 +227,10 @@ def get_gstr_1_filed_upto(gstin): @frappe.whitelist() def validate_gst_transporter_id(transporter_id): """ - Validates GST Transporter ID and warns user if transporter_id is not Active + Validates GST Transporter ID and warns user if transporter_id is not Active. + Just suggestive and not enforced. + + Only for Frontend use. Args: transporter_id (str): GST Transporter ID @@ -348,14 +249,11 @@ def validate_gst_transporter_id(transporter_id): gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, - callback=_validate_gst_transporter_id_status, ) # Use GSTIN API else: - gstin = create_or_update_gstin_status( - transporter_id, callback=_validate_gstin_status - ) + gstin = create_or_update_gstin_status(transporter_id) if not gstin: return @@ -365,7 +263,6 @@ def validate_gst_transporter_id(transporter_id): gstin = create_or_update_gstin_status( transporter_id, is_transporter_id=True, - callback=_validate_gst_transporter_id_status, ) # Return if GSTIN or transporter_id_status is Active @@ -373,7 +270,7 @@ def validate_gst_transporter_id(transporter_id): return frappe.msgprint( - _("Transporter ID {0} seems to be Inactive").format(transporter_id), + _("GST Transporter ID {0} seems to be Invalid").format(transporter_id), indicator="orange", ) <<<<<<< HEAD diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 853f57d00..1cfb603ad 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -17,6 +17,7 @@ STATE_NUMBERS, ) from india_compliance.gst_india.constants.custom_fields import E_WAYBILL_INV_FIELDS +<<<<<<< HEAD from india_compliance.gst_india.doctype.gst_settings.gst_settings import ( restrict_gstr_1_transaction_for, ) @@ -27,6 +28,9 @@ from india_compliance.gst_india.doctype.gstin.gstin import ( validate_gst_transporter_id as _validate_gst_transporter_id, ) +======= +from india_compliance.gst_india.doctype.gstin.gstin import get_and_validate_gstin_status +>>>>>>> 574b0160 (fix: changes as per reivew) from india_compliance.gst_india.utils import ( get_all_gst_accounts, get_gst_accounts_by_tax_type, @@ -1429,28 +1433,17 @@ def validate_gstin_status(gstin, transaction_date): if not settings.validate_gstin_status: return - gstin_doc = get_gstin_status(gstin, transaction_date) - - if not gstin_doc: - return - - _validate_gstin_status(gstin_doc, transaction_date, throw=True) + get_and_validate_gstin_status(gstin, transaction_date) def validate_gst_transporter_id(doc): if not doc.get("gst_transporter_id"): return - settings = frappe.get_cached_doc("GST Settings") - if not settings.validate_gstin_status: - return - doc.gst_transporter_id = validate_gstin( doc.gst_transporter_id, label="GST Transporter ID", is_transporter_id=True ) - _validate_gst_transporter_id(doc.gst_transporter_id) - def validate_company_address_field(doc): if doc.doctype not in DOCTYPES_WITH_GST_DETAIL: diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index 3f41c8bdd..ad310859b 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -173,7 +173,7 @@ def validate_gstin( # eg: 29AAFCA7488L1Z0 invalid check digit for valid transporter id if not is_transporter_id: - validate_gstin_check_digit(gstin, label=label) + validate_gstin_check_digit(gstin, label) if is_tcs_gstin and not TCS.match(gstin): frappe.throw( diff --git a/india_compliance/gst_india/utils/gstin_info.py b/india_compliance/gst_india/utils/gstin_info.py index a22cf4f61..ad8789b6b 100644 --- a/india_compliance/gst_india/utils/gstin_info.py +++ b/india_compliance/gst_india/utils/gstin_info.py @@ -8,11 +8,17 @@ from india_compliance.exceptions import GSPServerError from india_compliance.gst_india.api_classes.base import BASE_URL +from india_compliance.gst_india.api_classes.e_invoice import EInvoiceAPI +from india_compliance.gst_india.api_classes.e_waybill import EWaybillAPI from india_compliance.gst_india.api_classes.public import PublicAPI +<<<<<<< HEAD from india_compliance.gst_india.doctype.gstr_1_log.gstr_1_log import ( process_gstr_1_returns_info, ) from india_compliance.gst_india.utils import titlecase, validate_gstin +======= +from india_compliance.gst_india.utils import parse_datetime, titlecase, validate_gstin +>>>>>>> 574b0160 (fix: changes as per reivew) GST_CATEGORIES = { "Regular": "Registered Regular", @@ -54,7 +60,7 @@ def _get_gstin_info(gstin, *, throw_error=True): frappe.enqueue( "india_compliance.gst_india.doctype.gstin.gstin.create_or_update_gstin_status", queue="long", - response=response, + response=get_formatted_response_for_status(response), ) except Exception as exc: if isinstance(exc, GSPServerError): @@ -175,6 +181,111 @@ def _extract_address_lines(address): return address_line1, address_line2 +def fetch_gstin_status(*, gstin=None, throw=True): + """ + Fetch GSTIN status from E-Invoice API or Public API + + Uses Public API if credentials are not available or its a user initiated request + + :param gstin: GSTIN to fetch status for + :param throw: Raise exception if error occurs (used for user initiated requests) + """ + validate_gstin(gstin) + + try: + if not throw and frappe.cache.get_value("gst_server_error"): + return + + gst_settings = frappe.get_cached_doc("GST Settings", None) + company_gstin = gst_settings.get_gstin_with_credentials(service="e-Invoice") + + if throw or not company_gstin: + response = PublicAPI().get_gstin_info(gstin) + return get_formatted_response_for_status(response) + + response = EInvoiceAPI(company_gstin=company_gstin).get_gstin_info(gstin) + return frappe._dict( + { + "gstin": gstin, + "registration_date": parse_datetime(response.DtReg, throw=False), + "cancelled_date": parse_datetime(response.DtDReg, throw=False), + "status": response.Status, + "is_blocked": response.BlkStatus, + } + ) + + except Exception as e: + if throw: + raise e + + if isinstance(e, GSPServerError): + frappe.cache.set_value("gst_server_error", True, expires_in_sec=60) + + frappe.log_error( + title=_("Error fetching GSTIN status"), + message=frappe.get_traceback(), + ) + frappe.clear_last_message() + + +def get_formatted_response_for_status(response): + """ + Format response from Public API + """ + return frappe._dict( + { + "gstin": response.gstin, + "registration_date": parse_datetime( + response.rgdt, day_first=True, throw=False + ), + "cancelled_date": parse_datetime( + response.cxdt, day_first=True, throw=False + ), + "status": response.sts, + } + ) + + +def fetch_transporter_id_status(transporter_id, throw=True): + """ + Fetch Transporter ID status from E-Waybill API + + :param transporter_id: GSTIN of the transporter + :param throw: Raise exception if error occurs (used for user initiated requests) + """ + if not frappe.get_cached_value("GST Settings", None, "enable_e_waybill"): + return + + gst_settings = frappe.get_cached_doc("GST Settings", None) + company_gstin = gst_settings.get_gstin_with_credentials(service="e-Waybill") + + if not company_gstin: + return + + try: + response = EWaybillAPI(company_gstin=company_gstin).get_transporter_details( + transporter_id + ) + + except Exception as e: + if throw: + raise e + + frappe.log_error( + title=_("Error fetching Transporter ID status"), + message=frappe.get_traceback(), + ) + frappe.clear_last_message() + return + + return frappe._dict( + { + "gstin": transporter_id, + "transporter_id_status": "Active" if response.transin else "Invalid", + } + ) + + # ####### SAMPLE DATA for GST_CATEGORIES ######## # "Composition" 36AASFP8573D2ZN # "Input Service Distributor (ISD)" 29AABCF8078M2ZW Flipkart diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index 5e74c20cc..8dd170e7f 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -87,18 +87,13 @@ Object.assign(india_compliance, { return in_list(frappe.boot.sales_doctypes, doctype) ? "Customer" : "Supplier"; }, - async set_gstin_status(field, transaction_date, force_update = 0) { + async set_gstin_status(field, transaction_date, force_update) { const gstin = field.value; if (!gstin || gstin.length !== 15) return field.set_description(""); const { message } = await frappe.call({ method: "india_compliance.gst_india.doctype.gstin.gstin.get_gstin_status", - args: { - gstin, - transaction_date, - is_request_from_ui: 1, - force_update, - }, + args: { gstin, transaction_date, force_update }, }); if (!message) return field.set_description(""); @@ -156,7 +151,7 @@ Object.assign(india_compliance, { `).appendTo(field.$wrapper.find(".gstin-last-updated")); refresh_btn.on("click", async function () { - const force_update = 1; + const force_update = true; await india_compliance.set_gstin_status( field, transaction_date, From 9b0e6472d7c7bb293524ed9091b1239a71d04512 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 18 Jun 2024 13:13:12 +0530 Subject: [PATCH 8/9] chore: resolve merge conflicts --- .../gst_india/doctype/gstin/gstin.json | 32 +++++-------------- .../gst_india/doctype/gstin/gstin.py | 18 ++++------- .../gst_india/overrides/transaction.py | 10 ------ .../gst_india/utils/gstin_info.py | 4 --- 4 files changed, 15 insertions(+), 49 deletions(-) diff --git a/india_compliance/gst_india/doctype/gstin/gstin.json b/india_compliance/gst_india/doctype/gstin/gstin.json index 845329c49..a5acf0c6f 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.json +++ b/india_compliance/gst_india/doctype/gstin/gstin.json @@ -13,16 +13,9 @@ "column_break_nrjd", "last_updated_on", "cancelled_date", -<<<<<<< HEAD -<<<<<<< HEAD + "is_blocked", "section_break_ttzc", "gstr_1_filed_upto" -======= - "transporter_id_status" ->>>>>>> a87d2cbc (fix: gst-transporter-id refactor) -======= - "is_blocked" ->>>>>>> 574b0160 (fix: changes as per reivew) ], "fields": [ { @@ -70,7 +63,12 @@ "label": "Is Blocked" }, { -<<<<<<< HEAD + "fieldname": "transporter_id_status", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Transporter ID Status" + }, + { "fieldname": "section_break_ttzc", "fieldtype": "Section Break" }, @@ -79,26 +77,12 @@ "fieldtype": "Date", "hidden": 1, "label": "GSTR-1 Filed Upto" -======= - "fieldname": "transporter_id_status", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Transporter ID Status" ->>>>>>> a87d2cbc (fix: gst-transporter-id refactor) } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], -<<<<<<< HEAD -<<<<<<< HEAD - "modified": "2024-05-17 15:38:05.867522", -======= - "modified": "2024-05-21 12:59:58.322776", ->>>>>>> a87d2cbc (fix: gst-transporter-id refactor) -======= "modified": "2024-06-14 13:59:53.998262", ->>>>>>> 574b0160 (fix: changes as per reivew) "modified_by": "Administrator", "module": "GST India", "name": "GSTIN", @@ -130,7 +114,7 @@ "role": "Accounts User" } ], - "sort_field": "modified", + "sort_field": "creation", "sort_order": "DESC", "states": [], "title_field": "gstin" diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index c79c13f54..54aac8bd1 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -48,6 +48,13 @@ def update_transporter_id_status(self): create_or_update_gstin_status(self.gstin, is_transporter_id=True) +def get_gstr_1_filed_upto(gstin): + if not gstin: + return + + return frappe.db.get_value("GSTIN", gstin, "gstr_1_filed_upto") + + def create_or_update_gstin_status( gstin=None, response=None, @@ -217,13 +224,6 @@ def is_status_refresh_required(gstin, transaction_date): ### GST Transporter ID Validation ### -<<<<<<< HEAD -def get_gstr_1_filed_upto(gstin): - if not gstin: - return - - return frappe.db.get_value("GSTIN", gstin, "gstr_1_filed_upto") -======= @frappe.whitelist() def validate_gst_transporter_id(transporter_id): """ @@ -273,9 +273,6 @@ def validate_gst_transporter_id(transporter_id): _("GST Transporter ID {0} seems to be Invalid").format(transporter_id), indicator="orange", ) -<<<<<<< HEAD ->>>>>>> a87d2cbc (fix: gst-transporter-id refactor) -======= def has_gstin_check_digit_failed(gstin): @@ -286,4 +283,3 @@ def has_gstin_check_digit_failed(gstin): return True return False ->>>>>>> 6f3f5e84 (refactor: naming, usage etc) diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 1cfb603ad..6b79178d9 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -17,20 +17,10 @@ STATE_NUMBERS, ) from india_compliance.gst_india.constants.custom_fields import E_WAYBILL_INV_FIELDS -<<<<<<< HEAD from india_compliance.gst_india.doctype.gst_settings.gst_settings import ( restrict_gstr_1_transaction_for, ) -from india_compliance.gst_india.doctype.gstin.gstin import ( - _validate_gstin_status, - get_gstin_status, -) -from india_compliance.gst_india.doctype.gstin.gstin import ( - validate_gst_transporter_id as _validate_gst_transporter_id, -) -======= from india_compliance.gst_india.doctype.gstin.gstin import get_and_validate_gstin_status ->>>>>>> 574b0160 (fix: changes as per reivew) from india_compliance.gst_india.utils import ( get_all_gst_accounts, get_gst_accounts_by_tax_type, diff --git a/india_compliance/gst_india/utils/gstin_info.py b/india_compliance/gst_india/utils/gstin_info.py index ad8789b6b..72534135d 100644 --- a/india_compliance/gst_india/utils/gstin_info.py +++ b/india_compliance/gst_india/utils/gstin_info.py @@ -11,14 +11,10 @@ from india_compliance.gst_india.api_classes.e_invoice import EInvoiceAPI from india_compliance.gst_india.api_classes.e_waybill import EWaybillAPI from india_compliance.gst_india.api_classes.public import PublicAPI -<<<<<<< HEAD from india_compliance.gst_india.doctype.gstr_1_log.gstr_1_log import ( process_gstr_1_returns_info, ) -from india_compliance.gst_india.utils import titlecase, validate_gstin -======= from india_compliance.gst_india.utils import parse_datetime, titlecase, validate_gstin ->>>>>>> 574b0160 (fix: changes as per reivew) GST_CATEGORIES = { "Regular": "Registered Regular", From daefb6f8fd6f9d89e6432b1c794744c311b55bf9 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 18 Jun 2024 13:22:46 +0530 Subject: [PATCH 9/9] chore: resolve further conflicts --- india_compliance/gst_india/doctype/gstin/gstin.py | 9 --------- india_compliance/gst_india/utils/gstin_info.py | 8 ++++---- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index 54aac8bd1..283ae0ff0 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -127,10 +127,6 @@ def get_gstin_status(gstin, transaction_date=None, force_update=False): if not gstin: return -<<<<<<< HEAD - finally: - frappe.cache().set_value(gstin, True, expires_in_sec=180) -======= if not force_update and not is_status_refresh_required(gstin, transaction_date): if not frappe.db.exists("GSTIN", gstin): return @@ -138,7 +134,6 @@ def get_gstin_status(gstin, transaction_date=None, force_update=False): return frappe.get_doc("GSTIN", gstin) return create_or_update_gstin_status(gstin, throw=force_update) ->>>>>>> 574b0160 (fix: changes as per reivew) def validate_gstin_status(gstin_doc, transaction_date=None, throw=False): @@ -197,10 +192,6 @@ def is_status_refresh_required(gstin, transaction_date): not settings.validate_gstin_status or not is_api_enabled(settings) or settings.sandbox_mode -<<<<<<< HEAD - or frappe.cache().get_value(gstin) -======= ->>>>>>> 574b0160 (fix: changes as per reivew) ): return diff --git a/india_compliance/gst_india/utils/gstin_info.py b/india_compliance/gst_india/utils/gstin_info.py index 72534135d..24611bfe5 100644 --- a/india_compliance/gst_india/utils/gstin_info.py +++ b/india_compliance/gst_india/utils/gstin_info.py @@ -49,7 +49,7 @@ def _get_gstin_info(gstin, *, throw_error=True): if not response: try: - if frappe.cache.get_value("gst_server_error"): + if frappe.cache().get_value("gst_server_error"): return response = PublicAPI().get_gstin_info(gstin) @@ -60,7 +60,7 @@ def _get_gstin_info(gstin, *, throw_error=True): ) except Exception as exc: if isinstance(exc, GSPServerError): - frappe.cache.set_value("gst_server_error", True, expires_in_sec=60) + frappe.cache().set_value("gst_server_error", True, expires_in_sec=60) if throw_error: raise exc @@ -189,7 +189,7 @@ def fetch_gstin_status(*, gstin=None, throw=True): validate_gstin(gstin) try: - if not throw and frappe.cache.get_value("gst_server_error"): + if not throw and frappe.cache().get_value("gst_server_error"): return gst_settings = frappe.get_cached_doc("GST Settings", None) @@ -215,7 +215,7 @@ def fetch_gstin_status(*, gstin=None, throw=True): raise e if isinstance(e, GSPServerError): - frappe.cache.set_value("gst_server_error", True, expires_in_sec=60) + frappe.cache().set_value("gst_server_error", True, expires_in_sec=60) frappe.log_error( title=_("Error fetching GSTIN status"),