From 69b556c82dc3b7452573c2238e7171e40910c192 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Thu, 5 Dec 2024 10:59:28 +0530 Subject: [PATCH 01/35] refactor: Update `e-Waybill Log` list view --- .../doctype/e_waybill_log/e_waybill_log.js | 4 +-- .../doctype/e_waybill_log/e_waybill_log.json | 3 ++- .../e_waybill_log/e_waybill_log_list.js | 26 +++++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) create mode 100644 india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log_list.js diff --git a/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.js b/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.js index 6752e48320..79f4086f8f 100644 --- a/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.js +++ b/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.js @@ -1,8 +1,8 @@ // Copyright (c) 2022, Resilient Tech and contributors // For license information, please see license.txt -frappe.ui.form.on('e-Waybill Log', { +// frappe.ui.form.on('e-Waybill Log', { // refresh: function(frm) { // } -}); +// }); diff --git a/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.json b/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.json index 1a2cb9b6d2..da5633c6c6 100644 --- a/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.json +++ b/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log.json @@ -99,6 +99,7 @@ { "fieldname": "reference_doctype", "fieldtype": "Link", + "in_list_view": 1, "in_standard_filter": 1, "label": "Reference Document Type", "options": "DocType", @@ -177,7 +178,7 @@ "link_fieldname": "ewaybill" } ], - "modified": "2024-07-17 12:26:38.069883", + "modified": "2024-12-05 09:50:37.014667", "modified_by": "Administrator", "module": "GST India", "name": "e-Waybill Log", diff --git a/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log_list.js b/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log_list.js new file mode 100644 index 0000000000..34d344b1fd --- /dev/null +++ b/india_compliance/gst_india/doctype/e_waybill_log/e_waybill_log_list.js @@ -0,0 +1,26 @@ +// Copyright (c) 2024, Resilient Tech and contributors +// For license information, please see license.txt + +frappe.listview_settings["e-Waybill Log"] = { + hide_name_column: true, + + button: { + show: function (doc) { + return doc.reference_name; + }, + + get_label: function () { + return __("Open Reference"); + }, + + get_description: function (doc) { + return __("Open {0}", [ + `${__(doc.reference_doctype)}: ${doc.reference_name}`, + ]); + }, + + action: function (doc) { + frappe.set_route("Form", doc.reference_doctype, doc.reference_name); + }, + }, +}; From ba431347a978596284843ca6ad78b1a390ac41e8 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhoda Date: Thu, 5 Dec 2024 11:07:25 +0530 Subject: [PATCH 02/35] refactor: Update `e-Invoice Log` list view --- .../doctype/e_invoice_log/e_invoice_log.js | 8 +++--- .../e_invoice_log/e_invoice_log_list.js | 26 +++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log_list.js diff --git a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.js b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.js index 0c181cb0b3..f9cf744010 100644 --- a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.js +++ b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log.js @@ -1,8 +1,8 @@ // Copyright (c) 2022, Resilient Tech and contributors // For license information, please see license.txt -frappe.ui.form.on('e-Invoice Log', { - // refresh: function(frm) { +// frappe.ui.form.on('e-Invoice Log', { +// refresh: function(frm) { - // } -}); +// } +// }); diff --git a/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log_list.js b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log_list.js new file mode 100644 index 0000000000..fc4cf9e480 --- /dev/null +++ b/india_compliance/gst_india/doctype/e_invoice_log/e_invoice_log_list.js @@ -0,0 +1,26 @@ +// Copyright (c) 2024, Resilient Tech and contributors +// For license information, please see license.txt + +frappe.listview_settings["e-Invoice Log"] = { + hide_name_column: true, + + button: { + show: function (doc) { + return doc.reference_name; + }, + + get_label: function () { + return __("Open Reference"); + }, + + get_description: function (doc) { + return __("Open {0}", [ + `${__(doc.reference_doctype)}: ${doc.reference_name}`, + ]); + }, + + action: function (doc) { + frappe.set_route("Form", doc.reference_doctype, doc.reference_name); + }, + }, +}; From f10e55957f15428a03ed1cde05d3c749d64d1b9f Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:26:13 +0530 Subject: [PATCH 03/35] test: additional tests recommendation for sub contracting order (#2783) * test: coverage for onload method * test: validate doc references obtained from function call * fix: minor refactor * test: validate workflow for unregistered company * test: validate stock entry for different purpose and stock entry validations * fix: validate standalone stock entries * fix: better error strings * fix: minor fix * fix: company gstin instead of bill from gstin * fix: validate e-Waybill expiry --- .../test_subcontracting_transaction.py | 195 ++++++++++++++---- .../gst_india/utils/test_e_waybill.py | 26 ++- india_compliance/gst_india/utils/tests.py | 2 + 3 files changed, 181 insertions(+), 42 deletions(-) diff --git a/india_compliance/gst_india/overrides/test_subcontracting_transaction.py b/india_compliance/gst_india/overrides/test_subcontracting_transaction.py index 2fcb536d38..f7414d5e91 100644 --- a/india_compliance/gst_india/overrides/test_subcontracting_transaction.py +++ b/india_compliance/gst_india/overrides/test_subcontracting_transaction.py @@ -1,7 +1,7 @@ import re import frappe -from frappe.tests import IntegrationTestCase, change_settings +from frappe.tests import IntegrationTestCase from erpnext.controllers.subcontracting_controller import ( get_materials_from_supplier, make_rm_stock_entry, @@ -102,25 +102,14 @@ def make_item(item_code=None, properties=None): def create_purchase_order(**args): - po_dict = { - "doctype": "Purchase Order", - "supplier": args.get("supplier") or "_Test Registered Supplier", - "is_subcontracted": 1, - "items": args.get("items"), - "supplier_warehouse": "Finished Goods - _TIRC", - "do_not_save": 1, - "do_not_submit": 1, - } - - po = create_transaction(**po_dict) - - if po.is_subcontracted: - supp_items = po.get("supplied_items") - for d in supp_items: - if not d.reserve_warehouse: - d.reserve_warehouse = "Stores - _TIRC" + args.update( + { + "doctype": "Purchase Order", + "is_subcontracted": 1, + } + ) - return po.submit() + return create_transaction(**args) def make_stock_transfer_entry(**args): @@ -145,8 +134,10 @@ def make_stock_transfer_entry(**args): ste_dict = make_rm_stock_entry(args.sco_no, items) ste_dict.update( { - "bill_from_address": "_Test Indian Registered Company-Billing", - "bill_to_address": "_Test Registered Supplier-Billing", + "bill_from_address": args.bill_from_address + or "_Test Indian Registered Company-Billing", + "bill_to_address": args.bill_to_address + or "_Test Registered Supplier-Billing", } ) @@ -156,6 +147,38 @@ def make_stock_transfer_entry(**args): return doc.submit() +def make_stock_entry(**args): + items = [ + { + "item_code": "_Test Trading Goods 1", + "qty": 1, + "s_warehouse": args.get("from_warehouse") or "Stores - _TIRC", + "t_warehouse": args.get("to_warehouse") or "Finished Goods - _TIRC", + "amount": 100, + } + ] + se = frappe.new_doc("Stock Entry") + se.update( + { + "purpose": args.get("purpose") or "Material Receipt", + "stock_entry_type": args.get("purpose") or "Material Receipt", + "company": args.get("company") or "_Test Indian Registered Company", + "items": args.get("items") or items, + } + ) + + return se + + +SERVICE_ITEM = { + "item_code": "Subcontracted Service Item 1", + "qty": 10, + "rate": 100, + "fg_item": "Subcontracted Item SA1", + "fg_item_qty": 10, +} + + class TestSubcontractingTransaction(IntegrationTestCase): @classmethod def setUpClass(cls): @@ -165,6 +188,15 @@ def setUpClass(cls): make_subcontracted_items() make_boms() + frappe.db.set_single_value( + "GST Settings", + { + "enable_api": 1, + "enable_e_waybill": 1, + "enable_e_waybill_for_sc": 1, + }, + ) + def _create_stock_entry(self, doc_args): """Generate Stock Entry to test e-Waybill functionalities""" doc_args.update({"doctype": "Stock Entry"}) @@ -176,7 +208,6 @@ def test_create_and_update_stock_entry(self): # Create a subcontracting transaction args = { "stock_entry_type": "Send to Subcontractor", - "purpose": "Send to Subcontractor", "bill_from_address": "_Test Indian Registered Company-Billing", "bill_to_address": "_Test Registered Supplier-Billing", "items": [ @@ -187,11 +218,9 @@ def test_create_and_update_stock_entry(self): "s_warehouse": "Finished Goods - _TIRC", "t_warehouse": "Goods In Transit - _TIRC", "amount": 100, - "taxable_value": 100, } ], "company": "_Test Indian Registered Company", - "base_grand_total": 100, } stock_entry = self._create_stock_entry(args) @@ -203,19 +232,88 @@ def test_create_and_update_stock_entry(self): self.assertEqual(stock_entry.select_print_heading, "Credit Note") + def test_for_unregistered_company(self): + po = create_purchase_order( + company="_Test Indian Unregistered Company", + supplier_warehouse="Finished Goods - _TIUC", + **SERVICE_ITEM, + ) + + sco = create_subcontracting_order(po_name=po.name) + self.assertEqual(sco.total_taxes, None) + + rm_items = get_rm_items(sco.supplied_items) + args = { + "sco_no": sco.name, + "rm_items": rm_items, + "bill_from_address": "_Test Indian Unregistered Company-Billing", + "bill_to_address": "_Test Unregistered Supplier-Billing", + } + se = make_stock_transfer_entry(**args) + self.assertEqual(se.total_taxes, 0.0) + + scr = make_subcontracting_receipt(sco.name) + scr.submit() + self.assertEqual(scr.total_taxes, 0.0) + + def test_stock_entry_for_material_receipt(self): + se = make_stock_entry() + se.save() + + self.assertEqual(se.total_taxes, None) + + def test_subcontracting_validations(self): + po = create_purchase_order( + **SERVICE_ITEM, supplier_warehouse="Finished Goods - _TIRC" + ) + sco = create_subcontracting_order(po_name=po.name) + + rm_items = get_rm_items(sco.supplied_items) + make_stock_transfer_entry(sco_no=sco.name, rm_items=rm_items) + + scr = make_subcontracting_receipt(sco.name) + scr.save() + + scr.billing_address = None + self.assertRaisesRegex( + frappe.ValidationError, + re.compile(r"(to ensure Company GSTIN is fetched in the transaction.$)"), + scr.save, + ) + + scr.reload() + self.assertEqual(scr.total_taxes, 252.0) + + def test_standalone_stock_entry(self): + purpose = "Send to Subcontractor" + se = make_stock_entry(purpose=purpose) + + self.assertRaisesRegex( + frappe.ValidationError, + re.compile(r"(to ensure Company GSTIN is fetched in the transaction.$)"), + se.save, + ) + + se.bill_from_address = "_Test Indian Registered Company-Billing" + + self.assertRaisesRegex( + frappe.ValidationError, + re.compile(r"(.*GST Category is a mandatory field for GST Transactions.*)"), + se.save, + ) + + se.bill_to_address = "_Test Registered Supplier-Billing" + + se.save() + def test_validation_for_doc_references(self): - service_item = [ - { - "warehouse": "Stores - _TIRC", - "item_code": "Subcontracted Service Item 1", - "qty": 10, - "rate": 100, - "fg_item": "Subcontracted Item SA1", - "fg_item_qty": 10, - } - ] + from india_compliance.gst_india.overrides.subcontracting_transaction import ( + get_stock_entry_references, + ) - po = create_purchase_order(items=service_item) + po = create_purchase_order( + **SERVICE_ITEM, supplier_warehouse="Finished Goods - _TIRC" + ) sco = create_subcontracting_order(po_name=po.name) rm_items = get_rm_items(sco.supplied_items) @@ -227,6 +325,7 @@ def test_validation_for_doc_references(self): return_se.save() scr = make_subcontracting_receipt(sco.name) + scr.save() scr.submit() self.assertRaisesRegex( @@ -236,16 +335,25 @@ def test_validation_for_doc_references(self): ) return_se.reload() + + filters = { + "supplier": return_se.supplier, + "supplied_items": [d.item_code for d in return_se.items], + "subcontracting_orders": [return_se.subcontracting_order], + } + doc_references_data = get_stock_entry_references( + filters=filters, only_linked_references=True + ) + doc_references = [row[0] for row in doc_references_data] + + self.assertTrue(se.name in doc_references) + return_se.append( "doc_references", {"link_doctype": "Stock Entry", "link_name": se.name}, ) return_se.submit() - @change_settings( - "GST Settings", - {"enable_api": 1, "enable_e_waybill": 1, "enable_e_waybill_for_sc": 1}, - ) def test_validation_when_gstin_field_empty(self): service_item = [ { @@ -259,7 +367,12 @@ def test_validation_when_gstin_field_empty(self): ] po = create_purchase_order( - items=service_item, supplier="_Test Unregistered Supplier" + items=service_item, + supplier="_Test Unregistered Supplier", + supplier_warhouse="Finished Goods - _TIUC", ) - create_subcontracting_order(po_name=po.name) + sco = create_subcontracting_order(po_name=po.name, do_not_save=True) + sco.supplier_warehouse = "Finished Goods - _TIUC" + sco.save() + sco.submit() diff --git a/india_compliance/gst_india/utils/test_e_waybill.py b/india_compliance/gst_india/utils/test_e_waybill.py index 53bffa414f..e5ce3f7e79 100644 --- a/india_compliance/gst_india/utils/test_e_waybill.py +++ b/india_compliance/gst_india/utils/test_e_waybill.py @@ -15,7 +15,7 @@ from erpnext.controllers.sales_and_purchase_return import make_return_doc from india_compliance.gst_india.api_classes.base import BASE_URL -from india_compliance.gst_india.utils import load_doc +from india_compliance.gst_india.utils import load_doc, parse_datetime from india_compliance.gst_india.utils.e_invoice import ( retry_e_invoice_e_waybill_generation, ) @@ -1048,6 +1048,18 @@ def test_e_waybill_for_stock_entry(self): frappe.get_doc("e-Waybill Log", {"reference_name": stock_entry.name}), ) + stock_entry = load_doc("Stock Entry", stock_entry.name, "submit") + + e_waybill_info = stock_entry.get("__onload").e_waybill_info + + self.assertEqual( + e_waybill_info.valid_upto, + parse_datetime( + se_data.get("response_data").get("result").get("validUpto"), + day_first=True, + ), + ) + @change_settings("GST Settings", {"enable_e_waybill_for_sc": 1}) @responses.activate def test_e_waybill_for_stock_entry_same_gstin(self): @@ -1063,6 +1075,18 @@ def test_e_waybill_for_stock_entry_same_gstin(self): frappe.get_doc("e-Waybill Log", {"reference_name": stock_entry.name}), ) + stock_entry = load_doc("Stock Entry", stock_entry.name, "submit") + + e_waybill_info = stock_entry.get("__onload").e_waybill_info + + self.assertEqual( + e_waybill_info.valid_upto, + parse_datetime( + se_data.get("response_data").get("result").get("validUpto"), + day_first=True, + ), + ) + def update_dates_for_test_data(test_data): """Update dates in test data""" diff --git a/india_compliance/gst_india/utils/tests.py b/india_compliance/gst_india/utils/tests.py index ceb3bd8ae4..c773514397 100644 --- a/india_compliance/gst_india/utils/tests.py +++ b/india_compliance/gst_india/utils/tests.py @@ -108,6 +108,8 @@ def append_item(transaction, data=None, company_abbr="_TIRC"): "warehouse": f"Stores - {company_abbr}", "expense_account": f"Cost of Goods Sold - {company_abbr}", "taxable_value": data.taxable_value or 0, + "fg_item": data.fg_item, + "fg_item_qty": data.fg_item_qty, }, ) From ad75fb432d11db8ae68b95c5b8f358de8fb1ee92 Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Thu, 5 Dec 2024 13:45:40 +0530 Subject: [PATCH 04/35] fix: Only Validate Taxes if e-Waybill Applicable (#2803) * fix: better naming * fix: only validate taxes if e-waybill applicable * refactor: remove redundent function declaration --- .../gst_india/client_scripts/stock_entry.js | 5 ----- .../client_scripts/subcontracting_order.js | 5 ----- .../client_scripts/subcontracting_receipt.js | 13 +++---------- .../gst_india/constants/custom_fields.py | 8 ++++---- .../overrides/subcontracting_transaction.py | 18 +++++++++++------- india_compliance/hooks.py | 9 +++++++-- india_compliance/public/js/utils.js | 2 +- 7 files changed, 26 insertions(+), 34 deletions(-) diff --git a/india_compliance/gst_india/client_scripts/stock_entry.js b/india_compliance/gst_india/client_scripts/stock_entry.js index a1ac3fd457..f5d2a70a0f 100644 --- a/india_compliance/gst_india/client_scripts/stock_entry.js +++ b/india_compliance/gst_india/client_scripts/stock_entry.js @@ -56,11 +56,6 @@ frappe.ui.form.on(DOCTYPE, { "supplier_address", ...get_field_and_label(frm, "party_field") ); - - frm.get_docfield("taxes", "charge_type").options = [ - "On Net Total", - "On Item Quantity", - ]; }, refresh(frm) { diff --git a/india_compliance/gst_india/client_scripts/subcontracting_order.js b/india_compliance/gst_india/client_scripts/subcontracting_order.js index 9ea8504838..e2f463453d 100644 --- a/india_compliance/gst_india/client_scripts/subcontracting_order.js +++ b/india_compliance/gst_india/client_scripts/subcontracting_order.js @@ -13,11 +13,6 @@ frappe.ui.form.on("Subcontracting Order", { frm.taxes_controller = new india_compliance.taxes_controller(frm, { total_taxable_value: "total", }); - - frm.get_docfield("taxes", "charge_type").options = [ - "On Net Total", - "On Item Quantity", - ]; }, taxes_and_charges(frm) { diff --git a/india_compliance/gst_india/client_scripts/subcontracting_receipt.js b/india_compliance/gst_india/client_scripts/subcontracting_receipt.js index f0038c4d08..789a330913 100644 --- a/india_compliance/gst_india/client_scripts/subcontracting_receipt.js +++ b/india_compliance/gst_india/client_scripts/subcontracting_receipt.js @@ -42,28 +42,21 @@ frappe.ui.form.on(DOCTYPE, { ...filters, supplied_items: get_supplied_items(doc), }, - } - + }; else if (row.link_doctype == "Subcontracting Receipt") return { query: "india_compliance.gst_india.overrides.subcontracting_transaction.get_subcontracting_receipt_references", filters: { ...filters, received_items: get_received_items(doc), - } - } - + }, + }; }); }, onload(frm) { frm.taxes_controller = new india_compliance.taxes_controller(frm, { total_taxable_value: "total", }); - - frm.get_docfield("taxes", "charge_type").options = [ - "On Net Total", - "On Item Quantity", - ]; }, refresh() { diff --git a/india_compliance/gst_india/constants/custom_fields.py b/india_compliance/gst_india/constants/custom_fields.py index 6fed6fc111..f451ee7ccf 100644 --- a/india_compliance/gst_india/constants/custom_fields.py +++ b/india_compliance/gst_india/constants/custom_fields.py @@ -56,7 +56,7 @@ "fieldtype": "Section Break", "insert_after": "total", "hide_border": 1, - "depends_on": "eval: doc.purchase_order && india_compliance.is_e_waybill_generatable_for_subcontracting(doc)", + "depends_on": "eval: doc.purchase_order && india_compliance.is_e_waybill_applicable_for_subcontracting(doc)", }, ], "Subcontracting Receipt": [ @@ -66,7 +66,7 @@ "fieldtype": "Section Break", "insert_after": "total", "hide_border": 1, - "depends_on": "eval: india_compliance.is_e_waybill_generatable_for_subcontracting(doc)", + "depends_on": "eval: india_compliance.is_e_waybill_applicable_for_subcontracting(doc)", }, { "fieldname": "section_break_ref_doc", @@ -182,14 +182,14 @@ "label": "Taxes", "fieldtype": "Section Break", "insert_after": "get_stock_and_rate", - "depends_on": "eval: india_compliance.is_e_waybill_generatable_for_subcontracting(doc)", + "depends_on": "eval: india_compliance.is_e_waybill_applicable_for_subcontracting(doc)", }, { "label": "E-Waybill Info", "fieldname": "tab_break_ewaybill", "fieldtype": "Tab Break", "insert_after": "address_display", - "depends_on": "eval: india_compliance.is_e_waybill_generatable_for_subcontracting(doc)", + "depends_on": "eval: india_compliance.is_e_waybill_applicable_for_subcontracting(doc)", }, { "label": "e-Waybill Address", diff --git a/india_compliance/gst_india/overrides/subcontracting_transaction.py b/india_compliance/gst_india/overrides/subcontracting_transaction.py index 4c9f9db280..aab00c6b72 100644 --- a/india_compliance/gst_india/overrides/subcontracting_transaction.py +++ b/india_compliance/gst_india/overrides/subcontracting_transaction.py @@ -205,20 +205,24 @@ def validate(doc, method=None): def before_save(doc, method=None): - if ignore_gst_validations(doc): + if not is_e_waybill_applicable(doc): + doc.taxes_and_charges = "" + doc.taxes = [] return - validate_doc_references(doc) + for row in doc.taxes: + if row.charge_type == "Actual": + frappe.throw( + _( + "Tax Row #{0}: Charge Type cannot be {1}. Try setting it to 'On Net Total' or 'On Item Quantity'." + ).format(row.idx, bold(row.charge_type)) + ) -def before_submit(doc, method=None): +def validate_doc_references(doc, method=None): if ignore_gst_validations(doc): return - validate_doc_references(doc) - - -def validate_doc_references(doc): is_return_material_transfer = ( doc.doctype == "Stock Entry" and doc.purpose == "Material Transfer" diff --git a/india_compliance/hooks.py b/india_compliance/hooks.py index 6adae4196b..c03d87911a 100644 --- a/india_compliance/hooks.py +++ b/india_compliance/hooks.py @@ -232,17 +232,22 @@ "Stock Entry": { "onload": "india_compliance.gst_india.overrides.subcontracting_transaction.onload", "validate": "india_compliance.gst_india.overrides.subcontracting_transaction.validate", - "before_submit": "india_compliance.gst_india.overrides.subcontracting_transaction.before_submit", + "before_save": "india_compliance.gst_india.overrides.subcontracting_transaction.before_save", + "before_submit": "india_compliance.gst_india.overrides.subcontracting_transaction.validate_doc_references", "after_mapping": "india_compliance.gst_india.overrides.subcontracting_transaction.after_mapping_stock_entry", }, "Subcontracting Order": { "validate": "india_compliance.gst_india.overrides.subcontracting_transaction.validate", + "before_save": "india_compliance.gst_india.overrides.subcontracting_transaction.before_save", "after_mapping": "india_compliance.gst_india.overrides.subcontracting_transaction.after_mapping_subcontracting_order", }, "Subcontracting Receipt": { "onload": "india_compliance.gst_india.overrides.subcontracting_transaction.onload", "validate": "india_compliance.gst_india.overrides.subcontracting_transaction.validate", - "before_save": "india_compliance.gst_india.overrides.subcontracting_transaction.before_save", + "before_save": [ + "india_compliance.gst_india.overrides.subcontracting_transaction.before_save", + "india_compliance.gst_india.overrides.subcontracting_transaction.validate_doc_references", + ], "before_mapping": "india_compliance.gst_india.overrides.subcontracting_transaction.before_mapping_subcontracting_receipt", }, "Supplier": { diff --git a/india_compliance/public/js/utils.js b/india_compliance/public/js/utils.js index a23cede8e3..1dd9dd6e93 100644 --- a/india_compliance/public/js/utils.js +++ b/india_compliance/public/js/utils.js @@ -470,7 +470,7 @@ Object.assign(india_compliance, { return alert; }, - is_e_waybill_generatable_for_subcontracting(doc) { + is_e_waybill_applicable_for_subcontracting(doc) { if ( !( gst_settings.enable_api && From 9c72e059fb1fcd8d8e6589d2853d165e69095ce0 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Thu, 5 Dec 2024 17:25:25 +0530 Subject: [PATCH 05/35] fix: avoid duplicate taxes when same name --- .../gst_india/doctype/gstr_3b_report/gstr_3b_report.py | 1 + .../gst_india/report/gstr_3b_details/gstr_3b_details.py | 1 + 2 files changed, 2 insertions(+) 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 00906762d8..363e535398 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 @@ -295,6 +295,7 @@ def _get_tax_amount(account_type): & boe.docstatus.eq(1) & boe_taxes.gst_tax_type.eq(account_type) ) + .where(boe_taxes.parenttype == "Bill of Entry") .run() )[0][0] or 0 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 6e4d88a2cf..aa6dd5470d 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 @@ -208,6 +208,7 @@ def get_itc_from_boe(self): & (boe.company == self.company) & (boe.company_gstin == self.company_gstin) ) + .where(boe_taxes.parenttype == "Bill of Entry") .groupby(boe.name) ) From acedac0c3c33cd4e313469f102a326b8628ac797 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Thu, 5 Dec 2024 17:26:58 +0530 Subject: [PATCH 06/35] fix: update custom fields --- india_compliance/patches.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/patches.txt b/india_compliance/patches.txt index 42d31d02a7..e15618118d 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() #59 +execute:from india_compliance.gst_india.setup import create_custom_fields; create_custom_fields() #60 execute:from india_compliance.gst_india.setup import create_property_setters; create_property_setters() #10 execute:from india_compliance.income_tax_india.setup import create_custom_fields; create_custom_fields() #2 india_compliance.patches.post_install.remove_old_fields #2 From a82287c2135667047db93d61f09e1f8f6b900a65 Mon Sep 17 00:00:00 2001 From: Abdeali Chharchhodawala <99460106+Abdeali099@users.noreply.github.com> Date: Thu, 5 Dec 2024 19:59:19 +0530 Subject: [PATCH 07/35] fix: handle `Actions` button for smaller screens (#2806) * fix: Hide `Actions` text on small screens * fix: Handle translation * fix: Check translated text for Actions group button * fix: Show dot-horizontal icon on smaller screen for Actions * fix: Check button element innerText for Actions * fix: Remove `Actions` from page actions menu * fix: Hide custom `Actions` in smaller screen --- .../purchase_reconciliation_tool.js | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 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 d4aaaaaf8e..bfe0b7461f 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 @@ -16,12 +16,13 @@ const ALERT_HTML = `
You have missing GSTR-2B downloads
- ${api_enabled - ? ` + ${ + api_enabled + ? ` Download 2B ` - : "" - } + : "" + } `; @@ -98,28 +99,30 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { frm.save(); }); + const action_group = __("Actions"); + // add custom buttons api_enabled ? frm.add_custom_button(__("Download 2A/2B"), () => new ImportDialog(frm)) : frm.add_custom_button( - __("Upload 2A/2B"), - () => new ImportDialog(frm, false) - ); + __("Upload 2A/2B"), + () => new ImportDialog(frm, false) + ); if (!frm.purchase_reconciliation_tool?.data?.length) return; if (frm.get_active_tab()?.df.fieldname == "invoice_tab") { frm.add_custom_button( __("Unlink"), () => unlink_documents(frm), - __("Actions") + action_group ); - frm.add_custom_button(__("dropdown-divider"), () => { }, __("Actions")); + frm.add_custom_button(__("dropdown-divider"), () => {}, action_group); } ["Accept", "Pending", "Ignore"].forEach(action => frm.add_custom_button( __(action), () => apply_action(frm, action), - __("Actions") + action_group ) ); frm.$wrapper @@ -132,10 +135,16 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { ); // move actions button next to filters - for (let button of $(".custom-actions .inner-group-button")) { - if (button.innerText?.trim() != "Actions") continue; + for (const group_div of $(".custom-actions .inner-group-button")) { + const btn_label = group_div.querySelector("button").innerText?.trim(); + if (btn_label != action_group) continue; + $(".custom-button-group .inner-group-button").remove(); - $(button).appendTo($(".custom-button-group")); + + // to hide `Actions` button group on smaller screens + $(group_div).addClass("hidden-md"); + + $(group_div).appendTo($(".custom-button-group")); } }, @@ -192,8 +201,8 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { method == "update_api_progress" ? __("Fetching data from GSTN") : __("Updating Inward Supply for Return Period {0}", [ - data.return_period, - ]); + data.return_period, + ]); frm.dashboard.show_progress( "Import GSTR Progress", @@ -921,8 +930,9 @@ class DetailViewDialog { ? ["GST Inward Supply"] : ["Purchase Invoice", "Bill of Entry"], - read_only_depends_on: `eval: ${this.missing_doctype == "GST Inward Supply" - }`, + read_only_depends_on: `eval: ${ + this.missing_doctype == "GST Inward Supply" + }`, onchange: () => { const doctype = this.dialog.get_value("doctype"); From 8f01a4a26d60f1361c68e9e9d1f0fe89d223ed91 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Fri, 6 Dec 2024 16:23:51 +0530 Subject: [PATCH 08/35] test: fix test cases and related valiation for invalid item tax tempate --- india_compliance/gst_india/overrides/test_transaction.py | 2 +- india_compliance/gst_india/overrides/transaction.py | 5 ++++- india_compliance/gst_india/utils/__init__.py | 9 +++++++++ 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/india_compliance/gst_india/overrides/test_transaction.py b/india_compliance/gst_india/overrides/test_transaction.py index 167efe78f5..ed49128292 100644 --- a/india_compliance/gst_india/overrides/test_transaction.py +++ b/india_compliance/gst_india/overrides/test_transaction.py @@ -589,7 +589,7 @@ def test_purchase_from_unregistered_supplier(self): def test_invalid_charge_type_as_actual(self): doc = create_transaction(**self.transaction_details, do_not_save=True) - _append_taxes(doc, ["CGST", "SGST"], charge_type="Actual", tax_amount=9) + _append_taxes(doc, ["CGST", "SGST"], charge_type="Actual", tax_amount=9, rate=0) self.assertRaisesRegex( frappe.exceptions.ValidationError, diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index e42d94912b..6e953bddff 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -23,6 +23,7 @@ from india_compliance.gst_india.doctype.gstin.gstin import get_and_validate_gstin_status from india_compliance.gst_india.utils import ( get_all_gst_accounts, + get_gst_account_by_item_tax_template, get_gst_account_gst_tax_type_map, get_gst_accounts_by_type, get_hsn_settings, @@ -545,8 +546,10 @@ def validate_missing_accounts_in_item_tax_template(self): if not row.item_tax_template: continue + template_rows = get_gst_account_by_item_tax_template(row.item_tax_template) + for account in self.used_accounts: - if account in row.item_tax_rate: + if account in template_rows: continue frappe.msgprint( diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index 37974602a1..ae838bbadb 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -568,6 +568,15 @@ def get_gst_accounts_by_tax_type(company, tax_type, throw=True): ) +@frappe.request_cache +def get_gst_account_by_item_tax_template(item_tax_template): + return frappe.get_all( + "Item Tax Template Detail", + filters={"parent": item_tax_template}, + pluck="tax_type", + ) + + def get_gst_account_gst_tax_type_map(): """ - Returns gst_account by tax_type for all the companies From 5f16ec2cf994ce8d03dc1c504533fe20945aa354 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Fri, 6 Dec 2024 16:39:35 +0530 Subject: [PATCH 09/35] test: partial match of error message --- .../gst_india/overrides/test_subcontracting_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/gst_india/overrides/test_subcontracting_transaction.py b/india_compliance/gst_india/overrides/test_subcontracting_transaction.py index f7414d5e91..ed61d4243f 100644 --- a/india_compliance/gst_india/overrides/test_subcontracting_transaction.py +++ b/india_compliance/gst_india/overrides/test_subcontracting_transaction.py @@ -298,7 +298,7 @@ def test_standalone_stock_entry(self): self.assertRaisesRegex( frappe.ValidationError, - re.compile(r"(.*GST Category is a mandatory field for GST Transactions.*)"), + re.compile(r"(.*is a mandatory field for GST Transactions.*)"), se.save, ) From c95f60da39cac1d8ed90f8609ad1ded0ca1b0e5d Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Mon, 9 Dec 2024 14:09:23 +0530 Subject: [PATCH 10/35] fix: use bulk insert to ignore validations --- .../v15/migrate_boe_taxes_to_ic_taxes.py | 23 +++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/india_compliance/patches/v15/migrate_boe_taxes_to_ic_taxes.py b/india_compliance/patches/v15/migrate_boe_taxes_to_ic_taxes.py index ad0cbe2ea5..36f36f9161 100644 --- a/india_compliance/patches/v15/migrate_boe_taxes_to_ic_taxes.py +++ b/india_compliance/patches/v15/migrate_boe_taxes_to_ic_taxes.py @@ -1,4 +1,6 @@ import frappe +from frappe.model.document import bulk_insert +from frappe.model.naming import _generate_random_string def execute(): @@ -8,16 +10,33 @@ def execute(): boe_taxes = frappe.qb.DocType("Bill of Entry Taxes") boe_taxes_docs = frappe.qb.from_(boe_taxes).select("*").run(as_dict=True) + ic_taxes_names = set( + frappe.get_all("India Compliance Taxes and Charges", pluck="name") + ) + ic_taxes = [] + for doc in boe_taxes_docs: ic_taxes_doc = frappe.get_doc( { **doc, "doctype": "India Compliance Taxes and Charges", - "name": None, + "name": set_name(doc.name, ic_taxes_names), "base_total": doc.total, } ) - ic_taxes_doc.insert(ignore_if_duplicate=True) + + ic_taxes.append(ic_taxes_doc) + + bulk_insert("India Compliance Taxes and Charges", ic_taxes) # Drop the old table frappe.db.delete("Bill of Entry Taxes") + + +def set_name(name, names): + new_name = name + while new_name in names: + new_name = _generate_random_string(10) + + names.add(new_name) + return new_name From fb7a785cdd65e2aa1d2cc8ac72da45dde804b9fc Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 9 Dec 2024 16:08:14 +0530 Subject: [PATCH 11/35] fix: taxable value for invoice without qty and value --- .../gst_india/overrides/transaction.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 6e953bddff..bdc75d0ae7 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -69,6 +69,7 @@ def update_taxable_values(doc): total_charges = 0 apportioned_charges = 0 tax_witholding_amount = 0 + has_no_qty_value = False if doc.taxes: if any( @@ -100,8 +101,10 @@ def update_taxable_values(doc): # base net total may be zero if invoice has zero rated items + shipping total_value = doc.base_net_total if doc.base_net_total else doc.total_qty + # credit note without item qty and value but with charges if not total_value: - return + total_value = len(doc.items) + has_no_qty_value = True for item in doc.items: item.taxable_value = item.base_net_amount @@ -109,7 +112,12 @@ def update_taxable_values(doc): if not total_charges: continue - proportionate_value = item.base_net_amount if doc.base_net_total else item.qty + if has_no_qty_value: + proportionate_value = 1 + elif doc.base_net_total: + proportionate_value = item.base_net_amount + else: + proportionate_value = item.qty applicable_charges = flt( proportionate_value * (total_charges / total_value), @@ -498,7 +506,7 @@ def validate_for_charge_type(self): ) if row.charge_type == "On Previous Row Total": - previous_row_references.add(row.row_id) + previous_row_references.add(flt(row.row_id)) # validating charge type "On Item Quantity" and non_cess_advol_account self.validate_charge_type_for_cess_non_advol_accounts(row) From 3051b0ea884107f736ed1b6ea749e0840518e5ff Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Tue, 10 Dec 2024 11:52:36 +0530 Subject: [PATCH 12/35] fix: change regional override as per erpnext changes --- india_compliance/gst_india/overrides/payment_entry.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/india_compliance/gst_india/overrides/payment_entry.py b/india_compliance/gst_india/overrides/payment_entry.py index b3dc948061..1dd7c35b77 100644 --- a/india_compliance/gst_india/overrides/payment_entry.py +++ b/india_compliance/gst_india/overrides/payment_entry.py @@ -332,6 +332,7 @@ def get_advance_payment_entries_for_regional( party_account, order_doctype, order_list=None, + default_advance_account=None, include_unallocated=True, against_all_orders=False, limit=None, @@ -347,6 +348,7 @@ def get_advance_payment_entries_for_regional( party_account=party_account, order_doctype=order_doctype, order_list=order_list, + default_advance_account=default_advance_account, include_unallocated=include_unallocated, against_all_orders=against_all_orders, limit=limit, From ae4792e4d845f361df38090cf9d209c5e973a1d1 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Wed, 11 Dec 2024 10:33:09 +0530 Subject: [PATCH 13/35] fix: correct categorisation of is_export and fetching taxes accordingly --- india_compliance/gst_india/overrides/transaction.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/india_compliance/gst_india/overrides/transaction.py b/india_compliance/gst_india/overrides/transaction.py index 6e953bddff..0e7cc489f9 100644 --- a/india_compliance/gst_india/overrides/transaction.py +++ b/india_compliance/gst_india/overrides/transaction.py @@ -917,7 +917,12 @@ def get_gst_details(party_details, doctype, company, *, update_place_of_supply=F == party_details.get(party_gstin_field) ) # Internal transfer ) - or (is_sales_transaction and is_export_without_payment_of_gst(party_details)) + or ( + is_sales_transaction + and is_export_without_payment_of_gst( + frappe._dict({**party_details, "doctype": doctype}) + ) + ) or ( not is_sales_transaction and ( From fbb6d2eb522f766fa5e70fb85106a0a5aa27aa1a Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Wed, 11 Dec 2024 22:47:02 +0530 Subject: [PATCH 14/35] test: added the test case --- .../gst_india/overrides/test_transaction.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/india_compliance/gst_india/overrides/test_transaction.py b/india_compliance/gst_india/overrides/test_transaction.py index ed49128292..b4299d00ea 100644 --- a/india_compliance/gst_india/overrides/test_transaction.py +++ b/india_compliance/gst_india/overrides/test_transaction.py @@ -466,6 +466,41 @@ def test_taxable_value_with_charges_after_tax(self): doc.insert() self.assertDocumentEqual({"taxable_value": 100}, doc.items[0]) + def test_credit_not_without_quantity(self): + if self.doctype != "Sales Invoice": + return + + doc = create_transaction( + **self.transaction_details, is_return=True, do_not_save=True + ) + append_item(doc) + + for item in doc.items: + item.qty = 0 + item.rate = 0 + item.price_list_rate = 0 + + # Adding charges + doc.append( + "taxes", + { + "charge_type": "Actual", + "account_head": "Freight and Forwarding Charges - _TIRC", + "description": "Freight", + "tax_amount": 20, + "cost_center": "Main - _TIRC", + }, + ) + + # Adding taxes + _append_taxes( + doc, ("CGST", "SGST"), charge_type="On Previous Row Total", row_id=1 + ) + doc.insert() + + for item in doc.items: + self.assertDocumentEqual({"taxable_value": 10}, item) + def test_validate_place_of_supply(self): doc = create_transaction(**self.transaction_details, do_not_save=True) doc.place_of_supply = "96-Others" From 159ed7573494dc0c89798b4eb16e68ce02b1b221 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 12 Dec 2024 17:44:45 +0530 Subject: [PATCH 15/35] test: additionally test gst details for credit note with zero value --- india_compliance/gst_india/overrides/test_transaction.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/india_compliance/gst_india/overrides/test_transaction.py b/india_compliance/gst_india/overrides/test_transaction.py index b4299d00ea..4493116b29 100644 --- a/india_compliance/gst_india/overrides/test_transaction.py +++ b/india_compliance/gst_india/overrides/test_transaction.py @@ -466,7 +466,7 @@ def test_taxable_value_with_charges_after_tax(self): doc.insert() self.assertDocumentEqual({"taxable_value": 100}, doc.items[0]) - def test_credit_not_without_quantity(self): + def test_credit_note_without_quantity(self): if self.doctype != "Sales Invoice": return @@ -498,8 +498,11 @@ def test_credit_not_without_quantity(self): ) doc.insert() + # Ensure correct taxable_value and gst details for item in doc.items: - self.assertDocumentEqual({"taxable_value": 10}, item) + self.assertDocumentEqual( + {"taxable_value": 10, "cgst_amount": 0.9, "sgst_amount": 0.9}, item + ) def test_validate_place_of_supply(self): doc = create_transaction(**self.transaction_details, do_not_save=True) From 49137584050e360c748067cca4d8cffa4cb2d22c Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Thu, 12 Dec 2024 18:18:06 +0530 Subject: [PATCH 16/35] fix(patch): ignore validate when creating item tax template (#2831) --- .../patches/post_install/improve_item_tax_template.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/india_compliance/patches/post_install/improve_item_tax_template.py b/india_compliance/patches/post_install/improve_item_tax_template.py index 436c94a0cb..7df878f3b1 100644 --- a/india_compliance/patches/post_install/improve_item_tax_template.py +++ b/india_compliance/patches/post_install/improve_item_tax_template.py @@ -125,6 +125,7 @@ def create_or_update_item_tax_templates(companies): elif doc.gst_rate == 0: doc.gst_treatment = "Nil-Rated" + doc.flags.ignore_validate = True # eg: account_type validation doc.save() # create new templates for nil rated, exempted, non gst @@ -261,6 +262,7 @@ def remove_old_item_variant_settings(): if field.field_name in ("is_nil_exempt", "is_non_gst"): item_variant.fields.remove(field) + item_variant.flags.ignore_validate = True item_variant.save() From 28d45b2cb5c418eba62e2111d0d5bd7a29ff7d64 Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:19:01 +0530 Subject: [PATCH 17/35] fix: add documentation links in help (#2810) * fix: add documentation links in help * fix: added e-waybill links * fix: formatting changes * fix: added links for company and gst settings * fix: remove e-waybill links and add blog links --------- Co-authored-by: Sanket322 --- india_compliance/public/js/help_links.js | 151 ++++++++++++++++++ .../public/js/india_compliance.bundle.js | 1 + 2 files changed, 152 insertions(+) create mode 100644 india_compliance/public/js/help_links.js diff --git a/india_compliance/public/js/help_links.js b/india_compliance/public/js/help_links.js new file mode 100644 index 0000000000..940e6f9ca4 --- /dev/null +++ b/india_compliance/public/js/help_links.js @@ -0,0 +1,151 @@ +frappe.provide("frappe.help.help_links"); + +const docsUrl = "https://docs.indiacompliance.app/docs/"; +const blogUrl = "https://docs.indiacompliance.app/blog/"; + +//India Compliance Account +frappe.help.help_links["india-compliance-account"] = [ + { + label: "India Compliance Account", + url: docsUrl + "getting-started/india_compliance_account", + }, +]; + +//GST Settings +frappe.help.help_links["Form/GST Settings"] = [ + { + label: "Setting Up GST accounts", + url: docsUrl + "configuration/gst_setup#gst-accounts" + }, + { + label: "Setting Up API", + url: docsUrl + "ewaybill-and-einvoice/gst_settings" + }, +]; + +//Company +frappe.help.help_links["Form/Company"] = [ + { + label: "Print Settings", + url: docsUrl + "configuration/gst_setup#print-format", + } +]; + + +//Doctypes +//Sales Invoice +if (!frappe.help.help_links["Form/Sales Invoice"]) { + frappe.help.help_links["Form/Sales Invoice"] = []; +} + +frappe.help.help_links["Form/Sales Invoice"].push( + { + label: "e-Waybill", + url: docsUrl + "ewaybill-and-einvoice/generating_e_waybill", + }, + { + label: "e-Invoice", + url: docsUrl + "ewaybill-and-einvoice/generating_e_invoice", + }, +); + +//Stock Entry +frappe.help.help_links["Form/Stock Entry"].push({ + label: "Subcontracting Workflow", + url: blogUrl + "posts/post5", +}) + +//Subcontracting Receipt +frappe.help.help_links["Form/Subcontracting Receipt"] = [ + { + label: "Subcontracting Workflow", + url: blogUrl + "posts/post5", + }, + { + label: "GST Job Work Stock Movement report", + url: docsUrl + "gst-reports/miscellaneous_reports#gst-job-work-stock-movement-report", + }, +] + +//Journal Entry +frappe.help.help_links["Form/Journal Entry"] = [ + { + label: "Reversal of Input Tax Credit", + url: docsUrl + "configuration/other_transaction#reversal-of-input-tax-credit", + } +] + +// GST Reports +frappe.help.help_links["Form/GSTR-1 Beta"] = [ + { + label: "GSTR-1 Beta", + url: docsUrl + "gst-reports/gstr1", + }, +]; + +frappe.help.help_links["Form/GSTR 3B Report"] = [ + { + label: "GSTR 3B Report", + url: docsUrl + "gst-reports/gstr3b", + }, +]; + +frappe.help.help_links["List/GSTR 3B Report"] = [ + { + label: "GSTR 3B Report", + url: docsUrl + "gst-reports/gstr3b", + }, +]; + + +//Query Reports +frappe.help.help_links["query-report/GST Job Work Stock Movement"] = [ + { + label: "GST Job Work Stock Movement", + url: docsUrl + "gst-reports/miscellaneous_reports#gst-job-work-stock-movement-report", + }, +]; + +frappe.help.help_links["query-report/GST Balance"] = [ + { + label: "GST Balance", + url: docsUrl + "gst-reports/miscellaneous_reports#gst-balance-report", + }, +]; + +frappe.help.help_links["query-report/GST Sales Register Beta"] = [ + { + label: "GST Sales Register Beta", + url: docsUrl + "gst-reports/miscellaneous_reports#gst-sales-register-beta-report", + }, +]; + +frappe.help.help_links["query-report/GST Purchase Register"] = [ + { + label: "GST Purchase Register", + url: docsUrl + "gst-reports/miscellaneous_reports#gst-purchase-register-beta-report", + }, +]; + +//Purchase Reconciliation +frappe.help.help_links["Form/Purchase Reconciliation Tool"] = [ + { + label: "Reconciling Purchase", + url: docsUrl + "purchase-reconciliation/reconciling_purchase", + }, +]; + +//Miscellaneous +frappe.help.help_links["query-report/Audit Trail"] = [ + { + label: "Audit Trail", + url: docsUrl + "miscellaneous/audit_trail", + }, +]; + +frappe.help.help_links["Form/Lower Deduction Certificate"] = [ + { + label: "Lower Deduction Certificate", + url: docsUrl + "miscellaneous/lower_deduction_certificate", + }, +]; \ 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 d43ba882a2..580889b4d8 100644 --- a/india_compliance/public/js/india_compliance.bundle.js +++ b/india_compliance/public/js/india_compliance.bundle.js @@ -8,3 +8,4 @@ import "./new_gst_category_notification"; import "./quick_info_popover"; import "./custom_number_card"; import "./taxes_controller"; +import "./help_links"; From 90788529e4869c78ec9902866550afbb8557f5b1 Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Thu, 12 Dec 2024 18:53:53 +0530 Subject: [PATCH 18/35] fix: Error Handling for download of GSTR2A/2B (#2832) Co-authored-by: Sanket322 --- .../purchase_reconciliation_tool.js | 13 +++++++++++++ .../purchase_reconciliation_tool.py | 17 +++++++++++++---- 2 files changed, 26 insertions(+), 4 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 bfe0b7461f..3a119d73b5 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 @@ -70,6 +70,8 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { await frappe.require("purchase_reconciliation_tool.bundle.js"); frm.trigger("company"); frm.purchase_reconciliation_tool = new PurchaseReconciliationTool(frm); + + frm.events.handle_download_failure(frm); }, onload(frm) { @@ -229,6 +231,17 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { } }); }, + + handle_download_failure(frm) { + frappe.realtime.on("gstr_2a_2b_download_failed", message => { + frm.dashboard.hide(); + frappe.msgprint({ + title: __("2A/2B Download Failed"), + message: message.error, + indicator: "red" + }); + }) + }, }); class PurchaseReconciliationTool { 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 3c9cf1ad9a..ebb090c2e1 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 @@ -460,11 +460,20 @@ def download_gstr( if not periods: return - if return_type == ReturnType.GSTR2A: - return download_gstr_2a(company_gstin, periods, gst_categories) + try: + if return_type == ReturnType.GSTR2A: + return download_gstr_2a(company_gstin, periods, gst_categories) - if return_type == ReturnType.GSTR2B: - return download_gstr_2b(company_gstin, periods) + if return_type == ReturnType.GSTR2B: + return download_gstr_2b(company_gstin, periods) + + except Exception as e: + frappe.publish_realtime( + "gstr_2a_2b_download_failed", + {"error": str(e)}, + user=frappe.session.user, + doctype="Purchase Reconciliation Tool", + ) def get_periods_to_download(company_gstin, return_type, periods): From 92e29d5022344010779288763c4217634cf4c87a Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Thu, 12 Dec 2024 19:45:54 +0530 Subject: [PATCH 19/35] fix: cancel e-invoice on cancel of invoice from list view (#2668) Co-authored-by: Sanket322 --- .../client_scripts/e_invoice_actions.js | 11 +++- .../client_scripts/e_waybill_actions.js | 8 ++- .../doctype/gst_settings/gst_settings.js | 12 +++++ .../doctype/gst_settings/gst_settings.json | 46 ++++++++++++++-- .../gst_india/overrides/sales_invoice.py | 54 ++++++++++++++++++- india_compliance/gst_india/utils/e_invoice.py | 8 ++- 6 files changed, 130 insertions(+), 9 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 aff1439769..fbe167e446 100644 --- a/india_compliance/gst_india/client_scripts/e_invoice_actions.js +++ b/india_compliance/gst_india/client_scripts/e_invoice_actions.js @@ -21,7 +21,7 @@ frappe.ui.form.on("Sales Invoice", { ) return; - if(frm.doc.docstatus === 2) return; + if (frm.doc.docstatus === 2) return; const is_einv_generatable = is_e_invoice_generatable(frm, true); @@ -144,6 +144,10 @@ frappe.ui.form.on("Sales Invoice", { return; } + if (gst_settings.auto_cancel_e_invoice === 1) { + continueCancellation(); + return; + } return show_cancel_e_invoice_dialog(frm, continueCancellation); }); }, @@ -281,7 +285,10 @@ function get_cancel_e_invoice_dialog_fields(frm, manual_cancel = false) { fieldname: "reason", fieldtype: "Select", reqd: 1, - default: manual_cancel ? "Others" : "Data Entry Mistake", + default: manual_cancel + ? "Others" + : gst_settings.reason_for_e_invoice_cancellation || + "Data Entry Mistake", options: ["Duplicate", "Data Entry Mistake", "Order Cancelled", "Others"], }, { diff --git a/india_compliance/gst_india/client_scripts/e_waybill_actions.js b/india_compliance/gst_india/client_scripts/e_waybill_actions.js index cfcebd60a6..c580614568 100644 --- a/india_compliance/gst_india/client_scripts/e_waybill_actions.js +++ b/india_compliance/gst_india/client_scripts/e_waybill_actions.js @@ -221,6 +221,11 @@ function setup_e_waybill_actions(doctype) { return; } + if (gst_settings.auto_cancel_e_waybill === 1) { + continueCancellation(); + return; + } + return show_cancel_e_waybill_dialog(frm, continueCancellation); }); }, @@ -758,7 +763,8 @@ function get_cancel_e_waybill_dialog_fields(frm) { fieldname: "reason", fieldtype: "Select", reqd: 1, - default: "Data Entry Mistake", + default: + gst_settings.reason_for_e_waybill_cancellation || "Data Entry Mistake", options: ["Duplicate", "Order Cancelled", "Data Entry Mistake", "Others"], }, { diff --git a/india_compliance/gst_india/doctype/gst_settings/gst_settings.js b/india_compliance/gst_india/doctype/gst_settings/gst_settings.js index 378ff8f94e..c632e4fed8 100644 --- a/india_compliance/gst_india/doctype/gst_settings/gst_settings.js +++ b/india_compliance/gst_india/doctype/gst_settings/gst_settings.js @@ -33,6 +33,8 @@ frappe.ui.form.on("GST Settings", { enable_e_invoice: set_auto_generate_e_waybill, auto_generate_e_invoice: set_auto_generate_e_waybill, generate_e_waybill_with_e_invoice: set_auto_generate_e_waybill, + auto_cancel_e_invoice: auto_cancel_e_invoice, + reason_for_e_invoice_cancellation: reason_for_e_invoice_cancellation, after_save(frm) { // sets latest values in frappe.boot for current user // other users will still need to refresh page @@ -115,4 +117,14 @@ function set_auto_generate_e_waybill(frm) { "auto_generate_e_waybill", frm.doc.auto_generate_e_invoice && frm.doc.generate_e_waybill_with_e_invoice ); + + frm.set_value("auto_cancel_e_waybill", frm.doc.auto_cancel_e_invoice) +} + +function auto_cancel_e_invoice(frm){ + frm.set_value("auto_cancel_e_waybill", frm.doc.auto_cancel_e_invoice) } + +function reason_for_e_invoice_cancellation(frm){ + frm.set_value("reason_for_e_waybill_cancellation", frm.doc.reason_for_e_invoice_cancellation) +} \ No newline at end of file diff --git a/india_compliance/gst_india/doctype/gst_settings/gst_settings.json b/india_compliance/gst_india/doctype/gst_settings/gst_settings.json index 49b8f8a9d5..391cabae49 100644 --- a/india_compliance/gst_india/doctype/gst_settings/gst_settings.json +++ b/india_compliance/gst_india/doctype/gst_settings/gst_settings.json @@ -34,12 +34,16 @@ "column_break_10", "auto_generate_e_waybill", "e_waybill_threshold", + "auto_cancel_e_waybill", + "reason_for_e_waybill_cancellation", "e_invoice_section", "enable_e_invoice", - "auto_generate_e_invoice", "generate_e_waybill_with_e_invoice", - "fetch_e_invoice_details_from_gst_portal", + "auto_generate_e_invoice", + "auto_cancel_e_invoice", + "reason_for_e_invoice_cancellation", "column_break_17", + "fetch_e_invoice_details_from_gst_portal", "apply_e_invoice_only_for_selected_companies", "e_invoice_applicable_from", "e_invoice_applicable_companies", @@ -628,12 +632,48 @@ "fieldname": "fetch_e_invoice_details_from_gst_portal", "fieldtype": "Check", "label": "Fetch e-Invoice details from GST Portal" + }, + { + "default": "0", + "depends_on": "eval: doc.enable_e_waybill", + "description": "e-Waybill will be automatically cancelled after Sales Invoice cancellation if possible", + "fieldname": "auto_cancel_e_waybill", + "fieldtype": "Check", + "label": "Automatically Cancel e-Waybill on Invoice Cancellation", + "read_only_depends_on": "eval: doc.enable_e_invoice" + }, + { + "default": "0", + "depends_on": "eval: doc.enable_e_invoice", + "description": "e-Invoice will be automatically cancelled after Sales Invoice cancellation if possible", + "fieldname": "auto_cancel_e_invoice", + "fieldtype": "Check", + "label": "Automatically Cancel e-Invoice on Invoice Cancellation" + }, + { + "default": "Data Entry Mistake", + "depends_on": "eval: doc.enable_e_waybill", + "fieldname": "reason_for_e_waybill_cancellation", + "fieldtype": "Select", + "label": "Default Reason for e-Waybill Cancellation", + "mandatory_depends_on": "eval: doc.auto_cancel_e_waybill", + "options": "Duplicate\nOrder Cancelled\nData Entry Mistake", + "read_only_depends_on": "eval: doc.enable_e_invoice" + }, + { + "default": "Data Entry Mistake", + "depends_on": "eval: doc.enable_e_invoice", + "fieldname": "reason_for_e_invoice_cancellation", + "fieldtype": "Select", + "label": "Default Reason for e-Invoice Cancellation", + "mandatory_depends_on": "eval: doc.auto_cancel_e_invoice", + "options": "Duplicate\nOrder Cancelled\nData Entry Mistake" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-07-25 19:16:25.036677", + "modified": "2024-12-12 19:09:07.185191", "modified_by": "Administrator", "module": "GST India", "name": "GST Settings", diff --git a/india_compliance/gst_india/overrides/sales_invoice.py b/india_compliance/gst_india/overrides/sales_invoice.py index 65b53e4774..4619b30444 100644 --- a/india_compliance/gst_india/overrides/sales_invoice.py +++ b/india_compliance/gst_india/overrides/sales_invoice.py @@ -1,6 +1,7 @@ import frappe from frappe import _, bold -from frappe.utils import flt, fmt_money +from frappe.desk.form.load import run_onload +from frappe.utils import add_days, days_diff, flt, fmt_money from india_compliance.gst_india.overrides.payment_entry import get_taxes_summary from india_compliance.gst_india.overrides.transaction import ( @@ -21,10 +22,14 @@ validate_invoice_number, ) from india_compliance.gst_india.utils.e_invoice import ( + _cancel_e_invoice, get_e_invoice_info, validate_e_invoice_applicability, ) -from india_compliance.gst_india.utils.e_waybill import get_e_waybill_info +from india_compliance.gst_india.utils.e_waybill import ( + _cancel_e_waybill, + get_e_waybill_info, +) from india_compliance.gst_india.utils.transaction_data import ( validate_unique_hsn_and_uom, ) @@ -182,6 +187,7 @@ def on_submit(doc, method=None): def before_cancel(doc, method=None): + cancel_e_waybill_e_invoice(doc) if ignore_gst_validations(doc): return @@ -206,6 +212,50 @@ def before_cancel(doc, method=None): ) +def cancel_e_waybill_e_invoice(doc, method=None): + gst_settings = frappe.get_cached_doc("GST Settings") + + if not is_api_enabled(gst_settings): + return + + def auto_cancel(cancel_func, action_type): + run_onload(doc) + + if action_type == "e_invoice": + generated_on = ( + doc.get_onload().get("e_invoice_info", {}).get("acknowledged_on") + ) + reason = gst_settings.reason_for_e_invoice_cancellation + + else: + generated_on = doc.get_onload().get("e_waybill_info", {}).get("created_on") + reason = gst_settings.reason_for_e_waybill_cancellation + + if not generated_on or days_diff(add_days(generated_on, 1), generated_on) > 1: + return + + values = frappe._dict( + { + "irn": doc.irn or "", + "reason": reason, + "ewaybill": doc.ewaybill or "", + "remark": "", + } + ) + cancel_func(doc, values) + + if doc.irn and gst_settings.enable_e_invoice and gst_settings.auto_cancel_e_invoice: + auto_cancel(_cancel_e_invoice, "e_invoice") + return + + if ( + doc.ewaybill + and gst_settings.enable_e_waybill + and gst_settings.auto_cancel_e_waybill + ): + auto_cancel(_cancel_e_waybill, "e_waybill") + + def is_e_waybill_applicable(doc, gst_settings=None): if not gst_settings: gst_settings = frappe.get_cached_doc("GST Settings") diff --git a/india_compliance/gst_india/utils/e_invoice.py b/india_compliance/gst_india/utils/e_invoice.py index f1437bc7ea..aefa17759a 100644 --- a/india_compliance/gst_india/utils/e_invoice.py +++ b/india_compliance/gst_india/utils/e_invoice.py @@ -331,6 +331,13 @@ def log_and_process_e_invoice_generation(doc, result, sandbox_mode=False, messag def cancel_e_invoice(docname, values): doc = load_doc("Sales Invoice", docname, "cancel") values = frappe.parse_json(values) + + _cancel_e_invoice(doc, values) + + return send_updated_doc(doc) + + +def _cancel_e_invoice(doc, values): validate_if_e_invoice_can_be_cancelled(doc) if doc.get("ewaybill"): @@ -349,7 +356,6 @@ def cancel_e_invoice(docname, values): ) doc.cancel() - return send_updated_doc(doc) def log_and_process_e_invoice_cancellation(doc, values, result, message): From b2fd024980d8e21297bf9328ac4d99acc2d573c8 Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Thu, 12 Dec 2024 21:43:10 +0530 Subject: [PATCH 20/35] chore: compatibiltiy for erpnext --- india_compliance/patches/check_version_compatibility.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/india_compliance/patches/check_version_compatibility.py b/india_compliance/patches/check_version_compatibility.py index 2067317b31..fb12de4091 100644 --- a/india_compliance/patches/check_version_compatibility.py +++ b/india_compliance/patches/check_version_compatibility.py @@ -18,7 +18,7 @@ { "app_name": "ERPNext", "current_version": version.parse(erpnext.__version__), - "required_versions": {"version-14": "14.70.7", "version-15": "15.27.7"}, + "required_versions": {"version-14": "14.70.7", "version-15": "15.45.5"}, }, ] From 703df384dc0af5e257d9b40a6f2696223c810f6a Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Fri, 13 Dec 2024 11:48:44 +0530 Subject: [PATCH 21/35] fix: use shipping address instread of port address for e-invoice (#2799) * fix: use shipping address instread of port address for e-invoice * test: remove port address from foreign transaction --------- Co-authored-by: Sanket322 --- india_compliance/gst_india/data/test_e_invoice.json | 9 --------- india_compliance/gst_india/utils/e_invoice.py | 6 +----- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/india_compliance/gst_india/data/test_e_invoice.json b/india_compliance/gst_india/data/test_e_invoice.json index 5859cc3f47..f5a2fb3eac 100644 --- a/india_compliance/gst_india/data/test_e_invoice.json +++ b/india_compliance/gst_india/data/test_e_invoice.json @@ -664,15 +664,6 @@ "Stcd": "02", "TrdNm": "_Test Indian Registered Company" }, - "ShipDtls": { - "Gstin": "URP", - "LglNm": "Test Indian Unregistered Company", - "TrdNm": "Test Indian Unregistered Company", - "Addr1": "Test Address - 2", - "Loc": "Test City", - "Pin": 380015, - "Stcd": "24" - }, "TranDtls": { "RegRev": "N", "SupTyp": "EXPWOP", diff --git a/india_compliance/gst_india/utils/e_invoice.py b/india_compliance/gst_india/utils/e_invoice.py index aefa17759a..d340e2db6c 100644 --- a/india_compliance/gst_india/utils/e_invoice.py +++ b/india_compliance/gst_india/utils/e_invoice.py @@ -766,11 +766,7 @@ def set_party_address_details(self): self.doc.company_address, validate_gstin=True ) - ship_to_address = ( - self.doc.port_address - if (is_foreign_doc(self.doc) and self.doc.port_address) - else self.doc.shipping_address_name - ) + ship_to_address = self.doc.shipping_address_name # Defaults self.shipping_address = None From 41550f4064cfb5c0fb7eb52298da8a642eef1652 Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Fri, 13 Dec 2024 14:02:02 +0530 Subject: [PATCH 22/35] fix: Fields to identify from where document is downloaded (#2839) Huly®: IC-2966 --- .../gst_inward_supply/gst_inward_supply.json | 20 ++++++- .../gst_india/utils/gstr_2/gstr.py | 20 +++---- .../gst_india/utils/gstr_2/gstr_2a.py | 14 ++++- .../gst_india/utils/gstr_2/gstr_2b.py | 58 +++++++++++++++++++ .../gst_india/utils/gstr_2/test_gstr_2a.py | 7 +++ .../utils/gstr_2/test_gstr_2b_v3_0.py | 8 +++ .../utils/gstr_2/test_gstr_2b_v4_0.py | 8 +++ india_compliance/patches.txt | 1 + .../v15/set_download_document_for_2a_2b.py | 18 ++++++ 9 files changed, 141 insertions(+), 13 deletions(-) create mode 100644 india_compliance/patches/v15/set_download_document_for_2a_2b.py diff --git a/india_compliance/gst_india/doctype/gst_inward_supply/gst_inward_supply.json b/india_compliance/gst_india/doctype/gst_inward_supply/gst_inward_supply.json index 1da9c332f7..96091e8262 100644 --- a/india_compliance/gst_india/doctype/gst_inward_supply/gst_inward_supply.json +++ b/india_compliance/gst_india/doctype/gst_inward_supply/gst_inward_supply.json @@ -23,6 +23,8 @@ "supply_type", "classification", "is_reverse_charge", + "is_downloaded_from_2a", + "is_downloaded_from_2b", "section_break_16", "items", "section_break_ykls", @@ -394,11 +396,27 @@ "fieldname": "taxable_value", "fieldtype": "Float", "label": "Taxable Value" + }, + { + "default": "0", + "fieldname": "is_downloaded_from_2b", + "fieldtype": "Check", + "hidden": 1, + "label": "Downloaded from 2B", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "is_downloaded_from_2a", + "fieldtype": "Check", + "hidden": 1, + "label": "Downloaded from 2A", + "read_only": 1 } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2024-11-16 16:23:13.402870", + "modified": "2024-12-12 17:28:52.668508", "modified_by": "Administrator", "module": "GST India", "name": "GST Inward Supply", diff --git a/india_compliance/gst_india/utils/gstr_2/gstr.py b/india_compliance/gst_india/utils/gstr_2/gstr.py index 890285f761..7677e0a767 100644 --- a/india_compliance/gst_india/utils/gstr_2/gstr.py +++ b/india_compliance/gst_india/utils/gstr_2/gstr.py @@ -47,8 +47,7 @@ def __init__(self, company, gstin, return_period, data, gen_date_2b): self.setup() def setup(self): - self.existing_transaction = {} - pass + self.existing_transaction = self.get_existing_transaction() def create_transactions(self, category, suppliers): if not suppliers: @@ -75,17 +74,14 @@ def create_transactions(self, category, suppliers): if transaction.get("unique_key") in self.existing_transaction: self.existing_transaction.pop(transaction.get("unique_key")) - self.delete_missing_transactions() - - def delete_missing_transactions(self): - """ - For GSTR2a, transactions are reflected immediately after it's pushed to GSTR-1. - At times, it may later be removed from GSTR-1. + self.handle_missing_transactions() - In such cases, we need to delete such unfilled transactions not present in the latest data. - """ + def handle_missing_transactions(self): return + def get_existing_transaction(self): + return {} + def get_all_transactions(self, category, suppliers): transactions = [] for supplier in suppliers: @@ -110,6 +106,7 @@ def get_transaction(self, category, supplier, invoice): classification=category.value, **self.get_supplier_details(supplier), **self.get_invoice_details(invoice), + **self.get_download_details(), items=self.get_transaction_items(invoice), ) @@ -134,6 +131,9 @@ def get_supplier_details(self, supplier): def get_invoice_details(self, invoice): return {} + def get_download_details(self): + return {} + def get_transaction_items(self, invoice): return [ self.get_transaction_item(frappe._dict(item)) diff --git a/india_compliance/gst_india/utils/gstr_2/gstr_2a.py b/india_compliance/gst_india/utils/gstr_2/gstr_2a.py index 7459f2da16..c96ffc58ff 100644 --- a/india_compliance/gst_india/utils/gstr_2/gstr_2a.py +++ b/india_compliance/gst_india/utils/gstr_2/gstr_2a.py @@ -14,9 +14,9 @@ def map_date_format(date_str, source_format, target_format): class GSTR2a(GSTR): def setup(self): + super().setup() self.all_gstins = set() self.cancelled_gstins = {} - self.existing_transaction = self.get_existing_transaction() def get_existing_transaction(self): category = type(self).__name__[6:] @@ -37,7 +37,14 @@ def get_existing_transaction(self): for transaction in existing_transactions } - def delete_missing_transactions(self): + def handle_missing_transactions(self): + """ + For GSTR2a, transactions are reflected immediately after it's pushed to GSTR-1. + At times, it may later be removed from GSTR-1. + + In such cases, we need to delete such unfilled transactions not present in the latest data. + """ + if self.existing_transaction: for inward_supply_name in self.existing_transaction.values(): frappe.delete_doc("GST Inward Supply", inward_supply_name) @@ -60,6 +67,9 @@ def get_supplier_details(self, supplier): return supplier_details + def get_download_details(self): + return {"is_downloaded_from_2a": 1} + def update_gstins_list(self, supplier_details): self.all_gstins.add(supplier_details.get("supplier_gstin")) diff --git a/india_compliance/gst_india/utils/gstr_2/gstr_2b.py b/india_compliance/gst_india/utils/gstr_2/gstr_2b.py index b062cabc9d..bc8eeff44e 100644 --- a/india_compliance/gst_india/utils/gstr_2/gstr_2b.py +++ b/india_compliance/gst_india/utils/gstr_2/gstr_2b.py @@ -1,10 +1,65 @@ import frappe +from frappe.query_builder.functions import IfNull from india_compliance.gst_india.utils import parse_datetime from india_compliance.gst_india.utils.gstr_2.gstr import GSTR, get_mapped_value class GSTR2b(GSTR): + def get_existing_transaction(self): + category = type(self).__name__[6:] + + gst_is = frappe.qb.DocType("GST Inward Supply") + existing_transactions = ( + frappe.qb.from_(gst_is) + .select(gst_is.name, gst_is.supplier_gstin, gst_is.bill_no) + .where(gst_is.return_period_2b == self.return_period) + .where(gst_is.classification == category) + ).run(as_dict=True) + + return { + f"{transaction.get('supplier_gstin', '')}-{transaction.get('bill_no', '')}": transaction.get( + "name" + ) + for transaction in existing_transactions + } + + def handle_missing_transactions(self): + """ + For GSTR2b, only filed transactions are reported. They may be removed from GSTR-2b later + if marked as pending / rejected from IMS Dashboard. + + In such cases, + 1) we need to clear the return_period_2b as this could change in future. + 2) safer to clear delete them as well if no matching transactions are found (possibly rejected). + """ + if not self.existing_transaction: + return + + missing_transactions = list(self.existing_transaction.values()) + + # clear return_period_2b + inward_supply = frappe.qb.DocType("GST Inward Supply") + ( + frappe.qb.update(inward_supply) + .set(inward_supply.return_period_2b, "") + .set(inward_supply.is_downloaded_from_2b, 0) + .where(inward_supply.name.isin(missing_transactions)) + .run() + ) + + # delete unmatched transactions + unmatched_transactions = ( + frappe.qb.from_(inward_supply) + .select(inward_supply.name) + .where(inward_supply.name.isin(missing_transactions)) + .where(IfNull(inward_supply.link_name, "") == "") + .run(pluck=True) + ) + + for transaction_name in unmatched_transactions: + frappe.delete_doc("GST Inward Supply", transaction_name) + def get_transaction(self, category, supplier, invoice): transaction = super().get_transaction(category, supplier, invoice) transaction.return_period_2b = self.return_period @@ -19,6 +74,9 @@ def get_supplier_details(self, supplier): "sup_return_period": supplier.supprd, } + def get_download_details(self): + return {"is_downloaded_from_2b": 1} + def get_transaction_item(self, item): return { "item_number": item.num, diff --git a/india_compliance/gst_india/utils/gstr_2/test_gstr_2a.py b/india_compliance/gst_india/utils/gstr_2/test_gstr_2a.py index 4126583b3c..09c261d83f 100644 --- a/india_compliance/gst_india/utils/gstr_2/test_gstr_2a.py +++ b/india_compliance/gst_india/utils/gstr_2/test_gstr_2a.py @@ -115,6 +115,7 @@ def test_gstr2a_b2b(self): "gstr_3b_filled": 1, "gstr_1_filing_date": date(2019, 11, 18), "registration_cancel_date": date(2019, 8, 27), + "is_downloaded_from_2a": 1, }, doc, ) @@ -162,6 +163,7 @@ def test_gstr2a_b2ba(self): "gstr_3b_filled": 1, "gstr_1_filing_date": date(2020, 5, 12), "registration_cancel_date": date(2019, 8, 27), + "is_downloaded_from_2a": 1, }, doc, ) @@ -202,6 +204,7 @@ def test_gstr2a_cdn(self): "897ADG56RTY78956HYUG90BNHHIJK453GFTD99845672FDHHHSHGFH4567FG56TR" ), "irn_gen_date": date(2019, 12, 24), + "is_downloaded_from_2a": 1, }, doc, ) @@ -239,6 +242,7 @@ def test_gstr2a_cdna(self): "gstr_3b_filled": 1, "gstr_1_filing_date": date(2019, 11, 18), "registration_cancel_date": date(2019, 8, 27), + "is_downloaded_from_2a": 1, }, doc, ) @@ -261,6 +265,7 @@ def test_gstr2a_isd(self): "cgst": 20, "sgst": 20, "cess": 20, + "is_downloaded_from_2a": 1, }, doc, ) @@ -283,6 +288,7 @@ def test_gstr2a_impg(self): "taxable_value": 123.02, "igst": 123.02, "cess": 0.5, + "is_downloaded_from_2a": 1, }, doc, ) @@ -305,6 +311,7 @@ def test_gstr2a_impgsez(self): "cgst": 0, "sgst": 0, "cess": 0.5, + "is_downloaded_from_2a": 1, }, doc, ) diff --git a/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v3_0.py b/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v3_0.py index 01538c8932..22b537fdcc 100644 --- a/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v3_0.py +++ b/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v3_0.py @@ -67,6 +67,7 @@ def test_gstr2b_b2b(self): "cess": 0, } ], + "is_downloaded_from_2b": 1, }, doc, ) @@ -107,6 +108,7 @@ def test_gstr2b_b2ba(self): "cess": 0, } ], + "is_downloaded_from_2b": 1, }, doc, ) @@ -147,6 +149,7 @@ def test_gstr2b_cdnr(self): "cess": 0, } ], + "is_downloaded_from_2b": 1, }, doc, ) @@ -185,6 +188,7 @@ def test_gstr2b_cdnra(self): "cess": 0, } ], + "is_downloaded_from_2b": 1, }, doc, ) @@ -208,6 +212,7 @@ def test_gstr2b_isd(self): "cgst": 200, "sgst": 200, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -234,6 +239,7 @@ def test_gstr2b_isda(self): "cgst": 200, "sgst": 200, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -253,6 +259,7 @@ def test_gstr2b_impg(self): "taxable_value": 123.02, "igst": 123.02, "cess": 0.5, + "is_downloaded_from_2b": 1, }, doc, ) @@ -274,6 +281,7 @@ def test_gstr2b_impgsez(self): "taxable_value": 123.02, "igst": 123.02, "cess": 0.5, + "is_downloaded_from_2b": 1, }, doc, ) diff --git a/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v4_0.py b/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v4_0.py index fead03e510..a32e7db70f 100644 --- a/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v4_0.py +++ b/india_compliance/gst_india/utils/gstr_2/test_gstr_2b_v4_0.py @@ -61,6 +61,7 @@ def test_gstr2b_b2b(self): "cgst": 0, "sgst": 0, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -95,6 +96,7 @@ def test_gstr2b_b2ba(self): "cgst": 0, "sgst": 0, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -129,6 +131,7 @@ def test_gstr2b_cdnr(self): "cgst": 0, "sgst": 0, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -161,6 +164,7 @@ def test_gstr2b_cdnra(self): "cgst": 0, "sgst": 0, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -184,6 +188,7 @@ def test_gstr2b_isd(self): "cgst": 200, "sgst": 200, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -210,6 +215,7 @@ def test_gstr2b_isda(self): "cgst": 200, "sgst": 200, "cess": 0, + "is_downloaded_from_2b": 1, }, doc, ) @@ -229,6 +235,7 @@ def test_gstr2b_impg(self): "taxable_value": 123.02, "igst": 123.02, "cess": 0.5, + "is_downloaded_from_2b": 1, }, doc, ) @@ -250,6 +257,7 @@ def test_gstr2b_impgsez(self): "taxable_value": 123.02, "igst": 123.02, "cess": 0.5, + "is_downloaded_from_2b": 1, }, doc, ) diff --git a/india_compliance/patches.txt b/india_compliance/patches.txt index e15618118d..37fa0ca3d1 100644 --- a/india_compliance/patches.txt +++ b/india_compliance/patches.txt @@ -66,3 +66,4 @@ india_compliance.patches.v15.update_action_for_gst_inward_supply india_compliance.patches.v15.set_default_for_new_gst_category_notification india_compliance.patches.v15.make_e_invoice_log_extensible india_compliance.patches.v15.migrate_boe_taxes_to_ic_taxes +india_compliance.patches.v15.set_download_document_for_2a_2b diff --git a/india_compliance/patches/v15/set_download_document_for_2a_2b.py b/india_compliance/patches/v15/set_download_document_for_2a_2b.py new file mode 100644 index 0000000000..b5eba2e0f2 --- /dev/null +++ b/india_compliance/patches/v15/set_download_document_for_2a_2b.py @@ -0,0 +1,18 @@ +import frappe + + +def execute(): + if frappe.db.has_column("GST Inward Supply", "is_downloaded_from_2a"): + # set "is_downloaded_from_2a" to "1" for all GST Inward Supply + frappe.db.set_value( + "GST Inward Supply", {"name": ["is", "set"]}, "is_downloaded_from_2a", 1 + ) + + if frappe.db.has_column("GST Inward Supply", "is_downloaded_from_2b"): + # set "is_downloaded_from_2b" to "1" where 2B return period is set + frappe.db.set_value( + "GST Inward Supply", + {"return_period_2b": ["is", "set"]}, + "is_downloaded_from_2b", + 1, + ) From a8e5490089dac669458bf5f0a4c3e4a1ed767121 Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Fri, 13 Dec 2024 20:09:48 +0530 Subject: [PATCH 23/35] fix: show error message instead of throw during Purchase Invoice name validation (#2864) * fix: show error message instead of throw * fix: for purchase invoice use msgprint instead of throw * test: minor change in test case * fix: resolve test case for sales invoice --------- Co-authored-by: Sanket322 --- .../overrides/test_purchase_invoice.py | 7 +++-- .../gst_india/overrides/test_sales_invoice.py | 4 ++- india_compliance/gst_india/utils/__init__.py | 29 ++++++++++--------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/india_compliance/gst_india/overrides/test_purchase_invoice.py b/india_compliance/gst_india/overrides/test_purchase_invoice.py index 015ae0cac5..cc887c16b5 100644 --- a/india_compliance/gst_india/overrides/test_purchase_invoice.py +++ b/india_compliance/gst_india/overrides/test_purchase_invoice.py @@ -79,8 +79,9 @@ def test_validate_invoice_length(self): setattr(pinv, "__newname", "INV/2022/00001/asdfsadg") # NOQA pinv.meta.autoname = "prompt" - self.assertRaisesRegex( - frappe.exceptions.ValidationError, + pinv.save() + + self.assertEqual( + frappe.parse_json(frappe.message_log[-1]).get("message"), "Transaction Name must be 16 characters or fewer to meet GST requirements", - pinv.save, ) diff --git a/india_compliance/gst_india/overrides/test_sales_invoice.py b/india_compliance/gst_india/overrides/test_sales_invoice.py index da2c3285f8..caaadaaf96 100644 --- a/india_compliance/gst_india/overrides/test_sales_invoice.py +++ b/india_compliance/gst_india/overrides/test_sales_invoice.py @@ -16,7 +16,9 @@ def test_validate_invoice_number(self): "PI2021 - 001", ] for name in invalid_names: - doc = frappe._dict(name=name, posting_date=posting_date) + doc = frappe._dict( + name=name, posting_date=posting_date, doctype="Sales Invoice" + ) self.assertRaises(frappe.ValidationError, validate_invoice_number, doc) valid_names = [ diff --git a/india_compliance/gst_india/utils/__init__.py b/india_compliance/gst_india/utils/__init__.py index ae838bbadb..5763d14143 100644 --- a/india_compliance/gst_india/utils/__init__.py +++ b/india_compliance/gst_india/utils/__init__.py @@ -940,23 +940,26 @@ def validate_invoice_number(doc, throw=True): if not throw: return is_valid_length and is_valid_format + if is_valid_length and is_valid_format: + return + + title = _("Invalid GST Transaction Name") + if not is_valid_length: - frappe.throw( - _( - "Transaction Name must be 16 characters or fewer to meet GST requirements" - ), - title=_("Invalid GST Transaction Name"), + message = _( + "Transaction Name must be 16 characters or fewer to meet GST requirements" ) - - if not is_valid_format: - frappe.throw( - _( - "Transaction Name should start with an alphanumeric character and can" - " only contain alphanumeric characters, dash (-) and slash (/) to meet GST requirements" - ), - title=_("Invalid GST Transaction Name"), + else: + message = _( + "Transaction Name should start with an alphanumeric character and can" + " only contain alphanumeric characters, dash (-) and slash (/) to meet GST requirements" ) + if doc.doctype == "Sales Invoice": + frappe.throw(message, title=title) + + frappe.msgprint(message, title=title) + def handle_server_errors(settings, doc, document_type, error): if not doc.doctype == "Sales Invoice": From 128cc73e09dc2b67281cf1f190d6d700cfa7bf97 Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Mon, 16 Dec 2024 14:56:43 +0530 Subject: [PATCH 24/35] fix: update minimum and maximum net rate field in item (#2870) * fix: update minimum and maximum net rate field in item * fix: update the alert message --------- Co-authored-by: Sanket322 --- india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.js | 2 +- india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.py | 2 ++ india_compliance/gst_india/overrides/item.py | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.js b/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.js index a563ab4c81..4f5b020598 100644 --- a/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.js +++ b/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.js @@ -16,7 +16,7 @@ frappe.ui.form.on('GST HSN Code', { method: 'india_compliance.gst_india.doctype.gst_hsn_code.gst_hsn_code.update_taxes_in_item_master', callback: function(r) { if(r.message){ - frappe.show_alert(__('Item taxes updated')); + frappe.show_alert(__('Items with this HSN code will be updated shortly')); } } }); diff --git a/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.py b/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.py index ed9ab17f09..5ff47a1482 100644 --- a/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.py +++ b/india_compliance/gst_india/doctype/gst_hsn_code/gst_hsn_code.py @@ -37,6 +37,8 @@ def update_item_document(taxes, hsn_code): "item_tax_template": tax.item_tax_template, "tax_category": tax.tax_category, "valid_from": tax.valid_from, + "minimum_net_rate": tax.minimum_net_rate, + "maximum_net_rate": tax.maximum_net_rate, }, ) diff --git a/india_compliance/gst_india/overrides/item.py b/india_compliance/gst_india/overrides/item.py index c69d6d3b87..c983712e7d 100644 --- a/india_compliance/gst_india/overrides/item.py +++ b/india_compliance/gst_india/overrides/item.py @@ -43,5 +43,7 @@ def set_taxes_from_hsn_code(doc): "item_tax_template": tax.item_tax_template, "tax_category": tax.tax_category, "valid_from": tax.valid_from, + "minimum_net_rate": tax.minimum_net_rate, + "maximum_net_rate": tax.maximum_net_rate, }, ) From 191f298d1ad7665cb61a2099cf6277ba4064f415 Mon Sep 17 00:00:00 2001 From: Ninad Parikh <109862100+Ninad1306@users.noreply.github.com> Date: Wed, 18 Dec 2024 13:01:43 +0530 Subject: [PATCH 25/35] fix: Remove Filter on Second Click (#2874) * fix: remove filter on second click * fix: changes as per review --- .../doctype/gstr_1_beta/gstr_1_beta.js | 44 ++++++++++++------- .../purchase_reconciliation_tool.js | 22 +++++----- .../public/js/components/filter_group.js | 28 ++++++++++-- 3 files changed, 63 insertions(+), 31 deletions(-) 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 0479d087f8..272e0ddc00 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 @@ -648,7 +648,12 @@ class GSTR1 { } filter_detailed_view = async (fieldname, value) => { - await this.filter_group.push_new_filter([DOCTYPE, fieldname, "=", value]); + await this.filter_group.add_or_remove_filter([ + DOCTYPE, + fieldname, + "=", + value.trim(), + ]); this.filter_group.apply(); }; @@ -1135,10 +1140,10 @@ class TabManager { args[2]?.indent == 0 ? `${value}` : isDescriptionCell - ? ` + ? `

