2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-07-01 03:00:54 +00:00

[Plugin] Enhanced custom validation (#6410)

* Use registry.get_plugin()

- Instead of registry.plugins.get()
- get_plugin checks registry hash
- performs registry reload if necessary

* Add PluginValidationMixin class

- Allows the entire model to be validated via plugins
- Called on model.full_clean()
- Called on model.save()

* Update Validation sample plugin

* Fix for InvenTreeTree models

* Refactor build.models

- Expose models to plugin validation

* Update stock.models

* Update more models

- common.models
- company.models

* Update more models

- label.models
- order.models
- part.models

* More model updates

* Update docs

* Fix for potential plugin edge case

- plugin slug is globally unique
- do not use get_or_create with two lookup fields
- will throw an IntegrityError if you change the name of a plugin

* Inherit DiffMixin into PluginValidationMixin

- Allows us to pass model diffs through to validation
- Plugins can validate based on what has *changed*

* Update documentation

* Add get_plugin_config helper function

* Bug fix

* Bug fix

* Update plugin hash when calling set_plugin_state

* Working on unit testing

* More unit testing

* Move get_plugin_config into registry.py

* Move extract_int into InvenTree.helpers

* Fix log formatting

* Update model definitions

- Ensure there are no changes to the migrations

* Comment out format line

* Fix access to get_plugin_config

* Fix tests for SimpleActionPlugin

* More unit test fixes
This commit is contained in:
Oliver
2024-02-06 22:00:22 +11:00
committed by GitHub
parent dce2954466
commit 2b9816d1a3
30 changed files with 618 additions and 253 deletions

View File

@ -26,20 +26,13 @@ from taggit.managers import TaggableManager
import common.models
import InvenTree.helpers
import InvenTree.models
import InvenTree.ready
import InvenTree.tasks
import label.models
import report.models
from company import models as CompanyModels
from InvenTree.fields import InvenTreeModelMoneyField, InvenTreeURLField
from InvenTree.models import (
InvenTreeAttachment,
InvenTreeBarcodeMixin,
InvenTreeNotesMixin,
InvenTreeTree,
MetadataMixin,
extract_int,
)
from InvenTree.status_codes import (
SalesOrderStatusGroups,
StockHistoryCode,
@ -53,7 +46,7 @@ from users.models import Owner
logger = logging.getLogger('inventree')
class StockLocationType(MetadataMixin, models.Model):
class StockLocationType(InvenTree.models.MetadataMixin, models.Model):
"""A type of stock location like Warehouse, room, shelf, drawer.
Attributes:
@ -111,7 +104,9 @@ class StockLocationManager(TreeManager):
return super().get_queryset()
class StockLocation(InvenTreeBarcodeMixin, MetadataMixin, InvenTreeTree):
class StockLocation(
InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeTree
):
"""Organization tree for StockItem objects.
A "StockLocation" can be considered a warehouse, or storage location
@ -352,9 +347,10 @@ def default_delete_on_deplete():
class StockItem(
InvenTreeBarcodeMixin,
InvenTreeNotesMixin,
MetadataMixin,
InvenTree.models.InvenTreeBarcodeMixin,
InvenTree.models.InvenTreeNotesMixin,
InvenTree.models.MetadataMixin,
InvenTree.models.PluginValidationMixin,
common.models.MetaMixin,
MPTTModel,
):
@ -450,7 +446,7 @@ class StockItem(
serial_int = 0
if serial not in [None, '']:
serial_int = extract_int(serial)
serial_int = InvenTree.helpers.extract_int(serial)
self.serial_int = serial_int
@ -2193,7 +2189,7 @@ def after_save_stock_item(sender, instance: StockItem, created, **kwargs):
instance.part.schedule_pricing_update(create=True)
class StockItemAttachment(InvenTreeAttachment):
class StockItemAttachment(InvenTree.models.InvenTreeAttachment):
"""Model for storing file attachments against a StockItem object."""
@staticmethod
@ -2210,7 +2206,7 @@ class StockItemAttachment(InvenTreeAttachment):
)
class StockItemTracking(models.Model):
class StockItemTracking(InvenTree.models.InvenTreeModel):
"""Stock tracking entry - used for tracking history of a particular StockItem.
Note: 2021-05-11
@ -2274,7 +2270,7 @@ def rename_stock_item_test_result_attachment(instance, filename):
)
class StockItemTestResult(MetadataMixin, models.Model):
class StockItemTestResult(InvenTree.models.InvenTreeMetadataModel):
"""A StockItemTestResult records results of custom tests against individual StockItem objects.
This is useful for tracking unit acceptance tests, and particularly useful when integrated

View File

@ -22,7 +22,6 @@ import InvenTree.status_codes
import part.models as part_models
import stock.filters
from company.serializers import SupplierPartSerializer
from InvenTree.models import extract_int
from InvenTree.serializers import InvenTreeCurrencySerializer, InvenTreeDecimalField
from part.serializers import PartBriefSerializer
@ -114,7 +113,7 @@ class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
def validate_serial(self, value):
"""Make sure serial is not to big."""
if abs(extract_int(value)) > 0x7FFFFFFF:
if abs(InvenTree.helpers.extract_int(value)) > 0x7FFFFFFF:
raise serializers.ValidationError(_('Serial number is too large'))
return value

View File

@ -1117,6 +1117,8 @@ class TestResultTest(StockTestBase):
"""Test duplicate item behaviour."""
# Create an example stock item by copying one from the database (because we are lazy)
from plugin.registry import registry
StockItem.objects.rebuild()
item = StockItem.objects.get(pk=522)
@ -1125,9 +1127,12 @@ class TestResultTest(StockTestBase):
item.serial = None
item.quantity = 50
# Try with an invalid batch code (according to sample validatoin plugin)
# Try with an invalid batch code (according to sample validation plugin)
item.batch = 'X234'
# Ensure that the sample validation plugin is activated
registry.set_plugin_state('validator', True)
with self.assertRaises(ValidationError):
item.save()