mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-29 20:16:44 +00:00
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:
parent
4cd4a84bac
commit
a1f9260da6
@ -9,6 +9,59 @@ from InvenTree.fields import InvenTreeNotesField
|
|||||||
from InvenTree.helpers import remove_non_printable_characters, strip_html_tags
|
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():
|
class CleanMixin():
|
||||||
"""Model mixin class which cleans inputs using the Mozilla bleach tools."""
|
"""Model mixin class which cleans inputs using the Mozilla bleach tools."""
|
||||||
|
|
||||||
|
@ -28,6 +28,7 @@ from build.validators import generate_next_build_reference, validate_build_order
|
|||||||
import InvenTree.fields
|
import InvenTree.fields
|
||||||
import InvenTree.helpers
|
import InvenTree.helpers
|
||||||
import InvenTree.helpers_model
|
import InvenTree.helpers_model
|
||||||
|
import InvenTree.mixins
|
||||||
import InvenTree.models
|
import InvenTree.models
|
||||||
import InvenTree.ready
|
import InvenTree.ready
|
||||||
import InvenTree.tasks
|
import InvenTree.tasks
|
||||||
@ -44,7 +45,7 @@ import users.models
|
|||||||
logger = logging.getLogger('inventree')
|
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.
|
"""A Build object organises the creation of new StockItem objects from other existing StockItem objects.
|
||||||
|
|
||||||
Attributes:
|
Attributes:
|
||||||
@ -108,6 +109,12 @@ class Build(MPTTModel, InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.
|
|||||||
self.validate_reference_field(self.reference)
|
self.validate_reference_field(self.reference)
|
||||||
self.reference_int = self.rebuild_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:
|
try:
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
except InvalidMove:
|
except InvalidMove:
|
||||||
|
@ -471,6 +471,28 @@ class BuildTest(BuildTestBase):
|
|||||||
# Check that the "consumed_by" item count has increased
|
# Check that the "consumed_by" item count has increased
|
||||||
self.assertEqual(StockItem.objects.filter(consumed_by=self.build).count(), n + 8)
|
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):
|
def test_cancel(self):
|
||||||
"""Test cancellation of the build"""
|
"""Test cancellation of the build"""
|
||||||
# TODO
|
# TODO
|
||||||
|
@ -130,6 +130,9 @@ function editBuildOrder(pk) {
|
|||||||
|
|
||||||
var fields = buildFormFields();
|
var fields = buildFormFields();
|
||||||
|
|
||||||
|
// Cannot edit "part" field after creation
|
||||||
|
delete fields['part'];
|
||||||
|
|
||||||
constructForm(`{% url "api-build-list" %}${pk}/`, {
|
constructForm(`{% url "api-build-list" %}${pk}/`, {
|
||||||
fields: fields,
|
fields: fields,
|
||||||
reload: true,
|
reload: true,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user