From 246c1a9380238212b821951db6ed3e8ef88d9922 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 13 Sep 2022 20:05:20 +0530 Subject: [PATCH 1/9] fix: Add child table for tax withheld vouchers --- .../purchase_invoice/purchase_invoice.json | 21 ++++++-- .../doctype/tax_withheld_vouchers/__init__.py | 0 .../tax_withheld_vouchers.json | 48 +++++++++++++++++++ .../tax_withheld_vouchers.py | 9 ++++ 4 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 erpnext/accounts/doctype/tax_withheld_vouchers/__init__.py create mode 100644 erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json create mode 100644 erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 534b879e78..1d59651405 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -93,6 +93,8 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", + "tax_withheld_vouchers_section", + "tax_withheld_vouchers", "section_break_44", "apply_discount_on", "base_discount_amount", @@ -1367,7 +1369,7 @@ "width": "50px" }, { - "depends_on": "eval:doc.is_subcontracted", + "depends_on": "eval:doc.is_subcontracted", "fieldname": "supplier_warehouse", "fieldtype": "Link", "label": "Supplier Warehouse", @@ -1426,13 +1428,25 @@ "hidden": 1, "label": "Is Old Subcontracting Flow", "read_only": 1 - } + }, + { + "fieldname": "tax_withheld_vouchers_section", + "fieldtype": "Section Break", + "label": "Tax Withheld Vouchers" + }, + { + "fieldname": "tax_withheld_vouchers", + "fieldtype": "Table", + "label": "Tax Withheld Vouchers", + "options": "Tax Withheld Vouchers", + "read_only": 1 + } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-06-15 15:40:58.527065", + "modified": "2022-09-13 16:22:04.103982", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1492,6 +1506,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "timeline_field": "supplier", "title_field": "title", "track_changes": 1 diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/__init__.py b/erpnext/accounts/doctype/tax_withheld_vouchers/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json new file mode 100644 index 0000000000..cecc6fb20a --- /dev/null +++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json @@ -0,0 +1,48 @@ +{ + "actions": [], + "autoname": "autoincrement", + "creation": "2022-09-13 16:18:59.404842", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "voucher_type", + "voucher_name", + "taxable_amount" + ], + "fields": [ + { + "fieldname": "voucher_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Voucher Type", + "options": "DocType" + }, + { + "fieldname": "voucher_name", + "fieldtype": "Dynamic Link", + "in_list_view": 1, + "label": "Voucher Name", + "options": "voucher_type" + }, + { + "fieldname": "taxable_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Taxable Amount" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2022-09-13 17:31:52.321034", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Tax Withheld Vouchers", + "naming_rule": "Autoincrement", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py new file mode 100644 index 0000000000..ea54c5403a --- /dev/null +++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py @@ -0,0 +1,9 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class TaxWithheldVouchers(Document): + pass From 3fb1595a4ecdaeac4a6d85be1d8bfa4a62f181d8 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 13 Sep 2022 20:31:31 +0530 Subject: [PATCH 2/9] fix: Fetch vouchers to show in Invoice --- .../purchase_invoice/purchase_invoice.py | 15 +++- .../tax_withholding_category.py | 72 +++++++++---------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index fea81e9c27..d185300289 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1519,7 +1519,7 @@ class PurchaseInvoice(BuyingController): if not self.tax_withholding_category: return - tax_withholding_details, advance_taxes = get_party_tax_withholding_details( + tax_withholding_details, advance_taxes, voucher_wise_amount = get_party_tax_withholding_details( self, self.tax_withholding_category ) @@ -1548,6 +1548,19 @@ class PurchaseInvoice(BuyingController): for d in to_remove: self.remove(d) + ## Add pending vouchers on which tax was withheld + self.set("tax_withheld_vouchers", []) + + for voucher_no, voucher_details in voucher_wise_amount.items(): + self.append( + "tax_withheld_vouchers", + { + "voucher_name": voucher_no, + "voucher_type": voucher_details.get("voucher_type"), + "taxable_amount": voucher_details.get("amount"), + }, + ) + # calculate totals again after applying TDS self.calculate_taxes_and_totals() diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 6004e2b19b..f2fa77004c 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -109,7 +109,7 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None): ).format(tax_withholding_category, inv.company, party) ) - tax_amount, tax_deducted, tax_deducted_on_advances = get_tax_amount( + tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount = get_tax_amount( party_type, parties, inv, tax_details, posting_date, pan_no ) @@ -119,7 +119,7 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None): tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted) if inv.doctype == "Purchase Invoice": - return tax_row, tax_deducted_on_advances + return tax_row, tax_deducted_on_advances, voucher_wise_amount else: return tax_row @@ -217,7 +217,9 @@ def get_lower_deduction_certificate(tax_details, pan_no): def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=None): - vouchers = get_invoice_vouchers(parties, tax_details, inv.company, party_type=party_type) + vouchers, voucher_wise_amount = get_invoice_vouchers( + parties, tax_details, inv.company, party_type=party_type + ) advance_vouchers = get_advance_vouchers( parties, company=inv.company, @@ -236,6 +238,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N tax_deducted = get_deducted_tax(taxable_vouchers, tax_details) tax_amount = 0 + if party_type == "Supplier": ldc = get_lower_deduction_certificate(tax_details, pan_no) if tax_deducted: @@ -261,12 +264,13 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N if cint(tax_details.round_off_tax_amount): tax_amount = round(tax_amount) - return tax_amount, tax_deducted, tax_deducted_on_advances + return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): - dr_or_cr = "credit" if party_type == "Supplier" else "debit" doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice" + voucher_wise_amount = {} + vouchers = [] filters = { "company": company, @@ -281,29 +285,42 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) - invoices = frappe.get_all(doctype, filters=filters, pluck="name") or [""] + invoices_details = frappe.get_all( + doctype, filters=filters, fields=["name", "base_net_total"] + ) or [""] - journal_entries = frappe.db.sql( + for d in invoices_details: + vouchers.append(d.name) + voucher_wise_amount.update({d.name: {"amount": d.base_net_total, "voucher_type": doctype}}) + + journal_entries_details = frappe.db.sql( """ - SELECT j.name + SELECT j.name, ja.credit - ja.debit AS amount FROM `tabJournal Entry` j, `tabJournal Entry Account` ja WHERE - j.docstatus = 1 + j.name = ja.parent + AND j.docstatus = 1 AND j.is_opening = 'No' AND j.posting_date between %s and %s - AND ja.{dr_or_cr} > 0 AND ja.party in %s - """.format( - dr_or_cr=dr_or_cr + AND j.apply_tds = 1 + AND j.tax_withholding_category = %s + """, + ( + tax_details.from_date, + tax_details.to_date, + tuple(parties), + tax_details.get("tax_withholding_category"), ), - (tax_details.from_date, tax_details.to_date, tuple(parties)), - as_list=1, + as_dict=1, ) - if journal_entries: - journal_entries = journal_entries[0] + if journal_entries_details: + for d in journal_entries_details: + vouchers.append(d.name) + voucher_wise_amount.update({d.name: {"amount": d.amount, "voucher_type": "Journal Entry"}}) - return invoices + journal_entries + return vouchers, voucher_wise_amount def get_advance_vouchers( @@ -394,11 +411,6 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): supp_credit_amt += supp_jv_credit_amt supp_credit_amt += inv.net_total - debit_note_amount = get_debit_note_amount( - parties, tax_details.from_date, tax_details.to_date, inv.company - ) - supp_credit_amt -= debit_note_amount - threshold = tax_details.get("threshold", 0) cumulative_threshold = tax_details.get("cumulative_threshold", 0) @@ -515,22 +527,6 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net return tds_amount -def get_debit_note_amount(suppliers, from_date, to_date, company=None): - - filters = { - "supplier": ["in", suppliers], - "is_return": 1, - "docstatus": 1, - "posting_date": ["between", (from_date, to_date)], - } - fields = ["abs(sum(net_total)) as net_total"] - - if company: - filters["company"] = company - - return frappe.get_all("Purchase Invoice", filters, fields)[0].get("net_total") or 0.0 - - def get_ltds_amount(current_amount, deducted_amount, certificate_limit, rate, tax_details): if current_amount < (certificate_limit - deducted_amount): return current_amount * rate / 100 From b6184ce4715111add6dad3acf839097639cb4b51 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 14 Sep 2022 09:13:02 +0530 Subject: [PATCH 3/9] test: Add tests --- .../purchase_invoice/purchase_invoice.json | 6 +- .../tax_withheld_vouchers.json | 5 +- .../tax_withholding_category.py | 4 +- .../test_tax_withholding_category.py | 68 ++++++++++++++++++- erpnext/www/lms/__init__.py | 0 5 files changed, 72 insertions(+), 11 deletions(-) create mode 100644 erpnext/www/lms/__init__.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 1d59651405..1eeaf13abc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -83,6 +83,8 @@ "section_break_51", "taxes_and_charges", "taxes", + "tax_withheld_vouchers_section", + "tax_withheld_vouchers", "sec_tax_breakup", "other_charges_calculation", "totals", @@ -93,8 +95,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "tax_withheld_vouchers_section", - "tax_withheld_vouchers", "section_break_44", "apply_discount_on", "base_discount_amount", @@ -1446,7 +1446,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-09-13 16:22:04.103982", + "modified": "2022-09-13 23:39:54.525037", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json index cecc6fb20a..ce8c0c3708 100644 --- a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json +++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json @@ -29,13 +29,14 @@ "fieldname": "taxable_amount", "fieldtype": "Currency", "in_list_view": 1, - "label": "Taxable Amount" + "label": "Taxable Amount", + "options": "Company:company:default_currency" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-09-13 17:31:52.321034", + "modified": "2022-09-13 23:40:41.479208", "modified_by": "Administrator", "module": "Accounts", "name": "Tax Withheld Vouchers", diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index f2fa77004c..15f75d1510 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -285,9 +285,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): {"apply_tds": 1, "tax_withholding_category": tax_details.get("tax_withholding_category")} ) - invoices_details = frappe.get_all( - doctype, filters=filters, fields=["name", "base_net_total"] - ) or [""] + invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", "base_net_total"]) for d in invoices_details: vouchers.append(d.name) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 3059f8d64b..5c031a9954 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -148,7 +148,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): self.assertEqual(tcs_charged, 500) invoices.append(si) - # delete invoices to avoid clashing + # cancel invoices to avoid clashing for d in invoices: d.cancel() @@ -182,7 +182,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): self.assertEqual(pi1.taxes[0].tax_amount, 4000) - # delete invoices to avoid clashing + # cancel invoices to avoid clashing for d in invoices: d.cancel() @@ -207,10 +207,52 @@ class TestTaxWithholdingCategory(unittest.TestCase): self.assertEqual(pi1.taxes[0].tax_amount, 250) - # delete invoices to avoid clashing + # cancel invoices to avoid clashing for d in invoices: d.cancel() + def test_tax_withholding_category_voucher_display(self): + frappe.db.set_value( + "Supplier", "Test TDS Supplier6", "tax_withholding_category", "Test Multi Invoice Category" + ) + invoices = [] + + pi = create_purchase_invoice(supplier="Test TDS Supplier6", rate=4000, do_not_save=True) + pi.apply_tds = 1 + pi.tax_withholding_category = "Test Multi Invoice Category" + pi.save() + pi.submit() + invoices.append(pi) + + pi1 = create_purchase_invoice(supplier="Test TDS Supplier6", rate=2000, do_not_save=True) + pi1.apply_tds = 1 + pi1.is_return = 1 + pi1.items[0].qty = -1 + pi1.tax_withholding_category = "Test Multi Invoice Category" + pi1.save() + pi1.submit() + invoices.append(pi1) + + pi2 = create_purchase_invoice(supplier="Test TDS Supplier6", rate=9000, do_not_save=True) + pi2.apply_tds = 1 + pi2.tax_withholding_category = "Test Multi Invoice Category" + pi2.save() + pi2.submit() + invoices.append(pi2) + + pi2.load_from_db() + + self.assertTrue(pi2.taxes[0].tax_amount, 1100) + + self.assertTrue(pi2.tax_withheld_vouchers[0].voucher_name == pi1.name) + self.assertTrue(pi2.tax_withheld_vouchers[0].taxable_amount == pi1.net_total) + self.assertTrue(pi2.tax_withheld_vouchers[1].voucher_name == pi.name) + self.assertTrue(pi2.tax_withheld_vouchers[1].taxable_amount == pi.net_total) + + # cancel invoices to avoid clashing + for d in reversed(invoices): + d.cancel() + def cancel_invoices(): purchase_invoices = frappe.get_all( @@ -308,6 +350,7 @@ def create_records(): "Test TDS Supplier3", "Test TDS Supplier4", "Test TDS Supplier5", + "Test TDS Supplier6", ]: if frappe.db.exists("Supplier", name): continue @@ -498,3 +541,22 @@ def create_tax_with_holding_category(): "accounts": [{"company": "_Test Company", "account": "TDS - _TC"}], } ).insert() + + if not frappe.db.exists("Tax Withholding Category", "Test Multi Invoice Category"): + frappe.get_doc( + { + "doctype": "Tax Withholding Category", + "name": "Test Multi Invoice Category", + "category_name": "Test Multi Invoice Category", + "rates": [ + { + "from_date": fiscal_year[1], + "to_date": fiscal_year[2], + "tax_withholding_rate": 10, + "single_threshold": 5000, + "cumulative_threshold": 10000, + } + ], + "accounts": [{"company": "_Test Company", "account": "TDS - _TC"}], + } + ).insert() diff --git a/erpnext/www/lms/__init__.py b/erpnext/www/lms/__init__.py new file mode 100644 index 0000000000..e69de29bb2 From 36d0906ea26b65b9a4111439e2046bcf16305273 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Fri, 16 Sep 2022 13:50:37 +0530 Subject: [PATCH 4/9] fix: TDS deduction via journal entry --- .../doctype/journal_entry/journal_entry.py | 4 ++- .../tax_withholding_category.py | 30 ++++++++++--------- 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 63c6547f1d..52690e1e66 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -184,7 +184,9 @@ class JournalEntry(AccountsController): } ) - tax_withholding_details = get_party_tax_withholding_details(inv, self.tax_withholding_category) + tax_withholding_details, advance_taxes, voucher_wise_amount = get_party_tax_withholding_details( + inv, self.tax_withholding_category + ) if not tax_withholding_details: return diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 15f75d1510..0b5df9e0cc 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -344,23 +344,25 @@ def get_advance_vouchers( def get_taxes_deducted_on_advances_allocated(inv, tax_details): - advances = [d.reference_name for d in inv.get("advances")] tax_info = [] - if advances: - pe = frappe.qb.DocType("Payment Entry").as_("pe") - at = frappe.qb.DocType("Advance Taxes and Charges").as_("at") + if inv.get("advances"): + advances = [d.reference_name for d in inv.get("advances")] - tax_info = ( - frappe.qb.from_(at) - .inner_join(pe) - .on(pe.name == at.parent) - .select(at.parent, at.name, at.tax_amount, at.allocated_amount) - .where(pe.tax_withholding_category == tax_details.get("tax_withholding_category")) - .where(at.parent.isin(advances)) - .where(at.account_head == tax_details.account_head) - .run(as_dict=True) - ) + if advances: + pe = frappe.qb.DocType("Payment Entry").as_("pe") + at = frappe.qb.DocType("Advance Taxes and Charges").as_("at") + + tax_info = ( + frappe.qb.from_(at) + .inner_join(pe) + .on(pe.name == at.parent) + .select(at.parent, at.name, at.tax_amount, at.allocated_amount) + .where(pe.tax_withholding_category == tax_details.get("tax_withholding_category")) + .where(at.parent.isin(advances)) + .where(at.account_head == tax_details.account_head) + .run(as_dict=True) + ) return tax_info From 8c5b420aea221b001ea8537a8391654699757586 Mon Sep 17 00:00:00 2001 From: Maharshi Patel Date: Fri, 16 Sep 2022 18:26:00 +0530 Subject: [PATCH 5/9] fix: remove no_copy for ignore_pricing_rule --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 4 ++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 3 +-- erpnext/buying/doctype/purchase_order/purchase_order.json | 3 +-- erpnext/selling/doctype/quotation/quotation.json | 3 +-- erpnext/selling/doctype/sales_order/sales_order.json | 3 +-- erpnext/stock/doctype/delivery_note/delivery_note.json | 3 +-- erpnext/stock/doctype/purchase_receipt/purchase_receipt.json | 3 +-- 7 files changed, 8 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 534b879e78..a93965e2a5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -512,7 +512,6 @@ "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "label": "Ignore Pricing Rule", - "no_copy": 1, "permlevel": 1, "print_hide": 1 }, @@ -1432,7 +1431,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-06-15 15:40:58.527065", + "modified": "2022-09-16 17:45:25.345996", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1492,6 +1491,7 @@ "show_name_in_global_search": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "timeline_field": "supplier", "title_field": "title", "track_changes": 1 diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 1c9d3fbfb2..2da515737a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -649,7 +649,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Ignore Pricing Rule", - "no_copy": 1, "print_hide": 1 }, { @@ -2022,7 +2021,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-07-11 17:43:56.435382", + "modified": "2022-09-16 17:44:22.227332", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index acca380672..fb8f25a0df 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -441,7 +441,6 @@ "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "label": "Ignore Pricing Rule", - "no_copy": 1, "permlevel": 1, "print_hide": 1 }, @@ -1180,7 +1179,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-07 11:06:46.035093", + "modified": "2022-09-16 17:45:04.954055", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index bb2f95dd17..c58a46ba51 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -402,7 +402,6 @@ "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "label": "Ignore Pricing Rule", - "no_copy": 1, "permlevel": 1, "print_hide": 1 }, @@ -986,7 +985,7 @@ "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-06-11 20:35:32.635804", + "modified": "2022-09-16 17:44:43.221804", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 74c5c07e47..ff269d0e68 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -544,7 +544,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Ignore Pricing Rule", - "no_copy": 1, "permlevel": 1, "print_hide": 1 }, @@ -1549,7 +1548,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-06-10 03:52:22.212953", + "modified": "2022-09-16 17:43:57.007441", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index f9e934921d..a8f907ed71 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -490,7 +490,6 @@ "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "label": "Ignore Pricing Rule", - "no_copy": 1, "permlevel": 1, "print_hide": 1 }, @@ -1336,7 +1335,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-06-10 03:52:04.197415", + "modified": "2022-09-16 17:46:17.701904", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index a70415dfc3..acaac920c9 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -405,7 +405,6 @@ "fieldname": "ignore_pricing_rule", "fieldtype": "Check", "label": "Ignore Pricing Rule", - "no_copy": 1, "permlevel": 1, "print_hide": 1 }, @@ -1158,7 +1157,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-06-15 15:43:40.664382", + "modified": "2022-09-16 17:45:58.430132", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", From fac82cf69be487f4bfba953c722700fcc515b044 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 18 Sep 2022 19:41:05 +0530 Subject: [PATCH 6/9] fix: Depreciation posting date only when calculate depreciation is checked --- erpnext/assets/doctype/asset/asset.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index f414930d72..a43a16c9ec 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -239,8 +239,10 @@ frappe.ui.form.on('Asset', { item_code: function(frm) { - if(frm.doc.item_code) { + if(frm.doc.item_code && frm.doc.calculate_depreciation) { frm.trigger('set_finance_book'); + } else { + frm.set_value('finance_books', []); } }, @@ -381,6 +383,11 @@ frappe.ui.form.on('Asset', { calculate_depreciation: function(frm) { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); + if (frm.doc.item_code && frm.doc.calculate_depreciation ) { + frm.trigger("set_finance_book"); + } else { + frm.set_value("finance_books", []); + } }, gross_purchase_amount: function(frm) { From 9aa1f84d4578901902bfdb3889c571fced8861c1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 20 Sep 2022 09:06:18 +0530 Subject: [PATCH 7/9] chore: fix tests --- .../test_tax_withholding_category.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 5c031a9954..e80fe11ab3 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -52,7 +52,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): invoices.append(pi) # delete invoices to avoid clashing - for d in invoices: + for d in reversed(invoices): d.cancel() def test_single_threshold_tds(self): @@ -88,7 +88,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): self.assertEqual(pi.taxes_and_charges_deducted, 1000) # delete invoices to avoid clashing - for d in invoices: + for d in reversed(invoices): d.cancel() def test_tax_withholding_category_checks(self): @@ -114,7 +114,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): # TDS should be applied only on 1000 self.assertEqual(pi1.taxes[0].tax_amount, 1000) - for d in invoices: + for d in reversed(invoices): d.cancel() def test_cumulative_threshold_tcs(self): @@ -149,7 +149,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): invoices.append(si) # cancel invoices to avoid clashing - for d in invoices: + for d in reversed(invoices): d.cancel() def test_tds_calculation_on_net_total(self): @@ -183,7 +183,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): self.assertEqual(pi1.taxes[0].tax_amount, 4000) # cancel invoices to avoid clashing - for d in invoices: + for d in reversed(invoices): d.cancel() def test_multi_category_single_supplier(self): @@ -208,7 +208,7 @@ class TestTaxWithholdingCategory(unittest.TestCase): self.assertEqual(pi1.taxes[0].tax_amount, 250) # cancel invoices to avoid clashing - for d in invoices: + for d in reversed(invoices): d.cancel() def test_tax_withholding_category_voucher_display(self): From f0a78aa559c94e45564130f9571d269eefe6c551 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 20 Sep 2022 09:43:12 +0530 Subject: [PATCH 8/9] refactor: rewrite `Item Shortage Report` queries in QB --- .../item_shortage_report.py | 67 ++++++++----------- 1 file changed, 28 insertions(+), 39 deletions(-) diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.py b/erpnext/stock/report/item_shortage_report/item_shortage_report.py index 03a3a6a0b8..9fafe91c3f 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.py +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.py @@ -8,8 +8,7 @@ from frappe import _ def execute(filters=None): columns = get_columns() - conditions = get_conditions(filters) - data = get_data(conditions, filters) + data = get_data(filters) if not data: return [], [], None, [] @@ -19,49 +18,39 @@ def execute(filters=None): return columns, data, None, chart_data -def get_conditions(filters): - conditions = "" +def get_data(filters): + bin = frappe.qb.DocType("Bin") + wh = frappe.qb.DocType("Warehouse") + item = frappe.qb.DocType("Item") - if filters.get("warehouse"): - conditions += "AND warehouse in %(warehouse)s" - if filters.get("company"): - conditions += "AND company = %(company)s" - - return conditions - - -def get_data(conditions, filters): - data = frappe.db.sql( - """ - SELECT + query = ( + frappe.qb.from_(bin) + .from_(wh) + .from_(item) + .select( bin.warehouse, bin.item_code, - bin.actual_qty , - bin.ordered_qty , - bin.planned_qty , - bin.reserved_qty , + bin.actual_qty, + bin.ordered_qty, + bin.planned_qty, + bin.reserved_qty, bin.reserved_qty_for_production, - bin.projected_qty , - warehouse.company, - item.item_name , - item.description - FROM - `tabBin` bin, - `tabWarehouse` warehouse, - `tabItem` item - WHERE - bin.projected_qty<0 - AND warehouse.name = bin.warehouse - AND bin.item_code=item.name - {0} - ORDER BY bin.projected_qty;""".format( - conditions - ), - filters, - as_dict=1, + bin.projected_qty, + wh.company, + item.item_name, + item.description, + ) + .where((bin.projected_qty < 0) & (wh.name == bin.warehouse) & (bin.item_code == item.name)) + .orderby(bin.projected_qty) ) - return data + if filters.get("warehouse"): + query = query.where(bin.warehouse.isin(filters.get("warehouse"))) + + if filters.get("company"): + query = query.where(wh.company == filters.get("company")) + + return query.run(as_dict=True) def get_chart_data(data): From 3dc754cac2f21cd738a896e6bb3bf9d54be1d6b1 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 20 Sep 2022 10:46:28 +0530 Subject: [PATCH 9/9] test: add test cases for `Item Shortage Report` --- .../test_item_shortage_report.py | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 erpnext/stock/report/item_shortage_report/test_item_shortage_report.py diff --git a/erpnext/stock/report/item_shortage_report/test_item_shortage_report.py b/erpnext/stock/report/item_shortage_report/test_item_shortage_report.py new file mode 100644 index 0000000000..5884c32acc --- /dev/null +++ b/erpnext/stock/report/item_shortage_report/test_item_shortage_report.py @@ -0,0 +1,51 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe.tests.utils import FrappeTestCase + +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.stock.doctype.item.test_item import make_item +from erpnext.stock.report.item_shortage_report.item_shortage_report import ( + execute as item_shortage_report, +) + + +class TestItemShortageReport(FrappeTestCase): + def test_item_shortage_report(self): + item = make_item().name + so = make_sales_order(item_code=item) + + reserved_qty, projected_qty = frappe.db.get_value( + "Bin", + { + "item_code": item, + "warehouse": so.items[0].warehouse, + }, + ["reserved_qty", "projected_qty"], + ) + self.assertEqual(reserved_qty, so.items[0].qty) + self.assertEqual(projected_qty, -(so.items[0].qty)) + + filters = { + "company": so.company, + } + report_data = item_shortage_report(filters)[1] + item_code_list = [row.get("item_code") for row in report_data] + self.assertIn(item, item_code_list) + + filters = { + "company": so.company, + "warehouse": [so.items[0].warehouse], + } + report_data = item_shortage_report(filters)[1] + item_code_list = [row.get("item_code") for row in report_data] + self.assertIn(item, item_code_list) + + filters = { + "company": so.company, + "warehouse": ["Work In Progress - _TC"], + } + report_data = item_shortage_report(filters)[1] + item_code_list = [row.get("item_code") for row in report_data] + self.assertNotIn(item, item_code_list)