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:
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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>
|
||||
|
Reference in New Issue
Block a user