From d0184e07b3e2a173aa4daae471ebf27c172ac35e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sun, 21 Jun 2026 12:50:33 +0530 Subject: [PATCH 1/4] fix(selling): hide commission fields without a sales partner and stop copying them Across Sales Order, Delivery Note, Sales Invoice and POS Invoice, the Commission section's commission_rate, total_commission and amount_eligible_for_commission are sales-partner commission fields: - depends_on eval:doc.sales_partner so they only show when a Sales Partner is set; - no_copy so a duplicated/amended document does not carry a stale commission rate or computed commission amount (the sales partner itself still copies). depends_on is client-only, so the server-side commission calculation is unchanged. Add a Sales Order test for the no_copy behaviour. --- .../doctype/pos_invoice/pos_invoice.json | 8 +++++++- .../doctype/sales_invoice/sales_invoice.json | 8 +++++++- .../doctype/sales_order/sales_order.json | 8 +++++++- .../doctype/sales_order/test_sales_order.py | 19 +++++++++++++++++++ .../doctype/delivery_note/delivery_note.json | 8 +++++++- 5 files changed, 47 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 14c5153f0b9..a837e5c145a 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1425,19 +1425,23 @@ "width": "50%" }, { + "depends_on": "eval:doc.sales_partner", "fetch_from": "sales_partner.commission_rate", "fetch_if_empty": 1, "fieldname": "commission_rate", "fieldtype": "Float", "label": "Commission Rate (%)", + "no_copy": 1, "oldfieldname": "commission_rate", "oldfieldtype": "Currency", "print_hide": 1 }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "total_commission", "fieldtype": "Currency", "label": "Total Commission", + "no_copy": 1, "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -1528,9 +1532,11 @@ "print_hide": 1 }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "amount_eligible_for_commission", "fieldtype": "Currency", "label": "Amount Eligible for Commission", + "no_copy": 1, "options": "Company:company:default_currency", "read_only": 1 }, @@ -1636,7 +1642,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2026-05-28 12:22:50.253090", + "modified": "2026-06-21 12:46:13.250145", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 0c137db39de..a2c79e63eb4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1747,6 +1747,7 @@ "width": "50%" }, { + "depends_on": "eval:doc.sales_partner", "fetch_from": "sales_partner.commission_rate", "fetch_if_empty": 1, "fieldname": "commission_rate", @@ -1754,16 +1755,19 @@ "hide_days": 1, "hide_seconds": 1, "label": "Commission Rate (%)", + "no_copy": 1, "oldfieldname": "commission_rate", "oldfieldtype": "Currency", "print_hide": 1 }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "total_commission", "fieldtype": "Currency", "hide_days": 1, "hide_seconds": 1, "label": "Total Commission", + "no_copy": 1, "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -1970,9 +1974,11 @@ "read_only": 1 }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "amount_eligible_for_commission", "fieldtype": "Currency", "label": "Amount Eligible for Commission", + "no_copy": 1, "options": "Company:company:default_currency", "read_only": 1 }, @@ -2354,7 +2360,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2026-05-28 12:15:12.486443", + "modified": "2026-06-21 12:46:13.250145", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 739b91f18b1..bd40fbb9e01 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -1329,6 +1329,7 @@ "width": "50%" }, { + "depends_on": "eval:doc.sales_partner", "fetch_from": "sales_partner.commission_rate", "fetch_if_empty": 1, "fieldname": "commission_rate", @@ -1336,17 +1337,20 @@ "hide_days": 1, "hide_seconds": 1, "label": "Commission Rate", + "no_copy": 1, "oldfieldname": "commission_rate", "oldfieldtype": "Currency", "print_hide": 1, "width": "100px" }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "total_commission", "fieldtype": "Currency", "hide_days": 1, "hide_seconds": 1, "label": "Total Commission", + "no_copy": 1, "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -1477,9 +1481,11 @@ }, { "allow_on_submit": 1, + "depends_on": "eval:doc.sales_partner", "fieldname": "amount_eligible_for_commission", "fieldtype": "Currency", "label": "Amount Eligible for Commission", + "no_copy": 1, "options": "Company:company:default_currency", "read_only": 1 }, @@ -1760,7 +1766,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2026-05-28 11:41:11.823034", + "modified": "2026-06-21 12:46:13.250145", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 1f7daf43e13..625fc45d492 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -3044,6 +3044,25 @@ class TestSalesOrder(ERPNextTestSuite): finally: frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) + def test_commission_fields_not_copied_on_duplicate(self): + """Commission rate/amount fields are no_copy; only the sales partner carries to a copy.""" + frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) + try: + so = make_sales_order(qty=10, rate=100, do_not_save=True) + so.sales_partner = "_Test Sales Partner India - 1" + so.commission_rate = 7 + so.save() + self.assertEqual(so.total_commission, 70) + + # ignore_no_copy=False mirrors UI "Duplicate"/amend, which honour no_copy + duplicate = frappe.copy_doc(so, ignore_no_copy=False) + self.assertEqual(duplicate.sales_partner, "_Test Sales Partner India - 1") + self.assertFalse(duplicate.commission_rate) + self.assertFalse(duplicate.total_commission) + self.assertFalse(duplicate.amount_eligible_for_commission) + finally: + frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) + def compare_payment_schedules(doc, doc1, doc2): for index, schedule in enumerate(doc1.get("payment_schedule")): diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 6cb8e707449..5e0fc9bfaf2 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1154,11 +1154,13 @@ "width": "50%" }, { + "depends_on": "eval:doc.sales_partner", "fetch_from": "sales_partner.commission_rate", "fetch_if_empty": 1, "fieldname": "commission_rate", "fieldtype": "Float", "label": "Commission Rate (%)", + "no_copy": 1, "oldfieldname": "commission_rate", "oldfieldtype": "Currency", "print_hide": 1, @@ -1166,9 +1168,11 @@ "width": "100px" }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "total_commission", "fieldtype": "Currency", "label": "Total Commission", + "no_copy": 1, "oldfieldname": "total_commission", "oldfieldtype": "Currency", "options": "Company:company:default_currency", @@ -1262,9 +1266,11 @@ "read_only": 1 }, { + "depends_on": "eval:doc.sales_partner", "fieldname": "amount_eligible_for_commission", "fieldtype": "Currency", "label": "Amount Eligible for Commission", + "no_copy": 1, "options": "Company:company:default_currency", "read_only": 1 }, @@ -1466,7 +1472,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2026-05-28 11:44:37.286743", + "modified": "2026-06-21 12:46:13.250145", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", From 2d54f651cdcd8eca68bac0c22ecf729979999ecd Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Jun 2026 14:20:00 +0530 Subject: [PATCH 2/4] fix: restore apply_permission value after running test Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- erpnext/selling/doctype/sales_order/test_sales_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 625fc45d492..c841bcdd02b 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -3061,7 +3061,7 @@ class TestSalesOrder(ERPNextTestSuite): self.assertFalse(duplicate.total_commission) self.assertFalse(duplicate.amount_eligible_for_commission) finally: - frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) + frappe.db.set_value("Item", "_Test Item", "grant_commission", 0) def compare_payment_schedules(doc, doc1, doc2): From 92047e896c4294b5e7b30a2691e1f29c8e9ce3ed Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Jun 2026 17:01:40 +0530 Subject: [PATCH 3/4] fix(selling): carry commission_rate through Make Delivery Note / Sales Invoice commission_rate is no_copy so it is not carried on Duplicate/amend, but the mapper also skips no_copy fields, leaving the mapped Delivery Note / Sales Invoice showing 0 commission until saved (it only re-fetched from the sales partner on save). Map commission_rate explicitly in the SO->DN, SO->SI and DN->SI mappers so it carries over immediately; Duplicate still does not copy it. --- erpnext/selling/doctype/sales_order/mapper.py | 9 ++++++++- .../doctype/sales_order/test_sales_order.py | 18 ++++++++++++++++++ erpnext/stock/doctype/delivery_note/mapper.py | 3 ++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/mapper.py b/erpnext/selling/doctype/sales_order/mapper.py index 086e6c5b6fd..d2972fdc302 100644 --- a/erpnext/selling/doctype/sales_order/mapper.py +++ b/erpnext/selling/doctype/sales_order/mapper.py @@ -245,7 +245,12 @@ def make_delivery_note( sre_details = get_sre_reserved_qty_details_for_voucher("Sales Order", source_name) mapper = { - "Sales Order": {"doctype": "Delivery Note", "validation": {"docstatus": ["=", 1]}}, + "Sales Order": { + "doctype": "Delivery Note", + "validation": {"docstatus": ["=", 1]}, + # commission_rate is no_copy (so it isn't carried on Duplicate), map it explicitly here + "field_map": {"commission_rate": "commission_rate"}, + }, "Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True}, "Sales Team": {"doctype": "Sales Team", "add_if_empty": True}, } @@ -558,6 +563,8 @@ def make_sales_invoice( "doctype": "Sales Invoice", "field_map": { "party_account_currency": "party_account_currency", + # commission_rate is no_copy (so it isn't carried on Duplicate), map it explicitly here + "commission_rate": "commission_rate", }, "field_no_map": ["payment_terms_template"], "validation": {"docstatus": ["=", 1]}, diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index c841bcdd02b..934141a2090 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -3063,6 +3063,24 @@ class TestSalesOrder(ERPNextTestSuite): finally: frappe.db.set_value("Item", "_Test Item", "grant_commission", 0) + def test_commission_rate_carried_through_mapper(self): + """commission_rate is no_copy, but Make Delivery Note / Sales Invoice still carries it.""" + from erpnext.selling.doctype.sales_order.mapper import make_delivery_note, make_sales_invoice + + original = frappe.db.get_value("Item", "_Test Item", "grant_commission") + frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) + try: + so = make_sales_order(qty=10, rate=100, do_not_save=True) + so.sales_partner = "_Test Sales Partner India - 1" + so.commission_rate = 7 + so.submit() + + # carried to the mapped (unsaved) documents even though the field is no_copy + self.assertEqual(make_delivery_note(so.name).commission_rate, 7) + self.assertEqual(make_sales_invoice(so.name).commission_rate, 7) + finally: + frappe.db.set_value("Item", "_Test Item", "grant_commission", original) + def compare_payment_schedules(doc, doc1, doc2): for index, schedule in enumerate(doc1.get("payment_schedule")): diff --git a/erpnext/stock/doctype/delivery_note/mapper.py b/erpnext/stock/doctype/delivery_note/mapper.py index a77a94dcf88..605a2d22df6 100644 --- a/erpnext/stock/doctype/delivery_note/mapper.py +++ b/erpnext/stock/doctype/delivery_note/mapper.py @@ -132,7 +132,8 @@ def make_sales_invoice( { "Delivery Note": { "doctype": "Sales Invoice", - "field_map": {"is_return": "is_return"}, + # commission_rate is no_copy (so it isn't carried on Duplicate), map it explicitly here + "field_map": {"is_return": "is_return", "commission_rate": "commission_rate"}, "validation": {"docstatus": ["=", 1]}, }, "Delivery Note Item": { From eee1fdf2768042ee3092710da5398bbe70a68b3d Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Jun 2026 18:05:20 +0530 Subject: [PATCH 4/4] fix: reset grant_commission to default 1 after tests Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --- erpnext/selling/doctype/sales_order/test_sales_order.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 934141a2090..5ba55d304c3 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -3061,7 +3061,7 @@ class TestSalesOrder(ERPNextTestSuite): self.assertFalse(duplicate.total_commission) self.assertFalse(duplicate.amount_eligible_for_commission) finally: - frappe.db.set_value("Item", "_Test Item", "grant_commission", 0) + frappe.db.set_value("Item", "_Test Item", "grant_commission", 1) def test_commission_rate_carried_through_mapper(self): """commission_rate is no_copy, but Make Delivery Note / Sales Invoice still carries it."""