mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-05 13:10:57 +00:00
Extend functionality of custom validation plugins (#4391)
* Pass "Part" instance to plugins when calling validate_serial_number * Pass part instance through when validating IPN * Improve custom part name validation - Pass the Part instance through to the plugins - Validation is performed at the model instance level - Updates to sample plugin code * Pass StockItem through when validating batch code * Pass Part instance through when calling validate_serial_number * Bug fix * Update unit tests * Unit test fixes * Fixes for unit tests * More unit test fixes * More unit tests * Furrther unit test fixes * Simplify custom batch code validation * Further improvements to unit tests * Further unit test
This commit is contained in:
@ -6,6 +6,7 @@ import decimal
|
||||
import hashlib
|
||||
import logging
|
||||
import os
|
||||
import re
|
||||
from datetime import datetime, timedelta
|
||||
from decimal import Decimal, InvalidOperation
|
||||
|
||||
@ -538,7 +539,60 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
|
||||
return result
|
||||
|
||||
def validate_serial_number(self, serial: str, stock_item=None, check_duplicates=True, raise_error=False):
|
||||
def validate_name(self, raise_error=True):
|
||||
"""Validate the name field for this Part instance
|
||||
|
||||
This function is exposed to any Validation plugins, and thus can be customized.
|
||||
"""
|
||||
|
||||
from plugin.registry import registry
|
||||
|
||||
for plugin in registry.with_mixin('validation'):
|
||||
# Run the name through each custom validator
|
||||
# If the plugin returns 'True' we will skip any subsequent validation
|
||||
|
||||
try:
|
||||
result = plugin.validate_part_name(self.name, self)
|
||||
if result:
|
||||
return
|
||||
except ValidationError as exc:
|
||||
if raise_error:
|
||||
raise ValidationError({
|
||||
'name': exc.message,
|
||||
})
|
||||
|
||||
def validate_ipn(self, raise_error=True):
|
||||
"""Ensure that the IPN (internal part number) is valid for this Part"
|
||||
|
||||
- Validation is handled by custom plugins
|
||||
- By default, no validation checks are perfomed
|
||||
"""
|
||||
|
||||
from plugin.registry import registry
|
||||
|
||||
for plugin in registry.with_mixin('validation'):
|
||||
try:
|
||||
result = plugin.validate_part_ipn(self.IPN, self)
|
||||
|
||||
if result:
|
||||
# A "true" result force skips any subsequent checks
|
||||
break
|
||||
except ValidationError as exc:
|
||||
if raise_error:
|
||||
raise ValidationError({
|
||||
'IPN': exc.message
|
||||
})
|
||||
|
||||
# If we get to here, none of the plugins have raised an error
|
||||
pattern = common.models.InvenTreeSetting.get_setting('PART_IPN_REGEX', '', create=False).strip()
|
||||
|
||||
if pattern:
|
||||
match = re.search(pattern, self.IPN)
|
||||
|
||||
if match is None:
|
||||
raise ValidationError(_('IPN must match regex pattern {pat}').format(pat=pattern))
|
||||
|
||||
def validate_serial_number(self, serial: str, stock_item=None, check_duplicates=True, raise_error=False, **kwargs):
|
||||
"""Validate a serial number against this Part instance.
|
||||
|
||||
Note: This function is exposed to any Validation plugins, and thus can be customized.
|
||||
@ -570,7 +624,7 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
for plugin in registry.with_mixin('validation'):
|
||||
# Run the serial number through each custom validator
|
||||
# If the plugin returns 'True' we will skip any subsequent validation
|
||||
if plugin.validate_serial_number(serial):
|
||||
if plugin.validate_serial_number(serial, self):
|
||||
return True
|
||||
except ValidationError as exc:
|
||||
if raise_error:
|
||||
@ -620,7 +674,7 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
conflicts = []
|
||||
|
||||
for serial in serials:
|
||||
if not self.validate_serial_number(serial):
|
||||
if not self.validate_serial_number(serial, part=self):
|
||||
conflicts.append(serial)
|
||||
|
||||
return conflicts
|
||||
@ -765,6 +819,12 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
if type(self.IPN) is str:
|
||||
self.IPN = self.IPN.strip()
|
||||
|
||||
# Run custom validation for the IPN field
|
||||
self.validate_ipn()
|
||||
|
||||
# Run custom validation for the name field
|
||||
self.validate_name()
|
||||
|
||||
if self.trackable:
|
||||
for part in self.get_used_in().all():
|
||||
|
||||
@ -777,7 +837,6 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
max_length=100, blank=False,
|
||||
help_text=_('Part name'),
|
||||
verbose_name=_('Name'),
|
||||
validators=[validators.validate_part_name]
|
||||
)
|
||||
|
||||
is_template = models.BooleanField(
|
||||
@ -821,7 +880,6 @@ class Part(InvenTreeBarcodeMixin, MetadataMixin, MPTTModel):
|
||||
max_length=100, blank=True, null=True,
|
||||
verbose_name=_('IPN'),
|
||||
help_text=_('Internal Part Number'),
|
||||
validators=[validators.validate_part_ipn]
|
||||
)
|
||||
|
||||
revision = models.CharField(
|
||||
|
Reference in New Issue
Block a user