Build part change fix (#5808)

* Add DiffMixin class

- Allows us to compute "diffs" against items stored in the database, when they are being updated

* Prevent build order part from being changed after creation

* Remove "part" field from buildorder edit form

* Add unit test
This commit is contained in:
Oliver 2023-10-29 23:13:38 +11:00 committed by GitHub
parent 4cd4a84bac
commit a1f9260da6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 1 deletions

View File

@ -9,6 +9,59 @@ from InvenTree.fields import InvenTreeNotesField
from InvenTree.helpers import remove_non_printable_characters, strip_html_tags
class DiffMixin:
"""Mixin which can be used to determine which fields have changed, compared to the instance saved to the database."""
def get_db_instance(self):
"""Return the instance of the object saved in the database.
Returns:
object: Instance of the object saved in the database
"""
if self.pk:
try:
return self.__class__.objects.get(pk=self.pk)
except self.__class__.DoesNotExist:
pass
return None
def get_field_deltas(self):
"""Return a dict of field deltas.
Compares the current instance with the instance saved in the database,
and returns a dict of fields which have changed.
Returns:
dict: Dict of field deltas
"""
db_instance = self.get_db_instance()
if db_instance is None:
return {}
deltas = {}
for field in self._meta.fields:
if field.name == 'id':
continue
if getattr(self, field.name) != getattr(db_instance, field.name):
deltas[field.name] = {
'old': getattr(db_instance, field.name),
'new': getattr(self, field.name),
}
return deltas
def has_field_changed(self, field_name):
"""Determine if a particular field has changed."""
return field_name in self.get_field_deltas()
class CleanMixin():
"""Model mixin class which cleans inputs using the Mozilla bleach tools."""

View File

@ -28,6 +28,7 @@ from build.validators import generate_next_build_reference, validate_build_order
import InvenTree.fields
import InvenTree.helpers
import InvenTree.helpers_model
import InvenTree.mixins
import InvenTree.models
import InvenTree.ready
import InvenTree.tasks
@ -44,7 +45,7 @@ import users.models
logger = logging.getLogger('inventree')
class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNotesMixin, InvenTree.models.MetadataMixin, InvenTree.models.ReferenceIndexingMixin):
class Build(MPTTModel, InvenTree.mixins.DiffMixin, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNotesMixin, InvenTree.models.MetadataMixin, InvenTree.models.ReferenceIndexingMixin):
"""A Build object organises the creation of new StockItem objects from other existing StockItem objects.
Attributes:
@ -108,6 +109,12 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
self.validate_reference_field(self.reference)
self.reference_int = self.rebuild_reference_field(self.reference)
# Prevent changing target part after creation
if self.has_field_changed('part'):
raise ValidationError({
'part': _('Build order part cannot be changed')
})
try:
super().save(*args, **kwargs)
except InvalidMove:

View File

@ -471,6 +471,28 @@ class BuildTest(BuildTestBase):
# Check that the "consumed_by" item count has increased
self.assertEqual(StockItem.objects.filter(consumed_by=self.build).count(), n + 8)
def test_change_part(self):
"""Try to change target part after creating a build"""
bo = Build.objects.create(
reference='BO-9999',
title='Some new build',
part=self.assembly,
quantity=5,
issued_by=get_user_model().objects.get(pk=1),
)
assembly_2 = Part.objects.create(
name="Another assembly",
description="A different assembly",
assembly=True,
)
# Should not be able to change the part after the Build is saved
with self.assertRaises(ValidationError):
bo.part = assembly_2
bo.save()
def test_cancel(self):
"""Test cancellation of the build"""
# TODO

View File

@ -130,6 +130,9 @@ function editBuildOrder(pk) {
var fields = buildFormFields();
// Cannot edit "part" field after creation
delete fields['part'];
constructForm(`{% url "api-build-list" %}${pk}/`, {
fields: fields,
reload: true,