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

[PUI] Part scheduling (#8163)

* Placeholder part scheduling panel

* Add API endpoint definition

* Add defined serializer to scheduling endpoint

* Refactor add_schedule_entry

* Fix field type

* API tweak

* Render scheduling data

* Make links clickable

* Correctly account for dates in the past

* Cleanup table

* Bump API version information

* js linting
This commit is contained in:
Oliver
2024-09-24 13:57:28 +10:00
committed by GitHub
parent 82b7e97df6
commit 4f2baab43b
8 changed files with 400 additions and 36 deletions

View File

@ -1,13 +1,17 @@
"""InvenTree API version information."""
# InvenTree API version
INVENTREE_API_VERSION = 257
INVENTREE_API_VERSION = 258
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """
v258 - 2024-09-24 : https://github.com/inventree/InvenTree/pull/8163
- Enhances the existing PartScheduling API endpoint
- Adds a formal DRF serializer to the endpoint
v257 - 2024-09-22 : https://github.com/inventree/InvenTree/pull/8150
- Adds API endpoint for reporting barcode scan history

View File

@ -559,7 +559,7 @@ class PartScheduling(RetrieveAPI):
"""
queryset = Part.objects.all()
serializer_class = EmptySerializer
serializer_class = part_serializers.PartSchedulingSerializer
def retrieve(self, request, *args, **kwargs):
"""Return scheduling information for the referenced Part instance."""
@ -567,23 +567,24 @@ class PartScheduling(RetrieveAPI):
schedule = []
def add_schedule_entry(
date, quantity, title, label, url, speculative_quantity=0
):
"""Check if a scheduled entry should be added.
def add_schedule_entry(date, quantity, title, instance, speculative_quantity=0):
"""Add a new entry to the schedule list.
Rules:
- date must be non-null
- date cannot be in the "past"
- quantity must not be zero
Arguments:
- date: The date of the scheduled event
- quantity: The quantity of stock to be added or removed
- title: The title of the scheduled event
- instance: The associated model instance (e.g. SalesOrder object)
- speculative_quantity: A speculative quantity to be added or removed
"""
schedule.append({
'date': date,
'quantity': quantity,
'speculative_quantity': speculative_quantity,
'title': title,
'label': label,
'url': url,
'label': str(instance.reference),
'model': instance.__class__.__name__.lower(),
'model_id': instance.pk,
})
# Add purchase order (incoming stock) information
@ -600,11 +601,7 @@ class PartScheduling(RetrieveAPI):
quantity = line.part.base_quantity(line_quantity)
add_schedule_entry(
target_date,
quantity,
_('Incoming Purchase Order'),
str(line.order),
line.order.get_absolute_url(),
target_date, quantity, _('Incoming Purchase Order'), line.order
)
# Add sales order (outgoing stock) information
@ -618,11 +615,7 @@ class PartScheduling(RetrieveAPI):
quantity = max(line.quantity - line.shipped, 0)
add_schedule_entry(
target_date,
-quantity,
_('Outgoing Sales Order'),
str(line.order),
line.order.get_absolute_url(),
target_date, -quantity, _('Outgoing Sales Order'), line.order
)
# Add build orders (incoming stock) information
@ -634,11 +627,7 @@ class PartScheduling(RetrieveAPI):
quantity = max(build.quantity - build.completed, 0)
add_schedule_entry(
build.target_date,
quantity,
_('Stock produced by Build Order'),
str(build),
build.get_absolute_url(),
build.target_date, quantity, _('Stock produced by Build Order'), build
)
"""
@ -721,8 +710,7 @@ class PartScheduling(RetrieveAPI):
build.target_date,
-part_allocated_quantity,
_('Stock required for Build Order'),
str(build),
build.get_absolute_url(),
build,
speculative_quantity=speculative_quantity,
)
@ -742,9 +730,13 @@ class PartScheduling(RetrieveAPI):
return -1 if date_1 < date_2 else 1
# Sort by incrementing date values
schedule = sorted(schedule, key=functools.cmp_to_key(compare))
schedules = sorted(schedule, key=functools.cmp_to_key(compare))
return Response(schedule)
serializers = part_serializers.PartSchedulingSerializer(
schedules, many=True, context={'request': request}
)
return Response(serializers.data)
class PartRequirements(RetrieveAPI):

View File

@ -244,6 +244,39 @@ class PartInternalPriceSerializer(InvenTree.serializers.InvenTreeModelSerializer
)
class PartSchedulingSerializer(serializers.Serializer):
"""Serializer class for a PartScheduling entry."""
class Meta:
"""Metaclass options for this serializer."""
fields = [
'date',
'quantity',
'speculative_quantity',
'title',
'label',
'model',
'model_id',
]
date = serializers.DateField(label=_('Date'), required=True, allow_null=True)
quantity = serializers.FloatField(label=_('Quantity'), required=True)
speculative_quantity = serializers.FloatField(
label=_('Speculative Quantity'), required=False
)
title = serializers.CharField(label=_('Title'), required=True)
label = serializers.CharField(label=_('Label'), required=True)
model = serializers.CharField(label=_('Model'), required=True)
model_id = serializers.IntegerField(label=_('Model ID'), required=True)
class PartThumbSerializer(serializers.Serializer):
"""Serializer for the 'image' field of the Part model.

View File

@ -3078,10 +3078,26 @@ function loadPartSchedulingChart(canvas_id, part_id) {
quantity_string += makeIconBadge('fa-question-circle icon-blue', '{% trans "Speculative" %}');
}
let url = '#';
switch (entry.model) {
case 'salesorder':
url = `/order/sales-order/${entry.model_id}/`;
break;
case 'purchaseorder':
url = `/order/purchase-order/${entry.model_id}/`;
break;
case 'build':
url = `/build/${entry.model_id}/`;
break;
default:
break;
}
// Add an entry to the scheduling table
table_html += `
<tr>
<td><a href="${entry.url}">${entry.label}</a></td>
<td><a href="${url}">${entry.label}</a></td>
<td>${entry.title}</td>
<td>${date_string}</td>
<td>${quantity_string}</td>