mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Enhance plugin SN validation (#7942)
* Update function signature for 'validate_serial_number' - Pass through stock item parameter - Required if we want to exclude a particular item from that test * Update documentation * Docs fixes * Add type annotations
This commit is contained in:
parent
f2f90dd1e4
commit
e837e5d7d7
@ -108,22 +108,71 @@ By default, part names are not subject to any particular naming conventions or r
|
||||
|
||||
If the custom method determines that the part name is *objectionable*, it should throw a `ValidationError` which will be handled upstream by parent calling methods.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_name
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Part IPN
|
||||
|
||||
Validation of the Part IPN (Internal Part Number) field is exposed to custom plugins via the `validate_part_IPN` method. Any plugins which extend the `ValidationMixin` class can implement this method, and raise a `ValidationError` if the IPN value does not match a required convention.
|
||||
Validation of the Part IPN (Internal Part Number) field is exposed to custom plugins via the `validate_part_ipn` method. Any plugins which extend the `ValidationMixin` class can implement this method, and raise a `ValidationError` if the IPN value does not match a required convention.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_ipn
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Part Parameter Values
|
||||
|
||||
[Part parameters](../../part/parameter.md) can also have custom validation rules applied, by implementing the `validate_part_parameter` method. A plugin which implements this method should raise a `ValidationError` with an appropriate message if the part parameter value does not match a required convention.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_part_parameter
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Batch Codes
|
||||
|
||||
[Batch codes](../../stock/tracking.md#batch-codes) can be generated and/or validated by custom plugins.
|
||||
|
||||
#### Validate Batch Code
|
||||
|
||||
The `validate_batch_code` method allows plugins to raise an error if a batch code input by the user does not meet a particular pattern.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_batch_code
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
#### Generate Batch Code
|
||||
|
||||
The `generate_batch_code` method can be implemented to generate a new batch code, based on a set of provided information.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.generate_batch_code
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
### Serial Numbers
|
||||
|
||||
Requirements for serial numbers can vary greatly depending on the application. Rather than attempting to provide a "one size fits all" serial number implementation, InvenTree allows custom serial number schemes to be implemented via plugins.
|
||||
@ -134,17 +183,30 @@ The default InvenTree [serial numbering system](../../stock/tracking.md#serial-n
|
||||
|
||||
Custom serial number validation can be implemented using the `validate_serial_number` method. A *proposed* serial number is passed to this method, which then has the opportunity to raise a `ValidationError` to indicate that the serial number is not valid.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.validate_serial_number
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
!!! info "Stock Item"
|
||||
If the `stock_item` argument is provided, then this stock item has already been assigned with the provided serial number. This stock item should be excluded from any subsequent checks for *uniqueness*. The `stock_item` parameter is optional, and may be `None` if the serial number is being validated in a context where no stock item is available.
|
||||
|
||||
##### Example
|
||||
|
||||
A plugin which requires all serial numbers to be valid hexadecimal values may implement this method as follows:
|
||||
|
||||
```python
|
||||
def validate_serial_number(self, serial: str, part: Part):
|
||||
def validate_serial_number(self, serial: str, part: Part, stock_item: StockItem = None):
|
||||
"""Validate the supplied serial number
|
||||
|
||||
Arguments:
|
||||
serial: The proposed serial number (string)
|
||||
part: The Part instance for which this serial number is being validated
|
||||
stock_item: The StockItem instance for which this serial number is being validated
|
||||
"""
|
||||
|
||||
try:
|
||||
@ -160,6 +222,15 @@ While InvenTree supports arbitrary text values in the serial number fields, behi
|
||||
|
||||
A custom plugin can implement the `convert_serial_to_int` method to determine how a particular serial number is converted to an integer representation.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.convert_serial_to_int
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
!!! info "Not Required"
|
||||
If this method is not implemented, or the serial number cannot be converted to an integer, then the sorting algorithm falls back to the text (string) value
|
||||
|
||||
@ -169,6 +240,15 @@ A core component of the InvenTree serial number system is the ability to *increm
|
||||
|
||||
For custom serial number schemes, it is important to provide a method to generate the *next* serial number given a current value. The `increment_serial_number` method can be implemented by a plugin to achieve this.
|
||||
|
||||
::: plugin.base.integration.ValidationMixin.ValidationMixin.increment_serial_number
|
||||
options:
|
||||
show_bases: False
|
||||
show_root_heading: False
|
||||
show_root_toc_entry: False
|
||||
show_sources: True
|
||||
summary: False
|
||||
members: []
|
||||
|
||||
!!! info "Invalid Increment"
|
||||
If the provided number cannot be incremented (or an error occurs) the method should return `None`
|
||||
|
||||
|
@ -46,7 +46,7 @@ Additionally, the following information is stored for each part, in relation to
|
||||
|
||||
InvenTree supports pricing data in multiple currencies, allowing integration with suppliers and customers using different currency systems.
|
||||
|
||||
Supported currencies must be configured as part of [the InvenTree setup process](../start/config.md#supported-currencies).
|
||||
Supported currencies can be configured in the [InvenTree settings](../settings/currency.md).
|
||||
|
||||
!!! info "Currency Support"
|
||||
InvenTree provides multi-currency pricing support via the [django-money](https://django-money.readthedocs.io/en/latest/) library.
|
||||
|
@ -4,6 +4,7 @@ from __future__ import annotations
|
||||
|
||||
import decimal
|
||||
import hashlib
|
||||
import inspect
|
||||
import logging
|
||||
import math
|
||||
import os
|
||||
@ -775,7 +776,22 @@ class Part(
|
||||
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, self):
|
||||
|
||||
result = False
|
||||
|
||||
if hasattr(plugin, 'validate_serial_number'):
|
||||
signature = inspect.signature(plugin.validate_serial_number)
|
||||
|
||||
if 'stock_item' in signature.parameters:
|
||||
# 2024-08-21: New method signature accepts a 'stock_item' parameter
|
||||
result = plugin.validate_serial_number(
|
||||
serial, self, stock_item=stock_item
|
||||
)
|
||||
else:
|
||||
# Old method signature - does not accept a 'stock_item' parameter
|
||||
result = plugin.validate_serial_number(serial, self)
|
||||
|
||||
if result is True:
|
||||
return True
|
||||
except ValidationError as exc:
|
||||
if raise_error:
|
||||
|
@ -63,7 +63,7 @@ class ValidationMixin:
|
||||
None: or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError: if the instance cannot be deleted
|
||||
ValidationError: If the instance cannot be deleted
|
||||
"""
|
||||
return None
|
||||
|
||||
@ -81,11 +81,11 @@ class ValidationMixin:
|
||||
None: or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError: if the instance is invalid
|
||||
ValidationError: If the instance is invalid
|
||||
"""
|
||||
return None
|
||||
|
||||
def validate_part_name(self, name: str, part: part.models.Part):
|
||||
def validate_part_name(self, name: str, part: part.models.Part) -> None:
|
||||
"""Perform validation on a proposed Part name.
|
||||
|
||||
Arguments:
|
||||
@ -96,11 +96,11 @@ class ValidationMixin:
|
||||
None or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError if the proposed name is objectionable
|
||||
ValidationError: If the proposed name is objectionable
|
||||
"""
|
||||
return None
|
||||
|
||||
def validate_part_ipn(self, ipn: str, part: part.models.Part):
|
||||
def validate_part_ipn(self, ipn: str, part: part.models.Part) -> None:
|
||||
"""Perform validation on a proposed Part IPN (internal part number).
|
||||
|
||||
Arguments:
|
||||
@ -111,11 +111,13 @@ class ValidationMixin:
|
||||
None or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError if the proposed IPN is objectionable
|
||||
ValidationError: If the proposed IPN is objectionable
|
||||
"""
|
||||
return None
|
||||
|
||||
def validate_batch_code(self, batch_code: str, item: stock.models.StockItem):
|
||||
def validate_batch_code(
|
||||
self, batch_code: str, item: stock.models.StockItem
|
||||
) -> None:
|
||||
"""Validate the supplied batch code.
|
||||
|
||||
Arguments:
|
||||
@ -126,11 +128,11 @@ class ValidationMixin:
|
||||
None or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError if the proposed batch code is objectionable
|
||||
ValidationError: If the proposed batch code is objectionable
|
||||
"""
|
||||
return None
|
||||
|
||||
def generate_batch_code(self, **kwargs):
|
||||
def generate_batch_code(self, **kwargs) -> str:
|
||||
"""Generate a new batch code.
|
||||
|
||||
This method is called when a new batch code is required.
|
||||
@ -143,22 +145,28 @@ class ValidationMixin:
|
||||
"""
|
||||
return None
|
||||
|
||||
def validate_serial_number(self, serial: str, part: part.models.Part):
|
||||
def validate_serial_number(
|
||||
self,
|
||||
serial: str,
|
||||
part: part.models.Part,
|
||||
stock_item: stock.models.StockItem = None,
|
||||
) -> None:
|
||||
"""Validate the supplied serial number.
|
||||
|
||||
Arguments:
|
||||
serial: The proposed serial number (string)
|
||||
part: The Part instance for which this serial number is being validated
|
||||
stock_item: The StockItem instance for which this serial number is being validated (if applicable)
|
||||
|
||||
Returns:
|
||||
None or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError if the proposed serial is objectionable
|
||||
ValidationError: If the proposed serial is objectionable
|
||||
"""
|
||||
return None
|
||||
|
||||
def convert_serial_to_int(self, serial: str):
|
||||
def convert_serial_to_int(self, serial: str) -> int:
|
||||
"""Convert a serial number (string) into an integer representation.
|
||||
|
||||
This integer value is used for efficient sorting based on serial numbers.
|
||||
@ -179,7 +187,7 @@ class ValidationMixin:
|
||||
"""
|
||||
return None
|
||||
|
||||
def increment_serial_number(self, serial: str):
|
||||
def increment_serial_number(self, serial: str) -> str:
|
||||
"""Return the next sequential serial based on the provided value.
|
||||
|
||||
A plugin which implements this method can either return:
|
||||
@ -189,10 +197,15 @@ class ValidationMixin:
|
||||
|
||||
Arguments:
|
||||
serial: Current serial value (string)
|
||||
|
||||
Returns:
|
||||
The next serial number in the sequence (string), or None
|
||||
"""
|
||||
return None
|
||||
|
||||
def validate_part_parameter(self, parameter, data):
|
||||
def validate_part_parameter(
|
||||
self, parameter: part.models.PartParameter, data: str
|
||||
) -> None:
|
||||
"""Validate a parameter value.
|
||||
|
||||
Arguments:
|
||||
@ -203,6 +216,6 @@ class ValidationMixin:
|
||||
None or True (refer to class docstring)
|
||||
|
||||
Raises:
|
||||
ValidationError if the proposed parameter value is objectionable
|
||||
ValidationError: If the proposed parameter value is objectionable
|
||||
"""
|
||||
pass
|
||||
|
@ -121,7 +121,7 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin):
|
||||
if d >= 100:
|
||||
self.raise_error('Value must be less than 100')
|
||||
|
||||
def validate_serial_number(self, serial: str, part):
|
||||
def validate_serial_number(self, serial: str, part, stock_item=None):
|
||||
"""Validate serial number for a given StockItem.
|
||||
|
||||
These examples are silly, but serve to demonstrate how the feature could be used
|
||||
|
Loading…
Reference in New Issue
Block a user