From 1ba51e51c329dc67fdf5840d9beb594b6a943af5 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 14 Mar 2023 13:53:33 +1100 Subject: [PATCH] Update BomItem 'validated' field (#4486) * Adds new "validated" field to the BomItem model - Previously this was dynamically calculated (very expensive) - Now cached and updated whenever a BomItem instance is saved - Will make the BOM API much more responsive - Cleanup BomItem list API code also * Adds data migration to update existing BomItem objects * Exclude 'validated' field from BomItemResource class --- InvenTree/part/admin.py | 1 + InvenTree/part/api.py | 42 +++++-------------- .../part/migrations/0101_bomitem_validated.py | 18 ++++++++ .../migrations/0102_auto_20230314_0112.py | 40 ++++++++++++++++++ InvenTree/part/models.py | 15 ++++++- InvenTree/part/serializers.py | 2 - 6 files changed, 84 insertions(+), 34 deletions(-) create mode 100644 InvenTree/part/migrations/0101_bomitem_validated.py create mode 100644 InvenTree/part/migrations/0102_auto_20230314_0112.py diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index 13d5875ccc..f4b77d7303 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -259,6 +259,7 @@ class BomItemResource(InvenTreeResource): 'id', 'part', 'sub_part', + 'validated', ] level = Field(attribute='level', column_name=_('BOM Level'), readonly=True) diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 5c875a6c0f..74f49fd0a7 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -1598,11 +1598,17 @@ class PartStocktakeReportGenerate(CreateAPI): class BomFilter(rest_filters.FilterSet): """Custom filters for the BOM list.""" - # Boolean filters for BOM item - optional = rest_filters.BooleanFilter(label='BOM item is optional') - consumable = rest_filters.BooleanFilter(label='BOM item is consumable') - inherited = rest_filters.BooleanFilter(label='BOM item gets inherited') - allow_variants = rest_filters.BooleanFilter(label='Variants are allowed') + class Meta: + """Metaclass options""" + + model = BomItem + fields = [ + 'optional', + 'consumable', + 'inherited', + 'allow_variants', + 'validated', + ] # Filters for linked 'part' part_active = rest_filters.BooleanFilter(label='Master part is active', field_name='part__active') @@ -1612,29 +1618,6 @@ class BomFilter(rest_filters.FilterSet): sub_part_trackable = rest_filters.BooleanFilter(label='Sub part is trackable', field_name='sub_part__trackable') sub_part_assembly = rest_filters.BooleanFilter(label='Sub part is an assembly', field_name='sub_part__assembly') - validated = rest_filters.BooleanFilter(label='BOM line has been validated', method='filter_validated') - - def filter_validated(self, queryset, name, value): - """Filter by which lines have actually been validated""" - pks = [] - - value = str2bool(value) - - # Shortcut for quicker filtering - BomItem with empty 'checksum' values are not validated - if value: - queryset = queryset.exclude(checksum=None).exclude(checksum='') - - for bom_item in queryset.all(): - if bom_item.is_line_valid: - pks.append(bom_item.pk) - - if value: - queryset = queryset.filter(pk__in=pks) - else: - queryset = queryset.exclude(pk__in=pks) - - return queryset - available_stock = rest_filters.BooleanFilter(label="Has available stock", method="filter_available_stock") def filter_available_stock(self, queryset, name, value): @@ -1814,9 +1797,6 @@ class BomList(ListCreateDestroyAPIView): InvenTreeOrderingFilter, ] - filterset_fields = [ - ] - search_fields = [ 'reference', 'sub_part__name', diff --git a/InvenTree/part/migrations/0101_bomitem_validated.py b/InvenTree/part/migrations/0101_bomitem_validated.py new file mode 100644 index 0000000000..977095e287 --- /dev/null +++ b/InvenTree/part/migrations/0101_bomitem_validated.py @@ -0,0 +1,18 @@ +# Generated by Django 3.2.18 on 2023-03-14 01:08 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0100_alter_bomitem_reference'), + ] + + operations = [ + migrations.AddField( + model_name='bomitem', + name='validated', + field=models.BooleanField(default=False, help_text='This BOM item has been validated', verbose_name='Validated'), + ), + ] diff --git a/InvenTree/part/migrations/0102_auto_20230314_0112.py b/InvenTree/part/migrations/0102_auto_20230314_0112.py new file mode 100644 index 0000000000..066f782306 --- /dev/null +++ b/InvenTree/part/migrations/0102_auto_20230314_0112.py @@ -0,0 +1,40 @@ +# Generated by Django 3.2.18 on 2023-03-14 01:12 + +import logging + +from django.db import migrations + + +logger = logging.getLogger('inventree') + + +def update_bom_item(apps, schema_editor): + """Update all existing BomItem instances""" + + from part.models import BomItem + + if n := BomItem.objects.count(): + + for item in BomItem.objects.all(): + item.save() + + logger.info(f"Updated 'validated' flag for {n} BomItem objects") + + +def meti_mob_etadpu(apps, schema_editor): + """Provided for reverse compatibility""" + pass + + +class Migration(migrations.Migration): + + dependencies = [ + ('part', '0101_bomitem_validated'), + ] + + operations = [ + migrations.RunPython( + update_bom_item, + reverse_code=meti_mob_etadpu + ) + ] diff --git a/InvenTree/part/models.py b/InvenTree/part/models.py index 4e3e56e046..4acf4daf9b 100644 --- a/InvenTree/part/models.py +++ b/InvenTree/part/models.py @@ -3543,6 +3543,10 @@ class BomItem(DataImportMixin, models.Model): def save(self, *args, **kwargs): """Enforce 'clean' operation when saving a BomItem instance""" self.clean() + + # Update the 'validated' field based on checksum calculation + self.validated = self.is_line_valid + super().save(*args, **kwargs) # A link to the parent part @@ -3588,7 +3592,16 @@ class BomItem(DataImportMixin, models.Model): # Note attached to this BOM line item note = models.CharField(max_length=500, blank=True, verbose_name=_('Note'), help_text=_('BOM item notes')) - checksum = models.CharField(max_length=128, blank=True, verbose_name=_('Checksum'), help_text=_('BOM line checksum')) + checksum = models.CharField( + max_length=128, blank=True, + verbose_name=_('Checksum'), help_text=_('BOM line checksum') + ) + + validated = models.BooleanField( + default=False, + verbose_name=_('Validated'), + help_text=_('This BOM item has been validated') + ) inherited = models.BooleanField( default=False, diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index c5600fb36c..3646df6fe5 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -1099,8 +1099,6 @@ class BomItemSerializer(InvenTreeModelSerializer): sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True) - validated = serializers.BooleanField(read_only=True, source='is_line_valid') - on_order = serializers.FloatField(read_only=True) # Cached pricing fields