mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-30 04:26:44 +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,6 +1036,18 @@ class Build(MPTTModel):
|
|||||||
StockModels.StockItem.IN_STOCK_FILTER
|
StockModels.StockItem.IN_STOCK_FILTER
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# 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)
|
items = items.filter(part=part)
|
||||||
|
|
||||||
# Exclude any items which have already been allocated
|
# Exclude any items which have already been allocated
|
||||||
@ -1160,10 +1172,6 @@ class BuildItem(models.Model):
|
|||||||
if self.stock_item.part and self.stock_item.part.trackable and not self.install_into:
|
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'))
|
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
|
# Allocated quantity cannot exceed available stock quantity
|
||||||
if self.quantity > self.stock_item.quantity:
|
if self.quantity > self.stock_item.quantity:
|
||||||
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})").format(
|
errors['quantity'] = [_("Allocated quantity ({n}) must not exceed available quantity ({q})").format(
|
||||||
@ -1189,6 +1197,61 @@ class BuildItem(models.Model):
|
|||||||
if len(errors) > 0:
|
if len(errors) > 0:
|
||||||
raise ValidationError(errors)
|
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
|
@transaction.atomic
|
||||||
def complete_allocation(self, user):
|
def complete_allocation(self, user):
|
||||||
"""
|
"""
|
||||||
@ -1225,6 +1288,15 @@ class BuildItem(models.Model):
|
|||||||
help_text=_('Build to allocate parts')
|
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_item = models.ForeignKey(
|
||||||
'stock.StockItem',
|
'stock.StockItem',
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user