2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-29 12:06:44 +00:00

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): def is_complete(self):
return self.shipment_date is not None return self.shipment_date is not None
def check_can_complete(self): def check_can_complete(self, raise_error=True):
if self.shipment_date: try:
# Shipment has already been sent! if self.shipment_date:
raise ValidationError(_("Shipment has already been sent")) # Shipment has already been sent!
raise ValidationError(_("Shipment has already been sent"))
if self.allocations.count() == 0: if self.allocations.count() == 0:
raise ValidationError(_("Shipment has no allocated stock items")) raise ValidationError(_("Shipment has no allocated stock items"))
except ValidationError as e:
if raise_error:
raise e
else:
return False
return True
@transaction.atomic @transaction.atomic
def complete_shipment(self, user, **kwargs): def complete_shipment(self, user, **kwargs):
@ -1235,7 +1244,7 @@ class SalesOrderShipment(models.Model):
allocation.complete_allocation(user) allocation.complete_allocation(user)
# Update the "shipment" date # Update the "shipment" date
self.shipment_date = datetime.now() self.shipment_date = kwargs.get('shipment_date', datetime.now())
self.shipped_by = user self.shipped_by = user
# Was a tracking number provided? # Was a tracking number provided?

View File

@ -2,6 +2,7 @@
JSON serializers for the Order API JSON serializers for the Order API
""" """
from datetime import datetime
from decimal import Decimal from decimal import Decimal
from django.core.exceptions import ValidationError as DjangoValidationError from django.core.exceptions import ValidationError as DjangoValidationError
@ -899,6 +900,7 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer):
fields = [ fields = [
'tracking_number', 'tracking_number',
'shipment_date',
] ]
def validate(self, data): def validate(self, data):
@ -910,7 +912,7 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer):
if not shipment: if not shipment:
raise ValidationError(_("No shipment details provided")) raise ValidationError(_("No shipment details provided"))
shipment.check_can_complete() shipment.check_can_complete(raise_error=True)
return data return data
@ -927,9 +929,16 @@ class SalesOrderShipmentCompleteSerializer(serializers.ModelSerializer):
user = request.user user = request.user
# Extract provided tracking number (optional) # 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): class SalesOrderShipmentAllocationItemSerializer(serializers.Serializer):

View File

@ -5,6 +5,7 @@ Tests for the Order API
import io import io
from datetime import datetime, timedelta from datetime import datetime, timedelta
from django.core.exceptions import ValidationError
from django.urls import reverse from django.urls import reverse
from rest_framework import status from rest_framework import status
@ -1275,3 +1276,96 @@ class SalesOrderAllocateTest(OrderTest):
for line in self.order.lines.all(): for line in self.order.lines.all():
self.assertEqual(line.allocations.count(), 1) 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', method: 'POST',
title: `{% trans "Complete Shipment" %} ${shipment.reference}`, title: `{% trans "Complete Shipment" %} ${shipment.reference}`,
fields: { fields: {
tracking_number: {}, tracking_number: {
value: shipment.tracking_number,
},
shipment_date: {
value: moment().format('YYYY-MM-DD'),
}
}, },
preFormContent: html, preFormContent: html,
confirm: true, confirm: true,