Uninstall fix (#12378)

* Prevent uninstall into structural location

* Regression tests
This commit is contained in:
Oliver 2026-07-13 20:21:56 +10:00 committed by GitHub
parent a12fd546ff
commit b64182b565
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 60 additions and 0 deletions

View File

@ -1760,6 +1760,11 @@ class StockItem(
if self.belongs_to is None:
return False
if location and location.structural:
raise ValidationError({
'location': _('Cannot assign stock to structural location')
})
# Add a transaction note to the parent item
self.belongs_to.add_tracking_entry(
StockHistoryCode.REMOVED_CHILD_ITEM,

View File

@ -938,6 +938,15 @@ class UninstallStockItemSerializer(serializers.Serializer):
allow_blank=True,
)
def validate_location(self, location):
"""Validate the provided location."""
if location and location.structural:
raise ValidationError(
_('Structural locations cannot be assigned stock items')
)
return location
def save(self):
"""Uninstall stock item."""
item = self.context.get('item')

View File

@ -1977,6 +1977,20 @@ class StockItemTest(StockAPITestCase):
url = reverse('api-stock-item-uninstall', kwargs={'pk': sub_item.pk})
# An uninstalled item cannot be moved into a structural location
structural = StockLocation.objects.create(
name='Structural location', structural=True
)
response = self.post(url, {'location': structural.pk}, expected_code=400)
self.assertIn(
'Structural locations cannot be assigned stock items', str(response.data)
)
sub_item.refresh_from_db()
self.assertEqual(sub_item.belongs_to, item)
self.post(url, {'location': 1}, expected_code=201)
sub_item.refresh_from_db()

View File

@ -351,6 +351,38 @@ class StockTest(StockTestBase):
stock.splitStock(stock.quantity, None, self.user)
self.assertEqual(StockItem.objects.filter(part=3).count(), n + 1)
def test_uninstall_into_structural_location(self):
"""Test that an item cannot be uninstalled into a structural location."""
parent = StockItem.objects.get(pk=1)
item = StockItem.objects.get(pk=2)
item.belongs_to = parent
item.save()
n_entries = item.tracking_info.count()
n_parent_entries = parent.tracking_info.count()
structural = StockLocation.objects.create(
name='Structural location', structural=True
)
with self.assertRaises(ValidationError):
item.uninstall_into_location(structural, self.user, 'Uninstalling')
# The item remains installed, with no location change or tracking entries
item.refresh_from_db()
self.assertEqual(item.belongs_to, parent)
self.assertNotEqual(item.location, structural)
self.assertEqual(item.tracking_info.count(), n_entries)
self.assertEqual(parent.tracking_info.count(), n_parent_entries)
# Uninstalling into a non-structural location is still permitted
item.uninstall_into_location(self.drawer2, self.user, 'Uninstalling')
item.refresh_from_db()
self.assertIsNone(item.belongs_to)
self.assertEqual(item.location, self.drawer2)
def test_child_items(self):
"""Test the 'children' reverse relation and 'child_count' property.