Refactor existing implementations
This commit is contained in:
parent
0d16a17fe3
commit
0943733328
|
|
@ -32,6 +32,7 @@ from InvenTree.helpers import extract_serial_numbers, hash_barcode, normalize, s
|
||||||
from InvenTree.mixins import DataImportExportSerializerMixin
|
from InvenTree.mixins import DataImportExportSerializerMixin
|
||||||
from InvenTree.serializers import (
|
from InvenTree.serializers import (
|
||||||
CustomStatusSerializerMixin,
|
CustomStatusSerializerMixin,
|
||||||
|
DuplicateOptionsSerializer,
|
||||||
FilterableSerializerMixin,
|
FilterableSerializerMixin,
|
||||||
InvenTreeCurrencySerializer,
|
InvenTreeCurrencySerializer,
|
||||||
InvenTreeDecimalField,
|
InvenTreeDecimalField,
|
||||||
|
|
@ -67,40 +68,6 @@ class TotalPriceMixin(serializers.Serializer):
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DuplicateOrderSerializer(serializers.Serializer):
|
|
||||||
"""Serializer for specifying options when duplicating an order."""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
fields = ['order_id', 'copy_lines', 'copy_extra_lines', 'copy_parameters']
|
|
||||||
|
|
||||||
order_id = serializers.IntegerField(
|
|
||||||
required=True, label=_('Order ID'), help_text=_('ID of the order to duplicate')
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_lines = serializers.BooleanField(
|
|
||||||
required=False,
|
|
||||||
default=True,
|
|
||||||
label=_('Copy Lines'),
|
|
||||||
help_text=_('Copy line items from the original order'),
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_extra_lines = serializers.BooleanField(
|
|
||||||
required=False,
|
|
||||||
default=True,
|
|
||||||
label=_('Copy Extra Lines'),
|
|
||||||
help_text=_('Copy extra line items from the original order'),
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_parameters = serializers.BooleanField(
|
|
||||||
required=False,
|
|
||||||
default=True,
|
|
||||||
label=_('Copy Parameters'),
|
|
||||||
help_text=_('Copy order parameters from the original order'),
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class AbstractOrderSerializer(
|
class AbstractOrderSerializer(
|
||||||
CustomStatusSerializerMixin,
|
CustomStatusSerializerMixin,
|
||||||
DataImportExportSerializerMixin,
|
DataImportExportSerializerMixin,
|
||||||
|
|
@ -195,12 +162,8 @@ class AbstractOrderSerializer(
|
||||||
|
|
||||||
created_by = UserSerializer(read_only=True)
|
created_by = UserSerializer(read_only=True)
|
||||||
|
|
||||||
duplicate = DuplicateOrderSerializer(
|
# Note: The 'duplicate' field must be defined by each concrete serializer class,
|
||||||
label=_('Duplicate Order'),
|
# as it requires a queryset specific to the order model type
|
||||||
help_text=_('Specify options for duplicating this order'),
|
|
||||||
required=False,
|
|
||||||
write_only=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_reference(self, reference):
|
def validate_reference(self, reference):
|
||||||
"""Custom validation for the reference field."""
|
"""Custom validation for the reference field."""
|
||||||
|
|
@ -293,30 +256,21 @@ class AbstractOrderSerializer(
|
||||||
instance = super().create(validated_data)
|
instance = super().create(validated_data)
|
||||||
|
|
||||||
if duplicate:
|
if duplicate:
|
||||||
order_id = duplicate.get('order_id', None)
|
original = duplicate['original']
|
||||||
copy_lines = duplicate.get('copy_lines', True)
|
|
||||||
copy_extra_lines = duplicate.get('copy_extra_lines', True)
|
|
||||||
copy_parameters = duplicate.get('copy_parameters', True)
|
|
||||||
|
|
||||||
try:
|
if duplicate.get('copy_lines', False):
|
||||||
copy_from = instance.__class__.objects.get(pk=order_id)
|
for line in original.lines.all():
|
||||||
except Exception:
|
|
||||||
# If the order ID is invalid, raise a validation error
|
|
||||||
raise ValidationError(_('Invalid order ID'))
|
|
||||||
|
|
||||||
if copy_lines:
|
|
||||||
for line in copy_from.lines.all():
|
|
||||||
instance.clean_line_item(line)
|
instance.clean_line_item(line)
|
||||||
line.save()
|
line.save()
|
||||||
|
|
||||||
if copy_extra_lines:
|
if duplicate.get('copy_extra_lines', False):
|
||||||
for line in copy_from.extra_lines.all():
|
for line in original.extra_lines.all():
|
||||||
line.pk = None
|
line.pk = None
|
||||||
line.order = instance
|
line.order = instance
|
||||||
line.save()
|
line.save()
|
||||||
|
|
||||||
if copy_parameters:
|
if duplicate.get('copy_parameters', False):
|
||||||
instance.copy_parameters_from(copy_from)
|
instance.copy_parameters_from(original)
|
||||||
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
@ -452,6 +406,13 @@ class PurchaseOrderSerializer(
|
||||||
|
|
||||||
return [*fields, 'duplicate']
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
|
duplicate = DuplicateOptionsSerializer(
|
||||||
|
order.models.PurchaseOrder.objects.all(),
|
||||||
|
copy_lines=True,
|
||||||
|
copy_extra_lines=True,
|
||||||
|
copy_parameters=True,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add extra information to the queryset.
|
"""Add extra information to the queryset.
|
||||||
|
|
@ -1134,6 +1095,13 @@ class SalesOrderSerializer(
|
||||||
|
|
||||||
return [*fields, 'duplicate']
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
|
duplicate = DuplicateOptionsSerializer(
|
||||||
|
order.models.SalesOrder.objects.all(),
|
||||||
|
copy_lines=True,
|
||||||
|
copy_extra_lines=True,
|
||||||
|
copy_parameters=True,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add extra information to the queryset.
|
"""Add extra information to the queryset.
|
||||||
|
|
@ -2198,6 +2166,14 @@ class ReturnOrderSerializer(
|
||||||
|
|
||||||
return [*fields, 'duplicate']
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
|
# Note: line items cannot be duplicated from a ReturnOrder,
|
||||||
|
# as they are linked to specific stock items
|
||||||
|
duplicate = DuplicateOptionsSerializer(
|
||||||
|
order.models.ReturnOrder.objects.all(),
|
||||||
|
copy_extra_lines=True,
|
||||||
|
copy_parameters=True,
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Custom annotation for the serializer queryset."""
|
"""Custom annotation for the serializer queryset."""
|
||||||
|
|
@ -2485,6 +2461,11 @@ class TransferOrderSerializer(
|
||||||
|
|
||||||
return [*fields, 'duplicate']
|
return [*fields, 'duplicate']
|
||||||
|
|
||||||
|
# Note: TransferOrder does not have "extra" line items
|
||||||
|
duplicate = DuplicateOptionsSerializer(
|
||||||
|
order.models.TransferOrder.objects.all(), copy_lines=True, copy_parameters=True
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def annotate_queryset(queryset):
|
def annotate_queryset(queryset):
|
||||||
"""Add extra information to the queryset.
|
"""Add extra information to the queryset.
|
||||||
|
|
|
||||||
|
|
@ -573,7 +573,7 @@ class PurchaseOrderTest(OrderTest):
|
||||||
|
|
||||||
# Duplicate with non-existent PK to provoke error
|
# Duplicate with non-existent PK to provoke error
|
||||||
data['duplicate'] = {
|
data['duplicate'] = {
|
||||||
'order_id': 10000001,
|
'original': 10000001,
|
||||||
'copy_lines': True,
|
'copy_lines': True,
|
||||||
'copy_extra_lines': False,
|
'copy_extra_lines': False,
|
||||||
}
|
}
|
||||||
|
|
@ -584,7 +584,7 @@ class PurchaseOrderTest(OrderTest):
|
||||||
response = self.post(reverse('api-po-list'), data, expected_code=400)
|
response = self.post(reverse('api-po-list'), data, expected_code=400)
|
||||||
|
|
||||||
data['duplicate'] = {
|
data['duplicate'] = {
|
||||||
'order_id': 1,
|
'original': 1,
|
||||||
'copy_lines': True,
|
'copy_lines': True,
|
||||||
'copy_extra_lines': False,
|
'copy_extra_lines': False,
|
||||||
}
|
}
|
||||||
|
|
@ -605,7 +605,7 @@ class PurchaseOrderTest(OrderTest):
|
||||||
data['reference'] = 'PO-9998'
|
data['reference'] = 'PO-9998'
|
||||||
|
|
||||||
data['duplicate'] = {
|
data['duplicate'] = {
|
||||||
'order_id': 1,
|
'original': 1,
|
||||||
'copy_lines': False,
|
'copy_lines': False,
|
||||||
'copy_extra_lines': True,
|
'copy_extra_lines': True,
|
||||||
}
|
}
|
||||||
|
|
@ -1792,7 +1792,7 @@ class SalesOrderTest(OrderTest):
|
||||||
{
|
{
|
||||||
'reference': 'SO-12345',
|
'reference': 'SO-12345',
|
||||||
'customer': so.customer.pk,
|
'customer': so.customer.pk,
|
||||||
'duplicate': {'order_id': so.pk, 'copy_parameters': False},
|
'duplicate': {'original': so.pk, 'copy_parameters': False},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1809,7 +1809,7 @@ class SalesOrderTest(OrderTest):
|
||||||
{
|
{
|
||||||
'reference': 'SO-12346',
|
'reference': 'SO-12346',
|
||||||
'customer': so.customer.pk,
|
'customer': so.customer.pk,
|
||||||
'duplicate': {'order_id': so.pk},
|
'duplicate': {'original': so.pk},
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -407,67 +407,6 @@ class PartBriefSerializer(
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class DuplicatePartSerializer(serializers.Serializer):
|
|
||||||
"""Serializer for specifying options when duplicating a Part.
|
|
||||||
|
|
||||||
The fields in this serializer control how the Part is duplicated.
|
|
||||||
"""
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"""Metaclass options."""
|
|
||||||
|
|
||||||
fields = [
|
|
||||||
'part',
|
|
||||||
'copy_image',
|
|
||||||
'copy_bom',
|
|
||||||
'copy_parameters',
|
|
||||||
'copy_notes',
|
|
||||||
'copy_tests',
|
|
||||||
]
|
|
||||||
|
|
||||||
part = serializers.PrimaryKeyRelatedField(
|
|
||||||
queryset=Part.objects.all(),
|
|
||||||
label=_('Original Part'),
|
|
||||||
help_text=_('Select original part to duplicate'),
|
|
||||||
required=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_image = serializers.BooleanField(
|
|
||||||
label=_('Copy Image'),
|
|
||||||
help_text=_('Copy image from original part'),
|
|
||||||
required=False,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_bom = serializers.BooleanField(
|
|
||||||
label=_('Copy BOM'),
|
|
||||||
help_text=_('Copy bill of materials from original part'),
|
|
||||||
required=False,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_parameters = serializers.BooleanField(
|
|
||||||
label=_('Copy Parameters'),
|
|
||||||
help_text=_('Copy parameter data from original part'),
|
|
||||||
required=False,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_notes = serializers.BooleanField(
|
|
||||||
label=_('Copy Notes'),
|
|
||||||
help_text=_('Copy notes from original part'),
|
|
||||||
required=False,
|
|
||||||
default=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
copy_tests = serializers.BooleanField(
|
|
||||||
label=_('Copy Tests'),
|
|
||||||
help_text=_('Copy test templates from original part'),
|
|
||||||
required=False,
|
|
||||||
default=False,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class InitialStockSerializer(serializers.Serializer):
|
class InitialStockSerializer(serializers.Serializer):
|
||||||
"""Serializer for creating initial stock quantity."""
|
"""Serializer for creating initial stock quantity."""
|
||||||
|
|
||||||
|
|
@ -1007,11 +946,42 @@ class PartSerializer(
|
||||||
)
|
)
|
||||||
|
|
||||||
# Extra fields used only for creation of a new Part instance
|
# Extra fields used only for creation of a new Part instance
|
||||||
duplicate = DuplicatePartSerializer(
|
duplicate = InvenTree.serializers.DuplicateOptionsSerializer(
|
||||||
|
Part.objects.all(),
|
||||||
label=_('Duplicate Part'),
|
label=_('Duplicate Part'),
|
||||||
help_text=_('Copy initial data from another Part'),
|
help_text=_('Copy initial data from another Part'),
|
||||||
write_only=True,
|
copy_fields=[
|
||||||
required=False,
|
{
|
||||||
|
'name': 'copy_image',
|
||||||
|
'label': _('Copy Image'),
|
||||||
|
'help_text': _('Copy image from original part'),
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'copy_bom',
|
||||||
|
'label': _('Copy BOM'),
|
||||||
|
'help_text': _('Copy bill of materials from original part'),
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'copy_parameters',
|
||||||
|
'label': _('Copy Parameters'),
|
||||||
|
'help_text': _('Copy parameter data from original part'),
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'copy_notes',
|
||||||
|
'label': _('Copy Notes'),
|
||||||
|
'help_text': _('Copy notes from original part'),
|
||||||
|
'default': True,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
'name': 'copy_tests',
|
||||||
|
'label': _('Copy Tests'),
|
||||||
|
'help_text': _('Copy test templates from original part'),
|
||||||
|
'default': False,
|
||||||
|
},
|
||||||
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
initial_stock = InitialStockSerializer(
|
initial_stock = InitialStockSerializer(
|
||||||
|
|
@ -1077,7 +1047,7 @@ class PartSerializer(
|
||||||
|
|
||||||
# Copy data from original Part
|
# Copy data from original Part
|
||||||
if duplicate:
|
if duplicate:
|
||||||
original = duplicate['part']
|
original = duplicate['original']
|
||||||
|
|
||||||
if duplicate.get('copy_bom', False):
|
if duplicate.get('copy_bom', False):
|
||||||
instance.copy_bom_from(original)
|
instance.copy_bom_from(original)
|
||||||
|
|
|
||||||
|
|
@ -1649,7 +1649,7 @@ class PartCreationTests(PartAPITestBase):
|
||||||
'testable': do_copy,
|
'testable': do_copy,
|
||||||
'assembly': do_copy,
|
'assembly': do_copy,
|
||||||
'duplicate': {
|
'duplicate': {
|
||||||
'part': 100,
|
'original': 100,
|
||||||
'copy_bom': do_copy,
|
'copy_bom': do_copy,
|
||||||
'copy_notes': do_copy,
|
'copy_notes': do_copy,
|
||||||
'copy_image': do_copy,
|
'copy_image': do_copy,
|
||||||
|
|
|
||||||
|
|
@ -156,7 +156,7 @@ export function usePartFields({
|
||||||
fields.duplicate = {
|
fields.duplicate = {
|
||||||
icon: <IconCopy />,
|
icon: <IconCopy />,
|
||||||
children: {
|
children: {
|
||||||
part: {
|
original: {
|
||||||
value: duplicatePartInstance?.pk,
|
value: duplicatePartInstance?.pk,
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -316,7 +316,7 @@ export function usePurchaseOrderFields({
|
||||||
if (!!duplicateOrderId) {
|
if (!!duplicateOrderId) {
|
||||||
fields.duplicate = {
|
fields.duplicate = {
|
||||||
children: {
|
children: {
|
||||||
order_id: {
|
original: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
value: duplicateOrderId
|
value: duplicateOrderId
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -84,15 +84,10 @@ export function useReturnOrderFields({
|
||||||
if (!!duplicateOrderId) {
|
if (!!duplicateOrderId) {
|
||||||
fields.duplicate = {
|
fields.duplicate = {
|
||||||
children: {
|
children: {
|
||||||
order_id: {
|
original: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
value: duplicateOrderId
|
value: duplicateOrderId
|
||||||
},
|
},
|
||||||
copy_lines: {
|
|
||||||
// Cannot duplicate lines from a return order!
|
|
||||||
value: false,
|
|
||||||
hidden: true
|
|
||||||
},
|
|
||||||
copy_extra_lines: {},
|
copy_extra_lines: {},
|
||||||
copy_parameters: {}
|
copy_parameters: {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -97,7 +97,7 @@ export function useSalesOrderFields({
|
||||||
if (!!duplicateOrderId) {
|
if (!!duplicateOrderId) {
|
||||||
fields.duplicate = {
|
fields.duplicate = {
|
||||||
children: {
|
children: {
|
||||||
order_id: {
|
original: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
value: duplicateOrderId
|
value: duplicateOrderId
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -54,13 +54,12 @@ export function useTransferOrderFields({
|
||||||
if (!!duplicateOrderId) {
|
if (!!duplicateOrderId) {
|
||||||
fields.duplicate = {
|
fields.duplicate = {
|
||||||
children: {
|
children: {
|
||||||
order_id: {
|
original: {
|
||||||
hidden: true,
|
hidden: true,
|
||||||
value: duplicateOrderId
|
value: duplicateOrderId
|
||||||
},
|
},
|
||||||
copy_lines: {},
|
copy_lines: {},
|
||||||
// Transfer Orders don't have extra lines for now...
|
copy_parameters: {}
|
||||||
copy_extra_lines: { hidden: true, value: false }
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue