InvenTree/InvenTree/order/test_sales_order.py
2021-12-04 17:30:13 +11:00

203 lines
6.6 KiB
Python

# -*- coding: utf-8 -*-
from django.test import TestCase
from django.core.exceptions import ValidationError
from datetime import datetime, timedelta
from company.models import Company
from InvenTree import status_codes as status
from order.models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation, SalesOrderShipment
from part.models import Part
from stock.models import StockItem
class SalesOrderTest(TestCase):
"""
Run tests to ensure that the SalesOrder model is working correctly.
"""
def setUp(self):
# Create a Company to ship the goods to
self.customer = Company.objects.create(name="ABC Co", description="My customer", is_customer=True)
# Create a Part to ship
self.part = Part.objects.create(name='Spanner', salable=True, description='A spanner that I sell')
# Create some stock!
self.Sa = StockItem.objects.create(part=self.part, quantity=100)
self.Sb = StockItem.objects.create(part=self.part, quantity=200)
# Create a SalesOrder to ship against
self.order = SalesOrder.objects.create(
customer=self.customer,
reference='1234',
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)
def test_overdue(self):
"""
Tests for overdue functionality
"""
today = datetime.now().date()
# By default, order is *not* overdue as the target date is not set
self.assertFalse(self.order.is_overdue)
# Set target date in the past
self.order.target_date = today - timedelta(days=5)
self.order.save()
self.assertTrue(self.order.is_overdue)
# Set target date in the future
self.order.target_date = today + timedelta(days=5)
self.order.save()
self.assertFalse(self.order.is_overdue)
def test_empty_order(self):
self.assertEqual(self.line.quantity, 50)
self.assertEqual(self.line.allocated_quantity(), 0)
self.assertEqual(self.line.fulfilled_quantity(), 0)
self.assertFalse(self.line.is_fully_allocated())
self.assertFalse(self.line.is_over_allocated())
self.assertTrue(self.order.is_pending)
self.assertFalse(self.order.is_fully_allocated())
def test_add_duplicate_line_item(self):
# Adding a duplicate line item to a SalesOrder is accepted
for ii in range(1, 5):
SalesOrderLineItem.objects.create(order=self.order, part=self.part, quantity=ii)
def allocate_stock(self, full=True):
# 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
)
def test_allocate_partial(self):
# Partially allocate stock
self.allocate_stock(False)
self.assertFalse(self.order.is_fully_allocated())
self.assertFalse(self.line.is_fully_allocated())
self.assertEqual(self.line.allocated_quantity(), 45)
self.assertEqual(self.line.fulfilled_quantity(), 0)
def test_allocate_full(self):
# Fully allocate stock
self.allocate_stock(True)
self.assertTrue(self.order.is_fully_allocated())
self.assertTrue(self.line.is_fully_allocated())
self.assertEqual(self.line.allocated_quantity(), 50)
def test_order_cancel(self):
# Allocate line items then cancel the order
self.allocate_stock(True)
self.assertEqual(SalesOrderAllocation.objects.count(), 2)
self.assertEqual(self.order.status, status.SalesOrderStatus.PENDING)
self.order.cancel_order()
self.assertEqual(SalesOrderAllocation.objects.count(), 0)
self.assertEqual(self.order.status, status.SalesOrderStatus.CANCELLED)
with self.assertRaises(ValidationError):
self.order.can_complete(raise_error=True)
# Now try to ship it - should fail
result = self.order.complete_order(None)
self.assertFalse(result)
def test_complete_order(self):
# Allocate line items, then ship the order
# Assert some stuff before we run the test
# Initially there are two stock items
self.assertEqual(StockItem.objects.count(), 2)
# Take 25 units from each StockItem
self.allocate_stock(True)
self.assertEqual(SalesOrderAllocation.objects.count(), 2)
# Attempt to complete the order (but shipments are not completed!)
result = self.order.complete_order(None)
self.assertFalse(result)
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
result = self.order.complete_order(None)
self.assertTrue(result)
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)
sa = StockItem.objects.get(pk=self.Sa.pk)
sb = StockItem.objects.get(pk=self.Sb.pk)
# 25 units subtracted from each of the original items
self.assertEqual(sa.quantity, 75)
self.assertEqual(sb.quantity, 175)
# And 2 items created which are associated with the order
outputs = StockItem.objects.filter(sales_order=self.order)
self.assertEqual(outputs.count(), 2)
for item in outputs.all():
self.assertEqual(item.quantity, 25)
self.assertEqual(sa.sales_order, None)
self.assertEqual(sb.sales_order, None)
# 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(), 50)