From 68747b5818e10cbe2a92df2b1d6f0d9e236e76e7 Mon Sep 17 00:00:00 2001 From: diptanilsaha Date: Sat, 8 Nov 2025 12:31:40 +0530 Subject: [PATCH] fix: prevent pos opening entry creation for disabled pos profile (cherry picked from commit e35e8968f0825256e09b7beba8615270b9559e4d) # Conflicts: # erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py --- .../pos_opening_entry/pos_opening_entry.py | 14 ++- .../test_pos_opening_entry.py | 99 +++++++++++++++++++ 2 files changed, 111 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py index 7f1890ceabf..d7965c58831 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py +++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.py @@ -41,9 +41,19 @@ class POSOpeningEntry(StatusUpdater): self.set_status() def validate_pos_profile_and_cashier(self): - if self.company != frappe.db.get_value("POS Profile", self.pos_profile, "company"): + if not frappe.db.exists("POS Profile", self.pos_profile): + frappe.throw(_("POS Profile {} does not exist.").format(self.pos_profile)) + + pos_profile_company, pos_profile_disabled = frappe.db.get_value( + "POS Profile", self.pos_profile, ["company", "disabled"] + ) + + if pos_profile_disabled: + frappe.throw(_("POS Profile {} is disabled.").format(frappe.bold(self.pos_profile))) + + if self.company != pos_profile_company: frappe.throw( - _("POS Profile {} does not belongs to company {}").format(self.pos_profile, self.company) + _("POS Profile {} does not belong to company {}").format(self.pos_profile, self.company) ) if not cint(frappe.db.get_value("User", self.user, "enabled")): diff --git a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py index 64c658ab151..e0276f8bb62 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py +++ b/erpnext/accounts/doctype/pos_opening_entry/test_pos_opening_entry.py @@ -6,8 +6,107 @@ import unittest import frappe +<<<<<<< HEAD class TestPOSOpeningEntry(unittest.TestCase): pass +======= +class TestPOSOpeningEntry(IntegrationTestCase): + @classmethod + def setUpClass(cls): + frappe.db.sql("delete from `tabPOS Opening Entry`") + cls.enterClassContext(cls.change_settings("POS Settings", {"invoice_type": "POS Invoice"})) + + @classmethod + def tearDownClass(cls): + frappe.db.sql("delete from `tabPOS Opening Entry`") + + def setUp(self): + # Make stock available for POS Sales + frappe.db.sql("delete from `tabPOS Opening Entry`") + make_stock_entry(target="_Test Warehouse - _TC", qty=2, basic_rate=100) + from erpnext.accounts.doctype.pos_closing_entry.test_pos_closing_entry import init_user_and_profile + + self.init_user_and_profile = init_user_and_profile + + def tearDown(self): + frappe.set_user("Administrator") + frappe.db.sql("delete from `tabPOS Profile`") + + def test_pos_opening_entry(self): + test_user, pos_profile = self.init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name) + + self.assertEqual(opening_entry.status, "Open") + self.assertNotEqual(opening_entry.docstatus, 0) + + def test_pos_opening_entry_on_disabled_pos(self): + test_user, pos_profile = self.init_user_and_profile(disabled=1) + + with self.assertRaises(frappe.ValidationError): + create_opening_entry(pos_profile, test_user.name) + + def test_multiple_pos_opening_entries_for_same_pos_profile(self): + test_user, pos_profile = self.init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name) + + self.assertEqual(opening_entry.status, "Open") + with self.assertRaises(frappe.ValidationError): + create_opening_entry(pos_profile, test_user.name) + + def test_multiple_pos_opening_entry_for_multiple_pos_profiles(self): + test_user, pos_profile = self.init_user_and_profile() + opening_entry_1 = create_opening_entry(pos_profile, test_user.name) + + self.assertEqual(opening_entry_1.status, "Open") + self.assertEqual(opening_entry_1.user, test_user.name) + + cashier_user = create_user("test_cashier@example.com", "Accounts Manager", "Sales Manager") + frappe.set_user(cashier_user.name) + + pos_profile2 = make_pos_profile(name="_Test POS Profile 2") + opening_entry_2 = create_opening_entry(pos_profile2, cashier_user.name) + + self.assertEqual(opening_entry_2.status, "Open") + self.assertEqual(opening_entry_2.user, cashier_user.name) + + def test_multiple_pos_opening_entry_for_same_pos_profile_by_multiple_user(self): + test_user, pos_profile = self.init_user_and_profile() + cashier_user = create_user("test_cashier@example.com", "Accounts Manager", "Sales Manager") + + opening_entry = create_opening_entry(pos_profile, test_user.name) + self.assertEqual(opening_entry.status, "Open") + + with self.assertRaises(frappe.ValidationError): + create_opening_entry(pos_profile, cashier_user.name) + + def test_user_assignment_to_multiple_pos_profile(self): + test_user, pos_profile = self.init_user_and_profile() + opening_entry_1 = create_opening_entry(pos_profile, test_user.name) + self.assertEqual(opening_entry_1.user, test_user.name) + + pos_profile2 = make_pos_profile(name="_Test POS Profile 2") + with self.assertRaises(frappe.ValidationError): + create_opening_entry(pos_profile2, test_user.name) + + def test_cancel_pos_opening_entry_without_invoices(self): + test_user, pos_profile = self.init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name, get_obj=True) + + opening_entry.cancel() + self.assertEqual(opening_entry.status, "Cancelled") + self.assertNotEqual(opening_entry.docstatus, 1) + + def test_cancel_pos_opening_entry_with_invoice(self): + test_user, pos_profile = self.init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name, get_obj=True) + + pos_inv1 = create_pos_invoice(pos_profile=pos_profile.name, rate=100, do_not_save=1) + pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) + pos_inv1.save() + pos_inv1.submit() + + self.assertRaises(frappe.ValidationError, opening_entry.cancel) +>>>>>>> e35e8968f0 (fix: prevent pos opening entry creation for disabled pos profile) def create_opening_entry(pos_profile, user):