mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 19:46:46 +00:00
[Refactor] Sales Order Shipment (#8249)
* Refactor serial number allocation * Refactor API query Note: This should be further improved, not to automatically return *all* allocation data * Push expensive operations off to background worker * Bump API version
This commit is contained in:
parent
44d9484715
commit
198a39a33c
@ -1,14 +1,17 @@
|
|||||||
"""InvenTree API version information."""
|
"""InvenTree API version information."""
|
||||||
|
|
||||||
# InvenTree API version
|
# InvenTree API version
|
||||||
INVENTREE_API_VERSION = 265
|
INVENTREE_API_VERSION = 266
|
||||||
|
|
||||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||||
|
|
||||||
|
|
||||||
INVENTREE_API_TEXT = """
|
INVENTREE_API_TEXT = """
|
||||||
|
|
||||||
265 - 2024-10-06 : https://github.com/inventree/InvenTree/pull/8228
|
266 - 2024-10-07 : https://github.com/inventree/InvenTree/pull/8249
|
||||||
|
- Tweak SalesOrderShipment API for more efficient data retrieval
|
||||||
|
|
||||||
|
265 - 2024-10-07 : https://github.com/inventree/InvenTree/pull/8228
|
||||||
- Adds API endpoint for providing custom admin integration details for plugins
|
- Adds API endpoint for providing custom admin integration details for plugins
|
||||||
|
|
||||||
264 - 2024-10-03 : https://github.com/inventree/InvenTree/pull/8231
|
264 - 2024-10-03 : https://github.com/inventree/InvenTree/pull/8231
|
||||||
|
@ -767,11 +767,15 @@ class SalesOrderLineItemMixin:
|
|||||||
'part',
|
'part',
|
||||||
'part__stock_items',
|
'part__stock_items',
|
||||||
'allocations',
|
'allocations',
|
||||||
|
'allocations__shipment',
|
||||||
|
'allocations__item__part',
|
||||||
'allocations__item__location',
|
'allocations__item__location',
|
||||||
'order',
|
'order',
|
||||||
'order__stock_items',
|
'order__stock_items',
|
||||||
)
|
)
|
||||||
|
|
||||||
|
queryset = queryset.select_related('part__pricing_data')
|
||||||
|
|
||||||
queryset = serializers.SalesOrderLineItemSerializer.annotate_queryset(queryset)
|
queryset = serializers.SalesOrderLineItemSerializer.annotate_queryset(queryset)
|
||||||
|
|
||||||
return queryset
|
return queryset
|
||||||
|
@ -1878,16 +1878,11 @@ class SalesOrderShipment(
|
|||||||
2. Update the "shipped" quantity of all associated line items
|
2. Update the "shipped" quantity of all associated line items
|
||||||
3. Set the "shipment_date" to now
|
3. Set the "shipment_date" to now
|
||||||
"""
|
"""
|
||||||
|
import order.tasks
|
||||||
|
|
||||||
# Check if the shipment can be completed (throw error if not)
|
# Check if the shipment can be completed (throw error if not)
|
||||||
self.check_can_complete()
|
self.check_can_complete()
|
||||||
|
|
||||||
allocations = self.allocations.all()
|
|
||||||
|
|
||||||
# Iterate through each stock item assigned to this shipment
|
|
||||||
for allocation in allocations:
|
|
||||||
# Mark the allocation as "complete"
|
|
||||||
allocation.complete_allocation(user)
|
|
||||||
|
|
||||||
# Update the "shipment" date
|
# Update the "shipment" date
|
||||||
self.shipment_date = kwargs.get(
|
self.shipment_date = kwargs.get(
|
||||||
'shipment_date', InvenTree.helpers.current_date()
|
'shipment_date', InvenTree.helpers.current_date()
|
||||||
@ -1920,6 +1915,14 @@ class SalesOrderShipment(
|
|||||||
|
|
||||||
self.save()
|
self.save()
|
||||||
|
|
||||||
|
# Offload the "completion" of each line item to the background worker
|
||||||
|
# This may take some time, and we don't want to block the main thread
|
||||||
|
InvenTree.tasks.offload_task(
|
||||||
|
order.tasks.complete_sales_order_shipment,
|
||||||
|
shipment_id=self.pk,
|
||||||
|
user_id=user.pk if user else None,
|
||||||
|
)
|
||||||
|
|
||||||
trigger_event('salesordershipment.completed', id=self.pk)
|
trigger_event('salesordershipment.completed', id=self.pk)
|
||||||
|
|
||||||
def create_attachment(self, *args, **kwargs):
|
def create_attachment(self, *args, **kwargs):
|
||||||
|
@ -1094,10 +1094,10 @@ class SalesOrderAllocationSerializer(InvenTreeModelSerializer):
|
|||||||
# Extra detail fields
|
# Extra detail fields
|
||||||
order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
|
order_detail = SalesOrderSerializer(source='line.order', many=False, read_only=True)
|
||||||
part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
|
part_detail = PartBriefSerializer(source='item.part', many=False, read_only=True)
|
||||||
item_detail = stock.serializers.StockItemSerializer(
|
item_detail = stock.serializers.StockItemSerializerBrief(
|
||||||
source='item', many=False, read_only=True
|
source='item', many=False, read_only=True
|
||||||
)
|
)
|
||||||
location_detail = stock.serializers.LocationSerializer(
|
location_detail = stock.serializers.LocationBriefSerializer(
|
||||||
source='item.location', many=False, read_only=True
|
source='item.location', many=False, read_only=True
|
||||||
)
|
)
|
||||||
customer_detail = CompanyBriefSerializer(
|
customer_detail = CompanyBriefSerializer(
|
||||||
@ -1659,12 +1659,18 @@ class SalesOrderSerialAllocationSerializer(serializers.Serializer):
|
|||||||
stock_items = data['stock_items']
|
stock_items = data['stock_items']
|
||||||
shipment = data['shipment']
|
shipment = data['shipment']
|
||||||
|
|
||||||
with transaction.atomic():
|
allocations = []
|
||||||
for stock_item in stock_items:
|
|
||||||
# Create a new SalesOrderAllocation
|
for stock_item in stock_items:
|
||||||
order.models.SalesOrderAllocation.objects.create(
|
# Create a new SalesOrderAllocation
|
||||||
|
allocations.append(
|
||||||
|
order.models.SalesOrderAllocation(
|
||||||
line=line_item, item=stock_item, quantity=1, shipment=shipment
|
line=line_item, item=stock_item, quantity=1, shipment=shipment
|
||||||
)
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
order.models.SalesOrderAllocation.objects.bulk_create(allocations)
|
||||||
|
|
||||||
|
|
||||||
class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
|
class SalesOrderShipmentAllocationSerializer(serializers.Serializer):
|
||||||
|
@ -1,7 +1,10 @@
|
|||||||
"""Background tasks for the 'order' app."""
|
"""Background tasks for the 'order' app."""
|
||||||
|
|
||||||
|
import logging
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
|
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.db import transaction
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
import common.notifications
|
import common.notifications
|
||||||
@ -11,6 +14,8 @@ from InvenTree.tasks import ScheduledTask, scheduled_task
|
|||||||
from order.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups
|
from order.status_codes import PurchaseOrderStatusGroups, SalesOrderStatusGroups
|
||||||
from plugin.events import trigger_event
|
from plugin.events import trigger_event
|
||||||
|
|
||||||
|
logger = logging.getLogger('inventree')
|
||||||
|
|
||||||
|
|
||||||
def notify_overdue_purchase_order(po: order.models.PurchaseOrder):
|
def notify_overdue_purchase_order(po: order.models.PurchaseOrder):
|
||||||
"""Notify users that a PurchaseOrder has just become 'overdue'."""
|
"""Notify users that a PurchaseOrder has just become 'overdue'."""
|
||||||
@ -109,3 +114,31 @@ def check_overdue_sales_orders():
|
|||||||
|
|
||||||
for po in overdue_orders:
|
for po in overdue_orders:
|
||||||
notify_overdue_sales_order(po)
|
notify_overdue_sales_order(po)
|
||||||
|
|
||||||
|
|
||||||
|
def complete_sales_order_shipment(shipment_id: int, user_id: int) -> None:
|
||||||
|
"""Complete allocations for a pending shipment against a SalesOrder.
|
||||||
|
|
||||||
|
At this stage, the shipment is assumed to be complete,
|
||||||
|
and we need to perform the required "processing" tasks.
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
shipment = order.models.SalesOrderShipment.objects.get(pk=shipment_id)
|
||||||
|
except Exception:
|
||||||
|
# Shipping object does not exist
|
||||||
|
logger.warning(
|
||||||
|
'Failed to complete shipment - no matching SalesOrderShipment for ID <%s>',
|
||||||
|
shipment_id,
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
user = User.objects.get(pk=user_id)
|
||||||
|
except Exception:
|
||||||
|
user = None
|
||||||
|
|
||||||
|
logger.info('Completing SalesOrderShipment <%s>', shipment)
|
||||||
|
|
||||||
|
with transaction.atomic():
|
||||||
|
for allocation in shipment.allocations.all():
|
||||||
|
allocation.complete_allocation(user=user)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user