mirror of
https://github.com/inventree/InvenTree.git
synced 2025-05-01 13:06:45 +00:00
Move Meta class to top of class definition (#4363)
This commit is contained in:
parent
139274f356
commit
cc2e7ee8a5
@ -126,6 +126,16 @@ class EditUserForm(HelperForm):
|
|||||||
class SetPasswordForm(HelperForm):
|
class SetPasswordForm(HelperForm):
|
||||||
"""Form for setting user password."""
|
"""Form for setting user password."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = User
|
||||||
|
fields = [
|
||||||
|
'enter_password',
|
||||||
|
'confirm_password',
|
||||||
|
'old_password',
|
||||||
|
]
|
||||||
|
|
||||||
enter_password = forms.CharField(
|
enter_password = forms.CharField(
|
||||||
max_length=100,
|
max_length=100,
|
||||||
min_length=8,
|
min_length=8,
|
||||||
@ -152,16 +162,6 @@ class SetPasswordForm(HelperForm):
|
|||||||
widget=forms.PasswordInput(attrs={'autocomplete': 'current-password', 'autofocus': True}),
|
widget=forms.PasswordInput(attrs={'autocomplete': 'current-password', 'autofocus': True}),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = User
|
|
||||||
fields = [
|
|
||||||
'enter_password',
|
|
||||||
'confirm_password',
|
|
||||||
'old_password',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# override allauth
|
# override allauth
|
||||||
class CustomSignupForm(SignupForm):
|
class CustomSignupForm(SignupForm):
|
||||||
|
@ -116,6 +116,11 @@ class ReferenceIndexingMixin(models.Model):
|
|||||||
# Name of the global setting which defines the required reference pattern for this model
|
# Name of the global setting which defines the required reference pattern for this model
|
||||||
REFERENCE_PATTERN_SETTING = None
|
REFERENCE_PATTERN_SETTING = None
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options. Abstract ensures no database table is created."""
|
||||||
|
|
||||||
|
abstract = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_reference_pattern(cls):
|
def get_reference_pattern(cls):
|
||||||
"""Returns the reference pattern associated with this model.
|
"""Returns the reference pattern associated with this model.
|
||||||
@ -272,11 +277,6 @@ class ReferenceIndexingMixin(models.Model):
|
|||||||
# Check that the reference field can be rebuild
|
# Check that the reference field can be rebuild
|
||||||
cls.rebuild_reference_field(value, validate=True)
|
cls.rebuild_reference_field(value, validate=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options. Abstract ensures no database table is created."""
|
|
||||||
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def rebuild_reference_field(cls, reference, validate=False):
|
def rebuild_reference_field(cls, reference, validate=False):
|
||||||
"""Extract integer out of reference for sorting.
|
"""Extract integer out of reference for sorting.
|
||||||
@ -369,6 +369,10 @@ class InvenTreeAttachment(models.Model):
|
|||||||
upload_date: Date the file was uploaded
|
upload_date: Date the file was uploaded
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options. Abstract ensures no database table is created."""
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def getSubdir(self):
|
def getSubdir(self):
|
||||||
"""Return the subdirectory under which attachments should be stored.
|
"""Return the subdirectory under which attachments should be stored.
|
||||||
|
|
||||||
@ -483,11 +487,6 @@ class InvenTreeAttachment(models.Model):
|
|||||||
except Exception:
|
except Exception:
|
||||||
raise ValidationError(_("Error renaming file"))
|
raise ValidationError(_("Error renaming file"))
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options. Abstract ensures no database table is created."""
|
|
||||||
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class InvenTreeTree(MPTTModel):
|
class InvenTreeTree(MPTTModel):
|
||||||
"""Provides an abstracted self-referencing tree model for data categories.
|
"""Provides an abstracted self-referencing tree model for data categories.
|
||||||
@ -503,7 +502,6 @@ class InvenTreeTree(MPTTModel):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defines extra model properties."""
|
"""Metaclass defines extra model properties."""
|
||||||
|
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
class MPTTMeta:
|
class MPTTMeta:
|
||||||
|
@ -17,6 +17,17 @@ class BuildResource(InvenTreeResource):
|
|||||||
# but we don't for other ones.
|
# but we don't for other ones.
|
||||||
# TODO: 2022-05-12 - Need to investigate why this is the case!
|
# TODO: 2022-05-12 - Need to investigate why this is the case!
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options"""
|
||||||
|
models = Build
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = False
|
||||||
|
clean_model_instances = True
|
||||||
|
exclude = [
|
||||||
|
'lft', 'rght', 'tree_id', 'level',
|
||||||
|
'metadata',
|
||||||
|
]
|
||||||
|
|
||||||
id = Field(attribute='pk')
|
id = Field(attribute='pk')
|
||||||
|
|
||||||
reference = Field(attribute='reference')
|
reference = Field(attribute='reference')
|
||||||
@ -39,17 +50,6 @@ class BuildResource(InvenTreeResource):
|
|||||||
|
|
||||||
notes = Field(attribute='notes')
|
notes = Field(attribute='notes')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options"""
|
|
||||||
models = Build
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = False
|
|
||||||
clean_model_instances = True
|
|
||||||
exclude = [
|
|
||||||
'lft', 'rght', 'tree_id', 'level',
|
|
||||||
'metadata',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class BuildAdmin(ImportExportModelAdmin):
|
class BuildAdmin(ImportExportModelAdmin):
|
||||||
"""Class for managing the Build model via the admin interface"""
|
"""Class for managing the Build model via the admin interface"""
|
||||||
|
@ -64,6 +64,11 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
priority: Priority of the build
|
priority: Priority of the build
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options for the BuildOrder model"""
|
||||||
|
verbose_name = _("Build Order")
|
||||||
|
verbose_name_plural = _("Build Orders")
|
||||||
|
|
||||||
OVERDUE_FILTER = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
|
OVERDUE_FILTER = Q(status__in=BuildStatus.ACTIVE_CODES) & ~Q(target_date=None) & Q(target_date__lte=datetime.now().date())
|
||||||
|
|
||||||
# Global setting for specifying reference pattern
|
# Global setting for specifying reference pattern
|
||||||
@ -106,11 +111,6 @@ class Build(MPTTModel, ReferenceIndexingMixin):
|
|||||||
'parent': _('Invalid choice for parent build'),
|
'parent': _('Invalid choice for parent build'),
|
||||||
})
|
})
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options for the BuildOrder model"""
|
|
||||||
verbose_name = _("Build Order")
|
|
||||||
verbose_name_plural = _("Build Orders")
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def filterByDate(queryset, min_date, max_date):
|
def filterByDate(queryset, min_date, max_date):
|
||||||
"""Filter by 'minimum and maximum date range'.
|
"""Filter by 'minimum and maximum date range'.
|
||||||
@ -1153,17 +1153,17 @@ class BuildItem(models.Model):
|
|||||||
install_into: Destination stock item (or None)
|
install_into: Destination stock item (or None)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_api_url():
|
|
||||||
"""Return the API URL used to access this model"""
|
|
||||||
return reverse('api-build-item-list')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Serializer metaclass"""
|
"""Serializer metaclass"""
|
||||||
unique_together = [
|
unique_together = [
|
||||||
('build', 'stock_item', 'install_into'),
|
('build', 'stock_item', 'install_into'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_api_url():
|
||||||
|
"""Return the API URL used to access this model"""
|
||||||
|
return reverse('api-build-item-list')
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Custom save method for the BuildItem model"""
|
"""Custom save method for the BuildItem model"""
|
||||||
self.clean()
|
self.clean()
|
||||||
|
@ -30,7 +30,48 @@ from .models import Build, BuildItem, BuildOrderAttachment
|
|||||||
class BuildSerializer(InvenTreeModelSerializer):
|
class BuildSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializes a Build object."""
|
"""Serializes a Build object."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Serializer metaclass"""
|
||||||
|
model = Build
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'url',
|
||||||
|
'title',
|
||||||
|
'batch',
|
||||||
|
'creation_date',
|
||||||
|
'completed',
|
||||||
|
'completion_date',
|
||||||
|
'destination',
|
||||||
|
'parent',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'overdue',
|
||||||
|
'reference',
|
||||||
|
'sales_order',
|
||||||
|
'quantity',
|
||||||
|
'status',
|
||||||
|
'status_text',
|
||||||
|
'target_date',
|
||||||
|
'take_from',
|
||||||
|
'notes',
|
||||||
|
'link',
|
||||||
|
'issued_by',
|
||||||
|
'issued_by_detail',
|
||||||
|
'responsible',
|
||||||
|
'responsible_detail',
|
||||||
|
'priority',
|
||||||
|
]
|
||||||
|
|
||||||
|
read_only_fields = [
|
||||||
|
'completed',
|
||||||
|
'creation_date',
|
||||||
|
'completion_data',
|
||||||
|
'status',
|
||||||
|
'status_text',
|
||||||
|
]
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
status_text = serializers.CharField(source='get_status_display', read_only=True)
|
||||||
|
|
||||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||||
@ -83,46 +124,6 @@ class BuildSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Serializer metaclass"""
|
|
||||||
model = Build
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'url',
|
|
||||||
'title',
|
|
||||||
'batch',
|
|
||||||
'creation_date',
|
|
||||||
'completed',
|
|
||||||
'completion_date',
|
|
||||||
'destination',
|
|
||||||
'parent',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'overdue',
|
|
||||||
'reference',
|
|
||||||
'sales_order',
|
|
||||||
'quantity',
|
|
||||||
'status',
|
|
||||||
'status_text',
|
|
||||||
'target_date',
|
|
||||||
'take_from',
|
|
||||||
'notes',
|
|
||||||
'link',
|
|
||||||
'issued_by',
|
|
||||||
'issued_by_detail',
|
|
||||||
'responsible',
|
|
||||||
'responsible_detail',
|
|
||||||
'priority',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'completed',
|
|
||||||
'creation_date',
|
|
||||||
'completion_data',
|
|
||||||
'status',
|
|
||||||
'status_text',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputSerializer(serializers.Serializer):
|
class BuildOutputSerializer(serializers.Serializer):
|
||||||
"""Serializer for a "BuildOutput".
|
"""Serializer for a "BuildOutput".
|
||||||
@ -130,6 +131,12 @@ class BuildOutputSerializer(serializers.Serializer):
|
|||||||
Note that a "BuildOutput" is really just a StockItem which is "in production"!
|
Note that a "BuildOutput" is really just a StockItem which is "in production"!
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Serializer metaclass"""
|
||||||
|
fields = [
|
||||||
|
'output',
|
||||||
|
]
|
||||||
|
|
||||||
output = serializers.PrimaryKeyRelatedField(
|
output = serializers.PrimaryKeyRelatedField(
|
||||||
queryset=StockItem.objects.all(),
|
queryset=StockItem.objects.all(),
|
||||||
many=False,
|
many=False,
|
||||||
@ -170,12 +177,6 @@ class BuildOutputSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
return output
|
return output
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Serializer metaclass"""
|
|
||||||
fields = [
|
|
||||||
'output',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class BuildOutputCreateSerializer(serializers.Serializer):
|
class BuildOutputCreateSerializer(serializers.Serializer):
|
||||||
"""Serializer for creating a new BuildOutput against a BuildOrder.
|
"""Serializer for creating a new BuildOutput against a BuildOrder.
|
||||||
@ -633,6 +634,15 @@ class BuildUnallocationSerializer(serializers.Serializer):
|
|||||||
class BuildAllocationItemSerializer(serializers.Serializer):
|
class BuildAllocationItemSerializer(serializers.Serializer):
|
||||||
"""A serializer for allocating a single stock item against a build order."""
|
"""A serializer for allocating a single stock item against a build order."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Serializer metaclass"""
|
||||||
|
fields = [
|
||||||
|
'bom_item',
|
||||||
|
'stock_item',
|
||||||
|
'quantity',
|
||||||
|
'output',
|
||||||
|
]
|
||||||
|
|
||||||
bom_item = serializers.PrimaryKeyRelatedField(
|
bom_item = serializers.PrimaryKeyRelatedField(
|
||||||
queryset=BomItem.objects.all(),
|
queryset=BomItem.objects.all(),
|
||||||
many=False,
|
many=False,
|
||||||
@ -693,15 +703,6 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
|||||||
label=_('Build Output'),
|
label=_('Build Output'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Serializer metaclass"""
|
|
||||||
fields = [
|
|
||||||
'bom_item',
|
|
||||||
'stock_item',
|
|
||||||
'quantity',
|
|
||||||
'output',
|
|
||||||
]
|
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
"""Perform data validation for this item"""
|
"""Perform data validation for this item"""
|
||||||
super().validate(data)
|
super().validate(data)
|
||||||
@ -751,14 +752,14 @@ class BuildAllocationItemSerializer(serializers.Serializer):
|
|||||||
class BuildAllocationSerializer(serializers.Serializer):
|
class BuildAllocationSerializer(serializers.Serializer):
|
||||||
"""DRF serializer for allocation stock items against a build order."""
|
"""DRF serializer for allocation stock items against a build order."""
|
||||||
|
|
||||||
items = BuildAllocationItemSerializer(many=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Serializer metaclass"""
|
"""Serializer metaclass"""
|
||||||
fields = [
|
fields = [
|
||||||
'items',
|
'items',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
items = BuildAllocationItemSerializer(many=True)
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
"""Validation."""
|
"""Validation."""
|
||||||
data = super().validate(data)
|
data = super().validate(data)
|
||||||
@ -870,6 +871,24 @@ class BuildAutoAllocationSerializer(serializers.Serializer):
|
|||||||
class BuildItemSerializer(InvenTreeModelSerializer):
|
class BuildItemSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializes a BuildItem object."""
|
"""Serializes a BuildItem object."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Serializer metaclass"""
|
||||||
|
model = BuildItem
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'bom_part',
|
||||||
|
'build',
|
||||||
|
'build_detail',
|
||||||
|
'install_into',
|
||||||
|
'location',
|
||||||
|
'location_detail',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'stock_item',
|
||||||
|
'stock_item_detail',
|
||||||
|
'quantity'
|
||||||
|
]
|
||||||
|
|
||||||
bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True)
|
bom_part = serializers.IntegerField(source='bom_item.sub_part.pk', read_only=True)
|
||||||
part = serializers.IntegerField(source='stock_item.part.pk', read_only=True)
|
part = serializers.IntegerField(source='stock_item.part.pk', read_only=True)
|
||||||
location = serializers.IntegerField(source='stock_item.location.pk', read_only=True)
|
location = serializers.IntegerField(source='stock_item.location.pk', read_only=True)
|
||||||
@ -903,24 +922,6 @@ class BuildItemSerializer(InvenTreeModelSerializer):
|
|||||||
if not stock_detail:
|
if not stock_detail:
|
||||||
self.fields.pop('stock_item_detail')
|
self.fields.pop('stock_item_detail')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Serializer metaclass"""
|
|
||||||
model = BuildItem
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'bom_part',
|
|
||||||
'build',
|
|
||||||
'build_detail',
|
|
||||||
'install_into',
|
|
||||||
'location',
|
|
||||||
'location_detail',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'stock_item',
|
|
||||||
'stock_item_detail',
|
|
||||||
'quantity'
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
|
class BuildAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||||
"""Serializer for a BuildAttachment."""
|
"""Serializer for a BuildAttachment."""
|
||||||
|
@ -852,6 +852,12 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
|||||||
even if that key does not exist.
|
even if that key does not exist.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta options for InvenTreeSetting."""
|
||||||
|
|
||||||
|
verbose_name = "InvenTree Setting"
|
||||||
|
verbose_name_plural = "InvenTree Settings"
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""When saving a global setting, check to see if it requires a server restart.
|
"""When saving a global setting, check to see if it requires a server restart.
|
||||||
|
|
||||||
@ -1601,12 +1607,6 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
|||||||
|
|
||||||
typ = 'inventree'
|
typ = 'inventree'
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta options for InvenTreeSetting."""
|
|
||||||
|
|
||||||
verbose_name = "InvenTree Setting"
|
|
||||||
verbose_name_plural = "InvenTree Settings"
|
|
||||||
|
|
||||||
key = models.CharField(
|
key = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=False,
|
blank=False,
|
||||||
@ -1631,6 +1631,15 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
|||||||
class InvenTreeUserSetting(BaseInvenTreeSetting):
|
class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||||
"""An InvenTreeSetting object with a usercontext."""
|
"""An InvenTreeSetting object with a usercontext."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta options for InvenTreeUserSetting."""
|
||||||
|
|
||||||
|
verbose_name = "InvenTree User Setting"
|
||||||
|
verbose_name_plural = "InvenTree User Settings"
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(fields=['key', 'user'], name='unique key and user')
|
||||||
|
]
|
||||||
|
|
||||||
SETTINGS = {
|
SETTINGS = {
|
||||||
'HOMEPAGE_PART_STARRED': {
|
'HOMEPAGE_PART_STARRED': {
|
||||||
'name': _('Show subscribed parts'),
|
'name': _('Show subscribed parts'),
|
||||||
@ -1947,15 +1956,6 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
|||||||
|
|
||||||
typ = 'user'
|
typ = 'user'
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta options for InvenTreeUserSetting."""
|
|
||||||
|
|
||||||
verbose_name = "InvenTree User Setting"
|
|
||||||
verbose_name_plural = "InvenTree User Settings"
|
|
||||||
constraints = [
|
|
||||||
models.UniqueConstraint(fields=['key', 'user'], name='unique key and user')
|
|
||||||
]
|
|
||||||
|
|
||||||
key = models.CharField(
|
key = models.CharField(
|
||||||
max_length=50,
|
max_length=50,
|
||||||
blank=False,
|
blank=False,
|
||||||
|
@ -77,8 +77,6 @@ class GlobalSettingsSerializer(SettingsSerializer):
|
|||||||
class UserSettingsSerializer(SettingsSerializer):
|
class UserSettingsSerializer(SettingsSerializer):
|
||||||
"""Serializer for the InvenTreeUserSetting model."""
|
"""Serializer for the InvenTreeUserSetting model."""
|
||||||
|
|
||||||
user = serializers.PrimaryKeyRelatedField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta options for UserSettingsSerializer."""
|
"""Meta options for UserSettingsSerializer."""
|
||||||
|
|
||||||
@ -97,6 +95,8 @@ class UserSettingsSerializer(SettingsSerializer):
|
|||||||
'typ',
|
'typ',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
user = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class GenericReferencedSettingSerializer(SettingsSerializer):
|
class GenericReferencedSettingSerializer(SettingsSerializer):
|
||||||
"""Serializer for a GenericReferencedSetting model.
|
"""Serializer for a GenericReferencedSetting model.
|
||||||
@ -140,6 +140,33 @@ class GenericReferencedSettingSerializer(SettingsSerializer):
|
|||||||
class NotificationMessageSerializer(InvenTreeModelSerializer):
|
class NotificationMessageSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the InvenTreeUserSetting model."""
|
"""Serializer for the InvenTreeUserSetting model."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta options for NotificationMessageSerializer."""
|
||||||
|
|
||||||
|
model = NotificationMessage
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'target',
|
||||||
|
'source',
|
||||||
|
'user',
|
||||||
|
'category',
|
||||||
|
'name',
|
||||||
|
'message',
|
||||||
|
'creation',
|
||||||
|
'age',
|
||||||
|
'age_human',
|
||||||
|
'read',
|
||||||
|
]
|
||||||
|
|
||||||
|
read_only_fields = [
|
||||||
|
'category',
|
||||||
|
'name',
|
||||||
|
'message',
|
||||||
|
'creation',
|
||||||
|
'age',
|
||||||
|
'age_human',
|
||||||
|
]
|
||||||
|
|
||||||
target = serializers.SerializerMethodField(read_only=True)
|
target = serializers.SerializerMethodField(read_only=True)
|
||||||
source = serializers.SerializerMethodField(read_only=True)
|
source = serializers.SerializerMethodField(read_only=True)
|
||||||
user = serializers.PrimaryKeyRelatedField(read_only=True)
|
user = serializers.PrimaryKeyRelatedField(read_only=True)
|
||||||
@ -170,39 +197,10 @@ class NotificationMessageSerializer(InvenTreeModelSerializer):
|
|||||||
"""Function to resolve generic object reference to source."""
|
"""Function to resolve generic object reference to source."""
|
||||||
return get_objectreference(obj, 'source_content_type', 'source_object_id')
|
return get_objectreference(obj, 'source_content_type', 'source_object_id')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta options for NotificationMessageSerializer."""
|
|
||||||
|
|
||||||
model = NotificationMessage
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'target',
|
|
||||||
'source',
|
|
||||||
'user',
|
|
||||||
'category',
|
|
||||||
'name',
|
|
||||||
'message',
|
|
||||||
'creation',
|
|
||||||
'age',
|
|
||||||
'age_human',
|
|
||||||
'read',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'category',
|
|
||||||
'name',
|
|
||||||
'message',
|
|
||||||
'creation',
|
|
||||||
'age',
|
|
||||||
'age_human',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class NewsFeedEntrySerializer(InvenTreeModelSerializer):
|
class NewsFeedEntrySerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the NewsFeedEntry model."""
|
"""Serializer for the NewsFeedEntry model."""
|
||||||
|
|
||||||
read = serializers.BooleanField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta options for NewsFeedEntrySerializer."""
|
"""Meta options for NewsFeedEntrySerializer."""
|
||||||
|
|
||||||
@ -218,6 +216,8 @@ class NewsFeedEntrySerializer(InvenTreeModelSerializer):
|
|||||||
'read',
|
'read',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
read = serializers.BooleanField()
|
||||||
|
|
||||||
|
|
||||||
class ConfigSerializer(serializers.Serializer):
|
class ConfigSerializer(serializers.Serializer):
|
||||||
"""Serializer for the InvenTree configuration.
|
"""Serializer for the InvenTree configuration.
|
||||||
|
@ -41,6 +41,13 @@ class CompanyAdmin(ImportExportModelAdmin):
|
|||||||
class SupplierPartResource(InvenTreeResource):
|
class SupplierPartResource(InvenTreeResource):
|
||||||
"""Class for managing SupplierPart data import/export."""
|
"""Class for managing SupplierPart data import/export."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra admin options"""
|
||||||
|
model = SupplierPart
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = True
|
||||||
|
clean_model_instances = True
|
||||||
|
|
||||||
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
|
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
|
||||||
|
|
||||||
part_name = Field(attribute='part__full_name', readonly=True)
|
part_name = Field(attribute='part__full_name', readonly=True)
|
||||||
@ -49,13 +56,6 @@ class SupplierPartResource(InvenTreeResource):
|
|||||||
|
|
||||||
supplier_name = Field(attribute='supplier__name', readonly=True)
|
supplier_name = Field(attribute='supplier__name', readonly=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra admin options"""
|
|
||||||
model = SupplierPart
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = True
|
|
||||||
clean_model_instances = True
|
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakInline(admin.TabularInline):
|
class SupplierPriceBreakInline(admin.TabularInline):
|
||||||
"""Inline for supplier-part pricing"""
|
"""Inline for supplier-part pricing"""
|
||||||
@ -87,6 +87,13 @@ class SupplierPartAdmin(ImportExportModelAdmin):
|
|||||||
class ManufacturerPartResource(InvenTreeResource):
|
class ManufacturerPartResource(InvenTreeResource):
|
||||||
"""Class for managing ManufacturerPart data import/export."""
|
"""Class for managing ManufacturerPart data import/export."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra admin options"""
|
||||||
|
model = ManufacturerPart
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = True
|
||||||
|
clean_model_instances = True
|
||||||
|
|
||||||
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
|
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part))
|
||||||
|
|
||||||
part_name = Field(attribute='part__full_name', readonly=True)
|
part_name = Field(attribute='part__full_name', readonly=True)
|
||||||
@ -95,13 +102,6 @@ class ManufacturerPartResource(InvenTreeResource):
|
|||||||
|
|
||||||
manufacturer_name = Field(attribute='manufacturer__name', readonly=True)
|
manufacturer_name = Field(attribute='manufacturer__name', readonly=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra admin options"""
|
|
||||||
model = ManufacturerPart
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = True
|
|
||||||
clean_model_instances = True
|
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartAdmin(ImportExportModelAdmin):
|
class ManufacturerPartAdmin(ImportExportModelAdmin):
|
||||||
"""Admin class for ManufacturerPart model."""
|
"""Admin class for ManufacturerPart model."""
|
||||||
@ -157,6 +157,13 @@ class ManufacturerPartParameterAdmin(ImportExportModelAdmin):
|
|||||||
class SupplierPriceBreakResource(InvenTreeResource):
|
class SupplierPriceBreakResource(InvenTreeResource):
|
||||||
"""Class for managing SupplierPriceBreak data import/export."""
|
"""Class for managing SupplierPriceBreak data import/export."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra admin options"""
|
||||||
|
model = SupplierPriceBreak
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = False
|
||||||
|
clean_model_instances = True
|
||||||
|
|
||||||
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart))
|
part = Field(attribute='part', widget=widgets.ForeignKeyWidget(SupplierPart))
|
||||||
|
|
||||||
supplier_id = Field(attribute='part__supplier__pk', readonly=True)
|
supplier_id = Field(attribute='part__supplier__pk', readonly=True)
|
||||||
@ -169,13 +176,6 @@ class SupplierPriceBreakResource(InvenTreeResource):
|
|||||||
|
|
||||||
MPN = Field(attribute='part__MPN', readonly=True)
|
MPN = Field(attribute='part__MPN', readonly=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra admin options"""
|
|
||||||
model = SupplierPriceBreak
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = False
|
|
||||||
clean_model_instances = True
|
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakAdmin(ImportExportModelAdmin):
|
class SupplierPriceBreakAdmin(ImportExportModelAdmin):
|
||||||
"""Admin class for the SupplierPriceBreak model"""
|
"""Admin class for the SupplierPriceBreak model"""
|
||||||
|
@ -427,6 +427,15 @@ class SupplierPartDetail(RetrieveUpdateDestroyAPI):
|
|||||||
class SupplierPriceBreakFilter(rest_filters.FilterSet):
|
class SupplierPriceBreakFilter(rest_filters.FilterSet):
|
||||||
"""Custom API filters for the SupplierPriceBreak list endpoint"""
|
"""Custom API filters for the SupplierPriceBreak list endpoint"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options"""
|
||||||
|
|
||||||
|
model = SupplierPriceBreak
|
||||||
|
fields = [
|
||||||
|
'part',
|
||||||
|
'quantity',
|
||||||
|
]
|
||||||
|
|
||||||
base_part = rest_filters.ModelChoiceFilter(
|
base_part = rest_filters.ModelChoiceFilter(
|
||||||
label='Base Part',
|
label='Base Part',
|
||||||
queryset=part.models.Part.objects.all(),
|
queryset=part.models.Part.objects.all(),
|
||||||
@ -439,15 +448,6 @@ class SupplierPriceBreakFilter(rest_filters.FilterSet):
|
|||||||
field_name='part__supplier',
|
field_name='part__supplier',
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options"""
|
|
||||||
|
|
||||||
model = SupplierPriceBreak
|
|
||||||
fields = [
|
|
||||||
'part',
|
|
||||||
'quantity',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SupplierPriceBreakList(ListCreateAPI):
|
class SupplierPriceBreakList(ListCreateAPI):
|
||||||
"""API endpoint for list view of SupplierPriceBreak object.
|
"""API endpoint for list view of SupplierPriceBreak object.
|
||||||
|
@ -81,11 +81,6 @@ class Company(MetadataMixin, models.Model):
|
|||||||
currency_code: Specifies the default currency for the company
|
currency_code: Specifies the default currency for the company
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_api_url():
|
|
||||||
"""Return the API URL associated with the Company model"""
|
|
||||||
return reverse('api-company-list')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defines extra model options"""
|
"""Metaclass defines extra model options"""
|
||||||
ordering = ['name', ]
|
ordering = ['name', ]
|
||||||
@ -94,6 +89,11 @@ class Company(MetadataMixin, models.Model):
|
|||||||
]
|
]
|
||||||
verbose_name_plural = "Companies"
|
verbose_name_plural = "Companies"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_api_url():
|
||||||
|
"""Return the API URL associated with the Company model"""
|
||||||
|
return reverse('api-company-list')
|
||||||
|
|
||||||
name = models.CharField(max_length=100, blank=False,
|
name = models.CharField(max_length=100, blank=False,
|
||||||
help_text=_('Company name'),
|
help_text=_('Company name'),
|
||||||
verbose_name=_('Company name'))
|
verbose_name=_('Company name'))
|
||||||
@ -258,15 +258,15 @@ class ManufacturerPart(models.Model):
|
|||||||
description: Descriptive notes field
|
description: Descriptive notes field
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra model options"""
|
||||||
|
unique_together = ('part', 'manufacturer', 'MPN')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_url():
|
def get_api_url():
|
||||||
"""Return the API URL associated with the ManufacturerPart instance"""
|
"""Return the API URL associated with the ManufacturerPart instance"""
|
||||||
return reverse('api-manufacturer-part-list')
|
return reverse('api-manufacturer-part-list')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra model options"""
|
|
||||||
unique_together = ('part', 'manufacturer', 'MPN')
|
|
||||||
|
|
||||||
part = models.ForeignKey('part.Part', on_delete=models.CASCADE,
|
part = models.ForeignKey('part.Part', on_delete=models.CASCADE,
|
||||||
related_name='manufacturer_parts',
|
related_name='manufacturer_parts',
|
||||||
verbose_name=_('Base Part'),
|
verbose_name=_('Base Part'),
|
||||||
@ -360,15 +360,15 @@ class ManufacturerPartParameter(models.Model):
|
|||||||
Each parameter is a simple string (text) value.
|
Each parameter is a simple string (text) value.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra model options"""
|
||||||
|
unique_together = ('manufacturer_part', 'name')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_url():
|
def get_api_url():
|
||||||
"""Return the API URL associated with the ManufacturerPartParameter model"""
|
"""Return the API URL associated with the ManufacturerPartParameter model"""
|
||||||
return reverse('api-manufacturer-part-parameter-list')
|
return reverse('api-manufacturer-part-parameter-list')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra model options"""
|
|
||||||
unique_together = ('manufacturer_part', 'name')
|
|
||||||
|
|
||||||
manufacturer_part = models.ForeignKey(
|
manufacturer_part = models.ForeignKey(
|
||||||
ManufacturerPart,
|
ManufacturerPart,
|
||||||
on_delete=models.CASCADE,
|
on_delete=models.CASCADE,
|
||||||
@ -434,6 +434,13 @@ class SupplierPart(InvenTreeBarcodeMixin, common.models.MetaMixin):
|
|||||||
updated: Date that the SupplierPart was last updated
|
updated: Date that the SupplierPart was last updated
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra model options"""
|
||||||
|
unique_together = ('part', 'supplier', 'SKU')
|
||||||
|
|
||||||
|
# This model was moved from the 'Part' app
|
||||||
|
db_table = 'part_supplierpart'
|
||||||
|
|
||||||
objects = SupplierPartManager()
|
objects = SupplierPartManager()
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@ -453,13 +460,6 @@ class SupplierPart(InvenTreeBarcodeMixin, common.models.MetaMixin):
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra model options"""
|
|
||||||
unique_together = ('part', 'supplier', 'SKU')
|
|
||||||
|
|
||||||
# This model was moved from the 'Part' app
|
|
||||||
db_table = 'part_supplierpart'
|
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
"""Custom clean action for the SupplierPart model:
|
"""Custom clean action for the SupplierPart model:
|
||||||
|
|
||||||
@ -696,13 +696,6 @@ class SupplierPriceBreak(common.models.PriceBreak):
|
|||||||
currency: Reference to the currency of this pricebreak (leave empty for base currency)
|
currency: Reference to the currency of this pricebreak (leave empty for base currency)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_api_url():
|
|
||||||
"""Return the API URL associated with the SupplierPriceBreak model"""
|
|
||||||
return reverse('api-part-supplier-price-list')
|
|
||||||
|
|
||||||
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks', verbose_name=_('Part'),)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defines extra model options"""
|
"""Metaclass defines extra model options"""
|
||||||
unique_together = ("part", "quantity")
|
unique_together = ("part", "quantity")
|
||||||
@ -714,6 +707,13 @@ class SupplierPriceBreak(common.models.PriceBreak):
|
|||||||
"""Format a string representation of a SupplierPriceBreak instance"""
|
"""Format a string representation of a SupplierPriceBreak instance"""
|
||||||
return f'{self.part.SKU} - {self.price} @ {self.quantity}'
|
return f'{self.part.SKU} - {self.price} @ {self.quantity}'
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_api_url():
|
||||||
|
"""Return the API URL associated with the SupplierPriceBreak model"""
|
||||||
|
return reverse('api-part-supplier-price-list')
|
||||||
|
|
||||||
|
part = models.ForeignKey(SupplierPart, on_delete=models.CASCADE, related_name='pricebreaks', verbose_name=_('Part'),)
|
||||||
|
|
||||||
|
|
||||||
@receiver(post_save, sender=SupplierPriceBreak, dispatch_uid='post_save_supplier_price_break')
|
@receiver(post_save, sender=SupplierPriceBreak, dispatch_uid='post_save_supplier_price_break')
|
||||||
def after_save_supplier_price(sender, instance, created, **kwargs):
|
def after_save_supplier_price(sender, instance, created, **kwargs):
|
||||||
|
@ -25,10 +25,6 @@ from .models import (Company, CompanyAttachment, ManufacturerPart,
|
|||||||
class CompanyBriefSerializer(InvenTreeModelSerializer):
|
class CompanyBriefSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for Company object (limited detail)"""
|
"""Serializer for Company object (limited detail)"""
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
|
||||||
|
|
||||||
image = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -41,33 +37,14 @@ class CompanyBriefSerializer(InvenTreeModelSerializer):
|
|||||||
'image',
|
'image',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
|
image = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class CompanySerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
class CompanySerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
||||||
"""Serializer for Company object (full detail)"""
|
"""Serializer for Company object (full detail)"""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def annotate_queryset(queryset):
|
|
||||||
"""Annoate the supplied queryset with aggregated information"""
|
|
||||||
# Add count of parts manufactured
|
|
||||||
queryset = queryset.annotate(
|
|
||||||
parts_manufactured=SubqueryCount('manufactured_parts')
|
|
||||||
)
|
|
||||||
|
|
||||||
queryset = queryset.annotate(
|
|
||||||
parts_supplied=SubqueryCount('supplied_parts')
|
|
||||||
)
|
|
||||||
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
|
||||||
|
|
||||||
image = InvenTreeImageSerializerField(required=False, allow_null=True)
|
|
||||||
|
|
||||||
parts_supplied = serializers.IntegerField(read_only=True)
|
|
||||||
parts_manufactured = serializers.IntegerField(read_only=True)
|
|
||||||
|
|
||||||
currency = InvenTreeCurrencySerializer(help_text=_('Default currency used for this supplier'), required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -95,6 +72,29 @@ class CompanySerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
|||||||
'remote_image',
|
'remote_image',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def annotate_queryset(queryset):
|
||||||
|
"""Annoate the supplied queryset with aggregated information"""
|
||||||
|
# Add count of parts manufactured
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
parts_manufactured=SubqueryCount('manufactured_parts')
|
||||||
|
)
|
||||||
|
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
parts_supplied=SubqueryCount('supplied_parts')
|
||||||
|
)
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
|
image = InvenTreeImageSerializerField(required=False, allow_null=True)
|
||||||
|
|
||||||
|
parts_supplied = serializers.IntegerField(read_only=True)
|
||||||
|
parts_manufactured = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
currency = InvenTreeCurrencySerializer(help_text=_('Default currency used for this supplier'), required=True)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Save the Company instance"""
|
"""Save the Company instance"""
|
||||||
super().save()
|
super().save()
|
||||||
@ -135,11 +135,21 @@ class CompanyAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
class ManufacturerPartSerializer(InvenTreeModelSerializer):
|
class ManufacturerPartSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for ManufacturerPart object."""
|
"""Serializer for ManufacturerPart object."""
|
||||||
|
|
||||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
manufacturer_detail = CompanyBriefSerializer(source='manufacturer', many=False, read_only=True)
|
model = ManufacturerPart
|
||||||
|
fields = [
|
||||||
pretty_name = serializers.CharField(read_only=True)
|
'pk',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'pretty_name',
|
||||||
|
'manufacturer',
|
||||||
|
'manufacturer_detail',
|
||||||
|
'description',
|
||||||
|
'MPN',
|
||||||
|
'link',
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize this serializer with extra detail fields as required"""
|
"""Initialize this serializer with extra detail fields as required"""
|
||||||
@ -158,24 +168,14 @@ class ManufacturerPartSerializer(InvenTreeModelSerializer):
|
|||||||
if prettify is not True:
|
if prettify is not True:
|
||||||
self.fields.pop('pretty_name')
|
self.fields.pop('pretty_name')
|
||||||
|
|
||||||
|
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||||
|
|
||||||
|
manufacturer_detail = CompanyBriefSerializer(source='manufacturer', many=False, read_only=True)
|
||||||
|
|
||||||
|
pretty_name = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
manufacturer = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_manufacturer=True))
|
manufacturer = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_manufacturer=True))
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = ManufacturerPart
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'pretty_name',
|
|
||||||
'manufacturer',
|
|
||||||
'manufacturer_detail',
|
|
||||||
'description',
|
|
||||||
'MPN',
|
|
||||||
'link',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class ManufacturerPartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
class ManufacturerPartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||||
"""Serializer for the ManufacturerPartAttachment class."""
|
"""Serializer for the ManufacturerPartAttachment class."""
|
||||||
@ -193,17 +193,6 @@ class ManufacturerPartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the ManufacturerPartParameter model."""
|
"""Serializer for the ManufacturerPartParameter model."""
|
||||||
|
|
||||||
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', many=False, read_only=True)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Initialize this serializer with extra detail fields as required"""
|
|
||||||
man_detail = kwargs.pop('manufacturer_part_detail', False)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
if not man_detail:
|
|
||||||
self.fields.pop('manufacturer_part_detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -218,66 +207,20 @@ class ManufacturerPartParameterSerializer(InvenTreeModelSerializer):
|
|||||||
'units',
|
'units',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class SupplierPartSerializer(InvenTreeModelSerializer):
|
|
||||||
"""Serializer for SupplierPart object."""
|
|
||||||
|
|
||||||
# Annotated field showing total in-stock quantity
|
|
||||||
in_stock = serializers.FloatField(read_only=True)
|
|
||||||
|
|
||||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
|
||||||
|
|
||||||
supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True)
|
|
||||||
|
|
||||||
manufacturer_detail = CompanyBriefSerializer(source='manufacturer_part.manufacturer', many=False, read_only=True)
|
|
||||||
|
|
||||||
pretty_name = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
pack_size = serializers.FloatField(label=_('Pack Quantity'))
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize this serializer with extra detail fields as required"""
|
"""Initialize this serializer with extra detail fields as required"""
|
||||||
|
man_detail = kwargs.pop('manufacturer_part_detail', False)
|
||||||
# Check if 'available' quantity was supplied
|
|
||||||
self.has_available_quantity = 'available' in kwargs.get('data', {})
|
|
||||||
|
|
||||||
brief = kwargs.pop('brief', False)
|
|
||||||
|
|
||||||
detail_default = not brief
|
|
||||||
|
|
||||||
part_detail = kwargs.pop('part_detail', detail_default)
|
|
||||||
supplier_detail = kwargs.pop('supplier_detail', detail_default)
|
|
||||||
manufacturer_detail = kwargs.pop('manufacturer_detail', detail_default)
|
|
||||||
|
|
||||||
prettify = kwargs.pop('pretty', False)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
if part_detail is not True:
|
if not man_detail:
|
||||||
self.fields.pop('part_detail')
|
|
||||||
|
|
||||||
if supplier_detail is not True:
|
|
||||||
self.fields.pop('supplier_detail')
|
|
||||||
|
|
||||||
if manufacturer_detail is not True:
|
|
||||||
self.fields.pop('manufacturer_detail')
|
|
||||||
self.fields.pop('manufacturer_part_detail')
|
self.fields.pop('manufacturer_part_detail')
|
||||||
|
|
||||||
if prettify is not True:
|
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', many=False, read_only=True)
|
||||||
self.fields.pop('pretty_name')
|
|
||||||
|
|
||||||
supplier = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_supplier=True))
|
|
||||||
|
|
||||||
manufacturer = serializers.CharField(read_only=True)
|
class SupplierPartSerializer(InvenTreeModelSerializer):
|
||||||
|
"""Serializer for SupplierPart object."""
|
||||||
MPN = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', read_only=True)
|
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
|
||||||
|
|
||||||
# Date fields
|
|
||||||
updated = serializers.DateTimeField(allow_null=True, read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
@ -314,6 +257,63 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
|
|||||||
'barcode_hash',
|
'barcode_hash',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initialize this serializer with extra detail fields as required"""
|
||||||
|
|
||||||
|
# Check if 'available' quantity was supplied
|
||||||
|
self.has_available_quantity = 'available' in kwargs.get('data', {})
|
||||||
|
|
||||||
|
brief = kwargs.pop('brief', False)
|
||||||
|
|
||||||
|
detail_default = not brief
|
||||||
|
|
||||||
|
part_detail = kwargs.pop('part_detail', detail_default)
|
||||||
|
supplier_detail = kwargs.pop('supplier_detail', detail_default)
|
||||||
|
manufacturer_detail = kwargs.pop('manufacturer_detail', detail_default)
|
||||||
|
|
||||||
|
prettify = kwargs.pop('pretty', False)
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if part_detail is not True:
|
||||||
|
self.fields.pop('part_detail')
|
||||||
|
|
||||||
|
if supplier_detail is not True:
|
||||||
|
self.fields.pop('supplier_detail')
|
||||||
|
|
||||||
|
if manufacturer_detail is not True:
|
||||||
|
self.fields.pop('manufacturer_detail')
|
||||||
|
self.fields.pop('manufacturer_part_detail')
|
||||||
|
|
||||||
|
if prettify is not True:
|
||||||
|
self.fields.pop('pretty_name')
|
||||||
|
|
||||||
|
# Annotated field showing total in-stock quantity
|
||||||
|
in_stock = serializers.FloatField(read_only=True)
|
||||||
|
|
||||||
|
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||||
|
|
||||||
|
supplier_detail = CompanyBriefSerializer(source='supplier', many=False, read_only=True)
|
||||||
|
|
||||||
|
manufacturer_detail = CompanyBriefSerializer(source='manufacturer_part.manufacturer', many=False, read_only=True)
|
||||||
|
|
||||||
|
pretty_name = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
pack_size = serializers.FloatField(label=_('Pack Quantity'))
|
||||||
|
|
||||||
|
supplier = serializers.PrimaryKeyRelatedField(queryset=Company.objects.filter(is_supplier=True))
|
||||||
|
|
||||||
|
manufacturer = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
MPN = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
manufacturer_part_detail = ManufacturerPartSerializer(source='manufacturer_part', read_only=True)
|
||||||
|
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
|
# Date fields
|
||||||
|
updated = serializers.DateTimeField(allow_null=True, read_only=True)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Annotate the SupplierPart queryset with extra fields:
|
"""Annotate the SupplierPart queryset with extra fields:
|
||||||
@ -369,6 +369,22 @@ class SupplierPartSerializer(InvenTreeModelSerializer):
|
|||||||
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
|
class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for SupplierPriceBreak object."""
|
"""Serializer for SupplierPriceBreak object."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = SupplierPriceBreak
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'quantity',
|
||||||
|
'price',
|
||||||
|
'price_currency',
|
||||||
|
'supplier',
|
||||||
|
'supplier_detail',
|
||||||
|
'updated',
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialize this serializer with extra fields as required"""
|
"""Initialize this serializer with extra fields as required"""
|
||||||
|
|
||||||
@ -399,19 +415,3 @@ class SupplierPriceBreakSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
# Detail serializer for SupplierPart
|
# Detail serializer for SupplierPart
|
||||||
part_detail = SupplierPartSerializer(source='part', brief=True, many=False, read_only=True)
|
part_detail = SupplierPartSerializer(source='part', brief=True, many=False, read_only=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = SupplierPriceBreak
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'quantity',
|
|
||||||
'price',
|
|
||||||
'price_currency',
|
|
||||||
'supplier',
|
|
||||||
'supplier_detail',
|
|
||||||
'updated',
|
|
||||||
]
|
|
||||||
|
@ -9,8 +9,6 @@ from .models import PartLabel, StockItemLabel, StockLocationLabel
|
|||||||
class StockItemLabelSerializer(InvenTreeModelSerializer):
|
class StockItemLabelSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializes a StockItemLabel object."""
|
"""Serializes a StockItemLabel object."""
|
||||||
|
|
||||||
label = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -24,12 +22,12 @@ class StockItemLabelSerializer(InvenTreeModelSerializer):
|
|||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
label = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class StockLocationLabelSerializer(InvenTreeModelSerializer):
|
class StockLocationLabelSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializes a StockLocationLabel object."""
|
"""Serializes a StockLocationLabel object."""
|
||||||
|
|
||||||
label = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -43,12 +41,12 @@ class StockLocationLabelSerializer(InvenTreeModelSerializer):
|
|||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
label = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class PartLabelSerializer(InvenTreeModelSerializer):
|
class PartLabelSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializes a PartLabel object."""
|
"""Serializes a PartLabel object."""
|
||||||
|
|
||||||
label = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -61,3 +59,5 @@ class PartLabelSerializer(InvenTreeModelSerializer):
|
|||||||
'filters',
|
'filters',
|
||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
label = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
@ -101,12 +101,6 @@ class SalesOrderAdmin(ImportExportModelAdmin):
|
|||||||
class PurchaseOrderResource(InvenTreeResource):
|
class PurchaseOrderResource(InvenTreeResource):
|
||||||
"""Class for managing import / export of PurchaseOrder data."""
|
"""Class for managing import / export of PurchaseOrder data."""
|
||||||
|
|
||||||
# Add number of line items
|
|
||||||
line_items = Field(attribute='line_count', widget=widgets.IntegerWidget(), readonly=True)
|
|
||||||
|
|
||||||
# Is this order overdue?
|
|
||||||
overdue = Field(attribute='is_overdue', widget=widgets.BooleanWidget(), readonly=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass"""
|
"""Metaclass"""
|
||||||
model = PurchaseOrder
|
model = PurchaseOrder
|
||||||
@ -116,10 +110,23 @@ class PurchaseOrderResource(InvenTreeResource):
|
|||||||
'metadata',
|
'metadata',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Add number of line items
|
||||||
|
line_items = Field(attribute='line_count', widget=widgets.IntegerWidget(), readonly=True)
|
||||||
|
|
||||||
|
# Is this order overdue?
|
||||||
|
overdue = Field(attribute='is_overdue', widget=widgets.BooleanWidget(), readonly=True)
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderLineItemResource(InvenTreeResource):
|
class PurchaseOrderLineItemResource(InvenTreeResource):
|
||||||
"""Class for managing import / export of PurchaseOrderLineItem data."""
|
"""Class for managing import / export of PurchaseOrderLineItem data."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass"""
|
||||||
|
model = PurchaseOrderLineItem
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = False
|
||||||
|
clean_model_instances = True
|
||||||
|
|
||||||
part_name = Field(attribute='part__part__name', readonly=True)
|
part_name = Field(attribute='part__part__name', readonly=True)
|
||||||
|
|
||||||
manufacturer = Field(attribute='part__manufacturer', readonly=True)
|
manufacturer = Field(attribute='part__manufacturer', readonly=True)
|
||||||
@ -128,13 +135,6 @@ class PurchaseOrderLineItemResource(InvenTreeResource):
|
|||||||
|
|
||||||
SKU = Field(attribute='part__SKU', readonly=True)
|
SKU = Field(attribute='part__SKU', readonly=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass"""
|
|
||||||
model = PurchaseOrderLineItem
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = False
|
|
||||||
clean_model_instances = True
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExtraLineResource(InvenTreeResource):
|
class PurchaseOrderExtraLineResource(InvenTreeResource):
|
||||||
"""Class for managing import / export of PurchaseOrderExtraLine data."""
|
"""Class for managing import / export of PurchaseOrderExtraLine data."""
|
||||||
@ -148,12 +148,6 @@ class PurchaseOrderExtraLineResource(InvenTreeResource):
|
|||||||
class SalesOrderResource(InvenTreeResource):
|
class SalesOrderResource(InvenTreeResource):
|
||||||
"""Class for managing import / export of SalesOrder data."""
|
"""Class for managing import / export of SalesOrder data."""
|
||||||
|
|
||||||
# Add number of line items
|
|
||||||
line_items = Field(attribute='line_count', widget=widgets.IntegerWidget(), readonly=True)
|
|
||||||
|
|
||||||
# Is this order overdue?
|
|
||||||
overdue = Field(attribute='is_overdue', widget=widgets.BooleanWidget(), readonly=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options"""
|
"""Metaclass options"""
|
||||||
model = SalesOrder
|
model = SalesOrder
|
||||||
@ -163,10 +157,23 @@ class SalesOrderResource(InvenTreeResource):
|
|||||||
'metadata',
|
'metadata',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Add number of line items
|
||||||
|
line_items = Field(attribute='line_count', widget=widgets.IntegerWidget(), readonly=True)
|
||||||
|
|
||||||
|
# Is this order overdue?
|
||||||
|
overdue = Field(attribute='is_overdue', widget=widgets.BooleanWidget(), readonly=True)
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderLineItemResource(InvenTreeResource):
|
class SalesOrderLineItemResource(InvenTreeResource):
|
||||||
"""Class for managing import / export of SalesOrderLineItem data."""
|
"""Class for managing import / export of SalesOrderLineItem data."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options"""
|
||||||
|
model = SalesOrderLineItem
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = False
|
||||||
|
clean_model_instances = True
|
||||||
|
|
||||||
part_name = Field(attribute='part__name', readonly=True)
|
part_name = Field(attribute='part__name', readonly=True)
|
||||||
|
|
||||||
IPN = Field(attribute='part__IPN', readonly=True)
|
IPN = Field(attribute='part__IPN', readonly=True)
|
||||||
@ -185,13 +192,6 @@ class SalesOrderLineItemResource(InvenTreeResource):
|
|||||||
else:
|
else:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options"""
|
|
||||||
model = SalesOrderLineItem
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = False
|
|
||||||
clean_model_instances = True
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderExtraLineResource(InvenTreeResource):
|
class SalesOrderExtraLineResource(InvenTreeResource):
|
||||||
"""Class for managing import / export of SalesOrderExtraLine data."""
|
"""Class for managing import / export of SalesOrderExtraLine data."""
|
||||||
|
@ -113,6 +113,7 @@ class OrderFilter(rest_filters.FilterSet):
|
|||||||
|
|
||||||
class PurchaseOrderFilter(OrderFilter):
|
class PurchaseOrderFilter(OrderFilter):
|
||||||
"""Custom API filters for the PurchaseOrderList endpoint."""
|
"""Custom API filters for the PurchaseOrderList endpoint."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -124,6 +125,7 @@ class PurchaseOrderFilter(OrderFilter):
|
|||||||
|
|
||||||
class SalesOrderFilter(OrderFilter):
|
class SalesOrderFilter(OrderFilter):
|
||||||
"""Custom API filters for the SalesOrderList endpoint."""
|
"""Custom API filters for the SalesOrderList endpoint."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -1093,6 +1095,14 @@ class SalesOrderAllocationList(ListAPI):
|
|||||||
class SalesOrderShipmentFilter(rest_filters.FilterSet):
|
class SalesOrderShipmentFilter(rest_filters.FilterSet):
|
||||||
"""Custom filterset for the SalesOrderShipmentList endpoint."""
|
"""Custom filterset for the SalesOrderShipmentList endpoint."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = models.SalesOrderShipment
|
||||||
|
fields = [
|
||||||
|
'order',
|
||||||
|
]
|
||||||
|
|
||||||
shipped = rest_filters.BooleanFilter(label='shipped', method='filter_shipped')
|
shipped = rest_filters.BooleanFilter(label='shipped', method='filter_shipped')
|
||||||
|
|
||||||
def filter_shipped(self, queryset, name, value):
|
def filter_shipped(self, queryset, name, value):
|
||||||
@ -1106,14 +1116,6 @@ class SalesOrderShipmentFilter(rest_filters.FilterSet):
|
|||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = models.SalesOrderShipment
|
|
||||||
fields = [
|
|
||||||
'order',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentList(ListCreateAPI):
|
class SalesOrderShipmentList(ListCreateAPI):
|
||||||
"""API list endpoint for SalesOrderShipment model."""
|
"""API list endpoint for SalesOrderShipment model."""
|
||||||
|
@ -63,6 +63,10 @@ class Order(MetadataMixin, ReferenceIndexingMixin):
|
|||||||
responsible: User (or group) responsible for managing the order
|
responsible: User (or group) responsible for managing the order
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options. Abstract ensures no database table is created."""
|
||||||
|
abstract = True
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
"""Custom save method for the order models:
|
"""Custom save method for the order models:
|
||||||
|
|
||||||
@ -75,11 +79,6 @@ class Order(MetadataMixin, ReferenceIndexingMixin):
|
|||||||
|
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options. Abstract ensures no database table is created."""
|
|
||||||
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
description = models.CharField(max_length=250, verbose_name=_('Description'), help_text=_('Order description'))
|
description = models.CharField(max_length=250, verbose_name=_('Description'), help_text=_('Order description'))
|
||||||
|
|
||||||
link = InvenTreeURLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external page'))
|
link = InvenTreeURLField(blank=True, verbose_name=_('Link'), help_text=_('Link to external page'))
|
||||||
@ -927,7 +926,6 @@ class OrderLineItem(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options. Abstract ensures no database table is created."""
|
"""Metaclass options. Abstract ensures no database table is created."""
|
||||||
|
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
quantity = RoundingDecimalField(
|
quantity = RoundingDecimalField(
|
||||||
@ -958,7 +956,6 @@ class OrderExtraLine(OrderLineItem):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options. Abstract ensures no database table is created."""
|
"""Metaclass options. Abstract ensures no database table is created."""
|
||||||
|
|
||||||
abstract = True
|
abstract = True
|
||||||
|
|
||||||
context = models.JSONField(
|
context = models.JSONField(
|
||||||
|
@ -80,6 +80,40 @@ class AbstractExtraLineMeta:
|
|||||||
class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
||||||
"""Serializer for a PurchaseOrder object."""
|
"""Serializer for a PurchaseOrder object."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = order.models.PurchaseOrder
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'issue_date',
|
||||||
|
'complete_date',
|
||||||
|
'creation_date',
|
||||||
|
'description',
|
||||||
|
'line_items',
|
||||||
|
'link',
|
||||||
|
'overdue',
|
||||||
|
'reference',
|
||||||
|
'responsible',
|
||||||
|
'responsible_detail',
|
||||||
|
'supplier',
|
||||||
|
'supplier_detail',
|
||||||
|
'supplier_reference',
|
||||||
|
'status',
|
||||||
|
'status_text',
|
||||||
|
'target_date',
|
||||||
|
'notes',
|
||||||
|
'total_price',
|
||||||
|
]
|
||||||
|
|
||||||
|
read_only_fields = [
|
||||||
|
'status'
|
||||||
|
'issue_date',
|
||||||
|
'complete_date',
|
||||||
|
'creation_date',
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialization routine for the serializer"""
|
"""Initialization routine for the serializer"""
|
||||||
supplier_detail = kwargs.pop('supplier_detail', False)
|
supplier_detail = kwargs.pop('supplier_detail', False)
|
||||||
@ -131,40 +165,6 @@ class PurchaseOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer)
|
|||||||
|
|
||||||
responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False)
|
responsible_detail = OwnerSerializer(source='responsible', read_only=True, many=False)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = order.models.PurchaseOrder
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'issue_date',
|
|
||||||
'complete_date',
|
|
||||||
'creation_date',
|
|
||||||
'description',
|
|
||||||
'line_items',
|
|
||||||
'link',
|
|
||||||
'overdue',
|
|
||||||
'reference',
|
|
||||||
'responsible',
|
|
||||||
'responsible_detail',
|
|
||||||
'supplier',
|
|
||||||
'supplier_detail',
|
|
||||||
'supplier_reference',
|
|
||||||
'status',
|
|
||||||
'status_text',
|
|
||||||
'target_date',
|
|
||||||
'notes',
|
|
||||||
'total_price',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'status'
|
|
||||||
'issue_date',
|
|
||||||
'complete_date',
|
|
||||||
'creation_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderCancelSerializer(serializers.Serializer):
|
class PurchaseOrderCancelSerializer(serializers.Serializer):
|
||||||
"""Serializer for cancelling a PurchaseOrder."""
|
"""Serializer for cancelling a PurchaseOrder."""
|
||||||
@ -195,6 +195,11 @@ class PurchaseOrderCancelSerializer(serializers.Serializer):
|
|||||||
class PurchaseOrderCompleteSerializer(serializers.Serializer):
|
class PurchaseOrderCompleteSerializer(serializers.Serializer):
|
||||||
"""Serializer for completing a purchase order."""
|
"""Serializer for completing a purchase order."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
fields = []
|
||||||
|
|
||||||
accept_incomplete = serializers.BooleanField(
|
accept_incomplete = serializers.BooleanField(
|
||||||
label=_('Accept Incomplete'),
|
label=_('Accept Incomplete'),
|
||||||
help_text=_('Allow order to be closed with incomplete line items'),
|
help_text=_('Allow order to be closed with incomplete line items'),
|
||||||
@ -212,11 +217,6 @@ class PurchaseOrderCompleteSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
fields = []
|
|
||||||
|
|
||||||
def get_context_data(self):
|
def get_context_data(self):
|
||||||
"""Custom context information for this serializer."""
|
"""Custom context information for this serializer."""
|
||||||
order = self.context['order']
|
order = self.context['order']
|
||||||
@ -247,6 +247,47 @@ class PurchaseOrderIssueSerializer(serializers.Serializer):
|
|||||||
|
|
||||||
class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer class for the PurchaseOrderLineItem model"""
|
"""Serializer class for the PurchaseOrderLineItem model"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = order.models.PurchaseOrderLineItem
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'quantity',
|
||||||
|
'reference',
|
||||||
|
'notes',
|
||||||
|
'order',
|
||||||
|
'order_detail',
|
||||||
|
'overdue',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'supplier_part_detail',
|
||||||
|
'received',
|
||||||
|
'purchase_price',
|
||||||
|
'purchase_price_currency',
|
||||||
|
'destination',
|
||||||
|
'destination_detail',
|
||||||
|
'target_date',
|
||||||
|
'total_price',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initialization routine for the serializer"""
|
||||||
|
part_detail = kwargs.pop('part_detail', False)
|
||||||
|
|
||||||
|
order_detail = kwargs.pop('order_detail', False)
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if part_detail is not True:
|
||||||
|
self.fields.pop('part_detail')
|
||||||
|
self.fields.pop('supplier_part_detail')
|
||||||
|
|
||||||
|
if order_detail is not True:
|
||||||
|
self.fields.pop('order_detail')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add some extra annotations to this queryset:
|
"""Add some extra annotations to this queryset:
|
||||||
@ -272,21 +313,6 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Initialization routine for the serializer"""
|
|
||||||
part_detail = kwargs.pop('part_detail', False)
|
|
||||||
|
|
||||||
order_detail = kwargs.pop('order_detail', False)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
if part_detail is not True:
|
|
||||||
self.fields.pop('part_detail')
|
|
||||||
self.fields.pop('supplier_part_detail')
|
|
||||||
|
|
||||||
if order_detail is not True:
|
|
||||||
self.fields.pop('order_detail')
|
|
||||||
|
|
||||||
quantity = serializers.FloatField(min_value=0, required=True)
|
quantity = serializers.FloatField(min_value=0, required=True)
|
||||||
|
|
||||||
def validate_quantity(self, quantity):
|
def validate_quantity(self, quantity):
|
||||||
@ -352,31 +378,6 @@ class PurchaseOrderLineItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
return data
|
return data
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = order.models.PurchaseOrderLineItem
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'quantity',
|
|
||||||
'reference',
|
|
||||||
'notes',
|
|
||||||
'order',
|
|
||||||
'order_detail',
|
|
||||||
'overdue',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'supplier_part_detail',
|
|
||||||
'received',
|
|
||||||
'purchase_price',
|
|
||||||
'purchase_price_currency',
|
|
||||||
'destination',
|
|
||||||
'destination_detail',
|
|
||||||
'target_date',
|
|
||||||
'total_price',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderExtraLineSerializer(AbstractExtraLineSerializer, InvenTreeModelSerializer):
|
class PurchaseOrderExtraLineSerializer(AbstractExtraLineSerializer, InvenTreeModelSerializer):
|
||||||
"""Serializer for a PurchaseOrderExtraLine object."""
|
"""Serializer for a PurchaseOrderExtraLine object."""
|
||||||
@ -530,6 +531,14 @@ class PurchaseOrderLineItemReceiveSerializer(serializers.Serializer):
|
|||||||
class PurchaseOrderReceiveSerializer(serializers.Serializer):
|
class PurchaseOrderReceiveSerializer(serializers.Serializer):
|
||||||
"""Serializer for receiving items against a purchase order."""
|
"""Serializer for receiving items against a purchase order."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'items',
|
||||||
|
'location',
|
||||||
|
]
|
||||||
|
|
||||||
items = PurchaseOrderLineItemReceiveSerializer(many=True)
|
items = PurchaseOrderLineItemReceiveSerializer(many=True)
|
||||||
|
|
||||||
location = serializers.PrimaryKeyRelatedField(
|
location = serializers.PrimaryKeyRelatedField(
|
||||||
@ -619,14 +628,6 @@ class PurchaseOrderReceiveSerializer(serializers.Serializer):
|
|||||||
# Catch model errors and re-throw as DRF errors
|
# Catch model errors and re-throw as DRF errors
|
||||||
raise ValidationError(detail=serializers.as_serializer_error(exc))
|
raise ValidationError(detail=serializers.as_serializer_error(exc))
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'items',
|
|
||||||
'location',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
|
class PurchaseOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
|
||||||
"""Serializers for the PurchaseOrderAttachment model."""
|
"""Serializers for the PurchaseOrderAttachment model."""
|
||||||
@ -644,6 +645,37 @@ class PurchaseOrderAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
||||||
"""Serializers for the SalesOrder object."""
|
"""Serializers for the SalesOrder object."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = order.models.SalesOrder
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'creation_date',
|
||||||
|
'customer',
|
||||||
|
'customer_detail',
|
||||||
|
'customer_reference',
|
||||||
|
'description',
|
||||||
|
'line_items',
|
||||||
|
'link',
|
||||||
|
'notes',
|
||||||
|
'overdue',
|
||||||
|
'reference',
|
||||||
|
'responsible',
|
||||||
|
'status',
|
||||||
|
'status_text',
|
||||||
|
'shipment_date',
|
||||||
|
'target_date',
|
||||||
|
'total_price',
|
||||||
|
]
|
||||||
|
|
||||||
|
read_only_fields = [
|
||||||
|
'status',
|
||||||
|
'creation_date',
|
||||||
|
'shipment_date',
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialization routine for the serializer"""
|
"""Initialization routine for the serializer"""
|
||||||
customer_detail = kwargs.pop('customer_detail', False)
|
customer_detail = kwargs.pop('customer_detail', False)
|
||||||
@ -693,37 +725,6 @@ class SalesOrderSerializer(AbstractOrderSerializer, InvenTreeModelSerializer):
|
|||||||
|
|
||||||
return reference
|
return reference
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = order.models.SalesOrder
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'creation_date',
|
|
||||||
'customer',
|
|
||||||
'customer_detail',
|
|
||||||
'customer_reference',
|
|
||||||
'description',
|
|
||||||
'line_items',
|
|
||||||
'link',
|
|
||||||
'notes',
|
|
||||||
'overdue',
|
|
||||||
'reference',
|
|
||||||
'responsible',
|
|
||||||
'status',
|
|
||||||
'status_text',
|
|
||||||
'shipment_date',
|
|
||||||
'target_date',
|
|
||||||
'total_price',
|
|
||||||
]
|
|
||||||
|
|
||||||
read_only_fields = [
|
|
||||||
'status',
|
|
||||||
'creation_date',
|
|
||||||
'shipment_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the SalesOrderAllocation model.
|
"""Serializer for the SalesOrderAllocation model.
|
||||||
@ -731,20 +732,28 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
|||||||
This includes some fields from the related model objects.
|
This includes some fields from the related model objects.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
|
class Meta:
|
||||||
order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
|
"""Metaclass options."""
|
||||||
serial = serializers.CharField(source='get_serial', read_only=True)
|
|
||||||
quantity = serializers.FloatField(read_only=False)
|
|
||||||
location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)
|
|
||||||
|
|
||||||
# Extra detail fields
|
model = order.models.SalesOrderAllocation
|
||||||
order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
|
|
||||||
part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
|
|
||||||
item_detail = stock.serializers.StockItemSerializer(source='item', many=False, read_only=True)
|
|
||||||
location_detail = stock.serializers.LocationSerializer(source='item.location', many=False, read_only=True)
|
|
||||||
customer_detail = CompanyBriefSerializer(source='line.order.customer', many=False, read_only=True)
|
|
||||||
|
|
||||||
shipment_date = serializers.DateField(source='shipment.shipment_date', read_only=True)
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'line',
|
||||||
|
'customer_detail',
|
||||||
|
'serial',
|
||||||
|
'quantity',
|
||||||
|
'location',
|
||||||
|
'location_detail',
|
||||||
|
'item',
|
||||||
|
'item_detail',
|
||||||
|
'order',
|
||||||
|
'order_detail',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'shipment',
|
||||||
|
'shipment_date',
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Initialization routine for the serializer"""
|
"""Initialization routine for the serializer"""
|
||||||
@ -771,33 +780,74 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
|||||||
if not customer_detail:
|
if not customer_detail:
|
||||||
self.fields.pop('customer_detail')
|
self.fields.pop('customer_detail')
|
||||||
|
|
||||||
class Meta:
|
part = serializers.PrimaryKeyRelatedField(source='item.part', read_only=True)
|
||||||
"""Metaclass options."""
|
order = serializers.PrimaryKeyRelatedField(source='line.order', many=False, read_only=True)
|
||||||
|
serial = serializers.CharField(source='get_serial', read_only=True)
|
||||||
|
quantity = serializers.FloatField(read_only=False)
|
||||||
|
location = serializers.PrimaryKeyRelatedField(source='item.location', many=False, read_only=True)
|
||||||
|
|
||||||
model = order.models.SalesOrderAllocation
|
# Extra detail fields
|
||||||
|
order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
|
||||||
|
part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
|
||||||
|
item_detail = stock.serializers.StockItemSerializer(source='item', many=False, read_only=True)
|
||||||
|
location_detail = stock.serializers.LocationSerializer(source='item.location', many=False, read_only=True)
|
||||||
|
customer_detail = CompanyBriefSerializer(source='line.order.customer', many=False, read_only=True)
|
||||||
|
|
||||||
fields = [
|
shipment_date = serializers.DateField(source='shipment.shipment_date', read_only=True)
|
||||||
'pk',
|
|
||||||
'line',
|
|
||||||
'customer_detail',
|
|
||||||
'serial',
|
|
||||||
'quantity',
|
|
||||||
'location',
|
|
||||||
'location_detail',
|
|
||||||
'item',
|
|
||||||
'item_detail',
|
|
||||||
'order',
|
|
||||||
'order_detail',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'shipment',
|
|
||||||
'shipment_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderLineItemSerializer(InvenTreeModelSerializer):
|
class SalesOrderLineItemSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for a SalesOrderLineItem object."""
|
"""Serializer for a SalesOrderLineItem object."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = order.models.SalesOrderLineItem
|
||||||
|
|
||||||
|
fields = [
|
||||||
|
'pk',
|
||||||
|
'allocated',
|
||||||
|
'allocations',
|
||||||
|
'available_stock',
|
||||||
|
'customer_detail',
|
||||||
|
'quantity',
|
||||||
|
'reference',
|
||||||
|
'notes',
|
||||||
|
'order',
|
||||||
|
'order_detail',
|
||||||
|
'overdue',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'sale_price',
|
||||||
|
'sale_price_currency',
|
||||||
|
'shipped',
|
||||||
|
'target_date',
|
||||||
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Initializion routine for the serializer:
|
||||||
|
|
||||||
|
- Add extra related serializer information if required
|
||||||
|
"""
|
||||||
|
part_detail = kwargs.pop('part_detail', False)
|
||||||
|
order_detail = kwargs.pop('order_detail', False)
|
||||||
|
allocations = kwargs.pop('allocations', False)
|
||||||
|
customer_detail = kwargs.pop('customer_detail', False)
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if part_detail is not True:
|
||||||
|
self.fields.pop('part_detail')
|
||||||
|
|
||||||
|
if order_detail is not True:
|
||||||
|
self.fields.pop('order_detail')
|
||||||
|
|
||||||
|
if allocations is not True:
|
||||||
|
self.fields.pop('allocations')
|
||||||
|
|
||||||
|
if customer_detail is not True:
|
||||||
|
self.fields.pop('customer_detail')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add some extra annotations to this queryset:
|
"""Add some extra annotations to this queryset:
|
||||||
@ -832,30 +882,6 @@ class SalesOrderLineItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Initializion routine for the serializer:
|
|
||||||
|
|
||||||
- Add extra related serializer information if required
|
|
||||||
"""
|
|
||||||
part_detail = kwargs.pop('part_detail', False)
|
|
||||||
order_detail = kwargs.pop('order_detail', False)
|
|
||||||
allocations = kwargs.pop('allocations', False)
|
|
||||||
customer_detail = kwargs.pop('customer_detail', False)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
if part_detail is not True:
|
|
||||||
self.fields.pop('part_detail')
|
|
||||||
|
|
||||||
if order_detail is not True:
|
|
||||||
self.fields.pop('order_detail')
|
|
||||||
|
|
||||||
if allocations is not True:
|
|
||||||
self.fields.pop('allocations')
|
|
||||||
|
|
||||||
if customer_detail is not True:
|
|
||||||
self.fields.pop('customer_detail')
|
|
||||||
|
|
||||||
customer_detail = CompanyBriefSerializer(source='order.customer', many=False, read_only=True)
|
customer_detail = CompanyBriefSerializer(source='order.customer', many=False, read_only=True)
|
||||||
order_detail = SalesOrderSerializer(source='order', many=False, read_only=True)
|
order_detail = SalesOrderSerializer(source='order', many=False, read_only=True)
|
||||||
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
part_detail = PartBriefSerializer(source='part', many=False, read_only=True)
|
||||||
@ -875,39 +901,10 @@ class SalesOrderLineItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
sale_price_currency = InvenTreeCurrencySerializer(help_text=_('Sale price currency'))
|
sale_price_currency = InvenTreeCurrencySerializer(help_text=_('Sale price currency'))
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = order.models.SalesOrderLineItem
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'pk',
|
|
||||||
'allocated',
|
|
||||||
'allocations',
|
|
||||||
'available_stock',
|
|
||||||
'customer_detail',
|
|
||||||
'quantity',
|
|
||||||
'reference',
|
|
||||||
'notes',
|
|
||||||
'order',
|
|
||||||
'order_detail',
|
|
||||||
'overdue',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'sale_price',
|
|
||||||
'sale_price_currency',
|
|
||||||
'shipped',
|
|
||||||
'target_date',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentSerializer(InvenTreeModelSerializer):
|
class SalesOrderShipmentSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the SalesOrderShipment class."""
|
"""Serializer for the SalesOrderShipment class."""
|
||||||
|
|
||||||
allocations = SalesOrderAllocationSerializer(many=True, read_only=True, location_detail=True)
|
|
||||||
|
|
||||||
order_detail = SalesOrderSerializer(source='order', read_only=True, many=False)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -927,6 +924,10 @@ class SalesOrderShipmentSerializer(InvenTreeModelSerializer):
|
|||||||
'notes',
|
'notes',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
allocations = SalesOrderAllocationSerializer(many=True, read_only=True, location_detail=True)
|
||||||
|
|
||||||
|
order_detail = SalesOrderSerializer(source='order', read_only=True, many=False)
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer):
|
class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer):
|
||||||
"""Serializer for completing (shipping) a SalesOrderShipment."""
|
"""Serializer for completing (shipping) a SalesOrderShipment."""
|
||||||
|
@ -16,6 +16,20 @@ from stock.models import StockLocation
|
|||||||
class PartResource(InvenTreeResource):
|
class PartResource(InvenTreeResource):
|
||||||
"""Class for managing Part data import/export."""
|
"""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())
|
id = Field(attribute='pk', column_name=_('Part ID'), widget=widgets.IntegerWidget())
|
||||||
name = Field(attribute='name', column_name=_('Part Name'), widget=widgets.CharWidget())
|
name = Field(attribute='name', column_name=_('Part Name'), widget=widgets.CharWidget())
|
||||||
description = Field(attribute='description', column_name=_('Part Description'), 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:
|
if max_cost is not None:
|
||||||
return float(max_cost.amount)
|
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):
|
def get_queryset(self):
|
||||||
"""Prefetch related data for quicker access."""
|
"""Prefetch related data for quicker access."""
|
||||||
query = super().get_queryset()
|
query = super().get_queryset()
|
||||||
@ -175,18 +175,6 @@ class PartStocktakeReportAdmin(admin.ModelAdmin):
|
|||||||
class PartCategoryResource(InvenTreeResource):
|
class PartCategoryResource(InvenTreeResource):
|
||||||
"""Class for managing PartCategory data import/export."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass definition"""
|
"""Metaclass definition"""
|
||||||
model = models.PartCategory
|
model = models.PartCategory
|
||||||
@ -201,6 +189,18 @@ class PartCategoryResource(InvenTreeResource):
|
|||||||
'icon',
|
'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):
|
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
|
||||||
"""Rebuild MPTT tree structure after importing PartCategory data"""
|
"""Rebuild MPTT tree structure after importing PartCategory data"""
|
||||||
|
|
||||||
@ -247,6 +247,20 @@ class PartTestTemplateAdmin(admin.ModelAdmin):
|
|||||||
class BomItemResource(InvenTreeResource):
|
class BomItemResource(InvenTreeResource):
|
||||||
"""Class for managing BomItem data import/export."""
|
"""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)
|
level = Field(attribute='level', column_name=_('BOM Level'), readonly=True)
|
||||||
|
|
||||||
bom_id = Field(attribute='pk', column_name=_('BOM Item ID'))
|
bom_id = Field(attribute='pk', column_name=_('BOM Item ID'))
|
||||||
@ -335,20 +349,6 @@ class BomItemResource(InvenTreeResource):
|
|||||||
|
|
||||||
return fields
|
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):
|
class BomItemAdmin(ImportExportModelAdmin):
|
||||||
"""Admin class for the BomItem model"""
|
"""Admin class for the BomItem model"""
|
||||||
@ -373,6 +373,13 @@ class ParameterTemplateAdmin(ImportExportModelAdmin):
|
|||||||
class ParameterResource(InvenTreeResource):
|
class ParameterResource(InvenTreeResource):
|
||||||
"""Class for managing PartParameter data import/export."""
|
"""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 = Field(attribute='part', widget=widgets.ForeignKeyWidget(models.Part))
|
||||||
|
|
||||||
part_name = Field(attribute='part__name', readonly=True)
|
part_name = Field(attribute='part__name', readonly=True)
|
||||||
@ -381,13 +388,6 @@ class ParameterResource(InvenTreeResource):
|
|||||||
|
|
||||||
template_name = Field(attribute='template__name', readonly=True)
|
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):
|
class ParameterAdmin(ImportExportModelAdmin):
|
||||||
"""Admin class for the PartParameter model"""
|
"""Admin class for the PartParameter model"""
|
||||||
|
@ -34,16 +34,16 @@ class BomMatchItemForm(MatchItemForm):
|
|||||||
class PartPriceForm(forms.Form):
|
class PartPriceForm(forms.Form):
|
||||||
"""Simple form for viewing part pricing information."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass defines fields for this form"""
|
"""Metaclass defines fields for this form"""
|
||||||
model = Part
|
model = Part
|
||||||
fields = [
|
fields = [
|
||||||
'quantity',
|
'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):
|
class PartSellPriceBreak(common.models.PriceBreak):
|
||||||
"""Represents a price break for selling this part."""
|
"""Represents a price break for selling this part."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass providing extra model definition"""
|
||||||
|
unique_together = ('part', 'quantity')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_url():
|
def get_api_url():
|
||||||
"""Return the list API endpoint URL associated with the PartSellPriceBreak model"""
|
"""Return the list API endpoint URL associated with the PartSellPriceBreak model"""
|
||||||
@ -3057,14 +3061,14 @@ class PartSellPriceBreak(common.models.PriceBreak):
|
|||||||
verbose_name=_('Part')
|
verbose_name=_('Part')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass providing extra model definition"""
|
|
||||||
unique_together = ('part', 'quantity')
|
|
||||||
|
|
||||||
|
|
||||||
class PartInternalPriceBreak(common.models.PriceBreak):
|
class PartInternalPriceBreak(common.models.PriceBreak):
|
||||||
"""Represents a price break for internally selling this part."""
|
"""Represents a price break for internally selling this part."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass providing extra model definition"""
|
||||||
|
unique_together = ('part', 'quantity')
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_api_url():
|
def get_api_url():
|
||||||
"""Return the list API endpoint URL associated with the PartInternalPriceBreak model"""
|
"""Return the list API endpoint URL associated with the PartInternalPriceBreak model"""
|
||||||
@ -3076,10 +3080,6 @@ class PartInternalPriceBreak(common.models.PriceBreak):
|
|||||||
verbose_name=_('Part')
|
verbose_name=_('Part')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass providing extra model definition"""
|
|
||||||
unique_together = ('part', 'quantity')
|
|
||||||
|
|
||||||
|
|
||||||
class PartStar(models.Model):
|
class PartStar(models.Model):
|
||||||
"""A PartStar object creates a subscription relationship between a User and a Part.
|
"""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
|
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:
|
class Meta:
|
||||||
"""Metaclass providing extra model definition"""
|
"""Metaclass providing extra model definition"""
|
||||||
unique_together = [
|
unique_together = [
|
||||||
@ -3102,6 +3098,10 @@ class PartStar(models.Model):
|
|||||||
'user'
|
'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):
|
class PartCategoryStar(models.Model):
|
||||||
"""A PartCategoryStar creates a subscription relationship between a User and a PartCategory.
|
"""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
|
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:
|
class Meta:
|
||||||
"""Metaclass providing extra model definition"""
|
"""Metaclass providing extra model definition"""
|
||||||
unique_together = [
|
unique_together = [
|
||||||
@ -3122,6 +3118,10 @@ class PartCategoryStar(models.Model):
|
|||||||
'user',
|
'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):
|
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).
|
"""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]
|
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
|
@staticmethod
|
||||||
def get_api_url():
|
def get_api_url():
|
||||||
"""Return the list API endpoint URL associated with the PartParameter model"""
|
"""Return the list API endpoint URL associated with the PartParameter model"""
|
||||||
@ -3306,11 +3311,6 @@ class PartParameter(models.Model):
|
|||||||
units=str(self.template.units)
|
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'))
|
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'))
|
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
|
@staticmethod
|
||||||
def get_api_url():
|
def get_api_url():
|
||||||
"""Return the list API endpoint URL associated with the BomItem model"""
|
"""Return the list API endpoint URL associated with the BomItem model"""
|
||||||
@ -3638,17 +3649,6 @@ class BomItem(DataImportMixin, models.Model):
|
|||||||
except Part.DoesNotExist:
|
except Part.DoesNotExist:
|
||||||
raise ValidationError({'sub_part': _('Sub part must be specified')})
|
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):
|
def get_overage_quantity(self, quantity):
|
||||||
"""Calculate overage quantity."""
|
"""Calculate overage quantity."""
|
||||||
# Most of the time overage string will be empty
|
# Most of the time overage string will be empty
|
||||||
|
@ -46,6 +46,25 @@ from .models import (BomItem, BomItemSubstitute, Part, PartAttachment,
|
|||||||
class CategorySerializer(InvenTreeModelSerializer):
|
class CategorySerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for PartCategory."""
|
"""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):
|
def get_starred(self, category):
|
||||||
"""Return True if the category is directly "starred" by the current user."""
|
"""Return True if the category is directly "starred" by the current user."""
|
||||||
return category in self.context.get('starred_categories', [])
|
return category in self.context.get('starred_categories', [])
|
||||||
@ -69,25 +88,6 @@ class CategorySerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
starred = serializers.SerializerMethodField()
|
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):
|
class CategoryTree(InvenTreeModelSerializer):
|
||||||
"""Serializer for PartCategory tree."""
|
"""Serializer for PartCategory tree."""
|
||||||
@ -118,8 +118,6 @@ class PartAttachmentSerializer(InvenTreeAttachmentSerializer):
|
|||||||
class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the PartTestTemplate class."""
|
"""Serializer for the PartTestTemplate class."""
|
||||||
|
|
||||||
key = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartTestTemplate
|
model = PartTestTemplate
|
||||||
@ -135,16 +133,12 @@ class PartTestTemplateSerializer(InvenTreeModelSerializer):
|
|||||||
'requires_attachment',
|
'requires_attachment',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
key = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class PartSalePriceSerializer(InvenTreeModelSerializer):
|
class PartSalePriceSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for sale prices for Part model."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartSellPriceBreak
|
model = PartSellPriceBreak
|
||||||
@ -156,18 +150,16 @@ class PartSalePriceSerializer(InvenTreeModelSerializer):
|
|||||||
'price_currency',
|
'price_currency',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
quantity = InvenTreeDecimalField()
|
||||||
|
|
||||||
|
price = InvenTreeMoneySerializer(allow_null=True)
|
||||||
|
|
||||||
|
price_currency = InvenTreeCurrencySerializer(help_text=_('Purchase currency of this stock item'))
|
||||||
|
|
||||||
|
|
||||||
class PartInternalPriceSerializer(InvenTreeModelSerializer):
|
class PartInternalPriceSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for internal prices for Part model."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartInternalPriceBreak
|
model = PartInternalPriceBreak
|
||||||
@ -179,6 +171,14 @@ class PartInternalPriceSerializer(InvenTreeModelSerializer):
|
|||||||
'price_currency',
|
'price_currency',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
quantity = InvenTreeDecimalField()
|
||||||
|
|
||||||
|
price = InvenTreeMoneySerializer(
|
||||||
|
allow_null=True
|
||||||
|
)
|
||||||
|
|
||||||
|
price_currency = InvenTreeCurrencySerializer(help_text=_('Purchase currency of this stock item'))
|
||||||
|
|
||||||
|
|
||||||
class PartThumbSerializer(serializers.Serializer):
|
class PartThumbSerializer(serializers.Serializer):
|
||||||
"""Serializer for the 'image' field of the Part model.
|
"""Serializer for the 'image' field of the Part model.
|
||||||
@ -193,6 +193,13 @@ class PartThumbSerializer(serializers.Serializer):
|
|||||||
class PartThumbSerializerUpdate(InvenTreeModelSerializer):
|
class PartThumbSerializerUpdate(InvenTreeModelSerializer):
|
||||||
"""Serializer for updating Part thumbnail."""
|
"""Serializer for updating Part thumbnail."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defining serializer fields"""
|
||||||
|
model = Part
|
||||||
|
fields = [
|
||||||
|
'image',
|
||||||
|
]
|
||||||
|
|
||||||
def validate_image(self, value):
|
def validate_image(self, value):
|
||||||
"""Check that file is an image."""
|
"""Check that file is an image."""
|
||||||
validate = imghdr.what(value)
|
validate = imghdr.what(value)
|
||||||
@ -202,13 +209,6 @@ class PartThumbSerializerUpdate(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
image = InvenTreeAttachmentSerializerField(required=True)
|
image = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defining serializer fields"""
|
|
||||||
model = Part
|
|
||||||
fields = [
|
|
||||||
'image',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class PartParameterTemplateSerializer(InvenTreeModelSerializer):
|
class PartParameterTemplateSerializer(InvenTreeModelSerializer):
|
||||||
"""JSON serializer for the PartParameterTemplate model."""
|
"""JSON serializer for the PartParameterTemplate model."""
|
||||||
@ -227,6 +227,17 @@ class PartParameterTemplateSerializer(InvenTreeModelSerializer):
|
|||||||
class PartParameterSerializer(InvenTreeModelSerializer):
|
class PartParameterSerializer(InvenTreeModelSerializer):
|
||||||
"""JSON serializers for the PartParameter model."""
|
"""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):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Custom initialization method for the serializer.
|
"""Custom initialization method for the serializer.
|
||||||
|
|
||||||
@ -242,23 +253,10 @@ class PartParameterSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
template_detail = PartParameterTemplateSerializer(source='template', many=False, read_only=True)
|
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):
|
class PartBriefSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for Part (brief detail)"""
|
"""Serializer for Part (brief detail)"""
|
||||||
|
|
||||||
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = Part
|
model = Part
|
||||||
@ -286,6 +284,8 @@ class PartBriefSerializer(InvenTreeModelSerializer):
|
|||||||
'barcode_hash',
|
'barcode_hash',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
thumbnail = serializers.CharField(source='get_thumbnail_url', read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class DuplicatePartSerializer(serializers.Serializer):
|
class DuplicatePartSerializer(serializers.Serializer):
|
||||||
"""Serializer for specifying options when duplicating a Part.
|
"""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.
|
Used when displaying all details of a single component.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def get_api_url(self):
|
class Meta:
|
||||||
"""Return the API url associated with this serializer"""
|
"""Metaclass defining serializer fields"""
|
||||||
return reverse_lazy('api-part-list')
|
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):
|
# Fields only used for Part creation
|
||||||
"""Skip these fields when instantiating a new Part instance"""
|
|
||||||
|
|
||||||
fields = super().skip_create_fields()
|
|
||||||
|
|
||||||
fields += [
|
|
||||||
'duplicate',
|
'duplicate',
|
||||||
'initial_stock',
|
'initial_stock',
|
||||||
'initial_supplier',
|
'initial_supplier',
|
||||||
]
|
]
|
||||||
|
|
||||||
return fields
|
read_only_fields = [
|
||||||
|
'barcode_hash',
|
||||||
|
]
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Custom initialization method for PartSerializer:
|
"""Custom initialization method for PartSerializer:
|
||||||
@ -443,6 +486,23 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
|||||||
for f in self.skip_create_fields()[1:]:
|
for f in self.skip_create_fields()[1:]:
|
||||||
self.fields.pop(f)
|
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
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add some extra annotations to the queryset.
|
"""Add some extra annotations to the queryset.
|
||||||
@ -553,66 +613,6 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
|||||||
write_only=True, required=False,
|
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
|
@transaction.atomic
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
"""Custom method for creating a new Part instance using this serializer"""
|
"""Custom method for creating a new Part instance using this serializer"""
|
||||||
@ -708,16 +708,6 @@ class PartSerializer(RemoteImageMixin, InvenTreeModelSerializer):
|
|||||||
class PartStocktakeSerializer(InvenTreeModelSerializer):
|
class PartStocktakeSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the PartStocktake model"""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass options"""
|
"""Metaclass options"""
|
||||||
|
|
||||||
@ -742,6 +732,16 @@ class PartStocktakeSerializer(InvenTreeModelSerializer):
|
|||||||
'user',
|
'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):
|
def save(self):
|
||||||
"""Called when this serializer is saved"""
|
"""Called when this serializer is saved"""
|
||||||
|
|
||||||
@ -757,10 +757,6 @@ class PartStocktakeSerializer(InvenTreeModelSerializer):
|
|||||||
class PartStocktakeReportSerializer(InvenTreeModelSerializer):
|
class PartStocktakeReportSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for stocktake report class"""
|
"""Serializer for stocktake report class"""
|
||||||
|
|
||||||
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
|
||||||
|
|
||||||
report = InvenTreeAttachmentSerializerField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defines serializer fields"""
|
"""Metaclass defines serializer fields"""
|
||||||
|
|
||||||
@ -774,6 +770,10 @@ class PartStocktakeReportSerializer(InvenTreeModelSerializer):
|
|||||||
'user_detail',
|
'user_detail',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
user_detail = UserSerializer(source='user', read_only=True, many=False)
|
||||||
|
|
||||||
|
report = InvenTreeAttachmentSerializerField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class PartStocktakeReportGenerateSerializer(serializers.Serializer):
|
class PartStocktakeReportGenerateSerializer(serializers.Serializer):
|
||||||
"""Serializer class for manually generating a new PartStocktakeReport via the API"""
|
"""Serializer class for manually generating a new PartStocktakeReport via the API"""
|
||||||
@ -843,6 +843,32 @@ class PartStocktakeReportGenerateSerializer(serializers.Serializer):
|
|||||||
class PartPricingSerializer(InvenTreeModelSerializer):
|
class PartPricingSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for Part pricing information"""
|
"""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)
|
currency = serializers.CharField(allow_null=True, read_only=True)
|
||||||
|
|
||||||
updated = serializers.DateTimeField(allow_null=True, read_only=True)
|
updated = serializers.DateTimeField(allow_null=True, read_only=True)
|
||||||
@ -882,32 +908,6 @@ class PartPricingSerializer(InvenTreeModelSerializer):
|
|||||||
required=False,
|
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):
|
def save(self):
|
||||||
"""Called when the serializer is saved"""
|
"""Called when the serializer is saved"""
|
||||||
data = self.validated_data
|
data = self.validated_data
|
||||||
@ -921,9 +921,6 @@ class PartPricingSerializer(InvenTreeModelSerializer):
|
|||||||
class PartRelationSerializer(InvenTreeModelSerializer):
|
class PartRelationSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for a PartRelated model."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartRelated
|
model = PartRelated
|
||||||
@ -935,13 +932,13 @@ class PartRelationSerializer(InvenTreeModelSerializer):
|
|||||||
'part_2_detail',
|
'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):
|
class PartStarSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for a PartStar object."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartStar
|
model = PartStar
|
||||||
@ -953,12 +950,13 @@ class PartStarSerializer(InvenTreeModelSerializer):
|
|||||||
'username',
|
'username',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
partname = serializers.CharField(source='part.full_name', read_only=True)
|
||||||
|
username = serializers.CharField(source='user.username', read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class BomItemSubstituteSerializer(InvenTreeModelSerializer):
|
class BomItemSubstituteSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the BomItemSubstitute class."""
|
"""Serializer for the BomItemSubstitute class."""
|
||||||
|
|
||||||
part_detail = PartBriefSerializer(source='part', read_only=True, many=False)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = BomItemSubstitute
|
model = BomItemSubstitute
|
||||||
@ -969,10 +967,60 @@ class BomItemSubstituteSerializer(InvenTreeModelSerializer):
|
|||||||
'part_detail',
|
'part_detail',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
part_detail = PartBriefSerializer(source='part', read_only=True, many=False)
|
||||||
|
|
||||||
|
|
||||||
class BomItemSerializer(InvenTreeModelSerializer):
|
class BomItemSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for BomItem object."""
|
"""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)
|
quantity = InvenTreeDecimalField(required=True)
|
||||||
|
|
||||||
def validate_quantity(self, quantity):
|
def validate_quantity(self, quantity):
|
||||||
@ -1005,23 +1053,6 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
|||||||
available_substitute_stock = serializers.FloatField(read_only=True)
|
available_substitute_stock = serializers.FloatField(read_only=True)
|
||||||
available_variant_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
|
@staticmethod
|
||||||
def setup_eager_loading(queryset):
|
def setup_eager_loading(queryset):
|
||||||
"""Prefetch against the provided queryset to speed up database access"""
|
"""Prefetch against the provided queryset to speed up database access"""
|
||||||
@ -1116,45 +1147,10 @@ class BomItemSerializer(InvenTreeModelSerializer):
|
|||||||
|
|
||||||
return queryset
|
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):
|
class CategoryParameterTemplateSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for the PartCategoryParameterTemplate model."""
|
"""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:
|
class Meta:
|
||||||
"""Metaclass defining serializer fields"""
|
"""Metaclass defining serializer fields"""
|
||||||
model = PartCategoryParameterTemplate
|
model = PartCategoryParameterTemplate
|
||||||
@ -1167,6 +1163,10 @@ class CategoryParameterTemplateSerializer(InvenTreeModelSerializer):
|
|||||||
'default_value',
|
'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):
|
class PartCopyBOMSerializer(serializers.Serializer):
|
||||||
"""Serializer for copying a BOM from another part."""
|
"""Serializer for copying a BOM from another part."""
|
||||||
|
@ -18,11 +18,6 @@ class MetadataSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
metadata = serializers.JSONField(required=True)
|
metadata = serializers.JSONField(required=True)
|
||||||
|
|
||||||
def __init__(self, model_type, *args, **kwargs):
|
|
||||||
"""Initialize the metadata serializer with information on the model type"""
|
|
||||||
self.Meta.model = model_type
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -30,6 +25,11 @@ class MetadataSerializer(serializers.ModelSerializer):
|
|||||||
'metadata',
|
'metadata',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, model_type, *args, **kwargs):
|
||||||
|
"""Initialize the metadata serializer with information on the model type"""
|
||||||
|
self.Meta.model = model_type
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
def update(self, instance, data):
|
def update(self, instance, data):
|
||||||
"""Perform update on the metadata field:
|
"""Perform update on the metadata field:
|
||||||
|
|
||||||
@ -48,9 +48,6 @@ class MetadataSerializer(serializers.ModelSerializer):
|
|||||||
class PluginConfigSerializer(serializers.ModelSerializer):
|
class PluginConfigSerializer(serializers.ModelSerializer):
|
||||||
"""Serializer for a PluginConfig."""
|
"""Serializer for a PluginConfig."""
|
||||||
|
|
||||||
meta = serializers.DictField(read_only=True)
|
|
||||||
mixins = serializers.DictField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Meta for serializer."""
|
"""Meta for serializer."""
|
||||||
model = PluginConfig
|
model = PluginConfig
|
||||||
@ -62,10 +59,21 @@ class PluginConfigSerializer(serializers.ModelSerializer):
|
|||||||
'mixins',
|
'mixins',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
meta = serializers.DictField(read_only=True)
|
||||||
|
mixins = serializers.DictField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class PluginConfigInstallSerializer(serializers.Serializer):
|
class PluginConfigInstallSerializer(serializers.Serializer):
|
||||||
"""Serializer for installing a new plugin."""
|
"""Serializer for installing a new plugin."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Meta for serializer."""
|
||||||
|
fields = [
|
||||||
|
'url',
|
||||||
|
'packagename',
|
||||||
|
'confirm',
|
||||||
|
]
|
||||||
|
|
||||||
url = serializers.CharField(
|
url = serializers.CharField(
|
||||||
required=False,
|
required=False,
|
||||||
allow_blank=True,
|
allow_blank=True,
|
||||||
@ -83,14 +91,6 @@ class PluginConfigInstallSerializer(serializers.Serializer):
|
|||||||
help_text=_('This will install this plugin now into the current instance. The instance will go into maintenance.')
|
help_text=_('This will install this plugin now into the current instance. The instance will go into maintenance.')
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Meta for serializer."""
|
|
||||||
fields = [
|
|
||||||
'url',
|
|
||||||
'packagename',
|
|
||||||
'confirm',
|
|
||||||
]
|
|
||||||
|
|
||||||
def validate(self, data):
|
def validate(self, data):
|
||||||
"""Validate inputs.
|
"""Validate inputs.
|
||||||
|
|
||||||
|
@ -180,6 +180,10 @@ class ReportTemplateBase(ReportBase):
|
|||||||
Able to be passed context data
|
Able to be passed context data
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options. Abstract ensures no database table is created."""
|
||||||
|
abstract = True
|
||||||
|
|
||||||
# Pass a single top-level object to the report template
|
# Pass a single top-level object to the report template
|
||||||
object_to_print = None
|
object_to_print = None
|
||||||
|
|
||||||
@ -255,11 +259,6 @@ class ReportTemplateBase(ReportBase):
|
|||||||
help_text=_('Report template is enabled'),
|
help_text=_('Report template is enabled'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options. Abstract ensures no database table is created."""
|
|
||||||
|
|
||||||
abstract = True
|
|
||||||
|
|
||||||
|
|
||||||
class TestReport(ReportTemplateBase):
|
class TestReport(ReportTemplateBase):
|
||||||
"""Render a TestReport against a StockItem object."""
|
"""Render a TestReport against a StockItem object."""
|
||||||
|
@ -10,8 +10,6 @@ from .models import (BillOfMaterialsReport, BuildReport, PurchaseOrderReport,
|
|||||||
class TestReportSerializer(InvenTreeModelSerializer):
|
class TestReportSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer class for the TestReport model"""
|
"""Serializer class for the TestReport model"""
|
||||||
|
|
||||||
template = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -25,12 +23,12 @@ class TestReportSerializer(InvenTreeModelSerializer):
|
|||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
template = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class BuildReportSerializer(InvenTreeModelSerializer):
|
class BuildReportSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer class for the BuildReport model"""
|
"""Serializer class for the BuildReport model"""
|
||||||
|
|
||||||
template = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -44,10 +42,11 @@ class BuildReportSerializer(InvenTreeModelSerializer):
|
|||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
template = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class BOMReportSerializer(InvenTreeModelSerializer):
|
class BOMReportSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer class for the BillOfMaterialsReport model"""
|
"""Serializer class for the BillOfMaterialsReport model"""
|
||||||
template = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
@ -62,10 +61,11 @@ class BOMReportSerializer(InvenTreeModelSerializer):
|
|||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
template = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class PurchaseOrderReportSerializer(InvenTreeModelSerializer):
|
class PurchaseOrderReportSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer class for the PurchaseOrdeReport model"""
|
"""Serializer class for the PurchaseOrdeReport model"""
|
||||||
template = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
@ -80,10 +80,11 @@ class PurchaseOrderReportSerializer(InvenTreeModelSerializer):
|
|||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
template = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderReportSerializer(InvenTreeModelSerializer):
|
class SalesOrderReportSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer class for the SalesOrderReport model"""
|
"""Serializer class for the SalesOrderReport model"""
|
||||||
template = InvenTreeAttachmentSerializerField(required=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
@ -97,3 +98,5 @@ class SalesOrderReportSerializer(InvenTreeModelSerializer):
|
|||||||
'filters',
|
'filters',
|
||||||
'enabled',
|
'enabled',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
template = InvenTreeAttachmentSerializerField(required=True)
|
||||||
|
@ -20,16 +20,6 @@ from .models import (StockItem, StockItemAttachment, StockItemTestResult,
|
|||||||
class LocationResource(InvenTreeResource):
|
class LocationResource(InvenTreeResource):
|
||||||
"""Class for managing StockLocation data import/export."""
|
"""Class for managing StockLocation data import/export."""
|
||||||
|
|
||||||
id = Field(attribute='pk', column_name=_('Location ID'))
|
|
||||||
name = Field(attribute='name', column_name=_('Location Name'))
|
|
||||||
description = Field(attribute='description', column_name=_('Description'))
|
|
||||||
parent = Field(attribute='parent', column_name=_('Parent ID'), widget=widgets.ForeignKeyWidget(StockLocation))
|
|
||||||
parent_name = Field(attribute='parent__name', column_name=_('Parent Name'), readonly=True)
|
|
||||||
pathstring = Field(attribute='pathstring', column_name=_('Location Path'))
|
|
||||||
|
|
||||||
# Calculated fields
|
|
||||||
items = Field(attribute='item_count', column_name=_('Stock Items'), widget=widgets.IntegerWidget())
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -46,6 +36,16 @@ class LocationResource(InvenTreeResource):
|
|||||||
'owner', 'icon',
|
'owner', 'icon',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
id = Field(attribute='pk', column_name=_('Location ID'))
|
||||||
|
name = Field(attribute='name', column_name=_('Location Name'))
|
||||||
|
description = Field(attribute='description', column_name=_('Description'))
|
||||||
|
parent = Field(attribute='parent', column_name=_('Parent ID'), widget=widgets.ForeignKeyWidget(StockLocation))
|
||||||
|
parent_name = Field(attribute='parent__name', column_name=_('Parent Name'), readonly=True)
|
||||||
|
pathstring = Field(attribute='pathstring', column_name=_('Location Path'))
|
||||||
|
|
||||||
|
# Calculated fields
|
||||||
|
items = Field(attribute='item_count', column_name=_('Stock Items'), widget=widgets.IntegerWidget())
|
||||||
|
|
||||||
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
|
def after_import(self, dataset, result, using_transactions, dry_run, **kwargs):
|
||||||
"""Rebuild after import to keep tree intact."""
|
"""Rebuild after import to keep tree intact."""
|
||||||
super().after_import(dataset, result, using_transactions, dry_run, **kwargs)
|
super().after_import(dataset, result, using_transactions, dry_run, **kwargs)
|
||||||
@ -80,6 +80,23 @@ class LocationAdmin(ImportExportModelAdmin):
|
|||||||
class StockItemResource(InvenTreeResource):
|
class StockItemResource(InvenTreeResource):
|
||||||
"""Class for managing StockItem data import/export."""
|
"""Class for managing StockItem data import/export."""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = StockItem
|
||||||
|
skip_unchanged = True
|
||||||
|
report_skipped = False
|
||||||
|
clean_model_instance = True
|
||||||
|
|
||||||
|
exclude = [
|
||||||
|
# Exclude MPTT internal model fields
|
||||||
|
'lft', 'rght', 'tree_id', 'level',
|
||||||
|
# Exclude internal fields
|
||||||
|
'serial_int', 'metadata',
|
||||||
|
'barcode_hash', 'barcode_data',
|
||||||
|
'owner',
|
||||||
|
]
|
||||||
|
|
||||||
id = Field(attribute='pk', column_name=_('Stock Item ID'))
|
id = Field(attribute='pk', column_name=_('Stock Item ID'))
|
||||||
part = Field(attribute='part', column_name=_('Part ID'), widget=widgets.ForeignKeyWidget(Part))
|
part = Field(attribute='part', column_name=_('Part ID'), widget=widgets.ForeignKeyWidget(Part))
|
||||||
part_name = Field(attribute='part__full_name', column_name=_('Part Name'), readonly=True)
|
part_name = Field(attribute='part__full_name', column_name=_('Part Name'), readonly=True)
|
||||||
@ -114,23 +131,6 @@ class StockItemResource(InvenTreeResource):
|
|||||||
# Rebuild the StockItem tree(s)
|
# Rebuild the StockItem tree(s)
|
||||||
StockItem.objects.rebuild()
|
StockItem.objects.rebuild()
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = StockItem
|
|
||||||
skip_unchanged = True
|
|
||||||
report_skipped = False
|
|
||||||
clean_model_instance = True
|
|
||||||
|
|
||||||
exclude = [
|
|
||||||
# Exclude MPTT internal model fields
|
|
||||||
'lft', 'rght', 'tree_id', 'level',
|
|
||||||
# Exclude internal fields
|
|
||||||
'serial_int', 'metadata',
|
|
||||||
'barcode_hash', 'barcode_data',
|
|
||||||
'owner',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class StockItemAdmin(ImportExportModelAdmin):
|
class StockItemAdmin(ImportExportModelAdmin):
|
||||||
"""Admin class for StockItem."""
|
"""Admin class for StockItem."""
|
||||||
|
@ -46,10 +46,6 @@ class LocationBriefSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
|
class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
|
||||||
"""Brief serializers for a StockItem."""
|
"""Brief serializers for a StockItem."""
|
||||||
|
|
||||||
part_name = serializers.CharField(source='part.full_name', read_only=True)
|
|
||||||
|
|
||||||
quantity = InvenTreeDecimalField()
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -69,6 +65,10 @@ class StockItemSerializerBrief(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'barcode_hash',
|
'barcode_hash',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
part_name = serializers.CharField(source='part.full_name', read_only=True)
|
||||||
|
|
||||||
|
quantity = InvenTreeDecimalField()
|
||||||
|
|
||||||
def validate_serial(self, value):
|
def validate_serial(self, value):
|
||||||
"""Make sure serial is not to big."""
|
"""Make sure serial is not to big."""
|
||||||
if abs(extract_int(value)) > 0x7fffffff:
|
if abs(extract_int(value)) > 0x7fffffff:
|
||||||
@ -83,6 +83,60 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
- Includes serialization for the item location
|
- Includes serialization for the item location
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass options."""
|
||||||
|
|
||||||
|
model = StockItem
|
||||||
|
fields = [
|
||||||
|
'allocated',
|
||||||
|
'batch',
|
||||||
|
'belongs_to',
|
||||||
|
'build',
|
||||||
|
'customer',
|
||||||
|
'delete_on_deplete',
|
||||||
|
'expired',
|
||||||
|
'expiry_date',
|
||||||
|
'is_building',
|
||||||
|
'link',
|
||||||
|
'location',
|
||||||
|
'location_detail',
|
||||||
|
'notes',
|
||||||
|
'owner',
|
||||||
|
'packaging',
|
||||||
|
'part',
|
||||||
|
'part_detail',
|
||||||
|
'purchase_order',
|
||||||
|
'purchase_order_reference',
|
||||||
|
'pk',
|
||||||
|
'quantity',
|
||||||
|
'sales_order',
|
||||||
|
'sales_order_reference',
|
||||||
|
'serial',
|
||||||
|
'stale',
|
||||||
|
'status',
|
||||||
|
'status_text',
|
||||||
|
'stocktake_date',
|
||||||
|
'supplier_part',
|
||||||
|
'supplier_part_detail',
|
||||||
|
'tracking_items',
|
||||||
|
'barcode_hash',
|
||||||
|
'updated',
|
||||||
|
'purchase_price',
|
||||||
|
'purchase_price_currency',
|
||||||
|
]
|
||||||
|
|
||||||
|
"""
|
||||||
|
These fields are read-only in this context.
|
||||||
|
They can be updated by accessing the appropriate API endpoints
|
||||||
|
"""
|
||||||
|
read_only_fields = [
|
||||||
|
'allocated',
|
||||||
|
'barcode_hash',
|
||||||
|
'stocktake_date',
|
||||||
|
'stocktake_user',
|
||||||
|
'updated',
|
||||||
|
]
|
||||||
|
|
||||||
part = serializers.PrimaryKeyRelatedField(
|
part = serializers.PrimaryKeyRelatedField(
|
||||||
queryset=part_models.Part.objects.all(),
|
queryset=part_models.Part.objects.all(),
|
||||||
many=False, allow_null=False,
|
many=False, allow_null=False,
|
||||||
@ -197,60 +251,6 @@ class StockItemSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
if supplier_part_detail is not True:
|
if supplier_part_detail is not True:
|
||||||
self.fields.pop('supplier_part_detail')
|
self.fields.pop('supplier_part_detail')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
model = StockItem
|
|
||||||
fields = [
|
|
||||||
'allocated',
|
|
||||||
'batch',
|
|
||||||
'belongs_to',
|
|
||||||
'build',
|
|
||||||
'customer',
|
|
||||||
'delete_on_deplete',
|
|
||||||
'expired',
|
|
||||||
'expiry_date',
|
|
||||||
'is_building',
|
|
||||||
'link',
|
|
||||||
'location',
|
|
||||||
'location_detail',
|
|
||||||
'notes',
|
|
||||||
'owner',
|
|
||||||
'packaging',
|
|
||||||
'part',
|
|
||||||
'part_detail',
|
|
||||||
'purchase_order',
|
|
||||||
'purchase_order_reference',
|
|
||||||
'pk',
|
|
||||||
'quantity',
|
|
||||||
'sales_order',
|
|
||||||
'sales_order_reference',
|
|
||||||
'serial',
|
|
||||||
'stale',
|
|
||||||
'status',
|
|
||||||
'status_text',
|
|
||||||
'stocktake_date',
|
|
||||||
'supplier_part',
|
|
||||||
'supplier_part_detail',
|
|
||||||
'tracking_items',
|
|
||||||
'barcode_hash',
|
|
||||||
'updated',
|
|
||||||
'purchase_price',
|
|
||||||
'purchase_price_currency',
|
|
||||||
]
|
|
||||||
|
|
||||||
"""
|
|
||||||
These fields are read-only in this context.
|
|
||||||
They can be updated by accessing the appropriate API endpoints
|
|
||||||
"""
|
|
||||||
read_only_fields = [
|
|
||||||
'allocated',
|
|
||||||
'barcode_hash',
|
|
||||||
'stocktake_date',
|
|
||||||
'stocktake_user',
|
|
||||||
'updated',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class SerializeStockItemSerializer(serializers.Serializer):
|
class SerializeStockItemSerializer(serializers.Serializer):
|
||||||
"""A DRF serializer for "serializing" a StockItem.
|
"""A DRF serializer for "serializing" a StockItem.
|
||||||
@ -567,23 +567,6 @@ class LocationTreeSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||||
"""Detailed information about a stock location."""
|
"""Detailed information about a stock location."""
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def annotate_queryset(queryset):
|
|
||||||
"""Annotate extra information to the queryset"""
|
|
||||||
|
|
||||||
# Annotate the number of stock items which exist in this category (including subcategories)
|
|
||||||
queryset = queryset.annotate(
|
|
||||||
items=stock.filters.annotate_location_items()
|
|
||||||
)
|
|
||||||
|
|
||||||
return queryset
|
|
||||||
|
|
||||||
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
|
||||||
|
|
||||||
items = serializers.IntegerField(read_only=True)
|
|
||||||
|
|
||||||
level = serializers.IntegerField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -607,6 +590,23 @@ class LocationSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'barcode_hash',
|
'barcode_hash',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def annotate_queryset(queryset):
|
||||||
|
"""Annotate extra information to the queryset"""
|
||||||
|
|
||||||
|
# Annotate the number of stock items which exist in this category (including subcategories)
|
||||||
|
queryset = queryset.annotate(
|
||||||
|
items=stock.filters.annotate_location_items()
|
||||||
|
)
|
||||||
|
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
url = serializers.CharField(source='get_absolute_url', read_only=True)
|
||||||
|
|
||||||
|
items = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
level = serializers.IntegerField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSerializer):
|
class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSerializer):
|
||||||
"""Serializer for StockItemAttachment model."""
|
"""Serializer for StockItemAttachment model."""
|
||||||
@ -624,21 +624,6 @@ class StockItemAttachmentSerializer(InvenTree.serializers.InvenTreeAttachmentSer
|
|||||||
class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||||
"""Serializer for the StockItemTestResult model."""
|
"""Serializer for the StockItemTestResult model."""
|
||||||
|
|
||||||
user_detail = InvenTree.serializers.UserSerializer(source='user', read_only=True)
|
|
||||||
|
|
||||||
key = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(required=False)
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
"""Add detail fields."""
|
|
||||||
user_detail = kwargs.pop('user_detail', False)
|
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
if user_detail is not True:
|
|
||||||
self.fields.pop('user_detail')
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -664,30 +649,24 @@ class StockItemTestResultSerializer(InvenTree.serializers.InvenTreeModelSerializ
|
|||||||
'date',
|
'date',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class StockTrackingSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|
||||||
"""Serializer for StockItemTracking model."""
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
"""Add detail fields."""
|
"""Add detail fields."""
|
||||||
item_detail = kwargs.pop('item_detail', False)
|
|
||||||
user_detail = kwargs.pop('user_detail', False)
|
user_detail = kwargs.pop('user_detail', False)
|
||||||
|
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
if item_detail is not True:
|
|
||||||
self.fields.pop('item_detail')
|
|
||||||
|
|
||||||
if user_detail is not True:
|
if user_detail is not True:
|
||||||
self.fields.pop('user_detail')
|
self.fields.pop('user_detail')
|
||||||
|
|
||||||
label = serializers.CharField(read_only=True)
|
user_detail = InvenTree.serializers.UserSerializer(source='user', read_only=True)
|
||||||
|
|
||||||
item_detail = StockItemSerializerBrief(source='item', many=False, read_only=True)
|
key = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
user_detail = InvenTree.serializers.UserSerializer(source='user', many=False, read_only=True)
|
attachment = InvenTree.serializers.InvenTreeAttachmentSerializerField(required=False)
|
||||||
|
|
||||||
deltas = serializers.JSONField(read_only=True)
|
|
||||||
|
class StockTrackingSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
||||||
|
"""Serializer for StockItemTracking model."""
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
@ -713,6 +692,27 @@ class StockTrackingSerializer(InvenTree.serializers.InvenTreeModelSerializer):
|
|||||||
'tracking_type',
|
'tracking_type',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
"""Add detail fields."""
|
||||||
|
item_detail = kwargs.pop('item_detail', False)
|
||||||
|
user_detail = kwargs.pop('user_detail', False)
|
||||||
|
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
if item_detail is not True:
|
||||||
|
self.fields.pop('item_detail')
|
||||||
|
|
||||||
|
if user_detail is not True:
|
||||||
|
self.fields.pop('user_detail')
|
||||||
|
|
||||||
|
label = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
item_detail = StockItemSerializerBrief(source='item', many=False, read_only=True)
|
||||||
|
|
||||||
|
user_detail = InvenTree.serializers.UserSerializer(source='user', many=False, read_only=True)
|
||||||
|
|
||||||
|
deltas = serializers.JSONField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class StockAssignmentItemSerializer(serializers.Serializer):
|
class StockAssignmentItemSerializer(serializers.Serializer):
|
||||||
"""Serializer for a single StockItem with in StockAssignment request.
|
"""Serializer for a single StockItem with in StockAssignment request.
|
||||||
@ -1124,15 +1124,6 @@ class StockRemoveSerializer(StockAdjustmentSerializer):
|
|||||||
class StockTransferSerializer(StockAdjustmentSerializer):
|
class StockTransferSerializer(StockAdjustmentSerializer):
|
||||||
"""Serializer for transferring (moving) stock item(s)."""
|
"""Serializer for transferring (moving) stock item(s)."""
|
||||||
|
|
||||||
location = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=StockLocation.objects.all(),
|
|
||||||
many=False,
|
|
||||||
required=True,
|
|
||||||
allow_null=False,
|
|
||||||
label=_('Location'),
|
|
||||||
help_text=_('Destination stock location'),
|
|
||||||
)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass options."""
|
"""Metaclass options."""
|
||||||
|
|
||||||
@ -1142,6 +1133,15 @@ class StockTransferSerializer(StockAdjustmentSerializer):
|
|||||||
'location',
|
'location',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
location = serializers.PrimaryKeyRelatedField(
|
||||||
|
queryset=StockLocation.objects.all(),
|
||||||
|
many=False,
|
||||||
|
required=True,
|
||||||
|
allow_null=False,
|
||||||
|
label=_('Location'),
|
||||||
|
help_text=_('Destination stock location'),
|
||||||
|
)
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
"""Transfer stock."""
|
"""Transfer stock."""
|
||||||
request = self.context['request']
|
request = self.context['request']
|
||||||
|
@ -562,6 +562,14 @@ class Owner(models.Model):
|
|||||||
owner: Returns the Group or User instance combining the owner_type and owner_id fields
|
owner: Returns the Group or User instance combining the owner_type and owner_id fields
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""Metaclass defines extra model properties"""
|
||||||
|
# Ensure all owners are unique
|
||||||
|
constraints = [
|
||||||
|
UniqueConstraint(fields=['owner_type', 'owner_id'],
|
||||||
|
name='unique_owner')
|
||||||
|
]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_owners_matching_user(cls, user):
|
def get_owners_matching_user(cls, user):
|
||||||
"""Return all "owner" objects matching the provided user.
|
"""Return all "owner" objects matching the provided user.
|
||||||
@ -594,14 +602,6 @@ class Owner(models.Model):
|
|||||||
"""Returns the API endpoint URL associated with the Owner model"""
|
"""Returns the API endpoint URL associated with the Owner model"""
|
||||||
return reverse('api-owner-list')
|
return reverse('api-owner-list')
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass defines extra model properties"""
|
|
||||||
# Ensure all owners are unique
|
|
||||||
constraints = [
|
|
||||||
UniqueConstraint(fields=['owner_type', 'owner_id'],
|
|
||||||
name='unique_owner')
|
|
||||||
]
|
|
||||||
|
|
||||||
owner_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
|
owner_type = models.ForeignKey(ContentType, on_delete=models.CASCADE, null=True, blank=True)
|
||||||
|
|
||||||
owner_id = models.PositiveIntegerField(null=True, blank=True)
|
owner_id = models.PositiveIntegerField(null=True, blank=True)
|
||||||
|
@ -12,10 +12,6 @@ from .models import Owner
|
|||||||
class OwnerSerializer(InvenTreeModelSerializer):
|
class OwnerSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for an "Owner" (either a "user" or a "group")"""
|
"""Serializer for an "Owner" (either a "user" or a "group")"""
|
||||||
|
|
||||||
name = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
label = serializers.CharField(read_only=True)
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""Metaclass defines serializer fields."""
|
"""Metaclass defines serializer fields."""
|
||||||
model = Owner
|
model = Owner
|
||||||
@ -26,6 +22,10 @@ class OwnerSerializer(InvenTreeModelSerializer):
|
|||||||
'label',
|
'label',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
name = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
label = serializers.CharField(read_only=True)
|
||||||
|
|
||||||
|
|
||||||
class GroupSerializer(InvenTreeModelSerializer):
|
class GroupSerializer(InvenTreeModelSerializer):
|
||||||
"""Serializer for a 'Group'"""
|
"""Serializer for a 'Group'"""
|
||||||
|
Loading…
x
Reference in New Issue
Block a user