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.
|
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
|
### 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 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.
|
[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
|
||||||
|
|
||||||
[Batch codes](../../stock/tracking.md#batch-codes) can be generated and/or validated by custom plugins.
|
[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.
|
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.
|
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
|
### 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.
|
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.
|
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
|
##### Example
|
||||||
|
|
||||||
A plugin which requires all serial numbers to be valid hexadecimal values may implement this method as follows:
|
A plugin which requires all serial numbers to be valid hexadecimal values may implement this method as follows:
|
||||||
|
|
||||||
```python
|
```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
|
"""Validate the supplied serial number
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
serial: The proposed serial number (string)
|
serial: The proposed serial number (string)
|
||||||
part: The Part instance for which this serial number is being validated
|
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:
|
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.
|
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"
|
!!! 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
|
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.
|
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"
|
!!! info "Invalid Increment"
|
||||||
If the provided number cannot be incremented (or an error occurs) the method should return `None`
|
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.
|
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"
|
!!! info "Currency Support"
|
||||||
InvenTree provides multi-currency pricing support via the [django-money](https://django-money.readthedocs.io/en/latest/) library.
|
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 decimal
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import inspect
|
||||||
import logging
|
import logging
|
||||||
import math
|
import math
|
||||||
import os
|
import os
|
||||||
@ -775,7 +776,22 @@ class Part(
|
|||||||
for plugin in registry.with_mixin('validation'):
|
for plugin in registry.with_mixin('validation'):
|
||||||
# Run the serial number through each custom validator
|
# Run the serial number through each custom validator
|
||||||
# If the plugin returns 'True' we will skip any subsequent validation
|
# 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
|
return True
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
if raise_error:
|
if raise_error:
|
||||||
|
@ -63,7 +63,7 @@ class ValidationMixin:
|
|||||||
None: or True (refer to class docstring)
|
None: or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError: if the instance cannot be deleted
|
ValidationError: If the instance cannot be deleted
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
@ -81,11 +81,11 @@ class ValidationMixin:
|
|||||||
None: or True (refer to class docstring)
|
None: or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError: if the instance is invalid
|
ValidationError: If the instance is invalid
|
||||||
"""
|
"""
|
||||||
return None
|
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.
|
"""Perform validation on a proposed Part name.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -96,11 +96,11 @@ class ValidationMixin:
|
|||||||
None or True (refer to class docstring)
|
None or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError if the proposed name is objectionable
|
ValidationError: If the proposed name is objectionable
|
||||||
"""
|
"""
|
||||||
return None
|
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).
|
"""Perform validation on a proposed Part IPN (internal part number).
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -111,11 +111,13 @@ class ValidationMixin:
|
|||||||
None or True (refer to class docstring)
|
None or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError if the proposed IPN is objectionable
|
ValidationError: If the proposed IPN is objectionable
|
||||||
"""
|
"""
|
||||||
return None
|
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.
|
"""Validate the supplied batch code.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -126,11 +128,11 @@ class ValidationMixin:
|
|||||||
None or True (refer to class docstring)
|
None or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError if the proposed batch code is objectionable
|
ValidationError: If the proposed batch code is objectionable
|
||||||
"""
|
"""
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def generate_batch_code(self, **kwargs):
|
def generate_batch_code(self, **kwargs) -> str:
|
||||||
"""Generate a new batch code.
|
"""Generate a new batch code.
|
||||||
|
|
||||||
This method is called when a new batch code is required.
|
This method is called when a new batch code is required.
|
||||||
@ -143,22 +145,28 @@ class ValidationMixin:
|
|||||||
"""
|
"""
|
||||||
return None
|
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.
|
"""Validate the supplied serial number.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
serial: The proposed serial number (string)
|
serial: The proposed serial number (string)
|
||||||
part: The Part instance for which this serial number is being validated
|
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:
|
Returns:
|
||||||
None or True (refer to class docstring)
|
None or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError if the proposed serial is objectionable
|
ValidationError: If the proposed serial is objectionable
|
||||||
"""
|
"""
|
||||||
return None
|
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.
|
"""Convert a serial number (string) into an integer representation.
|
||||||
|
|
||||||
This integer value is used for efficient sorting based on serial numbers.
|
This integer value is used for efficient sorting based on serial numbers.
|
||||||
@ -179,7 +187,7 @@ class ValidationMixin:
|
|||||||
"""
|
"""
|
||||||
return None
|
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.
|
"""Return the next sequential serial based on the provided value.
|
||||||
|
|
||||||
A plugin which implements this method can either return:
|
A plugin which implements this method can either return:
|
||||||
@ -189,10 +197,15 @@ class ValidationMixin:
|
|||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
serial: Current serial value (string)
|
serial: Current serial value (string)
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The next serial number in the sequence (string), or None
|
||||||
"""
|
"""
|
||||||
return 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.
|
"""Validate a parameter value.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
@ -203,6 +216,6 @@ class ValidationMixin:
|
|||||||
None or True (refer to class docstring)
|
None or True (refer to class docstring)
|
||||||
|
|
||||||
Raises:
|
Raises:
|
||||||
ValidationError if the proposed parameter value is objectionable
|
ValidationError: If the proposed parameter value is objectionable
|
||||||
"""
|
"""
|
||||||
pass
|
pass
|
||||||
|
@ -121,7 +121,7 @@ class SampleValidatorPlugin(SettingsMixin, ValidationMixin, InvenTreePlugin):
|
|||||||
if d >= 100:
|
if d >= 100:
|
||||||
self.raise_error('Value must be less than 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.
|
"""Validate serial number for a given StockItem.
|
||||||
|
|
||||||
These examples are silly, but serve to demonstrate how the feature could be used
|
These examples are silly, but serve to demonstrate how the feature could be used
|
||||||
|
Loading…
Reference in New Issue
Block a user