From dd53748f9f0b96abce95887af0c6a01d207c412a Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 21 Dec 2021 08:49:53 +1100 Subject: [PATCH] Cleanup models --- .../0073_alter_stockitem_belongs_to.py | 19 ++++++++++++ .../migrations/0073_alter_stockitem_parent.py | 20 ------------ InvenTree/stock/models.py | 31 ++++++++++--------- .../stock/templates/stock/item_base.html | 8 ----- 4 files changed, 36 insertions(+), 42 deletions(-) create mode 100644 InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py delete mode 100644 InvenTree/stock/migrations/0073_alter_stockitem_parent.py diff --git a/InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py b/InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py new file mode 100644 index 0000000000..93000ef0f8 --- /dev/null +++ b/InvenTree/stock/migrations/0073_alter_stockitem_belongs_to.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.10 on 2021-12-20 21:49 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('stock', '0072_remove_stockitem_scheduled_for_deletion'), + ] + + operations = [ + migrations.AlterField( + model_name='stockitem', + name='belongs_to', + field=models.ForeignKey(blank=True, help_text='Is this item installed in another item?', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='installed_parts', to='stock.stockitem', verbose_name='Installed In'), + ), + ] diff --git a/InvenTree/stock/migrations/0073_alter_stockitem_parent.py b/InvenTree/stock/migrations/0073_alter_stockitem_parent.py deleted file mode 100644 index dca8788964..0000000000 --- a/InvenTree/stock/migrations/0073_alter_stockitem_parent.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 3.2.10 on 2021-12-20 13:54 - -from django.db import migrations -import django.db.models.deletion -import mptt.fields - - -class Migration(migrations.Migration): - - dependencies = [ - ('stock', '0072_remove_stockitem_scheduled_for_deletion'), - ] - - operations = [ - migrations.AlterField( - model_name='stockitem', - name='parent', - field=mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='children', to='stock.stockitem', verbose_name='Parent Stock Item'), - ), - ] diff --git a/InvenTree/stock/models.py b/InvenTree/stock/models.py index 6a9f12bd77..48db47ba0d 100644 --- a/InvenTree/stock/models.py +++ b/InvenTree/stock/models.py @@ -455,10 +455,11 @@ class StockItem(MPTTModel): uid = models.CharField(blank=True, max_length=128, help_text=("Unique identifier field")) + # Note: When a StockItem is deleted, a pre_delete signal handles the parent/child relationship parent = TreeForeignKey( 'self', verbose_name=_('Parent Stock Item'), - on_delete=models.SET_NULL, + on_delete=models.DO_NOTHING, blank=True, null=True, related_name='children' ) @@ -477,6 +478,7 @@ class StockItem(MPTTModel): help_text=_('Select a matching supplier part for this stock item') ) + # Note: When a StockLocation is deleted, stock items are updated via a signal location = TreeForeignKey( StockLocation, on_delete=models.DO_NOTHING, verbose_name=_('Stock Location'), @@ -492,10 +494,11 @@ class StockItem(MPTTModel): help_text=_('Packaging this stock item is stored in') ) + # When deleting a stock item with installed items, those installed items are also installed belongs_to = models.ForeignKey( 'self', verbose_name=_('Installed In'), - on_delete=models.DO_NOTHING, + on_delete=models.CASCADE, related_name='installed_parts', blank=True, null=True, help_text=_('Is this item installed in another item?') ) @@ -800,14 +803,14 @@ class StockItem(MPTTModel): def can_delete(self): """ Can this stock item be deleted? It can NOT be deleted under the following circumstances: - - Has child StockItems + - Has installed stock items - Has a serial number and is tracked - Is installed inside another StockItem - It has been assigned to a SalesOrder - It has been assigned to a BuildOrder """ - if self.child_count > 0: + if self.installed_item_count() > 0: return False if self.part.trackable and self.serial is not None: @@ -853,20 +856,13 @@ class StockItem(MPTTModel): return installed - def installedItemCount(self): + def installed_item_count(self): """ Return the number of stock items installed inside this one. """ return self.installed_parts.count() - def hasInstalledItems(self): - """ - Returns true if this stock item has other stock items installed in it. - """ - - return self.installedItemCount() > 0 - @transaction.atomic def installStockItem(self, other_item, quantity, user, notes): """ @@ -1225,6 +1221,8 @@ class StockItem(MPTTModel): location = kwargs.get('location', None) notes = kwargs.get('notes', None) + parent_id = self.parent.pk if self.parent else None + for other in other_items: # If the stock item cannot be merged, return if not self.can_merge(other, raise_error=raise_error, **kwargs): @@ -1246,7 +1244,11 @@ class StockItem(MPTTModel): allocation.stock_item = self() allocation.save() - # Delete the other stock item + # Prevent atomicity issues when we are merging our own "parent" part in + if parent_id and parent_id == other.pk: + self.parent = None + self.save() + other.delete() self.add_tracking_entry( @@ -1757,7 +1759,8 @@ class StockItem(MPTTModel): @receiver(pre_delete, sender=StockItem, dispatch_uid='stock_item_pre_delete_log') def before_delete_stock_item(sender, instance, using, **kwargs): - """ Receives pre_delete signal from StockItem object. + """ + Receives pre_delete signal from StockItem object. Before a StockItem is deleted, ensure that each child object is updated, to point to the new parent item. diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 65f5f21000..9113425520 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -274,14 +274,6 @@
{% trans "This stock item is serialized - it has a unique serial number and the quantity cannot be adjusted." %}
- {% elif item.child_count > 0 %} -
- {% trans "This stock item cannot be deleted as it has child items" %} -
- {% elif item.delete_on_deplete and item.can_delete %} -
- {% trans "This stock item will be automatically deleted when all stock is depleted." %} -
{% endif %}