diff --git a/InvenTree/InvenTree/apps.py b/InvenTree/InvenTree/apps.py index 5f347dd1e5..12285d494b 100644 --- a/InvenTree/InvenTree/apps.py +++ b/InvenTree/InvenTree/apps.py @@ -69,13 +69,6 @@ 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, - ) - # Delete old notification records InvenTree.tasks.schedule_task( 'common.tasks.delete_old_notifications', diff --git a/InvenTree/InvenTree/settings.py b/InvenTree/InvenTree/settings.py index bd1813312b..70447371f0 100644 --- a/InvenTree/InvenTree/settings.py +++ b/InvenTree/InvenTree/settings.py @@ -86,6 +86,9 @@ if not os.path.exists(cfg_filename): with open(cfg_filename, 'r') as cfg: CONFIG = yaml.safe_load(cfg) +# We will place any config files in the same directory as the config file +config_dir = os.path.dirname(cfg_filename) + # Default action is to run the system in Debug mode # SECURITY WARNING: don't run with debug turned on in production! DEBUG = _is_true(get_setting( @@ -206,6 +209,16 @@ if MEDIA_ROOT is None: print("ERROR: INVENTREE_MEDIA_ROOT directory is not defined") sys.exit(1) +# Options for django-maintenance-mode : https://pypi.org/project/django-maintenance-mode/ +MAINTENANCE_MODE_STATE_FILE_PATH = os.path.join( + config_dir, + 'maintenance_mode_state.txt', +) + +MAINTENANCE_MODE_IGNORE_ADMIN_SITE = True + +MAINTENANCE_MODE_IGNORE_SUPERUSER = True + # List of allowed hosts (default = allow all) ALLOWED_HOSTS = CONFIG.get('allowed_hosts', ['*']) diff --git a/InvenTree/build/test_build.py b/InvenTree/build/test_build.py index f8c381f224..3ecb630c87 100644 --- a/InvenTree/build/test_build.py +++ b/InvenTree/build/test_build.py @@ -10,7 +10,6 @@ from InvenTree import status_codes as status from build.models import Build, BuildItem, get_next_build_number from part.models import Part, BomItem from stock.models import StockItem -from stock.tasks import delete_old_stock_items class BuildTest(TestCase): @@ -354,11 +353,6 @@ class BuildTest(TestCase): # the original BuildItem objects should have been deleted! self.assertEqual(BuildItem.objects.count(), 0) - self.assertEqual(StockItem.objects.count(), 8) - - # Clean up old stock items - delete_old_stock_items() - # New stock items should have been created! self.assertEqual(StockItem.objects.count(), 7) diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 760a18c72c..8a2d2fa051 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -86,17 +86,6 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView): return self.serializer_class(*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 StockItemSerialize(generics.CreateAPIView): """ @@ -623,9 +612,6 @@ class StockList(generics.ListCreateAPIView): queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset) - # Do not expose StockItem objects which are scheduled for deletion - queryset = queryset.filter(scheduled_for_deletion=False) - return queryset def filter_queryset(self, queryset): diff --git a/InvenTree/stock/migrations/0072_remove_stockitem_scheduled_for_deletion.py b/InvenTree/stock/migrations/0072_remove_stockitem_scheduled_for_deletion.py new file mode 100644 index 0000000000..0db2141299 --- /dev/null +++ b/InvenTree/stock/migrations/0072_remove_stockitem_scheduled_for_deletion.py @@ -0,0 +1,17 @@ +# Generated by Django 3.2.5 on 2021-12-05 06:49 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0071_auto_20211205_1733'), + ] + + operations = [ + migrations.RemoveField( + model_name='stockitem', + name='scheduled_for_deletion', + ), + ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 0aa63687c9..7f385e7136 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -212,18 +212,12 @@ class StockItem(MPTTModel): belongs_to=None, customer=None, is_building=False, - status__in=StockStatus.AVAILABLE_CODES, - scheduled_for_deletion=False, + status__in=StockStatus.AVAILABLE_CODES ) # 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 update_serial_number(self): """ Update the 'serial_int' field, to be an integer representation of the serial number. @@ -615,12 +609,6 @@ class StockItem(MPTTModel): help_text=_('Select Owner'), related_name='stock_items') - scheduled_for_deletion = models.BooleanField( - default=False, - verbose_name=_('Scheduled for deletion'), - help_text=_('This StockItem will be deleted by the background worker'), - ) - def is_stale(self): """ Returns True if this Stock item is "stale". @@ -1327,7 +1315,7 @@ class StockItem(MPTTModel): self.quantity = quantity if quantity == 0 and self.delete_on_deplete and self.can_delete(): - self.mark_for_deletion() + self.delete() return False else: diff --git a/InvenTree/stock/tasks.py b/InvenTree/stock/tasks.py index 9fbd875384..a2b5079b33 100644 --- a/InvenTree/stock/tasks.py +++ b/InvenTree/stock/tasks.py @@ -1,35 +1,2 @@ # -*- 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) - - if items.count() > 0: - logger.info(f"Removing {items.count()} StockItem objects scheduled for deletion") - items.delete() diff --git a/InvenTree/stock/test_api.py b/InvenTree/stock/test_api.py index 2c1b250e5f..522468a740 100644 --- a/InvenTree/stock/test_api.py +++ b/InvenTree/stock/test_api.py @@ -18,7 +18,6 @@ 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): @@ -593,11 +592,7 @@ class StockItemDeletionTest(StockAPITestCase): def test_delete(self): - # Check there are no stock items scheduled for deletion - self.assertEqual( - StockItem.objects.filter(scheduled_for_deletion=True).count(), - 0 - ) + n = StockItem.objects.count() # Create and then delete a bunch of stock items for idx in range(10): @@ -615,9 +610,7 @@ class StockItemDeletionTest(StockAPITestCase): pk = response.data['pk'] - item = StockItem.objects.get(pk=pk) - - self.assertFalse(item.scheduled_for_deletion) + self.assertEqual(StockItem.objects.count(), n + 1) # Request deletion via the API self.delete( @@ -625,19 +618,7 @@ class StockItemDeletionTest(StockAPITestCase): 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 - ) + self.assertEqual(StockItem.objects.count(), n) class StockTestResultTest(StockAPITestCase): diff --git a/InvenTree/stock/tests.py b/InvenTree/stock/tests.py index cb1bd406bb..40e6561926 100644 --- a/InvenTree/stock/tests.py +++ b/InvenTree/stock/tests.py @@ -332,8 +332,6 @@ class StockTest(TestCase): w1 = StockItem.objects.get(pk=100) w2 = StockItem.objects.get(pk=101) - self.assertFalse(w2.scheduled_for_deletion) - # Take 25 units from w1 (there are only 10 in stock) w1.take_stock(30, None, notes='Took 30') @@ -344,15 +342,6 @@ class StockTest(TestCase): # Take 25 units from w2 (will be deleted) w2.take_stock(30, None, notes='Took 30') - # w2 should now be marked for future deletion - w2 = StockItem.objects.get(pk=101) - self.assertTrue(w2.scheduled_for_deletion) - - from stock.tasks import delete_old_stock_items - - # Now run the "background task" to delete these stock items - delete_old_stock_items() - # This StockItem should now have been deleted with self.assertRaises(StockItem.DoesNotExist): w2 = StockItem.objects.get(pk=101)