Remove the "scheduled_for_deletion" field from the StockItem model

Reverts back to the original behaviour - stock items are just deleted
This commit is contained in:
Oliver 2021-12-05 18:14:14 +11:00
parent a821717103
commit 93a240d9c3
9 changed files with 35 additions and 107 deletions

View File

@ -69,13 +69,6 @@ class InvenTreeConfig(AppConfig):
schedule_type=Schedule.DAILY, 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 # Delete old notification records
InvenTree.tasks.schedule_task( InvenTree.tasks.schedule_task(
'common.tasks.delete_old_notifications', 'common.tasks.delete_old_notifications',

View File

@ -86,6 +86,9 @@ if not os.path.exists(cfg_filename):
with open(cfg_filename, 'r') as cfg: with open(cfg_filename, 'r') as cfg:
CONFIG = yaml.safe_load(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 # Default action is to run the system in Debug mode
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = _is_true(get_setting( DEBUG = _is_true(get_setting(
@ -206,6 +209,16 @@ if MEDIA_ROOT is None:
print("ERROR: INVENTREE_MEDIA_ROOT directory is not defined") print("ERROR: INVENTREE_MEDIA_ROOT directory is not defined")
sys.exit(1) 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) # List of allowed hosts (default = allow all)
ALLOWED_HOSTS = CONFIG.get('allowed_hosts', ['*']) ALLOWED_HOSTS = CONFIG.get('allowed_hosts', ['*'])

View File

@ -10,7 +10,6 @@ from InvenTree import status_codes as status
from build.models import Build, BuildItem, get_next_build_number from build.models import Build, BuildItem, get_next_build_number
from part.models import Part, BomItem from part.models import Part, BomItem
from stock.models import StockItem from stock.models import StockItem
from stock.tasks import delete_old_stock_items
class BuildTest(TestCase): class BuildTest(TestCase):
@ -354,11 +353,6 @@ class BuildTest(TestCase):
# the original BuildItem objects should have been deleted! # the original BuildItem objects should have been deleted!
self.assertEqual(BuildItem.objects.count(), 0) 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! # New stock items should have been created!
self.assertEqual(StockItem.objects.count(), 7) self.assertEqual(StockItem.objects.count(), 7)

View File

@ -86,17 +86,6 @@ class StockDetail(generics.RetrieveUpdateDestroyAPIView):
return self.serializer_class(*args, **kwargs) 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): class StockItemSerialize(generics.CreateAPIView):
""" """
@ -623,9 +612,6 @@ class StockList(generics.ListCreateAPIView):
queryset = StockSerializers.StockItemSerializer.annotate_queryset(queryset) 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 return queryset
def filter_queryset(self, queryset): def filter_queryset(self, queryset):

View File

@ -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',
),
]

View File

@ -212,18 +212,12 @@ class StockItem(MPTTModel):
belongs_to=None, belongs_to=None,
customer=None, customer=None,
is_building=False, is_building=False,
status__in=StockStatus.AVAILABLE_CODES, status__in=StockStatus.AVAILABLE_CODES
scheduled_for_deletion=False,
) )
# A query filter which can be used to filter StockItem objects which have expired # 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()) 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): def update_serial_number(self):
""" """
Update the 'serial_int' field, to be an integer representation of the serial number. 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'), help_text=_('Select Owner'),
related_name='stock_items') 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): def is_stale(self):
""" """
Returns True if this Stock item is "stale". Returns True if this Stock item is "stale".
@ -1327,7 +1315,7 @@ class StockItem(MPTTModel):
self.quantity = quantity self.quantity = quantity
if quantity == 0 and self.delete_on_deplete and self.can_delete(): if quantity == 0 and self.delete_on_deplete and self.can_delete():
self.mark_for_deletion() self.delete()
return False return False
else: else:

View File

@ -1,35 +1,2 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from __future__ import unicode_literals 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()

View File

@ -18,7 +18,6 @@ from InvenTree.api_tester import InvenTreeAPITestCase
from common.models import InvenTreeSetting from common.models import InvenTreeSetting
from .models import StockItem, StockLocation from .models import StockItem, StockLocation
from .tasks import delete_old_stock_items
class StockAPITestCase(InvenTreeAPITestCase): class StockAPITestCase(InvenTreeAPITestCase):
@ -593,11 +592,7 @@ class StockItemDeletionTest(StockAPITestCase):
def test_delete(self): def test_delete(self):
# Check there are no stock items scheduled for deletion n = StockItem.objects.count()
self.assertEqual(
StockItem.objects.filter(scheduled_for_deletion=True).count(),
0
)
# Create and then delete a bunch of stock items # Create and then delete a bunch of stock items
for idx in range(10): for idx in range(10):
@ -615,9 +610,7 @@ class StockItemDeletionTest(StockAPITestCase):
pk = response.data['pk'] pk = response.data['pk']
item = StockItem.objects.get(pk=pk) self.assertEqual(StockItem.objects.count(), n + 1)
self.assertFalse(item.scheduled_for_deletion)
# Request deletion via the API # Request deletion via the API
self.delete( self.delete(
@ -625,19 +618,7 @@ class StockItemDeletionTest(StockAPITestCase):
expected_code=204 expected_code=204
) )
# There should be 100x StockItem objects marked for deletion self.assertEqual(StockItem.objects.count(), n)
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): class StockTestResultTest(StockAPITestCase):

View File

@ -332,8 +332,6 @@ class StockTest(TestCase):
w1 = StockItem.objects.get(pk=100) w1 = StockItem.objects.get(pk=100)
w2 = StockItem.objects.get(pk=101) w2 = StockItem.objects.get(pk=101)
self.assertFalse(w2.scheduled_for_deletion)
# Take 25 units from w1 (there are only 10 in stock) # Take 25 units from w1 (there are only 10 in stock)
w1.take_stock(30, None, notes='Took 30') w1.take_stock(30, None, notes='Took 30')
@ -344,15 +342,6 @@ class StockTest(TestCase):
# Take 25 units from w2 (will be deleted) # Take 25 units from w2 (will be deleted)
w2.take_stock(30, None, notes='Took 30') 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 # This StockItem should now have been deleted
with self.assertRaises(StockItem.DoesNotExist): with self.assertRaises(StockItem.DoesNotExist):
w2 = StockItem.objects.get(pk=101) w2 = StockItem.objects.get(pk=101)