diff --git a/docs/docs/build/build.md b/docs/docs/build/build.md index 5c889cc808..cf571be503 100644 --- a/docs/docs/build/build.md +++ b/docs/docs/build/build.md @@ -79,6 +79,18 @@ Each *Build Order* has an associated *Status* flag, which indicates the state of | `Cancelled` | Build has been cancelled | | `Completed` | Build has been completed | +**Source Code** + +Refer to the source code for the Build Order status codes: + +::: build.status_codes.BuildStatus + options: + show_bases: False + show_root_heading: False + show_root_toc_entry: False + show_source: True + members: [] + ### Stock Allocations When a *Build Order* is created, we then have the ability to *allocate* stock items against that build order. The particular parts we need to allocate against the build are specified by the BOM for the part we are assembling. diff --git a/docs/docs/order/purchase_order.md b/docs/docs/order/purchase_order.md index b75a6a5d87..066f64d058 100644 --- a/docs/docs/order/purchase_order.md +++ b/docs/docs/order/purchase_order.md @@ -22,6 +22,20 @@ Each Purchase Order has a specific status code which indicates the current state | In Progress | The purchase order has been issued to the supplier, and is in progress | | Complete | The purchase order has been completed, and is now closed | | Cancelled | The purchase order was cancelled, and is now closed | +| Lost | The purchase order was lost, and is now closed | +| Returned | The purchase order was returned, and is now closed | + +**Source Code** + +Refer to the source code for the Purchase Order status codes: + +::: order.status_codes.PurchaseOrderStatus + options: + show_bases: False + show_root_heading: False + show_root_toc_entry: False + show_source: True + members: [] ### Purchase Order Currency diff --git a/docs/docs/order/return_order.md b/docs/docs/order/return_order.md index 0a7df0d069..4b134601ae 100644 --- a/docs/docs/order/return_order.md +++ b/docs/docs/order/return_order.md @@ -48,6 +48,18 @@ Each Return Order has a specific status code, as follows: | Complete | The return order was marked as complete, and is now closed | | Cancelled | The return order was cancelled, and is now closed | +**Source Code** + +Refer to the source code for the Return Order status codes: + +::: order.status_codes.ReturnOrderStatus + options: + show_bases: False + show_root_heading: False + show_root_toc_entry: False + show_source: True + members: [] + ## Create a Return Order From the Return Order index, click on New Return Order which opens the "Create Return Order" form. diff --git a/docs/docs/order/sales_order.md b/docs/docs/order/sales_order.md index 1d37e2410d..43ab9cc533 100644 --- a/docs/docs/order/sales_order.md +++ b/docs/docs/order/sales_order.md @@ -20,8 +20,23 @@ Each Sales Order has a specific status code, which represents the state of the o | --- | --- | | Pending | The sales order has been created, but has not been finalized or submitted | | In Progress | The sales order has been issued, and is in progress | -| Shipped | The sales order has been completed, and is now closed | +| Shipped | The sales order has been shipped, but is not yet complete | +| Complete | The sales order is fully completed, and is now closed | | Cancelled | The sales order was cancelled, and is now closed | +| Lost | The sales order was lost, and is now closed | +| Returned | The sales order was returned, and is now closed | + +**Source Code** + +Refer to the source code for the Sales Order status codes: + +::: order.status_codes.SalesOrderStatus + options: + show_bases: False + show_root_heading: False + show_root_toc_entry: False + show_source: True + members: [] ### Sales Order Currency @@ -83,7 +98,7 @@ To view all the completed shipment, click on the Complete Order to mark the sales order as complete. +Once all items in the sales order have been shipped, click on Complete Order to mark the sales order as shipped. Confirm then click on Submit to complete the order. ### Cancel Order diff --git a/docs/docs/stock/status.md b/docs/docs/stock/status.md index 42e5bffe32..3ae6f12823 100644 --- a/docs/docs/stock/status.md +++ b/docs/docs/stock/status.md @@ -26,6 +26,18 @@ The *status* of a given stock item is displayed on the stock item detail page: {% include 'img.html' %} {% endwith %} +**Source Code** + +Refer to the source code for the Stock status codes: + +::: stock.status_codes.StockStatus + options: + show_bases: False + show_root_heading: False + show_root_toc_entry: False + show_source: True + members: [] + ### Default Status Code The default status code for any newly created Stock Item is OK diff --git a/src/backend/InvenTree/InvenTree/status_codes.py b/src/backend/InvenTree/InvenTree/status_codes.py index 2cd3f110ba..81d713f59c 100644 --- a/src/backend/InvenTree/InvenTree/status_codes.py +++ b/src/backend/InvenTree/InvenTree/status_codes.py @@ -1,198 +1,9 @@ -"""Status codes for InvenTree.""" +"""Global import of all status codes. -from django.utils.translation import gettext_lazy as _ +This file remains here for backwards compatibility, +as external plugins may import status codes from this file. +""" -from generic.states import StatusCode - - -class PurchaseOrderStatus(StatusCode): - """Defines a set of status codes for a PurchaseOrder.""" - - # Order status codes - PENDING = 10, _('Pending'), 'secondary' # Order is pending (not yet placed) - PLACED = 20, _('Placed'), 'primary' # Order has been placed with supplier - COMPLETE = 30, _('Complete'), 'success' # Order has been completed - CANCELLED = 40, _('Cancelled'), 'danger' # Order was cancelled - LOST = 50, _('Lost'), 'warning' # Order was lost - RETURNED = 60, _('Returned'), 'warning' # Order was returned - - -class PurchaseOrderStatusGroups: - """Groups for PurchaseOrderStatus codes.""" - - # Open orders - OPEN = [PurchaseOrderStatus.PENDING.value, PurchaseOrderStatus.PLACED.value] - - # Failed orders - FAILED = [ - PurchaseOrderStatus.CANCELLED.value, - PurchaseOrderStatus.LOST.value, - PurchaseOrderStatus.RETURNED.value, - ] - - -class SalesOrderStatus(StatusCode): - """Defines a set of status codes for a SalesOrder.""" - - PENDING = 10, _('Pending'), 'secondary' # Order is pending - IN_PROGRESS = ( - 15, - _('In Progress'), - 'primary', - ) # Order has been issued, and is in progress - SHIPPED = 20, _('Shipped'), 'success' # Order has been shipped to customer - COMPLETE = 30, _('Complete'), 'success' # Order is complete - CANCELLED = 40, _('Cancelled'), 'danger' # Order has been cancelled - LOST = 50, _('Lost'), 'warning' # Order was lost - RETURNED = 60, _('Returned'), 'warning' # Order was returned - - -class SalesOrderStatusGroups: - """Groups for SalesOrderStatus codes.""" - - # Open orders - OPEN = [SalesOrderStatus.PENDING.value, SalesOrderStatus.IN_PROGRESS.value] - - # Completed orders - COMPLETE = [SalesOrderStatus.SHIPPED.value, SalesOrderStatus.COMPLETE.value] - - -class StockStatus(StatusCode): - """Status codes for Stock.""" - - OK = 10, _('OK'), 'success' # Item is OK - ATTENTION = 50, _('Attention needed'), 'warning' # Item requires attention - DAMAGED = 55, _('Damaged'), 'warning' # Item is damaged - DESTROYED = 60, _('Destroyed'), 'danger' # Item is destroyed - REJECTED = 65, _('Rejected'), 'danger' # Item is rejected - LOST = 70, _('Lost'), 'dark' # Item has been lost - QUARANTINED = ( - 75, - _('Quarantined'), - 'info', - ) # Item has been quarantined and is unavailable - RETURNED = 85, _('Returned'), 'warning' # Item has been returned from a customer - - -class StockStatusGroups: - """Groups for StockStatus codes.""" - - # The following codes correspond to parts that are 'available' or 'in stock' - AVAILABLE_CODES = [ - StockStatus.OK.value, - StockStatus.ATTENTION.value, - StockStatus.DAMAGED.value, - StockStatus.RETURNED.value, - ] - - -class StockHistoryCode(StatusCode): - """Status codes for StockHistory.""" - - LEGACY = 0, _('Legacy stock tracking entry') - - CREATED = 1, _('Stock item created') - - # Manual editing operations - EDITED = 5, _('Edited stock item') - ASSIGNED_SERIAL = 6, _('Assigned serial number') - - # Manual stock operations - STOCK_COUNT = 10, _('Stock counted') - STOCK_ADD = 11, _('Stock manually added') - STOCK_REMOVE = 12, _('Stock manually removed') - - # Location operations - STOCK_MOVE = 20, _('Location changed') - STOCK_UPDATE = 25, _('Stock updated') - - # Installation operations - INSTALLED_INTO_ASSEMBLY = 30, _('Installed into assembly') - REMOVED_FROM_ASSEMBLY = 31, _('Removed from assembly') - - INSTALLED_CHILD_ITEM = 35, _('Installed component item') - REMOVED_CHILD_ITEM = 36, _('Removed component item') - - # Stock splitting operations - SPLIT_FROM_PARENT = 40, _('Split from parent item') - SPLIT_CHILD_ITEM = 42, _('Split child item') - - # Stock merging operations - MERGED_STOCK_ITEMS = 45, _('Merged stock items') - - # Convert stock item to variant - CONVERTED_TO_VARIANT = 48, _('Converted to variant') - - # Build order codes - BUILD_OUTPUT_CREATED = 50, _('Build order output created') - BUILD_OUTPUT_COMPLETED = 55, _('Build order output completed') - BUILD_OUTPUT_REJECTED = 56, _('Build order output rejected') - BUILD_CONSUMED = 57, _('Consumed by build order') - - # Sales order codes - SHIPPED_AGAINST_SALES_ORDER = 60, _('Shipped against Sales Order') - - # Purchase order codes - RECEIVED_AGAINST_PURCHASE_ORDER = 70, _('Received against Purchase Order') - - # Return order codes - RETURNED_AGAINST_RETURN_ORDER = 80, _('Returned against Return Order') - - # Customer actions - SENT_TO_CUSTOMER = 100, _('Sent to customer') - RETURNED_FROM_CUSTOMER = 105, _('Returned from customer') - - -class BuildStatus(StatusCode): - """Build status codes.""" - - PENDING = 10, _('Pending'), 'secondary' # Build is pending / active - PRODUCTION = 20, _('Production'), 'primary' # BuildOrder is in production - CANCELLED = 30, _('Cancelled'), 'danger' # Build was cancelled - COMPLETE = 40, _('Complete'), 'success' # Build is complete - - -class BuildStatusGroups: - """Groups for BuildStatus codes.""" - - ACTIVE_CODES = [BuildStatus.PENDING.value, BuildStatus.PRODUCTION.value] - - -class ReturnOrderStatus(StatusCode): - """Defines a set of status codes for a ReturnOrder.""" - - # Order is pending, waiting for receipt of items - PENDING = 10, _('Pending'), 'secondary' - - # Items have been received, and are being inspected - IN_PROGRESS = 20, _('In Progress'), 'primary' - - COMPLETE = 30, _('Complete'), 'success' - CANCELLED = 40, _('Cancelled'), 'danger' - - -class ReturnOrderStatusGroups: - """Groups for ReturnOrderStatus codes.""" - - OPEN = [ReturnOrderStatus.PENDING.value, ReturnOrderStatus.IN_PROGRESS.value] - - -class ReturnOrderLineStatus(StatusCode): - """Defines a set of status codes for a ReturnOrderLineItem.""" - - PENDING = 10, _('Pending'), 'secondary' - - # Item is to be returned to customer, no other action - RETURN = 20, _('Return'), 'success' - - # Item is to be repaired, and returned to customer - REPAIR = 30, _('Repair'), 'primary' - - # Item is to be replaced (new item shipped) - REPLACE = 40, _('Replace'), 'warning' - - # Item is to be refunded (cannot be repaired) - REFUND = 50, _('Refund'), 'info' - - # Item is rejected - REJECT = 60, _('Reject'), 'danger' +from build.status_codes import * +from order.status_codes import * +from stock.status_codes import * diff --git a/src/backend/InvenTree/build/api.py b/src/backend/InvenTree/build/api.py index e47011ae8b..f84bca1273 100644 --- a/src/backend/InvenTree/build/api.py +++ b/src/backend/InvenTree/build/api.py @@ -14,7 +14,7 @@ from django_filters import rest_framework as rest_filters from InvenTree.api import AttachmentMixin, APIDownloadMixin, ListCreateDestroyAPIView, MetadataView from generic.states.api import StatusView from InvenTree.helpers import str2bool, isNull, DownloadFile -from InvenTree.status_codes import BuildStatus, BuildStatusGroups +from build.status_codes import BuildStatus, BuildStatusGroups from InvenTree.mixins import CreateAPI, RetrieveUpdateDestroyAPI, ListCreateAPI import common.models diff --git a/src/backend/InvenTree/build/models.py b/src/backend/InvenTree/build/models.py index cd1713126e..ffbbc0b544 100644 --- a/src/backend/InvenTree/build/models.py +++ b/src/backend/InvenTree/build/models.py @@ -22,7 +22,8 @@ from mptt.exceptions import InvalidMove from rest_framework import serializers -from InvenTree.status_codes import BuildStatus, StockStatus, StockHistoryCode, BuildStatusGroups +from build.status_codes import BuildStatus, BuildStatusGroups +from stock.status_codes import StockStatus, StockHistoryCode from build.validators import generate_next_build_reference, validate_build_order_reference diff --git a/src/backend/InvenTree/build/serializers.py b/src/backend/InvenTree/build/serializers.py index ac19b6ef2c..92180f8d42 100644 --- a/src/backend/InvenTree/build/serializers.py +++ b/src/backend/InvenTree/build/serializers.py @@ -18,7 +18,7 @@ from InvenTree.serializers import UserSerializer import InvenTree.helpers from InvenTree.serializers import InvenTreeDecimalField -from InvenTree.status_codes import StockStatus +from stock.status_codes import StockStatus from stock.generators import generate_batch_code from stock.models import StockItem, StockLocation diff --git a/src/backend/InvenTree/build/status_codes.py b/src/backend/InvenTree/build/status_codes.py new file mode 100644 index 0000000000..463bd22059 --- /dev/null +++ b/src/backend/InvenTree/build/status_codes.py @@ -0,0 +1,20 @@ +"""Build status codes.""" + +from django.utils.translation import gettext_lazy as _ + +from generic.states import StatusCode + + +class BuildStatus(StatusCode): + """Build status codes.""" + + PENDING = 10, _('Pending'), 'secondary' # Build is pending / active + PRODUCTION = 20, _('Production'), 'primary' # BuildOrder is in production + CANCELLED = 30, _('Cancelled'), 'danger' # Build was cancelled + COMPLETE = 40, _('Complete'), 'success' # Build is complete + + +class BuildStatusGroups: + """Groups for BuildStatus codes.""" + + ACTIVE_CODES = [BuildStatus.PENDING.value, BuildStatus.PRODUCTION.value] diff --git a/src/backend/InvenTree/build/tasks.py b/src/backend/InvenTree/build/tasks.py index b30a15f3d2..82828d642d 100644 --- a/src/backend/InvenTree/build/tasks.py +++ b/src/backend/InvenTree/build/tasks.py @@ -17,8 +17,8 @@ import InvenTree.email import InvenTree.helpers import InvenTree.helpers_model import InvenTree.tasks -from InvenTree.status_codes import BuildStatusGroups from InvenTree.ready import isImportingData +from build.status_codes import BuildStatusGroups import part.models as part_models diff --git a/src/backend/InvenTree/build/test_api.py b/src/backend/InvenTree/build/test_api.py index 1fc1a368ee..b240521db6 100644 --- a/src/backend/InvenTree/build/test_api.py +++ b/src/backend/InvenTree/build/test_api.py @@ -10,7 +10,8 @@ from part.models import Part from build.models import Build, BuildItem from stock.models import StockItem -from InvenTree.status_codes import BuildStatus, StockStatus +from build.status_codes import BuildStatus +from stock.status_codes import StockStatus from InvenTree.unit_test import InvenTreeAPITestCase diff --git a/src/backend/InvenTree/build/tests.py b/src/backend/InvenTree/build/tests.py index 904f2a3a62..4dd7ee0fee 100644 --- a/src/backend/InvenTree/build/tests.py +++ b/src/backend/InvenTree/build/tests.py @@ -11,7 +11,7 @@ from InvenTree.unit_test import InvenTreeTestCase from .models import Build from stock.models import StockItem -from InvenTree.status_codes import BuildStatus +from build.status_codes import BuildStatus class BuildTestSimple(InvenTreeTestCase): diff --git a/src/backend/InvenTree/build/views.py b/src/backend/InvenTree/build/views.py index 36422e1688..2668b0fe99 100644 --- a/src/backend/InvenTree/build/views.py +++ b/src/backend/InvenTree/build/views.py @@ -5,7 +5,7 @@ from django.views.generic import DetailView, ListView from .models import Build from InvenTree.views import InvenTreeRoleMixin -from InvenTree.status_codes import BuildStatus +from build.status_codes import BuildStatus from plugin.views import InvenTreePluginViewMixin diff --git a/src/backend/InvenTree/common/models.py b/src/backend/InvenTree/common/models.py index 15855b462c..d1671dd68c 100644 --- a/src/backend/InvenTree/common/models.py +++ b/src/backend/InvenTree/common/models.py @@ -1874,6 +1874,14 @@ class InvenTreeSetting(BaseInvenTreeSetting): 'default': False, 'validator': bool, }, + 'SALESORDER_SHIP_COMPLETE': { + 'name': _('Mark Shipped Orders as Complete'), + 'description': _( + 'Sales orders marked as shipped will automatically be completed, bypassing the "shipped" status' + ), + 'default': False, + 'validator': bool, + }, 'PURCHASEORDER_REFERENCE_PATTERN': { 'name': _('Purchase Order Reference Pattern'), 'description': _( diff --git a/src/backend/InvenTree/company/models.py b/src/backend/InvenTree/company/models.py index eea0aa511a..4e102a160d 100644 --- a/src/backend/InvenTree/company/models.py +++ b/src/backend/InvenTree/company/models.py @@ -31,7 +31,7 @@ import InvenTree.tasks import InvenTree.validators from common.settings import currency_code_default from InvenTree.fields import InvenTreeURLField, RoundingDecimalField -from InvenTree.status_codes import PurchaseOrderStatusGroups +from order.status_codes import PurchaseOrderStatusGroups def rename_company_image(instance, filename): diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index 9ee4df9974..7db194469f 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -30,14 +30,6 @@ from InvenTree.filters import SEARCH_ORDER_FILTER, SEARCH_ORDER_FILTER_ALIAS from InvenTree.helpers import DownloadFile, str2bool from InvenTree.helpers_model import construct_absolute_url, get_base_url from InvenTree.mixins import CreateAPI, ListAPI, ListCreateAPI, RetrieveUpdateDestroyAPI -from InvenTree.status_codes import ( - PurchaseOrderStatus, - PurchaseOrderStatusGroups, - ReturnOrderLineStatus, - ReturnOrderStatus, - SalesOrderStatus, - SalesOrderStatusGroups, -) from order import models, serializers from order.admin import ( PurchaseOrderExtraLineResource, @@ -48,6 +40,14 @@ from order.admin import ( SalesOrderLineItemResource, SalesOrderResource, ) +from order.status_codes import ( + PurchaseOrderStatus, + PurchaseOrderStatusGroups, + ReturnOrderLineStatus, + ReturnOrderStatus, + SalesOrderStatus, + SalesOrderStatusGroups, +) from part.models import Part from users.models import Owner diff --git a/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py b/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py index 0efab70da8..69ef6e6add 100644 --- a/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py +++ b/src/backend/InvenTree/order/migrations/0055_auto_20211025_0645.py @@ -3,7 +3,7 @@ from django.db import migrations -from InvenTree.status_codes import SalesOrderStatus +from order.status_codes import SalesOrderStatus def add_shipment(apps, schema_editor): diff --git a/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py b/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py index 1377a9a0b9..6c31d93099 100644 --- a/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py +++ b/src/backend/InvenTree/order/migrations/0058_auto_20211126_1210.py @@ -2,7 +2,7 @@ from django.db import migrations -from InvenTree.status_codes import SalesOrderStatus +from order.status_codes import SalesOrderStatus def calculate_shipped_quantity(apps, schema_editor): diff --git a/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py b/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py index ac95830807..9c80796a6f 100644 --- a/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py +++ b/src/backend/InvenTree/order/migrations/0096_alter_returnorderlineitem_outcome.py @@ -1,7 +1,7 @@ # Generated by Django 3.2.19 on 2023-06-04 17:43 from django.db import migrations, models -import InvenTree.status_codes +import order.status_codes class Migration(migrations.Migration): @@ -14,6 +14,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='returnorderlineitem', name='outcome', - field=models.PositiveIntegerField(choices=InvenTree.status_codes.ReturnOrderLineStatus.items(), default=10, help_text='Outcome for this line item', verbose_name='Outcome'), + field=models.PositiveIntegerField(choices=order.status_codes.ReturnOrderLineStatus.items(), default=10, help_text='Outcome for this line item', verbose_name='Outcome'), ), ] diff --git a/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py b/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py index de56926bc9..23d9b95d86 100644 --- a/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py +++ b/src/backend/InvenTree/order/migrations/0099_alter_salesorder_status.py @@ -2,7 +2,7 @@ import django.core.validators from django.db import migrations, models -import InvenTree.status_codes +import order.status_codes class Migration(migrations.Migration): @@ -15,6 +15,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='salesorder', name='status', - field=models.PositiveIntegerField(choices=InvenTree.status_codes.SalesOrderStatus.items(), default=10, help_text='Purchase order status', verbose_name='Status'), + field=models.PositiveIntegerField(choices=order.status_codes.SalesOrderStatus.items(), default=10, help_text='Purchase order status', verbose_name='Status'), ), ] diff --git a/src/backend/InvenTree/order/models.py b/src/backend/InvenTree/order/models.py index 0543fbf88d..1f60121efa 100644 --- a/src/backend/InvenTree/order/models.py +++ b/src/backend/InvenTree/order/models.py @@ -45,7 +45,7 @@ from InvenTree.fields import ( ) from InvenTree.helpers import decimal2string, pui_url from InvenTree.helpers_model import getSetting, notify_responsible -from InvenTree.status_codes import ( +from order.status_codes import ( PurchaseOrderStatus, PurchaseOrderStatusGroups, ReturnOrderLineStatus, @@ -53,11 +53,10 @@ from InvenTree.status_codes import ( ReturnOrderStatusGroups, SalesOrderStatus, SalesOrderStatusGroups, - StockHistoryCode, - StockStatus, ) from part import models as PartModels from plugin.events import trigger_event +from stock.status_codes import StockHistoryCode, StockStatus logger = logging.getLogger('inventree') @@ -1024,6 +1023,12 @@ class SalesOrder(TotalPriceMixin, Order): Throws a ValidationError if cannot be completed. """ try: + if self.status == SalesOrderStatus.COMPLETE.value: + raise ValidationError(_('Order is already complete')) + + if self.status == SalesOrderStatus.CANCELLED.value: + raise ValidationError(_('Order is already cancelled')) + # Only an open order can be marked as shipped if self.is_open and not self.is_completed: raise ValidationError(_('Only an open order can be marked as complete')) @@ -1067,7 +1072,11 @@ class SalesOrder(TotalPriceMixin, Order): if not self.can_complete(**kwargs): return False - if self.status == SalesOrderStatus.SHIPPED: + bypass_shipped = InvenTree.helpers.str2bool( + common_models.InvenTreeSetting.get_setting('SALESORDER_SHIP_COMPLETE') + ) + + if bypass_shipped or self.status == SalesOrderStatus.SHIPPED: self.status = SalesOrderStatus.COMPLETE.value else: self.status = SalesOrderStatus.SHIPPED.value diff --git a/src/backend/InvenTree/order/serializers.py b/src/backend/InvenTree/order/serializers.py index 12f8e3b86d..f57491d49b 100644 --- a/src/backend/InvenTree/order/serializers.py +++ b/src/backend/InvenTree/order/serializers.py @@ -48,14 +48,14 @@ from InvenTree.serializers import ( InvenTreeModelSerializer, InvenTreeMoneySerializer, ) -from InvenTree.status_codes import ( +from order.status_codes import ( PurchaseOrderStatusGroups, ReturnOrderLineStatus, ReturnOrderStatus, SalesOrderStatusGroups, - StockStatus, ) from part.serializers import PartBriefSerializer +from stock.status_codes import StockStatus from users.serializers import OwnerSerializer diff --git a/src/backend/InvenTree/order/status_codes.py b/src/backend/InvenTree/order/status_codes.py new file mode 100644 index 0000000000..cec286dc09 --- /dev/null +++ b/src/backend/InvenTree/order/status_codes.py @@ -0,0 +1,97 @@ +"""Order status codes.""" + +from django.utils.translation import gettext_lazy as _ + +from generic.states import StatusCode + + +class PurchaseOrderStatus(StatusCode): + """Defines a set of status codes for a PurchaseOrder.""" + + # Order status codes + PENDING = 10, _('Pending'), 'secondary' # Order is pending (not yet placed) + PLACED = 20, _('Placed'), 'primary' # Order has been placed with supplier + COMPLETE = 30, _('Complete'), 'success' # Order has been completed + CANCELLED = 40, _('Cancelled'), 'danger' # Order was cancelled + LOST = 50, _('Lost'), 'warning' # Order was lost + RETURNED = 60, _('Returned'), 'warning' # Order was returned + + +class PurchaseOrderStatusGroups: + """Groups for PurchaseOrderStatus codes.""" + + # Open orders + OPEN = [PurchaseOrderStatus.PENDING.value, PurchaseOrderStatus.PLACED.value] + + # Failed orders + FAILED = [ + PurchaseOrderStatus.CANCELLED.value, + PurchaseOrderStatus.LOST.value, + PurchaseOrderStatus.RETURNED.value, + ] + + +class SalesOrderStatus(StatusCode): + """Defines a set of status codes for a SalesOrder.""" + + PENDING = 10, _('Pending'), 'secondary' # Order is pending + IN_PROGRESS = ( + 15, + _('In Progress'), + 'primary', + ) # Order has been issued, and is in progress + SHIPPED = 20, _('Shipped'), 'success' # Order has been shipped to customer + COMPLETE = 30, _('Complete'), 'success' # Order is complete + CANCELLED = 40, _('Cancelled'), 'danger' # Order has been cancelled + LOST = 50, _('Lost'), 'warning' # Order was lost + RETURNED = 60, _('Returned'), 'warning' # Order was returned + + +class SalesOrderStatusGroups: + """Groups for SalesOrderStatus codes.""" + + # Open orders + OPEN = [SalesOrderStatus.PENDING.value, SalesOrderStatus.IN_PROGRESS.value] + + # Completed orders + COMPLETE = [SalesOrderStatus.SHIPPED.value, SalesOrderStatus.COMPLETE.value] + + +class ReturnOrderStatus(StatusCode): + """Defines a set of status codes for a ReturnOrder.""" + + # Order is pending, waiting for receipt of items + PENDING = 10, _('Pending'), 'secondary' + + # Items have been received, and are being inspected + IN_PROGRESS = 20, _('In Progress'), 'primary' + + COMPLETE = 30, _('Complete'), 'success' + CANCELLED = 40, _('Cancelled'), 'danger' + + +class ReturnOrderStatusGroups: + """Groups for ReturnOrderStatus codes.""" + + OPEN = [ReturnOrderStatus.PENDING.value, ReturnOrderStatus.IN_PROGRESS.value] + + +class ReturnOrderLineStatus(StatusCode): + """Defines a set of status codes for a ReturnOrderLineItem.""" + + PENDING = 10, _('Pending'), 'secondary' + + # Item is to be returned to customer, no other action + RETURN = 20, _('Return'), 'success' + + # Item is to be repaired, and returned to customer + REPAIR = 30, _('Repair'), 'primary' + + # Item is to be replaced (new item shipped) + REPLACE = 40, _('Replace'), 'warning' + + # Item is to be refunded (cannot be repaired) + REFUND = 50, _('Refund'), 'info' + + # Item is rejected + REJECT = 60, _('Reject'), 'danger' diff --git a/src/backend/InvenTree/order/tasks.py b/src/backend/InvenTree/order/tasks.py index 0c0c8cea30..1e1231a810 100644 --- a/src/backend/InvenTree/order/tasks.py +++ b/src/backend/InvenTree/order/tasks.py @@ -7,8 +7,8 @@ from django.utils.translation import gettext_lazy as _ import common.notifications import InvenTree.helpers_model import order.models -from InvenTree.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups from InvenTree.tasks import ScheduledTask, scheduled_task +from order.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups from plugin.events import trigger_event diff --git a/src/backend/InvenTree/order/test_api.py b/src/backend/InvenTree/order/test_api.py index a9824442be..6e59e9a600 100644 --- a/src/backend/InvenTree/order/test_api.py +++ b/src/backend/InvenTree/order/test_api.py @@ -16,18 +16,18 @@ from rest_framework import status from common.models import InvenTreeSetting from common.settings import currency_codes from company.models import Company, SupplierPart, SupplierPriceBreak -from InvenTree.status_codes import ( +from InvenTree.unit_test import InvenTreeAPITestCase +from order import models +from order.status_codes import ( PurchaseOrderStatus, ReturnOrderLineStatus, ReturnOrderStatus, SalesOrderStatus, SalesOrderStatusGroups, - StockStatus, ) -from InvenTree.unit_test import InvenTreeAPITestCase -from order import models from part.models import Part from stock.models import StockItem +from stock.status_codes import StockStatus from users.models import Owner @@ -1476,6 +1476,77 @@ class SalesOrderTest(OrderTest): expected_fn=f'InvenTree_SalesOrders.{fmt}', ) + def test_sales_order_complete(self): + """Tests for marking a SalesOrder as complete.""" + self.assignRole('sales_order.add') + + # Let's create a SalesOrder + customer = Company.objects.filter(is_customer=True).first() + so = models.SalesOrder.objects.create( + customer=customer, reference='SO-12345', description='Test SO' + ) + + self.assertEqual(so.status, SalesOrderStatus.PENDING.value) + + # Create a line item + part = Part.objects.filter(salable=True).first() + + line = models.SalesOrderLineItem.objects.create( + order=so, part=part, quantity=10, sale_price=Money(10, 'USD') + ) + + shipment = so.shipments.first() + + if shipment is None: + shipment = models.SalesOrderShipment.objects.create( + order=so, reference='SHIP-12345' + ) + + # Allocate some stock + item = StockItem.objects.create(part=part, quantity=100, location=None) + models.SalesOrderAllocation.objects.create( + quantity=10, line=line, item=item, shipment=shipment + ) + + # Ship the shipment + shipment.complete_shipment(self.user) + + # Ok, now we should be able to "complete" the shipment via the API + # The 'SALESORDER_SHIP_COMPLETE' setting determines if the outcome is "SHIPPED" or "COMPLETE" + InvenTreeSetting.set_setting('SALESORDER_SHIP_COMPLETE', False) + + url = reverse('api-so-complete', kwargs={'pk': so.pk}) + self.post(url, {}, expected_code=201) + + so.refresh_from_db() + self.assertEqual(so.status, SalesOrderStatus.SHIPPED.value) + + # Now, let's try to "complete" the shipment again + # This time it should get marked as "COMPLETE" + self.post(url, {}, expected_code=201) + + so.refresh_from_db() + self.assertEqual(so.status, SalesOrderStatus.COMPLETE.value) + + # Now, let's try *again* (it should fail as the order is already complete) + response = self.post(url, {}, expected_code=400) + + self.assertIn('Order is already complete', str(response.data)) + + # Next, we'll change the setting so that the order status jumps straight to "complete" + so.status = SalesOrderStatus.PENDING.value + so.save() + so.refresh_from_db() + self.assertEqual(so.status, SalesOrderStatus.PENDING.value) + + InvenTreeSetting.set_setting('SALESORDER_SHIP_COMPLETE', True) + + self.post(url, {}, expected_code=201) + + # The orders status should now be "complete" (not "shipped") + so.refresh_from_db() + self.assertEqual(so.status, SalesOrderStatus.COMPLETE.value) + class SalesOrderLineItemTest(OrderTest): """Tests for the SalesOrderLineItem API.""" diff --git a/src/backend/InvenTree/order/test_migrations.py b/src/backend/InvenTree/order/test_migrations.py index 407468cd51..7eafd2032f 100644 --- a/src/backend/InvenTree/order/test_migrations.py +++ b/src/backend/InvenTree/order/test_migrations.py @@ -2,7 +2,7 @@ from django_test_migrations.contrib.unittest_case import MigratorTestCase -from InvenTree.status_codes import SalesOrderStatus +from order.status_codes import SalesOrderStatus class TestRefIntMigrations(MigratorTestCase): diff --git a/src/backend/InvenTree/order/tests.py b/src/backend/InvenTree/order/tests.py index 1b6cdb9081..c797149ae5 100644 --- a/src/backend/InvenTree/order/tests.py +++ b/src/backend/InvenTree/order/tests.py @@ -14,7 +14,7 @@ from djmoney.money import Money import common.models import order.tasks from company.models import Company, SupplierPart -from InvenTree.status_codes import PurchaseOrderStatus +from order.status_codes import PurchaseOrderStatus from part.models import Part from stock.models import StockItem, StockLocation from users.models import Owner diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index 0c3ba35818..824590e923 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -18,6 +18,7 @@ from rest_framework.response import Response import order.models import part.filters from build.models import Build, BuildItem +from build.status_codes import BuildStatusGroups from InvenTree.api import ( APIDownloadMixin, AttachmentMixin, @@ -45,11 +46,7 @@ from InvenTree.mixins import ( ) from InvenTree.permissions import RolePermission from InvenTree.serializers import EmptySerializer -from InvenTree.status_codes import ( - BuildStatusGroups, - PurchaseOrderStatusGroups, - SalesOrderStatusGroups, -) +from order.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups from part.admin import PartCategoryResource, PartResource from stock.models import StockLocation diff --git a/src/backend/InvenTree/part/filters.py b/src/backend/InvenTree/part/filters.py index 4d247529b9..0b9fafb983 100644 --- a/src/backend/InvenTree/part/filters.py +++ b/src/backend/InvenTree/part/filters.py @@ -40,11 +40,8 @@ from sql_util.utils import SubquerySum import part.models import stock.models -from InvenTree.status_codes import ( - BuildStatusGroups, - PurchaseOrderStatusGroups, - SalesOrderStatusGroups, -) +from build.status_codes import BuildStatusGroups +from order.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups def annotate_in_production_quantity(reference=''): diff --git a/src/backend/InvenTree/part/models.py b/src/backend/InvenTree/part/models.py index 6364683c5d..9dc2148fb6 100644 --- a/src/backend/InvenTree/part/models.py +++ b/src/backend/InvenTree/part/models.py @@ -46,20 +46,20 @@ import part.settings as part_settings import report.mixins import users.models from build import models as BuildModels +from build.status_codes import BuildStatusGroups from common.models import InvenTreeSetting from common.settings import currency_code_default from company.models import SupplierPart from InvenTree import helpers, validators from InvenTree.fields import InvenTreeURLField from InvenTree.helpers import decimal2money, decimal2string, normalize, str2bool -from InvenTree.status_codes import ( - BuildStatusGroups, +from order import models as OrderModels +from order.status_codes import ( PurchaseOrderStatus, PurchaseOrderStatusGroups, SalesOrderStatus, SalesOrderStatusGroups, ) -from order import models as OrderModels from stock import models as StockModels logger = logging.getLogger('inventree') diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 3df52b22ca..9ddf1447db 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -33,7 +33,7 @@ import part.stocktake import part.tasks import stock.models import users.models -from InvenTree.status_codes import BuildStatusGroups +from build.status_codes import BuildStatusGroups from InvenTree.tasks import offload_task from .models import ( diff --git a/src/backend/InvenTree/part/test_api.py b/src/backend/InvenTree/part/test_api.py index 77396176e3..b39ecc4834 100644 --- a/src/backend/InvenTree/part/test_api.py +++ b/src/backend/InvenTree/part/test_api.py @@ -19,11 +19,12 @@ from rest_framework.test import APIClient import build.models import company.models import order.models +from build.status_codes import BuildStatus from common.models import InvenTreeSetting from company.models import Company, SupplierPart from InvenTree.settings import BASE_DIR -from InvenTree.status_codes import BuildStatus, PurchaseOrderStatusGroups, StockStatus from InvenTree.unit_test import InvenTreeAPITestCase +from order.status_codes import PurchaseOrderStatusGroups from part.models import ( BomItem, BomItemSubstitute, @@ -37,6 +38,7 @@ from part.models import ( PartTestTemplate, ) from stock.models import StockItem, StockLocation +from stock.status_codes import StockStatus class PartCategoryAPITest(InvenTreeAPITestCase): diff --git a/src/backend/InvenTree/part/test_pricing.py b/src/backend/InvenTree/part/test_pricing.py index ba4e7d54ef..a9572b2f16 100644 --- a/src/backend/InvenTree/part/test_pricing.py +++ b/src/backend/InvenTree/part/test_pricing.py @@ -11,8 +11,8 @@ import company.models import order.models import part.models import stock.models -from InvenTree.status_codes import PurchaseOrderStatus from InvenTree.unit_test import InvenTreeTestCase +from order.status_codes import PurchaseOrderStatus class PartPricingTests(InvenTreeTestCase): diff --git a/src/backend/InvenTree/plugin/base/barcodes/serializers.py b/src/backend/InvenTree/plugin/base/barcodes/serializers.py index b614bf1a0b..6ad15713b7 100644 --- a/src/backend/InvenTree/plugin/base/barcodes/serializers.py +++ b/src/backend/InvenTree/plugin/base/barcodes/serializers.py @@ -7,7 +7,7 @@ from rest_framework import serializers import order.models import stock.models -from InvenTree.status_codes import PurchaseOrderStatus, SalesOrderStatus +from order.status_codes import PurchaseOrderStatus, SalesOrderStatus from plugin.builtin.barcodes.inventree_barcode import InvenTreeInternalBarcodePlugin diff --git a/src/backend/InvenTree/plugin/samples/integration/transition.py b/src/backend/InvenTree/plugin/samples/integration/transition.py index 97166506bd..b0a6ff1826 100644 --- a/src/backend/InvenTree/plugin/samples/integration/transition.py +++ b/src/backend/InvenTree/plugin/samples/integration/transition.py @@ -2,8 +2,8 @@ from common.notifications import trigger_notification from generic.states import TransitionMethod -from InvenTree.status_codes import ReturnOrderStatus from order.models import ReturnOrder +from order.status_codes import ReturnOrderStatus from plugin import InvenTreePlugin diff --git a/src/backend/InvenTree/stock/api.py b/src/backend/InvenTree/stock/api.py index 60402584ba..e78fd277a0 100644 --- a/src/backend/InvenTree/stock/api.py +++ b/src/backend/InvenTree/stock/api.py @@ -56,7 +56,6 @@ from InvenTree.mixins import ( RetrieveAPI, RetrieveUpdateDestroyAPI, ) -from InvenTree.status_codes import StockHistoryCode, StockStatus from order.models import PurchaseOrder, ReturnOrder, SalesOrder, SalesOrderAllocation from order.serializers import ( PurchaseOrderSerializer, @@ -75,6 +74,7 @@ from stock.models import ( StockLocation, StockLocationType, ) +from stock.status_codes import StockHistoryCode, StockStatus class GenerateBatchCode(GenericAPIView): diff --git a/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py b/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py index aacaf01edc..ba44e9c1bd 100644 --- a/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py +++ b/src/backend/InvenTree/stock/migrations/0061_auto_20210511_0911.py @@ -4,7 +4,7 @@ import re from django.db import migrations -from InvenTree.status_codes import StockHistoryCode +from stock.status_codes import StockHistoryCode def update_history(apps, schema_editor): diff --git a/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py b/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py index b2dd16cc03..e8db1c5f77 100644 --- a/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py +++ b/src/backend/InvenTree/stock/migrations/0096_auto_20230330_1121.py @@ -15,7 +15,7 @@ def update_stock_history(apps, schema_editor): - Add the appropriate history! """ - from InvenTree.status_codes import StockHistoryCode + from stock.status_codes import StockHistoryCode StockItem = apps.get_model('stock', 'stockitem') StockItemTracking = apps.get_model('stock', 'stockitemtracking') diff --git a/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py b/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py index 7b8cf4d239..90807a8c10 100644 --- a/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py +++ b/src/backend/InvenTree/stock/migrations/0102_alter_stockitem_status.py @@ -2,7 +2,7 @@ import django.core.validators from django.db import migrations, models -import InvenTree.status_codes +import stock.status_codes class Migration(migrations.Migration): @@ -15,6 +15,6 @@ class Migration(migrations.Migration): migrations.AlterField( model_name='stockitem', name='status', - field=models.PositiveIntegerField(choices=InvenTree.status_codes.StockStatus.items(), default=10, validators=[django.core.validators.MinValueValidator(0)]), + field=models.PositiveIntegerField(choices=stock.status_codes.StockStatus.items(), default=10, validators=[django.core.validators.MinValueValidator(0)]), ), ] diff --git a/src/backend/InvenTree/stock/models.py b/src/backend/InvenTree/stock/models.py index e4eb90192e..1b9cfa8905 100644 --- a/src/backend/InvenTree/stock/models.py +++ b/src/backend/InvenTree/stock/models.py @@ -34,15 +34,11 @@ import report.mixins import report.models from company import models as CompanyModels from InvenTree.fields import InvenTreeModelMoneyField, InvenTreeURLField -from InvenTree.status_codes import ( - SalesOrderStatusGroups, - StockHistoryCode, - StockStatus, - StockStatusGroups, -) +from order.status_codes import SalesOrderStatusGroups from part import models as PartModels from plugin.events import trigger_event from stock.generators import generate_batch_code +from stock.status_codes import StockHistoryCode, StockStatus, StockStatusGroups from users.models import Owner logger = logging.getLogger('inventree') @@ -348,7 +344,7 @@ class StockItem( stocktake_user: User that performed the most recent stocktake review_needed: Flag if StockItem needs review delete_on_deplete: If True, StockItem will be deleted when the stock level gets to zero - status: Status of this StockItem (ref: InvenTree.status_codes.StockStatus) + status: Status of this StockItem (ref: stock.status_codes.StockStatus) notes: Extra notes field build: Link to a Build (if this stock item was created from a build) is_building: Boolean field indicating if this stock item is currently being built (or is "in production") diff --git a/src/backend/InvenTree/stock/serializers.py b/src/backend/InvenTree/stock/serializers.py index 112647d6ce..91638c62ff 100644 --- a/src/backend/InvenTree/stock/serializers.py +++ b/src/backend/InvenTree/stock/serializers.py @@ -20,11 +20,11 @@ import common.models import company.models import InvenTree.helpers import InvenTree.serializers -import InvenTree.status_codes import order.models import part.filters as part_filters import part.models as part_models import stock.filters +import stock.status_codes from company.serializers import SupplierPartSerializer from InvenTree.serializers import InvenTreeCurrencySerializer, InvenTreeDecimalField from part.serializers import PartBriefSerializer, PartTestTemplateSerializer @@ -925,8 +925,8 @@ class StockChangeStatusSerializer(serializers.Serializer): return items status = serializers.ChoiceField( - choices=InvenTree.status_codes.StockStatus.items(), - default=InvenTree.status_codes.StockStatus.OK.value, + choices=stock.status_codes.StockStatus.items(), + default=stock.status_codes.StockStatus.OK.value, label=_('Status'), ) @@ -973,7 +973,7 @@ class StockChangeStatusSerializer(serializers.Serializer): transaction_notes.append( StockItemTracking( item=item, - tracking_type=InvenTree.status_codes.StockHistoryCode.EDITED.value, + tracking_type=stock.status_codes.StockHistoryCode.EDITED.value, date=now, deltas=deltas, user=user, @@ -1419,7 +1419,7 @@ def stock_item_adjust_status_options(): In particular, include a Null option for the status field. """ - return [(None, _('No Change'))] + InvenTree.status_codes.StockStatus.items() + return [(None, _('No Change'))] + stock.status_codes.StockStatus.items() class StockAdjustmentItemSerializer(serializers.Serializer): diff --git a/src/backend/InvenTree/stock/status_codes.py b/src/backend/InvenTree/stock/status_codes.py new file mode 100644 index 0000000000..3c646bc455 --- /dev/null +++ b/src/backend/InvenTree/stock/status_codes.py @@ -0,0 +1,91 @@ +"""Stock status codes.""" + +from django.utils.translation import gettext_lazy as _ + +from generic.states import StatusCode + + +class StockStatus(StatusCode): + """Status codes for Stock.""" + + OK = 10, _('OK'), 'success' # Item is OK + ATTENTION = 50, _('Attention needed'), 'warning' # Item requires attention + DAMAGED = 55, _('Damaged'), 'warning' # Item is damaged + DESTROYED = 60, _('Destroyed'), 'danger' # Item is destroyed + REJECTED = 65, _('Rejected'), 'danger' # Item is rejected + LOST = 70, _('Lost'), 'dark' # Item has been lost + QUARANTINED = ( + 75, + _('Quarantined'), + 'info', + ) # Item has been quarantined and is unavailable + RETURNED = 85, _('Returned'), 'warning' # Item has been returned from a customer + + +class StockStatusGroups: + """Groups for StockStatus codes.""" + + # The following codes correspond to parts that are 'available' or 'in stock' + AVAILABLE_CODES = [ + StockStatus.OK.value, + StockStatus.ATTENTION.value, + StockStatus.DAMAGED.value, + StockStatus.RETURNED.value, + ] + + +class StockHistoryCode(StatusCode): + """Status codes for StockHistory.""" + + LEGACY = 0, _('Legacy stock tracking entry') + + CREATED = 1, _('Stock item created') + + # Manual editing operations + EDITED = 5, _('Edited stock item') + ASSIGNED_SERIAL = 6, _('Assigned serial number') + + # Manual stock operations + STOCK_COUNT = 10, _('Stock counted') + STOCK_ADD = 11, _('Stock manually added') + STOCK_REMOVE = 12, _('Stock manually removed') + + # Location operations + STOCK_MOVE = 20, _('Location changed') + STOCK_UPDATE = 25, _('Stock updated') + + # Installation operations + INSTALLED_INTO_ASSEMBLY = 30, _('Installed into assembly') + REMOVED_FROM_ASSEMBLY = 31, _('Removed from assembly') + + INSTALLED_CHILD_ITEM = 35, _('Installed component item') + REMOVED_CHILD_ITEM = 36, _('Removed component item') + + # Stock splitting operations + SPLIT_FROM_PARENT = 40, _('Split from parent item') + SPLIT_CHILD_ITEM = 42, _('Split child item') + + # Stock merging operations + MERGED_STOCK_ITEMS = 45, _('Merged stock items') + + # Convert stock item to variant + CONVERTED_TO_VARIANT = 48, _('Converted to variant') + + # Build order codes + BUILD_OUTPUT_CREATED = 50, _('Build order output created') + BUILD_OUTPUT_COMPLETED = 55, _('Build order output completed') + BUILD_OUTPUT_REJECTED = 56, _('Build order output rejected') + BUILD_CONSUMED = 57, _('Consumed by build order') + + # Sales order codes + SHIPPED_AGAINST_SALES_ORDER = 60, _('Shipped against Sales Order') + + # Purchase order codes + RECEIVED_AGAINST_PURCHASE_ORDER = 70, _('Received against Purchase Order') + + # Return order codes + RETURNED_AGAINST_RETURN_ORDER = 80, _('Returned against Return Order') + + # Customer actions + SENT_TO_CUSTOMER = 100, _('Sent to customer') + RETURNED_FROM_CUSTOMER = 105, _('Returned from customer') diff --git a/src/backend/InvenTree/stock/test_api.py b/src/backend/InvenTree/stock/test_api.py index 42c29c4abf..07b08334ea 100644 --- a/src/backend/InvenTree/stock/test_api.py +++ b/src/backend/InvenTree/stock/test_api.py @@ -17,7 +17,6 @@ import build.models import company.models import part.models from common.models import InvenTreeSetting -from InvenTree.status_codes import StockHistoryCode, StockStatus from InvenTree.unit_test import InvenTreeAPITestCase from part.models import Part, PartTestTemplate from stock.models import ( @@ -26,6 +25,7 @@ from stock.models import ( StockLocation, StockLocationType, ) +from stock.status_codes import StockHistoryCode, StockStatus class StockAPITestCase(InvenTreeAPITestCase): diff --git a/src/backend/InvenTree/stock/test_views.py b/src/backend/InvenTree/stock/test_views.py index e6bb420636..0552a5af7a 100644 --- a/src/backend/InvenTree/stock/test_views.py +++ b/src/backend/InvenTree/stock/test_views.py @@ -5,9 +5,9 @@ from django.test import tag from django.urls import reverse from common.models import InvenTreeSetting -from InvenTree.status_codes import StockStatus from InvenTree.unit_test import InvenTreeTestCase from stock.models import StockItem, StockLocation +from stock.status_codes import StockStatus from users.models import Owner diff --git a/src/backend/InvenTree/stock/tests.py b/src/backend/InvenTree/stock/tests.py index 89d9e8e9d2..fe7a2b0e9d 100644 --- a/src/backend/InvenTree/stock/tests.py +++ b/src/backend/InvenTree/stock/tests.py @@ -10,10 +10,10 @@ from django.test import override_settings from build.models import Build from common.models import InvenTreeSetting from company.models import Company -from InvenTree.status_codes import StockHistoryCode from InvenTree.unit_test import InvenTreeTestCase from order.models import SalesOrder from part.models import Part, PartTestTemplate +from stock.status_codes import StockHistoryCode from .models import StockItem, StockItemTestResult, StockItemTracking, StockLocation diff --git a/src/backend/InvenTree/templates/InvenTree/settings/so.html b/src/backend/InvenTree/templates/InvenTree/settings/so.html index 9be6df6025..493ac32407 100644 --- a/src/backend/InvenTree/templates/InvenTree/settings/so.html +++ b/src/backend/InvenTree/templates/InvenTree/settings/so.html @@ -15,6 +15,7 @@ {% include "InvenTree/settings/setting.html" with key="SALESORDER_REQUIRE_RESPONSIBLE" %} {% include "InvenTree/settings/setting.html" with key="SALESORDER_DEFAULT_SHIPMENT" icon="fa-truck-loading" %} {% include "InvenTree/settings/setting.html" with key="SALESORDER_EDIT_COMPLETED_ORDERS" icon='fa-edit' %} + {% include "InvenTree/settings/setting.html" with key="SALESORDER_SHIP_COMPLETE" %} diff --git a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx index 904869bb7e..7feb204820 100644 --- a/src/frontend/src/pages/Index/Settings/SystemSettings.tsx +++ b/src/frontend/src/pages/Index/Settings/SystemSettings.tsx @@ -263,7 +263,8 @@ export default function SystemSettings() { 'SALESORDER_REFERENCE_PATTERN', 'SALESORDER_REQUIRE_RESPONSIBLE', 'SALESORDER_DEFAULT_SHIPMENT', - 'SALESORDER_EDIT_COMPLETED_ORDERS' + 'SALESORDER_EDIT_COMPLETED_ORDERS', + 'SALESORDER_SHIP_COMPLETE' ]} /> )