mirror of
https://github.com/inventree/InvenTree.git
synced 2025-10-24 01:47:39 +00:00
adjust naming and docs, add typing to clean stuff up
This commit is contained in:
@@ -183,5 +183,12 @@ Overriding a global setting with a different value than the current one.
|
||||
|
||||
See [Override global settings](../settings/global.md#override-global-settings) for more information.
|
||||
|
||||
#### INVE-I2
|
||||
**Issue with filtering serializer or decorator - Backend**
|
||||
|
||||
An issue was detected with the application of a filtering serializer or decorator. This might lead to unexpected behaviour or performance issues. Therefore an issue is raised to make the developer aware of the possible issue. Look into the docstrings of enable_filter, FilterableSerializerField or FilterableSerializerMixin.
|
||||
|
||||
This warning should only be raised during development and not in production, if you recently installed a plugin you might want to contact the plugin author.
|
||||
|
||||
### INVE-M (InvenTree Miscellaneous)
|
||||
Miscellaneous — These are information messages that might be used to mark debug information or other messages helpful for the InvenTree team to understand behaviour.
|
||||
|
||||
@@ -14,7 +14,7 @@ from InvenTree.helpers import (
|
||||
strip_html_tags,
|
||||
)
|
||||
from InvenTree.schema import schema_for_view_output_options
|
||||
from InvenTree.serializers import PathScopedMixin
|
||||
from InvenTree.serializers import FilterableSerializerMixin
|
||||
|
||||
|
||||
class CleanMixin:
|
||||
@@ -231,9 +231,9 @@ class OutputOptionsMixin:
|
||||
serializer = super().get_serializer(*args, **kwargs)
|
||||
|
||||
# Check if the serializer actually can be filtered - makes not much sense to use this mixin without that prerequisite
|
||||
if not isinstance(serializer, PathScopedMixin):
|
||||
if not isinstance(serializer, FilterableSerializerMixin):
|
||||
raise Exception(
|
||||
'INVE-W999: `OutputOptionsMixin` can only be used with serializers that contain the `PathScopedMixin` mixin'
|
||||
'INVE-I2: `OutputOptionsMixin` can only be used with serializers that contain the `FilterableSerializerMixin` mixin'
|
||||
)
|
||||
|
||||
return serializer
|
||||
|
||||
@@ -4,7 +4,7 @@ import os
|
||||
from collections import OrderedDict
|
||||
from copy import deepcopy
|
||||
from decimal import Decimal
|
||||
from typing import Optional
|
||||
from typing import Any, Optional
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
@@ -31,8 +31,11 @@ from InvenTree.helpers import str2bool
|
||||
|
||||
|
||||
# region path filtering
|
||||
class OptFilter:
|
||||
"""Filter for serializer or field."""
|
||||
class FilterableSerializerField:
|
||||
"""Mixin to mark serializer as filterable.
|
||||
|
||||
This needs to be used in conjunction with `enable_filter` on the serializer field!
|
||||
"""
|
||||
|
||||
is_filterable = None
|
||||
is_filterable_vals = {}
|
||||
@@ -45,15 +48,47 @@ class OptFilter:
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
|
||||
class PathScopedMixin:
|
||||
"""Mixin to disable a serializer field based on kwargs passed to the view."""
|
||||
def enable_filter(
|
||||
func: Any, default_include: bool = False, filter_name: Optional[str] = None
|
||||
):
|
||||
"""Decorator for marking a serializer field as filterable.
|
||||
|
||||
This can be customized by passing in arguments. This only works in conjunction with serializer fields or serializers that contain the `FilterableSerializerField` mixin.
|
||||
|
||||
Args:
|
||||
func: The serializer field to mark as filterable. Will automatically be passed when used as a decorator.
|
||||
default_include (bool): If True, the field will be included by default unless explicitly excluded. If False, the field will be excluded by default unless explicitly included.
|
||||
filter_name (str, optional): The name of the filter parameter to use in the URL. If None, the function name of the (decorated) function will be used.
|
||||
"""
|
||||
# Ensure this function can be actually filteres
|
||||
if not issubclass(func.__class__, FilterableSerializerField):
|
||||
raise TypeError(
|
||||
'INVE-I2: `enable_filter` can only be applied to serializer fields / serializers that contain the `FilterableSerializerField` mixin!'
|
||||
)
|
||||
|
||||
# Mark the function as filterable
|
||||
func._kwargs['is_filterable'] = True
|
||||
func._kwargs['is_filterable_vals'] = {
|
||||
'default': default_include,
|
||||
'filter_name': filter_name if filter_name else func.field_name,
|
||||
}
|
||||
return func
|
||||
|
||||
|
||||
class FilterableSerializerMixin:
|
||||
"""Mixin that enables filtering of marked fields on a serializer.
|
||||
|
||||
Use the `enable_filter` decorator to mark serializer fields as filterable.
|
||||
This introduces overhead during initialization, so only use this mixin when necessary.
|
||||
If you need to mark a serializer as filterable but it does not contain any filterable fields, set `no_filters = True` to avoid getting an exception that protects against over-application of this mixin.
|
||||
"""
|
||||
|
||||
_was_filtered = False
|
||||
no_filters = False
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Initialization routine for the serializer."""
|
||||
# add list_serializer_class to meta if not present
|
||||
"""Initialization routine for the serializer. This gathers and applies filters through kwargs."""
|
||||
# add list_serializer_class to meta if not present - reduces duplication
|
||||
if not isinstance(self, FilterableListSerializer) and (
|
||||
not hasattr(self.Meta, 'list_serializer_class')
|
||||
):
|
||||
@@ -61,98 +96,90 @@ class PathScopedMixin:
|
||||
|
||||
self.gather_filters(kwargs)
|
||||
super().__init__(*args, **kwargs)
|
||||
self.do_filtering(*args, **kwargs)
|
||||
self.do_filtering()
|
||||
|
||||
def gather_filters(self, kwargs):
|
||||
"""Gather filterable fields."""
|
||||
def gather_filters(self, kwargs) -> None:
|
||||
"""Gather filterable fields through introspection."""
|
||||
# Fast exit if this has already been done or would not have any effect
|
||||
if getattr(self, '_was_filtered', False) or not hasattr(self, 'fields'):
|
||||
return kwargs
|
||||
return
|
||||
self._was_filtered = True
|
||||
|
||||
# Actually gather the filterable fields
|
||||
fields = self.fields.items()
|
||||
self.filter_targets = {
|
||||
k: {'serializer': a, **getattr(a, 'is_filterable_vals', {})}
|
||||
for k, a in fields
|
||||
# Also see `enable_filter` where` is_filterable and is_filterable_vals are set
|
||||
self.filter_targets: dict[str, dict] = {
|
||||
str(k): {'serializer': a, **getattr(a, 'is_filterable_vals', {})}
|
||||
for k, a in self.fields.items()
|
||||
if getattr(a, 'is_filterable', None)
|
||||
}
|
||||
|
||||
# Remove filter args from kwargs to avoid issues with super().__init__
|
||||
poped_kwargs = {} # store popped kwargs as a arg might be reused for multiple fields
|
||||
tgs_vals = {}
|
||||
tgs_vals: dict[str, bool] = {}
|
||||
for k, v in self.filter_targets.items():
|
||||
pop_ref = v['name'] or k
|
||||
pop_ref = v['filter_name'] or k
|
||||
val = kwargs.pop(pop_ref, poped_kwargs.get(pop_ref))
|
||||
if val:
|
||||
if val: # Save popped value for reuse
|
||||
poped_kwargs[pop_ref] = val
|
||||
tgs_vals[k] = str2bool(val) if isinstance(val, str) else val
|
||||
tgs_vals[k] = (
|
||||
str2bool(val) if isinstance(val, (str, int, float)) else bool(val)
|
||||
) # Support for various filtering style for backwards compatibility
|
||||
self.filter_target_values = tgs_vals
|
||||
|
||||
# Ensure this mixin is not proadly applied as it is expensive on scale (total CI time increased by 21% when running all coverage tests)
|
||||
if len(self.filter_targets) == 0 and not self.no_filters:
|
||||
raise Exception(
|
||||
'INVE-W999: No filter targets found in fields, remove `PathScopedMixin`'
|
||||
'INVE-I2: No filter targets found in fields, remove `PathScopedMixin`'
|
||||
)
|
||||
|
||||
return kwargs
|
||||
|
||||
def do_filtering(self, *args, **kwargs):
|
||||
def do_filtering(self) -> None:
|
||||
"""Do the actual filtering."""
|
||||
# This serializer might not contain filters or we do not want to pop fields while generating the schema
|
||||
if (
|
||||
not hasattr(self, 'filter_target_values')
|
||||
or InvenTree.ready.isGeneratingSchema()
|
||||
):
|
||||
return
|
||||
|
||||
# Throw out fields which are not requested
|
||||
# Throw out fields which are not requested (either by default or explicitly)
|
||||
for k, v in self.filter_target_values.items():
|
||||
value = v if v is not None else self.filter_targets[k]['default']
|
||||
# See `enable_filter` where` is_filterable and is_filterable_vals are set
|
||||
value = v if v is not None else bool(self.filter_targets[k]['default'])
|
||||
if value is not True:
|
||||
self.fields.pop(k, None)
|
||||
|
||||
|
||||
# Decorator for marking serialzier fields that can be filtered out
|
||||
def can_filter(func, default=False, name: Optional[str] = None):
|
||||
"""Decorator for marking serializer fields as filterable."""
|
||||
# Ensure this function can be actually filteres
|
||||
if not issubclass(func.__class__, OptFilter):
|
||||
raise TypeError(
|
||||
'INVE-W999: `can_filter` can only be applied to serializers that contain `OptFilter` mixin!'
|
||||
)
|
||||
|
||||
# Mark the function as filterable
|
||||
func._kwargs['is_filterable'] = True
|
||||
func._kwargs['is_filterable_vals'] = {
|
||||
'default': default,
|
||||
'name': name if name else func.field_name,
|
||||
}
|
||||
return func
|
||||
|
||||
|
||||
class FilterableListSerializer(OptFilter, PathScopedMixin, serializers.ListSerializer):
|
||||
# special serializers which allow filtering
|
||||
class FilterableListSerializer(
|
||||
FilterableSerializerField, FilterableSerializerMixin, serializers.ListSerializer
|
||||
):
|
||||
"""Custom ListSerializer which allows filtering of fields."""
|
||||
|
||||
|
||||
class CfListField(OptFilter, serializers.ListField):
|
||||
# special serializer fields which allow filtering
|
||||
class FilterableListField(FilterableSerializerField, serializers.ListField):
|
||||
"""Custom ListField which allows filtering."""
|
||||
|
||||
|
||||
class CfSerializerMethodField(OptFilter, serializers.SerializerMethodField):
|
||||
class FilterableSerializerMethodField(
|
||||
FilterableSerializerField, serializers.SerializerMethodField
|
||||
):
|
||||
"""Custom SerializerMethodField which allows filtering."""
|
||||
|
||||
|
||||
class CfDateTimeField(OptFilter, serializers.DateTimeField):
|
||||
class FilterableDateTimeField(FilterableSerializerField, serializers.DateTimeField):
|
||||
"""Custom DateTimeField which allows filtering."""
|
||||
|
||||
|
||||
class CfFloatField(OptFilter, serializers.FloatField):
|
||||
class FilterableFloatField(FilterableSerializerField, serializers.FloatField):
|
||||
"""Custom FloatField which allows filtering."""
|
||||
|
||||
|
||||
class CfCharField(OptFilter, serializers.CharField):
|
||||
class FilterableCharField(FilterableSerializerField, serializers.CharField):
|
||||
"""Custom CharField which allows filtering."""
|
||||
|
||||
|
||||
class CfIntegerField(OptFilter, serializers.IntegerField):
|
||||
class FilterableIntegerField(FilterableSerializerField, serializers.IntegerField):
|
||||
"""Custom IntegerField which allows filtering."""
|
||||
|
||||
|
||||
@@ -163,10 +190,11 @@ class EmptySerializer(serializers.Serializer):
|
||||
"""Empty serializer for use in testing."""
|
||||
|
||||
|
||||
class InvenTreeMoneySerializer(OptFilter, MoneyField):
|
||||
class InvenTreeMoneySerializer(FilterableSerializerField, MoneyField):
|
||||
"""Custom serializer for 'MoneyField', which ensures that passed values are numerically valid.
|
||||
|
||||
Ref: https://github.com/django-money/django-money/blob/master/djmoney/contrib/django_rest_framework/fields.py
|
||||
This field allows filtering.
|
||||
"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -519,8 +547,11 @@ class BareInvenTreeModelSerializer(serializers.ModelSerializer):
|
||||
return data
|
||||
|
||||
|
||||
class InvenTreeModelSerializer(OptFilter, BareInvenTreeModelSerializer):
|
||||
"""Inherits the standard Django ModelSerializer class, but also ensures that the underlying model class data are checked on validation."""
|
||||
class InvenTreeModelSerializer(FilterableSerializerField, BareInvenTreeModelSerializer):
|
||||
"""Inherits the standard Django ModelSerializer class, but also ensures that the underlying model class data are checked on validation.
|
||||
|
||||
This field allows filtering.
|
||||
"""
|
||||
|
||||
|
||||
class InvenTreeTaggitSerializer(TaggitSerializer):
|
||||
|
||||
@@ -32,13 +32,13 @@ from common.settings import get_global_setting
|
||||
from generic.states.fields import InvenTreeCustomStatusSerializerMixin
|
||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||
from InvenTree.serializers import (
|
||||
CfCharField,
|
||||
CfIntegerField,
|
||||
FilterableCharField,
|
||||
FilterableIntegerField,
|
||||
FilterableSerializerMixin,
|
||||
InvenTreeDecimalField,
|
||||
InvenTreeModelSerializer,
|
||||
NotesFieldMixin,
|
||||
PathScopedMixin,
|
||||
can_filter,
|
||||
enable_filter,
|
||||
)
|
||||
from stock.generators import generate_batch_code
|
||||
from stock.models import StockItem, StockLocation
|
||||
@@ -55,7 +55,7 @@ from .status_codes import BuildStatus
|
||||
|
||||
|
||||
class BuildSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
NotesFieldMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTreeCustomStatusSerializerMixin,
|
||||
@@ -118,7 +118,7 @@ class BuildSerializer(
|
||||
|
||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
part_serializers.PartBriefSerializer(source='part', many=False, read_only=True),
|
||||
True,
|
||||
)
|
||||
@@ -131,46 +131,48 @@ class BuildSerializer(
|
||||
|
||||
overdue = serializers.BooleanField(read_only=True, default=False)
|
||||
|
||||
issued_by_detail = can_filter(
|
||||
UserSerializer(source='issued_by', read_only=True), True, name='user_detail'
|
||||
issued_by_detail = enable_filter(
|
||||
UserSerializer(source='issued_by', read_only=True),
|
||||
True,
|
||||
filter_name='user_detail',
|
||||
)
|
||||
|
||||
responsible_detail = can_filter(
|
||||
responsible_detail = enable_filter(
|
||||
OwnerSerializer(source='responsible', read_only=True, allow_null=True),
|
||||
True,
|
||||
name='user_detail',
|
||||
filter_name='user_detail',
|
||||
)
|
||||
|
||||
barcode_hash = serializers.CharField(read_only=True)
|
||||
|
||||
project_code_label = can_filter(
|
||||
CfCharField(
|
||||
project_code_label = enable_filter(
|
||||
FilterableCharField(
|
||||
source='project_code.code',
|
||||
read_only=True,
|
||||
label=_('Project Code Label'),
|
||||
allow_null=True,
|
||||
),
|
||||
True,
|
||||
name='project_code_detail',
|
||||
filter_name='project_code_detail',
|
||||
)
|
||||
|
||||
project_code_detail = can_filter(
|
||||
project_code_detail = enable_filter(
|
||||
ProjectCodeSerializer(
|
||||
source='project_code', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
True,
|
||||
name='project_code_detail',
|
||||
filter_name='project_code_detail',
|
||||
)
|
||||
|
||||
project_code = can_filter(
|
||||
CfIntegerField(
|
||||
project_code = enable_filter(
|
||||
FilterableIntegerField(
|
||||
allow_null=True,
|
||||
required=False,
|
||||
label=_('Project Code'),
|
||||
help_text=_('Project code for this build order'),
|
||||
),
|
||||
True,
|
||||
name='project_code_detail',
|
||||
filter_name='project_code_detail',
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
@@ -1157,7 +1159,7 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
||||
|
||||
|
||||
class BuildItemSerializer(
|
||||
PathScopedMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializes a BuildItem object, which is an allocation of a stock item against a build order."""
|
||||
|
||||
@@ -1228,7 +1230,7 @@ class BuildItemSerializer(
|
||||
)
|
||||
|
||||
# Extra (optional) detail fields
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
part_serializers.PartBriefSerializer(
|
||||
label=_('Part'),
|
||||
source='stock_item.part',
|
||||
@@ -1240,7 +1242,7 @@ class BuildItemSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
stock_item_detail = can_filter(
|
||||
stock_item_detail = enable_filter(
|
||||
StockItemSerializer(
|
||||
source='stock_item',
|
||||
read_only=True,
|
||||
@@ -1252,14 +1254,14 @@ class BuildItemSerializer(
|
||||
path_detail=False,
|
||||
),
|
||||
True,
|
||||
name='stock_detail',
|
||||
filter_name='stock_detail',
|
||||
)
|
||||
|
||||
location = serializers.PrimaryKeyRelatedField(
|
||||
label=_('Location'), source='stock_item.location', many=False, read_only=True
|
||||
)
|
||||
|
||||
location_detail = can_filter(
|
||||
location_detail = enable_filter(
|
||||
LocationBriefSerializer(
|
||||
label=_('Location'),
|
||||
source='stock_item.location',
|
||||
@@ -1269,7 +1271,7 @@ class BuildItemSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
build_detail = can_filter(
|
||||
build_detail = enable_filter(
|
||||
BuildSerializer(
|
||||
label=_('Build'),
|
||||
source='build_line.build',
|
||||
@@ -1293,7 +1295,7 @@ class BuildItemSerializer(
|
||||
|
||||
|
||||
class BuildLineSerializer(
|
||||
PathScopedMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for a BuildItem object."""
|
||||
|
||||
@@ -1369,7 +1371,7 @@ class BuildLineSerializer(
|
||||
read_only=True,
|
||||
)
|
||||
|
||||
allocations = can_filter(
|
||||
allocations = enable_filter(
|
||||
BuildItemSerializer(many=True, read_only=True, build_detail=False), True
|
||||
)
|
||||
|
||||
@@ -1402,7 +1404,7 @@ class BuildLineSerializer(
|
||||
bom_item = serializers.PrimaryKeyRelatedField(label=_('BOM Item'), read_only=True)
|
||||
|
||||
# Foreign key fields
|
||||
bom_item_detail = can_filter(
|
||||
bom_item_detail = enable_filter(
|
||||
part_serializers.BomItemSerializer(
|
||||
label=_('BOM Item'),
|
||||
source='bom_item',
|
||||
@@ -1417,7 +1419,7 @@ class BuildLineSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
assembly_detail = can_filter(
|
||||
assembly_detail = enable_filter(
|
||||
part_serializers.PartBriefSerializer(
|
||||
label=_('Assembly'),
|
||||
source='bom_item.part',
|
||||
@@ -1429,7 +1431,7 @@ class BuildLineSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
part_serializers.PartBriefSerializer(
|
||||
label=_('Part'),
|
||||
source='bom_item.sub_part',
|
||||
@@ -1440,7 +1442,7 @@ class BuildLineSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
build_detail = can_filter(
|
||||
build_detail = enable_filter(
|
||||
BuildSerializer(
|
||||
label=_('Build'),
|
||||
source='build',
|
||||
|
||||
@@ -18,7 +18,8 @@ from importer.registry import register_importer
|
||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||
from InvenTree.ready import isGeneratingSchema
|
||||
from InvenTree.serializers import (
|
||||
CfCharField,
|
||||
FilterableCharField,
|
||||
FilterableSerializerMixin,
|
||||
InvenTreeCurrencySerializer,
|
||||
InvenTreeDecimalField,
|
||||
InvenTreeImageSerializerField,
|
||||
@@ -26,9 +27,8 @@ from InvenTree.serializers import (
|
||||
InvenTreeMoneySerializer,
|
||||
InvenTreeTagModelSerializer,
|
||||
NotesFieldMixin,
|
||||
PathScopedMixin,
|
||||
RemoteImageMixin,
|
||||
can_filter,
|
||||
enable_filter,
|
||||
)
|
||||
|
||||
from .models import (
|
||||
@@ -251,7 +251,7 @@ class ContactSerializer(DataImportExportSerializerMixin, InvenTreeModelSerialize
|
||||
|
||||
@register_importer()
|
||||
class ManufacturerPartSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTreeTagModelSerializer,
|
||||
NotesFieldMixin,
|
||||
@@ -279,22 +279,22 @@ class ManufacturerPartSerializer(
|
||||
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
part_serializers.PartBriefSerializer(
|
||||
source='part', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
manufacturer_detail = can_filter(
|
||||
manufacturer_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='manufacturer', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
pretty_name = can_filter(
|
||||
CfCharField(read_only=True, allow_null=True), name='pretty'
|
||||
pretty_name = enable_filter(
|
||||
FilterableCharField(read_only=True, allow_null=True), filter_name='pretty'
|
||||
)
|
||||
|
||||
manufacturer = serializers.PrimaryKeyRelatedField(
|
||||
@@ -304,7 +304,7 @@ class ManufacturerPartSerializer(
|
||||
|
||||
@register_importer()
|
||||
class ManufacturerPartParameterSerializer(
|
||||
PathScopedMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for the ManufacturerPartParameter model."""
|
||||
|
||||
@@ -322,7 +322,7 @@ class ManufacturerPartParameterSerializer(
|
||||
'units',
|
||||
]
|
||||
|
||||
manufacturer_part_detail = can_filter(
|
||||
manufacturer_part_detail = enable_filter(
|
||||
ManufacturerPartSerializer(
|
||||
source='manufacturer_part', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -331,7 +331,7 @@ class ManufacturerPartParameterSerializer(
|
||||
|
||||
@register_importer()
|
||||
class SupplierPartSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTreeTagModelSerializer,
|
||||
NotesFieldMixin,
|
||||
@@ -532,7 +532,7 @@ class SupplierPartSerializer(
|
||||
|
||||
@register_importer()
|
||||
class SupplierPriceBreakSerializer(
|
||||
PathScopedMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, DataImportExportSerializerMixin, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for SupplierPriceBreak object."""
|
||||
|
||||
@@ -569,14 +569,14 @@ class SupplierPriceBreakSerializer(
|
||||
source='part.supplier', many=False, read_only=True
|
||||
)
|
||||
|
||||
supplier_detail = can_filter(
|
||||
supplier_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='part.supplier', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
)
|
||||
|
||||
# Detail serializer for SupplierPart
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
SupplierPartSerializer(
|
||||
source='part', brief=True, many=False, read_only=True, allow_null=True
|
||||
)
|
||||
|
||||
@@ -45,13 +45,13 @@ from InvenTree.helpers import (
|
||||
)
|
||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||
from InvenTree.serializers import (
|
||||
FilterableSerializerMixin,
|
||||
InvenTreeCurrencySerializer,
|
||||
InvenTreeDecimalField,
|
||||
InvenTreeModelSerializer,
|
||||
InvenTreeMoneySerializer,
|
||||
NotesFieldMixin,
|
||||
PathScopedMixin,
|
||||
can_filter,
|
||||
enable_filter,
|
||||
)
|
||||
from order.status_codes import (
|
||||
PurchaseOrderStatusGroups,
|
||||
@@ -303,7 +303,7 @@ class AbstractExtraLineMeta:
|
||||
|
||||
@register_importer()
|
||||
class PurchaseOrderSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
NotesFieldMixin,
|
||||
TotalPriceMixin,
|
||||
InvenTreeCustomStatusSerializerMixin,
|
||||
@@ -369,7 +369,7 @@ class PurchaseOrderSerializer(
|
||||
source='supplier.name', read_only=True, label=_('Supplier Name')
|
||||
)
|
||||
|
||||
supplier_detail = can_filter(
|
||||
supplier_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='supplier', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -460,7 +460,7 @@ class PurchaseOrderIssueSerializer(OrderAdjustSerializer):
|
||||
|
||||
@register_importer()
|
||||
class PurchaseOrderLineItemSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
AbstractLineItemSerializer,
|
||||
InvenTreeModelSerializer,
|
||||
@@ -583,18 +583,18 @@ class PurchaseOrderLineItemSerializer(
|
||||
|
||||
total_price = serializers.FloatField(read_only=True)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
PartBriefSerializer(
|
||||
source='get_base_part', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
name='part_detail',
|
||||
filter_name='part_detail',
|
||||
)
|
||||
|
||||
supplier_part_detail = can_filter(
|
||||
supplier_part_detail = enable_filter(
|
||||
SupplierPartSerializer(
|
||||
source='part', brief=True, many=False, read_only=True, allow_null=True
|
||||
),
|
||||
name='part_detail',
|
||||
filter_name='part_detail',
|
||||
)
|
||||
|
||||
purchase_price = InvenTreeMoneySerializer(allow_null=True)
|
||||
@@ -615,7 +615,7 @@ class PurchaseOrderLineItemSerializer(
|
||||
help_text=_('Purchase price currency')
|
||||
)
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
PurchaseOrderSerializer(
|
||||
source='order', read_only=True, allow_null=True, many=False
|
||||
)
|
||||
@@ -693,11 +693,11 @@ class PurchaseOrderLineItemSerializer(
|
||||
|
||||
@register_importer()
|
||||
class PurchaseOrderExtraLineSerializer(
|
||||
PathScopedMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for a PurchaseOrderExtraLine object."""
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
PurchaseOrderSerializer(
|
||||
source='order', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -956,7 +956,7 @@ class PurchaseOrderReceiveSerializer(serializers.Serializer):
|
||||
|
||||
@register_importer()
|
||||
class SalesOrderSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
NotesFieldMixin,
|
||||
TotalPriceMixin,
|
||||
InvenTreeCustomStatusSerializerMixin,
|
||||
@@ -1022,7 +1022,7 @@ class SalesOrderSerializer(
|
||||
|
||||
return queryset
|
||||
|
||||
customer_detail = can_filter(
|
||||
customer_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='customer', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -1047,7 +1047,7 @@ class SalesOrderIssueSerializer(OrderAdjustSerializer):
|
||||
|
||||
@register_importer()
|
||||
class SalesOrderLineItemSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
AbstractLineItemSerializer,
|
||||
InvenTreeModelSerializer,
|
||||
@@ -1179,15 +1179,15 @@ class SalesOrderLineItemSerializer(
|
||||
|
||||
return queryset
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
SalesOrderSerializer(
|
||||
source='order', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
)
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
PartBriefSerializer(source='part', many=False, read_only=True, allow_null=True)
|
||||
)
|
||||
customer_detail = can_filter(
|
||||
customer_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='order.customer', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -1215,7 +1215,7 @@ class SalesOrderLineItemSerializer(
|
||||
|
||||
@register_importer()
|
||||
class SalesOrderShipmentSerializer(
|
||||
PathScopedMixin, NotesFieldMixin, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, NotesFieldMixin, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for the SalesOrderShipment class."""
|
||||
|
||||
@@ -1253,7 +1253,7 @@ class SalesOrderShipmentSerializer(
|
||||
read_only=True, allow_null=True, label=_('Allocated Items')
|
||||
)
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
SalesOrderSerializer(
|
||||
source='order', read_only=True, allow_null=True, many=False
|
||||
),
|
||||
@@ -1261,7 +1261,9 @@ class SalesOrderShipmentSerializer(
|
||||
)
|
||||
|
||||
|
||||
class SalesOrderAllocationSerializer(PathScopedMixin, InvenTreeModelSerializer):
|
||||
class SalesOrderAllocationSerializer(
|
||||
FilterableSerializerMixin, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for the SalesOrderAllocation model.
|
||||
|
||||
This includes some fields from the related model objects.
|
||||
@@ -1303,18 +1305,18 @@ class SalesOrderAllocationSerializer(PathScopedMixin, InvenTreeModelSerializer):
|
||||
)
|
||||
|
||||
# Extra detail fields
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
SalesOrderSerializer(
|
||||
source='line.order', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
)
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
PartBriefSerializer(
|
||||
source='item.part', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
True,
|
||||
)
|
||||
item_detail = can_filter(
|
||||
item_detail = enable_filter(
|
||||
stock.serializers.StockItemSerializer(
|
||||
source='item',
|
||||
many=False,
|
||||
@@ -1326,12 +1328,12 @@ class SalesOrderAllocationSerializer(PathScopedMixin, InvenTreeModelSerializer):
|
||||
),
|
||||
True,
|
||||
)
|
||||
location_detail = can_filter(
|
||||
location_detail = enable_filter(
|
||||
stock.serializers.LocationBriefSerializer(
|
||||
source='item.location', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
)
|
||||
customer_detail = can_filter(
|
||||
customer_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='line.order.customer', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -1775,7 +1777,7 @@ class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
|
||||
|
||||
@register_importer()
|
||||
class SalesOrderExtraLineSerializer(
|
||||
PathScopedMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for a SalesOrderExtraLine object."""
|
||||
|
||||
@@ -1784,7 +1786,7 @@ class SalesOrderExtraLineSerializer(
|
||||
|
||||
model = order.models.SalesOrderExtraLine
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
SalesOrderSerializer(
|
||||
source='order', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -1793,7 +1795,7 @@ class SalesOrderExtraLineSerializer(
|
||||
|
||||
@register_importer()
|
||||
class ReturnOrderSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
NotesFieldMixin,
|
||||
InvenTreeCustomStatusSerializerMixin,
|
||||
AbstractOrderSerializer,
|
||||
@@ -1845,7 +1847,7 @@ class ReturnOrderSerializer(
|
||||
|
||||
return queryset
|
||||
|
||||
customer_detail = can_filter(
|
||||
customer_detail = enable_filter(
|
||||
CompanyBriefSerializer(
|
||||
source='customer', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -1984,7 +1986,7 @@ class ReturnOrderReceiveSerializer(serializers.Serializer):
|
||||
|
||||
@register_importer()
|
||||
class ReturnOrderLineItemSerializer(
|
||||
PathScopedMixin,
|
||||
FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
AbstractLineItemSerializer,
|
||||
InvenTreeModelSerializer,
|
||||
@@ -2014,7 +2016,7 @@ class ReturnOrderLineItemSerializer(
|
||||
'link',
|
||||
]
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
ReturnOrderSerializer(
|
||||
source='order', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -2024,13 +2026,13 @@ class ReturnOrderLineItemSerializer(
|
||||
label=_('Quantity'), help_text=_('Quantity to return')
|
||||
)
|
||||
|
||||
item_detail = can_filter(
|
||||
item_detail = enable_filter(
|
||||
stock.serializers.StockItemSerializer(
|
||||
source='item', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
PartBriefSerializer(
|
||||
source='item.part', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -2042,7 +2044,7 @@ class ReturnOrderLineItemSerializer(
|
||||
|
||||
@register_importer()
|
||||
class ReturnOrderExtraLineSerializer(
|
||||
PathScopedMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||
FilterableSerializerMixin, AbstractExtraLineSerializer, InvenTreeModelSerializer
|
||||
):
|
||||
"""Serializer for a ReturnOrderExtraLine object."""
|
||||
|
||||
@@ -2051,7 +2053,7 @@ class ReturnOrderExtraLineSerializer(
|
||||
|
||||
model = order.models.ReturnOrderExtraLine
|
||||
|
||||
order_detail = can_filter(
|
||||
order_detail = enable_filter(
|
||||
ReturnOrderSerializer(
|
||||
source='order', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
|
||||
@@ -33,11 +33,11 @@ from importer.registry import register_importer
|
||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||
from InvenTree.ready import isGeneratingSchema
|
||||
from InvenTree.serializers import (
|
||||
CfDateTimeField,
|
||||
CfFloatField,
|
||||
CfListField,
|
||||
FilterableDateTimeField,
|
||||
FilterableFloatField,
|
||||
FilterableListField,
|
||||
FilterableListSerializer,
|
||||
can_filter,
|
||||
enable_filter,
|
||||
)
|
||||
from users.serializers import UserSerializer
|
||||
|
||||
@@ -63,7 +63,7 @@ logger = structlog.get_logger('inventree')
|
||||
|
||||
@register_importer()
|
||||
class CategorySerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeModelSerializer,
|
||||
):
|
||||
@@ -131,14 +131,14 @@ class CategorySerializer(
|
||||
"""Return True if the category is directly "starred" by the current user."""
|
||||
return category in self.context.get('starred_categories', [])
|
||||
|
||||
path = can_filter(
|
||||
CfListField(
|
||||
path = enable_filter(
|
||||
FilterableListField(
|
||||
child=serializers.DictField(),
|
||||
source='get_path',
|
||||
read_only=True,
|
||||
allow_null=True,
|
||||
),
|
||||
name='path_detail',
|
||||
filter_name='path_detail',
|
||||
)
|
||||
|
||||
icon = serializers.CharField(
|
||||
@@ -314,7 +314,7 @@ class PartParameterTemplateSerializer(
|
||||
|
||||
|
||||
class PartBriefSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeModelSerializer,
|
||||
):
|
||||
"""Serializer for Part (brief detail)."""
|
||||
@@ -374,25 +374,25 @@ class PartBriefSerializer(
|
||||
)
|
||||
|
||||
# Pricing fields
|
||||
pricing_min = can_filter(
|
||||
pricing_min = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
source='pricing_data.overall_min', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_max = can_filter(
|
||||
pricing_max = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
source='pricing_data.overall_max', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
|
||||
|
||||
@register_importer()
|
||||
class PartParameterSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeModelSerializer,
|
||||
):
|
||||
@@ -428,11 +428,11 @@ class PartParameterSerializer(
|
||||
|
||||
return instance
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
PartBriefSerializer(source='part', many=False, read_only=True, allow_null=True)
|
||||
)
|
||||
|
||||
template_detail = can_filter(
|
||||
template_detail = enable_filter(
|
||||
PartParameterTemplateSerializer(
|
||||
source='template', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
@@ -627,7 +627,7 @@ class DefaultLocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
|
||||
@register_importer()
|
||||
class PartSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.NotesFieldMixin,
|
||||
InvenTree.serializers.RemoteImageMixin,
|
||||
@@ -878,27 +878,27 @@ class PartSerializer(
|
||||
return part in self.starred_parts
|
||||
|
||||
# Extra detail for the category
|
||||
category_detail = can_filter(
|
||||
category_detail = enable_filter(
|
||||
CategorySerializer(
|
||||
source='category', many=False, read_only=True, allow_null=True
|
||||
)
|
||||
)
|
||||
|
||||
category_path = can_filter(
|
||||
CfListField(
|
||||
category_path = enable_filter(
|
||||
FilterableListField(
|
||||
child=serializers.DictField(),
|
||||
source='category.get_path',
|
||||
read_only=True,
|
||||
allow_null=True,
|
||||
),
|
||||
name='path_detail',
|
||||
filter_name='path_detail',
|
||||
)
|
||||
|
||||
default_location_detail = can_filter(
|
||||
default_location_detail = enable_filter(
|
||||
DefaultLocationSerializer(
|
||||
source='default_location', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
name='location_detail',
|
||||
filter_name='location_detail',
|
||||
)
|
||||
|
||||
category_name = serializers.CharField(
|
||||
@@ -1006,27 +1006,29 @@ class PartSerializer(
|
||||
)
|
||||
|
||||
# Pricing fields
|
||||
pricing_min = can_filter(
|
||||
pricing_min = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
source='pricing_data.overall_min', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_max = can_filter(
|
||||
pricing_max = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
source='pricing_data.overall_max', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_updated = can_filter(
|
||||
CfDateTimeField(source='pricing_data.updated', allow_null=True, read_only=True),
|
||||
pricing_updated = enable_filter(
|
||||
FilterableDateTimeField(
|
||||
source='pricing_data.updated', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
|
||||
parameters = can_filter(
|
||||
parameters = enable_filter(
|
||||
PartParameterSerializer(many=True, read_only=True, allow_null=True)
|
||||
)
|
||||
|
||||
@@ -1615,7 +1617,7 @@ class BomItemSubstituteSerializer(InvenTree.serializers.InvenTreeModelSerializer
|
||||
|
||||
@register_importer()
|
||||
class BomItemSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeModelSerializer,
|
||||
):
|
||||
@@ -1694,11 +1696,11 @@ class BomItemSerializer(
|
||||
help_text=_('Select the parent assembly'),
|
||||
)
|
||||
|
||||
substitutes = can_filter(
|
||||
substitutes = enable_filter(
|
||||
BomItemSubstituteSerializer(many=True, read_only=True, allow_null=True), True
|
||||
)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
PartBriefSerializer(
|
||||
source='part',
|
||||
label=_('Assembly'),
|
||||
@@ -1714,7 +1716,7 @@ class BomItemSerializer(
|
||||
help_text=_('Select the component part'),
|
||||
)
|
||||
|
||||
sub_part_detail = can_filter(
|
||||
sub_part_detail = enable_filter(
|
||||
PartBriefSerializer(
|
||||
source='sub_part',
|
||||
label=_('Component'),
|
||||
@@ -1733,41 +1735,42 @@ class BomItemSerializer(
|
||||
label=_('In Production'), read_only=True, allow_null=True
|
||||
)
|
||||
|
||||
can_build = can_filter(
|
||||
CfFloatField(label=_('Can Build'), read_only=True, allow_null=True), True
|
||||
can_build = enable_filter(
|
||||
FilterableFloatField(label=_('Can Build'), read_only=True, allow_null=True),
|
||||
True,
|
||||
)
|
||||
|
||||
# Cached pricing fields
|
||||
pricing_min = can_filter(
|
||||
pricing_min = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
source='sub_part.pricing_data.overall_min', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_max = can_filter(
|
||||
pricing_max = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(
|
||||
source='sub_part.pricing_data.overall_max', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_min_total = can_filter(
|
||||
pricing_min_total = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(allow_null=True, read_only=True),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_max_total = can_filter(
|
||||
pricing_max_total = enable_filter(
|
||||
InvenTree.serializers.InvenTreeMoneySerializer(allow_null=True, read_only=True),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
pricing_updated = can_filter(
|
||||
CfDateTimeField(
|
||||
pricing_updated = enable_filter(
|
||||
FilterableDateTimeField(
|
||||
source='sub_part.pricing_data.updated', allow_null=True, read_only=True
|
||||
),
|
||||
True,
|
||||
name='pricing',
|
||||
filter_name='pricing',
|
||||
)
|
||||
|
||||
# Annotated fields for available stock
|
||||
|
||||
@@ -33,10 +33,10 @@ from generic.states.fields import InvenTreeCustomStatusSerializerMixin
|
||||
from importer.registry import register_importer
|
||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||
from InvenTree.serializers import (
|
||||
CfListField,
|
||||
FilterableListField,
|
||||
InvenTreeCurrencySerializer,
|
||||
InvenTreeDecimalField,
|
||||
can_filter,
|
||||
enable_filter,
|
||||
)
|
||||
from users.serializers import UserSerializer
|
||||
|
||||
@@ -194,7 +194,7 @@ class LocationBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
|
||||
@register_importer()
|
||||
class StockItemTestResultSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeModelSerializer,
|
||||
):
|
||||
@@ -222,7 +222,7 @@ class StockItemTestResultSerializer(
|
||||
]
|
||||
read_only_fields = ['pk', 'user', 'date']
|
||||
|
||||
user_detail = can_filter(
|
||||
user_detail = enable_filter(
|
||||
UserSerializer(source='user', read_only=True, allow_null=True)
|
||||
)
|
||||
|
||||
@@ -235,7 +235,7 @@ class StockItemTestResultSerializer(
|
||||
label=_('Test template for this result'),
|
||||
)
|
||||
|
||||
template_detail = can_filter(
|
||||
template_detail = enable_filter(
|
||||
part_serializers.PartTestTemplateSerializer(
|
||||
source='template', read_only=True, allow_null=True
|
||||
)
|
||||
@@ -297,7 +297,7 @@ class StockItemTestResultSerializer(
|
||||
|
||||
@register_importer()
|
||||
class StockItemSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTreeCustomStatusSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeTagModelSerializer,
|
||||
@@ -415,14 +415,14 @@ class StockItemSerializer(
|
||||
help_text=_('Parent stock item'),
|
||||
)
|
||||
|
||||
location_path = can_filter(
|
||||
CfListField(
|
||||
location_path = enable_filter(
|
||||
FilterableListField(
|
||||
child=serializers.DictField(),
|
||||
source='location.get_path',
|
||||
read_only=True,
|
||||
allow_null=True,
|
||||
),
|
||||
name='path_detail',
|
||||
filter_name='path_detail',
|
||||
)
|
||||
|
||||
in_stock = serializers.BooleanField(read_only=True, label=_('In Stock'))
|
||||
@@ -574,7 +574,7 @@ class StockItemSerializer(
|
||||
)
|
||||
|
||||
# Optional detail fields, which can be appended via query parameters
|
||||
supplier_part_detail = can_filter(
|
||||
supplier_part_detail = enable_filter(
|
||||
company_serializers.SupplierPartSerializer(
|
||||
label=_('Supplier Part'),
|
||||
source='supplier_part',
|
||||
@@ -589,14 +589,14 @@ class StockItemSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
part_detail = can_filter(
|
||||
part_detail = enable_filter(
|
||||
part_serializers.PartBriefSerializer(
|
||||
label=_('Part'), source='part', many=False, read_only=True, allow_null=True
|
||||
),
|
||||
True,
|
||||
)
|
||||
|
||||
location_detail = can_filter(
|
||||
location_detail = enable_filter(
|
||||
LocationBriefSerializer(
|
||||
label=_('Location'),
|
||||
source='location',
|
||||
@@ -607,7 +607,7 @@ class StockItemSerializer(
|
||||
True,
|
||||
)
|
||||
|
||||
tests = can_filter(
|
||||
tests = enable_filter(
|
||||
StockItemTestResultSerializer(
|
||||
source='test_results', many=True, read_only=True, allow_null=True
|
||||
)
|
||||
@@ -1122,7 +1122,7 @@ class LocationTreeSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||
|
||||
@register_importer()
|
||||
class LocationSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeTagModelSerializer,
|
||||
):
|
||||
@@ -1187,14 +1187,14 @@ class LocationSerializer(
|
||||
|
||||
tags = TagListSerializerField(required=False)
|
||||
|
||||
path = can_filter(
|
||||
CfListField(
|
||||
path = enable_filter(
|
||||
FilterableListField(
|
||||
child=serializers.DictField(),
|
||||
source='get_path',
|
||||
read_only=True,
|
||||
allow_null=True,
|
||||
),
|
||||
name='path_detail',
|
||||
filter_name='path_detail',
|
||||
)
|
||||
|
||||
# explicitly set this field, so it gets included for AutoSchema
|
||||
@@ -1208,7 +1208,7 @@ class LocationSerializer(
|
||||
|
||||
@register_importer()
|
||||
class StockTrackingSerializer(
|
||||
InvenTree.serializers.PathScopedMixin,
|
||||
InvenTree.serializers.FilterableSerializerMixin,
|
||||
DataImportExportSerializerMixin,
|
||||
InvenTree.serializers.InvenTreeModelSerializer,
|
||||
):
|
||||
@@ -1234,11 +1234,11 @@ class StockTrackingSerializer(
|
||||
|
||||
label = serializers.CharField(read_only=True)
|
||||
|
||||
item_detail = can_filter(
|
||||
item_detail = enable_filter(
|
||||
StockItemSerializer(source='item', many=False, read_only=True, allow_null=True)
|
||||
)
|
||||
|
||||
user_detail = can_filter(
|
||||
user_detail = enable_filter(
|
||||
UserSerializer(source='user', many=False, read_only=True, allow_null=True)
|
||||
)
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ from rest_framework import serializers
|
||||
from rest_framework.exceptions import PermissionDenied
|
||||
|
||||
from InvenTree.serializers import (
|
||||
CfSerializerMethodField,
|
||||
FilterableListSerializer,
|
||||
FilterableSerializerMethodField,
|
||||
FilterableSerializerMixin,
|
||||
InvenTreeModelSerializer,
|
||||
PathScopedMixin,
|
||||
can_filter,
|
||||
enable_filter,
|
||||
)
|
||||
|
||||
from .models import ApiToken, Owner, RuleSet, UserProfile
|
||||
@@ -239,7 +239,7 @@ class ApiTokenSerializer(InvenTreeModelSerializer):
|
||||
user_detail = UserSerializer(source='user', read_only=True)
|
||||
|
||||
|
||||
class GroupSerializer(PathScopedMixin, InvenTreeModelSerializer):
|
||||
class GroupSerializer(FilterableSerializerMixin, InvenTreeModelSerializer):
|
||||
"""Serializer for a 'Group'."""
|
||||
|
||||
class Meta:
|
||||
@@ -248,25 +248,25 @@ class GroupSerializer(PathScopedMixin, InvenTreeModelSerializer):
|
||||
model = Group
|
||||
fields = ['pk', 'name', 'permissions', 'roles', 'users']
|
||||
|
||||
permissions = can_filter(
|
||||
CfSerializerMethodField(allow_null=True, read_only=True),
|
||||
name='permission_detail',
|
||||
permissions = enable_filter(
|
||||
FilterableSerializerMethodField(allow_null=True, read_only=True),
|
||||
filter_name='permission_detail',
|
||||
)
|
||||
|
||||
def get_permissions(self, group: Group) -> dict:
|
||||
"""Return a list of permissions associated with the group."""
|
||||
return generate_permission_dict(group.permissions.all())
|
||||
|
||||
roles = can_filter(
|
||||
roles = enable_filter(
|
||||
RuleSetSerializer(
|
||||
source='rule_sets', many=True, read_only=True, allow_null=True
|
||||
),
|
||||
name='role_detail',
|
||||
filter_name='role_detail',
|
||||
)
|
||||
|
||||
users = can_filter(
|
||||
users = enable_filter(
|
||||
UserSerializer(source='user_set', many=True, read_only=True, allow_null=True),
|
||||
name='user_detail',
|
||||
filter_name='user_detail',
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user