[Build] Add extra validation options (#7560)

* Add new setting: BUILDORDER_REQUIRE_VALID_BOM

- Prevent build orders from being created if the assembly BOM has not been validated

* Add validation check when creating a build order

* Add unit tests
This commit is contained in:
Oliver 2024-07-05 12:46:34 +10:00 committed by GitHub
parent 720651602b
commit 13dbfd0b14
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 94 additions and 0 deletions

View File

@ -120,8 +120,23 @@ class Build(
self.validate_reference_field(self.reference)
self.reference_int = self.rebuild_reference_field(self.reference)
if get_global_setting('BUILDORDER_REQUIRE_VALID_BOM'):
# Check that the BOM is valid
if not self.part.is_bom_valid():
raise ValidationError({
'part': _('Assembly BOM has not been validated')
})
if get_global_setting('BUILDORDER_REQUIRE_ACTIVE_PART'):
# Check that the part is active
if not self.part.active:
raise ValidationError({
'part': _('Part is not active')
})
# On first save (i.e. creation), run some extra checks
if self.pk is None:
# Set the destination location (if not specified)
if not self.destination:
self.destination = self.part.get_default_location()

View File

@ -1,6 +1,7 @@
"""Basic unit tests for the BuildOrder app"""
from django.conf import settings
from django.core.exceptions import ValidationError
from django.test import tag
from django.urls import reverse
@ -9,8 +10,10 @@ from datetime import datetime, timedelta
from InvenTree.unit_test import InvenTreeTestCase
from .models import Build
from part.models import Part, BomItem
from stock.models import StockItem
from common.settings import get_global_setting, set_global_setting
from build.status_codes import BuildStatus
@ -88,6 +91,64 @@ class BuildTestSimple(InvenTreeTestCase):
self.assertEqual(build.status, BuildStatus.CANCELLED)
def test_build_create(self):
"""Test creation of build orders via API."""
n = Build.objects.count()
# Find an assembly part
assembly = Part.objects.filter(assembly=True).first()
self.assertEqual(assembly.get_bom_items().count(), 0)
# Let's create some BOM items for this assembly
for component in Part.objects.filter(assembly=False, component=True)[:15]:
try:
BomItem.objects.create(
part=assembly,
sub_part=component,
reference='xxx',
quantity=5
)
except ValidationError:
pass
# The assembly has a BOM, and is now *invalid*
self.assertGreater(assembly.get_bom_items().count(), 0)
self.assertFalse(assembly.is_bom_valid())
# Create a build for an assembly with an *invalid* BOM
set_global_setting('BUILDORDER_REQUIRE_VALID_BOM', False)
set_global_setting('BUILDORDER_REQUIRE_ACTIVE_PART', True)
bo = Build.objects.create(part=assembly, quantity=10, reference='BO-9990')
bo.save()
# Now, require a *valid* BOM
set_global_setting('BUILDORDER_REQUIRE_VALID_BOM', True)
with self.assertRaises(ValidationError):
bo = Build.objects.create(part=assembly, quantity=10, reference='BO-9991')
# Now, validate the BOM, and try again
assembly.validate_bom(None)
self.assertTrue(assembly.is_bom_valid())
bo = Build.objects.create(part=assembly, quantity=10, reference='BO-9992')
# Now, try and create a build for an inactive assembly
assembly.active = False
assembly.save()
with self.assertRaises(ValidationError):
bo = Build.objects.create(part=assembly, quantity=10, reference='BO-9993')
set_global_setting('BUILDORDER_REQUIRE_ACTIVE_PART', False)
Build.objects.create(part=assembly, quantity=10, reference='BO-9994')
# Check that expected quantity of new builds is created
self.assertEqual(Build.objects.count(), n + 3)
class TestBuildViews(InvenTreeTestCase):
"""Tests for Build app views."""

View File

@ -1786,6 +1786,20 @@ class InvenTreeSetting(BaseInvenTreeSetting):
'default': False,
'validator': bool,
},
'BUILDORDER_REQUIRE_ACTIVE_PART': {
'name': _('Require Active Part'),
'description': _('Prevent build order creation for inactive parts'),
'default': False,
'validator': bool,
},
'BUILDORDER_REQUIRE_VALID_BOM': {
'name': _('Require Valid BOM'),
'description': _(
'Prevent build order creation unless BOM has been validated'
),
'default': False,
'validator': bool,
},
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS': {
'name': _('Block Until Tests Pass'),
'description': _(

View File

@ -14,6 +14,8 @@
<tbody>
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REFERENCE_PATTERN" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_RESPONSIBLE" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_ACTIVE_PART" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_VALID_BOM" %}
{% include "InvenTree/settings/setting.html" with key="PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS" %}
</tbody>
</table>

View File

@ -245,6 +245,8 @@ export default function SystemSettings() {
keys={[
'BUILDORDER_REFERENCE_PATTERN',
'BUILDORDER_REQUIRE_RESPONSIBLE',
'BUILDORDER_REQUIRE_ACTIVE_PART',
'BUILDORDER_REQUIRE_VALID_BOM',
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS'
]}
/>