From 3c02b95f85f4f12120d9675d158d0c44c9962153 Mon Sep 17 00:00:00 2001 From: Oliver Date: Mon, 23 May 2022 13:57:40 +1000 Subject: [PATCH] Shipment date edit (#3050) * Allow user to select shipment date when shipping a salesorder - Defaults to 'today' * Retain the tracking number information through the from * JS linting * Add unit testing for the SalesOrderShipmentComplete serializer / API endpoint --- InvenTree/order/models.py | 23 ++++-- InvenTree/order/serializers.py | 15 +++- InvenTree/order/test_api.py | 94 ++++++++++++++++++++++ InvenTree/templates/js/translated/order.js | 7 +- 4 files changed, 128 insertions(+), 11 deletions(-) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 204130578f..550070e5df 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -1205,14 +1205,23 @@ class SalesOrderShipment(models.Model): def is_complete(self): return self.shipment_date is not None - def check_can_complete(self): + def check_can_complete(self, raise_error=True): - if self.shipment_date: - # Shipment has already been sent! - raise ValidationError(_("Shipment has already been sent")) + try: + if self.shipment_date: + # Shipment has already been sent! + raise ValidationError(_("Shipment has already been sent")) - if self.allocations.count() == 0: - raise ValidationError(_("Shipment has no allocated stock items")) + if self.allocations.count() == 0: + raise ValidationError(_("Shipment has no allocated stock items")) + + except ValidationError as e: + if raise_error: + raise e + else: + return False + + return True @transaction.atomic def complete_shipment(self, user, **kwargs): @@ -1235,7 +1244,7 @@ class SalesOrderShipment(models.Model): allocation.complete_allocation(user) # Update the "shipment" date - self.shipment_date = datetime.now() + self.shipment_date = kwargs.get('shipment_date', datetime.now()) self.shipped_by = user # Was a tracking number provided? diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index 379b9d5fc4..685697f99f 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -2,6 +2,7 @@ JSON serializers for the Order API """ +from datetime import datetime from decimal import Decimal from django.core.exceptions import ValidationError as DjangoValidationError @@ -899,6 +900,7 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer): fields = [ 'tracking_number', + 'shipment_date', ] def validate(self, data): @@ -910,7 +912,7 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer): if not shipment: raise ValidationError(_("No shipment details provided")) - shipment.check_can_complete() + shipment.check_can_complete(raise_error=True) return data @@ -927,9 +929,16 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer): user = request.user # Extract provided tracking number (optional) - tracking_number = data.get('tracking_number', None) + tracking_number = data.get('tracking_number', shipment.tracking_number) - shipment.complete_shipment(user, tracking_number=tracking_number) + # Extract shipping date (defaults to today's date) + shipment_date = data.get('shipment_date', datetime.now()) + + shipment.complete_shipment( + user, + tracking_number=tracking_number, + shipment_date=shipment_date, + ) class SalesOrderShipmentAllocationItemSerializer(serializers.Serializer): diff --git a/InvenTree/order/test_api.py b/InvenTree/order/test_api.py index ba95a243f0..dd17a033c9 100644 --- a/InvenTree/order/test_api.py +++ b/InvenTree/order/test_api.py @@ -5,6 +5,7 @@ Tests for the Order API import io from datetime import datetime, timedelta +from django.core.exceptions import ValidationError from django.urls import reverse from rest_framework import status @@ -1275,3 +1276,96 @@ class SalesOrderAllocateTest(OrderTest): for line in self.order.lines.all(): self.assertEqual(line.allocations.count(), 1) + + def test_shipment_complete(self): + """Test that we can complete a shipment via the API""" + + url = reverse('api-so-shipment-ship', kwargs={'pk': self.shipment.pk}) + + self.assertFalse(self.shipment.is_complete()) + self.assertFalse(self.shipment.check_can_complete(raise_error=False)) + + with self.assertRaises(ValidationError): + self.shipment.check_can_complete() + + # Attempting to complete this shipment via the API should fail + response = self.post( + url, {}, + expected_code=400 + ) + + self.assertIn('Shipment has no allocated stock items', str(response.data)) + + # Allocate stock against this shipment + line = self.order.lines.first() + part = line.part + + models.SalesOrderAllocation.objects.create( + shipment=self.shipment, + line=line, + item=part.stock_items.last(), + quantity=5 + ) + + # Shipment should now be able to be completed + self.assertTrue(self.shipment.check_can_complete()) + + # Attempt with an invalid date + response = self.post( + url, + { + 'shipment_date': 'asfasd', + }, + expected_code=400, + ) + + self.assertIn('Date has wrong format', str(response.data)) + + response = self.post( + url, + { + 'tracking_number': 'TRK12345', + 'shipment_date': '2020-12-05', + }, + expected_code=201, + ) + + self.shipment.refresh_from_db() + + self.assertTrue(self.shipment.is_complete()) + self.assertEqual(self.shipment.tracking_number, 'TRK12345') + + def test_sales_order_shipment_list(self): + + url = reverse('api-so-shipment-list') + + # Create some new shipments via the API + for order in models.SalesOrder.objects.all(): + + for idx in range(3): + self.post( + url, + { + 'order': order.pk, + 'reference': f"SH{idx + 1}", + 'tracking_number': f"TRK_{order.pk}_{idx}" + }, + expected_code=201 + ) + + # Filter API by order + response = self.get( + url, + { + 'order': order.pk, + }, + expected_code=200, + ) + + # 3 shipments returned for each SalesOrder instance + self.assertGreaterEqual(len(response.data), 3) + + # List *all* shipments + response = self.get(url, expected_code=200) + + self.assertEqual(len(response.data), 1 + 3 * models.SalesOrder.objects.count()) diff --git a/InvenTree/templates/js/translated/order.js b/InvenTree/templates/js/translated/order.js index ad9849edb8..5fa0d2f77a 100644 --- a/InvenTree/templates/js/translated/order.js +++ b/InvenTree/templates/js/translated/order.js @@ -129,7 +129,12 @@ function completeShipment(shipment_id, options={}) { method: 'POST', title: `{% trans "Complete Shipment" %} ${shipment.reference}`, fields: { - tracking_number: {}, + tracking_number: { + value: shipment.tracking_number, + }, + shipment_date: { + value: moment().format('YYYY-MM-DD'), + } }, preFormContent: html, confirm: true,