mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
[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:
parent
720651602b
commit
13dbfd0b14
@ -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()
|
||||
|
@ -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."""
|
||||
|
@ -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': _(
|
||||
|
@ -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>
|
||||
|
@ -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'
|
||||
]}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user