mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +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:
		@@ -259,6 +259,7 @@ class BomItemResource(InvenTreeResource):
 | 
			
		||||
            'id',
 | 
			
		||||
            'part',
 | 
			
		||||
            'sub_part',
 | 
			
		||||
            'validated',
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)
 | 
			
		||||
 
 | 
			
		||||
@@ -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',
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										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):
 | 
			
		||||
        """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,
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user