diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 017457f600..eaa65dd763 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -109,6 +109,17 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView): return super().update(request, *args, **kwargs) + def perform_destroy(self, instance): + """ + Instead of "deleting" the StockItem + (which may take a long time) + we instead schedule it for deletion at a later date. + + The background worker will delete these in the future + """ + + instance.mark_for_deletion() + class StockAdjust(APIView): """ diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 3344dec0eb..1372e63406 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -1650,9 +1650,6 @@ def before_delete_stock_item(sender, instance, using, **kwargs): child.parent = instance.parent child.save() - # Rebuild the MPTT tree - StockItem.objects.rebuild() - class StockItemAttachment(InvenTreeAttachment): """ diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 619b4444d7..21c355fae2 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -18,6 +18,7 @@ from InvenTree.api_tester import InvenTreeAPITestCase from common.models import InvenTreeSetting from .models import StockItem, StockLocation +from .tasks import delete_old_stock_items class StockAPITestCase(InvenTreeAPITestCase): @@ -37,6 +38,7 @@ class StockAPITestCase(InvenTreeAPITestCase): 'stock.add', 'stock_location.change', 'stock_location.add', + 'stock.delete', ] def setUp(self): @@ -591,6 +593,60 @@ class StocktakeTest(StockAPITestCase): self.assertContains(response, 'Valid location must be specified', status_code=status.HTTP_400_BAD_REQUEST) +class StockItemDeletionTest(StockAPITestCase): + """ + Tests for stock item deletion via the API + """ + + def test_delete(self): + + # Check there are no stock items scheduled for deletion + self.assertEqual( + StockItem.objects.filter(scheduled_for_deletion=True).count(), + 0 + ) + + # Create and then delete a bunch of stock items + for idx in range(10): + + # Create new StockItem via the API + response = self.post( + reverse('api-stock-list'), + { + 'part': 1, + 'location': 1, + 'quantity': idx, + }, + expected_code=201 + ) + + pk = response.data['pk'] + + item = StockItem.objects.get(pk=pk) + + self.assertFalse(item.scheduled_for_deletion) + + # Request deletion via the API + self.delete( + reverse('api-stock-detail', kwargs={'pk': pk}), + expected_code=204 + ) + + # There should be 100x StockItem objects marked for deletion + self.assertEqual( + StockItem.objects.filter(scheduled_for_deletion=True).count(), + 10 + ) + + # Perform the actual delete (will take some time) + delete_old_stock_items() + + self.assertEqual( + StockItem.objects.filter(scheduled_for_deletion=True).count(), + 0 + ) + + class StockTestResultTest(StockAPITestCase): def get_url(self):