Skip to content

Commit

Permalink
Update BomItem 'validated' field (#4486)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
SchrodingersGat authored Mar 14, 2023
1 parent 06f8a50 commit 1ba51e5
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 34 deletions.
1 change: 1 addition & 0 deletions InvenTree/part/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,7 @@ class Meta:
'id',
'part',
'sub_part',
'validated',
]

level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)
Expand Down
42 changes: 11 additions & 31 deletions InvenTree/part/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -1598,11 +1598,17 @@ def get_serializer_context(self):
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')
Expand All @@ -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):
Expand Down Expand Up @@ -1814,9 +1797,6 @@ def filter_queryset(self, queryset):
InvenTreeOrderingFilter,
]

filterset_fields = [
]

search_fields = [
'reference',
'sub_part__name',
Expand Down
18 changes: 18 additions & 0 deletions InvenTree/part/migrations/0101_bomitem_validated.py
Original file line number Diff line number Diff line change
@@ -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'),
),
]
40 changes: 40 additions & 0 deletions InvenTree/part/migrations/0102_auto_20230314_0112.py
Original file line number Diff line number Diff line change
@@ -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
)
]
15 changes: 14 additions & 1 deletion InvenTree/part/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -3543,6 +3543,10 @@ def get_stock_filter(self):
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
Expand Down Expand Up @@ -3588,7 +3592,16 @@ def save(self, *args, **kwargs):
# 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,
Expand Down
2 changes: 0 additions & 2 deletions InvenTree/part/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -1099,8 +1099,6 @@ def validate_quantity(self, quantity):

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
Expand Down

0 comments on commit 1ba51e5

Please sign in to comment.