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
This commit is contained in:
Oliver 2022-05-23 13:57:40 +10:00 committed by GitHub
parent 840ade25cd
commit 3c02b95f85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 128 additions and 11 deletions

View File

@ -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?

View File

@ -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):

View File

@ -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())

View File

@ -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,