diff --git a/india_compliance/gst_india/doctype/gstin/gstin.py b/india_compliance/gst_india/doctype/gstin/gstin.py index 86082c300..6a73684db 100644 --- a/india_compliance/gst_india/doctype/gstin/gstin.py +++ b/india_compliance/gst_india/doctype/gstin/gstin.py @@ -2,8 +2,9 @@ # For license information, please see license.txt import frappe +from frappe import _ from frappe.model.document import Document -from frappe.utils import date_diff, get_datetime +from frappe.utils import date_diff, format_date, get_datetime from india_compliance.gst_india.api_classes.e_invoice import EInvoiceAPI from india_compliance.gst_india.api_classes.public import PublicAPI @@ -49,7 +50,7 @@ def update_gstin_status(self): @frappe.whitelist() -def get_gstin_status(gstin): +def get_gstin_status(gstin, transaction_date=None, is_request_from_ui=0): settings = frappe.get_cached_doc("GST Settings") if ( @@ -59,7 +60,16 @@ def get_gstin_status(gstin): ): return - gstin_doc = get_updated_gstin(gstin, settings.gstin_status_refresh_interval) + gstin_doc = get_updated_gstin( + gstin, + settings.gstin_status_refresh_interval, + transaction_date, + is_request_from_ui, + ) + + if not gstin_doc: + return + return frappe._dict( { "status": gstin_doc.get("status"), @@ -69,24 +79,49 @@ def get_gstin_status(gstin): ) -def get_updated_gstin(gstin, refresh_interval): +def get_updated_gstin( + gstin, refresh_interval, transaction_date=None, is_request_from_ui=0 +): if not frappe.db.exists("GSTIN", gstin): return create_or_update_gstin_status(gstin) gstin_doc = frappe.get_doc("GSTIN", gstin) + # If request is from ui, api call is not made + if is_request_from_ui: + return gstin_doc + + # If request is not made from ui, enqueing the GST api call + # Validating the gstin in the callback and log errors if needs_status_update(refresh_interval, gstin_doc): - return create_or_update_gstin_status(gstin, doc=gstin_doc) + return ( + create_or_update_gstin_status(gstin, doc=gstin_doc) + if is_request_from_ui + else frappe.enqueue( + create_or_update_gstin_status, + enqueue_after_commit=True, + queue="short", + gstin=gstin, + doc=gstin_doc, + transaction_date=transaction_date, + callback=_validate_gstin_callback, + ) + ) return gstin_doc -def create_or_update_gstin_status(gstin=None, response=None, doc=None): +def create_or_update_gstin_status( + gstin=None, response=None, doc=None, transaction_date=None, callback=None +): if not response: response = _get_gstin_status(gstin=gstin, company_gstin=get_company_gstin()) else: response = get_formatted_response(response) + if not response: + return + if not doc: gstin_exists = frappe.db.exists("GSTIN", response.get("gstin")) if gstin_exists: @@ -97,24 +132,67 @@ def create_or_update_gstin_status(gstin=None, response=None, doc=None): doc.update(response) doc.save(ignore_permissions=True) + if callback: + callback(doc, transaction_date) + return doc -def _get_gstin_status(*, gstin, company_gstin=None): - if not company_gstin: - response = PublicAPI().get_gstin_info(gstin) - return get_formatted_response(response) +def _validate_gstin_callback(gstin_doc, transaction_date=None): + if not gstin_doc or not transaction_date: + return - response = EInvoiceAPI(company_gstin=company_gstin).get_gstin_info(gstin) - return frappe._dict( - { - "gstin": gstin, - "registration_date": response.DtReg, - "cancelled_date": response.DtDReg, - "status": response.Status, - "is_blocked": response.BlkStatus, - } - ) + if ( + not gstin_doc.registration_date + or date_diff(transaction_date, gstin_doc.registration_date) < 0 + ): + frappe.log_error( + title=_("Invalid Party GSTIN"), + message=_( + "Party GSTIN is Registered on {0}. Please make sure that document date is on or after {0}" + ).format(format_date(gstin_doc.registration_date)), + ) + + if ( + gstin_doc.status == "Cancelled" + and date_diff(transaction_date, gstin_doc.cancelled_date) >= 0 + ): + frappe.log_error( + title=_("Invalid Party GSTIN"), + message=_( + "Party GSTIN is Cancelled on {0}. Please make sure that document date is before {0}" + ).format(format_date(gstin_doc.cancelled_date)), + ) + + if gstin_doc.status not in ("Active", "Cancelled"): + frappe.log_error( + title=_("Invalid Party GSTIN Status"), + message=_("Status of Party GSTIN is {0}").format(gstin_doc.status), + ) + + +def _get_gstin_status(*, gstin, company_gstin=None): + try: + if 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": response.DtReg, + "cancelled_date": response.DtDReg, + "status": response.Status, + "is_blocked": response.BlkStatus, + } + ) + + except Exception: + frappe.log_error( + title=_("Error fetching GSTIN status"), + message=frappe.get_traceback(), + ) def get_formatted_response(response): diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 272f4ca3a..44f48cf40 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -805,7 +805,7 @@ def validate_transaction(doc, method=None): def validate_gstin(gstin, transaction_date, party): - gstin_doc = get_gstin_status(gstin) + gstin_doc = get_gstin_status(gstin, transaction_date) if not gstin_doc: return diff --git a/india_compliance/public/js/transaction.js b/india_compliance/public/js/transaction.js index ed7c6f7a8..70dc7cea0 100644 --- a/india_compliance/public/js/transaction.js +++ b/india_compliance/public/js/transaction.js @@ -163,6 +163,10 @@ function is_foreign_transaction(frm) { function set_and_validate_gstin_status(doctype, gstin_field_name) { frappe.ui.form.on(doctype, { + refresh(frm) { + _set_and_validate_gstin_status(frm, gstin_field_name); + }, + [gstin_field_name](frm) { _set_and_validate_gstin_status(frm, gstin_field_name); }, diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index de9100ccd..bf4f30406 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -46,6 +46,7 @@ Object.assign(india_compliance, { method: "india_compliance.gst_india.doctype.gstin.gstin.get_gstin_status", args: { gstin: field.value, + is_request_from_ui: 1, }}); field.set_description(