From b9fb1045d7f4331c064c491072199f65376159af Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 6 Nov 2022 11:50:20 +0530 Subject: [PATCH 1/7] feat: Item Wise TDS Calculation --- .../purchase_invoice/purchase_invoice.json | 25 +++++++++++- .../purchase_invoice_item.json | 7 ++++ .../tax_withholding_category.py | 32 +++++++++------ .../test_tax_withholding_category.py | 40 +++++++++++++++++++ erpnext/controllers/taxes_and_totals.py | 13 ++++++ erpnext/patches.txt | 2 +- erpnext/patches/v14_0/update_tds_fields.py | 29 ++++++++++++++ erpnext/public/js/controllers/transaction.js | 4 +- 8 files changed, 135 insertions(+), 17 deletions(-) create mode 100644 erpnext/patches/v14_0/update_tds_fields.py diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index e73d6023328..370c0fc9605 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -57,6 +57,8 @@ "column_break_28", "total", "net_total", + "tax_withholding_net_total", + "base_tax_withholding_net_total", "taxes_section", "taxes_and_charges", "column_break_58", @@ -89,7 +91,6 @@ "section_break_44", "apply_discount_on", "base_discount_amount", - "additional_discount_account", "column_break_46", "additional_discount_percentage", "discount_amount", @@ -1421,6 +1422,26 @@ "label": "Is Old Subcontracting Flow", "read_only": 1 }, + { + "default": "0", + "fieldname": "tax_withholding_net_total", + "fieldtype": "Currency", + "hidden": 1, + "label": "Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "read_only": 1 + }, + { + "fieldname": "base_tax_withholding_net_total", + "fieldtype": "Currency", + "hidden": 1, + "label": "Base Tax Withholding Net Total", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { "collapsible_depends_on": "tax_withheld_vouchers", "fieldname": "tax_withheld_vouchers_section", @@ -1519,7 +1540,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-10-11 13:04:44.304389", + "modified": "2022-11-04 01:02:44.544878", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index a8f6f80b6b9..5c1cb0dcc68 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -40,6 +40,7 @@ "discount_amount", "base_rate_with_margin", "sec_break2", + "apply_tds", "rate", "amount", "item_tax_template", @@ -868,6 +869,12 @@ "label": "Product Bundle", "options": "Product Bundle", "read_only": 1 + }, + { + "default": "1", + "fieldname": "apply_tds", + "fieldtype": "Check", + "label": "Apply TDS" } ], "idx": 1, 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 7eddd81ee03..30ed91b9744 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -61,6 +61,9 @@ def get_party_details(inv): def get_party_tax_withholding_details(inv, tax_withholding_category=None): + if inv.doctype == "Payment Entry": + inv.tax_withholding_net_total = inv.net_total + pan_no = "" parties = [] party_type, party = get_party_details(inv) @@ -242,7 +245,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N if party_type == "Supplier": ldc = get_lower_deduction_certificate(tax_details, pan_no) if tax_deducted: - net_total = inv.net_total + net_total = inv.tax_withholding_net_total if ldc: tax_amount = get_tds_amount_from_ldc( ldc, parties, pan_no, tax_details, posting_date, net_total @@ -272,6 +275,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice" + field = ( + "base_tax_withholding_net_total as base_net_total" + if party_type == "Supplier" + else "base_net_total" + ) voucher_wise_amount = {} vouchers = [] @@ -288,7 +296,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"]) + invoices_details = frappe.get_all(doctype, filters=filters, fields=["name", field]) for d in invoices_details: vouchers.append(d.name) @@ -392,7 +400,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): tds_amount = 0 invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1} - field = "sum(net_total)" + field = "sum(tax_withholding_net_total)" if cint(tax_details.consider_party_ledger_amount): invoice_filters.pop("apply_tds", None) @@ -415,12 +423,12 @@ 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 + supp_credit_amt += inv.tax_withholding_net_total threshold = tax_details.get("threshold", 0) cumulative_threshold = tax_details.get("cumulative_threshold", 0) - if (threshold and inv.net_total >= threshold) or ( + if (threshold and inv.tax_withholding_net_total >= threshold) or ( cumulative_threshold and supp_credit_amt >= cumulative_threshold ): if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint( @@ -428,11 +436,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ): # Get net total again as TDS is calculated on net total # Grand is used to just check for threshold breach - net_total = 0 - if vouchers: - net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") - - net_total += inv.net_total + net_total = ( + frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") + or 0.0 + ) + net_total += inv.tax_withholding_net_total supp_credit_amt = net_total - cumulative_threshold if ldc and is_valid_certificate( @@ -440,7 +448,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers): ldc.valid_upto, inv.get("posting_date") or inv.get("transaction_date"), tax_deducted, - inv.net_total, + inv.tax_withholding_net_total, ldc.certificate_limit, ): tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details) @@ -523,7 +531,7 @@ def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net limit_consumed = frappe.db.get_value( "Purchase Invoice", {"supplier": ("in", parties), "apply_tds": 1, "docstatus": 1}, - "sum(net_total)", + "sum(tax_withholding_net_total)", ) if is_valid_certificate( 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 e80fe11ab30..40c732bae52 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 @@ -186,6 +186,46 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in reversed(invoices): d.cancel() + def test_tds_calculation_on_net_total_partial_tds(self): + frappe.db.set_value( + "Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS" + ) + invoices = [] + + pi = create_purchase_invoice(supplier="Test TDS Supplier4", rate=20000, do_not_save=True) + pi.extend( + "items", + [ + { + "doctype": "Purchase Invoice Item", + "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"), + "qty": 1, + "rate": 20000, + "cost_center": "Main - _TC", + "expense_account": "Stock Received But Not Billed - _TC", + "apply_tds": 0, + }, + { + "doctype": "Purchase Invoice Item", + "item_code": frappe.db.get_value("Item", {"item_name": "TDS Item"}, "name"), + "qty": 1, + "rate": 35000, + "cost_center": "Main - _TC", + "expense_account": "Stock Received But Not Billed - _TC", + "apply_tds": 1, + }, + ], + ) + pi.save() + pi.submit() + invoices.append(pi) + + self.assertEqual(pi.taxes[0].tax_amount, 5500) + + # cancel invoices to avoid clashing + for d in reversed(invoices): + d.cancel() + def test_multi_category_single_supplier(self): frappe.db.set_value( "Supplier", "Test TDS Supplier5", "tax_withholding_category", "Test Service Category" diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index b5836c9070a..81de6823782 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -58,12 +58,25 @@ class calculate_taxes_and_totals(object): self.initialize_taxes() self.determine_exclusive_rate() self.calculate_net_total() + self.calculate_tax_withholding_net_total() self.calculate_taxes() self.manipulate_grand_total_for_inclusive_tax() self.calculate_totals() self._cleanup() self.calculate_total_net_weight() + def calculate_tax_withholding_net_total(self): + if hasattr(self.doc, "tax_withholding_net_total"): + sum_net_amount = 0 + sum_base_net_amount = 0 + for item in self.doc.get("items"): + if hasattr(item, "apply_tds") and item.apply_tds: + sum_net_amount += item.net_amount + sum_base_net_amount += item.base_net_amount + + self.doc.tax_withholding_net_total = sum_net_amount + self.doc.base_tax_withholding_net_total = sum_base_net_amount + def validate_item_tax_template(self): for item in self.doc.get("items"): if item.item_code and item.get("item_tax_template"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 5ed8f3f4a91..8e726bf0c0a 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,4 +317,4 @@ erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization erpnext.patches.v13_0.update_schedule_type_in_loans - +erpnext.patches.v14_0.update_tds_fields \ No newline at end of file diff --git a/erpnext/patches/v14_0/update_tds_fields.py b/erpnext/patches/v14_0/update_tds_fields.py new file mode 100644 index 00000000000..a333c5d7a50 --- /dev/null +++ b/erpnext/patches/v14_0/update_tds_fields.py @@ -0,0 +1,29 @@ +import frappe +from frappe.utils import nowdate + +from erpnext.accounts.utils import FiscalYearError, get_fiscal_year + + +def execute(): + # Only do for current fiscal year, no need to repost for all years + for company in frappe.get_all("Company"): + try: + fiscal_year_details = get_fiscal_year(date=nowdate(), company=company.name, as_dict=True) + + purchase_invoice = frappe.qb.DocType("Purchase Invoice") + + frappe.qb.update(purchase_invoice).set( + purchase_invoice.tax_withholding_net_total, purchase_invoice.net_total + ).set( + purchase_invoice.base_tax_withholding_net_total, purchase_invoice.base_net_total + ).where( + purchase_invoice.company == company.name + ).where( + purchase_invoice.apply_tds == 1 + ).where( + purchase_invoice.posting_date >= fiscal_year_details.year_start_date + ).where( + purchase_invoice.docstatus == 1 + ).run() + except FiscalYearError: + pass diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 677ca78829c..46ac80895cf 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1207,7 +1207,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "base_rounding_adjustment"], company_currency); this.frm.set_currency_labels(["total", "net_total", "total_taxes_and_charges", "discount_amount", - "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted", + "grand_total", "taxes_and_charges_added", "taxes_and_charges_deducted","tax_withholding_net_total", "rounded_total", "in_words", "paid_amount", "write_off_amount", "operating_cost", "scrap_material_cost", "rounding_adjustment", "raw_material_cost", "total_cost"], this.frm.doc.currency); @@ -1224,7 +1224,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } // toggle fields - this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", + this.frm.toggle_display(["conversion_rate", "base_total", "base_net_total", "base_tax_withholding_net_total", "base_total_taxes_and_charges", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", "base_grand_total", "base_rounded_total", "base_in_words", "base_discount_amount", "base_paid_amount", "base_write_off_amount", "base_operating_cost", "base_raw_material_cost", From e31655828668cdaf6647f76185ba3ee0bf4400bf Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 6 Nov 2022 13:32:15 +0530 Subject: [PATCH 2/7] fix: set `received_qty` before_validate SCR (cherry picked from commit c447dfaa9cbc59ed0e5a269eded3600d5acb0d3a) --- .../doctype/subcontracting_receipt/subcontracting_receipt.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index bce53608beb..c7f592b4d94 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -58,6 +58,7 @@ class SubcontractingReceipt(SubcontractingController): def before_validate(self): super(SubcontractingReceipt, self).before_validate() self.set_items_bom() + self.set_received_qty() self.set_items_cost_center() self.set_items_expense_account() @@ -212,6 +213,10 @@ class SubcontractingReceipt(SubcontractingController): "bom", ) + def set_received_qty(self): + for item in self.items: + item.received_qty = flt(item.qty) + flt(item.rejected_qty) + def set_items_cost_center(self): if self.company: cost_center = frappe.get_cached_value("Company", self.company, "cost_center") From ea9a50278d1a067275294db2f37fbbc51b0d5887 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 6 Nov 2022 13:33:47 +0530 Subject: [PATCH 3/7] fix: get `consumed_qty` based on `received_qty` in SCR (cherry picked from commit 70c9b8dc50d77f7bbc3da7f7341c3e6432902a0d) --- .../controllers/subcontracting_controller.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 8d67e300a30..7819fa586ca 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -100,7 +100,7 @@ class SubcontractingController(StockController): and self._doc_before_save ): for row in self._doc_before_save.get("items"): - item_dict[row.name] = (row.item_code, row.qty) + item_dict[row.name] = (row.item_code, row.received_qty or row.qty) return item_dict @@ -118,7 +118,9 @@ class SubcontractingController(StockController): for row in self.items: self.__reference_name.append(row.name) - if (row.name not in item_dict) or (row.item_code, row.qty) != item_dict[row.name]: + if (row.name not in item_dict) or (row.item_code, row.received_qty or row.qty) != item_dict[ + row.name + ]: self.__changed_name.append(row.name) if item_dict.get(row.name): @@ -461,12 +463,13 @@ class SubcontractingController(StockController): def __get_qty_based_on_material_transfer(self, item_row, transfer_item): key = (item_row.item_code, item_row.get(self.subcontract_data.order_field)) + item_qty = item_row.received_qty or item_row.qty - if self.qty_to_be_received == item_row.qty: + if self.qty_to_be_received.get(key) == item_qty: return transfer_item.qty if self.qty_to_be_received: - qty = (flt(item_row.qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0)) + qty = (flt(item_qty) * flt(transfer_item.qty)) / flt(self.qty_to_be_received.get(key, 0)) transfer_item.item_details.required_qty = transfer_item.qty if transfer_item.serial_no or frappe.get_cached_value( @@ -491,7 +494,11 @@ class SubcontractingController(StockController): for bom_item in self.__get_materials_from_bom( row.item_code, row.bom, row.get("include_exploded_items") ): - qty = flt(bom_item.qty_consumed_per_unit) * flt(row.qty) * row.conversion_factor + qty = ( + flt(bom_item.qty_consumed_per_unit) + * flt(row.received_qty or row.qty) + * row.conversion_factor + ) bom_item.main_item_code = row.item_code self.__update_reserve_warehouse(bom_item, row) self.__set_alternative_item(bom_item) From 8f78be8525cea5b02f5250b42bb5e68492779abb Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 6 Nov 2022 14:55:33 +0530 Subject: [PATCH 4/7] test: add test case for consumed-qty (cherry picked from commit 4d8da4420ebe664c31fe12392ace237bbeb9262c) --- .../test_subcontracting_receipt.py | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 34e78420d71..ca72ddfce81 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -468,6 +468,65 @@ class TestSubcontractingReceipt(FrappeTestCase): scr.cancel() self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name)) + def test_supplied_items_consumed_qty(self): + # Set Backflush Based On as "Material Transferred for Subcontracting" to transfer RM's more than the required qty + set_backflush_based_on("Material Transferred for Subcontract") + + # Create Material Receipt for RM's + make_stock_entry( + item_code="_Test Item", qty=100, target="_Test Warehouse 1 - _TC", basic_rate=100 + ) + make_stock_entry( + item_code="_Test Item Home Desktop 100", + qty=100, + target="_Test Warehouse 1 - _TC", + basic_rate=100, + ) + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 10, + "rate": 100, + "fg_item": "_Test FG Item", + "fg_item_qty": 10, + }, + ] + + # Create Subcontracting Order + sco = get_subcontracting_order(service_items=service_items) + + # Transfer RM's + rm_items = get_rm_items(sco.supplied_items) + rm_items[0]["qty"] = 20 # Extra 10 Qty + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + # Create Subcontracting Receipt + scr = make_subcontracting_receipt(sco.name) + scr.rejected_warehouse = "_Test Warehouse 1 - _TC" + + scr.items[0].qty = 5 # Accepted Qty + scr.items[0].rejected_qty = 3 + scr.save() + + # consumed_qty should be ((received_qty) * (transfered_qty / qty)) = ((5 + 3) * (20 / 10)) = 16 + self.assertEqual(scr.supplied_items[0].consumed_qty, 16) + + # Set Backflush Based On as "BOM" + set_backflush_based_on("BOM") + + scr.items[0].rejected_qty = 4 + scr.save() + + # consumed_qty should be ((received_qty) * (qty_consumed_per_unit)) = ((5 + 4) * (1)) = 9 + self.assertEqual(scr.supplied_items[0].consumed_qty, 9) + def make_return_subcontracting_receipt(**args): args = frappe._dict(args) From 34ca17ab11cc5150ed951b0222e0c1c55905c27a Mon Sep 17 00:00:00 2001 From: Daizy Modi Date: Mon, 7 Nov 2022 09:21:03 +0530 Subject: [PATCH 5/7] perf: use `get_cached_value` instead of `db.get_value` in controllers (#32776) (cherry picked from commit 4efc947f1494b1b0984009eb81f47f56bd31d952) --- erpnext/controllers/accounts_controller.py | 22 +++++++++++-------- .../controllers/sales_and_purchase_return.py | 6 ++--- erpnext/controllers/stock_controller.py | 20 ++++++++--------- erpnext/controllers/taxes_and_totals.py | 2 +- erpnext/controllers/trends.py | 4 ++-- 5 files changed, 29 insertions(+), 25 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 756fc6cc3d4..216c9f45d3f 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -227,7 +227,7 @@ class AccountsController(TransactionBase): for item in self.get("items"): if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): if not item.get(field_map.get(self.doctype)): - default_deferred_account = frappe.db.get_value( + default_deferred_account = frappe.get_cached_value( "Company", self.company, "default_" + field_map.get(self.doctype) ) if not default_deferred_account: @@ -676,7 +676,7 @@ class AccountsController(TransactionBase): def validate_enabled_taxes_and_charges(self): taxes_and_charges_doctype = self.meta.get_options("taxes_and_charges") - if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): + if frappe.get_cached_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): frappe.throw( _("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges) ) @@ -684,7 +684,7 @@ class AccountsController(TransactionBase): def validate_tax_account_company(self): for d in self.get("taxes"): if d.account_head: - tax_account_company = frappe.db.get_value("Account", d.account_head, "company") + tax_account_company = frappe.get_cached_value("Account", d.account_head, "company") if tax_account_company != self.company: frappe.throw( _("Row #{0}: Account {1} does not belong to company {2}").format( @@ -929,7 +929,9 @@ class AccountsController(TransactionBase): party_account = self.credit_to if is_purchase_invoice else self.debit_to party_type = "Supplier" if is_purchase_invoice else "Customer" - gain_loss_account = frappe.db.get_value("Company", self.company, "exchange_gain_loss_account") + gain_loss_account = frappe.get_cached_value( + "Company", self.company, "exchange_gain_loss_account" + ) if not gain_loss_account: frappe.throw( _("Please set default Exchange Gain/Loss Account in Company {}").format(self.get("company")) @@ -1026,7 +1028,7 @@ class AccountsController(TransactionBase): else self.grand_total ), "outstanding_amount": self.outstanding_amount, - "difference_account": frappe.db.get_value( + "difference_account": frappe.get_cached_value( "Company", self.company, "exchange_gain_loss_account" ), "exchange_gain_loss": flt(d.get("exchange_gain_loss")), @@ -1394,7 +1396,7 @@ class AccountsController(TransactionBase): @property def company_abbr(self): if not hasattr(self, "_abbr"): - self._abbr = frappe.db.get_value("Company", self.company, "abbr") + self._abbr = frappe.get_cached_value("Company", self.company, "abbr") return self._abbr @@ -1780,7 +1782,7 @@ class AccountsController(TransactionBase): """ if self.is_internal_transfer() and not self.unrealized_profit_loss_account: - unrealized_profit_loss_account = frappe.db.get_value( + unrealized_profit_loss_account = frappe.get_cached_value( "Company", self.company, "unrealized_profit_loss_account" ) @@ -1895,7 +1897,9 @@ class AccountsController(TransactionBase): @frappe.whitelist() def get_tax_rate(account_head): - return frappe.db.get_value("Account", account_head, ["tax_rate", "account_name"], as_dict=True) + return frappe.get_cached_value( + "Account", account_head, ["tax_rate", "account_name"], as_dict=True + ) @frappe.whitelist() @@ -1904,7 +1908,7 @@ def get_default_taxes_and_charges(master_doctype, tax_template=None, company=Non return {} if tax_template and company: - tax_template_company = frappe.db.get_value(master_doctype, tax_template, "company") + tax_template_company = frappe.get_cached_value(master_doctype, tax_template, "company") if tax_template_company == company: return diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 0054edbbd59..39ef68a9fb5 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -326,7 +326,7 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None): from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos company = frappe.db.get_value("Delivery Note", source_name, "company") - default_warehouse_for_sales_return = frappe.db.get_value( + default_warehouse_for_sales_return = frappe.get_cached_value( "Company", company, "default_warehouse_for_sales_return" ) @@ -340,11 +340,11 @@ def make_return_doc(doctype: str, source_name: str, target_doc=None): # look for Print Heading "Credit Note" if not doc.select_print_heading: - doc.select_print_heading = frappe.db.get_value("Print Heading", _("Credit Note")) + doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Credit Note")) elif doctype == "Purchase Invoice": # look for Print Heading "Debit Note" - doc.select_print_heading = frappe.db.get_value("Print Heading", _("Debit Note")) + doc.select_print_heading = frappe.get_cached_value("Print Heading", _("Debit Note")) for tax in doc.get("taxes") or []: if tax.charge_type == "Actual": diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 98dc58677b9..1e4fabe0d26 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -57,7 +57,7 @@ class StockController(AccountsController): make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) provisional_accounting_for_non_stock_items = cint( - frappe.db.get_value( + frappe.get_cached_value( "Company", self.company, "enable_provisional_accounting_for_non_stock_items" ) ) @@ -200,7 +200,7 @@ class StockController(AccountsController): elif self.get("is_internal_supplier"): warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"] - expense_account = frappe.db.get_value("Company", self.company, "default_expense_account") + expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account") gl_list.append( self.get_gl_dict( @@ -235,7 +235,7 @@ class StockController(AccountsController): if warehouse_with_no_account: for wh in warehouse_with_no_account: - if frappe.db.get_value("Warehouse", wh, "company"): + if frappe.get_cached_value("Warehouse", wh, "company"): frappe.throw( _( "Warehouse {0} is not linked to any account, please mention the account in the warehouse record or set default inventory account in company {1}." @@ -449,15 +449,15 @@ class StockController(AccountsController): # Get value based on doctype name if not sl_dict.get(dimension.target_fieldname): - fieldname = frappe.get_cached_value( - "DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" + fieldname = next( + ( + field.fieldname + for field in frappe.get_meta(self.doctype).fields + if field.options == dimension.fetch_from_parent + ), + None, ) - if not fieldname: - fieldname = frappe.get_cached_value( - "Custom Field", {"dt": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" - ) - if fieldname and self.get(fieldname): sl_dict[dimension.target_fieldname] = self.get(fieldname) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 81de6823782..c6a634ba806 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -1056,7 +1056,7 @@ class init_landed_taxes_and_totals(object): company_currency = erpnext.get_company_currency(self.doc.company) for d in self.doc.get(self.tax_field): if not d.account_currency: - account_currency = frappe.db.get_value("Account", d.expense_account, "account_currency") + account_currency = frappe.get_cached_value("Account", d.expense_account, "account_currency") d.account_currency = account_currency or company_currency def set_exchange_rate(self): diff --git a/erpnext/controllers/trends.py b/erpnext/controllers/trends.py index 1d6c5dc0be0..1fb722e1123 100644 --- a/erpnext/controllers/trends.py +++ b/erpnext/controllers/trends.py @@ -80,7 +80,7 @@ def get_data(filters, conditions): if conditions.get("trans") == "Quotation" and filters.get("group_by") == "Customer": cond += " and t1.quotation_to = 'Customer'" - year_start_date, year_end_date = frappe.db.get_value( + year_start_date, year_end_date = frappe.get_cached_value( "Fiscal Year", filters.get("fiscal_year"), ["year_start_date", "year_end_date"] ) @@ -275,7 +275,7 @@ def get_period_date_ranges(period, fiscal_year=None, year_start_date=None): from dateutil.relativedelta import relativedelta if not year_start_date: - year_start_date, year_end_date = frappe.db.get_value( + year_start_date, year_end_date = frappe.get_cached_value( "Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"] ) From 8c856cd5fc41c8603edf85e66db8ed1879b0ebb5 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Tue, 1 Nov 2022 19:30:06 +0530 Subject: [PATCH 6/7] fix: `Material Consumption` option in case of `Skip Transfer to WIP` in WO (cherry picked from commit 8ea69837348e4333f3b950a82b755076f58b8751) --- .../doctype/work_order/work_order.js | 101 +++++++++--------- 1 file changed, 52 insertions(+), 49 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 4aab3fa3734..62476182485 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -589,66 +589,69 @@ erpnext.work_order = { } } - if(!frm.doc.skip_transfer){ + if (frm.doc.status != 'Stopped') { // If "Material Consumption is check in Manufacturing Settings, allow Material Consumption - if (flt(doc.material_transferred_for_manufacturing) > 0 && frm.doc.status != 'Stopped') { - if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) { - frm.has_finish_btn = true; - - if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) { - // Only show "Material Consumption" when required_qty > consumed_qty - var counter = 0; - var tbl = frm.doc.required_items || []; - var tbl_lenght = tbl.length; - for (var i = 0, len = tbl_lenght; i < len; i++) { - let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; - if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { - counter += 1; - } - } - if (counter > 0) { - var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() { - const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on; - erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on); - }); - consumption_btn.addClass('btn-primary'); + if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) { + if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) { + // Only show "Material Consumption" when required_qty > consumed_qty + var counter = 0; + var tbl = frm.doc.required_items || []; + var tbl_lenght = tbl.length; + for (var i = 0, len = tbl_lenght; i < len; i++) { + let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; + if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { + counter += 1; } } + if (counter > 0) { + var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() { + const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on; + erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on); + }); + consumption_btn.addClass('btn-primary'); + } + } + } + if(!frm.doc.skip_transfer){ + if (flt(doc.material_transferred_for_manufacturing) > 0) { + if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) { + frm.has_finish_btn = true; + + var finish_btn = frm.add_custom_button(__('Finish'), function() { + erpnext.work_order.make_se(frm, 'Manufacture'); + }); + + if(doc.material_transferred_for_manufacturing>=doc.qty) { + // all materials transferred for manufacturing, make this primary + finish_btn.addClass('btn-primary'); + } + } else { + frappe.db.get_doc("Manufacturing Settings").then((doc) => { + let allowance_percentage = doc.overproduction_percentage_for_work_order; + + if (allowance_percentage > 0) { + let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); + + if ((flt(doc.produced_qty) < allowed_qty)) { + frm.add_custom_button(__('Finish'), function() { + erpnext.work_order.make_se(frm, 'Manufacture'); + }); + } + } + }); + } + } + } else { + if ((flt(doc.produced_qty) < flt(doc.qty))) { var finish_btn = frm.add_custom_button(__('Finish'), function() { erpnext.work_order.make_se(frm, 'Manufacture'); }); - - if(doc.material_transferred_for_manufacturing>=doc.qty) { - // all materials transferred for manufacturing, make this primary - finish_btn.addClass('btn-primary'); - } - } else { - frappe.db.get_doc("Manufacturing Settings").then((doc) => { - let allowance_percentage = doc.overproduction_percentage_for_work_order; - - if (allowance_percentage > 0) { - let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); - - if ((flt(doc.produced_qty) < allowed_qty)) { - frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); - }); - } - } - }); + finish_btn.addClass('btn-primary'); } } - } else { - if ((flt(doc.produced_qty) < flt(doc.qty)) && frm.doc.status != 'Stopped') { - var finish_btn = frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); - }); - finish_btn.addClass('btn-primary'); - } } } - }, calculate_cost: function(doc) { if (doc.operations){ From 0b09c31cb0cb174a79d2e26d520b869e31d02328 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" Date: Mon, 7 Nov 2022 11:45:47 +0530 Subject: [PATCH 7/7] fix: Increase columns width in Warehouse wise Item Balance Age and Value (cherry picked from commit 8355c1092c149a6697ef5e358f16670a3a0d9f1a) --- .../warehouse_wise_item_balance_age_and_value.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index eedf1a04601..b5c6764224b 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -91,8 +91,8 @@ def get_columns(filters): columns = [ _("Item") + ":Link/Item:180", _("Item Group") + "::100", - _("Value") + ":Currency:100", - _("Age") + ":Float:60", + _("Value") + ":Currency:120", + _("Age") + ":Float:80", ] return columns @@ -123,7 +123,7 @@ def get_warehouse_list(filters): def add_warehouse_column(columns, warehouse_list): if len(warehouse_list) > 1: - columns += [_("Total Qty") + ":Int:50"] + columns += [_("Total Qty") + ":Int:90"] for wh in warehouse_list: - columns += [_(wh.name) + ":Int:54"] + columns += [_(wh.name) + ":Int:120"]