mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-02 13:28:49 +00:00
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:
parent
06f8a50956
commit
1ba51e51c3
@ -259,6 +259,7 @@ class BomItemResource(InvenTreeResource):
|
|||||||
'id',
|
'id',
|
||||||
'part',
|
'part',
|
||||||
'sub_part',
|
'sub_part',
|
||||||
|
'validated',
|
||||||
]
|
]
|
||||||
|
|
||||||
level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)
|
level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)
|
||||||
|
@ -1598,11 +1598,17 @@ class PartStocktakeReportGenerate(CreateAPI):
|
|||||||
class BomFilter(rest_filters.FilterSet):
|
class BomFilter(rest_filters.FilterSet):
|
||||||
"""Custom filters for the BOM list."""
|
"""Custom filters for the BOM list."""
|
||||||
|
|
||||||
# Boolean filters for BOM item
|
class Meta:
|
||||||
optional = rest_filters.BooleanFilter(label='BOM item is optional')
|
"""Metaclass options"""
|
||||||
consumable = rest_filters.BooleanFilter(label='BOM item is consumable')
|
|
||||||
inherited = rest_filters.BooleanFilter(label='BOM item gets inherited')
|
model = BomItem
|
||||||
allow_variants = rest_filters.BooleanFilter(label='Variants are allowed')
|
fields = [
|
||||||
|
'optional',
|
||||||
|
'consumable',
|
||||||
|
'inherited',
|
||||||
|
'allow_variants',
|
||||||
|
'validated',
|
||||||
|
]
|
||||||
|
|
||||||
# Filters for linked 'part'
|
# Filters for linked 'part'
|
||||||
part_active = rest_filters.BooleanFilter(label='Master part is active', field_name='part__active')
|
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_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')
|
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")
|
available_stock = rest_filters.BooleanFilter(label="Has available stock", method="filter_available_stock")
|
||||||
|
|
||||||
def filter_available_stock(self, queryset, name, value):
|
def filter_available_stock(self, queryset, name, value):
|
||||||
@ -1814,9 +1797,6 @@ class BomList(ListCreateDestroyAPIView):
|
|||||||
InvenTreeOrderingFilter,
|
InvenTreeOrderingFilter,
|
||||||
]
|
]
|
||||||
|
|
||||||
filterset_fields = [
|
|
||||||
]
|
|
||||||
|
|
||||||
search_fields = [
|
search_fields = [
|
||||||
'reference',
|
'reference',
|
||||||
'sub_part__name',
|
'sub_part__name',
|
||||||
|
18
InvenTree/part/migrations/0101_bomitem_validated.py
Normal file
18
InvenTree/part/migrations/0101_bomitem_validated.py
Normal 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'),
|
||||||
|
),
|
||||||
|
]
|
40
InvenTree/part/migrations/0102_auto_20230314_0112.py
Normal file
40
InvenTree/part/migrations/0102_auto_20230314_0112.py
Normal 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
|
||||||
|
)
|
||||||
|
]
|
@ -3543,6 +3543,10 @@ class BomItem(DataImportMixin, models.Model):
|
|||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Enforce 'clean' operation when saving a BomItem instance"""
|
"""Enforce 'clean' operation when saving a BomItem instance"""
|
||||||
self.clean()
|
self.clean()
|
||||||
|
|
||||||
|
# Update the 'validated' field based on checksum calculation
|
||||||
|
self.validated = self.is_line_valid
|
||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
# A link to the parent part
|
# A link to the parent part
|
||||||
@ -3588,7 +3592,16 @@ class BomItem(DataImportMixin, models.Model):
|
|||||||
# Note attached to this BOM line item
|
# Note attached to this BOM line item
|
||||||
note = models.CharField(max_length=500, blank=True, verbose_name=_('Note'), help_text=_('BOM item notes'))
|
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(
|
inherited = models.BooleanField(
|
||||||
default=False,
|
default=False,
|
||||||
|
@ -1099,8 +1099,6 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
sub_part_detail = PartBriefSerializer(source='sub_part', many=False, read_only=True)
|
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)
|
on_order = serializers.FloatField(read_only=True)
|
||||||
|
|
||||||
# Cached pricing fields
|
# Cached pricing fields
|
||||||
|
Loading…
x
Reference in New Issue
Block a user