From 6157fed71c0a7b2cbe90cc81cb22f034b7d0a543 Mon Sep 17 00:00:00 2001 From: Mihir Kandoi Date: Wed, 19 Feb 2025 15:54:31 +0530 Subject: [PATCH 01/36] fix: source warehouse not fetched in bom creator --- .../doctype/bom_creator/bom_creator.py | 5 +++-- .../bom_creator_item/bom_creator_item.json | 9 ++++++++- .../bom_creator_item/bom_creator_item.py | 1 + .../bom_configurator/bom_configurator.bundle.js | 17 +++++++++++++++++ 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py index 45ce95b6d58..c4fb6345c93 100644 --- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.py +++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.py @@ -28,6 +28,8 @@ BOM_ITEM_FIELDS = [ "stock_uom", "conversion_factor", "do_not_explode", + "source_warehouse", + "allow_alternative_item", ] @@ -291,7 +293,6 @@ class BOMCreator(Document): "item": row.item_code, "bom_type": "Production", "quantity": row.qty, - "allow_alternative_item": 1, "bom_creator": self.name, "bom_creator_item": bom_creator_item, } @@ -315,7 +316,6 @@ class BOMCreator(Document): item_args.update( { "bom_no": bom_no, - "allow_alternative_item": 1, "allow_scrap_items": 1, "include_item_in_manufacturing": 1, } @@ -428,6 +428,7 @@ def add_sub_assembly(**kwargs): "do_not_explode": 1, "is_expandable": 1, "stock_uom": item_info.stock_uom, + "allow_alternative_item": kwargs.allow_alternative_item, }, ) diff --git a/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json b/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json index 1726f898751..a6e67b956cf 100644 --- a/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json +++ b/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.json @@ -15,6 +15,7 @@ "is_expandable", "sourced_by_supplier", "bom_created", + "allow_alternative_item", "description_section", "description", "quantity_and_rate_section", @@ -225,12 +226,18 @@ "label": "BOM Created", "no_copy": 1, "print_hide": 1 + }, + { + "default": "1", + "fieldname": "allow_alternative_item", + "fieldtype": "Check", + "label": "Allow Alternative Item" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-06-03 18:45:24.339532", + "modified": "2025-02-19 13:25:15.732496", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Creator Item", diff --git a/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py b/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py index e172f36224d..fdd3f77ae26 100644 --- a/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py +++ b/erpnext/manufacturing/doctype/bom_creator_item/bom_creator_item.py @@ -14,6 +14,7 @@ class BOMCreatorItem(Document): if TYPE_CHECKING: from frappe.types import DF + allow_alternative_item: DF.Check amount: DF.Currency base_amount: DF.Currency base_rate: DF.Currency diff --git a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js index b1019f67ca9..6d24751792c 100644 --- a/erpnext/public/js/bom_configurator/bom_configurator.bundle.js +++ b/erpnext/public/js/bom_configurator/bom_configurator.bundle.js @@ -210,6 +210,13 @@ class BOMConfigurator { [ { label: __("Item"), fieldname: "item_code", fieldtype: "Link", options: "Item", reqd: 1 }, { label: __("Qty"), fieldname: "qty", default: 1.0, fieldtype: "Float", reqd: 1 }, + { + label: __("Allow Alternative Item"), + fieldname: "allow_alternative_item", + default: 1.0, + fieldtype: "Check", + reqd: 1, + }, ], (data) => { if (!node.data.parent_id) { @@ -224,6 +231,7 @@ class BOMConfigurator { item_code: data.item_code, fg_reference_id: node.data.name || this.frm.doc.name, qty: data.qty, + allow_alternative_item: data.allow_alternative_item, }, callback: (r) => { view.events.load_tree(r, node); @@ -258,6 +266,7 @@ class BOMConfigurator { fg_item: node.data.value, fg_reference_id: node.data.name || this.frm.doc.name, bom_item: bom_item, + allow_alternative_item: bom_item.allow_alternative_item, }, callback: (r) => { view.events.load_tree(r, node); @@ -278,6 +287,14 @@ class BOMConfigurator { reqd: 1, read_only: read_only, }, + { + label: __("Allow Alternative Item"), + fieldname: "allow_alternative_item", + default: 1.0, + fieldtype: "Check", + reqd: 1, + read_only: read_only, + }, { fieldtype: "Column Break" }, { label: __("Qty"), From f609012f02c28317dec6a03f986ee25689879312 Mon Sep 17 00:00:00 2001 From: 0xD0M1M0 <76812428+0xD0M1M0@users.noreply.github.com> Date: Fri, 21 Feb 2025 22:21:09 +0100 Subject: [PATCH 02/36] fix: discount accounting for v15 --- .../doctype/payment_entry/payment_entry.py | 6 ++- .../payment_entry/test_payment_entry.py | 42 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 967b12599bf..d1533fe0131 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -3337,13 +3337,14 @@ def add_income_discount_loss(pe, doc, total_discount_percent) -> float: """Add loss on income discount in base currency.""" precision = doc.precision("total") base_loss_on_income = doc.get("base_total") * (total_discount_percent / 100) + positive_negative = -1 if pe.payment_type == "Pay" else 1 pe.append( "deductions", { "account": frappe.get_cached_value("Company", pe.company, "default_discount_account"), "cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), - "amount": flt(base_loss_on_income, precision), + "amount": flt(base_loss_on_income, precision) * positive_negative, }, ) @@ -3355,6 +3356,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float: tax_discount_loss = {} base_total_tax_loss = 0 precision = doc.precision("tax_amount_after_discount_amount", "taxes") + positive_negative = -1 if pe.payment_type == "Pay" else 1 # The same account head could be used more than once for tax in doc.get("taxes", []): @@ -3377,7 +3379,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float: "account": account, "cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), - "amount": flt(loss, precision), + "amount": flt(loss, precision) * positive_negative, }, ) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 5883d4e2f1f..e43ba85373c 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -282,6 +282,48 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(si.payment_schedule[0].paid_amount, 200.0) self.assertEqual(si.payment_schedule[1].paid_amount, 36.0) + def test_payment_entry_against_payment_terms_with_discount_on_pi(self): + pi = make_purchase_invoice(do_not_save=1) + create_payment_terms_template_with_discount() + pi.payment_terms_template = "Test Discount Template" + + frappe.db.set_value("Company", pi.company, "default_discount_account", "Write Off - _TC") + + pi.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "_Test Account Service Tax - _TC", + "cost_center": "_Test Cost Center - _TC", + "description": "Service Tax", + "rate": 18, + }, + ) + pi.save() + pi.submit() + + frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1) + pe_with_tax_loss = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Cash - _TC") + + self.assertEqual(pe_with_tax_loss.references[0].payment_term, "30 Credit Days with 10% Discount") + self.assertEqual(pe_with_tax_loss.payment_type, "Pay") + self.assertEqual(pe_with_tax_loss.references[0].allocated_amount, 295.0) + self.assertEqual(pe_with_tax_loss.paid_amount, 265.5) + self.assertEqual(pe_with_tax_loss.difference_amount, 0) + self.assertEqual(pe_with_tax_loss.deductions[0].amount, -25.0) # Loss on Income + self.assertEqual(pe_with_tax_loss.deductions[1].amount, -4.5) # Loss on Tax + self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC") + + frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0) + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Cash - _TC") + + self.assertEqual(pe.references[0].payment_term, "30 Credit Days with 10% Discount") + self.assertEqual(pe.payment_type, "Pay") + self.assertEqual(pe.references[0].allocated_amount, 295.0) + self.assertEqual(pe.paid_amount, 265.5) + self.assertEqual(pe.deductions[0].amount, -29.5) + self.assertEqual(pe.difference_amount, 0) + def test_payment_entry_against_payment_terms_with_discount(self): si = create_sales_invoice(do_not_save=1, qty=1, rate=200) create_payment_terms_template_with_discount() From 20c44878533e830a706a4d5f60a507a62d01038f Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Mon, 24 Feb 2025 12:42:05 +0530 Subject: [PATCH 03/36] fix(report): allow `Closed` purchase orders to be visible (cherry picked from commit 3b2879d3a1e81fd71cc34fa68543411b67ade36a) # Conflicts: # erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js --- .../purchase_order_analysis.js | 13 ++++++++++++- .../purchase_order_analysis.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 58657da9168..3fe5bbdcb81 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -52,9 +52,20 @@ frappe.query_reports["Purchase Order Analysis"] = { label: __("Status"), fieldtype: "MultiSelectList", width: "80", - options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed"], + options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed", "Closed"], get_data: function (txt) { +<<<<<<< HEAD let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"]; +======= + let status = [ + "To Pay", + "To Bill", + "To Receive", + "To Receive and Bill", + "Completed", + "Closed", + ]; +>>>>>>> 3b2879d3a1 (fix(report): allow `Closed` purchase orders to be visible) let options = []; for (let option of status) { options.push({ diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index f583ce3e6c8..3efb9b021a9 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -70,7 +70,7 @@ def get_data(filters): po.company, po_item.name, ) - .where((po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1)) + .where((po_item.parent == po.name) & (po.status.notin(("Stopped", "On Hold"))) & (po.docstatus == 1)) .groupby(po_item.name) .orderby(po.transaction_date) ) From 8799af974751998909fd0b73d611c39a87228a42 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 25 Feb 2025 15:59:46 +0530 Subject: [PATCH 04/36] fix(report): allow `Closed` sales orders to be visible (cherry picked from commit 2394e76e7d6c33bc23d0c9bbb4acf27a47f02a69) # Conflicts: # erpnext/selling/report/sales_order_analysis/sales_order_analysis.js --- .../sales_order_analysis/sales_order_analysis.js | 13 ++++++++++++- .../sales_order_analysis/sales_order_analysis.py | 2 +- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js index 5866fcbc845..3343c416749 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js @@ -53,10 +53,21 @@ frappe.query_reports["Sales Order Analysis"] = { fieldname: "status", label: __("Status"), fieldtype: "MultiSelectList", - options: ["To Pay", "To Bill", "To Deliver", "To Deliver and Bill", "Completed"], + options: ["To Pay", "To Bill", "To Deliver", "To Deliver and Bill", "Completed", "Closed"], width: "80", get_data: function (txt) { +<<<<<<< HEAD let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"]; +======= + let status = [ + "To Pay", + "To Bill", + "To Deliver", + "To Deliver and Bill", + "Completed", + "Closed", + ]; +>>>>>>> 2394e76e7d (fix(report): allow `Closed` sales orders to be visible) let options = []; for (let option of status) { options.push({ diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py index 8fcf29bd7a6..90c33c323ce 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.py @@ -86,7 +86,7 @@ def get_data(conditions, filters): ON sii.so_detail = soi.name and sii.docstatus = 1 WHERE soi.parent = so.name - and so.status not in ('Stopped', 'Closed', 'On Hold') + and so.status not in ('Stopped', 'On Hold') and so.docstatus = 1 {conditions} GROUP BY soi.name From 2221bf1cba47488ce897124ea92cf31bfaf4f84f Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 25 Feb 2025 17:13:09 +0530 Subject: [PATCH 05/36] fix(report): filter sales / purchase orders based on date filters (cherry picked from commit 936d7d434234c9e0447eb08a504f3a083db9ab42) --- .../purchase_order_analysis.js | 22 ++++++++++++++----- .../purchase_order_analysis.py | 8 ++++--- .../sales_order_analysis.js | 21 +++++++++++++----- 3 files changed, 37 insertions(+), 14 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 3fe5bbdcb81..8d254722a4f 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -19,6 +19,10 @@ frappe.query_reports["Purchase Order Analysis"] = { width: "80", reqd: 1, default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + on_change: (report) => { + report.set_filter_value("name", []); + report.refresh(); + }, }, { fieldname: "to_date", @@ -27,6 +31,10 @@ frappe.query_reports["Purchase Order Analysis"] = { width: "80", reqd: 1, default: frappe.datetime.get_today(), + on_change: (report) => { + report.set_filter_value("name", []); + report.refresh(); + }, }, { fieldname: "project", @@ -38,13 +46,17 @@ frappe.query_reports["Purchase Order Analysis"] = { { fieldname: "name", label: __("Purchase Order"), - fieldtype: "Link", + fieldtype: "MultiSelectList", width: "80", options: "Purchase Order", - get_query: () => { - return { - filters: { docstatus: 1 }, - }; + get_data: function (txt) { + let filters = { docstatus: 1 }; + + const from_date = frappe.query_report.get_filter_value("from_date"); + const to_date = frappe.query_report.get_filter_value("to_date"); + if (from_date && to_date) filters["transaction_date"] = ["between", [from_date, to_date]]; + + return frappe.db.get_link_options("Purchase Order", txt, filters); }, }, { diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index 3efb9b021a9..b6bf1d9f8da 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -75,9 +75,11 @@ def get_data(filters): .orderby(po.transaction_date) ) - for field in ("company", "name"): - if filters.get(field): - query = query.where(po[field] == filters.get(field)) + if filters.get("company"): + query = query.where(po.company == filters.get("company")) + + if filters.get("name"): + query = query.where(po.name.isin(filters.get("name"))) if filters.get("from_date") and filters.get("to_date"): query = query.where(po.transaction_date.between(filters.get("from_date"), filters.get("to_date"))) diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js index 3343c416749..e470672b3a5 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js @@ -19,6 +19,10 @@ frappe.query_reports["Sales Order Analysis"] = { width: "80", reqd: 1, default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + on_change: (report) => { + report.set_filter_value("sales_order", []); + report.refresh(); + }, }, { fieldname: "to_date", @@ -27,6 +31,10 @@ frappe.query_reports["Sales Order Analysis"] = { width: "80", reqd: 1, default: frappe.datetime.get_today(), + on_change: (report) => { + report.set_filter_value("sales_order", []); + report.refresh(); + }, }, { fieldname: "sales_order", @@ -35,12 +43,13 @@ frappe.query_reports["Sales Order Analysis"] = { width: "80", options: "Sales Order", get_data: function (txt) { - return frappe.db.get_link_options("Sales Order", txt); - }, - get_query: () => { - return { - filters: { docstatus: 1 }, - }; + let filters = { docstatus: 1 }; + + const from_date = frappe.query_report.get_filter_value("from_date"); + const to_date = frappe.query_report.get_filter_value("to_date"); + if (from_date && to_date) filters["transaction_date"] = ["between", [from_date, to_date]]; + + return frappe.db.get_link_options("Sales Order", txt, filters); }, }, { From faee8d6c5e54184cbc223f3a2dd1f21e5b41b63e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 11:16:37 +0530 Subject: [PATCH 06/36] fix: don't allow renaming account while system is actively in use (backport #46176) (#46210) fix: don't allow renaming account while system is actively in use (#46176) (cherry picked from commit 999f1cf96db3752ca816093a1f3b75236688ab71) Co-authored-by: Ankush Menat --- erpnext/accounts/doctype/account/account.py | 28 ++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index b510651e68f..4098084a802 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -4,7 +4,7 @@ import frappe from frappe import _, throw -from frappe.utils import cint, cstr +from frappe.utils import add_to_date, cint, cstr, pretty_date from frappe.utils.nestedset import NestedSet, get_ancestors_of, get_descendants_of import erpnext @@ -481,6 +481,7 @@ def get_account_autoname(account_number, account_name, company): @frappe.whitelist() def update_account_number(name, account_name, account_number=None, from_descendant=False): + _ensure_idle_system() account = frappe.get_cached_doc("Account", name) if not account: return @@ -542,6 +543,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda @frappe.whitelist() def merge_account(old, new): + _ensure_idle_system() # Validate properties before merging new_account = frappe.get_cached_doc("Account", new) old_account = frappe.get_cached_doc("Account", old) @@ -595,3 +597,27 @@ def sync_update_account_number_in_child( for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True): update_account_number(d["name"], account_name, account_number, from_descendant=True) + + +def _ensure_idle_system(): + # Don't allow renaming if accounting entries are actively being updated, there are two main reasons: + # 1. Correctness: It's next to impossible to ensure that renamed account is not being used *right now*. + # 2. Performance: Renaming requires locking out many tables entirely and severely degrades performance. + + if frappe.flags.in_test: + return + + try: + # We also lock inserts to GL entry table with for_update here. + last_gl_update = frappe.db.get_value("GL Entry", {}, "modified", for_update=True, wait=False) + except frappe.QueryTimeoutError: + # wait=False fails immediately if there's an active transaction. + last_gl_update = add_to_date(None, seconds=-1) + + if last_gl_update > add_to_date(None, minutes=-5): + frappe.throw( + _( + "Last GL Entry update was done {}. This operation is not allowed while system is actively being used. Please wait for 5 minutes before retrying." + ).format(pretty_date(last_gl_update)), + title=_("System In Use"), + ) From 899e468f6a70000a10f104a9db5fc53e36ee160e Mon Sep 17 00:00:00 2001 From: venkat102 Date: Fri, 28 Feb 2025 15:14:25 +0530 Subject: [PATCH 07/36] fix(asset depreciation schedules): enable auto commit (cherry picked from commit a4b24f7451c7d7c156d97dda62824c4ba09045a4) --- .../v15_0/create_asset_depreciation_schedules_from_assets.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py index ff77fbb91ec..d4350d8f9a1 100644 --- a/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py +++ b/erpnext/patches/v15_0/create_asset_depreciation_schedules_from_assets.py @@ -82,6 +82,9 @@ def get_asset_depreciation_schedules_map(): .orderby(ds.idx) ).run(as_dict=True) + if len(records) > 20000: + frappe.db.auto_commit_on_many_writes = True + asset_depreciation_schedules_map = frappe._dict() for d in records: asset_depreciation_schedules_map.setdefault((d.asset_name, cstr(d.finance_book)), []).append(d) From c247cf888b03cfcf172d90110611a89e23c16338 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 3 Mar 2025 13:38:38 +0530 Subject: [PATCH 08/36] fix: incorrectly billed amount in the purchase receipt (cherry picked from commit a5271fdb2e5c826ec24162ddd101ae95ed9fdcd7) --- .../purchase_receipt/purchase_receipt.py | 6 +++- .../purchase_receipt/test_purchase_receipt.py | 30 +++++++++++++++++++ 2 files changed, 35 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 0328c447ec2..7aa23c8153b 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1048,15 +1048,19 @@ def get_billed_amount_against_po(po_items): if not po_items: return {} + purchase_invoice = frappe.qb.DocType("Purchase Invoice") purchase_invoice_item = frappe.qb.DocType("Purchase Invoice Item") query = ( frappe.qb.from_(purchase_invoice_item) + .inner_join(purchase_invoice) + .on(purchase_invoice_item.parent == purchase_invoice.name) .select(fn.Sum(purchase_invoice_item.amount).as_("billed_amt"), purchase_invoice_item.po_detail) .where( (purchase_invoice_item.po_detail.isin(po_items)) - & (purchase_invoice_item.docstatus == 1) + & (purchase_invoice.docstatus == 1) & (purchase_invoice_item.pr_detail.isnull()) + & (purchase_invoice.update_stock == 0) ) .groupby(purchase_invoice_item.po_detail) ).run(as_dict=1) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index b097c0e6441..9cf9f4d4958 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -4047,6 +4047,36 @@ class TestPurchaseReceipt(FrappeTestCase): batch_return.save() batch_return.submit() + def test_pr_status_based_on_invoices_with_update_stock(self): + from erpnext.buying.doctype.purchase_order.purchase_order import ( + make_purchase_invoice as _make_purchase_invoice, + ) + from erpnext.buying.doctype.purchase_order.purchase_order import ( + make_purchase_receipt as _make_purchase_receipt, + ) + from erpnext.buying.doctype.purchase_order.test_purchase_order import ( + create_pr_against_po, + create_purchase_order, + ) + + item_code = "Test Item for PR Status Based on Invoices" + create_item(item_code) + + po = create_purchase_order(item_code=item_code, qty=10) + pi = _make_purchase_invoice(po.name) + pi.update_stock = 1 + pi.items[0].qty = 5 + pi.submit() + + po.reload() + self.assertEqual(po.per_billed, 50) + + pr = _make_purchase_receipt(po.name) + self.assertEqual(pr.items[0].qty, 5) + pr.submit() + pr.reload() + self.assertEqual(pr.status, "To Bill") + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From e94f0b1ccaa6b2cf96c67cd4013c76343b668ff3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 2 Mar 2025 12:36:17 +0530 Subject: [PATCH 09/36] fix: incorrect batch picked (cherry picked from commit d2564cad684d998dac15995017d79566525dee3c) --- erpnext/public/js/utils/serial_no_batch_selector.js | 2 ++ erpnext/stock/serial_batch_bundle.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index de1faf36ef5..e02d7a3d785 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -540,6 +540,8 @@ erpnext.SerialBatchPackageSelector = class SerialNoBatchBundleUpdate { has_batch_no: this.item.has_batch_no, qty: qty, based_on: based_on, + posting_date: this.frm.doc.posting_date, + posting_time: this.frm.doc.posting_time, }, callback: (r) => { if (r.message) { diff --git a/erpnext/stock/serial_batch_bundle.py b/erpnext/stock/serial_batch_bundle.py index 993d918f8bc..c88df01665f 100644 --- a/erpnext/stock/serial_batch_bundle.py +++ b/erpnext/stock/serial_batch_bundle.py @@ -1006,6 +1006,10 @@ class SerialBatchCreation: elif self.has_serial_no and not self.get("serial_nos"): self.serial_nos = get_serial_nos_for_outward(kwargs) elif not self.has_serial_no and self.has_batch_no and not self.get("batches"): + if self.get("posting_date"): + kwargs["posting_date"] = self.get("posting_date") + kwargs["posting_time"] = self.get("posting_time") + self.batches = get_available_batches(kwargs) def set_auto_serial_batch_entries_for_inward(self): From 90dea426d8adc86a56a37666e1f3f7b274b0df2f Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 18 Feb 2025 12:52:33 +0530 Subject: [PATCH 10/36] fix: set taxes before calculating taxes and totals (cherry picked from commit 0fd0695bbbaec834bd120b4fdc93bc7142f60532) --- erpnext/controllers/accounts_controller.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bb0a4070981..884ce4de71b 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -194,6 +194,14 @@ class AccountsController(TransactionBase): self.set_incoming_rate() self.init_internal_values() + # Need to set taxes based on taxes_and_charges template + # before calculating taxes and totals + if self.meta.get_field("taxes_and_charges"): + self.validate_enabled_taxes_and_charges() + self.validate_tax_account_company() + + self.set_taxes_and_charges() + if self.meta.get_field("currency"): self.calculate_taxes_and_totals() @@ -204,10 +212,6 @@ class AccountsController(TransactionBase): self.validate_all_documents_schedule() - if self.meta.get_field("taxes_and_charges"): - self.validate_enabled_taxes_and_charges() - self.validate_tax_account_company() - self.validate_party() self.validate_currency() self.validate_party_account_currency() @@ -252,8 +256,6 @@ class AccountsController(TransactionBase): self.validate_deferred_income_expense_account() self.set_inter_company_account() - self.set_taxes_and_charges() - if self.doctype == "Purchase Invoice": self.calculate_paid_amount() # apply tax withholding only if checked and applicable From 41e9a10ab413fd4d2c279aedb0fe08ff05426f2b Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Tue, 18 Feb 2025 14:03:35 +0530 Subject: [PATCH 11/36] test: validate fetching of taxes based on taxes and charges template (cherry picked from commit 196ef7ac4e496785ac7fb5dbff2e13c9317071bc) --- .../tests/test_accounts_controller.py | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 2c46f04af71..ccabb361b32 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -931,6 +931,35 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_pe, []) + @IntegrationTestCase.change_settings("Accounts Settings", {"add_taxes_from_item_tax_template": 1}) + def test_18_fetch_taxes_based_on_taxes_and_charges_template(self): + # Create a Sales Taxes and Charges Template + if not frappe.db.exists("Sales Taxes and Charges Template", "_Test Tax - _TC"): + doc = frappe.new_doc("Sales Taxes and Charges Template") + doc.company = self.company + doc.title = "_Test Tax" + doc.append( + "taxes", + { + "charge_type": "On Net Total", + "account_head": "Sales Expenses - _TC", + "description": "Test taxes", + "rate": 9, + }, + ) + doc.insert() + + # Create a Sales Invoice + sinv = frappe.new_doc("Sales Invoice") + sinv.customer = self.customer + sinv.company = self.company + sinv.currency = "INR" + sinv.taxes_and_charges = "_Test Tax - _TC" + sinv.append("items", {"item_code": "_Test Item", "qty": 1, "rate": 50}) + sinv.insert() + + self.assertEqual(sinv.total_taxes_and_charges, 4.5) + def test_20_journal_against_sales_invoice(self): # Invoice in Foreign Currency si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1) From 5e083861a419b6ead0071df9004f5c3d1c6f4977 Mon Sep 17 00:00:00 2001 From: Ninad1306 Date: Fri, 28 Feb 2025 11:08:30 +0530 Subject: [PATCH 12/36] fix: exclude cancelled gl entries (cherry picked from commit 3251a331dd76767e51f49dfb3cbbad6e932cb7cc) --- .../report/budget_variance_report/budget_variance_report.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py index e540aa9993c..db42d23a839 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.py +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.py @@ -263,6 +263,7 @@ def get_actual_details(name, filters): and ba.account=gl.account and b.{budget_against} = gl.{budget_against} and gl.fiscal_year between %s and %s + and gl.is_cancelled = 0 and b.{budget_against} = %s and exists( select From 35df539da3d059c82f798541a80ab2e5ccc555c7 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Mon, 3 Mar 2025 17:02:15 +0530 Subject: [PATCH 13/36] chore: fix linters --- erpnext/controllers/tests/test_accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index ccabb361b32..f959cbd0488 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -931,7 +931,7 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_pe, []) - @IntegrationTestCase.change_settings("Accounts Settings", {"add_taxes_from_item_tax_template": 1}) + @change_settings("Accounts Settings", {"add_taxes_from_item_tax_template": 1}) def test_18_fetch_taxes_based_on_taxes_and_charges_template(self): # Create a Sales Taxes and Charges Template if not frappe.db.exists("Sales Taxes and Charges Template", "_Test Tax - _TC"): From 8ed512f6c6e7cced1e48fb93ed71e9a08a46a8aa Mon Sep 17 00:00:00 2001 From: Nirmalrajaa K Date: Fri, 21 Feb 2025 14:57:16 +0530 Subject: [PATCH 14/36] fix: Batch Price gets updated only if it is a billed item (cherry picked from commit 9597b1a69e965638d68c7018fc564ba71e9d4246) # Conflicts: # erpnext/stock/get_item_details.py --- erpnext/stock/get_item_details.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 5c5fe5db276..1cc4cb750db 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1051,8 +1051,14 @@ def get_batch_based_item_price(params, item_code) -> float: if not item_price: item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True) +<<<<<<< HEAD if item_price and item_price[0][2] == params.get("uom"): return item_price[0][1] +======= + + if item_price and item_price[0].uom == pctx.uom and params.get("is_free_item") == 0: + return item_price[0].price_list_rate +>>>>>>> 9597b1a69e (fix: Batch Price gets updated only if it is a billed item) return 0.0 From dbd47dff985019d18a5b68de44149e81dd1663cf Mon Sep 17 00:00:00 2001 From: Nirmalrajaa K Date: Sat, 22 Feb 2025 22:04:00 +0530 Subject: [PATCH 15/36] fix: Batch Price gets updated only if it is a billed item (cherry picked from commit 1a56b83054003cde73aa2aa7c16f80e82be58a20) # Conflicts: # erpnext/stock/get_item_details.py --- erpnext/stock/get_item_details.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 1cc4cb750db..71a58e7f836 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1051,12 +1051,16 @@ def get_batch_based_item_price(params, item_code) -> float: if not item_price: item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True) +<<<<<<< HEAD <<<<<<< HEAD if item_price and item_price[0][2] == params.get("uom"): return item_price[0][1] ======= +======= + is_free_item = pctx.get('items', [{}])[0].get('is_free_item') +>>>>>>> 1a56b83054 (fix: Batch Price gets updated only if it is a billed item) - if item_price and item_price[0].uom == pctx.uom and params.get("is_free_item") == 0: + if item_price and item_price[0].uom == pctx.uom and not is_free_item: return item_price[0].price_list_rate >>>>>>> 9597b1a69e (fix: Batch Price gets updated only if it is a billed item) From 6762dc3392db930d920f5042eadb95ddf4d83998 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 3 Mar 2025 14:01:20 +0530 Subject: [PATCH 16/36] chore: linter fix (cherry picked from commit 0a2193e4589a9e748041001a8eb592be2bf091ce) # Conflicts: # erpnext/stock/get_item_details.py --- erpnext/stock/get_item_details.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 71a58e7f836..66c5ae28478 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1051,6 +1051,7 @@ def get_batch_based_item_price(params, item_code) -> float: if not item_price: item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True) +<<<<<<< HEAD <<<<<<< HEAD <<<<<<< HEAD if item_price and item_price[0][2] == params.get("uom"): @@ -1059,6 +1060,9 @@ def get_batch_based_item_price(params, item_code) -> float: ======= is_free_item = pctx.get('items', [{}])[0].get('is_free_item') >>>>>>> 1a56b83054 (fix: Batch Price gets updated only if it is a billed item) +======= + is_free_item = pctx.get("items", [{}])[0].get("is_free_item") +>>>>>>> 0a2193e458 (chore: linter fix) if item_price and item_price[0].uom == pctx.uom and not is_free_item: return item_price[0].price_list_rate From f3cafef6a744e728b74299ac55c757fde4d94ba9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 3 Mar 2025 21:37:02 +0530 Subject: [PATCH 17/36] fix(patch): Ensure SLE indexes (backport #46131) (#46135) * fix(patch): Ensure SLE indexes (#46131) Because of the way this change was pushed in parts, some sites don't see this as "update" and don't have the new indexes. (cherry picked from commit f62aa8fc57dcf4f2d4282811db1c85ff4259a2a5) # Conflicts: # erpnext/patches.txt * fix: resolved conflict --------- Co-authored-by: Ankush Menat Co-authored-by: Nabin Hait --- erpnext/patches.txt | 1 + .../stock/doctype/stock_ledger_entry/patches/__init__.py | 0 .../stock_ledger_entry/patches/ensure_sle_indexes.py | 9 +++++++++ 3 files changed, 10 insertions(+) create mode 100644 erpnext/stock/doctype/stock_ledger_entry/patches/__init__.py create mode 100644 erpnext/stock/doctype/stock_ledger_entry/patches/ensure_sle_indexes.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index cb10fcce1c4..df1b885ad18 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -394,3 +394,4 @@ execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_post erpnext.patches.v14_0.disable_add_row_in_gross_profit erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment erpnext.patches.v14_0.update_posting_datetime +erpnext.stock.doctype.stock_ledger_entry.patches.ensure_sle_indexes diff --git a/erpnext/stock/doctype/stock_ledger_entry/patches/__init__.py b/erpnext/stock/doctype/stock_ledger_entry/patches/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/doctype/stock_ledger_entry/patches/ensure_sle_indexes.py b/erpnext/stock/doctype/stock_ledger_entry/patches/ensure_sle_indexes.py new file mode 100644 index 00000000000..7f29b27af3f --- /dev/null +++ b/erpnext/stock/doctype/stock_ledger_entry/patches/ensure_sle_indexes.py @@ -0,0 +1,9 @@ +from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import ( + on_doctype_update as create_sle_indexes, +) + + +def execute(): + """Ensure SLE Indexes""" + + create_sle_indexes() From ef195513d0056f9b8069532e310e4902f469c564 Mon Sep 17 00:00:00 2001 From: Sanket Shah <113279972+Sanket322@users.noreply.github.com> Date: Mon, 3 Mar 2025 21:38:11 +0530 Subject: [PATCH 18/36] fix: Convert tuple of tuples to list of dicts for dot notation access (#46062) fix: use as_dict to convert tuples into list of dict Co-authored-by: Sanket322 (cherry picked from commit e4b0ab6656e9caf5d22fdc8ee8ac400cc99c1c9d) --- .../doctype/tax_withholding_category/tax_withholding_category.py | 1 + 1 file changed, 1 insertion(+) 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 06549973242..a355e5ddf44 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -436,6 +436,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): tax_details.get("tax_withholding_category"), company, ), + as_dict=1, ) for d in journal_entries_details: From 0a65217423035b0fe6e8a2934f1bd2aed85e2ac9 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Thu, 27 Feb 2025 13:17:57 +0530 Subject: [PATCH 19/36] fix: if invoice is return then add amount in proper column (cherry picked from commit ccb4bdbe4cd7e11118621968f4097aa20fc0dd1d) --- .../accounts_receivable/accounts_receivable.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 1ddf9bce06f..c7a0da5afe9 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -267,6 +267,18 @@ class ReceivablePayableReport: row.invoiced_in_account_currency += amount_in_account_currency else: if self.is_invoice(ple): + # when invoice has is_return marked + if self.invoice_details.get(row.voucher_no, {}).get("is_return"): + # for Credit Note + if row.voucher_type == "Sales Invoice": + row.credit_note -= amount + row.credit_note_in_account_currency -= amount_in_account_currency + # for Debit Note + else: + row.invoiced -= amount + row.invoiced_in_account_currency -= amount_in_account_currency + return + if row.voucher_no == ple.voucher_no == ple.against_voucher_no: row.paid -= amount row.paid_in_account_currency -= amount_in_account_currency @@ -421,7 +433,7 @@ class ReceivablePayableReport: # nosemgrep si_list = frappe.db.sql( """ - select name, due_date, po_no + select name, due_date, po_no, is_return from `tabSales Invoice` where posting_date <= %s and company = %s @@ -453,7 +465,7 @@ class ReceivablePayableReport: # nosemgrep for pi in frappe.db.sql( """ - select name, due_date, bill_no, bill_date + select name, due_date, bill_no, bill_date, is_return from `tabPurchase Invoice` where posting_date <= %s From 9f4311e7fbaa4ad607747f61a3a4b95ab61b75a2 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Thu, 27 Feb 2025 16:52:13 +0530 Subject: [PATCH 20/36] fix: fixing test case (cherry picked from commit 9b2b477ae05f1d0d6ccb5ce990061956e90acaef) --- .../accounts_receivable/test_accounts_receivable.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 39ca78153c3..f3513286c9e 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -204,7 +204,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): expected_data_after_credit_note = [ [100.0, 100.0, 40.0, 0.0, 60.0, si.name], - [0, 0, 100.0, 0.0, -100.0, cr_note.name], + [0, 0, 0, 100.0, -100.0, cr_note.name], ] self.assertEqual(len(report[1]), 2) si_row = next( @@ -478,13 +478,19 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): report = execute(filters)[1] self.assertEqual(len(report), 2) - expected_data = {sr.name: [10.0, -10.0, 0.0, -10], si.name: [100.0, 100.0, 10.0, 90.0]} + expected_data = {sr.name: [0.0, 10.0, -10.0, 0.0, -10], si.name: [100.0, 0.0, 100.0, 10.0, 90.0]} rows = report[:2] for row in rows: self.assertEqual( expected_data[row.voucher_no], - [row.invoiced or row.paid, row.outstanding, row.remaining_balance, row.future_amount], + [ + row.invoiced or row.paid, + row.credit_note, + row.outstanding, + row.remaining_balance, + row.future_amount, + ], ) pe.cancel() From a8b31df65ddeae3e0fcee7895dbafc9691034f10 Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Thu, 27 Feb 2025 17:14:29 +0530 Subject: [PATCH 21/36] fix: test case for debit note (cherry picked from commit 6719bbeb10156f1c0d7b148e178cafaa3014e248) --- .../accounts_payable/test_accounts_payable.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index 8971dc3d37b..69f332d9800 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -38,6 +38,23 @@ class TestAccountsPayable(AccountsTestMixin, FrappeTestCase): self.assertEqual(data[1][0].get("outstanding"), 300) self.assertEqual(data[1][0].get("currency"), "USD") + def test_account_payable_for_debit_note(self): + pi = self.create_purchase_invoice(do_not_submit=True) + pi.is_return = 1 + pi.items[0].qty = -1 + pi = pi.save().submit() + + filters = { + "company": self.company, + "party_type": "Supplier", + "party": [self.supplier], + "report_date": today(), + "range": "30, 60, 90, 120", + } + + data = execute(filters) + self.assertEqual(data[1][0].get("invoiced"), 300) + def create_purchase_invoice(self, do_not_submit=False): frappe.set_user("Administrator") pi = make_purchase_invoice( From 854632dd510c5371307d543d1cf04ce3c7e5cfae Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 3 Mar 2025 20:28:24 +0530 Subject: [PATCH 22/36] chore: resolve conflict --- erpnext/stock/get_item_details.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 66c5ae28478..c592d31f4dd 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -1051,22 +1051,10 @@ def get_batch_based_item_price(params, item_code) -> float: if not item_price: item_price = get_item_price(params, item_code, ignore_party=True, force_batch_no=True) -<<<<<<< HEAD -<<<<<<< HEAD -<<<<<<< HEAD - if item_price and item_price[0][2] == params.get("uom"): - return item_price[0][1] -======= -======= - is_free_item = pctx.get('items', [{}])[0].get('is_free_item') ->>>>>>> 1a56b83054 (fix: Batch Price gets updated only if it is a billed item) -======= - is_free_item = pctx.get("items", [{}])[0].get("is_free_item") ->>>>>>> 0a2193e458 (chore: linter fix) + is_free_item = params.get("items", [{}])[0].get("is_free_item") - if item_price and item_price[0].uom == pctx.uom and not is_free_item: - return item_price[0].price_list_rate ->>>>>>> 9597b1a69e (fix: Batch Price gets updated only if it is a billed item) + if item_price and item_price[0][2] == params.get("uom") and not is_free_item: + return item_price[0][1] return 0.0 From 3a03865a8f057283a0ce757acf920ca6d1750e46 Mon Sep 17 00:00:00 2001 From: Sugesh393 Date: Wed, 5 Feb 2025 18:40:21 +0530 Subject: [PATCH 23/36] fix: change voucher_type and voucher_no field type to data (cherry picked from commit f8ab02192037171e4f8de7d79edfd9978238ac09) # Conflicts: # erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json --- .../tax_withheld_vouchers.json | 14 ++++++++------ .../tax_withheld_vouchers/tax_withheld_vouchers.py | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json index 46b430c6594..4b1f586b60c 100644 --- a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json +++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json @@ -13,17 +13,15 @@ "fields": [ { "fieldname": "voucher_type", - "fieldtype": "Link", + "fieldtype": "Data", "in_list_view": 1, - "label": "Voucher Type", - "options": "DocType" + "label": "Voucher Type" }, { "fieldname": "voucher_name", - "fieldtype": "Dynamic Link", + "fieldtype": "Data", "in_list_view": 1, - "label": "Voucher Name", - "options": "voucher_type" + "label": "Voucher Name" }, { "fieldname": "taxable_amount", @@ -36,7 +34,11 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-01-13 13:40:41.479208", +======= + "modified": "2025-02-05 16:39:14.863698", +>>>>>>> f8ab021920 (fix: change voucher_type and voucher_no field type to data) "modified_by": "Administrator", "module": "Accounts", "name": "Tax Withheld Vouchers", diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py index bc2003e2bea..dbb69a2e769 100644 --- a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py +++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.py @@ -18,8 +18,8 @@ class TaxWithheldVouchers(Document): parentfield: DF.Data parenttype: DF.Data taxable_amount: DF.Currency - voucher_name: DF.DynamicLink | None - voucher_type: DF.Link | None + voucher_name: DF.Data | None + voucher_type: DF.Data | None # end: auto-generated types pass From 489efda98512bd347f79cc36abb8dd470f1f063a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Mar 2025 15:47:03 +0530 Subject: [PATCH 24/36] chore: resolve conflict --- .../doctype/tax_withheld_vouchers/tax_withheld_vouchers.json | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json index 4b1f586b60c..51dc3674594 100644 --- a/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json +++ b/erpnext/accounts/doctype/tax_withheld_vouchers/tax_withheld_vouchers.json @@ -34,11 +34,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-01-13 13:40:41.479208", -======= "modified": "2025-02-05 16:39:14.863698", ->>>>>>> f8ab021920 (fix: change voucher_type and voucher_no field type to data) "modified_by": "Administrator", "module": "Accounts", "name": "Tax Withheld Vouchers", From 83dcbec86af4fe281ac3abde7fec90efe0269679 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 4 Mar 2025 16:43:55 +0530 Subject: [PATCH 25/36] chore: resolve conflicts --- .../report/purchase_order_analysis/purchase_order_analysis.js | 4 ---- .../report/sales_order_analysis/sales_order_analysis.js | 4 ---- 2 files changed, 8 deletions(-) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 8d254722a4f..99b4c26ac8e 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -66,9 +66,6 @@ frappe.query_reports["Purchase Order Analysis"] = { width: "80", options: ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed", "Closed"], get_data: function (txt) { -<<<<<<< HEAD - let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"]; -======= let status = [ "To Pay", "To Bill", @@ -77,7 +74,6 @@ frappe.query_reports["Purchase Order Analysis"] = { "Completed", "Closed", ]; ->>>>>>> 3b2879d3a1 (fix(report): allow `Closed` purchase orders to be visible) let options = []; for (let option of status) { options.push({ diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js index e470672b3a5..b7f7a34c1b8 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js @@ -65,9 +65,6 @@ frappe.query_reports["Sales Order Analysis"] = { options: ["To Pay", "To Bill", "To Deliver", "To Deliver and Bill", "Completed", "Closed"], width: "80", get_data: function (txt) { -<<<<<<< HEAD - let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"]; -======= let status = [ "To Pay", "To Bill", @@ -76,7 +73,6 @@ frappe.query_reports["Sales Order Analysis"] = { "Completed", "Closed", ]; ->>>>>>> 2394e76e7d (fix(report): allow `Closed` sales orders to be visible) let options = []; for (let option of status) { options.push({ From 78a329e5738c92e34e6cac0350263aa40b93c940 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Thu, 13 Feb 2025 14:56:11 +0530 Subject: [PATCH 26/36] fix: auto allocation for negative amount outstanding for Customers in Payment Entry (cherry picked from commit 6275b44a0bdc016d6ed492475cf474c0e94e7a89) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index d1533fe0131..45462398e1c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1843,7 +1843,7 @@ class PaymentEntry(AccountsController): allocated_positive_outstanding = paid_amount + allocated_negative_outstanding - elif self.party_type in ("Supplier", "Employee"): + elif self.party_type in ("Supplier", "Customer"): if paid_amount > total_negative_outstanding: if total_negative_outstanding == 0: frappe.msgprint( From eee500f20e8c9df46e72cf7151d777016dbdc089 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 4 Mar 2025 12:49:48 +0530 Subject: [PATCH 27/36] fix: do not include opening invoices in billed items to be received report (cherry picked from commit c1ddf444c65b50ec59f95e6a88b7e263d303aadc) --- .../billed_items_to_be_received/billed_items_to_be_received.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py index f6efc8a685c..dc6192e7544 100644 --- a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py +++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.py @@ -27,6 +27,7 @@ def get_report_filters(report_filters): ["Purchase Invoice", "docstatus", "=", 1], ["Purchase Invoice", "per_received", "<", 100], ["Purchase Invoice", "update_stock", "=", 0], + ["Purchase Invoice", "is_opening", "!=", "Yes"], ] if report_filters.get("purchase_invoice"): From 1630979f0556de8fba6941de64f727e0b90ecbfc Mon Sep 17 00:00:00 2001 From: Sanket322 Date: Thu, 20 Feb 2025 11:33:53 +0530 Subject: [PATCH 28/36] refactor: add new line ragardless of postal code (cherry picked from commit 746adfd057cb77cbc601da96dcd069b3bc50027a) --- erpnext/regional/address_template/templates/united_states.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/address_template/templates/united_states.html b/erpnext/regional/address_template/templates/united_states.html index 77fce46b9d7..f00f99c1299 100644 --- a/erpnext/regional/address_template/templates/united_states.html +++ b/erpnext/regional/address_template/templates/united_states.html @@ -1,4 +1,4 @@ {{ address_line1 }}
{% if address_line2 %}{{ address_line2 }}
{% endif -%} -{{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}
{% endif -%} +{{ city }}, {% if state %}{{ state }}{% endif -%}{% if pincode %} {{ pincode }}{% endif -%}
{% if country != "United States" %}{{ country }}{% endif -%} From ddcf79da1da6dfe5b9cd7f367bf86c7c41992b21 Mon Sep 17 00:00:00 2001 From: mhh008 Date: Tue, 4 Mar 2025 13:56:19 +0100 Subject: [PATCH 29/36] fix: translation DE --- erpnext/translations/de.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 9ef1d4bc63a..aa8d7322958 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -5098,7 +5098,7 @@ Percentage you are allowed to transfer more against the quantity ordered. For ex PUR-ORD-.YYYY.-,PUR-ORD-.YYYY.-, Get Items from Open Material Requests,Hole Artikel von offenen Material Anfragen, Fetch items based on Default Supplier.,Abrufen von Elementen basierend auf dem Standardlieferanten., -Required By,Benötigt von, +Required By,Benötigt bis, Order Confirmation No,Auftragsbestätigung Nr, Order Confirmation Date,Auftragsbestätigungsdatum, Customer Mobile No,Mobilnummer des Kunden, From 8bd71954f3d67efbc88a3994f8870e63ef662af5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 28 Feb 2025 19:28:20 +0530 Subject: [PATCH 30/36] fix: stock reservation issue while making Purchase Invoice (cherry picked from commit 64985bffe049ecdb58d41760f89a142c7c41ed07) --- erpnext/stock/stock_ledger.py | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ca08a5ef121..aaeb90b7d30 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -561,12 +561,28 @@ class update_entries_after: self.new_items_found = False self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict()) self.affected_transactions: set[tuple[str, str]] = set() - self.reserved_stock = flt(self.args.reserved_stock) + self.reserved_stock = self.get_reserved_stock() self.data = frappe._dict() self.initialize_previous_data(self.args) self.build() + def get_reserved_stock(self): + sre = frappe.qb.DocType("Stock Reservation Entry") + posting_datetime = get_combine_datetime(self.args.posting_date, self.args.posting_time) + query = ( + frappe.qb.from_(sre) + .select(Sum(sre.reserved_qty) - Sum(sre.delivered_qty)) + .where( + (sre.item_code == self.item_code) + & (sre.warehouse == self.args.warehouse) + & (sre.docstatus == 1) + & (sre.creation <= posting_datetime) + ) + ).run() + + return flt(query[0][0]) if query else 0.0 + def set_precision(self): self.flt_precision = cint(frappe.db.get_default("float_precision")) or 2 self.currency_precision = get_field_precision( From 75bc68b8631a2c38fe82989ce65c203742334a14 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 4 Mar 2025 20:01:50 +0530 Subject: [PATCH 31/36] fix: rate changing on the deliver note (cherry picked from commit 6f40849d55d75979c9b116caba6cf4656a94ddec) --- erpnext/controllers/accounts_controller.py | 10 +++-- .../doctype/sales_order/test_sales_order.py | 39 +++++++++++++++++++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 884ce4de71b..6ed55a0b55e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -823,11 +823,15 @@ class AccountsController(TransactionBase): and item.get("use_serial_batch_fields") ) ): - if fieldname == "batch_no" and not item.batch_no and not item.is_free_item: - item.set("rate", ret.get("rate")) - item.set("price_list_rate", ret.get("price_list_rate")) item.set(fieldname, value) + if fieldname == "batch_no" and item.batch_no and not item.is_free_item: + if ret.get("rate"): + item.set("rate", ret.get("rate")) + + if not item.get("price_list_rate") and ret.get("price_list_rate"): + item.set("price_list_rate", ret.get("price_list_rate")) + elif fieldname in ["cost_center", "conversion_factor"] and not item.get( fieldname ): diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 47d42b0a9d5..003ffd5ac82 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -2097,6 +2097,45 @@ class TestSalesOrder(AccountsTestMixin, FrappeTestCase): frappe.db.set_single_value("Stock Settings", "update_existing_price_list_rate", 0) frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 0) + def test_delivery_note_rate_on_change_of_warehouse(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + item = make_item( + "_Test Batch Item for Delivery Note Rate", + { + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BH-SDDTBIFRM-.#####", + }, + ) + + frappe.db.set_single_value("Stock Settings", "auto_insert_price_list_rate_if_missing", 1) + so = make_sales_order( + item_code=item.name, rate=27648.00, price_list_rate=27648.00, qty=1, do_not_submit=True + ) + + so.items[0].rate = 90 + so.save() + self.assertTrue(so.items[0].discount_amount == 27558.0) + so.submit() + + warehouse = create_warehouse("NW Warehouse FOR Rate", company=so.company) + + make_stock_entry( + item_code=item.name, + qty=2, + target=warehouse, + basic_rate=100, + company=so.company, + use_serial_batch_fields=1, + ) + + dn = make_delivery_note(so.name) + dn.items[0].warehouse = warehouse + dn.save() + + self.assertEqual(dn.items[0].rate, 90) + def test_credit_limit_on_so_reopning(self): # set credit limit company = "_Test Company" From 41ab7f3f7c0a968171e2866cdd856584f67513a9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:28:22 +0530 Subject: [PATCH 32/36] perf: don't track seen for POS Invoice (backport #46187) (#46189) * perf: don't track seen for POS Invoice (#46187) This is a moving doctype. Do people even browse the list view? It doesn't make much sense, either. POS INvoices are rarely "reviewed" by multiple users. (cherry picked from commit ded0aab680932730273c6544256632b72ebed5e5) # Conflicts: # erpnext/accounts/doctype/pos_invoice/pos_invoice.json * chore: conflicts --------- Co-authored-by: Ankush Menat --- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 42861140494..c15309df294 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1623,6 +1623,5 @@ "states": [], "timeline_field": "customer", "title_field": "title", - "track_changes": 1, - "track_seen": 1 -} \ No newline at end of file + "track_changes": 1 +} From 1b00de18151b12f0b96df53ecc6493f192b600a8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 11:35:52 +0530 Subject: [PATCH 33/36] chore: erpnext.com -> frappe.io/erpnext (backport #46288) (#46290) * chore: erpnext.com -> frappe.io/erpnext (#46288) (cherry picked from commit 41fe30ea6e339e98633a73444f443c4b463bbe57) # Conflicts: # README.md * Update README.md --------- Co-authored-by: Ankush Menat --- erpnext/hooks.py | 4 ++-- erpnext/setup/install.py | 2 +- erpnext/templates/includes/footer/footer_powered.html | 2 +- package.json | 2 +- pyproject.toml | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 2b23cca886e..21f301f2450 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -4,7 +4,7 @@ app_publisher = "Frappe Technologies Pvt. Ltd." app_description = """ERP made simple""" app_icon = "fa fa-th" app_color = "#e74c3c" -app_email = "info@erpnext.com" +app_email = "hello@frappe.io" app_license = "GNU General Public License (v3)" source_link = "https://github.com/frappe/erpnext" app_logo_url = "/assets/erpnext/images/erpnext-logo.svg" @@ -484,7 +484,7 @@ email_brand_image = "assets/erpnext/images/erpnext-logo.jpg" default_mail_footer = """ Sent via - + ERPNext diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 97ec418d955..23ffd49de5d 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -16,7 +16,7 @@ from erpnext.setup.doctype.incoterm.incoterm import create_incoterms from .default_success_action import get_default_success_action default_mail_footer = """
Sent via - ERPNext
""" + ERPNext""" def after_install(): diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index 8310063e575..fb73931d18e 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -1 +1 @@ -{{ _("Powered by {0}").format('ERPNext') }} +{{ _("Powered by {0}").format('ERPNext') }} diff --git a/package.json b/package.json index 4e686f7ca74..509fe275e05 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "git", "url": "git+https://github.com/frappe/erpnext.git" }, - "homepage": "https://erpnext.com", + "homepage": "https://frappe.io/erpnext", "author": "Frappe Technologies Pvt. Ltd.", "license": "GPL-3.0", "bugs": { diff --git a/pyproject.toml b/pyproject.toml index d891b186d89..e122b2d176c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -70,6 +70,6 @@ docstring-code-format = true [project.urls] -Homepage = "https://erpnext.com/" +Homepage = "https://frappe.io/erpnext" Repository = "https://github.com/frappe/erpnext.git" "Bug Reports" = "https://github.com/frappe/erpnext/issues" From 7759775ee6696038c241466e120edefd193253a7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 12:04:17 +0530 Subject: [PATCH 34/36] fix: Add permission check in POS's `Toggle Recent Orders` (backport #46010) (#46274) fix: use get_list to check permissions (cherry picked from commit a08bc6b913b932231abfd72ebf3acf82cf32288c) Co-authored-by: Sanket322 --- erpnext/selling/page/point_of_sale/point_of_sale.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.py b/erpnext/selling/page/point_of_sale/point_of_sale.py index b86a87983d5..7f758f4c8db 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.py +++ b/erpnext/selling/page/point_of_sale/point_of_sale.py @@ -320,13 +320,13 @@ def get_past_order_list(search_term, status, limit=20): invoice_list = [] if search_term and status: - invoices_by_customer = frappe.db.get_all( + invoices_by_customer = frappe.db.get_list( "POS Invoice", filters={"customer": ["like", f"%{search_term}%"], "status": status}, fields=fields, page_length=limit, ) - invoices_by_name = frappe.db.get_all( + invoices_by_name = frappe.db.get_list( "POS Invoice", filters={"name": ["like", f"%{search_term}%"], "status": status}, fields=fields, @@ -335,7 +335,7 @@ def get_past_order_list(search_term, status, limit=20): invoice_list = invoices_by_customer + invoices_by_name elif status: - invoice_list = frappe.db.get_all( + invoice_list = frappe.db.get_list( "POS Invoice", filters={"status": status}, fields=fields, page_length=limit ) From 88234bbf9a6ca7c42ff093469a323ac751112185 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 12:19:31 +0530 Subject: [PATCH 35/36] fix: Include additional account types for Expense Account in LCV (backport #46206) (#46296) fix: Include additional account types for Expense Account in LCV (#46206) fix: additional account types in filters for the Expense account selection (cherry picked from commit 59e99f167d4abbd1959092a7d606d782940d7827) Co-authored-by: Priyansh Shah <108476017+priyanshshah2442@users.noreply.github.com> --- erpnext/public/js/utils/landed_taxes_and_charges_common.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/js/utils/landed_taxes_and_charges_common.js b/erpnext/public/js/utils/landed_taxes_and_charges_common.js index 2cb30160453..7d801ca91e6 100644 --- a/erpnext/public/js/utils/landed_taxes_and_charges_common.js +++ b/erpnext/public/js/utils/landed_taxes_and_charges_common.js @@ -14,6 +14,10 @@ erpnext.landed_cost_taxes_and_charges = { "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation", + "Expense Account", + "Direct Expense", + "Indirect Expense", + "Stock Received But Not Billed", ], ], company: frm.doc.company, From d02d0059139f088b343962ed20932e86216ceb6d Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 5 Mar 2025 12:40:13 +0530 Subject: [PATCH 36/36] fix: use valuation method from settings in stock ageing report (backport #46068) (#46297) fix: use valuation method from settings in stock ageing report (cherry picked from commit da09c278c8869fa671bdcd1c4844d169b0bb085a) Co-authored-by: Mihir Kandoi --- .../stock/report/stock_ageing/stock_ageing.py | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index cc597e14196..0edce832aec 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -51,6 +51,10 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li latest_age = date_diff(to_date, fifo_queue[-1][1]) range_values = get_range_age(filters, fifo_queue, to_date, item_dict) + check_and_replace_valuations_if_moving_average( + range_values, details.valuation_method, details.valuation_rate + ) + row = [details.name, details.item_name, details.description, details.item_group, details.brand] if filters.get("show_warehouse_wise_stock"): @@ -72,6 +76,15 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li return data +def check_and_replace_valuations_if_moving_average(range_values, item_valuation_method, valuation_rate): + if item_valuation_method == "Moving Average" or ( + not item_valuation_method + and frappe.db.get_single_value("Stock Settings", "valuation_method") == "Moving Average" + ): + for i in range(0, len(range_values), 2): + range_values[i + 1] = range_values[i] * valuation_rate + + def get_average_age(fifo_queue: list, to_date: str) -> float: batch_age = age_qty = total_qty = 0.0 for batch in fifo_queue: @@ -267,7 +280,7 @@ class FIFOSlots: self.__update_balances(d, key) - # Note that stock_ledger_entries is an iterator, you can not reuse it like a list + # Note that stock_ledger_entries is an iterator, you can not reuse it like a list del stock_ledger_entries if not self.filters.get("show_warehouse_wise_stock"): @@ -396,6 +409,7 @@ class FIFOSlots: self.item_details[key]["total_qty"] += row.actual_qty self.item_details[key]["has_serial_no"] = row.has_serial_no + self.item_details[key]["details"].valuation_rate = row.valuation_rate def __aggregate_details_by_item(self, wh_wise_data: dict) -> dict: "Aggregate Item-Wh wise data into single Item entry." @@ -437,8 +451,10 @@ class FIFOSlots: item.description, item.stock_uom, item.has_serial_no, + item.valuation_method, sle.actual_qty, sle.stock_value_difference, + sle.valuation_rate, sle.posting_date, sle.voucher_type, sle.voucher_no, @@ -506,7 +522,14 @@ class FIFOSlots: item_table = frappe.qb.DocType("Item") item = frappe.qb.from_("Item").select( - "name", "item_name", "description", "stock_uom", "brand", "item_group", "has_serial_no" + "name", + "item_name", + "description", + "stock_uom", + "brand", + "item_group", + "has_serial_no", + "valuation_method", ) if self.filters.get("item_code"):