From 88fce1e813efdfb54d74a8209d1c1d1d2c470026 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 3 Dec 2021 18:42:36 +1100 Subject: [PATCH] Unit test fixes --- InvenTree/order/models.py | 28 +++++++++++++++++++++- InvenTree/order/serializers.py | 10 ++++---- InvenTree/order/test_migrations.py | 1 - InvenTree/order/test_sales_order.py | 37 +++++++++++++++++++++++------ InvenTree/order/views.py | 4 +--- 5 files changed, 63 insertions(+), 17 deletions(-) diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 0c5fe5e3d4..e88572ae55 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -617,11 +617,34 @@ class SalesOrder(Order): def is_completed(self): """ Check if this order is "shipped" (all line items delivered), - and mark it as "shipped" if so. """ return self.lines.count() > 0 and all([line.is_completed() for line in self.lines.all()]) + def complete_order(self, user): + """ + Mark this order as "complete" + """ + + if self.lines.count() == 0: + # Order without line items cannot be completed + raise ValidationError(_('Order cannot be completed as no parts have been assigned')) + + if self.status != SalesOrderStatus.PENDING: + # Only a PENDING order can be marked as SHIPPED + raise ValidationError(_('Only a pending order can be marked as complete')) + + # Check if there are any incomplete shipments + for shipment in self.shipments.all(): + if not shipment.shipment_date: + raise ValidationError(_('Order cannot be completed as there are pending shipments')) + + self.status = SalesOrderStatus.SHIPPED + self.shipped_by = user + self.shipment_date = datetime.now() + + self.save() + def can_cancel(self): """ Return True if this order can be cancelled @@ -988,6 +1011,9 @@ class SalesOrderShipment(models.Model): help_text=_('Shipment tracking information'), ) + def is_complete(self): + return self.shipment_date is not None + def check_can_complete(self): if self.shipment_date: diff --git a/InvenTree/order/serializers.py b/InvenTree/order/serializers.py index b0af634a00..cb082dc1f7 100644 --- a/InvenTree/order/serializers.py +++ b/InvenTree/order/serializers.py @@ -26,7 +26,7 @@ from InvenTree.serializers import InvenTreeModelSerializer from InvenTree.serializers import InvenTreeDecimalField from InvenTree.serializers import InvenTreeMoneySerializer from InvenTree.serializers import ReferenceIndexingSerializerMixin -from InvenTree.status_codes import StockStatus, SalesOrderStatus +from InvenTree.status_codes import StockStatus import order.models @@ -745,11 +745,11 @@ class SalesOrderCompleteSerializer(serializers.Serializer): request = self.context['request'] order = self.context['order'] - data = self.validated_data + # data = self.validated_data - # Mark this order as complete! - order.status = SalesOrderStatus.SHIPPED - order.save() + user = getattr(request, 'user', None) + + order.complete_order(user) class SOShipmentAllocationSerializer(serializers.Serializer): diff --git a/InvenTree/order/test_migrations.py b/InvenTree/order/test_migrations.py index 97ced6dbbf..3afba65223 100644 --- a/InvenTree/order/test_migrations.py +++ b/InvenTree/order/test_migrations.py @@ -4,7 +4,6 @@ Unit tests for the 'order' model data migrations from django_test_migrations.contrib.unittest_case import MigratorTestCase -from InvenTree import helpers from InvenTree.status_codes import SalesOrderStatus diff --git a/InvenTree/order/test_sales_order.py b/InvenTree/order/test_sales_order.py index 40fab98349..a4fda63a46 100644 --- a/InvenTree/order/test_sales_order.py +++ b/InvenTree/order/test_sales_order.py @@ -10,7 +10,7 @@ from company.models import Company from InvenTree import status_codes as status -from order.models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation +from order.models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation, SalesOrderShipment from part.models import Part @@ -42,6 +42,12 @@ class SalesOrderTest(TestCase): customer_reference='ABC 55555' ) + # Create a Shipment against this SalesOrder + self.shipment = SalesOrderShipment.objects.create( + order=self.order, + reference='001', + ) + # Create a line item self.line = SalesOrderLineItem.objects.create(quantity=50, order=self.order, part=self.part) @@ -86,11 +92,13 @@ class SalesOrderTest(TestCase): # Allocate stock to the order SalesOrderAllocation.objects.create( line=self.line, + shipment=self.shipment, item=StockItem.objects.get(pk=self.Sa.pk), quantity=25) SalesOrderAllocation.objects.create( line=self.line, + shipment=self.shipment, item=StockItem.objects.get(pk=self.Sb.pk), quantity=25 if full else 20 ) @@ -126,9 +134,9 @@ class SalesOrderTest(TestCase): # Now try to ship it - should fail with self.assertRaises(ValidationError): - self.order.ship_order(None) + self.order.complete_order(None) - def test_ship_order(self): + def test_complete_order(self): # Allocate line items, then ship the order # Assert some stuff before we run the test @@ -140,7 +148,22 @@ class SalesOrderTest(TestCase): self.assertEqual(SalesOrderAllocation.objects.count(), 2) - self.order.ship_order(None) + # Attempt to complete the order (but shipments are not completed!) + with self.assertRaises(ValidationError): + self.order.complete_order(None) + + self.assertIsNone(self.shipment.shipment_date) + self.assertFalse(self.shipment.is_complete()) + + # Mark the shipments as complete + self.shipment.complete_shipment(None) + self.assertTrue(self.shipment.is_complete()) + + # Now, should be OK to ship + self.order.complete_order(None) + + self.assertEqual(self.order.status, status.SalesOrderStatus.SHIPPED) + self.assertIsNotNone(self.order.shipment_date) # There should now be 4 stock items self.assertEqual(StockItem.objects.count(), 4) @@ -162,12 +185,12 @@ class SalesOrderTest(TestCase): self.assertEqual(sa.sales_order, None) self.assertEqual(sb.sales_order, None) - # And no allocations - self.assertEqual(SalesOrderAllocation.objects.count(), 0) + # And the allocations still exist + self.assertEqual(SalesOrderAllocation.objects.count(), 2) self.assertEqual(self.order.status, status.SalesOrderStatus.SHIPPED) self.assertTrue(self.order.is_fully_allocated()) self.assertTrue(self.line.is_fully_allocated()) self.assertEqual(self.line.fulfilled_quantity(), 50) - self.assertEqual(self.line.allocated_quantity(), 0) + self.assertEqual(self.line.allocated_quantity(), 50) diff --git a/InvenTree/order/views.py b/InvenTree/order/views.py index 2c3e125dc0..6eec7145ee 100644 --- a/InvenTree/order/views.py +++ b/InvenTree/order/views.py @@ -792,14 +792,12 @@ class OrderParts(AjaxView): order.add_line_item(supplier_part, quantity, purchase_price=purchase_price) - -#### TODO: This class MUST be converted to the API forms! -#### TODO: We MUST select the shipment class SalesOrderAssignSerials(AjaxView, FormMixin): """ View for assigning stock items to a sales order, by serial number lookup. """ + # TODO: Remove this class and replace with an API endpoint model = SalesOrderAllocation role_required = 'sales_order.change'