${value}

` - : value; + : value; return value; } @@ -1893,9 +1898,9 @@ class FiledTab extends GSTR1_TabManager { const { include_uploaded, delete_missing } = dialog ? dialog.get_values() : { - include_uploaded: true, - delete_missing: false, - }; + include_uploaded: true, + delete_missing: false, + }; const doc = me.instance.frm.doc; @@ -2139,7 +2144,7 @@ class ReconcileTab extends FiledTab { }); } - get_creation_time_string() { } // pass + get_creation_time_string() {} // pass get_detail_view_column() { return [ @@ -2213,8 +2218,8 @@ class ErrorsTab extends TabManager { ]; } - setup_actions() { } - set_creation_time_string() { } + setup_actions() {} + set_creation_time_string() {} refresh_data(data) { data = data.error_report; @@ -2470,10 +2475,18 @@ class FileGSTR1Dialog { return ` ${description} - ${format_currency(liability.total_igst_amount)} - ${format_currency(liability.total_cgst_amount)} - ${format_currency(liability.total_sgst_amount)} - ${format_currency(liability.total_cess_amount)} + ${format_currency( + liability.total_igst_amount + )} + ${format_currency( + liability.total_cgst_amount + )} + ${format_currency( + liability.total_sgst_amount + )} + ${format_currency( + liability.total_cess_amount + )} `; } @@ -2581,8 +2594,7 @@ class GSTR1Action extends FileGSTR1Dialog { const draft_invoices = this.frm.gstr1.data.books["Document Issued"]?.filter( row => row.draft_count > 0 ); - if (!draft_invoices?.length) - return upload(); + if (!draft_invoices?.length) return upload(); frappe.confirm( __( @@ -2851,7 +2863,7 @@ function is_gstr1_api_enabled() { } function patch_set_indicator(frm) { - frm.toolbar.set_indicator = function () { }; + frm.toolbar.set_indicator = function () {}; } async function set_default_company_gstin(frm) { 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 3a119d73b5..74df0fb8ab 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 @@ -3,6 +3,7 @@ frappe.provide("purchase_reconciliation_tool"); +const DOCTYPE = "Purchase Reconciliation Tool"; const tooltip_info = { purchase_period: "Returns purchases during this period where no match is found.", inward_supply_period: @@ -62,7 +63,7 @@ async function add_gstr2b_alert(frm) { }); } -frappe.ui.form.on("Purchase Reconciliation Tool", { +frappe.ui.form.on(DOCTYPE, { async setup(frm) { patch_set_active_tab(frm); new india_compliance.quick_info_popover(frm, tooltip_info); @@ -238,9 +239,9 @@ frappe.ui.form.on("Purchase Reconciliation Tool", { frappe.msgprint({ title: __("2A/2B Download Failed"), message: message.error, - indicator: "red" + indicator: "red", }); - }) + }); }, }); @@ -330,7 +331,7 @@ class PurchaseReconciliationTool { setup_filter_button() { this.filter_group = new india_compliance.FilterGroup({ - doctype: "Purchase Reconciliation Tool", + doctype: DOCTYPE, parent: this.$wrapper.find(".form-tabs-list"), filter_options: { fieldname: "supplier_name", @@ -403,7 +404,7 @@ class PurchaseReconciliationTool { }, ]; - fields.forEach(field => (field.parent = "Purchase Reconciliation Tool")); + fields.forEach(field => (field.parent = DOCTYPE)); return fields; } @@ -495,16 +496,13 @@ class PurchaseReconciliationTool { selector, async function (e) { e.preventDefault(); - const value = $(this).text().trim(); - const field = filter_map[tab][selector]; - await me.filter_group.push_new_filter([ - "Purchase Reconciliation Tool", - field, + await me.filter_group.add_or_remove_filter([ + DOCTYPE, + filter_map[tab][selector], "=", - value, + $(this).text().trim(), ]); - me.filter_group.apply(); } ); diff --git a/india_compliance/public/js/components/filter_group.js b/india_compliance/public/js/components/filter_group.js index 363a613452..3ea3b45bc9 100644 --- a/india_compliance/public/js/components/filter_group.js +++ b/india_compliance/public/js/components/filter_group.js @@ -33,7 +33,9 @@ FILTER_GROUP_BUTTON = $( ${__("Filter")} -