From 089944111a09de19bb310f86503467b6cd69c81b Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 16 Dec 2024 19:25:53 +0530 Subject: [PATCH] feat: js api's to regenerate gstr-2b --- .../purchase_reconciliation_tool.py | 23 +++++--- .../gst_india/utils/gstr_2/__init__.py | 40 +++++++++++++ india_compliance/public/js/gstr_2b.js | 57 +++++++++++++++++++ .../js/purchase_reconciliation_tool.bundle.js | 1 + 4 files changed, 112 insertions(+), 9 deletions(-) create mode 100644 india_compliance/public/js/gstr_2b.js diff --git a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.py b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.py index 2d1b510c7e..ec1ffaa4f1 100644 --- a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.py +++ b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.py @@ -478,15 +478,8 @@ def download_gstr( def get_periods_to_download(company_gstin, return_type, periods, download_all=False): - # check if redownload is useful - dont_redownload = get_import_history( - company_gstin, return_type, periods, fields=("return_period", "dont_redownload") - ) - dont_redownload = [ - log.return_period for log in dont_redownload if log.dont_redownload - ] - - periods = [period for period in periods if period not in dont_redownload] + if return_type == ReturnType.GSTR2B: + periods = filter_redownload_periods(company_gstin, return_type, periods) if download_all: return periods @@ -499,6 +492,18 @@ def get_periods_to_download(company_gstin, return_type, periods, download_all=Fa return [period for period in periods if period not in existing_periods] +def filter_redownload_periods(company_gstin, return_type, periods): + # check if redownload is useful. not useful if data is downloaded after 3B is filed + dont_redownload = get_import_history( + company_gstin, return_type, periods, fields=("return_period", "dont_redownload") + ) + dont_redownload = [ + log.return_period for log in dont_redownload if log.dont_redownload + ] + + return [period for period in periods if period not in dont_redownload] + + def get_import_history( company_gstins: list | str, return_type: ReturnType, diff --git a/india_compliance/gst_india/utils/gstr_2/__init__.py b/india_compliance/gst_india/utils/gstr_2/__init__.py index 6efdf64a4b..ea61d7c41e 100644 --- a/india_compliance/gst_india/utils/gstr_2/__init__.py +++ b/india_compliance/gst_india/utils/gstr_2/__init__.py @@ -133,6 +133,7 @@ def download_gstr_2b(gstin, return_periods): is_last_period = return_periods[-1] == return_period requests_made += 1 frappe.publish_realtime( + # TODO: specific event name for purchase reconciliation tool "update_api_progress", { "current_progress": requests_made * 100 / total_expected_requests, @@ -140,6 +141,7 @@ def download_gstr_2b(gstin, return_periods): "is_last_period": is_last_period, }, user=frappe.session.user, + # TODO: doctype not respected doctype="Purchase Reconciliation Tool", ) @@ -169,6 +171,8 @@ def download_gstr_2b(gstin, return_periods): queued_message = True continue + # TODO: if requires regenration, publish realtime and regenerate + if response.error_type: continue @@ -303,6 +307,7 @@ def _download_gstr_2a(gstin, return_period, json_data): def show_queued_message(): + # TODO: publish realtime may be required frappe.msgprint( _( "Some returns are queued for download at GSTN as there may be large data." @@ -328,3 +333,38 @@ def end_transaction_progress(return_period): user=frappe.session.user, doctype="Purchase Reconciliation Tool", ) + + +@frappe.whitelist() +def regenerate_gstr_2b(gstin, return_period): + frappe.has_permission("Purchase Reconciliation Tool", throw=True) + + if not return_period: + # TODO: calculate return period that needs to be regenerated + pass + + try: + api = GSTR2bAPI(gstin) + return api.regenerate(return_period) + + except frappe.ValidationError as e: + frappe.clear_last_message() + frappe.throw( + str(e), title=_("GSTR 2B Regeneration Failed for {0}".format(return_period)) + ) + + +@frappe.whitelist() +def check_regenerate_status(gstin, reference_id): + frappe.has_permission("Purchase Reconciliation Tool", throw=True) + + if not reference_id: + return + + try: + api = GSTR2bAPI(gstin) + return api.generation_status(reference_id) + + except frappe.ValidationError as e: + frappe.clear_last_message() + frappe.throw(str(e), title=_("GSTR 2B Regeneration Failed")) diff --git a/india_compliance/public/js/gstr_2b.js b/india_compliance/public/js/gstr_2b.js new file mode 100644 index 0000000000..19ba9ee8f3 --- /dev/null +++ b/india_compliance/public/js/gstr_2b.js @@ -0,0 +1,57 @@ +const { resolve } = require("chart.js/helpers"); + +frappe.provide("gstr_2b"); + +RETRY_INTERVALS = [2000, 3000, 15000, 30000, 60000, 120000, 300000, 600000, 720000]; // 5 second, 15 second, 30 second, 1 min, 2 min, 5 min, 10 min, 12 min + +Object.assign(gstr_2b, { + regenerate: function (gstin, return_period, callback) { + let message = __("Regenerating GSTR-2B"); + if (return_period) message += __(" for period {0}", [return_period]); + + frappe.show_alert({ message: message, indicator: "blue" }) + + frappe.call({ + method: "india_compliance.gst_india.utils.gstr_2.regenerate_gstr_2b", + args: { gstin, return_period }, + callback: async function (r) { + if (r.exc) return; + + const { reference_id } = r.message; + await gstr_2b.check_regenerate_status(gstin, reference_id); + + callback && callback(gstin, return_period); + }, + }); + }, + + check_regenerate_status: function (gstin, reference_id) { + return new Promise(resolve => { + gstr_2b._check_regenerate_status(gstin, reference_id, resolve); + }); + }, + + _check_regenerate_status: function (gstin, reference_id, callback, retries = 0) { + if (retries >= RETRY_INTERVALS.length) { + frappe.show_alert({ message: __("Failed to regenerate GSTR-2B"), indicator: "red" }); + callback && callback(); + return; + } + + setTimeout(() => { + frappe.call({ + method: "india_compliance.gst_india.utils.gstr_2.check_regenerate_status", + args: { gstin, reference_id }, + callback: function (r) { + if (r.exc) return; + const { status_cd: status, err_msg: error } = r.message; + if (status === "IP") gstr_2b._check_regenerate_status(gstin, reference_id, callback, retries + 1); + if (status === "ER") frappe.throw(error); + + frappe.show_alert({ message: __("GSTR-2B Regenerated"), indicator: "green" }); + callback && callback(); + } + }); + }, RETRY_INTERVALS[retries]); + }, +}); diff --git a/india_compliance/public/js/purchase_reconciliation_tool.bundle.js b/india_compliance/public/js/purchase_reconciliation_tool.bundle.js index e236f74221..054b0463c2 100644 --- a/india_compliance/public/js/purchase_reconciliation_tool.bundle.js +++ b/india_compliance/public/js/purchase_reconciliation_tool.bundle.js @@ -1,3 +1,4 @@ +import "./gstr_2b"; import "./components/data_table_manager"; import "./components/filter_group"; import "./components/number_card";