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
This commit is contained in:
Oliver 2023-03-14 13:53:33 +11:00 committed by GitHub
parent 06f8a50956
commit 1ba51e51c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 84 additions and 34 deletions

View File

@ -259,6 +259,7 @@ class BomItemResource(InvenTreeResource):
'id',
'part',
'sub_part',
'validated',
]
level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)

View File

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

View File

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

View File

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

View File

@ -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,

View File

@ -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