diff --git a/InvenTree/common/models.py b/InvenTree/common/models.py index 83773fe48a..92e5c1522c 100644 --- a/InvenTree/common/models.py +++ b/InvenTree/common/models.py @@ -1111,6 +1111,13 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'default': 'SO', }, + 'SALESORDER_DEFAULT_SHIPMENT': { + 'name': _('Sales Order Default Shipment'), + 'description': _('Enable creation of default shipment with sales orders'), + 'default': False, + 'validator': bool, + }, + 'PURCHASEORDER_REFERENCE_PREFIX': { 'name': _('Purchase Order Reference Prefix'), 'description': _('Prefix value for purchase order reference'), diff --git a/InvenTree/order/models.py b/InvenTree/order/models.py index 7460e81e56..f4688f3736 100644 --- a/InvenTree/order/models.py +++ b/InvenTree/order/models.py @@ -12,6 +12,8 @@ from decimal import Decimal from django.db import models, transaction from django.db.models import Q, F, Sum from django.db.models.functions import Coalesce +from django.db.models.signals import post_save +from django.dispatch.dispatcher import receiver from django.core.validators import MinValueValidator from django.core.exceptions import ValidationError @@ -809,6 +811,21 @@ class SalesOrder(Order): return self.pending_shipments().count() +@receiver(post_save, sender=SalesOrder, dispatch_uid='build_post_save_log') +def after_save_sales_order(sender, instance: SalesOrder, created: bool, **kwargs): + """ + Callback function to be executed after a SalesOrder instance is saved + """ + if created and getSetting('SALESORDER_DEFAULT_SHIPMENT'): + # A new SalesOrder has just been created + + # Create default shipment + SalesOrderShipment.objects.create( + order=instance, + reference='1', + ) + + class PurchaseOrderAttachment(InvenTreeAttachment): """ Model for storing file attachments against a PurchaseOrder object diff --git a/InvenTree/order/test_sales_order.py b/InvenTree/order/test_sales_order.py index d43c94996c..cbd572e24d 100644 --- a/InvenTree/order/test_sales_order.py +++ b/InvenTree/order/test_sales_order.py @@ -10,6 +10,8 @@ from company.models import Company from InvenTree import status_codes as status +from common.models import InvenTreeSetting + from order.models import SalesOrder, SalesOrderLineItem, SalesOrderAllocation, SalesOrderShipment from part.models import Part @@ -200,3 +202,37 @@ class SalesOrderTest(TestCase): self.assertTrue(self.line.is_fully_allocated()) self.assertEqual(self.line.fulfilled_quantity(), 50) self.assertEqual(self.line.allocated_quantity(), 50) + + def test_default_shipment(self): + # Test sales order default shipment creation + + # Default setting value should be False + self.assertEqual(False, InvenTreeSetting.get_setting('SALESORDER_DEFAULT_SHIPMENT')) + + # Create an order + order_1 = SalesOrder.objects.create( + customer=self.customer, + reference='1235', + customer_reference='ABC 55556' + ) + + # Order should have no shipments when setting is False + self.assertEqual(0, order_1.shipment_count) + + # Update setting to True + InvenTreeSetting.set_setting('SALESORDER_DEFAULT_SHIPMENT', True, None) + self.assertEqual(True, InvenTreeSetting.get_setting('SALESORDER_DEFAULT_SHIPMENT')) + + # Create a second order + order_2 = SalesOrder.objects.create( + customer=self.customer, + reference='1236', + customer_reference='ABC 55557' + ) + + # Order should have one shipment + self.assertEqual(1, order_2.shipment_count) + self.assertEqual(1, order_2.pending_shipments().count()) + + # Shipment should have default reference of '1' + self.assertEqual('1', order_2.pending_shipments()[0].reference) diff --git a/InvenTree/templates/InvenTree/settings/so.html b/InvenTree/templates/InvenTree/settings/so.html index e6fde3a093..ac84f5fa86 100644 --- a/InvenTree/templates/InvenTree/settings/so.html +++ b/InvenTree/templates/InvenTree/settings/so.html @@ -12,6 +12,7 @@ {% include "InvenTree/settings/setting.html" with key="SALESORDER_REFERENCE_PREFIX" %} + {% include "InvenTree/settings/setting.html" with key="SALESORDER_DEFAULT_SHIPMENT" icon="fa-truck-loading" %}