2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-04-30 12:36:45 +00:00

Calculate 'fulfilled' quantity once a sales order is marked as shipped

- This allows us to delete the SalesOrderAllocation objects from the database
This commit is contained in:
Oliver Walters 2020-04-27 08:58:18 +10:00
parent 3c5ba75d27
commit 5167117067
10 changed files with 32 additions and 31 deletions

View File

@ -480,6 +480,8 @@ class BuildItem(models.Model):
self.stock_item = item
self.save()
# TODO - If the item__part object is not trackable, delete the stock item here
item.status = StockStatus.ASSIGNED_TO_BUILD
item.build_order = self.build
item.save()

View File

@ -348,14 +348,17 @@ class SOLineItemList(generics.ListCreateAPIView):
queryset = super().get_queryset(*args, **kwargs)
return queryset.prefetch_related(
queryset = queryset.prefetch_related(
'part',
'part__stock_items',
'allocations',
'allocations__item__location',
'order',
'order__stock_items',
)
return queryset
permission_classes = [permissions.IsAuthenticated]
filter_backends = [DjangoFilterBackend]

View File

@ -324,8 +324,11 @@ class SalesOrder(Order):
for allocation in line.allocations.all():
allocation.complete_allocation(user)
# TODO - Remove the allocation from the database
# allocation.delete()
# Remove the allocation from the database once it has been 'fulfilled'
if allocation.item.sales_order == self.order:
allocation.delete()
else:
raise ValidationError("Could not complete order - allocation item not fulfilled")
# Ensure the order status is marked as "Shipped"
self.status = SalesOrderStatus.SHIPPED
@ -458,6 +461,15 @@ class SalesOrderLineItem(OrderLineItem):
part = models.ForeignKey('part.Part', on_delete=models.SET_NULL, related_name='sales_order_line_items', null=True, help_text=_('Part'), limit_choices_to={'salable': True})
def fulfilled_quantity(self):
"""
Return the total stock quantity fulfilled against this line item.
"""
query = self.order.stock_items.filter(part=self.part).aggregate(fulfilled=Coalesce(Sum('quantity'), Decimal(0)))
return query['fulfilled']
def allocated_quantity(self):
""" Return the total stock quantity allocated to this LineItem.

View File

@ -211,6 +211,7 @@ class SOLineItemSerializer(InvenTreeModelSerializer):
quantity = serializers.FloatField()
allocated = serializers.FloatField(source='allocated_quantity', read_only=True)
fulfilled = serializers.FloatField(source='fulfilled_quantity', read_only=True)
class Meta:
model = SalesOrderLineItem
@ -220,6 +221,7 @@ class SOLineItemSerializer(InvenTreeModelSerializer):
'allocated',
'allocations',
'quantity',
'fulfilled',
'reference',
'notes',
'order',

View File

@ -5,7 +5,7 @@
{% include 'part/tabs.html' with tab='stock' %}
<h4>Part Stock</h4>
<h4>{% trans "Part Stock" %}</h4>
<hr>
{% if part.is_template %}
@ -40,6 +40,7 @@
part: {{ part.id }},
location_detail: true,
part_detail: true,
in_stock: true,
},
groupByField: 'location',
buttons: [

View File

@ -48,7 +48,7 @@
<a href="{% url 'part-sales-orders' part.id %}">{% trans "Sales Orders" %} <span class='badge'>{{ part.sales_orders|length }}</span></a>
</li>
{% endif %}
{% if part.trackable and 0 %}
{% if part.trackable %}
<li{% ifequal tab 'track' %} class="active"{% endifequal %}>
<a href="{% url 'part-track' part.id %}">{% trans "Tracking" %}
{% if parts.serials.all|length > 0 %}

View File

@ -1,28 +1,12 @@
{% extends "part/part_base.html" %}
{% load static %}
{% load i18n %}
{% block details %}
{% include 'part/tabs.html' with tab='track' %}
Part tracking for {{ part.full_name }}
<h4>{% trans "Part Tracking" %}</h4>
<table class="table table-striped">
<tr>
<th>Serial</th>
<th>Status</th>
</tr>
{% for track in part.tracked_parts.all %}
<tr>
<td><a href="{% url 'track-detail' track.id %}">{{ track.serial }}</a></td>
<td>{{ track.get_status_display }}</td>
</tr>
{% endfor %}
</table>
<div class='container-fluid'>
<a href="{% url 'track-create' %}?part={{ part.id }}">
<button class="btn btn-success">New Tracked Part</button>
</a>
</div>
<hr>
{% endblock %}

View File

@ -58,7 +58,8 @@ InvenTree | {% trans "Stock Item" %} - {{ item }}
<a href="{% url 'build-detail' item.build_order.id %}">
<div class='label label-large label-large-blue'>{% trans "Used in Build" %}</div>
</a>
{% elif item.status in StockStatus.UNAVAILABLE_CODES %}{% stock_status_label item.status large=True %}
{% else %}
{% stock_status_label item.status large=True %}
{% endif %}
</h3>
<hr>

View File

@ -233,6 +233,7 @@
{% endif %}
part_detail: true,
location_detail: true,
in_stock: true,
},
url: "{% url 'api-stock-list' %}",
});

View File

@ -21,11 +21,6 @@ function getAvailableTableFilters(tableKey) {
title: '{% trans "Include sublocations" %}',
description: '{% trans "Include stock in sublocations" %}',
},
in_stock: {
type: 'bool',
title: '{% trans "In stock" %}',
description: '{% trans "Item is in stock" %}',
},
active: {
type: 'bool',
title: '{% trans "Active parts" %}',