diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index ee86b975dc..6456c5994f 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -63,6 +63,13 @@ class InvenTreeConfig(AppConfig): schedule_type=Schedule.DAILY, ) + # Delete "old" stock items + InvenTree.tasks.schedule_task( + 'stock.tasks.delete_old_stock_items', + schedule_type=Schedule.MINUTES, + minutes=30, + ) + def update_exchange_rates(self): """ Update exchange rates each time the server is started, *if*: diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 992008b10e..b125fb55c9 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -216,6 +216,11 @@ class StockItem(MPTTModel): # A query filter which can be used to filter StockItem objects which have expired EXPIRED_FILTER = IN_STOCK_FILTER & ~Q(expiry_date=None) & Q(expiry_date__lt=datetime.now().date()) + def mark_for_deletion(self): + + self.scheduled_for_deletion = True + self.save() + def save(self, *args, **kwargs): """ Save this StockItem to the database. Performs a number of checks: @@ -1300,10 +1305,10 @@ class StockItem(MPTTModel): self.quantity = quantity - if quantity == 0 and self.delete_on_deplete and self.can_delete(): - # TODO - Do not actually "delete" stock at this point - instead give it a "DELETED" flag - self.delete() + if quantity == 0 and self.delete_on_deplete and self.can_delete(): + self.mark_for_deletion() + return False else: self.save() diff --git a/InvenTree/stock/tasks.py b/InvenTree/stock/tasks.py new file mode 100644 index 0000000000..b86e4e8ca9 --- /dev/null +++ b/InvenTree/stock/tasks.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +import logging + +from django.core.exceptions import AppRegistryNotReady + + +logger = logging.getLogger('inventree') + + +def delete_old_stock_items(): + """ + This function removes StockItem objects which have been marked for deletion. + + Bulk "delete" operations for database entries with foreign-key relationships + can be pretty expensive, and thus can "block" the UI for a period of time. + + Thus, instead of immediately deleting multiple StockItems, some UI actions + simply mark each StockItem as "scheduled for deletion". + + The background worker then manually deletes these at a later stage + """ + + try: + from stock.models import StockItem + except AppRegistryNotReady: + logger.info("Could not delete scheduled StockItems - AppRegistryNotReady") + return + + items = StockItem.objects.filter(scheduled_for_deletion=True) + + logger.info(f"Removing {items.count()} StockItem objects scheduled for deletion") + + items.delete()