From 67374bf460ea3147f986bf8db7353d954d5beea0 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Wed, 4 Sep 2024 12:07:55 +0530 Subject: [PATCH 01/29] fix: use `get_gstin_list` function for fetching list of gstins in GSTR1 Report (cherry picked from commit 5e657ec68f0b3ba3a2d2ccaabb78ef6648bc3384) --- .../gst_india/report/gstr_1/gstr_1.py | 20 ++++++------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/india_compliance/gst_india/report/gstr_1/gstr_1.py b/india_compliance/gst_india/report/gstr_1/gstr_1.py index da201d036..7b51e3837 100644 --- a/india_compliance/gst_india/report/gstr_1/gstr_1.py +++ b/india_compliance/gst_india/report/gstr_1/gstr_1.py @@ -22,7 +22,11 @@ get_hsn_data, get_hsn_wise_json_data, ) -from india_compliance.gst_india.utils import get_escaped_name, get_gst_accounts_by_type +from india_compliance.gst_india.utils import ( + get_escaped_name, + get_gst_accounts_by_type, + get_gstin_list, +) from india_compliance.gst_india.utils.exporter import ExcelExporter from india_compliance.gst_india.utils.gstr_1 import SUPECOM @@ -2204,19 +2208,7 @@ def get_company_gstin_number(company, address=None, all_gstins=False): gstin = frappe.db.get_value("Address", address, "gstin") if not gstin: - filters = [ - ["is_your_company_address", "=", 1], - ["Dynamic Link", "link_doctype", "=", "Company"], - ["Dynamic Link", "link_name", "=", company], - ["Dynamic Link", "parenttype", "=", "Address"], - ["gstin", "!=", ""], - ] - gstin = frappe.get_all( - "Address", - filters=filters, - pluck="gstin", - order_by="is_primary_address desc", - ) + gstin = get_gstin_list(company) if gstin and not all_gstins: gstin = gstin[0] From d495ac415a3c93ab84a731441ecbe158d472f099 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 6 Sep 2024 13:43:36 +0530 Subject: [PATCH 02/29] fix: fiscal year start-end date in GST Balance (cherry picked from commit 1d4183140b97b1171189ca281ceb7f530a1ffca4) --- india_compliance/gst_india/report/gst_balance/gst_balance.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/india_compliance/gst_india/report/gst_balance/gst_balance.js b/india_compliance/gst_india/report/gst_balance/gst_balance.js index 60a157e1f..0093c5e98 100644 --- a/india_compliance/gst_india/report/gst_balance/gst_balance.js +++ b/india_compliance/gst_india/report/gst_balance/gst_balance.js @@ -29,13 +29,13 @@ frappe.query_reports["GST Balance"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_start_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.defaults.get_user_default("year_end_date"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], }, { fieldname: "show_summary", From cd9ae199bb64b3fd93a9db860f2614482dd332e7 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 9 Sep 2024 16:52:24 +0530 Subject: [PATCH 03/29] fix: better default date for gst balance report (cherry picked from commit 65e48f2f22013a87e69d02197f4cf78d7c0aa2f7) --- india_compliance/gst_india/report/gst_balance/gst_balance.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/india_compliance/gst_india/report/gst_balance/gst_balance.js b/india_compliance/gst_india/report/gst_balance/gst_balance.js index 0093c5e98..5229e9365 100644 --- a/india_compliance/gst_india/report/gst_balance/gst_balance.js +++ b/india_compliance/gst_india/report/gst_balance/gst_balance.js @@ -29,13 +29,13 @@ frappe.query_reports["GST Balance"] = { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[1], + default: india_compliance.last_month_start(), }, { fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today(), true)[2], + default: india_compliance.last_month_end(), }, { fieldname: "show_summary", From fa7645274118a8c01fd648c34e4f03ff73492259 Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Tue, 10 Sep 2024 15:31:19 +0530 Subject: [PATCH 04/29] fix: GSTR3B Enhancements for better ux (#2590) * fix: use company_gstin instead of company_address * fix: returns can be quarterly or monthly * fix: show gstr3b form in doctype only * fix: implement functionality considering its references * fix: update test case * fix: update options * chore: consistent formatting with `spaces` * fix: fields repositioned for gstr-3b report * fix: clear extra column at start * fix: autoupdate gstin * chore: formatting * fix: set query on setup * fix: patch existing data with company_gstin and month_or_quarter * fix: moving function to utilities * fix: month or quarter for gstr3b_details * fix: updated patches * fix: migrate 3b fields in post install --------- Co-authored-by: Smit Vora (cherry picked from commit bd4eba3b6d69a32852ca1c75fbc044d7efe60aba) # Conflicts: # india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json --- .../doctype/gstr_3b_report/gstr_3b_report.js | 128 ++++++++++-------- .../gstr_3b_report/gstr_3b_report.json | 61 ++++++--- .../doctype/gstr_3b_report/gstr_3b_report.py | 121 ++++++++++------- .../gstr_3b_report/test_gstr_3b_report.py | 4 +- .../report/gstr_3b_details/gstr_3b_details.js | 34 +++-- .../report/gstr_3b_details/gstr_3b_details.py | 27 +++- india_compliance/gst_india/utils/__init__.py | 33 +++++ india_compliance/install.py | 1 + india_compliance/patches.txt | 1 + .../post_install/migrate_fields_for_gstr3b.py | 38 ++++++ 10 files changed, 297 insertions(+), 151 deletions(-) create mode 100644 india_compliance/patches/post_install/migrate_fields_for_gstr3b.py diff --git a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.js b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.js index cfca9b0c5..9a40f09b7 100644 --- a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.js +++ b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.js @@ -1,68 +1,84 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('GSTR 3B Report', { - refresh : function(frm) { - frm.doc.__unsaved = 1; - if(!frm.is_new()) { - frm.set_intro(__("Please save the report again to rebuild or update")); - frm.add_custom_button(__('Download JSON'), function() { - var w = window.open( - frappe.urllib.get_full_url( - "/api/method/india_compliance.gst_india.doctype.gstr_3b_report.gstr_3b_report.make_json?" - +"name="+encodeURIComponent(frm.doc.name))); +frappe.ui.form.on("GSTR 3B Report", { + refresh: function (frm) { + frm.doc.__unsaved = 1; + if (!frm.is_new()) { + frm.set_intro(__("Please save the report again to rebuild or update")); + frm.add_custom_button(__("Download JSON"), function () { + var w = window.open( + frappe.urllib.get_full_url( + "/api/method/india_compliance.gst_india.doctype.gstr_3b_report.gstr_3b_report.make_json?" + + "name=" + + encodeURIComponent(frm.doc.name) + ) + ); - if(!w) { - frappe.msgprint(__("Please enable pop-ups")); return; - } - }); - frm.add_custom_button(__('View Form'), function() { - frappe.call({ - "method" : "india_compliance.gst_india.doctype.gstr_3b_report.gstr_3b_report.view_report", - "args" : { - name : frm.doc.name, - }, - "callback" : function(r){ + if (!w) { + frappe.msgprint(__("Please enable pop-ups")); + return; + } + }); + frm.add_custom_button(__("View Form"), function () { + frappe.call({ + method: "india_compliance.gst_india.doctype.gstr_3b_report.gstr_3b_report.view_report", + args: { + name: frm.doc.name, + }, + callback: function (r) { + let data = r.message; - let data = r.message; + frappe.ui.get_print_settings(false, print_settings => { + frappe.render_grid({ + template: "gstr_3b_report", + title: __(this.doctype), + print_settings: print_settings, + data: data, + columns: [], + }); + }); + }, + }); + }); + } - frappe.ui.get_print_settings(false, print_settings => { + let current_year = new Date().getFullYear(); + let options = [current_year, current_year - 1, current_year - 2]; + frm.set_df_property("year", "options", options); - frappe.render_grid({ - template: 'gstr_3b_report', - title: __(this.doctype), - print_settings: print_settings, - data: data, - columns:[] - }); - }); - } - }); - }); - } + frappe.realtime.on("gstr3b_report_generation", function () { + frm.reload_doc(); + }); - let current_year = new Date().getFullYear(); - let options = [current_year, current_year-1, current_year-2]; - frm.set_df_property('year', 'options', options); + append_form(frm); + }, - frappe.realtime.on("gstr3b_report_generation", function(){ - frm.reload_doc(); - }); - }, + setup: async function (frm) { + await frappe.require( + "assets/india_compliance/js/components/set_gstin_options.js" + ); - setup: function(frm) { - frm.set_query('company_address', function(doc) { - if(!doc.company) { - frappe.throw(__('Please set Company')); - } + if (!frm.doc.company) return; + await india_compliance.set_gstin_options(frm); + }, - return { - query: 'frappe.contacts.doctype.address.address.address_query', - filters: { - link_doctype: 'Company', - link_name: doc.company - } - }; - }); - }, + company: async function (frm) { + if (!frm.doc.company) { + frm.set_value("company_gstin", ""); + return; + } + + const options = await india_compliance.set_gstin_options(frm); + frm.set_value("company_gstin", options[0]); + }, }); + +function append_form(frm) { + $(frm.fields_dict.gstr3b_form.wrapper).empty(); + $( + frappe.render_template("gstr_3b_report", { + data: JSON.parse(frm.doc.json_output), + }) + ).appendTo(frm.fields_dict.gstr3b_form.wrapper); +} diff --git a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json index 4d64c8e42..c4ebe6ca5 100644 --- a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json +++ b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json @@ -7,13 +7,16 @@ "engine": "InnoDB", "field_order": [ "company", - "company_address", - "year", - "month", - "column_break_jhew", "enqueue_report", + "column_break_pvct", + "company_gstin", "generation_status", + "column_break_ddqq", + "year", + "column_break_vtyi", + "month_or_quarter", "section_break_yqh7", + "gstr3b_form", "json_output", "missing_field_invoices" ], @@ -24,26 +27,15 @@ "label": "Company", "options": "Company" }, - { - "fieldname": "company_address", - "fieldtype": "Link", - "label": "Company Address", - "options": "Address" - }, { "fieldname": "year", "fieldtype": "Select", "label": "Year" }, - { - "fieldname": "month", - "fieldtype": "Select", - "label": "Month", - "options": "January\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" - }, { "fieldname": "json_output", "fieldtype": "Code", + "hidden": 1, "label": "JSON Output" }, { @@ -52,10 +44,6 @@ "label": "Invoices with no Place Of Supply", "read_only": 1 }, - { - "fieldname": "column_break_jhew", - "fieldtype": "Column Break" - }, { "default": "0", "description": "Generate report in the background.", @@ -73,12 +61,45 @@ { "fieldname": "generation_status", "fieldtype": "Data", + "hidden": 1, "label": "Status", "read_only": 1 + }, + { + "fieldname": "company_gstin", + "fieldtype": "Autocomplete", + "label": "Company GSTIN" + }, + { + "fieldname": "column_break_ddqq", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_vtyi", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_pvct", + "fieldtype": "Column Break" + }, + { + "fieldname": "gstr3b_form", + "fieldtype": "HTML", + "label": "GSTR-3B Form" + }, + { + "fieldname": "month_or_quarter", + "fieldtype": "Select", + "label": "Month or Quarter", + "options": "Apr - Jun\nJul - Sep\nOct - Dec\nJan - Mar\nJanuary\nFebruary\nMarch\nApril\nMay\nJune\nJuly\nAugust\nSeptember\nOctober\nNovember\nDecember" } ], "links": [], +<<<<<<< HEAD "modified": "2024-01-02 14:09:31.230340", +======= + "modified": "2024-09-09 08:37:11.799168", +>>>>>>> bd4eba3b (fix: GSTR3B Enhancements for better ux (#2590)) "modified_by": "Administrator", "module": "GST India", "name": "GSTR 3B Report", diff --git a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.py b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.py index 3a359f3a2..49c42b1f3 100644 --- a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.py +++ b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.py @@ -12,11 +12,12 @@ from frappe.query_builder.functions import Extract, IfNull, Sum from frappe.utils import cstr, flt, get_date_str, get_first_day, get_last_day -from india_compliance.gst_india.constants import INVOICE_DOCTYPES +from india_compliance.gst_india.constants import INVOICE_DOCTYPES, STATE_NUMBERS from india_compliance.gst_india.overrides.transaction import is_inter_state_supply from india_compliance.gst_india.report.gstr_3b_details.gstr_3b_details import ( IneligibleITC, ) +from india_compliance.gst_india.utils import get_period VALUES_TO_UPDATE = ["iamt", "camt", "samt", "csamt"] @@ -40,8 +41,10 @@ def get_data(self): self.gst_details = self.get_company_gst_details() self.report_dict["gstin"] = self.gst_details.get("gstin") - self.report_dict["ret_period"] = get_period(self.month, self.year) - self.month_no = get_period(self.month) + self.report_dict["ret_period"] = get_period( + self.month_or_quarter, self.year + ) + self.month_or_quarter_no = get_period(self.month_or_quarter) self.get_outward_supply_details("Sales Invoice") self.set_outward_taxable_supplies() @@ -126,7 +129,10 @@ def update_itc_reversal_from_purchase_invoice(self): def update_itc_reversal_for_purchase_due_to_pos(self): ineligible_credit = IneligibleITC( - self.company, self.gst_details.get("gstin"), self.month_no, self.year + self.company, + self.gst_details.get("gstin"), + self.month_or_quarter_no, + self.year, ).get_for_purchase( "ITC restricted due to PoS rules", group_by="ineligibility_reason" ) @@ -135,7 +141,10 @@ def update_itc_reversal_for_purchase_due_to_pos(self): def update_itc_reversal_for_purchase_us_17_4(self): ineligible_credit = IneligibleITC( - self.company, self.gst_details.get("gstin"), self.month_no, self.year + self.company, + self.gst_details.get("gstin"), + self.month_or_quarter_no, + self.year, ).get_for_purchase( "Ineligible As Per Section 17(5)", group_by="ineligibility_reason" ) @@ -144,7 +153,10 @@ def update_itc_reversal_for_purchase_us_17_4(self): def update_itc_reversal_from_bill_of_entry(self): ineligible_credit = IneligibleITC( - self.company, self.gst_details.get("gstin"), self.month_no, self.year + self.company, + self.gst_details.get("gstin"), + self.month_or_quarter_no, + self.year, ).get_for_bill_of_entry() self.process_ineligible_credit(ineligible_credit) @@ -226,11 +238,17 @@ def get_itc_details(self): WHERE docstatus = 1 and is_opening = 'No' and company_gstin != IFNULL(supplier_gstin, "") - and month(posting_date) = %s and year(posting_date) = %s and company = %s + and month(posting_date) between %s and %s and year(posting_date) = %s and company = %s and company_gstin = %s GROUP BY itc_classification """, - (self.month_no, self.year, self.company, self.gst_details.get("gstin")), + ( + self.month_or_quarter_no[0], + self.month_or_quarter_no[1], + self.year, + self.company, + self.gst_details.get("gstin"), + ), as_dict=1, ) @@ -262,8 +280,16 @@ def _get_tax_amount(account_type): .on(boe_taxes.parent == boe.name) .where( boe.posting_date.between( - get_date_str(get_first_day(f"{self.year}-{self.month_no}-01")), - get_date_str(get_last_day(f"{self.year}-{self.month_no}-01")), + get_date_str( + get_first_day( + f"{self.year}-{self.month_or_quarter_no[0]}-01" + ) + ), + get_date_str( + get_last_day( + f"{self.year}-{self.month_or_quarter_no[1]}-01" + ) + ), ) & boe.company_gstin.eq(self.gst_details.get("gstin")) & boe.docstatus.eq(1) @@ -287,10 +313,16 @@ def get_inward_nil_exempt(self, state): and p.is_opening = 'No' and p.company_gstin != IFNULL(p.supplier_gstin, "") and (i.gst_treatment != 'Taxable' or p.gst_category = 'Registered Composition') and - month(p.posting_date) = %s and year(p.posting_date) = %s + month(p.posting_date) between %s and %s and year(p.posting_date) = %s and p.company = %s and p.company_gstin = %s """, - (self.month_no, self.year, self.company, self.gst_details.get("gstin")), + ( + self.month_or_quarter_no[0], + self.month_or_quarter_no[1], + self.year, + self.company, + self.gst_details.get("gstin"), + ), as_dict=1, ) @@ -472,7 +504,11 @@ def get_outward_tax_invoices(self, doctype, reverse_charge=None): def get_query_with_conditions(self, invoice, query, party_gstin): return ( query.where(invoice.docstatus == 1) - .where(Extract(DatePart.month, invoice.posting_date).eq(self.month_no)) + .where( + Extract(DatePart.month, invoice.posting_date).between( + self.month_or_quarter_no[0], self.month_or_quarter_no[1] + ) + ) .where(Extract(DatePart.year, invoice.posting_date).eq(self.year)) .where(invoice.company == self.company) .where(invoice.company_gstin == self.gst_details.get("gstin")) @@ -623,20 +659,20 @@ def set_inter_state_supply(self, inter_state_supply): self.report_dict["inter_sup"][section].append(value) def get_company_gst_details(self): - gst_details = frappe.get_all( - "Address", - fields=["gstin", "gst_state", "gst_state_number"], - filters={"name": self.company_address}, - ) - - if gst_details: - return gst_details[0] - else: - frappe.throw( - _("Please enter GSTIN and state for the Company Address {0}").format( - self.company_address - ) - ) + if not self.company_gstin: + frappe.throw(_("Please enter GSTIN for Company {0}").format(self.company)) + + return { + "gstin": self.company_gstin, + "gst_state": next( + ( + key + for key, value in STATE_NUMBERS.items() + if value == self.company_gstin[:2] + ), + None, + ), + } def get_missing_field_invoices(self): missing_field_invoices = [] @@ -651,12 +687,17 @@ def get_missing_field_invoices(self): f""" SELECT name FROM `tab{doctype}` WHERE docstatus = 1 and is_opening = 'No' - and month(posting_date) = %s and year(posting_date) = %s + and month(posting_date) between %s and %s and year(posting_date) = %s and company = %s and place_of_supply IS NULL and company_gstin != IFNULL({party_gstin},"") and gst_category != 'Overseas' """, - (self.month_no, self.year, self.company), + ( + self.month_or_quarter_no[0], + self.month_or_quarter_no[1], + self.year, + self.company, + ), as_dict=1, ) # nosec @@ -680,28 +721,6 @@ def get_json(template): return cstr(f.read()) -def get_period(month, year=None): - month_no = { - "January": 1, - "February": 2, - "March": 3, - "April": 4, - "May": 5, - "June": 6, - "July": 7, - "August": 8, - "September": 9, - "October": 10, - "November": 11, - "December": 12, - }.get(month) - - if year: - return str(month_no).zfill(2) + str(year) - else: - return month_no - - def format_values(data, precision=2): if isinstance(data, dict): for key, value in data.items(): diff --git a/india_compliance/gst_india/doctype/gstr_3b_report/test_gstr_3b_report.py b/india_compliance/gst_india/doctype/gstr_3b_report/test_gstr_3b_report.py index f4712af3d..25e93bfe5 100644 --- a/india_compliance/gst_india/doctype/gstr_3b_report/test_gstr_3b_report.py +++ b/india_compliance/gst_india/doctype/gstr_3b_report/test_gstr_3b_report.py @@ -57,9 +57,9 @@ def test_gstr_3b_report(self): { "doctype": "GSTR 3B Report", "company": "_Test Indian Registered Company", - "company_address": "_Test Indian Registered Company-Billing", + "company_gstin": "24AAQCA8719H1ZC", "year": today.year, - "month": month_number_mapping.get(today.month), + "month_or_quarter": month_number_mapping.get(today.month), } ).insert() diff --git a/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.js b/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.js index abc4a1fbf..777d6aa08 100644 --- a/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.js +++ b/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.js @@ -30,24 +30,28 @@ frappe.query_reports["GSTR-3B Details"] = { "options": get_year_list(), }, { - "fieldname": "month", - "label": __("Month"), + "fieldname": "month_or_quarter", + "label": __("Month or Quarter"), "fieldtype": "Select", "reqd": 1, "options": [ - { "value": "1", "label": __("January") }, - { "value": "2", "label": __("February") }, - { "value": "3", "label": __("March") }, - { "value": "4", "label": __("April") }, - { "value": "5", "label": __("May") }, - { "value": "6", "label": __("June") }, - { "value": "7", "label": __("July") }, - { "value": "8", "label": __("August") }, - { "value": "9", "label": __("September") }, - { "value": "10", "label": __("October") }, - { "value": "11", "label": __("November") }, - { "value": "12", "label": __("December") } - ], + "Apr - Jun", + "Jul - Sep", + "Oct - Dec", + "Jan - Mar", + "January", + "February", + "March", + "April", + "May", + "June", + "July", + "August", + "September", + "October", + "November", + "December" + ], }, { "fieldname": "section", diff --git a/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.py b/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.py index af254461b..5847ecb1f 100644 --- a/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.py +++ b/india_compliance/gst_india/report/gstr_3b_details/gstr_3b_details.py @@ -8,6 +8,8 @@ from frappe.query_builder.functions import Extract, Ifnull, IfNull, LiteralValue, Sum from frappe.utils import cint, get_first_day, get_last_day +from india_compliance.gst_india.utils import get_period + def execute(filters=None): if not filters.get("section"): @@ -52,11 +54,12 @@ def __init__(self, filters=None): }, ] self.data = [] + self.month_or_quarter_no = get_period(self.filters.month_or_quarter) self.from_date = get_first_day( - f"{cint(self.filters.year)}-{cint(self.filters.month)}-01" + f"{cint(self.filters.year)}-{self.month_or_quarter_no[0]}-01" ) self.to_date = get_last_day( - f"{cint(self.filters.year)}-{cint(self.filters.month)}-01" + f"{cint(self.filters.year)}-{self.month_or_quarter_no[1]}-01" ) self.company = self.filters.company self.company_gstin = self.filters.company_gstin @@ -272,14 +275,20 @@ def get_itc_from_journal_entry(self): def get_ineligible_itc_from_purchase(self): ineligible_itc = IneligibleITC( - self.company, self.company_gstin, self.filters.month, self.filters.year + self.company, + self.company_gstin, + self.month_or_quarter_no, + self.filters.year, ).get_for_purchase("Ineligible As Per Section 17(5)") return self.process_ineligible_itc(ineligible_itc) def get_ineligible_itc_from_boe(self): ineligible_itc = IneligibleITC( - self.company, self.company_gstin, self.filters.month, self.filters.year + self.company, + self.company_gstin, + self.month_or_quarter_no, + self.filters.year, ).get_for_bill_of_entry() return self.process_ineligible_itc(ineligible_itc) @@ -420,10 +429,10 @@ def get_inward_nil_exempt(self): class IneligibleITC: - def __init__(self, company, gstin, month, year) -> None: + def __init__(self, company, gstin, month_or_quarter, year) -> None: self.company = company self.gstin = gstin - self.month = month + self.month_or_quarter = month_or_quarter self.year = year def get_for_purchase(self, ineligibility_reason, group_by="name"): @@ -476,6 +485,10 @@ def get_common_query(self, doctype, dt, dt_item): .where(dt.docstatus == 1) .where(dt.company_gstin == self.gstin) .where(dt.company == self.company) - .where(Extract(DatePart.month, dt.posting_date).eq(self.month)) + .where( + Extract(DatePart.month, dt.posting_date).between( + self.month_or_quarter[0], self.month_or_quarter[1] + ) + ) .where(Extract(DatePart.year, dt.posting_date).eq(self.year)) ) diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index e3b219cf1..554485ff1 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -962,3 +962,36 @@ def handle_server_errors(settings, doc, document_type, error): title=error_message_title.get(type(error)), indicator="yellow", ) + + +def get_month_or_quarter_dict(): + return { + "Jan - Mar": (1, 3), + "Apr - Jun": (4, 6), + "Jul - Sep": (7, 9), + "Oct - Dec": (10, 12), + "January": 1, + "February": 2, + "March": 3, + "April": 4, + "May": 5, + "June": 6, + "July": 7, + "August": 8, + "September": 9, + "October": 10, + "November": 11, + "December": 12, + } + + +def get_period(month_or_quarter, year=None): + month_or_quarter_no = get_month_or_quarter_dict().get(month_or_quarter) + + if isinstance(month_or_quarter_no, int): + month_or_quarter_no = (month_or_quarter_no, month_or_quarter_no) + + if year: + return str(month_or_quarter_no[1]).zfill(2) + str(year) + + return month_or_quarter_no diff --git a/india_compliance/install.py b/india_compliance/install.py index ef954b442..baf8f61ba 100644 --- a/india_compliance/install.py +++ b/india_compliance/install.py @@ -45,6 +45,7 @@ "update_reconciliation_status", "update_vehicle_no_field_in_purchase_receipt", "update_gst_treatment_for_taxable_nil_transaction_item", # it should be always after improve item tax template + "migrate_fields_for_gstr3b", ) diff --git a/india_compliance/patches.txt b/india_compliance/patches.txt index c8b0423df..e95b3d503 100644 --- a/india_compliance/patches.txt +++ b/india_compliance/patches.txt @@ -12,6 +12,7 @@ india_compliance.patches.post_install.set_gst_tax_type india_compliance.patches.post_install.set_gst_tax_type_in_journal_entry india_compliance.patches.post_install.update_company_gstin india_compliance.patches.post_install.update_custom_role_for_e_invoice_summary +india_compliance.patches.post_install.migrate_fields_for_gstr3b india_compliance.patches.v14.remove_ecommerce_gstin_from_purchase_invoice india_compliance.patches.v14.set_sandbox_mode_in_gst_settings execute:from india_compliance.gst_india.setup import add_fields_to_item_variant_settings; add_fields_to_item_variant_settings() diff --git a/india_compliance/patches/post_install/migrate_fields_for_gstr3b.py b/india_compliance/patches/post_install/migrate_fields_for_gstr3b.py new file mode 100644 index 000000000..ce9709d26 --- /dev/null +++ b/india_compliance/patches/post_install/migrate_fields_for_gstr3b.py @@ -0,0 +1,38 @@ +import frappe + + +def execute(): + if not frappe.db.has_column("GSTR 3B Report", "month"): + return + + # Update month_or_quarter from month field + gstr3b_report = frappe.qb.DocType("GSTR 3B Report") + + ( + frappe.qb.update(gstr3b_report) + .set(gstr3b_report.month_or_quarter, gstr3b_report.month) + .run() + ) + + # Update company_gstin based on company_address + addresses = frappe.get_all("GSTR 3B Report", pluck="company_address", distinct=True) + + for address in addresses: + frappe.db.set_value( + "GSTR 3B Report", + {"company_address": address}, + "company_gstin", + get_gstin_based_on_address(address), + ) + + +def get_gstin_based_on_address(address): + """Get GSTIN based on company_address or company""" + return ( + frappe.db.get_value( + "Address", + address, + "gstin", + ) + or "" + ) From 7b20ddacda530168aeca6c4af6d648434f73bb96 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 10 Sep 2024 17:02:08 +0530 Subject: [PATCH 05/29] fix: resolve conflicts --- .../gst_india/doctype/gstr_3b_report/gstr_3b_report.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json index c4ebe6ca5..14c482b87 100644 --- a/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json +++ b/india_compliance/gst_india/doctype/gstr_3b_report/gstr_3b_report.json @@ -95,11 +95,7 @@ } ], "links": [], -<<<<<<< HEAD - "modified": "2024-01-02 14:09:31.230340", -======= "modified": "2024-09-09 08:37:11.799168", ->>>>>>> bd4eba3b (fix: GSTR3B Enhancements for better ux (#2590)) "modified_by": "Administrator", "module": "GST India", "name": "GSTR 3B Report", From db5ca430d4d7710fe89584a20512f013e0eb05c5 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 10 Sep 2024 17:02:05 +0530 Subject: [PATCH 06/29] fix: changed `doctype` column in audit trail report from Link to Data (cherry picked from commit 76d47659e17754662b76912bbf22333697743d30) --- .../audit_trail/report/audit_trail/audit_trail.py | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/india_compliance/audit_trail/report/audit_trail/audit_trail.py b/india_compliance/audit_trail/report/audit_trail/audit_trail.py index aaf0c943e..1d7f0d56f 100644 --- a/india_compliance/audit_trail/report/audit_trail/audit_trail.py +++ b/india_compliance/audit_trail/report/audit_trail/audit_trail.py @@ -152,9 +152,8 @@ def get_columns(self): }, { "label": _("DocType"), - "fieldtype": "Link", + "fieldtype": "Data", "fieldname": "doctype", - "options": "DocType", "width": 120, }, { @@ -172,10 +171,9 @@ def get_columns(self): }, { "label": _("Party Type"), - "fieldtype": "Link", + "fieldtype": "Data", "fieldname": "party_type", "width": 100, - "options": "DocType", }, { "label": _("Party Name"), @@ -302,9 +300,8 @@ def get_columns(self): columns = [ { "label": _("DocType"), - "fieldtype": "Link", + "fieldtype": "Data", "fieldname": "doctype", - "options": "DocType", "width": 150, }, { From fcb8ae5a42211290143bd7d8b4198e7c91f4edcb Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 27 Aug 2024 17:36:30 +0530 Subject: [PATCH 07/29] fix: enhanced otp handeling taxpayer apis (cherry picked from commit 7afa94c76e50ad25582f3c0ea6b445977cc5c012) # Conflicts: # india_compliance/__init__.py --- india_compliance/__init__.py | 6 + india_compliance/exceptions.py | 12 ++ .../gst_india/api_classes/taxpayer_base.py | 34 ++++- .../gst_india/utils/gstr_utils.py | 9 +- india_compliance/public/js/gst_api_handler.js | 129 ++++++++++++++++++ .../public/js/india_compliance.bundle.js | 1 + india_compliance/public/js/utils.js | 93 ------------- 7 files changed, 184 insertions(+), 100 deletions(-) create mode 100644 india_compliance/public/js/gst_api_handler.js diff --git a/india_compliance/__init__.py b/india_compliance/__init__.py index b93534d70..7a9e6fa57 100644 --- a/india_compliance/__init__.py +++ b/india_compliance/__init__.py @@ -1 +1,7 @@ +<<<<<<< HEAD __version__ = "15.0.2" +======= +from .exceptions import * + +__version__ = "16.0.0-dev" +>>>>>>> 7afa94c7 (fix: enhanced otp handeling taxpayer apis) diff --git a/india_compliance/exceptions.py b/india_compliance/exceptions.py index 9f1ae8315..899cea9e5 100644 --- a/india_compliance/exceptions.py +++ b/india_compliance/exceptions.py @@ -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) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index acb431144..c38b418c7 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -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 @@ -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 ( @@ -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" @@ -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: @@ -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): diff --git a/india_compliance/gst_india/utils/gstr_utils.py b/india_compliance/gst_india/utils/gstr_utils.py index 5bc35e094..24d45f537 100644 --- a/india_compliance/gst_india/utils/gstr_utils.py +++ b/india_compliance/gst_india/utils/gstr_utils.py @@ -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, @@ -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) @@ -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, diff --git a/india_compliance/public/js/gst_api_handler.js b/india_compliance/public/js/gst_api_handler.js new file mode 100644 index 000000000..6a710773e --- /dev/null +++ b/india_compliance/public/js/gst_api_handler.js @@ -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; \ No newline at end of file diff --git a/india_compliance/public/js/india_compliance.bundle.js b/india_compliance/public/js/india_compliance.bundle.js index a3c4a7d25..d43ba882a 100644 --- a/india_compliance/public/js/india_compliance.bundle.js +++ b/india_compliance/public/js/india_compliance.bundle.js @@ -1,4 +1,5 @@ import "./utils"; +import "./gst_api_handler"; import "./quick_entry"; import "./transaction"; import "./audit_trail_notification"; diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index 01ed456e9..a889da558 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -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"; @@ -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 = $(`
From 959a1efc1cb64deef4ff6e1610bb0e88b89896fb Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 10 Sep 2024 16:43:52 +0530 Subject: [PATCH 08/29] fix: make a request to check if auth token is valid before enqueue (cherry picked from commit 7e1c4af56ae4076e99396e972f6650749d6d8b6d) # Conflicts: # india_compliance/__init__.py --- india_compliance/__init__.py | 3 + india_compliance/exceptions.py | 5 ++ .../gst_india/api_classes/taxpayer_base.py | 81 +++++++++++++++++-- 3 files changed, 83 insertions(+), 6 deletions(-) diff --git a/india_compliance/__init__.py b/india_compliance/__init__.py index 7a9e6fa57..c2e2d3b86 100644 --- a/india_compliance/__init__.py +++ b/india_compliance/__init__.py @@ -1,7 +1,10 @@ <<<<<<< HEAD +<<<<<<< HEAD __version__ = "15.0.2" ======= from .exceptions import * +======= +>>>>>>> 7e1c4af5 (fix: make a request to check if auth token is valid before enqueue) __version__ = "16.0.0-dev" >>>>>>> 7afa94c7 (fix: enhanced otp handeling taxpayer apis) diff --git a/india_compliance/exceptions.py b/india_compliance/exceptions.py index 899cea9e5..b9c29310b 100644 --- a/india_compliance/exceptions.py +++ b/india_compliance/exceptions.py @@ -18,3 +18,8 @@ class InvalidOTPError(Exception): def __init__(self, message="Invalid OTP", *args, **kwargs): self.response = kwargs.pop("response", None) super().__init__(message, *args, **kwargs) + + +class InvalidAuthTokenError(Exception): + def __init__(self, message="Invalid Auth Token", *args, **kwargs): + super().__init__(message, *args, **kwargs) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index c38b418c7..548445c6f 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -6,10 +6,15 @@ from cryptography.hazmat.backends import default_backend import frappe +import frappe.utils from frappe import _ from frappe.utils import add_to_date, cint, now_datetime -import india_compliance +from india_compliance.exceptions import ( + InvalidAuthTokenError, + InvalidOTPError, + OTPRequestedError, +) 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 ( @@ -28,10 +33,10 @@ def wrapper(*args, **kwargs): try: return func(*args, **kwargs) - except india_compliance.OTPRequestedError as e: + except OTPRequestedError as e: return e.response - except india_compliance.InvalidOTPError as e: + except InvalidOTPError as e: return e.response except Exception as e: @@ -127,7 +132,7 @@ def request_otp(self): response.update({"error_type": "otp_requested", "gstin": self.company_gstin}) - raise india_compliance.OTPRequestedError(response=response) + raise OTPRequestedError(response=response) def autheticate_with_otp(self, otp=None): if not otp: @@ -248,10 +253,31 @@ def get_auth_token(self): return self.auth_token + def reset_auth_token(self): + """ + Reset after job to clear the auth token + """ + frappe.db.set_value( + "GST Credential", + { + "gstin": self.company_gstin, + "username": self.username, + "service": "Returns", + }, + {"auth_token": None}, + ) + + frappe.db.commit() + class TaxpayerBaseAPI(TaxpayerAuthenticate): BASE_PATH = "standard/gstn" + IGNORED_ERROR_CODES = { + **TaxpayerAuthenticate.IGNORED_ERROR_CODES, + "RT-R1R3BAV-1007": "authorization_failed", # Either auth-token or username is invalid. Raised in get_filing_preference + } + def setup(self, company_gstin): if self.sandbox_mode: frappe.throw(_("Sandbox mode not supported for Returns API")) @@ -291,6 +317,12 @@ def _request( if response.error_type in ["otp_requested", "invalid_otp"]: return response + frappe.cache.set_value( + f"authenticated_gstin:{self.company_gstin}", + True, + expires_in_sec=60 * 15, + ) + headers = {"auth-token": auth_token} if return_period: headers["ret_period"] = return_period @@ -370,10 +402,17 @@ def is_ignored_error(self, response): response.gstin = self.company_gstin if response.error_type == "otp_requested": - raise india_compliance.OTPRequestedError(response=response) + raise OTPRequestedError(response=response) if response.error_type == "invalid_otp": - raise india_compliance.InvalidOTPError(response=response) + raise InvalidOTPError(response=response) + + if response.error_type == "authorization_failed" and getattr( + frappe.local, "job", None + ): + frappe.local.job.after_job.add(self.reset_auth_token) + + raise InvalidAuthTokenError return True @@ -404,3 +443,33 @@ def get_files(self, return_period, token, action, endpoint, otp=None): return response return FilesAPI().get_all(response) + + def validate_auth_token(self): + """ + Try refreshing the auth token without error + to check if the auth token is valid + + Generates a new OTP if the auth token is invalid + """ + if frappe.cache.get_value(f"authenticated_gstin:{self.company_gstin}"): + return + + # Dummy request + self.get_filing_preference() + + return + + def get_filing_preference(self): + return self.get( + action="GETPREF", params={"fy": self.get_fy()}, endpoint="returns" + ) + + @staticmethod + def get_fy(): + date = frappe.utils.getdate() + + # Standard for India as per GST + if date.month < 4: + return f"{date.year - 1}-{str(date.year)[2:]}" + + return f"{date.year}-{str(date.year + 1)[2:]}" From 890a27ba5e3de4732b6b3b9fa960d0248dc4f83a Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 10 Sep 2024 17:21:44 +0530 Subject: [PATCH 09/29] fix: better implementation of OTP for purchase recon tool (cherry picked from commit 08beacb7880cfccb3a066bcce7c14f33e2f093c4) --- .../purchase_reconciliation_tool.js | 68 ++++++---------- .../purchase_reconciliation_tool.py | 78 ++++++++++--------- .../gst_india/utils/gstr_2/__init__.py | 18 ----- india_compliance/public/js/gst_api_handler.js | 1 - 4 files changed, 64 insertions(+), 101 deletions(-) diff --git a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js index 5cc3d8e4e..9b7c86269 100644 --- a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js +++ b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js @@ -92,7 +92,9 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { frm.disable_save(); frm.page.set_primary_action(__("Reconcile"), () => { if (!frm.doc.company && !frm.doc.company_gstin) { - frappe.throw(__('Please provide either a Company name or Company GSTIN.')); + frappe.throw( + __("Please provide either a Company name or Company GSTIN.") + ); } frm.save(); }); @@ -114,13 +116,12 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { ); frm.add_custom_button(__("dropdown-divider"), () => {}, __("Actions")); } - ["Accept", "Pending", "Ignore"].forEach( - action => - frm.add_custom_button( - __(action), - () => apply_action(frm, action), - __("Actions") - ) + ["Accept", "Pending", "Ignore"].forEach(action => + frm.add_custom_button( + __(action), + () => apply_action(frm, action), + __("Actions") + ) ); frm.$wrapper .find("[data-label='dropdown-divider']") @@ -351,12 +352,7 @@ class PurchaseReconciliationTool { label: "Action", fieldname: "action", fieldtype: "Select", - options: [ - "No Action", - "Accept", - "Ignore", - "Pending", - ], + options: ["No Action", "Accept", "Ignore", "Pending"], }, { label: "Classification", @@ -979,12 +975,7 @@ class DetailViewDialog { if (doctype == "Purchase Invoice") actions.push("Create", "Link", "Pending", "Ignore"); else actions.push("Link", "Pending", "Ignore"); - else - actions.push( - "Unlink", - "Accept", - "Pending" - ); + else actions.push("Unlink", "Accept", "Pending"); // setup actions actions.forEach(action => { @@ -1456,37 +1447,24 @@ async function download_gstr( only_missing = true, gst_categories = null ) { + // TODO: This can be ignored const authenticated_company_gstins = await india_compliance.authenticate_company_gstins( frm.doc.company, company_gstin == "All" ? null : company_gstin ); - const args = { - return_type: return_type, - company_gstins: authenticated_company_gstins, - date_range: date_range, - force: !only_missing, - gst_categories, - }; - frm.events.show_progress(frm, "download"); - - const { message } = await frm.call("download_gstr", args); - - if (message && message.length) { - // TODO: Setup Listners similar to GSTR-1 Beta - message.forEach(async msg => { - await india_compliance.authenticate_otp(msg.gstin, msg.error_type); - download_gstr( - frm, - date_range, - return_type, - msg.gstin, - only_missing, - gst_categories - ); - }); - } + authenticated_company_gstins.forEach(async gstin => { + const args = { + return_type: return_type, + company_gstin: gstin, + date_range: date_range, + force: !only_missing, + gst_categories, + }; + frm.events.show_progress(frm, "download"); + await frm.taxpayer_api_call("download_gstr", args); + }); } class EmailDialog { 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 3e420fefd..80ca8735d 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 @@ -11,7 +11,10 @@ from frappe.utils import add_to_date, cint, now_datetime from frappe.utils.response import json_handler -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.constants import ORIGINAL_VS_AMENDED from india_compliance.gst_india.doctype.purchase_reconciliation_tool import ( BaseUtil, @@ -104,9 +107,10 @@ def upload_gstr(self, return_type, period, file_path): return save_gstr_2b(self.company_gstin, period, json_data) @frappe.whitelist() + @otp_handler def download_gstr( self, - company_gstins, + company_gstin, date_range, return_type=None, force=False, @@ -114,12 +118,18 @@ def download_gstr( ): frappe.has_permission("Purchase Reconciliation Tool", "write", throw=True) - return download_gstr( - company_gstins=company_gstins, + TaxpayerBaseAPI(company_gstin).validate_auth_token() + + frappe.enqueue( + download_gstr, + company_gstin=company_gstin, date_range=date_range, return_type=return_type, force=force, gst_categories=gst_categories, + queue="long", + now=frappe.flags.in_test, + timeout=1800, ) @frappe.whitelist() @@ -435,37 +445,28 @@ def _get_link_options(self, data): def download_gstr( - company_gstins, + company_gstin, date_range, - return_type=None, + return_type, force=False, gst_categories=None, ): - if return_type: + try: return_type = ReturnType(return_type) - otp_failures = [] - - for company_gstin in company_gstins: - try: - if not return_type or return_type == ReturnType.GSTR2A: - error = download_pending_gstr_2a( - date_range, company_gstin, force, gst_categories - ) - - if not return_type or return_type == ReturnType.GSTR2B: - error = download_pending_gstr_2b(date_range, company_gstin) - - if error: - otp_failures.append(error) - - except Exception: - frappe.log_error( - frappe.get_traceback(), - f"Error while downloading {return_type.value if return_type else 'GSTR 2A & 2B'} for {company_gstin} ", + if return_type == ReturnType.GSTR2A: + return download_pending_gstr_2a( + date_range, company_gstin, force, gst_categories ) - return otp_failures + if return_type == ReturnType.GSTR2B: + return download_pending_gstr_2b(date_range, company_gstin) + + except Exception: + frappe.log_error( + frappe.get_traceback(), + f"Error while downloading {return_type.value if return_type else 'GSTR 2A & 2B'} for {company_gstin} ", + ) def download_pending_gstr_2a( @@ -645,7 +646,7 @@ def __init__(self): ) self.reconciliation_companies = self.get_reconciliation_company_list() - def download_gstr(self): + def download_gst_returns(self): if not self.is_reconciliation_enabled(): return @@ -653,14 +654,17 @@ def download_gstr(self): gst_categories = self.get_gst_categories() gstins = self.get_gstins_with_valid_credentials() - download_gstr( - date_range=[ - self.inward_supply_from_date.strftime("%Y-%m-%d"), - self.today.strftime("%Y-%m-%d"), - ], - company_gstins=gstins, - gst_categories=gst_categories, - ) + for gstin in gstins: + for return_type in (ReturnType.GSTR2A, ReturnType.GSTR2B): + download_gstr( + date_range=[ + self.inward_supply_from_date.strftime("%Y-%m-%d"), + self.today.strftime("%Y-%m-%d"), + ], + company_gstin=gstin, + gst_categories=gst_categories, + return_type=return_type.value, + ) def get_gst_categories(self): return [ @@ -735,7 +739,7 @@ def is_reconciliation_enabled(self): def auto_download_gstr(): """Auto download GSTR 2A and 2B""" - AutoReconcile().download_gstr() + AutoReconcile().download_gst_returns() def auto_reconcile(): diff --git a/india_compliance/gst_india/utils/gstr_2/__init__.py b/india_compliance/gst_india/utils/gstr_2/__init__.py index 6e0420a65..10da82b6e 100644 --- a/india_compliance/gst_india/utils/gstr_2/__init__.py +++ b/india_compliance/gst_india/utils/gstr_2/__init__.py @@ -73,8 +73,6 @@ def download_gstr_2a(gstin, return_periods, gst_categories=None): continue response = api.get_data(action, return_period) - if response.error_type in ["otp_requested", "invalid_otp"]: - return response if response.error_type == "no_docs_found": create_import_log( @@ -146,8 +144,6 @@ def download_gstr_2b(gstin, return_periods): ) response = api.get_data(return_period) - if response.error_type in ["otp_requested", "invalid_otp"]: - return response if response.error_type == "not_generated": frappe.msgprint( @@ -248,20 +244,6 @@ def save_gstr_2b(gstin, return_period, json_data): def save_gstr(gstin, return_type, return_period, json_data, gen_date_2b=None): - frappe.enqueue( - _save_gstr, - queue="long", - now=frappe.flags.in_test, - timeout=1800, - gstin=gstin, - return_type=return_type.value, - return_period=return_period, - json_data=json_data, - gen_date_2b=gen_date_2b, - ) - - -def _save_gstr(gstin, return_type, return_period, json_data, gen_date_2b=None): """Save GSTR data to Inward Supply :param return_period: str diff --git a/india_compliance/public/js/gst_api_handler.js b/india_compliance/public/js/gst_api_handler.js index 6a710773e..b04bb79d4 100644 --- a/india_compliance/public/js/gst_api_handler.js +++ b/india_compliance/public/js/gst_api_handler.js @@ -79,7 +79,6 @@ Object.assign(india_compliance, { }, async authenticate_otp(gstin, error_type = null) { - console.log(gstin, error_type); if (!error_type) error_type = "otp_requested"; let is_authenticated = false; From 46c85408b2990ff67ba40b4cfacb031106729435 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Sep 2024 07:21:41 +0530 Subject: [PATCH 10/29] refactor: authenticate gstins one after another (cherry picked from commit 98c53638ab55308ca7aaaedd66213715e87b58c8) --- .../purchase_reconciliation_tool.js | 13 ++-- .../gst_india/utils/gstr_utils.py | 68 ------------------- india_compliance/public/js/gst_api_handler.js | 26 ------- 3 files changed, 6 insertions(+), 101 deletions(-) diff --git a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js index 9b7c86269..37964906d 100644 --- a/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js +++ b/india_compliance/gst_india/doctype/purchase_reconciliation_tool/purchase_reconciliation_tool.js @@ -1447,14 +1447,13 @@ async function download_gstr( only_missing = true, gst_categories = null ) { - // TODO: This can be ignored - const authenticated_company_gstins = - await india_compliance.authenticate_company_gstins( - frm.doc.company, - company_gstin == "All" ? null : company_gstin - ); + let company_gstins; + if (company_gstin == "All") + company_gstins = await india_compliance.get_gstin_options(frm.doc.company); + + else company_gstins = [company_gstin]; - authenticated_company_gstins.forEach(async gstin => { + company_gstins.forEach(async gstin => { const args = { return_type: return_type, company_gstin: gstin, diff --git a/india_compliance/gst_india/utils/gstr_utils.py b/india_compliance/gst_india/utils/gstr_utils.py index 24d45f537..f0d68345f 100644 --- a/india_compliance/gst_india/utils/gstr_utils.py +++ b/india_compliance/gst_india/utils/gstr_utils.py @@ -1,8 +1,6 @@ from enum import Enum import frappe -from frappe import _ -from frappe.utils import add_to_date, now_datetime from india_compliance.gst_india.api_classes.taxpayer_base import ( TaxpayerBaseAPI, @@ -13,7 +11,6 @@ create_import_log, toggle_scheduled_jobs, ) -from india_compliance.gst_india.utils import get_gstin_list from india_compliance.gst_india.utils.gstr_1.gstr_1_download import ( save_gstr_1_filed_data, save_gstr_1_unfiled_data, @@ -27,71 +24,6 @@ class ReturnType(Enum): UnfiledGSTR1 = "Unfiled GSTR1" -@frappe.whitelist() -def validate_company_gstins(company=None, company_gstin=None): - """ - Checks the validity of the company's GSTIN authentication. - - Args: - company_gstin (str): The GSTIN of the company to validate. - - Returns: - dict: A dictionary where the keys are the GSTINs and the values are booleans indicating whether the authentication is valid. - """ - frappe.has_permission("GST Settings", throw=True) - - credentials = get_company_gstin_credentials(company, company_gstin) - - if company_gstin and not credentials: - frappe.throw( - _("Missing GSTIN credentials for GSTIN: {gstin}.").format( - gstin=company_gstin - ) - ) - - if not credentials: - frappe.throw(_("Missing credentials in GST Settings")) - - if company and not company_gstin: - missing_credentials = set(get_gstin_list(company)) - set( - credential.gstin for credential in credentials - ) - - if missing_credentials: - frappe.throw( - _("Missing GSTIN credentials for GSTIN(s): {gstins}.").format( - gstins=", ".join(missing_credentials), - ) - ) - - gstin_authentication_status = { - credential.gstin: ( - credential.session_expiry - and credential.auth_token - and credential.session_expiry > add_to_date(now_datetime(), minutes=30) - ) - for credential in credentials - } - - return gstin_authentication_status - - -def get_company_gstin_credentials(company=None, company_gstin=None): - filters = {"service": "Returns"} - - if company: - filters["company"] = company - - if company_gstin: - filters["gstin"] = company_gstin - - return frappe.get_all( - "GST Credential", - filters=filters, - fields=["gstin", "session_expiry", "auth_token"], - ) - - @frappe.whitelist() def request_otp(company_gstin): frappe.has_permission("GST Settings", throw=True) diff --git a/india_compliance/public/js/gst_api_handler.js b/india_compliance/public/js/gst_api_handler.js index b04bb79d4..207fd061e 100644 --- a/india_compliance/public/js/gst_api_handler.js +++ b/india_compliance/public/js/gst_api_handler.js @@ -52,32 +52,6 @@ Object.assign(india_compliance, { }); }, - 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"; From fcb9c8e5810eb14016b10409c40e42cc8485e6d6 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Sep 2024 07:50:42 +0530 Subject: [PATCH 11/29] fix: better place for raising error, no need for try except in enqueue (cherry picked from commit dccca5d9419abf10c9e0851bdc9a59086e10d849) --- .../gst_india/api_classes/taxpayer_base.py | 28 +++++++++---------- .../purchase_reconciliation_tool.py | 21 +++++--------- 2 files changed, 21 insertions(+), 28 deletions(-) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index 548445c6f..36f89dfd4 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -136,6 +136,11 @@ def request_otp(self): def autheticate_with_otp(self, otp=None): if not otp: + # in enqueue / cron job + if getattr(frappe.local, "job", None): + frappe.local.job.after_job.add(self.reset_auth_token) + raise InvalidAuthTokenError + # reset auth token frappe.db.set_value( "GST Credential", @@ -150,7 +155,7 @@ def autheticate_with_otp(self, otp=None): self.auth_token = None return self.request_otp() - return super().post( + response = super().post( json={ "action": "AUTHTOKEN", "app_key": self.app_key, @@ -160,6 +165,14 @@ def autheticate_with_otp(self, otp=None): endpoint="authenticate", ) + frappe.cache.set_value( + f"authenticated_gstin:{self.company_gstin}", + True, + expires_in_sec=60 * 15, + ) + + return response + def refresh_auth_token(self): auth_token = self.get_auth_token() @@ -317,12 +330,6 @@ def _request( if response.error_type in ["otp_requested", "invalid_otp"]: return response - frappe.cache.set_value( - f"authenticated_gstin:{self.company_gstin}", - True, - expires_in_sec=60 * 15, - ) - headers = {"auth-token": auth_token} if return_period: headers["ret_period"] = return_period @@ -407,13 +414,6 @@ def is_ignored_error(self, response): if response.error_type == "invalid_otp": raise InvalidOTPError(response=response) - if response.error_type == "authorization_failed" and getattr( - frappe.local, "job", None - ): - frappe.local.job.after_job.add(self.reset_auth_token) - - raise InvalidAuthTokenError - return True def generate_app_key(self): 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 80ca8735d..d24e48614 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 @@ -451,23 +451,16 @@ def download_gstr( force=False, gst_categories=None, ): - try: - return_type = ReturnType(return_type) - - if return_type == ReturnType.GSTR2A: - return download_pending_gstr_2a( - date_range, company_gstin, force, gst_categories - ) + return_type = ReturnType(return_type) - if return_type == ReturnType.GSTR2B: - return download_pending_gstr_2b(date_range, company_gstin) - - except Exception: - frappe.log_error( - frappe.get_traceback(), - f"Error while downloading {return_type.value if return_type else 'GSTR 2A & 2B'} for {company_gstin} ", + if return_type == ReturnType.GSTR2A: + return download_pending_gstr_2a( + date_range, company_gstin, force, gst_categories ) + if return_type == ReturnType.GSTR2B: + return download_pending_gstr_2b(date_range, company_gstin) + def download_pending_gstr_2a( date_range, company_gstin, force=False, gst_categories=None From 271ea9b926d1aa469026d20126393720d71cfa5b Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Sep 2024 07:59:46 +0530 Subject: [PATCH 12/29] chore: comment (cherry picked from commit 5f6c61f757a139ddcbb08e1ff213f94e3c1b7854) --- india_compliance/gst_india/api_classes/taxpayer_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index 36f89dfd4..e4426cf0c 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -280,7 +280,7 @@ def reset_auth_token(self): {"auth_token": None}, ) - frappe.db.commit() + frappe.db.commit() # nosemgrep - executed in after enqueue class TaxpayerBaseAPI(TaxpayerAuthenticate): From c000cc8bcfe0f4fc909bcf7674883993acd0b60d Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Sep 2024 09:02:16 +0530 Subject: [PATCH 13/29] fix: otp handeling for gstr-1 with newer method (cherry picked from commit 3dd82bafce674bb8cd7f9be615ff2ddfa9297b24) --- .../gst_india/api_classes/taxpayer_base.py | 2 +- .../doctype/gst_return_log/generate_gstr_1.py | 10 -------- .../doctype/gstr_1_beta/gstr_1_beta.js | 20 ++++------------ .../doctype/gstr_1_beta/gstr_1_beta.py | 23 +++++++++---------- .../gst_india/utils/gstr_1/gstr_1_download.py | 3 --- 5 files changed, 16 insertions(+), 42 deletions(-) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index e4426cf0c..11700c969 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -110,7 +110,7 @@ class TaxpayerAuthenticate(BaseAPI): IGNORED_ERROR_CODES = { "RETOTPREQUEST": "otp_requested", "EVCREQUEST": "otp_requested", - "AUTH158": "invalid_otp", # Invalid OTP + "AUTH158": "authorization_failed", # GSTR1 "AUTH4033": "invalid_otp", # Invalid Session # "AUTH4034": "invalid_otp", # Invalid OTP "AUTH4038": "authorization_failed", # Session Expired diff --git a/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py b/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py index 48c43a625..75ea1ca07 100644 --- a/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py +++ b/india_compliance/gst_india/doctype/gst_return_log/generate_gstr_1.py @@ -20,7 +20,6 @@ GSTR1BooksData, summarize_retsum_data, ) -from india_compliance.gst_india.utils.gstr_utils import request_otp class SummarizeGSTR1: @@ -513,15 +512,6 @@ def generate_gstr1_data(self, filters, callback=None): # Get Data gov_data, is_enqueued = self.get_gov_gstr1_data() - if error_type := gov_data.get("error_type"): - # otp_requested, invalid_otp - - if error_type == "invalid_otp": - request_otp(filters.company_gstin) - - data = "otp_requested" - return callback and callback(data, filters) - books_data = self.get_books_gstr1_data(filters) if is_enqueued: diff --git a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js index f19222983..2df782df5 100644 --- a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js +++ b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.js @@ -195,7 +195,7 @@ frappe.ui.form.on(DOCTYPE, { // Primary Action frm.disable_save(); frm.page.set_primary_action(__("Generate"), () => - frm.call("generate_gstr1") + frm.taxpayer_api_call("generate_gstr1") ); // After indicator set in frappe refresh @@ -205,18 +205,6 @@ frappe.ui.form.on(DOCTYPE, { load_gstr1_data(frm) { const data = frm.doc.__gst_data; - if (!frm._otp_requested && data == "otp_requested") { - frm._otp_requested = true; - - india_compliance - .authenticate_otp(frm.doc.company_gstin) - .then(() => frm.call("generate_gstr1")); - - return; - } - - frm._otp_requested = false; - if (!data?.status) return; // Toggle HTML fields @@ -1522,7 +1510,7 @@ class BooksTab extends GSTR1_TabManager { recompute_books() { render_empty_state(this.instance.frm); - this.instance.frm.call("recompute_books"); + this.instance.frm.taxpayer_api_call("recompute_books"); } // COLUMNS @@ -1733,7 +1721,7 @@ class FiledTab extends GSTR1_TabManager { sync_with_gstn(sync_for) { render_empty_state(this.instance.frm); - this.instance.frm.call("sync_with_gstn", { sync_for }); + this.instance.frm.taxpayer_api_call("sync_with_gstn", { sync_for }); } download_filed_json() { @@ -1809,7 +1797,7 @@ class FiledTab extends GSTR1_TabManager { mark_as_filed() { render_empty_state(this.instance.frm); this.instance.frm - .call("mark_as_filed") + .taxpayer_api_call("mark_as_filed") .then(() => this.instance.frm.trigger("load_gstr1_data")); } diff --git a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py index 88253740f..d0f7f0028 100644 --- a/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py +++ b/india_compliance/gst_india/doctype/gstr_1_beta/gstr_1_beta.py @@ -9,20 +9,23 @@ from frappe.query_builder.functions import Date, Sum from frappe.utils import get_last_day, getdate +from india_compliance.gst_india.api_classes.taxpayer_base import ( + TaxpayerBaseAPI, + otp_handler, +) from india_compliance.gst_india.utils import get_gst_accounts_by_type from india_compliance.gst_india.utils.gstin_info import get_gstr_1_return_status -from india_compliance.gst_india.utils.gstr_utils import request_otp class GSTR1Beta(Document): @frappe.whitelist() def recompute_books(self): - self.generate_gstr1(recompute_books=True) + return self.generate_gstr1(recompute_books=True) @frappe.whitelist() def sync_with_gstn(self, sync_for): - self.generate_gstr1(sync_for=sync_for, recompute_books=True) + return self.generate_gstr1(sync_for=sync_for, recompute_books=True) @frappe.whitelist() def mark_as_filed(self): @@ -44,9 +47,10 @@ def mark_as_filed(self): return_status, ) - self.generate_gstr1() + return self.generate_gstr1() @frappe.whitelist() + @otp_handler def generate_gstr1(self, sync_for=None, recompute_books=False): period = get_period(self.month_or_quarter, self.year) @@ -100,14 +104,9 @@ def generate_gstr1(self, sync_for=None, recompute_books=False): self.on_generate(data) return - # request OTP - if gstr1_log.is_sek_needed(settings) and not settings.is_sek_valid( - self.company_gstin - ): - request_otp(self.company_gstin) - data = "otp_requested" - self.on_generate(data) - return + # validate auth token + if gstr1_log.is_sek_needed(settings): + TaxpayerBaseAPI(self.company_gstin).validate_auth_token() self.gstr1_log = gstr1_log diff --git a/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py b/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py index 29ff0c754..f623e208a 100644 --- a/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py +++ b/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py @@ -53,9 +53,6 @@ def download_gstr1_json_data(gstr1_log): for action in actions: response = api.get_gstr_1_data(action, return_period) - if response.error_type in ["otp_requested", "invalid_otp"]: - return response, None - if response.error_type == "no_docs_found": continue From a6686e7a7e13c730f61cf2f2c352415853e9c847 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Sep 2024 15:10:20 +0530 Subject: [PATCH 14/29] fix: ensure correct value for return type (cherry picked from commit 1ecb835f50c3e9b910219131018b4396954a97ae) --- india_compliance/gst_india/utils/gstr_2/__init__.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/india_compliance/gst_india/utils/gstr_2/__init__.py b/india_compliance/gst_india/utils/gstr_2/__init__.py index 10da82b6e..6efdf64a4 100644 --- a/india_compliance/gst_india/utils/gstr_2/__init__.py +++ b/india_compliance/gst_india/utils/gstr_2/__init__.py @@ -243,7 +243,9 @@ def save_gstr_2b(gstin, return_period, json_data): update_import_history(return_period) -def save_gstr(gstin, return_type, return_period, json_data, gen_date_2b=None): +def save_gstr( + gstin, return_type: ReturnType, return_period, json_data, gen_date_2b=None +): """Save GSTR data to Inward Supply :param return_period: str @@ -253,7 +255,7 @@ def save_gstr(gstin, return_type, return_period, json_data, gen_date_2b=None): company = get_party_for_gstin(gstin, "Company") for category in GSTRCategory: - gstr = get_data_handler(return_type, category) + gstr = get_data_handler(return_type.value, category) gstr(company, gstin, return_period, json_data, gen_date_2b).create_transactions( category, json_data.get(category.value.lower()), From cd4bef5d84e31c6bfbb84785d3dce0e1a8885b67 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Mon, 9 Sep 2024 18:08:19 +0530 Subject: [PATCH 15/29] fix: gst_hsn_code is mandatory if gst_category is overseas (cherry picked from commit 024e6f27afd70ba65ec7b4d1ac7d0acafccf02c8) --- .../client_scripts/purchase_invoice.js | 20 +++++++++++++++++-- .../gst_india/overrides/purchase_invoice.py | 11 +++++++++- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/purchase_invoice.js b/india_compliance/gst_india/client_scripts/purchase_invoice.js index bfc9a3701..dbca462d6 100644 --- a/india_compliance/gst_india/client_scripts/purchase_invoice.js +++ b/india_compliance/gst_india/client_scripts/purchase_invoice.js @@ -20,7 +20,10 @@ frappe.ui.form.on(DOCTYPE, { onload: toggle_reverse_charge, - gst_category: toggle_reverse_charge, + gst_category(frm) { + toggle_reverse_charge(frm); + validate_gst_hsn_code(frm); + }, after_save(frm) { if ( @@ -90,9 +93,14 @@ frappe.ui.form.on(DOCTYPE, { }); frappe.ui.form.on("Purchase Invoice Item", { - item_code: toggle_reverse_charge, + item_code(frm) { + toggle_reverse_charge(frm); + validate_gst_hsn_code(frm); + }, items_remove: toggle_reverse_charge, + + gst_hsn_code: validate_gst_hsn_code, }); function toggle_reverse_charge(frm) { @@ -104,3 +112,11 @@ function toggle_reverse_charge(frm) { frm.set_df_property("is_reverse_charge", "read_only", is_read_only); } + +function validate_gst_hsn_code(frm) { + if (frm.doc.gst_category !== "Overseas") return; + + if (frm.doc.items.some(item => !item.gst_hsn_code)) { + frappe.throw(__("GST HSN Code is mandatory for Overseas Purchase Invoice.")); + } +} diff --git a/india_compliance/gst_india/overrides/purchase_invoice.py b/india_compliance/gst_india/overrides/purchase_invoice.py index 57f6783ff..0b54eafc1 100644 --- a/india_compliance/gst_india/overrides/purchase_invoice.py +++ b/india_compliance/gst_india/overrides/purchase_invoice.py @@ -43,6 +43,7 @@ def validate(doc, method=None): if validate_transaction(doc) is False: return + validate_gst_hsn_code(doc) set_ineligibility_reason(doc) update_itc_totals(doc) validate_supplier_invoice_number(doc) @@ -51,7 +52,6 @@ def validate(doc, method=None): def on_cancel(doc, method=None): - frappe.db.set_value( "GST Inward Supply", {"link_doctype": "Purchase Invoice", "link_name": doc.name}, @@ -263,3 +263,12 @@ def validate_reverse_charge(doc): return frappe.throw(_("Reverse Charge is not applicable on Import of Goods")) + + +def validate_gst_hsn_code(doc): + if doc.gst_category != "Overseas": + return + + for item in doc.items: + if not item.get("gst_hsn_code"): + frappe.throw(_("GST HSN Code is mandatory for Overseas Purchase Invoice")) From 8bbabee8d67d89099e453e80ccea72a374cf22e2 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 10 Sep 2024 16:17:25 +0530 Subject: [PATCH 16/29] fix: use existing utility (cherry picked from commit 94434a49d1b3a49af3fcc08ed69b1241c3f823b5) --- .../gst_india/client_scripts/purchase_invoice.js | 4 ++-- .../gst_india/overrides/purchase_invoice.py | 15 ++++++++++----- .../gst_india/overrides/transaction.py | 8 +++++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/purchase_invoice.js b/india_compliance/gst_india/client_scripts/purchase_invoice.js index dbca462d6..dae0d69e3 100644 --- a/india_compliance/gst_india/client_scripts/purchase_invoice.js +++ b/india_compliance/gst_india/client_scripts/purchase_invoice.js @@ -21,8 +21,8 @@ frappe.ui.form.on(DOCTYPE, { onload: toggle_reverse_charge, gst_category(frm) { - toggle_reverse_charge(frm); validate_gst_hsn_code(frm); + toggle_reverse_charge(frm); }, after_save(frm) { @@ -94,8 +94,8 @@ frappe.ui.form.on(DOCTYPE, { frappe.ui.form.on("Purchase Invoice Item", { item_code(frm) { - toggle_reverse_charge(frm); validate_gst_hsn_code(frm); + toggle_reverse_charge(frm); }, items_remove: toggle_reverse_charge, diff --git a/india_compliance/gst_india/overrides/purchase_invoice.py b/india_compliance/gst_india/overrides/purchase_invoice.py index 0b54eafc1..cee58a68d 100644 --- a/india_compliance/gst_india/overrides/purchase_invoice.py +++ b/india_compliance/gst_india/overrides/purchase_invoice.py @@ -5,6 +5,9 @@ from india_compliance.gst_india.overrides.sales_invoice import ( update_dashboard_with_gst_logs, ) +from india_compliance.gst_india.overrides.transaction import ( + validate_hsn_codes as _validate_hsn_codes, +) from india_compliance.gst_india.overrides.transaction import validate_transaction from india_compliance.gst_india.utils import is_api_enabled from india_compliance.gst_india.utils.e_waybill import get_e_waybill_info @@ -43,7 +46,7 @@ def validate(doc, method=None): if validate_transaction(doc) is False: return - validate_gst_hsn_code(doc) + validate_hsn_codes(doc) set_ineligibility_reason(doc) update_itc_totals(doc) validate_supplier_invoice_number(doc) @@ -265,10 +268,12 @@ def validate_reverse_charge(doc): frappe.throw(_("Reverse Charge is not applicable on Import of Goods")) -def validate_gst_hsn_code(doc): +def validate_hsn_codes(doc): if doc.gst_category != "Overseas": return - for item in doc.items: - if not item.get("gst_hsn_code"): - frappe.throw(_("GST HSN Code is mandatory for Overseas Purchase Invoice")) + _validate_hsn_codes( + doc, + throw=True, + message="GST HSN Code is mandatory for Overseas Purchase Invoice.
", + ) diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index b56120e23..064de4a80 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -721,13 +721,13 @@ def validate_backdated_transaction(doc, gst_settings=None, action="create"): ) -def validate_hsn_codes(doc): +def validate_hsn_codes(doc, throw=False, message=None): validate_hsn_code, valid_hsn_length = get_hsn_settings() if not validate_hsn_code: return - return _validate_hsn_codes(doc, valid_hsn_length, message=None) + return _validate_hsn_codes(doc, valid_hsn_length, throw, message) def validate_sales_reverse_charge(doc): @@ -740,7 +740,7 @@ def validate_sales_reverse_charge(doc): ) -def _validate_hsn_codes(doc, valid_hsn_length, message=None): +def _validate_hsn_codes(doc, valid_hsn_length, throw=False, message=None): rows_with_missing_hsn = [] rows_with_invalid_hsn = [] @@ -773,6 +773,7 @@ def _validate_hsn_codes(doc, valid_hsn_length, message=None): "{0}" "Please enter HSN/SAC code for the following row numbers:
{1}" ).format(message or "", frappe.bold(", ".join(rows_with_missing_hsn))), title=_("Invalid HSN/SAC"), + raise_exception=throw, ) if rows_with_invalid_hsn: @@ -787,6 +788,7 @@ def _validate_hsn_codes(doc, valid_hsn_length, message=None): frappe.bold(", ".join(rows_with_invalid_hsn)), ), title=_("Invalid HSN/SAC"), + raise_exception=throw, ) From b548be89024f3f55f21bab4e25a6c5a3c1db3a5f Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Sep 2024 15:02:07 +0530 Subject: [PATCH 17/29] fix: show doc issued summary in filed tab (cherry picked from commit 13559678e8adfa9e92e1276aa14fc6d14521e836) --- .../gst_india/utils/gstr_1/gstr_1_json_map.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/india_compliance/gst_india/utils/gstr_1/gstr_1_json_map.py b/india_compliance/gst_india/utils/gstr_1/gstr_1_json_map.py index d57409262..7d1b0f147 100644 --- a/india_compliance/gst_india/utils/gstr_1/gstr_1_json_map.py +++ b/india_compliance/gst_india/utils/gstr_1/gstr_1_json_map.py @@ -1821,6 +1821,14 @@ def convert_to_internal_data_format(self, input_data): return {"summary": output} + def format_data(self, data, default_data=None, for_gov=False): + response = super().format_data(data, default_data, for_gov) + + if data.get("sec_nm") == "DOC_ISSUE": + response["no_of_records"] = data.get("net_doc_issued", 0) + + return response + def format_subsection_data(self, section, subsection_data): subsection = subsection_data.get("typ") or subsection_data.get("sec_nm") formatted_data = self.format_data(subsection_data) @@ -1935,6 +1943,7 @@ def summarize_retsum_data(input_data): summarized_data = [] total_values_keys = [ + "no_of_records", "total_igst_amount", "total_cgst_amount", "total_sgst_amount", From 937a2657761bb521c93364dfa87a2ad82e52e48a Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 16 Sep 2024 21:21:55 +0530 Subject: [PATCH 18/29] chore: resolve conflicts --- india_compliance/__init__.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/india_compliance/__init__.py b/india_compliance/__init__.py index c2e2d3b86..b93534d70 100644 --- a/india_compliance/__init__.py +++ b/india_compliance/__init__.py @@ -1,10 +1 @@ -<<<<<<< HEAD -<<<<<<< HEAD __version__ = "15.0.2" -======= -from .exceptions import * - -======= ->>>>>>> 7e1c4af5 (fix: make a request to check if auth token is valid before enqueue) -__version__ = "16.0.0-dev" ->>>>>>> 7afa94c7 (fix: enhanced otp handeling taxpayer apis) From 9438d8ddf9a976ef2f4ba06d1dadfdd8df1c08f7 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Wed, 11 Sep 2024 13:38:10 +0530 Subject: [PATCH 19/29] fix: manually mark e-Invoice as generated (cherry picked from commit 4511437b749e02a5d3ff025bd17cf67200c221a1) --- .../client_scripts/e_invoice_actions.js | 54 +++++++++++++++++++ .../gst_india/constants/custom_fields.py | 2 +- india_compliance/gst_india/utils/e_invoice.py | 19 ++++++- india_compliance/patches.txt | 2 +- 4 files changed, 74 insertions(+), 3 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/e_invoice_actions.js b/india_compliance/gst_india/client_scripts/e_invoice_actions.js index 8409d9114..aff143976 100644 --- a/india_compliance/gst_india/client_scripts/e_invoice_actions.js +++ b/india_compliance/gst_india/client_scripts/e_invoice_actions.js @@ -63,6 +63,12 @@ frappe.ui.form.on("Sales Invoice", { }, "e-Invoice" ); + + frm.add_custom_button( + __("Mark as Generated"), + () => show_mark_e_invoice_as_generated_dialog(frm), + "e-Invoice" + ); } if ( frm.doc.irn && @@ -189,6 +195,54 @@ function show_cancel_e_invoice_dialog(frm, callback) { `).prependTo(d.wrapper); } +function show_mark_e_invoice_as_generated_dialog(frm) { + const d = new frappe.ui.Dialog({ + title: __("Update e-Invoice Details"), + fields: get_generated_e_invoice_dialog_fields(), + primary_action_label: __("Update"), + primary_action(values) { + frappe.call({ + method: "india_compliance.gst_india.utils.e_invoice.mark_e_invoice_as_generated", + args: { + doctype: frm.doctype, + docname: frm.doc.name, + values, + }, + callback: () => { + d.hide(); + frm.refresh(); + }, + }); + }, + }); + + d.show(); +} + +function get_generated_e_invoice_dialog_fields() { + let fields = [ + { + label: "IRN Number", + fieldname: "irn", + fieldtype: "Data", + reqd: 1, + }, + { + label: "Acknowledgement Number", + fieldname: "ack_no", + fieldtype: "Data", + reqd: 1, + }, + { + label: "Acknowledged On", + fieldname: "ack_dt", + fieldtype: "Datetime", + reqd: 1, + }, + ]; + return fields; +} + function show_mark_e_invoice_as_cancelled_dialog(frm) { const d = new frappe.ui.Dialog({ title: __("Update Cancelled e-Invoice Details"), diff --git a/india_compliance/gst_india/constants/custom_fields.py b/india_compliance/gst_india/constants/custom_fields.py index 5994b0b80..4b418a17e 100644 --- a/india_compliance/gst_india/constants/custom_fields.py +++ b/india_compliance/gst_india/constants/custom_fields.py @@ -1384,7 +1384,7 @@ "label": "e-Invoice Status", "fieldtype": "Select", "insert_after": "status", - "options": "\nPending\nGenerated\nAuto-Retry\nCancelled\nManually Cancelled\nFailed\nNot Applicable\nPending Cancellation", + "options": "\nPending\nGenerated\nManually Generated\nAuto-Retry\nCancelled\nManually Cancelled\nFailed\nNot Applicable\nPending Cancellation", "default": None, "hidden": 1, "no_copy": 1, diff --git a/india_compliance/gst_india/utils/e_invoice.py b/india_compliance/gst_india/utils/e_invoice.py index d66eff3b3..54477625f 100644 --- a/india_compliance/gst_india/utils/e_invoice.py +++ b/india_compliance/gst_india/utils/e_invoice.py @@ -287,7 +287,7 @@ def log_and_process_e_invoice_generation(doc, result, sandbox_mode=False): doc.db_set( { "irn": result.Irn, - "einvoice_status": "Generated", + "einvoice_status": result.get("einvoice_status") or "Generated", } ) @@ -380,6 +380,23 @@ def log_and_process_e_invoice_cancellation(doc, values, result, message): frappe.msgprint(_(message), indicator="green", alert=True) +@frappe.whitelist() +def mark_e_invoice_as_generated(doctype, docname, values): + doc = load_doc(doctype, docname, "submit") + + values = frappe.parse_json(values) + result = frappe._dict( + { + "Irn": values.irn, + "AckDt": values.ack_dt, + "AckNo": values.ack_no, + "einvoice_status": "Manually Generated", + } + ) + + return log_and_process_e_invoice_generation(doc, result) + + @frappe.whitelist() def mark_e_invoice_as_cancelled(doctype, docname, values): doc = load_doc(doctype, docname, "cancel") diff --git a/india_compliance/patches.txt b/india_compliance/patches.txt index e95b3d503..fdc400132 100644 --- a/india_compliance/patches.txt +++ b/india_compliance/patches.txt @@ -4,7 +4,7 @@ india_compliance.patches.v15.remove_duplicate_web_template [post_model_sync] india_compliance.patches.v14.set_default_for_overridden_accounts_setting -execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #57 +execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #58 execute:from india_compliance.gst_india.setup import create_property_setters; create_property_setters() #8 execute:from india_compliance.income_tax_india.setup import create_custom_fields; create_custom_fields() #1 india_compliance.patches.post_install.remove_old_fields #2 From 781edf78338d81d45e21d3addb9e249d366b4950 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 16 Sep 2024 21:18:30 +0530 Subject: [PATCH 20/29] fix: update success message (cherry picked from commit be12ec3c3ede0f791ad42a6a00ea5c34c6bc4da5) --- india_compliance/gst_india/utils/e_invoice.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/india_compliance/gst_india/utils/e_invoice.py b/india_compliance/gst_india/utils/e_invoice.py index 54477625f..c99c21a5d 100644 --- a/india_compliance/gst_india/utils/e_invoice.py +++ b/india_compliance/gst_india/utils/e_invoice.py @@ -279,7 +279,7 @@ def verify_e_invoice_details(current_gstin, current_invoice_amount, signed_data) ) -def log_and_process_e_invoice_generation(doc, result, sandbox_mode=False): +def log_and_process_e_invoice_generation(doc, result, sandbox_mode=False, message=None): """ Load and process the e-Invoice generation result. """ @@ -320,11 +320,10 @@ def log_and_process_e_invoice_generation(doc, result, sandbox_mode=False): if not frappe.request: return - frappe.msgprint( - _("e-Invoice generated successfully"), - indicator="green", - alert=True, - ) + if not message: + message = "e-Invoice generated successfully" + + frappe.msgprint(_(message), indicator="green", alert=True) return send_updated_doc(doc) @@ -394,7 +393,9 @@ def mark_e_invoice_as_generated(doctype, docname, values): } ) - return log_and_process_e_invoice_generation(doc, result) + return log_and_process_e_invoice_generation( + doc, result, message="e-Invoice updated successfully" + ) @frappe.whitelist() From 4ef2021cd840a11f66e7c8c76408199707186408 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 16 Sep 2024 21:26:39 +0530 Subject: [PATCH 21/29] fix: minor enhancements for new otp, gstr-1 download queue status update (cherry picked from commit f4a963389a8c6261ca37bdaebed5c93e4020af01) --- india_compliance/gst_india/api_classes/taxpayer_base.py | 6 ++++++ india_compliance/gst_india/utils/gstr_1/gstr_1_download.py | 1 + 2 files changed, 7 insertions(+) diff --git a/india_compliance/gst_india/api_classes/taxpayer_base.py b/india_compliance/gst_india/api_classes/taxpayer_base.py index 11700c969..1e92ffee0 100644 --- a/india_compliance/gst_india/api_classes/taxpayer_base.py +++ b/india_compliance/gst_india/api_classes/taxpayer_base.py @@ -457,6 +457,12 @@ def validate_auth_token(self): # Dummy request self.get_filing_preference() + frappe.cache.set_value( + f"authenticated_gstin:{self.company_gstin}", + True, + expires_in_sec=60 * 15, + ) + return def get_filing_preference(self): diff --git a/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py b/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py index f623e208a..d6366e964 100644 --- a/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py +++ b/india_compliance/gst_india/utils/gstr_1/gstr_1_download.py @@ -110,6 +110,7 @@ def save_gstr_1(gstin, return_period, json_data, return_type): gstr1_log = frappe.get_doc("GST Return Log", f"GSTR1-{return_period}-{gstin}") gstr1_log.update_json_for(data_field, mapped_data, overwrite=False) + gstr1_log.update_status("Generated") def save_gstr_1_filed_data(gstin, return_period, json_data): From f02ff30dd46aba8985c14c98194dad7d551e614a Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 17 Sep 2024 18:04:21 +0530 Subject: [PATCH 22/29] fix: also check the purpose of transfer (cherry picked from commit b34a29a0155c18d20394f33247ef78615376bd06) --- .../gst_india/overrides/subcontracting_transaction.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/india_compliance/gst_india/overrides/subcontracting_transaction.py b/india_compliance/gst_india/overrides/subcontracting_transaction.py index 70710e966..7724ce106 100644 --- a/india_compliance/gst_india/overrides/subcontracting_transaction.py +++ b/india_compliance/gst_india/overrides/subcontracting_transaction.py @@ -167,6 +167,9 @@ def validate(doc, method=None): if ignore_gst_validation_for_subcontracting(doc): return + if doc.get("purpose") != "Send to Subcontractor": + return + field_map = ( STOCK_ENTRY_FIELD_MAP if doc.doctype == "Stock Entry" @@ -327,7 +330,6 @@ def set_address_display(doc): def get_relevant_references( supplier, supplied_items, received_items, subcontracting_orders ): - if isinstance(supplied_items, str): supplied_items = frappe.parse_json(supplied_items) received_items = frappe.parse_json(received_items) From 67df6c60642aa4ad0fe584f844d6e4fb3b6cf9c3 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Tue, 17 Sep 2024 16:16:14 +0530 Subject: [PATCH 23/29] fix: make e-Invoice log extensible (cherry picked from commit 279d59665afe89b5499abcf7f02a22fcea6b9271) # Conflicts: # india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json --- .../doctype/e_invoice_log/e_invoice_log.json | 32 +++++++++++++------ india_compliance/patches.txt | 3 +- .../v14/make_e_invoice_log_extensible.py | 9 ++++++ 3 files changed, 34 insertions(+), 10 deletions(-) create mode 100644 india_compliance/patches/v14/make_e_invoice_log_extensible.py diff --git a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json index c2a8450a6..554665656 100644 --- a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json +++ b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json @@ -7,7 +7,8 @@ "engine": "InnoDB", "field_order": [ "irn", - "sales_invoice", + "reference_doctype", + "reference_name", "is_generated_in_sandbox_mode", "column_break_1", "acknowledgement_number", @@ -73,13 +74,6 @@ "label": "Is Cancelled", "read_only": 1 }, - { - "fieldname": "sales_invoice", - "fieldtype": "Link", - "label": "Sales Invoice", - "options": "Sales Invoice", - "read_only": 1 - }, { "fieldname": "column_break_1", "fieldtype": "Column Break" @@ -120,6 +114,22 @@ "fieldname": "is_generated_in_sandbox_mode", "fieldtype": "Check", "label": "Is Generated in Sandbox Mode" + }, + { + "fieldname": "reference_doctype", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Reference Document Type", + "options": "DocType", + "read_only": 1 + }, + { + "fieldname": "reference_name", + "fieldtype": "Dynamic Link", + "in_standard_filter": 1, + "label": "Reference Document Name", + "options": "reference_doctype", + "read_only": 1 } ], "in_create": 1, @@ -129,7 +139,11 @@ "link_fieldname": "irn" } ], +<<<<<<< HEAD "modified": "2023-07-27 15:04:26.931722", +======= + "modified": "2024-09-17 15:15:35.720311", +>>>>>>> 279d5966 (fix: make e-Invoice log extensible) "modified_by": "Administrator", "module": "GST India", "name": "e-Invoice Log", @@ -160,5 +174,5 @@ "sort_field": "modified", "sort_order": "DESC", "states": [], - "title_field": "sales_invoice" + "title_field": "reference_name" } \ No newline at end of file diff --git a/india_compliance/patches.txt b/india_compliance/patches.txt index fdc400132..dcd349706 100644 --- a/india_compliance/patches.txt +++ b/india_compliance/patches.txt @@ -62,4 +62,5 @@ india_compliance.patches.v14.enable_sales_through_ecommerce_operator execute:from india_compliance.gst_india.setup import set_default_print_settings; set_default_print_settings() india_compliance.patches.v15.migrate_gstr1_log_to_returns_log india_compliance.patches.v15.update_action_for_gst_inward_supply -india_compliance.patches.v15.set_default_for_new_gst_category_notification \ No newline at end of file +india_compliance.patches.v15.set_default_for_new_gst_category_notification +india_compliance.patches.v14.make_e_invoice_log_extensible \ No newline at end of file diff --git a/india_compliance/patches/v14/make_e_invoice_log_extensible.py b/india_compliance/patches/v14/make_e_invoice_log_extensible.py new file mode 100644 index 000000000..cfe42dd0f --- /dev/null +++ b/india_compliance/patches/v14/make_e_invoice_log_extensible.py @@ -0,0 +1,9 @@ +import frappe + + +def execute(): + e_invoice_log = frappe.qb.DocType("e-Invoice Log") + + frappe.qb.update(e_invoice_log).set( + e_invoice_log.reference_name, e_invoice_log.sales_invoice + ).set(e_invoice_log.reference_doctype, "Sales Invoice").run() From 8ad9566e6ea57cfee48439f525bc320340d50063 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Tue, 17 Sep 2024 18:13:24 +0530 Subject: [PATCH 24/29] fix: change in test cases (cherry picked from commit 436aebb16cc45d4ad4b5af72d955b99fb8a8d823) --- india_compliance/gst_india/utils/e_invoice.py | 3 ++- india_compliance/gst_india/utils/test_e_invoice.py | 10 +++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/india_compliance/gst_india/utils/e_invoice.py b/india_compliance/gst_india/utils/e_invoice.py index c99c21a5d..223cf48d1 100644 --- a/india_compliance/gst_india/utils/e_invoice.py +++ b/india_compliance/gst_india/utils/e_invoice.py @@ -304,7 +304,8 @@ def log_and_process_e_invoice_generation(doc, result, sandbox_mode=False, messag doc, { "irn": doc.irn, - "sales_invoice": doc.name, + "reference_doctype": doc.doctype, + "reference_name": doc.name, "acknowledgement_number": result.AckNo, "acknowledged_on": parse_datetime(result.AckDt), "signed_invoice": result.SignedInvoice, diff --git a/india_compliance/gst_india/utils/test_e_invoice.py b/india_compliance/gst_india/utils/test_e_invoice.py index 030a1a9ea..2bcc816fa 100644 --- a/india_compliance/gst_india/utils/test_e_invoice.py +++ b/india_compliance/gst_india/utils/test_e_invoice.py @@ -251,7 +251,7 @@ def test_generate_e_invoice_with_goods_item(self): self.assertDocumentEqual( {"name": test_data.get("response_data").get("result").get("Irn")}, - frappe.get_doc("e-Invoice Log", {"sales_invoice": si.name}), + frappe.get_doc("e-Invoice Log", {"reference_name": si.name}), ) self.assertDocumentEqual( {"name": test_data.get("response_data").get("result").get("EwbNo")}, @@ -297,7 +297,7 @@ def test_generate_e_invoice_with_service_item(self): self.assertDocumentEqual( {"name": test_data.get("response_data").get("result").get("Irn")}, - frappe.get_doc("e-Invoice Log", {"sales_invoice": si.name}), + frappe.get_doc("e-Invoice Log", {"reference_name": si.name}), ) self.assertFalse( @@ -356,7 +356,7 @@ def test_generate_e_invoice_with_nil_exempted_item(self): self.assertDocumentEqual( {"name": test_data.get("response_data").get("result").get("Irn")}, - frappe.get_doc("e-Invoice Log", {"sales_invoice": si.name}), + frappe.get_doc("e-Invoice Log", {"reference_name": si.name}), ) self.assertFalse( @@ -430,7 +430,7 @@ def test_credit_note_e_invoice_with_goods_item(self): self.assertDocumentEqual( {"name": test_data.get("response_data").get("result").get("Irn")}, - frappe.get_doc("e-Invoice Log", {"sales_invoice": credit_note.name}), + frappe.get_doc("e-Invoice Log", {"reference_name": credit_note.name}), ) self.assertFalse( @@ -495,7 +495,7 @@ def test_debit_note_e_invoice_with_goods_item(self): self.assertDocumentEqual( {"name": test_data.get("response_data").get("result").get("Irn")}, - frappe.get_doc("e-Invoice Log", {"sales_invoice": debit_note.name}), + frappe.get_doc("e-Invoice Log", {"reference_name": debit_note.name}), ) self.assertFalse( From e92a697dec05534acff1e89d953842cb1a2cdc71 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 19 Sep 2024 19:13:03 +0530 Subject: [PATCH 25/29] fix: changes as per review (cherry picked from commit 589b19cd1e6dde9ec6b67bdc160ddccb6920f9bd) # Conflicts: # india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json --- .../gst_india/doctype/e_invoice_log/e_invoice_log.json | 5 +++++ india_compliance/patches.txt | 2 +- .../patches/post_install/update_e_invoice_fields_and_logs.py | 4 +++- .../patches/{v14 => v15}/make_e_invoice_log_extensible.py | 0 4 files changed, 9 insertions(+), 2 deletions(-) rename india_compliance/patches/{v14 => v15}/make_e_invoice_log_extensible.py (100%) diff --git a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json index 554665656..57c675ec5 100644 --- a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json +++ b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json @@ -118,6 +118,7 @@ { "fieldname": "reference_doctype", "fieldtype": "Link", + "in_list_view": 1, "in_standard_filter": 1, "label": "Reference Document Type", "options": "DocType", @@ -139,11 +140,15 @@ "link_fieldname": "irn" } ], +<<<<<<< HEAD <<<<<<< HEAD "modified": "2023-07-27 15:04:26.931722", ======= "modified": "2024-09-17 15:15:35.720311", >>>>>>> 279d5966 (fix: make e-Invoice log extensible) +======= + "modified": "2024-09-19 18:59:01.195753", +>>>>>>> 589b19cd (fix: changes as per review) "modified_by": "Administrator", "module": "GST India", "name": "e-Invoice Log", diff --git a/india_compliance/patches.txt b/india_compliance/patches.txt index dcd349706..a53eb6a5c 100644 --- a/india_compliance/patches.txt +++ b/india_compliance/patches.txt @@ -63,4 +63,4 @@ execute:from india_compliance.gst_india.setup import set_default_print_settings; india_compliance.patches.v15.migrate_gstr1_log_to_returns_log india_compliance.patches.v15.update_action_for_gst_inward_supply india_compliance.patches.v15.set_default_for_new_gst_category_notification -india_compliance.patches.v14.make_e_invoice_log_extensible \ No newline at end of file +india_compliance.patches.v15.make_e_invoice_log_extensible \ No newline at end of file diff --git a/india_compliance/patches/post_install/update_e_invoice_fields_and_logs.py b/india_compliance/patches/post_install/update_e_invoice_fields_and_logs.py index f6f7e27e4..0905f0fb6 100644 --- a/india_compliance/patches/post_install/update_e_invoice_fields_and_logs.py +++ b/india_compliance/patches/post_install/update_e_invoice_fields_and_logs.py @@ -94,7 +94,8 @@ def migrate_e_invoice_fields(): "owner", "modified_by", "irn", - "sales_invoice", + "reference_doctype", + "reference_name", "is_cancelled", "acknowledgement_number", "acknowledged_on", @@ -120,6 +121,7 @@ def migrate_e_invoice_fields(): user, user, doc.irn, + "Sales Invoice", doc.name, doc.irn_cancelled, doc.ack_no, diff --git a/india_compliance/patches/v14/make_e_invoice_log_extensible.py b/india_compliance/patches/v15/make_e_invoice_log_extensible.py similarity index 100% rename from india_compliance/patches/v14/make_e_invoice_log_extensible.py rename to india_compliance/patches/v15/make_e_invoice_log_extensible.py From 08065341c927a7602227561a740cceaec022107e Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Fri, 20 Sep 2024 11:35:08 +0530 Subject: [PATCH 26/29] fix: only check when doctype is stock entry (cherry picked from commit 860d74479818b233a612c71f6a8a0e59375e3abc) --- .../gst_india/overrides/subcontracting_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/gst_india/overrides/subcontracting_transaction.py b/india_compliance/gst_india/overrides/subcontracting_transaction.py index 7724ce106..a8732d44f 100644 --- a/india_compliance/gst_india/overrides/subcontracting_transaction.py +++ b/india_compliance/gst_india/overrides/subcontracting_transaction.py @@ -167,7 +167,7 @@ def validate(doc, method=None): if ignore_gst_validation_for_subcontracting(doc): return - if doc.get("purpose") != "Send to Subcontractor": + if doc.doctype == "Stock Entry" and doc.purpose != "Send to Subcontractor": return field_map = ( From 287fb8b183fee6827928f36b17b1c9c35631a5e0 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Fri, 20 Sep 2024 13:41:44 +0530 Subject: [PATCH 27/29] fix: resolve merge conflict --- .../gst_india/doctype/e_invoice_log/e_invoice_log.json | 8 -------- 1 file changed, 8 deletions(-) diff --git a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json index 57c675ec5..6d07db365 100644 --- a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json +++ b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.json @@ -140,15 +140,7 @@ "link_fieldname": "irn" } ], -<<<<<<< HEAD -<<<<<<< HEAD - "modified": "2023-07-27 15:04:26.931722", -======= - "modified": "2024-09-17 15:15:35.720311", ->>>>>>> 279d5966 (fix: make e-Invoice log extensible) -======= "modified": "2024-09-19 18:59:01.195753", ->>>>>>> 589b19cd (fix: changes as per review) "modified_by": "Administrator", "module": "GST India", "name": "e-Invoice Log", From 58ab922f8ce7912d734f0a578aef5b113196d86c Mon Sep 17 00:00:00 2001 From: ljain112 Date: Fri, 20 Sep 2024 20:47:43 +0530 Subject: [PATCH 28/29] fix: do not set options for non Autocomplete field (cherry picked from commit 18408e29bf16248f36ff67c17bcefbb40249742e) --- india_compliance/gst_india/client_scripts/party.js | 1 + 1 file changed, 1 insertion(+) diff --git a/india_compliance/gst_india/client_scripts/party.js b/india_compliance/gst_india/client_scripts/party.js index fdea52d27..f6e7931d5 100644 --- a/india_compliance/gst_india/client_scripts/party.js +++ b/india_compliance/gst_india/client_scripts/party.js @@ -149,6 +149,7 @@ async function set_gstin_options(frm) { frm._gstin_options_set_for = frm.doc.name; const field = frm.get_field("gstin"); + if (!field || field.df.fieldtype != "Autocomplete") return; field.df.ignore_validation = true; field.set_data(await india_compliance.get_gstin_options(frm.doc.name, frm.doctype)); } From 0e847ca31a9b6840d0344e8d007cf76e9535c096 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Sat, 21 Sep 2024 13:48:27 +0530 Subject: [PATCH 29/29] fix: connections for e-Invoice Log (cherry picked from commit 4ca0902d2c86fb53d97fa166d1ec285a7ed7b32c) --- india_compliance/gst_india/overrides/sales_invoice.py | 1 + 1 file changed, 1 insertion(+) diff --git a/india_compliance/gst_india/overrides/sales_invoice.py b/india_compliance/gst_india/overrides/sales_invoice.py index d41498245..1c02e333e 100644 --- a/india_compliance/gst_india/overrides/sales_invoice.py +++ b/india_compliance/gst_india/overrides/sales_invoice.py @@ -243,6 +243,7 @@ def update_dashboard_with_gst_logs(doctype, data, *log_doctypes): "e-Waybill Log": "reference_name", "Integration Request": "reference_docname", "GST Inward Supply": "link_name", + "e-Invoice Log": "reference_name", } )