diff --git a/CHANGELOG.md b/CHANGELOG.md index 457aa3295b..41f69a31f5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- [#11872](https://github.com/inventree/InvenTree/pull/11872) adds a global setting to allow or disallow the deletion of serialized stock items. - [#11861](https://github.com/inventree/InvenTree/pull/11861) adds support for bulk-replacing a component in multiple BOMs simultaneously - [#11853](https://github.com/inventree/InvenTree/pull/11853) adds BOM comparison functionality, allowing users to compare the BOM of one assembly with another assembly. - [#11809](https://github.com/inventree/InvenTree/pull/11809) adds multi-level subassembly display mode to the BOM table, allowing users to view multiple levels of subassemblies in a single table view. This is an optional display mode which can be toggled on or off by the user. diff --git a/docs/docs/settings/global.md b/docs/docs/settings/global.md index c729c61bb4..c1e4ad4379 100644 --- a/docs/docs/settings/global.md +++ b/docs/docs/settings/global.md @@ -206,6 +206,7 @@ Configuration of stock item options | Name | Description | Default | Units | | ---- | ----------- | ------- | ----- | {{ globalsetting("SERIAL_NUMBER_GLOBALLY_UNIQUE") }} +{{ globalsetting("STOCK_ALLOW_DELETE_SERIALIZED") }} {{ globalsetting("STOCK_DELETE_DEPLETED_DEFAULT") }} {{ globalsetting("STOCK_BATCH_CODE_TEMPLATE") }} {{ globalsetting("STOCK_ENABLE_EXPIRY") }} diff --git a/src/backend/InvenTree/common/setting/system.py b/src/backend/InvenTree/common/setting/system.py index bfab416a80..b06db79589 100644 --- a/src/backend/InvenTree/common/setting/system.py +++ b/src/backend/InvenTree/common/setting/system.py @@ -716,6 +716,12 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = { 'default': True, 'validator': bool, }, + 'STOCK_ALLOW_DELETE_SERIALIZED': { + 'name': _('Delete Serialized Stock'), + 'description': _('Allow deletion of stock items which have a serial number'), + 'default': True, + 'validator': bool, + }, 'STOCK_BATCH_CODE_TEMPLATE': { 'name': _('Batch Code Template'), 'description': _('Template for generating default batch codes for stock items'), diff --git a/src/backend/InvenTree/stock/models.py b/src/backend/InvenTree/stock/models.py index d2daf164c6..e841cf554e 100644 --- a/src/backend/InvenTree/stock/models.py +++ b/src/backend/InvenTree/stock/models.py @@ -449,6 +449,14 @@ class StockItem( order_insertion_by = ['part'] + def delete(self, **kwargs): + """Custom delete method for StockItem model.""" + if not get_global_setting('STOCK_ALLOW_DELETE_SERIALIZED', cache=False): + if self.serialized: + raise ValidationError(_('Serialized stock items cannot be deleted')) + + super().delete(**kwargs) + @staticmethod def get_api_url(): """Return API url.""" diff --git a/src/backend/InvenTree/stock/test_api.py b/src/backend/InvenTree/stock/test_api.py index e5b3bfe782..2ac10c75b8 100644 --- a/src/backend/InvenTree/stock/test_api.py +++ b/src/backend/InvenTree/stock/test_api.py @@ -2181,6 +2181,37 @@ class StockItemDeletionTest(StockAPITestCase): self.assertEqual(StockItem.objects.count(), n) + def test_delete_serialized(self): + """Test deletion of serialized stock items.""" + trackable_part = part.models.Part.objects.create( + name='My part', + description='A trackable part', + trackable=True, + default_location=StockLocation.objects.get(pk=1), + ) + + stock_item = StockItem.objects.create( + part=trackable_part, quantity=1, serial='12345' + ) + + set_global_setting('STOCK_ALLOW_DELETE_SERIALIZED', False) + + response = self.delete( + reverse('api-stock-detail', kwargs={'pk': stock_item.pk}), expected_code=400 + ) + + self.assertIn('Serialized stock items cannot be deleted', str(response.data)) + + set_global_setting('STOCK_ALLOW_DELETE_SERIALIZED', True) + + response = self.delete( + reverse('api-stock-detail', kwargs={'pk': stock_item.pk}), expected_code=204 + ) + + self.get( + reverse('api-stock-detail', kwargs={'pk': stock_item.pk}), expected_code=404 + ) + class StockTestResultTest(StockAPITestCase): """Tests for StockTestResult APIs.""" diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 44d8369f23..0556bac501 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -245,6 +245,7 @@ export default function SystemSettings() {