2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-19 05:25:42 +00:00

Scheduling information is now calculated on the server, and provided via a new API endpoint

- Much simpler than sequencing multiple API calls
This commit is contained in:
Oliver
2022-03-01 22:54:49 +11:00
parent dfd6684699
commit 776dffe779
3 changed files with 161 additions and 152 deletions

View File

@ -5,6 +5,8 @@ Provides a JSON API for the Part app
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import datetime
from django.conf.urls import url, include
from django.http import JsonResponse
from django.db.models import Q, F, Count, Min, Max, Avg
@ -38,14 +40,15 @@ from company.models import Company, ManufacturerPart, SupplierPart
from stock.models import StockItem, StockLocation
from common.models import InvenTreeSetting
from build.models import Build
from build.models import Build, BuildItem
import order.models
from . import serializers as part_serializers
from InvenTree.helpers import str2bool, isNull, increment
from InvenTree.api import AttachmentMixin
from InvenTree.status_codes import BuildStatus
from InvenTree.status_codes import BuildStatus, PurchaseOrderStatus, SalesOrderStatus
class CategoryList(generics.ListCreateAPIView):
@ -427,6 +430,137 @@ class PartThumbsUpdate(generics.RetrieveUpdateAPIView):
]
class PartScheduling(generics.RetrieveAPIView):
"""
API endpoint for delivering "scheduling" information about a given part via the API.
Returns a chronologically ordered list about future "scheduled" events,
concerning stock levels for the part:
- Purchase Orders (incoming stock)
- Sales Orders (outgoing stock)
- Build Orders (incoming completed stock)
- Build Orders (outgoing allocated stock)
"""
queryset = Part.objects.all()
def retrieve(self, request, *args, **kwargs):
today = datetime.datetime.now().date()
part = self.get_object()
schedule = []
def add_schedule_entry(date, quantity, label, url):
"""
Check if a scheduled entry should be added:
- date must be non-null
- date cannot be in the "past"
- quantity must not be zero
"""
if date and date >= today and quantity != 0:
schedule.append({
'date': date,
'quantity': quantity,
'url': url,
'label': label,
})
# Add purchase order (incoming stock) information
po_lines = order.models.PurchaseOrderLineItem.objects.filter(
part__part=part,
order__status__in=PurchaseOrderStatus.OPEN,
)
for line in po_lines:
target_date = line.target_date or line.order.target_date
quantity = max(line.quantity - line.received, 0)
add_schedule_entry(
target_date,
quantity,
str(line.order),
line.order.get_absolute_url()
)
# Add sales order (outgoing stock) information
so_lines = order.models.SalesOrderLineItem.objects.filter(
part=part,
order__status__in=SalesOrderStatus.OPEN,
)
for line in so_lines:
target_date = line.target_date or line.order.target_date
quantity = max(line.quantity - line.shipped, 0)
add_schedule_entry(
target_date,
-quantity,
str(line.order),
line.order.get_absolute_url(),
)
# Add build orders (incoming stock) information
build_orders = Build.objects.filter(
part=part,
status__in=BuildStatus.ACTIVE_CODES
)
for build in build_orders:
quantity = max(build.quantity - build.completed, 0)
add_schedule_entry(
build.target_date,
quantity,
str(build),
build.get_absolute_url(),
)
"""
Add build order allocation (outgoing stock) information.
Here we need some careful consideration:
- 'Tracked' stock items are removed from stock when the individual Build Output is completed
- 'Untracked' stock items are removed from stock when the Build Order is completed
The 'simplest' approach here is to look at existing BuildItem allocations which reference this part,
and "schedule" them for removal at the time of build order completion.
This assumes that the user is responsible for correctly allocating parts.
However, it has the added benefit of side-stepping the various BOM substition options,
and just looking at what stock items the user has actually allocated against the Build.
"""
build_allocations = BuildItem.objects.filter(
stock_item__part=part,
build__status__in=BuildStatus.ACTIVE_CODES,
)
for allocation in build_allocations:
add_schedule_entry(
allocation.build.target_date,
-allocation.quantity,
str(allocation.build),
allocation.build.get_absolute_url(),
)
# Sort by incrementing date values
schedule = sorted(schedule, key=lambda entry: entry['date'])
return Response(schedule)
class PartSerialNumberDetail(generics.RetrieveAPIView):
"""
API endpoint for returning extra serial number information about a particular part
@ -1715,6 +1849,9 @@ part_api_urls = [
# Endpoint for extra serial number information
url(r'^serial-numbers/', PartSerialNumberDetail.as_view(), name='api-part-serial-number-detail'),
# Endpoint for future scheduling information
url(r'^scheduling/', PartScheduling.as_view(), name='api-part-scheduling'),
# Endpoint for duplicating a BOM for the specific Part
url(r'^bom-copy/', PartCopyBOM.as_view(), name='api-part-bom-copy'),