mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Add 'bom_item' field to BuildItem model
- Required to link the build to the output in case of variant stock
This commit is contained in:
parent
9f407df15a
commit
7578cab9a8
20
InvenTree/build/migrations/0028_builditem_bom_item.py
Normal file
20
InvenTree/build/migrations/0028_builditem_bom_item.py
Normal file
@ -0,0 +1,20 @@
|
||||
# Generated by Django 3.2 on 2021-06-01 05:23
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('part', '0066_bomitem_allow_variants'),
|
||||
('build', '0027_auto_20210404_2016'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='builditem',
|
||||
name='bom_item',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='allocate_build_items', to='part.bomitem'),
|
||||
),
|
||||
]
|
@ -1036,7 +1036,19 @@ class Build(MPTTModel):
|
||||
StockModels.StockItem.IN_STOCK_FILTER
|
||||
)
|
||||
|
||||
items = items.filter(part=part)
|
||||
# Check if variants are allowed for this part
|
||||
try:
|
||||
bom_item = PartModels.BomItem.objects.get(part=self.part, sub_part=part)
|
||||
allow_part_variants = bom_item.allow_variants
|
||||
except PartModels.BomItem.DoesNotExist:
|
||||
allow_part_variants = False
|
||||
|
||||
if allow_part_variants:
|
||||
parts = part.get_descendants(include_self=True)
|
||||
items = items.filter(part__pk__in=[p.pk for p in parts])
|
||||
|
||||
else:
|
||||
items = items.filter(part=part)
|
||||
|
||||
# Exclude any items which have already been allocated
|
||||
allocated = BuildItem.objects.filter(
|
||||
@ -1160,10 +1172,6 @@ class BuildItem(models.Model):
|
||||
if self.stock_item.part and self.stock_item.part.trackable and not self.install_into:
|
||||
raise ValidationError(_('Build item must specify a build output, as master part is marked as trackable'))
|
||||
|
||||
# Allocated part must be in the BOM for the master part
|
||||
if self.stock_item.part not in self.build.part.getRequiredParts(recursive=False):
|
||||
errors['stock_item'] = [_("Selected stock item not found in BOM for part '{p}'").format(p=self.build.part.full_name)]
|
||||
|
||||
# Allocated quantity cannot exceed available stock quantity
|
||||
if self.quantity > self.stock_item.quantity:
|
||||
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})").format(
|
||||
@ -1189,6 +1197,61 @@ class BuildItem(models.Model):
|
||||
if len(errors) > 0:
|
||||
raise ValidationError(errors)
|
||||
|
||||
"""
|
||||
Attempt to find the "BomItem" which links this BuildItem to the build.
|
||||
|
||||
- If a BomItem is already set, and it is valid, then we are ok!
|
||||
"""
|
||||
|
||||
bom_item_valid = False
|
||||
|
||||
if self.bom_item:
|
||||
"""
|
||||
A BomItem object has already been assigned. This is valid if:
|
||||
|
||||
a) It points to the same "part" as the referened build
|
||||
b) Either:
|
||||
i) The sub_part points to the same part as the referenced StockItem
|
||||
ii) The BomItem allows variants and the part referenced by the StockItem
|
||||
is a variant of the sub_part referenced by the BomItem
|
||||
"""
|
||||
|
||||
if self.build and self.build.part == self.bom_item.part:
|
||||
|
||||
# Check that the sub_part points to the stock_item (either directly or via a variant)
|
||||
if self.bom_item.sub_part == self.stock_item.part:
|
||||
bom_item_valid = True
|
||||
|
||||
elif self.bom_item.allow_variants and self.stock_item.part in self.bom_item.sub_part.get_descendants(include_self=False):
|
||||
bom_item_valid = True
|
||||
|
||||
# If the existing BomItem is *not* valid, try to find a match
|
||||
if not bom_item_valid:
|
||||
|
||||
if self.build and self.stock_item:
|
||||
ancestors = self.stock_item.part.get_ancestors(include_self=True, ascending=True)
|
||||
|
||||
for idx, ancestor in enumerate(ancestors):
|
||||
|
||||
try:
|
||||
bom_item = PartModels.BomItem.objects.get(part=self.build.part, sub_part=ancestor)
|
||||
except PartModels.BomItem.DoesNotExist:
|
||||
continue
|
||||
|
||||
# A matching BOM item has been found!
|
||||
if idx == 0 or bom_item.allow_variants:
|
||||
bom_item_valid = True
|
||||
self.bom_item = bom_item
|
||||
break
|
||||
|
||||
# BomItem did not exist or could not be validated.
|
||||
# Search for a new one
|
||||
if not bom_item_valid:
|
||||
|
||||
raise ValidationError({
|
||||
'stock_item': _("Selected stock item not found in BOM for part '{p}'").format(p=self.build.part.full_name)
|
||||
})
|
||||
|
||||
@transaction.atomic
|
||||
def complete_allocation(self, user):
|
||||
"""
|
||||
@ -1225,6 +1288,15 @@ class BuildItem(models.Model):
|
||||
help_text=_('Build to allocate parts')
|
||||
)
|
||||
|
||||
# Internal model which links part <-> sub_part
|
||||
# We need to track this separately, to allow for "variant' stock
|
||||
bom_item = models.ForeignKey(
|
||||
PartModels.BomItem,
|
||||
on_delete=models.CASCADE,
|
||||
related_name='allocate_build_items',
|
||||
blank=True, null=True,
|
||||
)
|
||||
|
||||
stock_item = models.ForeignKey(
|
||||
'stock.StockItem',
|
||||
on_delete=models.CASCADE,
|
||||
|
Loading…
Reference in New Issue
Block a user