From 16e00962f5f5b46cc09910ff0be2a5a5ee4ebf1e Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 28 Sep 2021 10:14:25 +1000 Subject: [PATCH 1/4] Override the "delete" behaviour for StockItem API - Mark for deletion instead of calling database delete - Returns (almost) instantly instead of hanging - Much better UI experience when performing bulk delete operations --- InvenTree/stock/api.py | 11 +++++++++++ 1 file changed, 11 insertions(+) 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): """ From 2d9ca7c1b654062d804c7ddcda52b6ac08c09cf5 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 28 Sep 2021 10:36:01 +1000 Subject: [PATCH 2/4] Do not rebuild the entire StockItem tree every time a single StockItem is deleted! --- InvenTree/stock/models.py | 3 --- 1 file changed, 3 deletions(-) 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): """ From d9704f4123e1cbe5f840bd7964ec8eb12cd7854a Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 28 Sep 2021 10:41:03 +1000 Subject: [PATCH 3/4] Add unit test for deleting stock items via the API --- InvenTree/stock/test_api.py | 57 +++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 619b4444d7..3b6facc9a6 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,61 @@ 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) + + url = reverse('api-stock-detail', kwargs={'pk': pk}) + + # 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): From 62e62af5fc08cde8bd7b9102d6685c5a2194e80e Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 28 Sep 2021 10:41:30 +1000 Subject: [PATCH 4/4] PEP style fixes --- InvenTree/stock/test_api.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 3b6facc9a6..21c355fae2 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -626,8 +626,6 @@ class StockItemDeletionTest(StockAPITestCase): self.assertFalse(item.scheduled_for_deletion) - url = reverse('api-stock-detail', kwargs={'pk': pk}) - # Request deletion via the API self.delete( reverse('api-stock-detail', kwargs={'pk': pk}), @@ -648,6 +646,7 @@ class StockItemDeletionTest(StockAPITestCase): 0 ) + class StockTestResultTest(StockAPITestCase): def get_url(self):