2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-08-06 20:11:37 +00:00

Add custom state field validation

This commit is contained in:
Oliver Walters
2024-12-28 03:41:38 +00:00
parent 5be8d7ce70
commit ad79c79983
6 changed files with 59 additions and 5 deletions

View File

@@ -319,6 +319,7 @@ class Build(
verbose_name=_('Build Status'),
default=BuildStatus.PENDING.value,
choices=BuildStatus.items(),
status_class=BuildStatus,
validators=[MinValueValidator(0)],
help_text=_('Build status code'),
)

View File

@@ -90,6 +90,20 @@ class InvenTreeCustomStatusModelField(models.PositiveIntegerField):
Models using this model field must also include the InvenTreeCustomStatusSerializerMixin in all serializers that create or update the value.
"""
def __init__(self, *args, **kwargs):
"""Initialize the field."""
from generic.states.validators import CustomStatusCodeValidator
self.status_class = kwargs.pop('status_class', None)
validators = kwargs.pop('validators', None) or []
if self.status_class:
validators.append(CustomStatusCodeValidator(status_class=self.status_class))
kwargs['validators'] = validators
super().__init__(*args, **kwargs)
def deconstruct(self):
"""Deconstruct the field for migrations."""
name, path, args, kwargs = super().deconstruct()
@@ -109,14 +123,23 @@ class InvenTreeCustomStatusModelField(models.PositiveIntegerField):
"""Ensure that the value is not an empty string."""
if value == '':
value = None
return super().clean(value, model_instance)
def add_field(self, cls, name):
"""Adds custom_key_field to the model class to save additional status information."""
from generic.states.validators import CustomStatusCodeValidator
validators = []
if self.status_class:
validators.append(CustomStatusCodeValidator(status_class=self.status_class))
custom_key_field = ExtraInvenTreeCustomStatusModelField(
default=None,
verbose_name=_('Custom status key'),
help_text=_('Additional status information for this item'),
validators=validators,
blank=True,
null=True,
)
@@ -130,6 +153,10 @@ class ExtraInvenTreeCustomStatusModelField(models.PositiveIntegerField):
This is not intended to be used directly, if you want to support custom states in your model use InvenTreeCustomStatusModelField.
"""
def __init__(self, *args, **kwargs):
"""Initialize the field."""
super().__init__(*args, **kwargs)
class InvenTreeCustomStatusSerializerMixin:
"""Mixin to ensure custom status fields are set.

View File

@@ -117,7 +117,7 @@ class StatusCode(BaseEnum):
return []
@classmethod
def values(cls, key=None, custom=True):
def values(cls, key=None):
"""Return a dict representation containing all required information."""
elements = [itm for itm in cls if cls._is_element(itm.name)]
@@ -167,14 +167,14 @@ class StatusCode(BaseEnum):
return data
@classmethod
def keys(cls):
def keys(cls, custom=True):
"""All status code keys."""
return [x.value for x in cls.values()]
return [el[0] for el in cls.items(custom=custom)]
@classmethod
def labels(cls):
def labels(cls, custom=True):
"""All status code labels."""
return [x.label for x in cls.values()]
return [el[1] for el in cls.items(custom=custom)]
@classmethod
def names(cls):

View File

@@ -0,0 +1,21 @@
"""Validators for generic state management."""
from django.core.exceptions import ValidationError
from django.core.validators import BaseValidator
from django.utils.translation import gettext_lazy as _
class CustomStatusCodeValidator(BaseValidator):
"""Custom validator class for checking that a provided status code is valid."""
def __init__(self, *args, **kwargs):
"""Initialize the validator."""
self.status_class = kwargs.pop('status_class', None)
super().__init__(limit_value=None, **kwargs)
def __call__(self, value):
"""Check that the provided status code is valid."""
if status_class := self.status_class:
values = status_class.keys(custom=True)
if value not in values:
raise ValidationError(_('Invalid status code'))

View File

@@ -483,6 +483,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
status = InvenTreeCustomStatusModelField(
default=PurchaseOrderStatus.PENDING.value,
choices=PurchaseOrderStatus.items(),
status_class=PurchaseOrderStatus,
verbose_name=_('Status'),
help_text=_('Purchase order status'),
)
@@ -1029,6 +1030,7 @@ class SalesOrder(TotalPriceMixin, Order):
status = InvenTreeCustomStatusModelField(
default=SalesOrderStatus.PENDING.value,
choices=SalesOrderStatus.items(),
status_class=SalesOrderStatus,
verbose_name=_('Status'),
help_text=_('Sales order status'),
)
@@ -2231,6 +2233,7 @@ class ReturnOrder(TotalPriceMixin, Order):
status = InvenTreeCustomStatusModelField(
default=ReturnOrderStatus.PENDING.value,
choices=ReturnOrderStatus.items(),
status_class=ReturnOrderStatus,
verbose_name=_('Status'),
help_text=_('Return order status'),
)
@@ -2522,6 +2525,7 @@ class ReturnOrderLineItem(OrderLineItem):
outcome = InvenTreeCustomStatusModelField(
default=ReturnOrderLineStatus.PENDING.value,
choices=ReturnOrderLineStatus.items(),
status_class=ReturnOrderLineStatus,
verbose_name=_('Outcome'),
help_text=_('Outcome for this line item'),
)

View File

@@ -1020,6 +1020,7 @@ class StockItem(
status = InvenTreeCustomStatusModelField(
default=StockStatus.OK.value,
status_class=StockStatus,
choices=StockStatus.items(),
validators=[MinValueValidator(0)],
)