From a87015e8e69215b71c9b71b3779193a7f94e7be2 Mon Sep 17 00:00:00 2001 From: ravibharathi656 Date: Tue, 7 Apr 2026 16:54:44 +0530 Subject: [PATCH 01/27] fix: preserve asset movement field properties after save (cherry picked from commit 4a004a2a82b8fc7d371abe2bfd95de4173d9861b) --- erpnext/assets/doctype/asset_movement/asset_movement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index f56c1e31f27..2e73d0d9df8 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -41,7 +41,7 @@ frappe.ui.form.on("Asset Movement", { }); }, - onload: (frm) => { + refresh: (frm) => { frm.trigger("set_required_fields"); }, From 77545042a5e2fa8265763d2df149fb7cdc85a151 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 7 Apr 2026 15:45:34 +0530 Subject: [PATCH 02/27] fix: hardcoded precision causing decimal issues (cherry picked from commit 90fd6f2e40e0fca8a716ed3ad6f9a68b238ded12) # Conflicts: # erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json # erpnext/stock/doctype/delivery_note_item/delivery_note_item.json # erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json --- .../purchase_invoice_item/purchase_invoice_item.json | 5 ++++- .../doctype/sales_invoice/test_sales_invoice.py | 2 +- erpnext/controllers/selling_controller.py | 10 +++++----- erpnext/controllers/taxes_and_totals.py | 5 ++++- .../doctype/delivery_note_item/delivery_note_item.json | 5 ++++- .../purchase_receipt_item/purchase_receipt_item.json | 5 ++++- 6 files changed, 22 insertions(+), 10 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 7acb36eca93..a691f8cad93 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -731,7 +731,6 @@ "label": "Valuation Rate", "no_copy": 1, "options": "Company:company:default_currency", - "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -984,7 +983,11 @@ "idx": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2025-10-14 13:01:54.441511", +======= + "modified": "2026-04-07 15:40:45.687554", +>>>>>>> 90fd6f2e40 (fix: hardcoded precision causing decimal issues) "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index a8fb4ab93a0..147bb76064d 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2917,7 +2917,7 @@ class TestSalesInvoice(FrappeTestCase): si.submit() # Check if adjustment entry is created - self.assertTrue( + self.assertFalse( frappe.db.exists( "GL Entry", { diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 20c8a72290b..d5bd3501527 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -616,11 +616,11 @@ class SellingController(StockController): if allow_at_arms_length_price: continue - rate = flt( - flt(d.incoming_rate, d.precision("incoming_rate")) * d.conversion_factor, - d.precision("rate"), - ) - if d.rate != rate: + rate = flt(flt(d.incoming_rate) * flt(d.conversion_factor or 1.0)) + + if flt(d.rate, d.precision("incoming_rate")) != flt( + rate, d.precision("incoming_rate") + ): d.rate = rate frappe.msgprint( _( diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index e75dd3dacd2..4b6fc4f4054 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -186,8 +186,11 @@ class calculate_taxes_and_totals: bill_for_rejected_quantity_in_purchase_invoice = frappe.get_single_value( "Buying Settings", "bill_for_rejected_quantity_in_purchase_invoice" ) + + do_not_round_fields = ["valuation_rate", "incoming_rate"] + for item in self.doc.items: - self.doc.round_floats_in(item) + self.doc.round_floats_in(item, do_not_round_fields=do_not_round_fields) if item.discount_percentage == 100: item.rate = 0.0 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 c8fcdb4c5a7..a6696cac254 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -759,7 +759,6 @@ "label": "Incoming Rate", "no_copy": 1, "options": "Company:company:default_currency", - "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -952,7 +951,11 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2025-05-31 18:51:32.651562", +======= + "modified": "2026-04-07 15:43:20.892151", +>>>>>>> 90fd6f2e40 (fix: hardcoded precision causing decimal issues) "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 463544a9952..d089da00a49 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -734,7 +734,6 @@ "oldfieldname": "valuation_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", - "precision": "6", "print_hide": 1, "print_width": "80px", "read_only": 1, @@ -1149,7 +1148,11 @@ "idx": 1, "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2025-10-14 12:59:20.384056", +======= + "modified": "2026-04-07 15:40:47.032889", +>>>>>>> 90fd6f2e40 (fix: hardcoded precision causing decimal issues) "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From d2c6a8958d6628ef17c1b68c3204dace1e35600b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 8 Apr 2026 12:28:29 +0530 Subject: [PATCH 03/27] chore: fix conflicts Updated the modified date for the delivery note item. --- .../doctype/delivery_note_item/delivery_note_item.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 a6696cac254..3a82642873d 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -951,11 +951,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2025-05-31 18:51:32.651562", -======= - "modified": "2026-04-07 15:43:20.892151", ->>>>>>> 90fd6f2e40 (fix: hardcoded precision causing decimal issues) + "modified": "2026-04-07 15:44:20.892151", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", From 39a4760e07a8b107f0be8c09f50f20b7ea007e36 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 8 Apr 2026 12:29:00 +0530 Subject: [PATCH 04/27] chore: fix conflicts --- .../purchase_receipt_item/purchase_receipt_item.json | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 d089da00a49..8fda1c44702 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -1148,11 +1148,7 @@ "idx": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2025-10-14 12:59:20.384056", -======= - "modified": "2026-04-07 15:40:47.032889", ->>>>>>> 90fd6f2e40 (fix: hardcoded precision causing decimal issues) + "modified": "2026-04-07 15:41:47.032889", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From 21607f39c5f0e644011faae52bd3a1508a05bfbf Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 8 Apr 2026 14:05:04 +0530 Subject: [PATCH 05/27] chore: fix conflicts --- .../purchase_invoice_item/purchase_invoice_item.json | 6 +----- 1 file changed, 1 insertion(+), 5 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 a691f8cad93..e059777dde2 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -983,11 +983,7 @@ "idx": 1, "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2025-10-14 13:01:54.441511", -======= - "modified": "2026-04-07 15:40:45.687554", ->>>>>>> 90fd6f2e40 (fix: hardcoded precision causing decimal issues) + "modified": "2026-04-07 15:41:45.687554", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", From bcd6d99549aae9b6ca433511c623869477ad18ba Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:05:39 +0000 Subject: [PATCH 06/27] fix: quality inspection item code fetch perm issue (backport #54121) (#54126) Co-authored-by: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com> Co-authored-by: Mihir Kandoi fix: quality inspection item code fetch perm issue (#54121) --- erpnext/stock/doctype/quality_inspection/quality_inspection.js | 1 + erpnext/stock/doctype/quality_inspection/quality_inspection.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index 69bc03a8bd4..8d5764d5697 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -58,6 +58,7 @@ frappe.ui.form.on("Quality Inspection", { if (doc.reference_type && doc.reference_name) { let filters = { from: doctype, + parent_doctype: doc.reference_type, inspection_type: doc.inspection_type, }; diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 648836d0f6e..6f5b184ec00 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -364,10 +364,11 @@ def item_query(doctype, txt, searchfield, start, page_len, filters): from frappe.desk.reportview import get_match_cond from_doctype = cstr(filters.get("from")) + parent_doctype = cstr(filters.get("parent_doctype")) if not from_doctype or not frappe.db.exists("DocType", from_doctype): return [] - mcond = get_match_cond(from_doctype) + mcond = get_match_cond(parent_doctype or from_doctype) cond, qi_condition = "", "and (quality_inspection is null or quality_inspection = '')" if filters.get("parent"): From a26c84533272416cf7332f9bcb3971929953c924 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 8 Apr 2026 14:23:20 +0000 Subject: [PATCH 07/27] fix: inventory dimensions should not be mandatory unnecesarily (backport #54064) (#54133) * fix: inventory dimensions should not be mandatory unnecesarily (#54064) (cherry picked from commit 6e44b8913e95ecb9b95a5c4e71bb2ae542b24352) # Conflicts: # erpnext/patches.txt # erpnext/stock/doctype/inventory_dimension/inventory_dimension.py * chore: resolve conflicts * chore: resolve conflicts * chore: resolve conflicts --------- Co-authored-by: Mihir Kandoi --- erpnext/patches.txt | 1 + .../v16_0/depends_on_inv_dimensions.py | 70 +++++++++++++++++++ .../inventory_dimension.json | 12 +--- .../inventory_dimension.py | 16 +++-- .../test_inventory_dimension.py | 6 +- 5 files changed, 86 insertions(+), 19 deletions(-) create mode 100644 erpnext/patches/v16_0/depends_on_inv_dimensions.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7d2c1757bda..2dcee807fec 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -432,3 +432,4 @@ erpnext.patches.v16_0.set_ordered_qty_in_quotation_item erpnext.patches.v15_0.replace_http_with_https_in_sales_partner erpnext.patches.v16_0.add_portal_redirects erpnext.patches.v16_0.update_order_qty_and_requested_qty_based_on_mr_and_po +erpnext.patches.v16_0.depends_on_inv_dimensions diff --git a/erpnext/patches/v16_0/depends_on_inv_dimensions.py b/erpnext/patches/v16_0/depends_on_inv_dimensions.py new file mode 100644 index 00000000000..114e6e4b725 --- /dev/null +++ b/erpnext/patches/v16_0/depends_on_inv_dimensions.py @@ -0,0 +1,70 @@ +import frappe + + +def get_inventory_dimensions(): + return frappe.get_all( + "Inventory Dimension", + fields=[ + "target_fieldname as fieldname", + "source_fieldname", + "reference_document as doctype", + "reqd", + "mandatory_depends_on", + ], + order_by="creation", + distinct=True, + ) + + +def get_display_depends_on(doctype): + if doctype not in [ + "Stock Entry Detail", + "Sales Invoice Item", + "Delivery Note Item", + "Purchase Invoice Item", + "Purchase Receipt Item", + ]: + return + + display_depends_on = "" + + if doctype in ["Purchase Invoice Item", "Purchase Receipt Item"]: + display_depends_on = "eval:parent.is_internal_supplier == 1" + elif doctype != "Stock Entry Detail": + display_depends_on = "eval:parent.is_internal_customer == 1" + elif doctype == "Stock Entry Detail": + display_depends_on = "eval:doc.t_warehouse" + + return display_depends_on + + +def execute(): + for dimension in get_inventory_dimensions(): + frappe.set_value( + "Custom Field", + {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail"}, + "depends_on", + "eval:doc.s_warehouse", + ) + frappe.set_value( + "Custom Field", + {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail", "reqd": 1}, + {"mandatory_depends_on": "eval:doc.s_warehouse", "reqd": 0}, + ) + frappe.set_value( + "Custom Field", + { + "fieldname": f"to_{dimension.fieldname}", + "dt": "Stock Entry Detail", + "depends_on": "eval:parent.purpose != 'Material Issue'", + }, + "depends_on", + "eval:doc.t_warehouse", + ) + if display_depends_on := get_display_depends_on(dimension.doctype): + frappe.set_value( + "Custom Field", + {"fieldname": dimension.fieldname, "dt": dimension.doctype}, + "mandatory_depends_on", + display_depends_on if dimension.reqd else dimension.mandatory_depends_on, + ) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json index 20250622fda..3ebd6b6a1fa 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json @@ -8,9 +8,8 @@ "field_order": [ "dimension_details_tab", "dimension_name", - "reference_document", "column_break_4", - "disabled", + "reference_document", "field_mapping_section", "source_fieldname", "column_break_9", @@ -93,12 +92,6 @@ "fieldtype": "Check", "label": "Apply to All Inventory Documents" }, - { - "default": "0", - "fieldname": "disabled", - "fieldtype": "Check", - "label": "Disabled" - }, { "fieldname": "target_fieldname", "fieldtype": "Data", @@ -159,6 +152,7 @@ "label": "Conditional Rule Examples" }, { + "depends_on": "eval:!doc.apply_to_all_doctypes", "description": "To apply condition on parent field use parent.field_name and to apply condition on child table use doc.field_name. Here field_name could be based on the actual column name of the respective field.", "fieldname": "mandatory_depends_on", "fieldtype": "Small Text", @@ -188,7 +182,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2025-07-07 15:51:29.329064", + "modified": "2026-04-08 10:10:16.884388", "modified_by": "Administrator", "module": "Stock", "name": "Inventory Dimension", diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index fbef891b745..b43f2991bad 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -31,7 +31,6 @@ class InventoryDimension(Document): apply_to_all_doctypes: DF.Check condition: DF.Code | None dimension_name: DF.Data - disabled: DF.Check document_type: DF.Link | None fetch_from_parent: DF.Literal[None] istable: DF.Check @@ -75,7 +74,6 @@ class InventoryDimension(Document): old_doc = self._doc_before_save allow_to_edit_fields = [ - "disabled", "fetch_from_parent", "type_of_transaction", "condition", @@ -119,6 +117,7 @@ class InventoryDimension(Document): def reset_value(self): if self.apply_to_all_doctypes: self.type_of_transaction = "" + self.mandatory_depends_on = "" self.istable = 0 for field in ["document_type", "condition"]: @@ -183,8 +182,12 @@ class InventoryDimension(Document): label=_(label), depends_on="eval:doc.s_warehouse" if doctype == "Stock Entry Detail" else "", search_index=1, - reqd=self.reqd, - mandatory_depends_on=self.mandatory_depends_on, + reqd=1 + if self.reqd and not self.mandatory_depends_on and doctype != "Stock Entry Detail" + else 0, + mandatory_depends_on="eval:doc.s_warehouse" + if self.reqd and doctype == "Stock Entry Detail" + else self.mandatory_depends_on, ), ] @@ -296,12 +299,13 @@ class InventoryDimension(Document): options=self.reference_document, label=label, depends_on=display_depends_on, + mandatory_depends_on=display_depends_on if self.reqd else self.mandatory_depends_on, ), ] ) -def field_exists(doctype, fieldname) -> str or None: +def field_exists(doctype, fieldname) -> str | None: return frappe.db.get_value("DocField", {"parent": doctype, "fieldname": fieldname}, "name") @@ -374,7 +378,6 @@ def get_document_wise_inventory_dimensions(doctype) -> dict: "type_of_transaction", "fetch_from_parent", ], - filters={"disabled": 0}, or_filters={"document_type": doctype, "apply_to_all_doctypes": 1}, ) @@ -397,7 +400,6 @@ def get_inventory_dimensions(): "reference_document as doctype", "validate_negative_stock", ], - filters={"disabled": 0}, ) frappe.local.inventory_dimensions = dimensions diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 96088db1923..29e811ea4c4 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -220,9 +220,9 @@ class TestInventoryDimension(FrappeTestCase): doc = create_inventory_dimension( reference_document="Pallet", type_of_transaction="Outward", - dimension_name="Pallet", + dimension_name="Pallet 75", apply_to_all_doctypes=0, - document_type="Stock Entry Detail", + document_type="Delivery Note Item", ) doc.reqd = 1 @@ -230,7 +230,7 @@ class TestInventoryDimension(FrappeTestCase): self.assertTrue( frappe.db.get_value( - "Custom Field", {"fieldname": "pallet", "dt": "Stock Entry Detail", "reqd": 1}, "name" + "Custom Field", {"fieldname": "pallet_75", "dt": "Delivery Note Item", "reqd": 1}, "name" ) ) From deb67db4a00ec5e642885f911cc06e528a04ce03 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 02:01:45 +0000 Subject: [PATCH 08/27] fix: inventory dimension patch (backport #54141) (#54145) Co-authored-by: Mihir Kandoi fix: inventory dimension patch (#54141) --- .../v16_0/depends_on_inv_dimensions.py | 49 +++++++++++++------ 1 file changed, 33 insertions(+), 16 deletions(-) diff --git a/erpnext/patches/v16_0/depends_on_inv_dimensions.py b/erpnext/patches/v16_0/depends_on_inv_dimensions.py index 114e6e4b725..3ee805df7ef 100644 --- a/erpnext/patches/v16_0/depends_on_inv_dimensions.py +++ b/erpnext/patches/v16_0/depends_on_inv_dimensions.py @@ -40,28 +40,45 @@ def get_display_depends_on(doctype): def execute(): for dimension in get_inventory_dimensions(): - frappe.set_value( - "Custom Field", - {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail"}, - "depends_on", - "eval:doc.s_warehouse", - ) - frappe.set_value( - "Custom Field", - {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail", "reqd": 1}, - {"mandatory_depends_on": "eval:doc.s_warehouse", "reqd": 0}, - ) - frappe.set_value( + if frappe.db.exists( + "Custom Field", {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail"} + ): + frappe.set_value( + "Custom Field", + {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail"}, + "depends_on", + "eval:doc.s_warehouse", + ) + if frappe.db.exists( + "Custom Field", {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail", "reqd": 1} + ): + frappe.set_value( + "Custom Field", + {"fieldname": dimension.source_fieldname, "dt": "Stock Entry Detail", "reqd": 1}, + {"mandatory_depends_on": "eval:doc.s_warehouse", "reqd": 0}, + ) + if frappe.db.exists( "Custom Field", { "fieldname": f"to_{dimension.fieldname}", "dt": "Stock Entry Detail", "depends_on": "eval:parent.purpose != 'Material Issue'", }, - "depends_on", - "eval:doc.t_warehouse", - ) - if display_depends_on := get_display_depends_on(dimension.doctype): + ): + frappe.set_value( + "Custom Field", + { + "fieldname": f"to_{dimension.fieldname}", + "dt": "Stock Entry Detail", + "depends_on": "eval:parent.purpose != 'Material Issue'", + }, + "depends_on", + "eval:doc.t_warehouse", + ) + if (display_depends_on := get_display_depends_on(dimension.doctype)) and frappe.db.exists( + "Custom Field", + {"fieldname": dimension.fieldname, "dt": dimension.doctype}, + ): frappe.set_value( "Custom Field", {"fieldname": dimension.fieldname, "dt": dimension.doctype}, From a56d6984d12784387cb7e181be0d8c727b517bd0 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 02:40:40 +0000 Subject: [PATCH 09/27] fix: inventory dimension patch (backport #54147) (#54148) --- .../patches/v16_0/depends_on_inv_dimensions.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/patches/v16_0/depends_on_inv_dimensions.py b/erpnext/patches/v16_0/depends_on_inv_dimensions.py index 3ee805df7ef..0de46f68f11 100644 --- a/erpnext/patches/v16_0/depends_on_inv_dimensions.py +++ b/erpnext/patches/v16_0/depends_on_inv_dimensions.py @@ -16,7 +16,7 @@ def get_inventory_dimensions(): ) -def get_display_depends_on(doctype): +def get_display_depends_on(doctype, fieldname): if doctype not in [ "Stock Entry Detail", "Sales Invoice Item", @@ -24,18 +24,20 @@ def get_display_depends_on(doctype): "Purchase Invoice Item", "Purchase Receipt Item", ]: - return + return None, None + fieldname_start_with = "to" display_depends_on = "" if doctype in ["Purchase Invoice Item", "Purchase Receipt Item"]: display_depends_on = "eval:parent.is_internal_supplier == 1" + fieldname_start_with = "from" elif doctype != "Stock Entry Detail": display_depends_on = "eval:parent.is_internal_customer == 1" elif doctype == "Stock Entry Detail": display_depends_on = "eval:doc.t_warehouse" - return display_depends_on + return f"{fieldname_start_with}_{fieldname}", display_depends_on def execute(): @@ -75,13 +77,13 @@ def execute(): "depends_on", "eval:doc.t_warehouse", ) - if (display_depends_on := get_display_depends_on(dimension.doctype)) and frappe.db.exists( - "Custom Field", - {"fieldname": dimension.fieldname, "dt": dimension.doctype}, + fieldname, display_depends_on = get_display_depends_on(dimension.doctype, dimension.fieldname) + if display_depends_on and frappe.db.exists( + "Custom Field", {"fieldname": fieldname, "dt": dimension.doctype} ): frappe.set_value( "Custom Field", - {"fieldname": dimension.fieldname, "dt": dimension.doctype}, + {"fieldname": fieldname, "dt": dimension.doctype}, "mandatory_depends_on", display_depends_on if dimension.reqd else dimension.mandatory_depends_on, ) From 8408e8133526d5d6ee83f39ee1f0d2f472b68d81 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 8 Apr 2026 18:22:08 +0530 Subject: [PATCH 10/27] fix: last SLE not updated in the file (cherry picked from commit 38ed425ee299e70d8c22b759899f250fd5429393) # Conflicts: # erpnext/manufacturing/doctype/work_order/test_work_order.py --- .../doctype/work_order/test_work_order.py | 38 ++++++++++++ erpnext/stock/stock_ledger.py | 59 ++++++++++--------- 2 files changed, 70 insertions(+), 27 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 374caf369ea..96bb75a841e 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -509,7 +509,45 @@ class TestWorkOrder(FrappeTestCase): def test_work_order_material_transferred_qty_with_process_loss(self): stock_entries = [] +<<<<<<< HEAD bom = frappe.get_doc("BOM", {"docstatus": 1, "with_operations": 1, "company": "_Test Company"}) +======= + item_code = make_item("_Test Item For Process Loss", {"is_stock_item": 1}).name + rm_item_code = make_item("Test Item For Process Loss RM", {"is_stock_item": 1}).name + + bom = make_bom( + item=item_code, + raw_materials=[rm_item_code], + with_operations=1, + do_not_save=True, + ) + + operation_name = "_Test Custom Operation" + workstation_name = "_Test Custom Workstation" + + if not frappe.db.exists("Workstation", workstation_name): + doc = frappe.new_doc("Workstation") + doc.workstation_name = workstation_name + doc.save() + + if not frappe.db.exists("Operation", operation_name): + doc = frappe.new_doc("Operation") + doc.name = operation_name + doc.workstation = workstation_name + doc.save() + + operation = { + "operation": operation_name, + "workstation": workstation_name, + "description": "Test Data", + "operating_cost": 100, + "time_in_mins": 40, + } + + bom.append("operations", operation) + bom.save() + bom.submit() +>>>>>>> 38ed425ee2 (fix: last SLE not updated in the file) work_order = make_wo_order_test_record( item=bom.item, diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ccc65a1e329..3e5974e0f53 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -263,17 +263,11 @@ def update_args_in_repost_item_valuation( items_to_be_repost, repost_affected_transaction, item_wh_wise_last_posted_sle=None, - only_affected_transaction=False, ): file_name = "" - has_file = False - if not item_wh_wise_last_posted_sle: item_wh_wise_last_posted_sle = {} - if doc.reposting_data_file: - has_file = True - if doc.reposting_data_file: file_name = get_reposting_file_name(doc.doctype, doc.name) # frappe.delete_doc("File", file_name, ignore_permissions=True, delete_permanently=True) @@ -288,15 +282,14 @@ def update_args_in_repost_item_valuation( file_name, ) - if not only_affected_transaction or not has_file: - doc.db_set( - { - "current_index": index, - "items_to_be_repost": frappe.as_json(items_to_be_repost), - "total_reposting_count": len(items_to_be_repost), - "reposting_data_file": doc.reposting_data_file, - } - ) + doc.db_set( + { + "current_index": index, + "items_to_be_repost": frappe.as_json(items_to_be_repost), + "total_reposting_count": len(items_to_be_repost), + "reposting_data_file": doc.reposting_data_file, + } + ) if not frappe.flags.in_test: frappe.db.commit() @@ -577,13 +570,9 @@ class update_entries_after: self.update_bin() else: self.item_wh_wise_last_posted_sle = self.get_item_wh_wise_last_posted_sle() - _item_wh_sle = self.sort_sles(self.item_wh_wise_last_posted_sle.values()) - - while _item_wh_sle: - self.initialize_reposting() - sle_dict = _item_wh_sle.pop(0) - self.repost_stock_ledgers(sle_dict) - + item_wh_sles = self.sort_sles(self.item_wh_wise_last_posted_sle.values()) + self.initialize_reposting() + self.repost_stock_ledgers(item_wh_sles) self.update_bin() self.reset_vouchers_and_idx() self.update_data_in_repost() @@ -618,8 +607,19 @@ class update_entries_after: ) } - def repost_stock_ledgers(self, sle_dict=None): - self._sles = self.get_future_entries_to_repost(sle_dict) + def _get_future_entries_to_repost(self, item_wh_sles): + sles = [] + + for sle in item_wh_sles: + if (sle.item_code, sle.warehouse) not in self.distinct_dependant_item_wh: + self.distinct_dependant_item_wh.add((sle.item_code, sle.warehouse)) + + sles.extend(self.get_future_entries_to_repost(sle)) + + return self.sort_sles(sles) + + def repost_stock_ledgers(self, item_wh_sles=None): + self._sles = self._get_future_entries_to_repost(item_wh_sles) if not isinstance(self._sles, deque): self._sles = deque(self._sles) @@ -627,10 +627,13 @@ class update_entries_after: i = 0 while self._sles: sle = self._sles.popleft() - i += 1 + if (sle.item_code, sle.warehouse) not in self.distinct_dependant_item_wh: + self.distinct_dependant_item_wh.add((sle.item_code, sle.warehouse)) + if sle.name in self.distinct_sles: continue + i += 1 item_wh_key = (sle.item_code, sle.warehouse) if item_wh_key not in self.prev_sle_dict: self.prev_sle_dict[item_wh_key] = get_previous_sle_of_current_voucher(sle) @@ -644,7 +647,7 @@ class update_entries_after: self.include_dependant_sle_in_reposting(sle) self.update_item_wh_wise_last_posted_sle(sle) - if i % 1000 == 0: + if i % 2000 == 0: self.update_data_in_repost(len(self._sles), i) def sort_sles(self, sles): @@ -726,7 +729,6 @@ class update_entries_after: self.items_to_be_repost, self.repost_affected_transaction, self.item_wh_wise_last_posted_sle, - only_affected_transaction=True, ) if not frappe.flags.in_test: @@ -982,6 +984,9 @@ class update_entries_after: ): return + if not cint(erpnext.is_perpetual_inventory_enabled(sle.company)): + return + if self.args.item_code != sle.item_code or self.args.warehouse != sle.warehouse: self.repost_affected_transaction.add((sle.voucher_type, sle.voucher_no)) From c70259687a034416706d204c0e7035e17cca93d2 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Apr 2026 09:06:31 +0530 Subject: [PATCH 11/27] chore: fix conflicts --- erpnext/manufacturing/doctype/work_order/test_work_order.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 96bb75a841e..f4a0d6f6145 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -509,9 +509,6 @@ class TestWorkOrder(FrappeTestCase): def test_work_order_material_transferred_qty_with_process_loss(self): stock_entries = [] -<<<<<<< HEAD - bom = frappe.get_doc("BOM", {"docstatus": 1, "with_operations": 1, "company": "_Test Company"}) -======= item_code = make_item("_Test Item For Process Loss", {"is_stock_item": 1}).name rm_item_code = make_item("Test Item For Process Loss RM", {"is_stock_item": 1}).name @@ -547,7 +544,6 @@ class TestWorkOrder(FrappeTestCase): bom.append("operations", operation) bom.save() bom.submit() ->>>>>>> 38ed425ee2 (fix: last SLE not updated in the file) work_order = make_wo_order_test_record( item=bom.item, From 39a473455dc29359752060dee27540b8affe5e38 Mon Sep 17 00:00:00 2001 From: AarDG10 Date: Wed, 8 Apr 2026 17:19:43 +0530 Subject: [PATCH 12/27] refactor: update reset password method name (cherry picked from commit c4d74483e1895b4d10a7d28b808c2781e32b2428) --- .../doctype/request_for_quotation/request_for_quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index e14a0265119..73ff7545ca5 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -283,7 +283,7 @@ class RequestforQuotation(BuyingController): } ) user.save(ignore_permissions=True) - update_password_link = user.reset_password() + update_password_link = user._reset_password() return user, update_password_link From 5de4102dda154063b0dcf4f293dfb2976d8db177 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:58:07 +0530 Subject: [PATCH 13/27] fix(sales invoice): toggle Get Items From button based on is_return and POS view (backport #52594) (#54138) Co-authored-by: NaviN <118178330+Navin-S-R@users.noreply.github.com> Co-authored-by: Navin-S-R fix(sales invoice): toggle Get Items From button based on is_return and POS view (#52594) --- .../doctype/sales_invoice/sales_invoice.js | 176 ++++++++++-------- erpnext/controllers/queries.py | 59 +++--- 2 files changed, 135 insertions(+), 100 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index aeb993e2399..b4b49828a58 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -166,13 +166,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( ); } } - - // Show buttons only when pos view is active - if (cint(doc.docstatus == 0) && cur_frm.page.current_view_name !== "pos" && !doc.is_return) { - this.frm.cscript.sales_order_btn(); - this.frm.cscript.delivery_note_btn(); - this.frm.cscript.quotation_btn(); - } + this.toggle_get_items(); this.set_default_print_format(); if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) { @@ -258,6 +252,93 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( } } + toggle_get_items() { + const buttons = ["Sales Order", "Quotation", "Timesheet", "Delivery Note"]; + + buttons.forEach((label) => { + this.frm.remove_custom_button(label, "Get Items From"); + }); + + if (cint(this.frm.doc.docstatus) !== 0 || this.frm.page.current_view_name === "pos") { + return; + } + + if (!this.frm.doc.is_return) { + this.frm.cscript.sales_order_btn(); + this.frm.cscript.quotation_btn(); + this.frm.cscript.timesheet_btn(); + } + + this.frm.cscript.delivery_note_btn(); + } + + timesheet_btn() { + var me = this; + + me.frm.add_custom_button( + __("Timesheet"), + function () { + let d = new frappe.ui.Dialog({ + title: __("Fetch Timesheet"), + fields: [ + { + label: __("From"), + fieldname: "from_time", + fieldtype: "Date", + reqd: 1, + }, + { + label: __("Item Code"), + fieldname: "item_code", + fieldtype: "Link", + options: "Item", + get_query: () => { + return { + query: "erpnext.controllers.queries.item_query", + filters: { + is_sales_item: 1, + customer: me.frm.doc.customer, + has_variants: 0, + }, + }; + }, + }, + { + fieldtype: "Column Break", + fieldname: "col_break_1", + }, + { + label: __("To"), + fieldname: "to_time", + fieldtype: "Date", + reqd: 1, + }, + { + label: __("Project"), + fieldname: "project", + fieldtype: "Link", + options: "Project", + default: me.frm.doc.project, + }, + ], + primary_action: function () { + const data = d.get_values(); + me.frm.events.add_timesheet_data(me.frm, { + from_time: data.from_time, + to_time: data.to_time, + project: data.project, + item_code: data.item_code, + }); + d.hide(); + }, + primary_action_label: __("Get Timesheets"), + }); + d.show(); + }, + __("Get Items From") + ); + } + sales_order_btn() { var me = this; this.$sales_order_btn = this.frm.add_custom_button( @@ -322,6 +403,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( this.$delivery_note_btn = this.frm.add_custom_button( __("Delivery Note"), function () { + if (!me.frm.doc.customer) { + frappe.throw({ + title: __("Mandatory"), + message: __("Please Select a Customer"), + }); + } erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice", source_doctype: "Delivery Note", @@ -334,7 +421,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( var filters = { docstatus: 1, company: me.frm.doc.company, - is_return: 0, + is_return: me.frm.doc.is_return, }; if (me.frm.doc.customer) filters["customer"] = me.frm.doc.customer; return { @@ -594,6 +681,14 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( this.calculate_taxes_and_totals(); } + + apply_tds(frm) { + this.frm.clear_table("tax_withholding_entries"); + } + + is_return() { + this.toggle_get_items(); + } }; // for backward compatibility: combine new and previous states @@ -1039,71 +1134,6 @@ frappe.ui.form.on("Sales Invoice", { }, refresh: function (frm) { - if (frm.doc.docstatus === 0 && !frm.doc.is_return) { - frm.add_custom_button( - __("Timesheet"), - function () { - let d = new frappe.ui.Dialog({ - title: __("Fetch Timesheet"), - fields: [ - { - label: __("From"), - fieldname: "from_time", - fieldtype: "Date", - reqd: 1, - }, - { - label: __("Item Code"), - fieldname: "item_code", - fieldtype: "Link", - options: "Item", - get_query: () => { - return { - query: "erpnext.controllers.queries.item_query", - filters: { - is_sales_item: 1, - customer: frm.doc.customer, - has_variants: 0, - }, - }; - }, - }, - { - fieldtype: "Column Break", - fieldname: "col_break_1", - }, - { - label: __("To"), - fieldname: "to_time", - fieldtype: "Date", - reqd: 1, - }, - { - label: __("Project"), - fieldname: "project", - fieldtype: "Link", - options: "Project", - default: frm.doc.project, - }, - ], - primary_action: function () { - const data = d.get_values(); - frm.events.add_timesheet_data(frm, { - from_time: data.from_time, - to_time: data.to_time, - project: data.project, - item_code: data.item_code, - }); - d.hide(); - }, - primary_action_label: __("Get Timesheets"), - }); - d.show(); - }, - __("Get Items From") - ); - } - if (frm.doc.is_debit_note) { frm.set_df_property("return_against", "label", __("Adjustment Against")); } diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 9e072ee4017..dbfa6e7f9fb 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -356,38 +356,43 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs -def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict): - doctype = "Delivery Note" +def get_delivery_notes_to_be_billed( + doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict, as_dict: bool = False +): + DeliveryNote = frappe.qb.DocType("Delivery Note") + fields = get_fields(doctype, ["name", "customer", "posting_date"]) - return frappe.db.sql( - """ - select {fields} - from `tabDelivery Note` - where `tabDelivery Note`.`{key}` like {txt} and - `tabDelivery Note`.docstatus = 1 - and status not in ('Stopped', 'Closed') {fcond} - and ( - (`tabDelivery Note`.is_return = 0 and `tabDelivery Note`.per_billed < 100) - or (`tabDelivery Note`.grand_total = 0 and `tabDelivery Note`.per_billed < 100) - or ( - `tabDelivery Note`.is_return = 1 - and return_against in (select name from `tabDelivery Note` where per_billed < 100) + original_dn = ( + frappe.qb.from_(DeliveryNote) + .select(DeliveryNote.name) + .where((DeliveryNote.docstatus == 1) & (DeliveryNote.is_return == 0) & (DeliveryNote.per_billed > 0)) + ) + + query = ( + frappe.qb.from_(DeliveryNote) + .select(*[DeliveryNote[f] for f in fields]) + .where( + (DeliveryNote.docstatus == 1) + & (DeliveryNote.status.notin(["Stopped", "Closed"])) + & (DeliveryNote[searchfield].like(f"%{txt}%")) + & ( + ((DeliveryNote.is_return == 0) & (DeliveryNote.per_billed < 100)) + | ((DeliveryNote.grand_total == 0) & (DeliveryNote.per_billed < 100)) + | ( + (DeliveryNote.is_return == 1) + & (DeliveryNote.per_billed < 100) + & (DeliveryNote.return_against.isin(original_dn)) ) ) - {mcond} order by `tabDelivery Note`.`{key}` asc limit {page_len} offset {start} - """.format( - fields=", ".join([f"`tabDelivery Note`.{f}" for f in fields]), - key=searchfield, - fcond=get_filters_cond(doctype, filters, []), - mcond=get_match_cond(doctype), - start=start, - page_len=page_len, - txt="%(txt)s", - ), - {"txt": ("%%%s%%" % txt)}, - as_dict=as_dict, + ) ) + if filters and isinstance(filters, dict): + for key, value in filters.items(): + query = query.where(DeliveryNote[key] == value) + + query = query.orderby(DeliveryNote[searchfield], order=Order.asc).limit(page_len).offset(start) + return query.run(as_dict=as_dict) @frappe.whitelist() From 1604c216026d1138e4e9c71e0dfef1febe6a6402 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Thu, 9 Apr 2026 12:53:12 +0530 Subject: [PATCH 14/27] fix(list_opportunity_report): parameterized `lost_reason` (#54160) --- erpnext/crm/report/lost_opportunity/lost_opportunity.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.py b/erpnext/crm/report/lost_opportunity/lost_opportunity.py index eb09711667a..1f826fe227a 100644 --- a/erpnext/crm/report/lost_opportunity/lost_opportunity.py +++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.py @@ -117,7 +117,7 @@ def get_join(filters): join = """JOIN `tabOpportunity Lost Reason Detail` ON `tabOpportunity Lost Reason Detail`.parenttype = 'Opportunity' and `tabOpportunity Lost Reason Detail`.parent = `tabOpportunity`.name and - `tabOpportunity Lost Reason Detail`.lost_reason = '{}' - """.format(filters.get("lost_reason")) + `tabOpportunity Lost Reason Detail`.lost_reason=%(lost_reason)s + """ return join From 6e438e71ebcb6933d3af659a8234a772e4bd5fe2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 9 Apr 2026 12:47:36 +0530 Subject: [PATCH 15/27] fix: set default posting time in RIV (cherry picked from commit a7ece65536d54c03a615ba43b5f23e4643492d6b) # Conflicts: # erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py --- .../repost_item_valuation.py | 19 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 2 ++ 2 files changed, 21 insertions(+) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 44cf9280780..96e49b288cc 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -74,6 +74,11 @@ class RepostItemValuation(Document): repost(self) def validate(self): +<<<<<<< HEAD +======= + self.set_default_posting_time() + self.reset_repost_only_accounting_ledgers() +>>>>>>> a7ece65536 (fix: set default posting time in RIV) self.set_company() self.validate_update_stock() self.validate_period_closing_voucher() @@ -83,6 +88,20 @@ class RepostItemValuation(Document): self.reset_recreate_stock_ledgers() self.validate_recreate_stock_ledgers() +<<<<<<< HEAD +======= + def set_default_posting_time(self): + if not self.posting_time: + self.posting_time = nowtime() + + if not self.posting_date: + frappe.throw(_("Posting date is required")) + + def reset_repost_only_accounting_ledgers(self): + if self.repost_only_accounting_ledgers and self.based_on != "Transaction": + self.repost_only_accounting_ledgers = 0 + +>>>>>>> a7ece65536 (fix: set default posting time in RIV) def validate_update_stock(self): if self.voucher_type in ["Sales Invoice", "Purchase Invoice"]: update_stock = frappe.get_value(self.voucher_type, self.voucher_no, "update_stock") diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 3e5974e0f53..18e06fd6c10 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -233,6 +233,8 @@ def repost_future_sle( index = get_current_index(doc) or 0 while index < len(items_to_be_repost): + validate_item_warehouse(items_to_be_repost[index]) + obj = update_entries_after( { "item_code": items_to_be_repost[index].get("item_code"), From 2df574baae31be34951b8deda8149fd3ca801ff2 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Apr 2026 14:24:07 +0530 Subject: [PATCH 16/27] chore: fix conflicts Removed unused method reset_repost_only_accounting_ledgers and fixed the validate method to set default posting time. --- .../repost_item_valuation/repost_item_valuation.py | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index 96e49b288cc..bfc857ed80b 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -74,11 +74,7 @@ class RepostItemValuation(Document): repost(self) def validate(self): -<<<<<<< HEAD -======= self.set_default_posting_time() - self.reset_repost_only_accounting_ledgers() ->>>>>>> a7ece65536 (fix: set default posting time in RIV) self.set_company() self.validate_update_stock() self.validate_period_closing_voucher() @@ -88,8 +84,6 @@ class RepostItemValuation(Document): self.reset_recreate_stock_ledgers() self.validate_recreate_stock_ledgers() -<<<<<<< HEAD -======= def set_default_posting_time(self): if not self.posting_time: self.posting_time = nowtime() @@ -97,11 +91,6 @@ class RepostItemValuation(Document): if not self.posting_date: frappe.throw(_("Posting date is required")) - def reset_repost_only_accounting_ledgers(self): - if self.repost_only_accounting_ledgers and self.based_on != "Transaction": - self.repost_only_accounting_ledgers = 0 - ->>>>>>> a7ece65536 (fix: set default posting time in RIV) def validate_update_stock(self): if self.voucher_type in ["Sales Invoice", "Purchase Invoice"]: update_stock = frappe.get_value(self.voucher_type, self.voucher_no, "update_stock") From e9c1a09af3a6ffbe000cddd89bcb05b38868ea11 Mon Sep 17 00:00:00 2001 From: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com> Date: Thu, 9 Apr 2026 15:57:22 +0530 Subject: [PATCH 17/27] fix: update_nsm only in warehouse creation (#54165) (cherry picked from commit b0e3fa3979e6efe6c9d230ac7e0812832b4563d2) --- erpnext/stock/doctype/warehouse/warehouse.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index a1991dd9c07..a19c35f26fa 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -65,7 +65,8 @@ class Warehouse(NestedSet): self.warn_about_multiple_warehouse_account() def on_update(self): - self.update_nsm_model() + if self.is_new() or self.has_value_changed("parent_warehouse"): + self.update_nsm_model() def update_nsm_model(self): frappe.utils.nestedset.update_nsm(self) From 76e910e8c05961a8e30ae436d9db7e2cc029f9f8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 9 Apr 2026 11:30:24 +0000 Subject: [PATCH 18/27] fix: sanitize genericode import inputs and secure XML parser (backport #53302) (#54174) Co-authored-by: Shllokkk --- erpnext/edi/doctype/code_list/code_list.py | 11 +++++++---- .../edi/doctype/code_list/code_list_import.py | 16 +++++++++++----- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/erpnext/edi/doctype/code_list/code_list.py b/erpnext/edi/doctype/code_list/code_list.py index 8957c6565b9..e723157e7a0 100644 --- a/erpnext/edi/doctype/code_list/code_list.py +++ b/erpnext/edi/doctype/code_list/code_list.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING import frappe from frappe.model.document import Document +from frappe.utils import escape_html if TYPE_CHECKING: from lxml.etree import Element @@ -63,14 +64,16 @@ class CodeList(Document): def from_genericode(self, root: "Element"): """Extract Code List details from genericode XML""" - self.title = root.find(".//Identification/ShortName").text + self.title = escape_html(root.find(".//Identification/ShortName").text) self.version = root.find(".//Identification/Version").text self.canonical_uri = root.find(".//CanonicalUri").text # optionals - self.description = getattr(root.find(".//Identification/LongName"), "text", None) - self.publisher = getattr(root.find(".//Identification/Agency/ShortName"), "text", None) + self.description = escape_html(getattr(root.find(".//Identification/LongName"), "text", None)) + self.publisher = escape_html(getattr(root.find(".//Identification/Agency/ShortName"), "text", None)) if not self.publisher: - self.publisher = getattr(root.find(".//Identification/Agency/LongName"), "text", None) + self.publisher = escape_html( + getattr(root.find(".//Identification/Agency/LongName"), "text", None) + ) self.publisher_id = getattr(root.find(".//Identification/Agency/Identifier"), "text", None) self.url = getattr(root.find(".//Identification/LocationUri"), "text", None) diff --git a/erpnext/edi/doctype/code_list/code_list_import.py b/erpnext/edi/doctype/code_list/code_list_import.py index ecabb256026..7368d3c012e 100644 --- a/erpnext/edi/doctype/code_list/code_list_import.py +++ b/erpnext/edi/doctype/code_list/code_list_import.py @@ -3,6 +3,7 @@ import json import frappe import requests from frappe import _ +from frappe.utils import escape_html from lxml import etree URL_PREFIXES = ("http://", "https://") @@ -32,7 +33,12 @@ def import_genericode(): content = f.read() # Parse the xml content - parser = etree.XMLParser(remove_blank_text=True) + parser = etree.XMLParser( + remove_blank_text=True, + resolve_entities=False, + load_dtd=False, + no_network=True, + ) try: root = etree.fromstring(content, parser=parser) except Exception as e: @@ -104,7 +110,7 @@ def get_genericode_columns_and_examples(root): # Get column names for column in root.findall(".//Column"): - column_id = column.get("Id") + column_id = escape_html(column.get("Id")) columns.append(column_id) example_values[column_id] = [] filterable_columns[column_id] = set() @@ -112,7 +118,7 @@ def get_genericode_columns_and_examples(root): # Get all values and count unique occurrences for row in root.findall(".//SimpleCodeList/Row"): for value in row.findall("Value"): - column_id = value.get("ColumnRef") + column_id = escape_html(value.get("ColumnRef")) if column_id not in columns: # Handle undeclared column columns.append(column_id) @@ -123,7 +129,7 @@ def get_genericode_columns_and_examples(root): if simple_value is None: continue - filterable_columns[column_id].add(simple_value.text) + filterable_columns[column_id].add(escape_html(simple_value.text)) # Get example values (up to 3) and filter columns with cardinality <= 5 for row in root.findall(".//SimpleCodeList/Row")[:3]: @@ -133,7 +139,7 @@ def get_genericode_columns_and_examples(root): if simple_value is None: continue - example_values[column_id].append(simple_value.text) + example_values[column_id].append(escape_html(simple_value.text)) filterable_columns = {k: list(v) for k, v in filterable_columns.items() if len(v) <= 5} From 8b16c310f4f09fdfc3af83001e043754674fecf8 Mon Sep 17 00:00:00 2001 From: Nishka Gosalia <58264710+nishkagosalia@users.noreply.github.com> Date: Thu, 9 Apr 2026 18:12:46 +0530 Subject: [PATCH 19/27] Revert "fix: update_nsm only in warehouse creation (backport #54165)" --- erpnext/stock/doctype/warehouse/warehouse.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index a19c35f26fa..a1991dd9c07 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -65,8 +65,7 @@ class Warehouse(NestedSet): self.warn_about_multiple_warehouse_account() def on_update(self): - if self.is_new() or self.has_value_changed("parent_warehouse"): - self.update_nsm_model() + self.update_nsm_model() def update_nsm_model(self): frappe.utils.nestedset.update_nsm(self) From cb24d9404dae28cb033b1c314db5ac2b35a7d712 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 10:53:04 +0000 Subject: [PATCH 20/27] fix: remove unneccessary function for serial no status updation (backport #54191) (#54196) Co-authored-by: Mihir Kandoi fix: remove unneccessary function for serial no status updation (#54191) --- .../test_stock_reconciliation.py | 41 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 28 ------------- 2 files changed, 41 insertions(+), 28 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 4e81c65a58d..d720ff260ae 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1793,6 +1793,47 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): elif s.id_plant == plant_b.name: self.assertEqual(s.actual_qty, 3) + def test_serial_no_status_with_backdated_stock_reco(self): + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + item_code = self.make_item( + "Test Item", + { + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "SERIAL.###", + }, + ).name + + warehouse = "_Test Warehouse - _TC" + + reco = create_stock_reconciliation( + item_code=item_code, + posting_date=add_days(nowdate(), -2), + warehouse=warehouse, + qty=1, + rate=80, + purpose="Opening Stock", + ) + + serial_no = get_serial_nos_from_bundle(reco.items[0].serial_and_batch_bundle)[0] + + create_delivery_note( + item_code=item_code, warehouse=warehouse, qty=1, rate=100, posting_date=nowdate() + ) + + self.assertEqual(frappe.get_value("Serial No", serial_no, "status"), "Delivered") + + reco = create_stock_reconciliation( + item_code=item_code, + posting_date=add_days(nowdate(), -1), + warehouse=warehouse, + qty=1, + rate=90, + ) + + self.assertEqual(frappe.get_value("Serial No", serial_no, "status"), "Delivered") + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 18e06fd6c10..978de2e172b 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1063,34 +1063,6 @@ class update_entries_after: sabb_doc.voucher_no = None sabb_doc.cancel() - if sle.serial_and_batch_bundle and frappe.get_cached_value("Item", sle.item_code, "has_serial_no"): - self.update_serial_no_status(sle) - - def update_serial_no_status(self, sle): - from erpnext.stock.serial_batch_bundle import get_serial_nos - - serial_nos = get_serial_nos(sle.serial_and_batch_bundle) - if not serial_nos: - return - - warehouse = None - status = "Inactive" - - if sle.actual_qty > 0: - warehouse = sle.warehouse - status = "Active" - - sn_table = frappe.qb.DocType("Serial No") - - query = ( - frappe.qb.update(sn_table) - .set(sn_table.warehouse, warehouse) - .set(sn_table.status, status) - .where(sn_table.name.isin(serial_nos)) - ) - - query.run() - def calculate_valuation_for_serial_batch_bundle(self, sle): if not frappe.db.exists("Serial and Batch Bundle", sle.serial_and_batch_bundle): return From 0a3f9f0b9f9bd0b50d4435cc613442e2c8a530fd Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 16:50:32 +0530 Subject: [PATCH 21/27] fix: update return value in workstation list view indicator (backport #54198) (#54200) Co-authored-by: Praveenkumar Dhanasekar <164200710+Praveenku-mar@users.noreply.github.com> fix: update return value in workstation list view indicator (#54198) --- erpnext/manufacturing/doctype/workstation/workstation_list.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/workstation/workstation_list.js b/erpnext/manufacturing/doctype/workstation/workstation_list.js index 33722634b96..4c81ab082bf 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_list.js +++ b/erpnext/manufacturing/doctype/workstation/workstation_list.js @@ -10,6 +10,6 @@ frappe.listview_settings["Workstation"] = { Setup: "blue", }; - return [__(doc.status), color_map[doc.status], true]; + return [__(doc.status), color_map[doc.status], "status,=," + doc.status]; }, }; From 430705f56c1192607e251dc9671c60ef76850a85 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 20:32:33 +0530 Subject: [PATCH 22/27] fix: account change in warehouse (backport #54182) (#54204) Co-authored-by: nishkagosalia --- erpnext/stock/doctype/warehouse/warehouse.py | 52 +++++--------------- 1 file changed, 13 insertions(+), 39 deletions(-) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index a1991dd9c07..45d7a459fb5 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -101,49 +101,23 @@ class Warehouse(NestedSet): def warn_about_multiple_warehouse_account(self): "If Warehouse value is split across multiple accounts, warn." - def get_accounts_where_value_is_booked(name): - sle = frappe.qb.DocType("Stock Ledger Entry") - gle = frappe.qb.DocType("GL Entry") - ac = frappe.qb.DocType("Account") - - return ( - frappe.qb.from_(sle) - .join(gle) - .on(sle.voucher_no == gle.voucher_no) - .join(ac) - .on(ac.name == gle.account) - .select(gle.account) - .distinct() - .where((sle.warehouse == name) & (ac.account_type == "Stock")) - .orderby(sle.creation) - .run(as_dict=True) - ) - - if self.is_new(): + if not frappe.db.count("Stock Ledger Entry", {"warehouse": self.name}): return - old_wh_account = frappe.db.get_value("Warehouse", self.name, "account") + doc_before_save = self.get_doc_before_save() + old_wh_account = doc_before_save.account if doc_before_save else None - # WH account is being changed or set get all accounts against which wh value is booked - if self.account != old_wh_account: - accounts = get_accounts_where_value_is_booked(self.name) - accounts = [d.account for d in accounts] + if self.is_new() or (self.account and old_wh_account == self.account): + return - if not accounts or (len(accounts) == 1 and self.account in accounts): - # if same singular account has stock value booked ignore - return - - warning = _("Warehouse's Stock Value has already been booked in the following accounts:") - account_str = "
" + ", ".join(frappe.bold(ac) for ac in accounts) - reason = "

" + _( - "Booking stock value across multiple accounts will make it harder to track stock and account value." - ) - - frappe.msgprint( - warning + account_str + reason, - title=_("Multiple Warehouse Accounts"), - indicator="orange", - ) + frappe.msgprint( + title=_("Warning: Account changed for warehouse"), + indicator="orange", + msg=_( + "Stock entries exist with the old account. Changing the account may lead to a mismatch between the warehouse closing balance and the account closing balance. The overall closing balance will still match, but not for the specific account." + ), + alert=True, + ) def check_if_sle_exists(self): return frappe.db.exists("Stock Ledger Entry", {"warehouse": self.name}) From 17ce550417661c4419e2ce96ad45d5b7ff562655 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 10 Apr 2026 23:44:55 +0530 Subject: [PATCH 23/27] Fix(bom): refetch the rate of item when 'source_from_supplier' is updated (backport #54187) (#54207) Co-authored-by: Sambhav Saxena <76242518+sambhavsaxena@users.noreply.github.com> Fix(bom): refetch the rate of item when 'source_from_supplier' is updated (#54187) --- erpnext/manufacturing/doctype/bom/bom.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index ed1628ded5f..3895f0bf17a 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -760,6 +760,8 @@ frappe.ui.form.on("BOM Item", "sourced_by_supplier", function (frm, cdt, cdn) { if (d.sourced_by_supplier) { d.rate = 0; refresh_field("rate", d.name, d.parentfield); + } else { + get_bom_material_detail(frm.doc, cdt, cdn, false); } }); From cbe5ad6337eeeb5965cd530792fefd15e699d366 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 21:06:05 +0530 Subject: [PATCH 24/27] fix: make operation mandatory when any sub operation row is added (backport #54245) (#54247) Co-authored-by: Sudarshan <73628063+sudarsan2001@users.noreply.github.com> fix: make operation mandatory when any sub operation row is added (#54245) --- .../manufacturing/doctype/sub_operation/sub_operation.json | 5 +++-- erpnext/manufacturing/doctype/sub_operation/sub_operation.py | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json index c7530e41aca..36160fec699 100644 --- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.json +++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.json @@ -16,7 +16,8 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Operation", - "options": "Operation" + "options": "Operation", + "reqd": 1 }, { "default": "0", @@ -40,7 +41,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2025-08-04 16:15:11.425349", + "modified": "2026-04-13 12:17:33.776504", "modified_by": "Administrator", "module": "Manufacturing", "name": "Sub Operation", diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.py b/erpnext/manufacturing/doctype/sub_operation/sub_operation.py index f4bb62e8f6c..a34a96fc1a5 100644 --- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.py +++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.py @@ -16,7 +16,7 @@ class SubOperation(Document): from frappe.types import DF description: DF.SmallText | None - operation: DF.Link | None + operation: DF.Link parent: DF.Data parentfield: DF.Data parenttype: DF.Data From 46a1c6fda072dad6077fc5171bfcdb8395acdb64 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:49:45 +0000 Subject: [PATCH 25/27] fix(stock): update bin to zero when no previous sle exists (backport #54236) (#54263) Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com> fix(stock): update bin to zero when no previous sle exists (#54236) --- erpnext/stock/stock_ledger.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 978de2e172b..084b37113f0 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -549,6 +549,16 @@ class update_entries_after: previous_sle = get_previous_sle_of_current_voucher(args) if previous_sle: self.prev_sle_dict[(args.get("item_code"), args.get("warehouse"))] = previous_sle + else: + self.prev_sle_dict[(args.get("item_code"), args.get("warehouse"))] = frappe._dict( + { + "qty_after_transaction": 0.0, + "valuation_rate": 0.0, + "stock_value": 0.0, + "prev_stock_value": 0.0, + "stock_queue": [], + } + ) warehouse_dict.previous_sle = previous_sle From 0e9b3b459a45092c6a46a0457a961316ed4b4cc6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 11:33:23 +0000 Subject: [PATCH 26/27] fix(stock): remove float precision to fix precision issue (backport #54284) (#54288) Co-authored-by: Sudharsanan Ashok <135326972+Sudharsanan11@users.noreply.github.com> fix(stock): remove float precision to fix precision issue (#54284) --- .../batch_wise_balance_history/batch_wise_balance_history.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index f895947f503..e5cb69ff816 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -219,7 +219,7 @@ def get_item_warehouse_batch_map(filters, float_precision): ) qty_dict.bal_qty = flt(qty_dict.bal_qty, float_precision) + flt(d.actual_qty, float_precision) - qty_dict.bal_value += flt(d.stock_value_difference, float_precision) + qty_dict.bal_value += flt(d.stock_value_difference) return iwb_map From 8b3d65ae78055648dfcb73873f0e975887e5b530 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 14 Apr 2026 22:27:04 +0530 Subject: [PATCH 27/27] Revert "fix: sync paid and received amount" (backport #54238) (#54292) Co-authored-by: Vishnu Priya Baskaran <145791817+ervishnucs@users.noreply.github.com> fix: sync paid and received amount" (#54238) --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 42fdc5124bf..60c8e47f8f0 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -839,7 +839,7 @@ frappe.ui.form.on("Payment Entry", { paid_amount: function (frm) { frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; - if (frm.doc.paid_amount) { + if (!frm.doc.received_amount) { if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { frm.set_value("received_amount", frm.doc.paid_amount); } else if (company_currency == frm.doc.paid_to_account_currency) { @@ -860,7 +860,7 @@ frappe.ui.form.on("Payment Entry", { flt(frm.doc.received_amount) * flt(frm.doc.target_exchange_rate) ); - if (frm.doc.received_amount) { + if (!frm.doc.paid_amount) { if (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { frm.set_value("paid_amount", frm.doc.received_amount); if (frm.doc.target_exchange_rate) {