mirror of
https://github.com/inventree/InvenTree.git
synced 2025-08-07 20:32:12 +00:00
Add custom state field validation
This commit is contained in:
@@ -319,6 +319,7 @@ class Build(
|
|||||||
verbose_name=_('Build Status'),
|
verbose_name=_('Build Status'),
|
||||||
default=BuildStatus.PENDING.value,
|
default=BuildStatus.PENDING.value,
|
||||||
choices=BuildStatus.items(),
|
choices=BuildStatus.items(),
|
||||||
|
status_class=BuildStatus,
|
||||||
validators=[MinValueValidator(0)],
|
validators=[MinValueValidator(0)],
|
||||||
help_text=_('Build status code'),
|
help_text=_('Build status code'),
|
||||||
)
|
)
|
||||||
|
@@ -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.
|
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):
|
def deconstruct(self):
|
||||||
"""Deconstruct the field for migrations."""
|
"""Deconstruct the field for migrations."""
|
||||||
name, path, args, kwargs = super().deconstruct()
|
name, path, args, kwargs = super().deconstruct()
|
||||||
@@ -109,14 +123,23 @@ class InvenTreeCustomStatusModelField(models.PositiveIntegerField):
|
|||||||
"""Ensure that the value is not an empty string."""
|
"""Ensure that the value is not an empty string."""
|
||||||
if value == '':
|
if value == '':
|
||||||
value = None
|
value = None
|
||||||
|
|
||||||
return super().clean(value, model_instance)
|
return super().clean(value, model_instance)
|
||||||
|
|
||||||
def add_field(self, cls, name):
|
def add_field(self, cls, name):
|
||||||
"""Adds custom_key_field to the model class to save additional status information."""
|
"""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(
|
custom_key_field = ExtraInvenTreeCustomStatusModelField(
|
||||||
default=None,
|
default=None,
|
||||||
verbose_name=_('Custom status key'),
|
verbose_name=_('Custom status key'),
|
||||||
help_text=_('Additional status information for this item'),
|
help_text=_('Additional status information for this item'),
|
||||||
|
validators=validators,
|
||||||
blank=True,
|
blank=True,
|
||||||
null=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.
|
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:
|
class InvenTreeCustomStatusSerializerMixin:
|
||||||
"""Mixin to ensure custom status fields are set.
|
"""Mixin to ensure custom status fields are set.
|
||||||
|
@@ -117,7 +117,7 @@ class StatusCode(BaseEnum):
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def values(cls, key=None, custom=True):
|
def values(cls, key=None):
|
||||||
"""Return a dict representation containing all required information."""
|
"""Return a dict representation containing all required information."""
|
||||||
elements = [itm for itm in cls if cls._is_element(itm.name)]
|
elements = [itm for itm in cls if cls._is_element(itm.name)]
|
||||||
|
|
||||||
@@ -167,14 +167,14 @@ class StatusCode(BaseEnum):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def keys(cls):
|
def keys(cls, custom=True):
|
||||||
"""All status code keys."""
|
"""All status code keys."""
|
||||||
return [x.value for x in cls.values()]
|
return [el[0] for el in cls.items(custom=custom)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def labels(cls):
|
def labels(cls, custom=True):
|
||||||
"""All status code labels."""
|
"""All status code labels."""
|
||||||
return [x.label for x in cls.values()]
|
return [el[1] for el in cls.items(custom=custom)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def names(cls):
|
def names(cls):
|
||||||
|
21
src/backend/InvenTree/generic/states/validators.py
Normal file
21
src/backend/InvenTree/generic/states/validators.py
Normal 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'))
|
@@ -483,6 +483,7 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
|||||||
status = InvenTreeCustomStatusModelField(
|
status = InvenTreeCustomStatusModelField(
|
||||||
default=PurchaseOrderStatus.PENDING.value,
|
default=PurchaseOrderStatus.PENDING.value,
|
||||||
choices=PurchaseOrderStatus.items(),
|
choices=PurchaseOrderStatus.items(),
|
||||||
|
status_class=PurchaseOrderStatus,
|
||||||
verbose_name=_('Status'),
|
verbose_name=_('Status'),
|
||||||
help_text=_('Purchase order status'),
|
help_text=_('Purchase order status'),
|
||||||
)
|
)
|
||||||
@@ -1029,6 +1030,7 @@ class SalesOrder(TotalPriceMixin, Order):
|
|||||||
status = InvenTreeCustomStatusModelField(
|
status = InvenTreeCustomStatusModelField(
|
||||||
default=SalesOrderStatus.PENDING.value,
|
default=SalesOrderStatus.PENDING.value,
|
||||||
choices=SalesOrderStatus.items(),
|
choices=SalesOrderStatus.items(),
|
||||||
|
status_class=SalesOrderStatus,
|
||||||
verbose_name=_('Status'),
|
verbose_name=_('Status'),
|
||||||
help_text=_('Sales order status'),
|
help_text=_('Sales order status'),
|
||||||
)
|
)
|
||||||
@@ -2231,6 +2233,7 @@ class ReturnOrder(TotalPriceMixin, Order):
|
|||||||
status = InvenTreeCustomStatusModelField(
|
status = InvenTreeCustomStatusModelField(
|
||||||
default=ReturnOrderStatus.PENDING.value,
|
default=ReturnOrderStatus.PENDING.value,
|
||||||
choices=ReturnOrderStatus.items(),
|
choices=ReturnOrderStatus.items(),
|
||||||
|
status_class=ReturnOrderStatus,
|
||||||
verbose_name=_('Status'),
|
verbose_name=_('Status'),
|
||||||
help_text=_('Return order status'),
|
help_text=_('Return order status'),
|
||||||
)
|
)
|
||||||
@@ -2522,6 +2525,7 @@ class ReturnOrderLineItem(OrderLineItem):
|
|||||||
outcome = InvenTreeCustomStatusModelField(
|
outcome = InvenTreeCustomStatusModelField(
|
||||||
default=ReturnOrderLineStatus.PENDING.value,
|
default=ReturnOrderLineStatus.PENDING.value,
|
||||||
choices=ReturnOrderLineStatus.items(),
|
choices=ReturnOrderLineStatus.items(),
|
||||||
|
status_class=ReturnOrderLineStatus,
|
||||||
verbose_name=_('Outcome'),
|
verbose_name=_('Outcome'),
|
||||||
help_text=_('Outcome for this line item'),
|
help_text=_('Outcome for this line item'),
|
||||||
)
|
)
|
||||||
|
@@ -1020,6 +1020,7 @@ class StockItem(
|
|||||||
|
|
||||||
status = InvenTreeCustomStatusModelField(
|
status = InvenTreeCustomStatusModelField(
|
||||||
default=StockStatus.OK.value,
|
default=StockStatus.OK.value,
|
||||||
|
status_class=StockStatus,
|
||||||
choices=StockStatus.items(),
|
choices=StockStatus.items(),
|
||||||
validators=[MinValueValidator(0)],
|
validators=[MinValueValidator(0)],
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user