From 29197dcd7fd56ad5ab962cb77362940f6b3c4bf0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 21:18:50 +0530 Subject: [PATCH 01/27] fix: Filter fixes in Accounts Payable report (cherry picked from commit a5a73ba85762e0940ea1c48f39b22f035923a860) --- erpnext/accounts/report/accounts_payable/accounts_payable.js | 2 ++ .../accounts/report/accounts_receivable/accounts_receivable.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 7cf14e67385..e1a30a4b77e 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -51,6 +51,8 @@ frappe.query_reports["Accounts Payable"] = { } else { frappe.query_report.set_filter_value('tax_id', ""); } + + frappe.query_report.refresh(); } }, { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 8557c03bd5e..f2ee1eb10ea 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -748,7 +748,7 @@ class ReceivablePayableReport(object): self.add_accounting_dimensions_filters() - def get_cost_center_conditions(self, conditions): + def get_cost_center_conditions(self): lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"]) cost_center_list = [ center.name From 953f78d6a9ac4e346f7a0ae71aaf10be95ae0cd9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 16:48:24 +0530 Subject: [PATCH 02/27] fix: Add condition for discount section collapse (cherry picked from commit 4cd65027c4a80acce8251c3d3b274c3963daad17) --- .../doctype/purchase_invoice_item/purchase_invoice_item.json | 4 +++- .../doctype/sales_invoice_item/sales_invoice_item.json | 3 ++- .../doctype/purchase_order_item/purchase_order_item.json | 3 ++- .../selling/doctype/sales_order_item/sales_order_item.json | 3 ++- .../stock/doctype/delivery_note_item/delivery_note_item.json | 3 ++- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 3 ++- 6 files changed, 13 insertions(+), 6 deletions(-) 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 9de90368871..a8f6f80b6b9 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -214,6 +214,7 @@ "reqd": 1 }, { + "default": "1", "depends_on": "eval:doc.uom != doc.stock_uom", "fieldname": "conversion_factor", "fieldtype": "Float", @@ -820,6 +821,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "section_break_26", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -871,7 +873,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-12 03:37:29.032732", + "modified": "2022-10-26 16:05:37.304788", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index a307a6c17cd..7f1a1eccc45 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -247,6 +247,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -876,7 +877,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-10 20:57:38.340026", + "modified": "2022-10-26 11:38:36.119339", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 82e92e87bc9..b8203bd1286 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -777,6 +777,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin_section", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -894,7 +895,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-09-07 11:12:38.634976", + "modified": "2022-10-26 16:47:41.364387", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 2cf836f9fcc..ea0b25f41cc 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -272,6 +272,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -842,7 +843,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-06 13:24:18.065312", + "modified": "2022-10-26 16:05:02.712705", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 0a5cbabab0b..77c32534321 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -261,6 +261,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -814,7 +815,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-12 03:36:05.344847", + "modified": "2022-10-26 16:05:17.720768", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 772736e0adc..474ee92e268 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -919,6 +919,7 @@ }, { "collapsible": 1, + "collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount", "fieldname": "discount_and_margin_section", "fieldtype": "Section Break", "label": "Discount and Margin" @@ -1000,7 +1001,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-12 03:37:59.516609", + "modified": "2022-10-26 16:06:02.524435", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 797512ca131ea70e844a5b9bf2b38f64605e945a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 11:02:14 +0530 Subject: [PATCH 03/27] fix: Company bank account filter in Bank Clearance (cherry picked from commit f9f78c1086a5d19f9aef7370c85ca76b404e4c63) --- .../doctype/bank_clearance/bank_clearance.js | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js index 63cc46518ff..7e57c2fc471 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js @@ -4,6 +4,23 @@ frappe.ui.form.on("Bank Clearance", { setup: function(frm) { frm.add_fetch("account", "account_currency", "account_currency"); + + frm.set_query("account", function() { + return { + "filters": { + "account_type": ["in",["Bank","Cash"]], + "is_group": 0, + } + }; + }); + + frm.set_query("bank_account", function () { + return { + filters: { + 'is_company_account': 1 + }, + }; + }); }, onload: function(frm) { @@ -12,14 +29,7 @@ frappe.ui.form.on("Bank Clearance", { locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: ""; frm.set_value("account", default_bank_account); - frm.set_query("account", function() { - return { - "filters": { - "account_type": ["in",["Bank","Cash"]], - "is_group": 0 - } - }; - }); + frm.set_value("from_date", frappe.datetime.month_start()); frm.set_value("to_date", frappe.datetime.month_end()); From de3996e411c29d3e4eaa0a948bad6e178eb91dfb Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 28 Oct 2022 11:36:14 +0530 Subject: [PATCH 04/27] chore: Added Material Request Reference in Purchase Recipt Dashboard for Tracking (cherry picked from commit a04c44fe34845b6e1e254fc86ea452a6ce8f3930) --- .../doctype/purchase_receipt/purchase_receipt_dashboard.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 06ba9365561..60e5fcffd06 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -1,6 +1,5 @@ from frappe import _ - def get_data(): return { "fieldname": "purchase_receipt_no", @@ -12,13 +11,14 @@ def get_data(): "Purchase Receipt": "return_against", }, "internal_links": { + "Material Request": ["items", "material_request"], "Purchase Order": ["items", "purchase_order"], "Project": ["items", "project"], "Quality Inspection": ["items", "quality_inspection"], }, "transactions": [ {"label": _("Related"), "items": ["Purchase Invoice", "Landed Cost Voucher", "Asset"]}, - {"label": _("Reference"), "items": ["Purchase Order", "Quality Inspection", "Project"]}, + {"label": _("Reference"), "items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"]}, {"label": _("Returns"), "items": ["Purchase Receipt"]}, {"label": _("Subscription"), "items": ["Auto Repeat"]}, ], From 0ab69c0e329c3a86bce992950b0709e00da71515 Mon Sep 17 00:00:00 2001 From: Vishal Date: Fri, 28 Oct 2022 11:59:10 +0530 Subject: [PATCH 05/27] chore: minor linting issue fixed (cherry picked from commit e8c01570176212322bf7b0688ece2f3fbf98a184) --- .../doctype/purchase_receipt/purchase_receipt_dashboard.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py index 60e5fcffd06..b3ae7b58b49 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_dashboard.py @@ -1,5 +1,6 @@ from frappe import _ + def get_data(): return { "fieldname": "purchase_receipt_no", @@ -18,7 +19,10 @@ def get_data(): }, "transactions": [ {"label": _("Related"), "items": ["Purchase Invoice", "Landed Cost Voucher", "Asset"]}, - {"label": _("Reference"), "items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"]}, + { + "label": _("Reference"), + "items": ["Material Request", "Purchase Order", "Quality Inspection", "Project"], + }, {"label": _("Returns"), "items": ["Purchase Receipt"]}, {"label": _("Subscription"), "items": ["Auto Repeat"]}, ], From 6eafff86941d49983b6c7934a580479b3781f5e4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 11:45:24 +0530 Subject: [PATCH 06/27] fix: Clear invoice table post importing invoices (cherry picked from commit 267e7c3a9001c56bf7c1af88e2dfdef175e0502f) --- .../opening_invoice_creation_tool.js | 13 +++---------- .../opening_invoice_creation_tool.py | 2 -- 2 files changed, 3 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js index 7eb5c4234d1..1f4166151a1 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.js @@ -22,13 +22,13 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { } if (data.user != frappe.session.user) return; if (data.count == data.total) { - setTimeout((title) => { + setTimeout(() => { frm.doc.import_in_progress = false; frm.clear_table("invoices"); frm.refresh_fields(); frm.page.clear_indicator(); - frm.dashboard.hide_progress(title); - frappe.msgprint(__("Opening {0} Invoice created", [frm.doc.invoice_type])); + frm.dashboard.hide_progress(); + frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type])); }, 1500, data.title); return; } @@ -51,13 +51,6 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { method: "make_invoices", freeze: 1, freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]), - callback: function(r) { - if (r.message.length == 1) { - frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type])); - } else if (r.message.length < 50) { - frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type])); - } - } }); }); diff --git a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py index 99377421c58..c566b420ba8 100644 --- a/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py +++ b/erpnext/accounts/doctype/opening_invoice_creation_tool/opening_invoice_creation_tool.py @@ -257,8 +257,6 @@ def start_import(invoices): def publish(index, total, doctype): - if total < 50: - return frappe.publish_realtime( "opening_invoice_creation_progress", dict( From 195500cb32b274b21ff63525bbe898f022bf1845 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 17:33:44 +0530 Subject: [PATCH 07/27] fix: Curreny in SOA print for multi-currency party (cherry picked from commit 49ee8736558d4c788ebb70cfddbd116e18d1e4b2) --- .../accounts/report/general_ledger/general_ledger.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 378fa3791c1..7e1df267b12 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -52,22 +52,22 @@ {% } %} - {%= format_currency(data[i].debit, filters.presentation_currency) %} + {%= format_currency(data[i].debit, data[i].account_currency) %} - {%= format_currency(data[i].credit, filters.presentation_currency) %} + {%= format_currency(data[i].credit, data[i].account_currency) %} {% } else { %} {%= frappe.format(data[i].account, {fieldtype: "Link"}) || " " %} - {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency) %} + {%= data[i].account && format_currency(data[i].debit, data[i].account_currency) %} - {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency) %} + {%= data[i].account && format_currency(data[i].credit, data[i].account_currency) %} {% } %} - {%= format_currency(data[i].balance, filters.presentation_currency) %} + {%= format_currency(data[i].balance, data[i].account_currency) %} {% } %} From 3f0b03c0a4b4d49db37b007fe646262f30d133ac Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 24 Oct 2022 19:13:02 +0530 Subject: [PATCH 08/27] chore: Use account currency as fallback (cherry picked from commit a18a715bb47c69578a435af326c0efb9e0baec23) --- .../accounts/report/general_ledger/general_ledger.html | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.html b/erpnext/accounts/report/general_ledger/general_ledger.html index 7e1df267b12..c04f518d7e6 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.html +++ b/erpnext/accounts/report/general_ledger/general_ledger.html @@ -52,22 +52,22 @@ {% } %} - {%= format_currency(data[i].debit, data[i].account_currency) %} + {%= format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %} - {%= format_currency(data[i].credit, data[i].account_currency) %} + {%= format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %} {% } else { %} {%= frappe.format(data[i].account, {fieldtype: "Link"}) || " " %} - {%= data[i].account && format_currency(data[i].debit, data[i].account_currency) %} + {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %} - {%= data[i].account && format_currency(data[i].credit, data[i].account_currency) %} + {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %} {% } %} - {%= format_currency(data[i].balance, data[i].account_currency) %} + {%= format_currency(data[i].balance, filters.presentation_currency || data[i].account_currency) %} {% } %} From d742e6d56b8c66df9f582a763da13a716faf4850 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 18:01:39 +0530 Subject: [PATCH 09/27] fix: Total Sales amount update in project via Sales Order (cherry picked from commit 6063c4e3c0bca9ff6ad3798888b307c839025d5c) --- erpnext/selling/doctype/sales_order/sales_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index f0e9e4b7d92..1f3419fd5dd 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -627,6 +627,7 @@ def make_project(source_name, target_doc=None): "field_map": { "name": "sales_order", "base_grand_total": "estimated_costing", + "net_total": "total_sales_amount", }, }, }, From faf25c0b950e72a164acaa892c1124e956110a09 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 26 Oct 2022 20:21:36 +0530 Subject: [PATCH 10/27] fix: Reference due date field type in Journal Entry Accounts table (cherry picked from commit e7caa48e2fd2ad7bd8f2a45cbe25c79c7008a8a2) --- erpnext/accounts/doctype/journal_entry/journal_entry.js | 3 +-- erpnext/accounts/doctype/journal_entry/journal_entry.py | 6 +++++- .../journal_entry_account/journal_entry_account.json | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 763e2e6992c..a5ff7f1aa79 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -312,8 +312,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro } } - get_outstanding(doctype, docname, company, child, due_date) { - var me = this; + get_outstanding(doctype, docname, company, child) { var args = { "doctype": doctype, "docname": docname, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 52690e1e662..de012b28ec8 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1210,6 +1210,7 @@ def get_outstanding(args): args = json.loads(args) company_currency = erpnext.get_company_currency(args.get("company")) + due_date = None if args.get("doctype") == "Journal Entry": condition = " and party=%(party)s" if args.get("party") else "" @@ -1234,10 +1235,12 @@ def get_outstanding(args): invoice = frappe.db.get_value( args["doctype"], args["docname"], - ["outstanding_amount", "conversion_rate", scrub(party_type)], + ["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"], as_dict=1, ) + due_date = invoice.get("due_date") + exchange_rate = ( invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1 ) @@ -1260,6 +1263,7 @@ def get_outstanding(args): "exchange_rate": exchange_rate, "party_type": party_type, "party": invoice.get(scrub(party_type)), + "reference_due_date": due_date, } diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index a0ea43332c9..47ad19e0f98 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -216,7 +216,7 @@ { "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])", "fieldname": "reference_due_date", - "fieldtype": "Select", + "fieldtype": "Date", "label": "Reference Due Date", "no_copy": 1 }, @@ -284,7 +284,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-10-13 17:07:17.999191", + "modified": "2022-10-26 20:03:10.906259", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", From c4587d8caa3486a299f6ad41d82050f907c29b48 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Oct 2022 14:04:43 +0530 Subject: [PATCH 11/27] refactor: rewrite `Stock Ledger Report` queries in `QB` (cherry picked from commit feaa2dbba8226adea4094d87fd783877364c1335) --- .../stock/report/stock_ledger/stock_ledger.py | 84 +++++++++++-------- 1 file changed, 47 insertions(+), 37 deletions(-) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index a95119736a0..af7f20f3ccf 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -305,20 +305,25 @@ def get_inventory_dimension_fields(): def get_items(filters): + item = frappe.qb.DocType("Item") + query = frappe.qb.from_(item).select(item.name) conditions = [] - if filters.get("item_code"): - conditions.append("item.name=%(item_code)s") + + if item_code := filters.get("item_code"): + conditions.append(item.name == item_code) else: - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - if filters.get("item_group"): - conditions.append(get_item_group_condition(filters.get("item_group"))) + if brand := filters.get("brand"): + conditions.append(item.brand == brand) + if item_group := filters.get("item_group"): + if condition := get_item_group_condition(item_group, item): + conditions.append(condition) items = [] if conditions: - items = frappe.db.sql_list( - """select name from `tabItem` item where {}""".format(" and ".join(conditions)), filters - ) + for condition in conditions: + query = query.where(condition) + items = [r[0] for r in query.run()] + return items @@ -330,29 +335,22 @@ def get_item_details(items, sl_entries, include_uom): if not items: return item_details - cf_field = cf_join = "" + item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(item) + .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom) + .where(item.name.isin(items)) + ) + if include_uom: - cf_field = ", ucd.conversion_factor" - cf_join = ( - "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%s" - % frappe.db.escape(include_uom) + ucd = frappe.qb.DocType("UOM Conversion Detail") + query = ( + query.left_join(ucd) + .on((ucd.parent == item.name) & (ucd.uom == include_uom)) + .select(ucd.conversion_factor) ) - res = frappe.db.sql( - """ - select - item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom {cf_field} - from - `tabItem` item - {cf_join} - where - item.name in ({item_codes}) - """.format( - cf_field=cf_field, cf_join=cf_join, item_codes=",".join(["%s"] * len(items)) - ), - items, - as_dict=1, - ) + res = query.run(as_dict=True) for item in res: item_details.setdefault(item.name, item) @@ -427,16 +425,28 @@ def get_warehouse_condition(warehouse): return "" -def get_item_group_condition(item_group): +def get_item_group_condition(item_group, item_table=None): item_group_details = frappe.db.get_value("Item Group", item_group, ["lft", "rgt"], as_dict=1) if item_group_details: - return ( - "item.item_group in (select ig.name from `tabItem Group` ig \ - where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)" - % (item_group_details.lft, item_group_details.rgt) - ) - - return "" + if item_table: + ig = frappe.qb.DocType("Item Group") + return item_table.item_group.isin( + ( + frappe.qb.from_(ig) + .select(ig.name) + .where( + (ig.lft >= item_group_details.lft) + & (ig.rgt <= item_group_details.rgt) + & (item_table.item_group == ig.name) + ) + ) + ) + else: + return ( + "item.item_group in (select ig.name from `tabItem Group` ig \ + where ig.lft >= %s and ig.rgt <= %s and item.item_group = ig.name)" + % (item_group_details.lft, item_group_details.rgt) + ) def check_inventory_dimension_filters_applied(filters) -> bool: From a4b99a34cb99de2adfee8602967d73e018913210 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Oct 2022 15:35:19 +0530 Subject: [PATCH 12/27] refactor: rewrite `Product Bundle Balance Report` queries in `QB` (cherry picked from commit cde785f1bb8692fbb6f22f554f56f5f463488ab9) --- .../product_bundle_balance.py | 158 +++++++++--------- 1 file changed, 83 insertions(+), 75 deletions(-) diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py index 854875a0532..9e75201bd14 100644 --- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py +++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.py @@ -4,7 +4,9 @@ import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import flt +from pypika.terms import ExistsCriterion from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition @@ -123,43 +125,65 @@ def get_items(filters): pb_details = frappe._dict() item_details = frappe._dict() - conditions = get_parent_item_conditions(filters) - parent_item_details = frappe.db.sql( - """ - select item.name as item_code, item.item_name, pb.description, item.item_group, item.brand, item.stock_uom - from `tabItem` item - inner join `tabProduct Bundle` pb on pb.new_item_code = item.name - where ifnull(item.disabled, 0) = 0 {0} - """.format( - conditions - ), - filters, - as_dict=1, - ) # nosec + item = frappe.qb.DocType("Item") + pb = frappe.qb.DocType("Product Bundle") + + query = ( + frappe.qb.from_(item) + .inner_join(pb) + .on(pb.new_item_code == item.name) + .select( + item.name.as_("item_code"), + item.item_name, + pb.description, + item.item_group, + item.brand, + item.stock_uom, + ) + .where(IfNull(item.disabled, 0) == 0) + ) + + if item_code := filters.get("item_code"): + query = query.where(item.item_code == item_code) + else: + if brand := filters.get("brand"): + query = query.where(item.brand == brand) + if item_group := filters.get("item_group"): + if conditions := get_item_group_condition(item_group, item): + query = query.where(conditions) + + parent_item_details = query.run(as_dict=True) parent_items = [] for d in parent_item_details: parent_items.append(d.item_code) item_details[d.item_code] = d + child_item_details = [] if parent_items: - child_item_details = frappe.db.sql( - """ - select - pb.new_item_code as parent_item, pbi.item_code, item.item_name, pbi.description, item.item_group, item.brand, - item.stock_uom, pbi.uom, pbi.qty - from `tabProduct Bundle Item` pbi - inner join `tabProduct Bundle` pb on pb.name = pbi.parent - inner join `tabItem` item on item.name = pbi.item_code - where pb.new_item_code in ({0}) - """.format( - ", ".join(["%s"] * len(parent_items)) - ), - parent_items, - as_dict=1, - ) # nosec - else: - child_item_details = [] + item = frappe.qb.DocType("Item") + pb = frappe.qb.DocType("Product Bundle") + pbi = frappe.qb.DocType("Product Bundle Item") + + child_item_details = ( + frappe.qb.from_(pbi) + .inner_join(pb) + .on(pb.name == pbi.parent) + .inner_join(item) + .on(item.name == pbi.item_code) + .select( + pb.new_item_code.as_("parent_item"), + pbi.item_code, + item.item_name, + pbi.description, + item.item_group, + item.brand, + item.stock_uom, + pbi.uom, + pbi.qty, + ) + .where(pb.new_item_code.isin(parent_items)) + ).run(as_dict=1) child_items = set() for d in child_item_details: @@ -184,58 +208,42 @@ def get_stock_ledger_entries(filters, items): if not items: return [] - item_conditions_sql = " and sle.item_code in ({})".format( - ", ".join(frappe.db.escape(i) for i in items) + sle = frappe.qb.DocType("Stock Ledger Entry") + sle2 = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(sle) + .force_index("posting_sort_index") + .left_join(sle2) + .on( + (sle.item_code == sle2.item_code) + & (sle.warehouse == sle2.warehouse) + & (sle.posting_date < sle2.posting_date) + & (sle.posting_time < sle2.posting_time) + & (sle.name < sle2.name) + ) + .select(sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company) + .where((sle2.name.isnull()) & (sle.docstatus < 2) & (sle.item_code.isin(items))) ) - conditions = get_sle_conditions(filters) - - return frappe.db.sql( - """ - select - sle.item_code, sle.warehouse, sle.qty_after_transaction, sle.company - from - `tabStock Ledger Entry` sle force index (posting_sort_index) - left join `tabStock Ledger Entry` sle2 on - sle.item_code = sle2.item_code and sle.warehouse = sle2.warehouse - and (sle.posting_date, sle.posting_time, sle.name) < (sle2.posting_date, sle2.posting_time, sle2.name) - where sle2.name is null and sle.docstatus < 2 %s %s""" - % (item_conditions_sql, conditions), - as_dict=1, - ) # nosec - - -def get_parent_item_conditions(filters): - conditions = [] - - if filters.get("item_code"): - conditions.append("item.item_code = %(item_code)s") + if date := filters.get("date"): + query = query.where(sle.posting_date <= date) else: - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - if filters.get("item_group"): - conditions.append(get_item_group_condition(filters.get("item_group"))) - - conditions = " and ".join(conditions) - return "and {0}".format(conditions) if conditions else "" - - -def get_sle_conditions(filters): - conditions = "" - if not filters.get("date"): frappe.throw(_("'Date' is required")) - conditions += " and sle.posting_date <= %s" % frappe.db.escape(filters.get("date")) - if filters.get("warehouse"): warehouse_details = frappe.db.get_value( "Warehouse", filters.get("warehouse"), ["lft", "rgt"], as_dict=1 ) - if warehouse_details: - conditions += ( - " and exists (select name from `tabWarehouse` wh \ - where wh.lft >= %s and wh.rgt <= %s and sle.warehouse = wh.name)" - % (warehouse_details.lft, warehouse_details.rgt) - ) # nosec - return conditions + if warehouse_details: + wh = frappe.qb.DocType("Warehouse") + query = query.where( + ExistsCriterion( + frappe.qb.from_(wh) + .select(wh.name) + .where((wh.lft >= warehouse_details.lft) & (wh.rgt <= warehouse_details.rgt)) + ) + ) + + return query.run(as_dict=True) From 98428f0bce44bb56d22488311aeac18e33fe7f8d Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Fri, 14 Oct 2022 16:33:02 +0530 Subject: [PATCH 13/27] refactor: rewrite `Itemwise Recommended Reorder Level Report` queries in `QB` (cherry picked from commit 40bd1215932689cc37619a2797298e55ed2235cc) --- .../itemwise_recommended_reorder_level.py | 130 +++++++++--------- 1 file changed, 68 insertions(+), 62 deletions(-) diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py index f308e9e41f1..a6fc049cbde 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.py @@ -3,6 +3,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Abs, Sum from frappe.utils import flt, getdate @@ -11,8 +12,6 @@ def execute(filters=None): filters = {} float_precision = frappe.db.get_default("float_precision") - condition = get_condition(filters) - avg_daily_outgoing = 0 diff = ((getdate(filters.get("to_date")) - getdate(filters.get("from_date"))).days) + 1 if diff <= 0: @@ -20,8 +19,8 @@ def execute(filters=None): columns = get_columns() items = get_item_info(filters) - consumed_item_map = get_consumed_items(condition) - delivered_item_map = get_delivered_items(condition) + consumed_item_map = get_consumed_items(filters) + delivered_item_map = get_delivered_items(filters) data = [] for item in items: @@ -71,76 +70,86 @@ def get_columns(): def get_item_info(filters): from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition - conditions = [get_item_group_condition(filters.get("item_group"))] - if filters.get("brand"): - conditions.append("item.brand=%(brand)s") - conditions.append("is_stock_item = 1") - - return frappe.db.sql( - """select name, item_name, description, brand, item_group, - safety_stock, lead_time_days from `tabItem` item where {}""".format( - " and ".join(conditions) - ), - filters, - as_dict=1, + item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(item) + .select( + item.name, + item.item_name, + item.description, + item.brand, + item.item_group, + item.safety_stock, + item.lead_time_days, + ) + .where(item.is_stock_item == 1) ) + if brand := filters.get("brand"): + query = query.where(item.brand == brand) -def get_consumed_items(condition): + if conditions := get_item_group_condition(filters.get("item_group"), item): + query = query.where(conditions) + + return query.run(as_dict=True) + + +def get_consumed_items(filters): purpose_to_exclude = [ "Material Transfer for Manufacture", "Material Transfer", "Send to Subcontractor", ] - condition += """ - and ( - purpose is NULL - or purpose not in ({}) + se = frappe.qb.DocType("Stock Entry") + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .left_join(se) + .on(sle.voucher_no == se.name) + .select(sle.item_code, Abs(Sum(sle.actual_qty)).as_("consumed_qty")) + .where( + (sle.actual_qty < 0) + & (sle.is_cancelled == 0) + & (sle.voucher_type.notin(["Delivery Note", "Sales Invoice"])) + & ((se.purpose.isnull()) | (se.purpose.notin(purpose_to_exclude))) ) - """.format( - ", ".join(f"'{p}'" for p in purpose_to_exclude) + .groupby(sle.item_code) ) - condition = condition.replace("posting_date", "sle.posting_date") + query = get_filtered_query(filters, sle, query) - consumed_items = frappe.db.sql( - """ - select item_code, abs(sum(actual_qty)) as consumed_qty - from `tabStock Ledger Entry` as sle left join `tabStock Entry` as se - on sle.voucher_no = se.name - where - actual_qty < 0 - and is_cancelled = 0 - and voucher_type not in ('Delivery Note', 'Sales Invoice') - %s - group by item_code""" - % condition, - as_dict=1, - ) + consumed_items = query.run(as_dict=True) consumed_items_map = {item.item_code: item.consumed_qty for item in consumed_items} return consumed_items_map -def get_delivered_items(condition): - dn_items = frappe.db.sql( - """select dn_item.item_code, sum(dn_item.stock_qty) as dn_qty - from `tabDelivery Note` dn, `tabDelivery Note Item` dn_item - where dn.name = dn_item.parent and dn.docstatus = 1 %s - group by dn_item.item_code""" - % (condition), - as_dict=1, +def get_delivered_items(filters): + parent = frappe.qb.DocType("Delivery Note") + child = frappe.qb.DocType("Delivery Note Item") + query = ( + frappe.qb.from_(parent) + .from_(child) + .select(child.item_code, Sum(child.stock_qty).as_("dn_qty")) + .where((parent.name == child.parent) & (parent.docstatus == 1)) + .groupby(child.item_code) ) + query = get_filtered_query(filters, parent, query) - si_items = frappe.db.sql( - """select si_item.item_code, sum(si_item.stock_qty) as si_qty - from `tabSales Invoice` si, `tabSales Invoice Item` si_item - where si.name = si_item.parent and si.docstatus = 1 and - si.update_stock = 1 %s - group by si_item.item_code""" - % (condition), - as_dict=1, + dn_items = query.run(as_dict=True) + + parent = frappe.qb.DocType("Sales Invoice") + child = frappe.qb.DocType("Sales Invoice Item") + query = ( + frappe.qb.from_(parent) + .from_(child) + .select(child.item_code, Sum(child.stock_qty).as_("si_qty")) + .where((parent.name == child.parent) & (parent.docstatus == 1) & (parent.update_stock == 1)) + .groupby(child.item_code) ) + query = get_filtered_query(filters, parent, query) + + si_items = query.run(as_dict=True) dn_item_map = {} for item in dn_items: @@ -152,13 +161,10 @@ def get_delivered_items(condition): return dn_item_map -def get_condition(filters): - conditions = "" +def get_filtered_query(filters, table, query): if filters.get("from_date") and filters.get("to_date"): - conditions += " and posting_date between '%s' and '%s'" % ( - filters["from_date"], - filters["to_date"], - ) + query = query.where(table.posting_date.between(filters["from_date"], filters["to_date"])) else: - frappe.throw(_("From and To dates required")) - return conditions + frappe.throw(_("From and To dates are required")) + + return query From a03ec0afb382e506bfdf2af109b89644d82c1fd7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Oct 2022 16:22:52 +0530 Subject: [PATCH 14/27] feat: additional filters on Payment terms report Filter on Status and Due dates (cherry picked from commit aadb6b1772815095a1778ce324e6877b6464d55a) --- .../payment_terms_status_for_sales_order.js | 30 ++++++++++++++++++- .../payment_terms_status_for_sales_order.py | 14 +++++++++ 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js index c068ae3b5a4..991ac719cdc 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js @@ -74,7 +74,35 @@ function get_filters() { ] } } - } + }, + { + "fieldname":"from_due_date", + "label": __("From Due Date"), + "fieldtype": "Date", + }, + { + "fieldname":"to_due_date", + "label": __("To Due Date"), + "fieldtype": "Date", + }, + { + "fieldname":"status", + "label": __("Status"), + "fieldtype": "MultiSelectList", + "width": 100, + get_data: function(txt) { + let status = ["Overdue", "Unpaid", "Completed", "Partly Paid"] + let options = [] + for (let option of status){ + options.push({ + "value": option, + "label": __(option), + "description": "" + }) + } + return options + } + }, ] return filters; } diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py index 91f4a5e50a5..8bf56865a7d 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py @@ -162,6 +162,12 @@ def build_filter_criterions(filters): if filters.item: qb_criterions.append(qb.DocType("Sales Order Item").item_code == filters.item) + if filters.from_due_date: + qb_criterions.append(qb.DocType("Payment Schedule").due_date.gte(filters.from_due_date)) + + if filters.to_due_date: + qb_criterions.append(qb.DocType("Payment Schedule").due_date.lte(filters.to_due_date)) + return qb_criterions @@ -279,11 +285,19 @@ def prepare_chart(s_orders): return chart +def filter_on_calculated_status(filters, sales_orders): + if filters.status and sales_orders: + return [x for x in sales_orders if x.status in filters.status] + return sales_orders + + def execute(filters=None): columns = get_columns() sales_orders, so_invoices = get_so_with_invoices(filters) sales_orders, so_invoices = set_payment_terms_statuses(sales_orders, so_invoices, filters) + sales_orders = filter_on_calculated_status(filters, sales_orders) + prepare_chart(sales_orders) data = sales_orders From 6114241ff2545cbede7bf55ccfa5b68dba07955b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Oct 2022 17:50:01 +0530 Subject: [PATCH 15/27] fix: key error in filter access (cherry picked from commit 4765f937eaa0dd6cf3397245a5ef55d27805df16) --- ...st_payment_terms_status_for_sales_order.py | 28 +++++++++++-------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py index 9d542f5079c..67c96af9017 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py @@ -77,12 +77,14 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase): sinv.insert() sinv.submit() columns, data, message, chart = execute( - { - "company": "_Test Company", - "period_start_date": "2021-06-01", - "period_end_date": "2021-06-30", - "item": item.item_code, - } + frappe._dict( + { + "company": "_Test Company", + "period_start_date": "2021-06-01", + "period_end_date": "2021-06-30", + "item": item.item_code, + } + ) ) expected_value = [ @@ -167,12 +169,14 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase): sinv.insert() sinv.submit() columns, data, message, chart = execute( - { - "company": "_Test Company", - "period_start_date": "2021-06-01", - "period_end_date": "2021-06-30", - "item": item.item_code, - } + frappe._dict( + { + "company": "_Test Company", + "period_start_date": "2021-06-01", + "period_end_date": "2021-06-30", + "item": item.item_code, + } + ) ) # report defaults to company currency. From 6dbb4a3b0d7b614acf73e2f61547ea620558db63 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 28 Oct 2022 18:14:51 +0530 Subject: [PATCH 16/27] test: due date filter on Payment Terms report (cherry picked from commit fed39a53cbbae7743b5aa65bba3f6f93044e751e) --- ...st_payment_terms_status_for_sales_order.py | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py index 67c96af9017..525ae8e7ea7 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/test_payment_terms_status_for_sales_order.py @@ -2,7 +2,7 @@ import datetime import frappe from frappe.tests.utils import FrappeTestCase -from frappe.utils import add_days +from frappe.utils import add_days, nowdate from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -342,3 +342,60 @@ class TestPaymentTermsStatusForSalesOrder(FrappeTestCase): with self.subTest(filters=filters): columns, data, message, chart = execute(filters) self.assertEqual(data, expected_values_for_group_filters[idx]) + + def test_04_due_date_filter(self): + self.create_payment_terms_template() + item = create_item(item_code="_Test Excavator 1", is_stock_item=0) + transaction_date = nowdate() + so = make_sales_order( + transaction_date=add_days(transaction_date, -30), + delivery_date=add_days(transaction_date, -15), + item=item.item_code, + qty=10, + rate=100000, + do_not_save=True, + ) + so.po_no = "" + so.taxes_and_charges = "" + so.taxes = "" + so.payment_terms_template = self.template.name + so.save() + so.submit() + + # make invoice with 60% of the total sales order value + sinv = make_sales_invoice(so.name) + sinv.taxes_and_charges = "" + sinv.taxes = "" + sinv.items[0].qty = 6 + sinv.insert() + sinv.submit() + columns, data, message, chart = execute( + frappe._dict( + { + "company": "_Test Company", + "item": item.item_code, + "from_due_date": add_days(transaction_date, -30), + "to_due_date": add_days(transaction_date, -15), + } + ) + ) + + expected_value = [ + { + "name": so.name, + "customer": so.customer, + "submitted": datetime.date.fromisoformat(add_days(transaction_date, -30)), + "status": "Completed", + "payment_term": None, + "description": "_Test 50-50", + "due_date": datetime.date.fromisoformat(add_days(transaction_date, -15)), + "invoice_portion": 50.0, + "currency": "INR", + "base_payment_amount": 500000.0, + "paid_amount": 500000.0, + "invoices": "," + sinv.name, + }, + ] + # Only the first term should be pulled + self.assertEqual(len(data), 1) + self.assertEqual(data, expected_value) From fc6389280ce790aa98c345578d3da39579c2cf25 Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sat, 29 Oct 2022 11:56:34 +0530 Subject: [PATCH 17/27] fix: add `Sales Order` reference in Material Request Dashboard (cherry picked from commit 15ebf4a0cf87360ce4265014fe23b2a95e171506) --- .../doctype/material_request/material_request_dashboard.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py index b073e6a22ee..691a8b39b1b 100644 --- a/erpnext/stock/doctype/material_request/material_request_dashboard.py +++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py @@ -4,10 +4,13 @@ from frappe import _ def get_data(): return { "fieldname": "material_request", + "internal_links": { + "Sales Order": ["items", "sales_order"], + }, "transactions": [ { "label": _("Reference"), - "items": ["Request for Quotation", "Supplier Quotation", "Purchase Order"], + "items": ["Sales Order", "Request for Quotation", "Supplier Quotation", "Purchase Order"], }, {"label": _("Stock"), "items": ["Stock Entry", "Purchase Receipt", "Pick List"]}, {"label": _("Manufacturing"), "items": ["Work Order"]}, From 403587329508822ec33cae87c86de562d26277e5 Mon Sep 17 00:00:00 2001 From: Hossein Yousefian <86075967+ihosseinu@users.noreply.github.com> Date: Sat, 29 Oct 2022 20:24:59 +0330 Subject: [PATCH 18/27] fix: Pass project to stock entry items fix: Pass project to stock entry items (cherry picked from commit 54c2ffc36b8976fdc0139a8cb06e5e0a9d4f19e5) --- erpnext/assets/doctype/asset_repair/asset_repair.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 8758e9c17db..d5913c59463 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -135,6 +135,7 @@ class AssetRepair(AccountsController): "basic_rate": stock_item.valuation_rate, "serial_no": stock_item.serial_no, "cost_center": self.cost_center, + "project": self.project, }, ) From 34bd7837e27cbc57ebedbdfaa1dc5745a4f36869 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 31 Oct 2022 19:01:54 +0530 Subject: [PATCH 19/27] fix: Reset advance paid amount on Oreder cancel and amend (cherry picked from commit 92f37ca111504eb3aa7092ec61db4513f24b2c61) --- erpnext/buying/doctype/purchase_order/purchase_order.js | 5 +++++ erpnext/selling/doctype/sales_order/sales_order.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index ddf81ca3aec..06fdea030c8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -101,6 +101,11 @@ frappe.ui.form.on("Purchase Order", { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); + + // On cancel and amending a purchase order with advance payment, reset advance paid amount + if (frm.is_new()) { + frm.set_value("advance_paid", 0) + } }, apply_tds: function(frm) { diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 386c12b6386..fb64772479b 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -124,6 +124,11 @@ frappe.ui.form.on("Sales Order", { return query; }); + // On cancel and amending a sales order with advance payment, reset advance paid amount + if (frm.is_new()) { + frm.set_value("advance_paid", 0) + } + frm.ignore_doctypes_on_cancel_all = ['Purchase Order']; }, From 89a1c83431950129bd305c35f012e07843763098 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 30 Oct 2022 19:33:27 +0530 Subject: [PATCH 20/27] fix: Budget validation for main cost center (cherry picked from commit 4e26d42d1723fcc82ed8f990fad4bf0f4a56d135) --- erpnext/accounts/doctype/budget/budget.py | 8 ++--- .../accounts/doctype/budget/test_budget.py | 33 +++++++++++++++++++ erpnext/accounts/general_ledger.py | 6 ++++ 3 files changed, 43 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 6ac3350c3b0..637ac7a04ce 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -107,7 +107,7 @@ class Budget(Document): self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###" -def validate_expense_against_budget(args): +def validate_expense_against_budget(args, expense_amount=0): args = frappe._dict(args) if args.get("company") and not args.fiscal_year: @@ -175,13 +175,13 @@ def validate_expense_against_budget(args): ) # nosec if budget_records: - validate_budget_records(args, budget_records) + validate_budget_records(args, budget_records, expense_amount) -def validate_budget_records(args, budget_records): +def validate_budget_records(args, budget_records, expense_amount): for budget in budget_records: if flt(budget.budget_amount): - amount = get_amount(args, budget) + amount = expense_amount or get_amount(args, budget) yearly_action, monthly_action = get_actions(args, budget) if monthly_action in ["Stop", "Warn"]: diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index c48c7d97a2a..11af9a29f6f 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -334,6 +334,39 @@ class TestBudget(unittest.TestCase): budget.cancel() jv.cancel() + def test_monthly_budget_against_main_cost_center(self): + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + from erpnext.accounts.doctype.cost_center_allocation.test_cost_center_allocation import ( + create_cost_center_allocation, + ) + + cost_centers = [ + "Main Budget Cost Center 1", + "Sub Budget Cost Center 1", + "Sub Budget Cost Center 2", + ] + + for cc in cost_centers: + create_cost_center(cost_center_name=cc, company="_Test Company") + + create_cost_center_allocation( + "_Test Company", + "Main Budget Cost Center 1 - _TC", + {"Sub Budget Cost Center 1 - _TC": 60, "Sub Budget Cost Center 2 - _TC": 40}, + ) + + make_budget(budget_against="Cost Center", cost_center="Main Budget Cost Center 1 - _TC") + + jv = make_journal_entry( + "_Test Account Cost for Goods Sold - _TC", + "_Test Bank - _TC", + 400000, + "Main Budget Cost Center 1 - _TC", + posting_date=nowdate(), + ) + + self.assertRaises(BudgetError, jv.submit) + def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): if budget_against_field == "project": diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index f4a50a5f915..6d164eef2be 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -128,6 +128,12 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None): new_gl_map = [] for d in gl_map: cost_center = d.get("cost_center") + + # Validate budget against main cost center + validate_expense_against_budget( + d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision) + ) + if cost_center and cost_center_allocation.get(cost_center): for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items(): gle = copy.deepcopy(d) From 1152ac3ff10c73acddb0ec9973baa2cada2e9345 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 27 Oct 2022 18:16:30 +0530 Subject: [PATCH 21/27] fix: duplicate custom fields for inventory dimension (cherry picked from commit 45ededbed52e61910476ac4df33b62d16ea89395) --- .../inventory_dimension/inventory_dimension.py | 14 ++++++++++---- .../test_inventory_dimension.py | 15 +++++++++++++++ 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 9e8c10b394d..7b99b0097bb 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -121,18 +121,24 @@ class InventoryDimension(Document): if self.apply_to_all_doctypes: for doctype in get_inventory_documents(): - custom_fields.setdefault(doctype[0], dimension_fields) - else: + if not field_exists(doctype[0], self.source_fieldname): + custom_fields.setdefault(doctype[0], dimension_fields) + elif not field_exists(self.document_type, self.source_fieldname): custom_fields.setdefault(self.document_type, dimension_fields) if not frappe.db.get_value( "Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname} - ): + ) and not field_exists("Stock Ledger Entry", self.target_fieldname): dimension_field = dimension_fields[1] dimension_field["fieldname"] = self.target_fieldname custom_fields["Stock Ledger Entry"] = dimension_field - create_custom_fields(custom_fields) + if custom_fields: + create_custom_fields(custom_fields) + + +def field_exists(doctype, fieldname) -> str or None: + return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name") @frappe.whitelist() diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 19ddc449f0e..52b3deb3f01 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -191,6 +191,21 @@ class TestInventoryDimension(FrappeTestCase): self.assertEqual(sle_rack, "Rack 1") + def test_check_standard_dimensions(self): + create_inventory_dimension( + reference_document="Project", + type_of_transaction="Outward", + dimension_name="Project", + apply_to_all_doctypes=0, + document_type="Stock Ledger Entry", + ) + + self.assertFalse( + frappe.db.get_value( + "Custom Field", {"fieldname": "project", "dt": "Stock Ledger Entry"}, "name" + ) + ) + def prepare_test_data(): if not frappe.db.exists("DocType", "Shelf"): From d1b2786f24559c46fbee02e354016243f9c0ecfe Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Tue, 1 Nov 2022 12:45:28 +0530 Subject: [PATCH 22/27] fix: pro_rata_amount calculation in assets tests (cherry picked from commit 65e855bfff6b0e2ca935c3f81a907ad9b6eb7e2d) --- erpnext/assets/doctype/asset/test_asset.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 370b13bb98d..5c1311d68a9 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -221,7 +221,7 @@ class TestAsset(AssetSetup): asset.precision("gross_purchase_amount"), ) pro_rata_amount, _, _ = asset.get_pro_rata_amt( - asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date + asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date ) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount) @@ -283,7 +283,7 @@ class TestAsset(AssetSetup): self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") pro_rata_amount, _, _ = asset.get_pro_rata_amt( - asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date + asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date ) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) From 9b66020fc70209196cda3d9b6e6a654b3b32d8c1 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Tue, 1 Nov 2022 16:39:32 +0530 Subject: [PATCH 24/27] feat(pricing rule): free qty rounding and recursion qty (#32577) Option to specify recursion start qty and repeating qty Co-authored-by: Deepesh Garg (cherry picked from commit 1d83fb20d6678a2495c128380969b673ebc41b1a) --- .../doctype/pricing_rule/pricing_rule.json | 27 ++++++++++++- .../doctype/pricing_rule/pricing_rule.py | 13 +++++++ .../doctype/pricing_rule/test_pricing_rule.py | 39 +++++++++++++++++++ .../accounts/doctype/pricing_rule/utils.py | 8 +++- erpnext/public/js/controllers/transaction.js | 3 +- 5 files changed, 86 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json index 6e7ebd1414d..ce9ce647db0 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.json +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.json @@ -52,7 +52,10 @@ "free_item_rate", "column_break_42", "free_item_uom", + "round_free_qty", "is_recursive", + "recurse_for", + "apply_recursion_over", "section_break_23", "valid_from", "valid_upto", @@ -578,12 +581,34 @@ "fieldtype": "Select", "label": "Naming Series", "options": "PRLE-.####" + }, + { + "default": "0", + "fieldname": "round_free_qty", + "fieldtype": "Check", + "label": "Round Free Qty" + }, + { + "depends_on": "is_recursive", + "description": "Give free item for every N quantity", + "fieldname": "recurse_for", + "fieldtype": "Float", + "label": "Recurse Every (As Per Transaction UOM)", + "mandatory_depends_on": "is_recursive" + }, + { + "default": "0", + "depends_on": "is_recursive", + "description": "Qty for which recursion isn't applicable.", + "fieldname": "apply_recursion_over", + "fieldtype": "Float", + "label": "Apply Recursion Over (As Per Transaction UOM)" } ], "icon": "fa fa-gift", "idx": 1, "links": [], - "modified": "2022-09-16 16:00:38.356266", + "modified": "2022-10-13 19:05:35.056304", "modified_by": "Administrator", "module": "Accounts", "name": "Pricing Rule", diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 826d71b12e9..ed46d85e3a4 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -24,6 +24,7 @@ class PricingRule(Document): self.validate_applicable_for_selling_or_buying() self.validate_min_max_amt() self.validate_min_max_qty() + self.validate_recursion() self.cleanup_fields_value() self.validate_rate_or_discount() self.validate_max_discount() @@ -109,6 +110,18 @@ class PricingRule(Document): if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt): throw(_("Min Amt can not be greater than Max Amt")) + def validate_recursion(self): + if self.price_or_product_discount != "Product": + return + if self.free_item or self.same_item: + if flt(self.recurse_for) <= 0: + self.recurse_for = 1 + if self.is_recursive: + if flt(self.apply_recursion_over) > flt(self.min_qty): + throw(_("Min Qty should be greater than Recurse Over Qty")) + if flt(self.apply_recursion_over) < 0: + throw(_("Recurse Over Qty cannot be less than 0")) + def cleanup_fields_value(self): for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]: fieldname = frappe.scrub(self.get(logic_field) or "") diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index fbe567824f2..79c7c12b413 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -943,6 +943,45 @@ class TestPricingRule(unittest.TestCase): si.delete() rule.delete() + def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self): + frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule") + test_record = { + "doctype": "Pricing Rule", + "title": "_Test Pricing Rule", + "apply_on": "Item Code", + "currency": "USD", + "items": [ + { + "item_code": "_Test Item", + } + ], + "selling": 1, + "rate": 0, + "min_qty": 3, + "max_qty": 7, + "price_or_product_discount": "Product", + "same_item": 1, + "free_qty": 1, + "round_free_qty": 1, + "is_recursive": 1, + "recurse_for": 2, + "company": "_Test Company", + } + frappe.get_doc(test_record.copy()).insert() + + # With pricing rule + so = make_sales_order(item_code="_Test Item", qty=5) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + self.assertEqual(so.items[1].qty, 2) + + so = make_sales_order(item_code="_Test Item", qty=7) + so.load_from_db() + self.assertEqual(so.items[1].is_free_item, 1) + self.assertEqual(so.items[1].item_code, "_Test Item") + self.assertEqual(so.items[1].qty, 4) + test_dependencies = ["Campaign"] diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 4c78d7261d0..35eed49da70 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -627,9 +627,13 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None): qty = pricing_rule.free_qty or 1 if pricing_rule.is_recursive: - transaction_qty = args.get("qty") if args else doc.total_qty + transaction_qty = ( + args.get("qty") if args else doc.total_qty + ) - pricing_rule.apply_recursion_over if transaction_qty: - qty = flt(transaction_qty) * qty + qty = flt(transaction_qty) * qty / pricing_rule.recurse_for + if pricing_rule.round_free_qty: + qty = round(qty) free_item_data_args = { "item_code": free_item, diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 7fecb18fad4..dd957c72ac6 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1404,7 +1404,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if (!r.exc && r.message) { me._set_values_for_item_list(r.message); if(item) me.set_gross_profit(item); - if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on") + if (me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on") } } }); @@ -1577,6 +1577,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe for (let key in pr_row) { row_to_modify[key] = pr_row[key]; } + this.frm.script_manager.copy_from_first_row("items", row_to_modify, ["expense_account", "income_account"]); }); // free_item_data is a temporary variable From 74a6479f707c7c849b5aac626e698dca94111a03 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Nov 2022 19:54:41 +0530 Subject: [PATCH 25/27] fix: Issues while cancel/amending Purchase Invoice with TDS enabled (cherry picked from commit f7c9258770a79aff0b951dc18c0b7c39f794b03f) --- .../accounts/doctype/purchase_invoice/purchase_invoice.js | 4 ++++ .../accounts/doctype/purchase_invoice/purchase_invoice.py | 5 ++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c3a9855ff44..39a623519a6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -569,6 +569,10 @@ frappe.ui.form.on("Purchase Invoice", { erpnext.queries.setup_queries(frm, "Warehouse", function() { return erpnext.queries.warehouse(frm.doc); }); + + if (frm.is_new()) { + frm.clear_table("tax_withheld_vouchers"); + } }, is_subcontracted: function(frm) { diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 3d74b8f139b..882a374046d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -71,6 +71,9 @@ class PurchaseInvoice(BuyingController): supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") self.set_onload("supplier_tds", supplier_tds) + if self.is_new(): + self.set("tax_withheld_vouchers", []) + def before_save(self): if not self.on_hold: self.release_date = "" @@ -1415,7 +1418,7 @@ class PurchaseInvoice(BuyingController): "Stock Ledger Entry", "Repost Item Valuation", "Payment Ledger Entry", - "Purchase Invoice", + "Tax Withheld Vouchers", ) self.update_advance_tax_references(cancel=1) From a260426dd471d7b186fb1f2a9352e48d06b0cd4e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Mon, 31 Oct 2022 19:58:46 +0530 Subject: [PATCH 26/27] fix: Mode of payment for returns in POS Sales Invoice (cherry picked from commit 06e8e28531e2584fd5499df1c233082657de12b0) --- erpnext/controllers/taxes_and_totals.py | 41 +++++++++++++++---------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index cbcccce5f71..b5836c9070a 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -889,24 +889,33 @@ class calculate_taxes_and_totals(object): self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) def set_total_amount_to_default_mop(self, total_amount_to_pay): - default_mode_of_payment = frappe.db.get_value( - "POS Payment Method", - {"parent": self.doc.pos_profile, "default": 1}, - ["mode_of_payment"], - as_dict=1, - ) - - if default_mode_of_payment: - self.doc.payments = [] - self.doc.append( - "payments", - { - "mode_of_payment": default_mode_of_payment.mode_of_payment, - "amount": total_amount_to_pay, - "default": 1, - }, + total_paid_amount = 0 + for payment in self.doc.get("payments"): + total_paid_amount += ( + payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount ) + pending_amount = total_amount_to_pay - total_paid_amount + + if pending_amount > 0: + default_mode_of_payment = frappe.db.get_value( + "POS Payment Method", + {"parent": self.doc.pos_profile, "default": 1}, + ["mode_of_payment"], + as_dict=1, + ) + + if default_mode_of_payment: + self.doc.payments = [] + self.doc.append( + "payments", + { + "mode_of_payment": default_mode_of_payment.mode_of_payment, + "amount": pending_amount, + "default": 1, + }, + ) + def get_itemised_tax_breakup_html(doc): if not doc.taxes: From 3f72156ea93235240dba7e4b7ef53e6a345252e0 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 1 Nov 2022 20:17:34 +0530 Subject: [PATCH 27/27] chore: Update tests (cherry picked from commit 5b74161195b3b006d47f29de5a97428effca527e) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 1ba782451b4..cb0d1a75a0c 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -965,7 +965,8 @@ class TestSalesInvoice(unittest.TestCase): pos_return.insert() pos_return.submit() - self.assertEqual(pos_return.get("payments")[0].amount, -1000) + self.assertEqual(pos_return.get("payments")[0].amount, -500) + self.assertEqual(pos_return.get("payments")[1].amount, -500) def test_pos_change_amount(self): make_pos_profile(