mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Move Meta class to top of class definition (#4363)
This commit is contained in:
@ -16,6 +16,20 @@ from stock.models import StockLocation
|
||||
class PartResource(InvenTreeResource):
|
||||
"""Class for managing Part data import/export."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.Part
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
clean_model_instances = True
|
||||
exclude = [
|
||||
'bom_checksum', 'bom_checked_by', 'bom_checked_date',
|
||||
'lft', 'rght', 'tree_id', 'level',
|
||||
'image',
|
||||
'metadata',
|
||||
'barcode_data', 'barcode_hash',
|
||||
]
|
||||
|
||||
id = Field(attribute='pk', column_name=_('Part ID'), widget=widgets.IntegerWidget())
|
||||
name = Field(attribute='name', column_name=_('Part Name'), widget=widgets.CharWidget())
|
||||
description = Field(attribute='description', column_name=_('Part Description'), widget=widgets.CharWidget())
|
||||
@ -68,20 +82,6 @@ class PartResource(InvenTreeResource):
|
||||
if max_cost is not None:
|
||||
return float(max_cost.amount)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.Part
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
clean_model_instances = True
|
||||
exclude = [
|
||||
'bom_checksum', 'bom_checked_by', 'bom_checked_date',
|
||||
'lft', 'rght', 'tree_id', 'level',
|
||||
'image',
|
||||
'metadata',
|
||||
'barcode_data', 'barcode_hash',
|
||||
]
|
||||
|
||||
def get_queryset(self):
|
||||
"""Prefetch related data for quicker access."""
|
||||
query = super().get_queryset()
|
||||
@ -175,18 +175,6 @@ class PartStocktakeReportAdmin(admin.ModelAdmin):
|
||||
class PartCategoryResource(InvenTreeResource):
|
||||
"""Class for managing PartCategory data import/export."""
|
||||
|
||||
id = Field(attribute='pk', column_name=_('Category ID'))
|
||||
name = Field(attribute='name', column_name=_('Category Name'))
|
||||
description = Field(attribute='description', column_name=_('Description'))
|
||||
parent = Field(attribute='parent', column_name=_('Parent ID'), widget=widgets.ForeignKeyWidget(models.PartCategory))
|
||||
parent_name = Field(attribute='parent__name', column_name=_('Parent Name'), readonly=True)
|
||||
default_location = Field(attribute='default_location', column_name=_('Default Location ID'), widget=widgets.ForeignKeyWidget(StockLocation))
|
||||
default_keywords = Field(attribute='default_keywords', column_name=_('Keywords'))
|
||||
pathstring = Field(attribute='pathstring', column_name=_('Category Path'))
|
||||
|
||||
# Calculated fields
|
||||
parts = Field(attribute='item_count', column_name=_('Parts'), widget=widgets.IntegerWidget(), readonly=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.PartCategory
|
||||
@ -201,6 +189,18 @@ class PartCategoryResource(InvenTreeResource):
|
||||
'icon',
|
||||
]
|
||||
|
||||
id = Field(attribute='pk', column_name=_('Category ID'))
|
||||
name = Field(attribute='name', column_name=_('Category Name'))
|
||||
description = Field(attribute='description', column_name=_('Description'))
|
||||
parent = Field(attribute='parent', column_name=_('Parent ID'), widget=widgets.ForeignKeyWidget(models.PartCategory))
|
||||
parent_name = Field(attribute='parent__name', column_name=_('Parent Name'), readonly=True)
|
||||
default_location = Field(attribute='default_location', column_name=_('Default Location ID'), widget=widgets.ForeignKeyWidget(StockLocation))
|
||||
default_keywords = Field(attribute='default_keywords', column_name=_('Keywords'))
|
||||
pathstring = Field(attribute='pathstring', column_name=_('Category Path'))
|
||||
|
||||
# Calculated fields
|
||||
parts = Field(attribute='item_count', column_name=_('Parts'), widget=widgets.IntegerWidget(), readonly=True)
|
||||
|
||||
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
|
||||
"""Rebuild MPTT tree structure after importing PartCategory data"""
|
||||
|
||||
@ -247,6 +247,20 @@ class PartTestTemplateAdmin(admin.ModelAdmin):
|
||||
class BomItemResource(InvenTreeResource):
|
||||
"""Class for managing BomItem data import/export."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.BomItem
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
clean_model_instances = True
|
||||
|
||||
exclude = [
|
||||
'checksum',
|
||||
'id',
|
||||
'part',
|
||||
'sub_part',
|
||||
]
|
||||
|
||||
level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)
|
||||
|
||||
bom_id = Field(attribute='pk', column_name=_('BOM Item ID'))
|
||||
@ -335,20 +349,6 @@ class BomItemResource(InvenTreeResource):
|
||||
|
||||
return fields
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.BomItem
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
clean_model_instances = True
|
||||
|
||||
exclude = [
|
||||
'checksum',
|
||||
'id',
|
||||
'part',
|
||||
'sub_part',
|
||||
]
|
||||
|
||||
|
||||
class BomItemAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the BomItem model"""
|
||||
@ -373,6 +373,13 @@ class ParameterTemplateAdmin(ImportExportModelAdmin):
|
||||
class ParameterResource(InvenTreeResource):
|
||||
"""Class for managing PartParameter data import/export."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.PartParameter
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
clean_model_instance = True
|
||||
|
||||
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(models.Part))
|
||||
|
||||
part_name = Field(attribute='part__name', readonly=True)
|
||||
@ -381,13 +388,6 @@ class ParameterResource(InvenTreeResource):
|
||||
|
||||
template_name = Field(attribute='template__name', readonly=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass definition"""
|
||||
model = models.PartParameter
|
||||
skip_unchanged = True
|
||||
report_skipped = False
|
||||
clean_model_instance = True
|
||||
|
||||
|
||||
class ParameterAdmin(ImportExportModelAdmin):
|
||||
"""Admin class for the PartParameter model"""
|
||||
|
@ -34,16 +34,16 @@ class BomMatchItemForm(MatchItemForm):
|
||||
class PartPriceForm(forms.Form):
|
||||
"""Simple form for viewing part pricing information."""
|
||||
|
||||
quantity = forms.IntegerField(
|
||||
required=True,
|
||||
initial=1,
|
||||
label=_('Quantity'),
|
||||
help_text=_('Input quantity for price calculation')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines fields for this form"""
|
||||
model = Part
|
||||
fields = [
|
||||
'quantity',
|
||||
]
|
||||
|
||||
quantity = forms.IntegerField(
|
||||
required=True,
|
||||
initial=1,
|
||||
label=_('Quantity'),
|
||||
help_text=_('Input quantity for price calculation')
|
||||
)
|
||||
|
@ -3045,6 +3045,10 @@ class PartAttachment(InvenTreeAttachment):
|
||||
class PartSellPriceBreak(common.models.PriceBreak):
|
||||
"""Represents a price break for selling this part."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
unique_together = ('part', 'quantity')
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the list API endpoint URL associated with the PartSellPriceBreak model"""
|
||||
@ -3057,14 +3061,14 @@ class PartSellPriceBreak(common.models.PriceBreak):
|
||||
verbose_name=_('Part')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
unique_together = ('part', 'quantity')
|
||||
|
||||
|
||||
class PartInternalPriceBreak(common.models.PriceBreak):
|
||||
"""Represents a price break for internally selling this part."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
unique_together = ('part', 'quantity')
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the list API endpoint URL associated with the PartInternalPriceBreak model"""
|
||||
@ -3076,10 +3080,6 @@ class PartInternalPriceBreak(common.models.PriceBreak):
|
||||
verbose_name=_('Part')
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
unique_together = ('part', 'quantity')
|
||||
|
||||
|
||||
class PartStar(models.Model):
|
||||
"""A PartStar object creates a subscription relationship between a User and a Part.
|
||||
@ -3091,10 +3091,6 @@ class PartStar(models.Model):
|
||||
user: Link to a User object
|
||||
"""
|
||||
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, verbose_name=_('Part'), related_name='starred_users')
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('User'), related_name='starred_parts')
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
unique_together = [
|
||||
@ -3102,6 +3098,10 @@ class PartStar(models.Model):
|
||||
'user'
|
||||
]
|
||||
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, verbose_name=_('Part'), related_name='starred_users')
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('User'), related_name='starred_parts')
|
||||
|
||||
|
||||
class PartCategoryStar(models.Model):
|
||||
"""A PartCategoryStar creates a subscription relationship between a User and a PartCategory.
|
||||
@ -3111,10 +3111,6 @@ class PartCategoryStar(models.Model):
|
||||
user: Link to a User object
|
||||
"""
|
||||
|
||||
category = models.ForeignKey(PartCategory, on_delete=models.CASCADE, verbose_name=_('Category'), related_name='starred_users')
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('User'), related_name='starred_categories')
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
unique_together = [
|
||||
@ -3122,6 +3118,10 @@ class PartCategoryStar(models.Model):
|
||||
'user',
|
||||
]
|
||||
|
||||
category = models.ForeignKey(PartCategory, on_delete=models.CASCADE, verbose_name=_('Category'), related_name='starred_users')
|
||||
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name=_('User'), related_name='starred_categories')
|
||||
|
||||
|
||||
class PartTestTemplate(models.Model):
|
||||
"""A PartTestTemplate defines a 'template' for a test which is required to be run against a StockItem (an instance of the Part).
|
||||
@ -3292,6 +3292,11 @@ class PartParameter(models.Model):
|
||||
data: The data (value) of the Parameter [string]
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
# Prevent multiple instances of a parameter for a single part
|
||||
unique_together = ('part', 'template')
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the list API endpoint URL associated with the PartParameter model"""
|
||||
@ -3306,11 +3311,6 @@ class PartParameter(models.Model):
|
||||
units=str(self.template.units)
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
# Prevent multiple instances of a parameter for a single part
|
||||
unique_together = ('part', 'template')
|
||||
|
||||
part = models.ForeignKey(Part, on_delete=models.CASCADE, related_name='parameters', verbose_name=_('Part'), help_text=_('Parent Part'))
|
||||
|
||||
template = models.ForeignKey(PartParameterTemplate, on_delete=models.CASCADE, related_name='instances', verbose_name=_('Template'), help_text=_('Parameter Template'))
|
||||
@ -3424,6 +3424,17 @@ class BomItem(DataImportMixin, models.Model):
|
||||
}
|
||||
}
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
verbose_name = _("BOM Item")
|
||||
|
||||
def __str__(self):
|
||||
"""Return a string representation of this BomItem instance"""
|
||||
return "{n} x {child} to make {parent}".format(
|
||||
parent=self.part.full_name,
|
||||
child=self.sub_part.full_name,
|
||||
n=decimal2string(self.quantity))
|
||||
|
||||
@staticmethod
|
||||
def get_api_url():
|
||||
"""Return the list API endpoint URL associated with the BomItem model"""
|
||||
@ -3638,17 +3649,6 @@ class BomItem(DataImportMixin, models.Model):
|
||||
except Part.DoesNotExist:
|
||||
raise ValidationError({'sub_part': _('Sub part must be specified')})
|
||||
|
||||
class Meta:
|
||||
"""Metaclass providing extra model definition"""
|
||||
verbose_name = _("BOM Item")
|
||||
|
||||
def __str__(self):
|
||||
"""Return a string representation of this BomItem instance"""
|
||||
return "{n} x {child} to make {parent}".format(
|
||||
parent=self.part.full_name,
|
||||
child=self.sub_part.full_name,
|
||||
n=decimal2string(self.quantity))
|
||||
|
||||
def get_overage_quantity(self, quantity):
|
||||
"""Calculate overage quantity."""
|
||||
# Most of the time overage string will be empty
|
||||
|
@ -46,6 +46,25 @@ from .models import (BomItem, BomItemSubstitute, Part, PartAttachment,
|
||||
class CategorySerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for PartCategory."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartCategory
|
||||
fields = [
|
||||
'pk',
|
||||
'name',
|
||||
'description',
|
||||
'default_location',
|
||||
'default_keywords',
|
||||
'level',
|
||||
'parent',
|
||||
'part_count',
|
||||
'pathstring',
|
||||
'starred',
|
||||
'url',
|
||||
'structural',
|
||||
'icon',
|
||||
]
|
||||
|
||||
def get_starred(self, category):
|
||||
"""Return True if the category is directly "starred" by the current user."""
|
||||
return category in self.context.get('starred_categories', [])
|
||||
@ -69,25 +88,6 @@ class CategorySerializer(InvenTreeModelSerializer):
|
||||
|
||||
starred = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartCategory
|
||||
fields = [
|
||||
'pk',
|
||||
'name',
|
||||
'description',
|
||||
'default_location',
|
||||
'default_keywords',
|
||||
'level',
|
||||
'parent',
|
||||
'part_count',
|
||||
'pathstring',
|
||||
'starred',
|
||||
'url',
|
||||
'structural',
|
||||
'icon',
|
||||
]
|
||||
|
||||
|
||||
class CategoryTree(InvenTreeModelSerializer):
|
||||
"""Serializer for PartCategory tree."""
|
||||
@ -118,8 +118,6 @@ class PartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||
class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the PartTestTemplate class."""
|
||||
|
||||
key = serializers.CharField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartTestTemplate
|
||||
@ -135,16 +133,12 @@ class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
||||
'requires_attachment',
|
||||
]
|
||||
|
||||
key = serializers.CharField(read_only=True)
|
||||
|
||||
|
||||
class PartSalePriceSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for sale prices for Part model."""
|
||||
|
||||
quantity = InvenTreeDecimalField()
|
||||
|
||||
price = InvenTreeMoneySerializer(allow_null=True)
|
||||
|
||||
price_currency = InvenTreeCurrencySerializer(help_text=_('Purchase currency of this stock item'))
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartSellPriceBreak
|
||||
@ -156,18 +150,16 @@ class PartSalePriceSerializer(InvenTreeModelSerializer):
|
||||
'price_currency',
|
||||
]
|
||||
|
||||
quantity = InvenTreeDecimalField()
|
||||
|
||||
price = InvenTreeMoneySerializer(allow_null=True)
|
||||
|
||||
price_currency = InvenTreeCurrencySerializer(help_text=_('Purchase currency of this stock item'))
|
||||
|
||||
|
||||
class PartInternalPriceSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for internal prices for Part model."""
|
||||
|
||||
quantity = InvenTreeDecimalField()
|
||||
|
||||
price = InvenTreeMoneySerializer(
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
price_currency = InvenTreeCurrencySerializer(help_text=_('Purchase currency of this stock item'))
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartInternalPriceBreak
|
||||
@ -179,6 +171,14 @@ class PartInternalPriceSerializer(InvenTreeModelSerializer):
|
||||
'price_currency',
|
||||
]
|
||||
|
||||
quantity = InvenTreeDecimalField()
|
||||
|
||||
price = InvenTreeMoneySerializer(
|
||||
allow_null=True
|
||||
)
|
||||
|
||||
price_currency = InvenTreeCurrencySerializer(help_text=_('Purchase currency of this stock item'))
|
||||
|
||||
|
||||
class PartThumbSerializer(serializers.Serializer):
|
||||
"""Serializer for the 'image' field of the Part model.
|
||||
@ -193,6 +193,13 @@ class PartThumbSerializer(serializers.Serializer):
|
||||
class PartThumbSerializerUpdate(InvenTreeModelSerializer):
|
||||
"""Serializer for updating Part thumbnail."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = Part
|
||||
fields = [
|
||||
'image',
|
||||
]
|
||||
|
||||
def validate_image(self, value):
|
||||
"""Check that file is an image."""
|
||||
validate = imghdr.what(value)
|
||||
@ -202,13 +209,6 @@ class PartThumbSerializerUpdate(InvenTreeModelSerializer):
|
||||
|
||||
image = InvenTreeAttachmentSerializerField(required=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = Part
|
||||
fields = [
|
||||
'image',
|
||||
]
|
||||
|
||||
|
||||
class PartParameterTemplateSerializer(InvenTreeModelSerializer):
|
||||
"""JSON serializer for the PartParameterTemplate model."""
|
||||
@ -227,6 +227,17 @@ class PartParameterTemplateSerializer(InvenTreeModelSerializer):
|
||||
class PartParameterSerializer(InvenTreeModelSerializer):
|
||||
"""JSON serializers for the PartParameter model."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartParameter
|
||||
fields = [
|
||||
'pk',
|
||||
'part',
|
||||
'template',
|
||||
'template_detail',
|
||||
'data'
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Custom initialization method for the serializer.
|
||||
|
||||
@ -242,23 +253,10 @@ class PartParameterSerializer(InvenTreeModelSerializer):
|
||||
|
||||
template_detail = PartParameterTemplateSerializer(source='template', many=False, read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartParameter
|
||||
fields = [
|
||||
'pk',
|
||||
'part',
|
||||
'template',
|
||||
'template_detail',
|
||||
'data'
|
||||
]
|
||||
|
||||
|
||||
class PartBriefSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for Part (brief detail)"""
|
||||
|
||||
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = Part
|
||||
@ -286,6 +284,8 @@ class PartBriefSerializer(InvenTreeModelSerializer):
|
||||
'barcode_hash',
|
||||
]
|
||||
|
||||
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||
|
||||
|
||||
class DuplicatePartSerializer(serializers.Serializer):
|
||||
"""Serializer for specifying options when duplicating a Part.
|
||||
@ -400,22 +400,65 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
||||
Used when displaying all details of a single component.
|
||||
"""
|
||||
|
||||
def get_api_url(self):
|
||||
"""Return the API url associated with this serializer"""
|
||||
return reverse_lazy('api-part-list')
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = Part
|
||||
partial = True
|
||||
fields = [
|
||||
'active',
|
||||
'allocated_to_build_orders',
|
||||
'allocated_to_sales_orders',
|
||||
'assembly',
|
||||
'barcode_hash',
|
||||
'category',
|
||||
'category_detail',
|
||||
'component',
|
||||
'default_expiry',
|
||||
'default_location',
|
||||
'default_supplier',
|
||||
'description',
|
||||
'full_name',
|
||||
'image',
|
||||
'in_stock',
|
||||
'variant_stock',
|
||||
'ordering',
|
||||
'building',
|
||||
'IPN',
|
||||
'is_template',
|
||||
'keywords',
|
||||
'last_stocktake',
|
||||
'link',
|
||||
'minimum_stock',
|
||||
'name',
|
||||
'notes',
|
||||
'parameters',
|
||||
'pk',
|
||||
'purchaseable',
|
||||
'remote_image',
|
||||
'revision',
|
||||
'salable',
|
||||
'starred',
|
||||
'stock_item_count',
|
||||
'suppliers',
|
||||
'thumbnail',
|
||||
'trackable',
|
||||
'unallocated_stock',
|
||||
'units',
|
||||
'variant_of',
|
||||
'virtual',
|
||||
'pricing_min',
|
||||
'pricing_max',
|
||||
'responsible',
|
||||
|
||||
def skip_create_fields(self):
|
||||
"""Skip these fields when instantiating a new Part instance"""
|
||||
|
||||
fields = super().skip_create_fields()
|
||||
|
||||
fields += [
|
||||
# Fields only used for Part creation
|
||||
'duplicate',
|
||||
'initial_stock',
|
||||
'initial_supplier',
|
||||
]
|
||||
|
||||
return fields
|
||||
read_only_fields = [
|
||||
'barcode_hash',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Custom initialization method for PartSerializer:
|
||||
@ -443,6 +486,23 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
||||
for f in self.skip_create_fields()[1:]:
|
||||
self.fields.pop(f)
|
||||
|
||||
def get_api_url(self):
|
||||
"""Return the API url associated with this serializer"""
|
||||
return reverse_lazy('api-part-list')
|
||||
|
||||
def skip_create_fields(self):
|
||||
"""Skip these fields when instantiating a new Part instance"""
|
||||
|
||||
fields = super().skip_create_fields()
|
||||
|
||||
fields += [
|
||||
'duplicate',
|
||||
'initial_stock',
|
||||
'initial_supplier',
|
||||
]
|
||||
|
||||
return fields
|
||||
|
||||
@staticmethod
|
||||
def annotate_queryset(queryset):
|
||||
"""Add some extra annotations to the queryset.
|
||||
@ -553,66 +613,6 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
||||
write_only=True, required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = Part
|
||||
partial = True
|
||||
fields = [
|
||||
'active',
|
||||
'allocated_to_build_orders',
|
||||
'allocated_to_sales_orders',
|
||||
'assembly',
|
||||
'barcode_hash',
|
||||
'category',
|
||||
'category_detail',
|
||||
'component',
|
||||
'default_expiry',
|
||||
'default_location',
|
||||
'default_supplier',
|
||||
'description',
|
||||
'full_name',
|
||||
'image',
|
||||
'in_stock',
|
||||
'variant_stock',
|
||||
'ordering',
|
||||
'building',
|
||||
'IPN',
|
||||
'is_template',
|
||||
'keywords',
|
||||
'last_stocktake',
|
||||
'link',
|
||||
'minimum_stock',
|
||||
'name',
|
||||
'notes',
|
||||
'parameters',
|
||||
'pk',
|
||||
'purchaseable',
|
||||
'remote_image',
|
||||
'revision',
|
||||
'salable',
|
||||
'starred',
|
||||
'stock_item_count',
|
||||
'suppliers',
|
||||
'thumbnail',
|
||||
'trackable',
|
||||
'unallocated_stock',
|
||||
'units',
|
||||
'variant_of',
|
||||
'virtual',
|
||||
'pricing_min',
|
||||
'pricing_max',
|
||||
'responsible',
|
||||
|
||||
# Fields only used for Part creation
|
||||
'duplicate',
|
||||
'initial_stock',
|
||||
'initial_supplier',
|
||||
]
|
||||
|
||||
read_only_fields = [
|
||||
'barcode_hash',
|
||||
]
|
||||
|
||||
@transaction.atomic
|
||||
def create(self, validated_data):
|
||||
"""Custom method for creating a new Part instance using this serializer"""
|
||||
@ -708,16 +708,6 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
||||
class PartStocktakeSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the PartStocktake model"""
|
||||
|
||||
quantity = serializers.FloatField()
|
||||
|
||||
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
||||
|
||||
cost_min = InvenTreeMoneySerializer(allow_null=True)
|
||||
cost_min_currency = InvenTreeCurrencySerializer()
|
||||
|
||||
cost_max = InvenTreeMoneySerializer(allow_null=True)
|
||||
cost_max_currency = InvenTreeCurrencySerializer()
|
||||
|
||||
class Meta:
|
||||
"""Metaclass options"""
|
||||
|
||||
@ -742,6 +732,16 @@ class PartStocktakeSerializer(InvenTreeModelSerializer):
|
||||
'user',
|
||||
]
|
||||
|
||||
quantity = serializers.FloatField()
|
||||
|
||||
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
||||
|
||||
cost_min = InvenTreeMoneySerializer(allow_null=True)
|
||||
cost_min_currency = InvenTreeCurrencySerializer()
|
||||
|
||||
cost_max = InvenTreeMoneySerializer(allow_null=True)
|
||||
cost_max_currency = InvenTreeCurrencySerializer()
|
||||
|
||||
def save(self):
|
||||
"""Called when this serializer is saved"""
|
||||
|
||||
@ -757,10 +757,6 @@ class PartStocktakeSerializer(InvenTreeModelSerializer):
|
||||
class PartStocktakeReportSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for stocktake report class"""
|
||||
|
||||
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
||||
|
||||
report = InvenTreeAttachmentSerializerField(read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defines serializer fields"""
|
||||
|
||||
@ -774,6 +770,10 @@ class PartStocktakeReportSerializer(InvenTreeModelSerializer):
|
||||
'user_detail',
|
||||
]
|
||||
|
||||
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
||||
|
||||
report = InvenTreeAttachmentSerializerField(read_only=True)
|
||||
|
||||
|
||||
class PartStocktakeReportGenerateSerializer(serializers.Serializer):
|
||||
"""Serializer class for manually generating a new PartStocktakeReport via the API"""
|
||||
@ -843,6 +843,32 @@ class PartStocktakeReportGenerateSerializer(serializers.Serializer):
|
||||
class PartPricingSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for Part pricing information"""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartPricing
|
||||
fields = [
|
||||
'currency',
|
||||
'updated',
|
||||
'scheduled_for_update',
|
||||
'bom_cost_min',
|
||||
'bom_cost_max',
|
||||
'purchase_cost_min',
|
||||
'purchase_cost_max',
|
||||
'internal_cost_min',
|
||||
'internal_cost_max',
|
||||
'supplier_price_min',
|
||||
'supplier_price_max',
|
||||
'variant_cost_min',
|
||||
'variant_cost_max',
|
||||
'overall_min',
|
||||
'overall_max',
|
||||
'sale_price_min',
|
||||
'sale_price_max',
|
||||
'sale_history_min',
|
||||
'sale_history_max',
|
||||
'update',
|
||||
]
|
||||
|
||||
currency = serializers.CharField(allow_null=True, read_only=True)
|
||||
|
||||
updated = serializers.DateTimeField(allow_null=True, read_only=True)
|
||||
@ -882,32 +908,6 @@ class PartPricingSerializer(InvenTreeModelSerializer):
|
||||
required=False,
|
||||
)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartPricing
|
||||
fields = [
|
||||
'currency',
|
||||
'updated',
|
||||
'scheduled_for_update',
|
||||
'bom_cost_min',
|
||||
'bom_cost_max',
|
||||
'purchase_cost_min',
|
||||
'purchase_cost_max',
|
||||
'internal_cost_min',
|
||||
'internal_cost_max',
|
||||
'supplier_price_min',
|
||||
'supplier_price_max',
|
||||
'variant_cost_min',
|
||||
'variant_cost_max',
|
||||
'overall_min',
|
||||
'overall_max',
|
||||
'sale_price_min',
|
||||
'sale_price_max',
|
||||
'sale_history_min',
|
||||
'sale_history_max',
|
||||
'update',
|
||||
]
|
||||
|
||||
def save(self):
|
||||
"""Called when the serializer is saved"""
|
||||
data = self.validated_data
|
||||
@ -921,9 +921,6 @@ class PartPricingSerializer(InvenTreeModelSerializer):
|
||||
class PartRelationSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for a PartRelated model."""
|
||||
|
||||
part_1_detail = PartSerializer(source='part_1', read_only=True, many=False)
|
||||
part_2_detail = PartSerializer(source='part_2', read_only=True, many=False)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartRelated
|
||||
@ -935,13 +932,13 @@ class PartRelationSerializer(InvenTreeModelSerializer):
|
||||
'part_2_detail',
|
||||
]
|
||||
|
||||
part_1_detail = PartSerializer(source='part_1', read_only=True, many=False)
|
||||
part_2_detail = PartSerializer(source='part_2', read_only=True, many=False)
|
||||
|
||||
|
||||
class PartStarSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for a PartStar object."""
|
||||
|
||||
partname = serializers.CharField(source='part.full_name', read_only=True)
|
||||
username = serializers.CharField(source='user.username', read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartStar
|
||||
@ -953,12 +950,13 @@ class PartStarSerializer(InvenTreeModelSerializer):
|
||||
'username',
|
||||
]
|
||||
|
||||
partname = serializers.CharField(source='part.full_name', read_only=True)
|
||||
username = serializers.CharField(source='user.username', read_only=True)
|
||||
|
||||
|
||||
class BomItemSubstituteSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the BomItemSubstitute class."""
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', read_only=True, many=False)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = BomItemSubstitute
|
||||
@ -969,10 +967,60 @@ class BomItemSubstituteSerializer(InvenTreeModelSerializer):
|
||||
'part_detail',
|
||||
]
|
||||
|
||||
part_detail = PartBriefSerializer(source='part', read_only=True, many=False)
|
||||
|
||||
|
||||
class BomItemSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for BomItem object."""
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = BomItem
|
||||
fields = [
|
||||
'allow_variants',
|
||||
'inherited',
|
||||
'note',
|
||||
'optional',
|
||||
'consumable',
|
||||
'overage',
|
||||
'pk',
|
||||
'part',
|
||||
'part_detail',
|
||||
'pricing_min',
|
||||
'pricing_max',
|
||||
'quantity',
|
||||
'reference',
|
||||
'sub_part',
|
||||
'sub_part_detail',
|
||||
'substitutes',
|
||||
'validated',
|
||||
|
||||
# Annotated fields describing available quantity
|
||||
'available_stock',
|
||||
'available_substitute_stock',
|
||||
'available_variant_stock',
|
||||
|
||||
# Annotated field describing quantity on order
|
||||
'on_order',
|
||||
]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Determine if extra detail fields are to be annotated on this serializer
|
||||
|
||||
- part_detail and sub_part_detail serializers are only included if requested.
|
||||
- This saves a bunch of database requests
|
||||
"""
|
||||
part_detail = kwargs.pop('part_detail', False)
|
||||
sub_part_detail = kwargs.pop('sub_part_detail', False)
|
||||
|
||||
super(BomItemSerializer, self).__init__(*args, **kwargs)
|
||||
|
||||
if part_detail is not True:
|
||||
self.fields.pop('part_detail')
|
||||
|
||||
if sub_part_detail is not True:
|
||||
self.fields.pop('sub_part_detail')
|
||||
|
||||
quantity = InvenTreeDecimalField(required=True)
|
||||
|
||||
def validate_quantity(self, quantity):
|
||||
@ -1005,23 +1053,6 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
||||
available_substitute_stock = serializers.FloatField(read_only=True)
|
||||
available_variant_stock = serializers.FloatField(read_only=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Determine if extra detail fields are to be annotated on this serializer
|
||||
|
||||
- part_detail and sub_part_detail serializers are only included if requested.
|
||||
- This saves a bunch of database requests
|
||||
"""
|
||||
part_detail = kwargs.pop('part_detail', False)
|
||||
sub_part_detail = kwargs.pop('sub_part_detail', False)
|
||||
|
||||
super(BomItemSerializer, self).__init__(*args, **kwargs)
|
||||
|
||||
if part_detail is not True:
|
||||
self.fields.pop('part_detail')
|
||||
|
||||
if sub_part_detail is not True:
|
||||
self.fields.pop('sub_part_detail')
|
||||
|
||||
@staticmethod
|
||||
def setup_eager_loading(queryset):
|
||||
"""Prefetch against the provided queryset to speed up database access"""
|
||||
@ -1116,45 +1147,10 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
||||
|
||||
return queryset
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = BomItem
|
||||
fields = [
|
||||
'allow_variants',
|
||||
'inherited',
|
||||
'note',
|
||||
'optional',
|
||||
'consumable',
|
||||
'overage',
|
||||
'pk',
|
||||
'part',
|
||||
'part_detail',
|
||||
'pricing_min',
|
||||
'pricing_max',
|
||||
'quantity',
|
||||
'reference',
|
||||
'sub_part',
|
||||
'sub_part_detail',
|
||||
'substitutes',
|
||||
'validated',
|
||||
|
||||
# Annotated fields describing available quantity
|
||||
'available_stock',
|
||||
'available_substitute_stock',
|
||||
'available_variant_stock',
|
||||
|
||||
# Annotated field describing quantity on order
|
||||
'on_order',
|
||||
]
|
||||
|
||||
|
||||
class CategoryParameterTemplateSerializer(InvenTreeModelSerializer):
|
||||
"""Serializer for the PartCategoryParameterTemplate model."""
|
||||
|
||||
parameter_template_detail = PartParameterTemplateSerializer(source='parameter_template', many=False, read_only=True)
|
||||
|
||||
category_detail = CategorySerializer(source='category', many=False, read_only=True)
|
||||
|
||||
class Meta:
|
||||
"""Metaclass defining serializer fields"""
|
||||
model = PartCategoryParameterTemplate
|
||||
@ -1167,6 +1163,10 @@ class CategoryParameterTemplateSerializer(InvenTreeModelSerializer):
|
||||
'default_value',
|
||||
]
|
||||
|
||||
parameter_template_detail = PartParameterTemplateSerializer(source='parameter_template', many=False, read_only=True)
|
||||
|
||||
category_detail = CategorySerializer(source='category', many=False, read_only=True)
|
||||
|
||||
|
||||
class PartCopyBOMSerializer(serializers.Serializer):
|
||||
"""Serializer for copying a BOM from another part."""
|
||||
|
Reference in New Issue
Block a user