From 99ed620dadb054530713f54df9905a8985776a5e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 4 Jul 2026 17:54:46 +0530 Subject: [PATCH 1/2] fix: reset_mode_of_payments raises AttributeError on POS Invoice --- .../pos_invoice/test_pos_invoice_reset_mop.py | 36 +++++++++++++++++++ .../doctype/sales_invoice/services/pos.py | 4 ++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py new file mode 100644 index 00000000000..623211a6ed2 --- /dev/null +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py @@ -0,0 +1,36 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# Regression test for https://github.com/frappe/erpnext/issues/56501 +# AttributeError: 'POSInvoice' object has no attribute 'is_created_using_pos' +# when calling reset_mode_of_payments on a draft POS Invoice. + +import frappe + +from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import ( + POSInvoiceTestMixin, + create_pos_invoice, +) +from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry + + +class TestPOSInvoiceResetModeOfPayments(POSInvoiceTestMixin): + def setUp(self): + super().setUp() + create_opening_entry(self.pos_profile, self.test_user.name) + + def test_reset_mode_of_payments_does_not_raise_attribute_error(self): + """Calling reset_mode_of_payments on a draft POS Invoice must not raise + AttributeError for the missing is_created_using_pos attribute. + + update_multi_mode_option accesses doc.is_created_using_pos, which is a + field on SalesInvoice but does not exist on POSInvoice, causing the error + reported in #56501 when a user tries to edit a saved draft order. + """ + inv = create_pos_invoice(do_not_submit=True) + + # This call must not raise AttributeError on the missing field. + inv.reset_mode_of_payments() + + # Payments should have been repopulated from the POS profile. + self.assertTrue(len(inv.payments) > 0, "Payments should be populated after reset") diff --git a/erpnext/accounts/doctype/sales_invoice/services/pos.py b/erpnext/accounts/doctype/sales_invoice/services/pos.py index 9c7a7c2654c..76fc770de47 100644 --- a/erpnext/accounts/doctype/sales_invoice/services/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/services/pos.py @@ -344,7 +344,9 @@ def update_multi_mode_option(doc, pos_profile) -> None: payment.account = payment_mode.default_account payment.type = payment_mode.type - mop_refetched = bool(doc.payments) and not doc.is_created_using_pos + # is_created_using_pos exists on Sales Invoice but not POS Invoice; use get() so this + # shared helper doesn't raise AttributeError when called on a POS Invoice + mop_refetched = bool(doc.payments) and not doc.get("is_created_using_pos") doc.set("payments", []) invalid_modes = [] From a1f6ae56ff0c988860fd5715b3799abeb5e22b21 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Sat, 4 Jul 2026 18:39:52 +0530 Subject: [PATCH 2/2] fix: removed unused import Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com> --- .../accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py index 623211a6ed2..0f0f6052576 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice_reset_mop.py @@ -5,8 +5,6 @@ # AttributeError: 'POSInvoice' object has no attribute 'is_created_using_pos' # when calling reset_mode_of_payments on a draft POS Invoice. -import frappe - from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import ( POSInvoiceTestMixin, create_pos_invoice,