mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-03 22:55:43 +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:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user