From f6831558a44bbc53c0bb45d6f2d03340a56cc845 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 28 Apr 2023 06:54:50 +1000 Subject: [PATCH] Fix export of order data (#4714) * Adds extra unit test for exporting sales orders - Exporting sales orders to .xls currently throws exception * Fix 'total_price' field when exporting orders * Fix for unit test --- InvenTree/InvenTree/api_tester.py | 3 +-- InvenTree/order/admin.py | 44 ++++++++++++++++++++++++------- InvenTree/order/test_api.py | 22 ++++++++++++++++ 3 files changed, 58 insertions(+), 11 deletions(-) diff --git a/InvenTree/InvenTree/api_tester.py b/InvenTree/InvenTree/api_tester.py index 75e41a6bae..56b691d69d 100644 --- a/InvenTree/InvenTree/api_tester.py +++ b/InvenTree/InvenTree/api_tester.py @@ -265,8 +265,7 @@ class InvenTreeAPITestCase(ExchangeRateMixin, UserMixin, APITestCase): """Download a file from the server, and return an in-memory file.""" response = self.client.get(url, data=data, format='json') - if expected_code is not None: - self.assertEqual(response.status_code, expected_code) + self.checkResponse(url, 'DOWNLOAD_FILE', expected_code, response) # Check that the response is of the correct type if not isinstance(response, StreamingHttpResponse): diff --git a/InvenTree/order/admin.py b/InvenTree/order/admin.py index 479c350256..648d71c8d8 100644 --- a/InvenTree/order/admin.py +++ b/InvenTree/order/admin.py @@ -24,6 +24,32 @@ class ProjectCodeResourceMixin: return '' +class TotalPriceResourceMixin: + """Mixin for exporting total price data""" + + total_price = Field(attribute='total_price', column_name=_('Total Price')) + + def dehydrate_total_price(self, order): + """Return the total price amount, not the object itself""" + if order.total_price: + return order.total_price.amount + else: + return '' + + +class PriceResourceMixin: + """Mixin for 'price' field""" + + price = Field(attribute='price', column_name=_('Price')) + + def dehydrate_price(self, line): + """Return the price amount, not the object itself""" + if line.price: + return line.price.amount + else: + return '' + + # region general classes class GeneralExtraLineAdmin: """Admin class template for the 'ExtraLineItem' models""" @@ -108,7 +134,7 @@ class SalesOrderAdmin(ImportExportModelAdmin): autocomplete_fields = ('customer',) -class PurchaseOrderResource(ProjectCodeResourceMixin, InvenTreeResource): +class PurchaseOrderResource(ProjectCodeResourceMixin, TotalPriceResourceMixin, InvenTreeResource): """Class for managing import / export of PurchaseOrder data.""" class Meta: @@ -127,7 +153,7 @@ class PurchaseOrderResource(ProjectCodeResourceMixin, InvenTreeResource): overdue = Field(attribute='is_overdue', widget=widgets.BooleanWidget(), readonly=True) -class PurchaseOrderLineItemResource(InvenTreeResource): +class PurchaseOrderLineItemResource(PriceResourceMixin, InvenTreeResource): """Class for managing import / export of PurchaseOrderLineItem data.""" class Meta: @@ -146,7 +172,7 @@ class PurchaseOrderLineItemResource(InvenTreeResource): SKU = Field(attribute='part__SKU', readonly=True) -class PurchaseOrderExtraLineResource(InvenTreeResource): +class PurchaseOrderExtraLineResource(PriceResourceMixin, InvenTreeResource): """Class for managing import / export of PurchaseOrderExtraLine data.""" class Meta(GeneralExtraLineMeta): @@ -155,7 +181,7 @@ class PurchaseOrderExtraLineResource(InvenTreeResource): model = models.PurchaseOrderExtraLine -class SalesOrderResource(ProjectCodeResourceMixin, InvenTreeResource): +class SalesOrderResource(ProjectCodeResourceMixin, TotalPriceResourceMixin, InvenTreeResource): """Class for managing import / export of SalesOrder data.""" class Meta: @@ -174,7 +200,7 @@ class SalesOrderResource(ProjectCodeResourceMixin, InvenTreeResource): overdue = Field(attribute='is_overdue', widget=widgets.BooleanWidget(), readonly=True) -class SalesOrderLineItemResource(InvenTreeResource): +class SalesOrderLineItemResource(PriceResourceMixin, InvenTreeResource): """Class for managing import / export of SalesOrderLineItem data.""" class Meta: @@ -203,7 +229,7 @@ class SalesOrderLineItemResource(InvenTreeResource): return '' -class SalesOrderExtraLineResource(InvenTreeResource): +class SalesOrderExtraLineResource(PriceResourceMixin, InvenTreeResource): """Class for managing import / export of SalesOrderExtraLine data.""" class Meta(GeneralExtraLineMeta): @@ -290,7 +316,7 @@ class SalesOrderAllocationAdmin(ImportExportModelAdmin): autocomplete_fields = ('line', 'shipment', 'item',) -class ReturnOrderResource(ProjectCodeResourceMixin, InvenTreeResource): +class ReturnOrderResource(ProjectCodeResourceMixin, TotalPriceResourceMixin, InvenTreeResource): """Class for managing import / export of ReturnOrder data""" class Meta: @@ -327,7 +353,7 @@ class ReturnOrderAdmin(ImportExportModelAdmin): ] -class ReturnOrderLineItemResource(InvenTreeResource): +class ReturnOrderLineItemResource(PriceResourceMixin, InvenTreeResource): """Class for managing import / export of ReturnOrderLineItem data""" class Meta: @@ -350,7 +376,7 @@ class ReturnOrderLineItemAdmin(ImportExportModelAdmin): ] -class ReturnOrderExtraLineClass(InvenTreeResource): +class ReturnOrderExtraLineClass(PriceResourceMixin, InvenTreeResource): """Class for managing import/export of ReturnOrderExtraLine data""" class Meta(GeneralExtraLineMeta): diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py index f8e6846a5c..42b404316b 100644 --- a/InvenTree/order/test_api.py +++ b/InvenTree/order/test_api.py @@ -1452,6 +1452,28 @@ class SalesOrderTest(OrderTest): self.assertGreaterEqual(n_events, 1) self.assertEqual(number_orders_incl_complete, n_events) + def test_export(self): + """Test we can export the SalesOrder list""" + + n = models.SalesOrder.objects.count() + + # Check there are some sales orders + self.assertGreater(n, 0) + + for order in models.SalesOrder.objects.all(): + # Reconstruct the total price + order.save() + + # Download file, check we get a 200 response + for fmt in ['csv', 'xls', 'xlsx']: + self.download_file( + reverse('api-so-list'), + {'export': fmt}, + decode=True if fmt == 'csv' else False, + expected_code=200, + expected_fn=f"InvenTree_SalesOrders.{fmt}" + ) + class SalesOrderLineItemTest(OrderTest): """Tests for the SalesOrderLineItem API."""