2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-03-11 22:54:17 +00:00

Copy order params (#11479)

* Copy parameters when duplicating an order

* Add unit test for order parameter duplication

* Bunmp API version

* Fix test reliability

* Disable image fetching for SampleSupplierPlugin

- Allow turning on manually
- Prevent CI issues due to rate limiting

* Revery pypdf.. ???
This commit is contained in:
Oliver
2026-03-11 19:26:56 +11:00
committed by GitHub
parent bcde2706c7
commit 27809d712a
8 changed files with 102 additions and 10 deletions

View File

@@ -1,11 +1,14 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 460
INVENTREE_API_VERSION = 461
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v461 -> 2026-03-10 : https://github.com/inventree/InvenTree/pull/11479
- Adds option to copy parameters when duplicating an order via the API
v460 -> 2026-02-25 : https://github.com/inventree/InvenTree/pull/11374
- Adds "updated_at" field to PurchaseOrder, SalesOrder and ReturnOrder API endpoints
- Adds "updated_before" and "updated_after" date filters to all three order list endpoints

View File

@@ -75,7 +75,7 @@ class DuplicateOrderSerializer(serializers.Serializer):
class Meta:
"""Metaclass options."""
fields = ['order_id', 'copy_lines', 'copy_extra_lines']
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')
@@ -95,6 +95,13 @@ class DuplicateOrderSerializer(serializers.Serializer):
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(
DataImportExportSerializerMixin, FilterableSerializerMixin, serializers.Serializer
@@ -242,6 +249,7 @@ class AbstractOrderSerializer(
order_id = duplicate.get('order_id', None)
copy_lines = duplicate.get('copy_lines', True)
copy_extra_lines = duplicate.get('copy_extra_lines', True)
copy_parameters = duplicate.get('copy_parameters', True)
try:
copy_from = instance.__class__.objects.get(pk=order_id)
@@ -260,6 +268,9 @@ class AbstractOrderSerializer(
line.order = instance
line.save()
if copy_parameters:
instance.copy_parameters_from(copy_from)
return instance

View File

@@ -1574,6 +1574,65 @@ class SalesOrderTest(OrderTest):
expected_code=201,
)
def test_so_duplicate(self):
"""Test SalesOrder duplication via the API."""
from common.models import Parameter, ParameterTemplate
url = reverse('api-so-list')
self.assignRole('sales_order.add')
so = models.SalesOrder.objects.get(pk=1)
self.assertEqual(so.status, SalesOrderStatus.PENDING)
# Add some parameters to the sales order
for idx in range(5):
template = ParameterTemplate.objects.create(name=f'Template {idx}')
Parameter.objects.create(
template=template,
model_type=so.get_content_type(),
model_id=so.pk,
data=f'Value {idx}',
)
self.assertEqual(so.parameters.count(), 5)
# Create a duplicate of this sales order
# We explicitly specify "copy_parameters" as False, so the duplicated sales order should not have any parameters
response = self.post(
url,
{
'reference': 'SO-12345',
'customer': so.customer.pk,
'duplicate': {'order_id': so.pk, 'copy_parameters': False},
},
)
duplicate_id = response.data['pk']
duplicate_so = models.SalesOrder.objects.get(pk=duplicate_id)
self.assertEqual(duplicate_so.reference, 'SO-12345')
self.assertEqual(duplicate_so.customer, so.customer)
self.assertEqual(duplicate_so.parameters.count(), 0)
# Duplicate again, with default values for the "duplicate" options (which should result in parameters being copied)
response = self.post(
url,
{
'reference': 'SO-12346',
'customer': so.customer.pk,
'duplicate': {'order_id': so.pk},
},
)
duplicate_id = response.data['pk']
duplicate_so = models.SalesOrder.objects.get(pk=duplicate_id)
self.assertEqual(duplicate_so.reference, 'SO-12346')
self.assertEqual(duplicate_so.customer, so.customer)
self.assertEqual(duplicate_so.parameters.count(), 5)
def test_so_cancel(self):
"""Test API endpoint for cancelling a SalesOrder."""
so = models.SalesOrder.objects.get(pk=1)

View File

@@ -1,5 +1,7 @@
"""Sample supplier plugin."""
from django.conf import settings
from company.models import Company, ManufacturerPart, SupplierPart, SupplierPriceBreak
from part.models import Part
from plugin.mixins import SupplierMixin, supplier
@@ -13,7 +15,16 @@ class SampleSupplierPlugin(SupplierMixin, InvenTreePlugin):
SLUG = 'samplesupplier'
TITLE = 'My sample supplier plugin'
VERSION = '0.0.1'
VERSION = '0.0.2'
SETTINGS = {
'DOWNLOAD_IMAGES': {
'name': 'Download part images',
'description': 'Enable downloading of part images during import (not recommended during testing)',
'validator': bool,
'default': False,
}
}
def __init__(self):
"""Initialize the sample supplier plugin."""
@@ -108,7 +119,12 @@ class SampleSupplierPlugin(SupplierMixin, InvenTreePlugin):
# If the part was created, set additional fields
if created:
if data['image_url']:
# Prevent downloading images during testing, as this can lead to unreliable tests
if (
data['image_url']
and not settings.TESTING
and self.get_setting('DOWNLOAD_IMAGES')
):
file, fmt = self.download_image(data['image_url'])
filename = f'part_{part.pk}_image.{fmt.lower()}'
part.image.save(filename, file)

View File

@@ -1669,9 +1669,9 @@ pynacl==1.6.2 \
# via
# -c src/backend/requirements.txt
# paramiko
pypdf==6.8.0 \
--hash=sha256:2a025080a8dd73f48123c89c57174a5ff3806c71763ee4e49572dc90454943c7 \
--hash=sha256:cb7eaeaa4133ce76f762184069a854e03f4d9a08568f0e0623f7ea810407833b
pypdf==6.7.5 \
--hash=sha256:07ba7f1d6e6d9aa2a17f5452e320a84718d4ce863367f7ede2fd72280349ab13 \
--hash=sha256:40bb2e2e872078655f12b9b89e2f900888bb505e88a82150b64f9f34fa25651d
# via
# -c src/backend/requirements.txt
# -r src/backend/requirements.in

View File

@@ -292,7 +292,8 @@ export function usePurchaseOrderFields({
value: duplicateOrderId
},
copy_lines: {},
copy_extra_lines: {}
copy_extra_lines: {},
copy_parameters: {}
}
};
}

View File

@@ -91,7 +91,8 @@ export function useReturnOrderFields({
value: false,
hidden: true
},
copy_extra_lines: {}
copy_extra_lines: {},
copy_parameters: {}
}
};
}

View File

@@ -90,7 +90,8 @@ export function useSalesOrderFields({
value: duplicateOrderId
},
copy_lines: {},
copy_extra_lines: {}
copy_extra_lines: {},
copy_parameters: {}
}
};
}