From 383925de4e7006e9e6ae9ea18adfc606754a8104 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 4 Jul 2025 11:44:33 +1000 Subject: [PATCH] Logic fix for LOCK_SETTING (#9948) * Logic fix for LOCK_SETTING - Because the setting is to "allow editing", it is really an UNLOCK setting - This setting had to be DISABLED (false) to allow editing of completed orders * Updated logic * Tweak unit tests --- src/backend/InvenTree/order/models.py | 23 +++++++++++++--------- src/backend/InvenTree/order/test_api.py | 2 ++ src/backend/InvenTree/order/tests.py | 11 ++++++++--- src/backend/InvenTree/part/test_pricing.py | 2 ++ 4 files changed, 26 insertions(+), 12 deletions(-) diff --git a/src/backend/InvenTree/order/models.py b/src/backend/InvenTree/order/models.py index 788fd52bc0..cb360198d7 100644 --- a/src/backend/InvenTree/order/models.py +++ b/src/backend/InvenTree/order/models.py @@ -297,7 +297,7 @@ class Order( """ REQUIRE_RESPONSIBLE_SETTING = None - LOCK_SETTING = None + UNLOCK_SETTING = None class Meta: """Metaclass options. Abstract ensures no database table is created.""" @@ -334,14 +334,19 @@ class Order( def check_locked(self, db: bool = False) -> bool: """Check if this order is 'locked'. + A locked order cannot be modified after it has been completed. + Args: db: If True, check with the database. If False, check the instance (default False). """ - return ( - self.LOCK_SETTING - and get_global_setting(self.LOCK_SETTING) - and self.check_complete(db) - ) + if not self.check_complete(db=db): + # If the order is not complete, it is not locked + return False + + if self.UNLOCK_SETTING: + return get_global_setting(self.UNLOCK_SETTING, backup_value=False) is False + + return False def check_complete(self, db: bool = False) -> bool: """Check if this order is 'complete'. @@ -532,7 +537,7 @@ class PurchaseOrder(TotalPriceMixin, Order): REFERENCE_PATTERN_SETTING = 'PURCHASEORDER_REFERENCE_PATTERN' REQUIRE_RESPONSIBLE_SETTING = 'PURCHASEORDER_REQUIRE_RESPONSIBLE' STATUS_CLASS = PurchaseOrderStatus - LOCK_SETTING = 'PURCHASEORDER_EDIT_COMPLETED_ORDERS' + UNLOCK_SETTING = 'PURCHASEORDER_EDIT_COMPLETED_ORDERS' class Meta: """Model meta options.""" @@ -1114,7 +1119,7 @@ class SalesOrder(TotalPriceMixin, Order): REFERENCE_PATTERN_SETTING = 'SALESORDER_REFERENCE_PATTERN' REQUIRE_RESPONSIBLE_SETTING = 'SALESORDER_REQUIRE_RESPONSIBLE' STATUS_CLASS = SalesOrderStatus - LOCK_SETTING = 'SALESORDER_EDIT_COMPLETED_ORDERS' + UNLOCK_SETTING = 'SALESORDER_EDIT_COMPLETED_ORDERS' class Meta: """Model meta options.""" @@ -2415,7 +2420,7 @@ class ReturnOrder(TotalPriceMixin, Order): REFERENCE_PATTERN_SETTING = 'RETURNORDER_REFERENCE_PATTERN' REQUIRE_RESPONSIBLE_SETTING = 'RETURNORDER_REQUIRE_RESPONSIBLE' STATUS_CLASS = ReturnOrderStatus - LOCK_SETTING = 'RETURNORDER_EDIT_COMPLETED_ORDERS' + UNLOCK_SETTING = 'RETURNORDER_EDIT_COMPLETED_ORDERS' class Meta: """Model meta options.""" diff --git a/src/backend/InvenTree/order/test_api.py b/src/backend/InvenTree/order/test_api.py index e82a1ac767..58fe2ff17f 100644 --- a/src/backend/InvenTree/order/test_api.py +++ b/src/backend/InvenTree/order/test_api.py @@ -1590,6 +1590,8 @@ class SalesOrderTest(OrderTest): def test_export(self): """Test we can export the SalesOrder list.""" + set_global_setting(models.SalesOrder.UNLOCK_SETTING, True) + n = models.SalesOrder.objects.count() # Check there are some sales orders diff --git a/src/backend/InvenTree/order/tests.py b/src/backend/InvenTree/order/tests.py index 734288ff1f..5a56382d11 100644 --- a/src/backend/InvenTree/order/tests.py +++ b/src/backend/InvenTree/order/tests.py @@ -65,6 +65,8 @@ class OrderTest(TestCase, ExchangeRateMixin): """Test the (auto)locking functionality of the (Purchase)Order model.""" order = PurchaseOrder.objects.get(pk=1) + set_global_setting(PurchaseOrder.UNLOCK_SETTING, True) + order.status = PurchaseOrderStatus.PENDING order.save() self.assertFalse(order.check_locked()) @@ -82,16 +84,18 @@ class OrderTest(TestCase, ExchangeRateMixin): order.save() # Turn on auto-locking - set_global_setting(PurchaseOrder.LOCK_SETTING, True) + set_global_setting(PurchaseOrder.UNLOCK_SETTING, False) # still not locked self.assertFalse(order.check_locked()) order.status = PurchaseOrderStatus.COMPLETE - # the instance is locked, the db instance is not + + # The instance is locked, the db instance is not self.assertFalse(order.check_locked(True)) self.assertTrue(order.check_locked()) order.save() - # now everything is locked + + # Now everything is locked self.assertTrue(order.check_locked(True)) self.assertTrue(order.check_locked()) @@ -99,6 +103,7 @@ class OrderTest(TestCase, ExchangeRateMixin): with self.assertRaises(django_exceptions.ValidationError): order.description = 'test1' order.save() + order.refresh_from_db() self.assertEqual(order.description, 'Ordering some screws') diff --git a/src/backend/InvenTree/part/test_pricing.py b/src/backend/InvenTree/part/test_pricing.py index c7dca0f169..7cbfcb2583 100644 --- a/src/backend/InvenTree/part/test_pricing.py +++ b/src/backend/InvenTree/part/test_pricing.py @@ -334,6 +334,8 @@ class PartPricingTests(InvenTreeTestCase): po.status = PurchaseOrderStatus.COMPLETE.value po.save() + set_global_setting(order.models.PurchaseOrder.UNLOCK_SETTING, True) + pricing.update_purchase_cost() # Cost is still null, as the lines have not been received