2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-06 09:43:38 +00:00

Prevent delete serialized stock (#11872)

* Add setting to prevent deletion of serialized stock

Co-authored-by: Copilot <copilot@github.com>

* Add unit test

Co-authored-by: Copilot <copilot@github.com>

* Update CHANGELOG

Co-authored-by: Copilot <copilot@github.com>

---------

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
Oliver
2026-05-05 10:43:02 +10:00
committed by GitHub
parent 3c3964062f
commit 18e3c5a0e9
6 changed files with 48 additions and 0 deletions
@@ -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'),
+8
View File
@@ -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."""
+31
View File
@@ -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."""
@@ -245,6 +245,7 @@ export default function SystemSettings() {
<GlobalSettingList
keys={[
'SERIAL_NUMBER_GLOBALLY_UNIQUE',
'STOCK_ALLOW_DELETE_SERIALIZED',
'STOCK_DELETE_DEPLETED_DEFAULT',
'STOCK_BATCH_CODE_TEMPLATE',
'STOCK_OWNERSHIP_CONTROL',