Sales Order Allocation Improvements (#4556)

* Do not remove sales order allocations when returning an item against a return order

* Also do not clear allocations when returning manually

* stock item display tweaks

* Add extra column to sales order allocation table

* Improve methods for introspecting sales order allocations for stockitems

* Only display "active" sales order allocations on a stock item detail page

- All allocations are still visible in the allocation table

* Can't have available quantity if you're not available

tap's side of nose
This commit is contained in:
Oliver 2023-04-02 21:43:05 +10:00 committed by GitHub
parent 847d49a42d
commit 4d8cfd77ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 56 additions and 18 deletions

View File

@ -1766,9 +1766,6 @@ class ReturnOrder(TotalPriceMixin, Order):
stock_item = line.item stock_item = line.item
# Remove any allocations against the returned StockItem
stock_item.clearAllocations()
deltas = { deltas = {
'status': StockStatus.QUARANTINED, 'status': StockStatus.QUARANTINED,
'returnorder': self.pk, 'returnorder': self.pk,

View File

@ -33,7 +33,8 @@ from InvenTree.fields import (InvenTreeModelMoneyField, InvenTreeNotesField,
InvenTreeURLField) InvenTreeURLField)
from InvenTree.models import (InvenTreeAttachment, InvenTreeBarcodeMixin, from InvenTree.models import (InvenTreeAttachment, InvenTreeBarcodeMixin,
InvenTreeTree, extract_int) InvenTreeTree, extract_int)
from InvenTree.status_codes import StockHistoryCode, StockStatus from InvenTree.status_codes import (SalesOrderStatus, StockHistoryCode,
StockStatus)
from part import models as PartModels from part import models as PartModels
from plugin.events import trigger_event from plugin.events import trigger_event
from plugin.models import MetadataMixin from plugin.models import MetadataMixin
@ -1013,7 +1014,6 @@ class StockItem(InvenTreeBarcodeMixin, MetadataMixin, common.models.MetaMixin, M
location=location location=location
) )
self.clearAllocations()
self.customer = None self.customer = None
self.belongs_to = None self.belongs_to = None
self.sales_order = None self.sales_order = None
@ -1059,9 +1059,33 @@ class StockItem(InvenTreeBarcodeMixin, MetadataMixin, common.models.MetaMixin, M
return total return total
def sales_order_allocation_count(self): def get_sales_order_allocations(self, active=True):
"""Return a queryset for SalesOrderAllocations against this StockItem, with optional filters.
Arguments:
active: Filter by 'active' status of the allocation
"""
query = self.sales_order_allocations.all()
if active is True:
query = query.filter(
line__order__status__in=SalesOrderStatus.OPEN,
shipment__shipment_date=None
)
elif active is False:
query = query.exclude(
line__order__status__in=SalesOrderStatus.OPEN
).exclude(
shipment__shipment_date=None
)
return query
def sales_order_allocation_count(self, active=True):
"""Return the total quantity allocated to SalesOrders.""" """Return the total quantity allocated to SalesOrders."""
query = self.sales_order_allocations.aggregate(q=Coalesce(Sum('quantity'), Decimal(0)))
query = self.get_sales_order_allocations(active=active)
query = query.aggregate(q=Coalesce(Sum('quantity'), Decimal(0)))
total = query['q'] total = query['q']

View File

@ -59,7 +59,7 @@
{% include "filter_list.html" with id="salesorderallocation" %} {% include "filter_list.html" with id="salesorderallocation" %}
</div> </div>
</div> </div>
<table class='table table-striped table-condensed' data-toolbar='#sales-order-allocation-toolbar' id='sales-order-allocation-table'></table> <table class='table table-striped table-condensed' data-toolbar='#sales-order-allocations-toolbar' id='sales-order-allocation-table'></table>
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -266,6 +266,12 @@
<div class='info-messages'> <div class='info-messages'>
{% if not item.in_stock %}
<div class='alert alert-block alert-danger'>
<span class='fas fa-info-circle'></span> {% trans "This stock item is unavailable" %}
</div>
{% endif %}
{% if item.is_building %} {% if item.is_building %}
<div class='alert alert-block alert-info'> <div class='alert alert-block alert-info'>
{% trans "This stock item is in production and cannot be edited." %}<br> {% trans "This stock item is in production and cannot be edited." %}<br>
@ -281,12 +287,12 @@
{% endif %} {% endif %}
{% if item.hasRequiredTests and not item.passedAllRequiredTests %} {% if item.hasRequiredTests and not item.passedAllRequiredTests %}
<div class='alert alert-block alert-danger'> <div class='alert alert-block alert-warning'>
{% trans "This stock item has not passed all required tests" %} {% trans "This stock item has not passed all required tests" %}
</div> </div>
{% endif %} {% endif %}
{% for allocation in item.sales_order_allocations.all %} {% for allocation in item.get_sales_order_allocations.all %}
<div class='alert alert-block alert-info'> <div class='alert alert-block alert-info'>
{% object_link 'so-detail' allocation.line.order.id allocation.line.order as link %} {% object_link 'so-detail' allocation.line.order.id allocation.line.order as link %}
{% decimal allocation.quantity as qty %} {% decimal allocation.quantity as qty %}
@ -301,12 +307,6 @@
{% trans "This stock item is allocated to Build Order" %} {{ link }} {% if qty < item.quantity %}({% trans "Quantity" %}: {{ qty }}){% endif %} {% trans "This stock item is allocated to Build Order" %} {{ link }} {% if qty < item.quantity %}({% trans "Quantity" %}: {{ qty }}){% endif %}
</div> </div>
{% endfor %} {% endfor %}
{% if item.serialized %}
<div class='alert alert-block alert-warning'>
{% trans "This stock item is serialized - it has a unique serial number and the quantity cannot be adjusted." %}
</div>
{% endif %}
</div> </div>
{% endblock details %} {% endblock details %}
@ -320,7 +320,7 @@
<h5><span class='fas fa-hashtag'></span></h5> <h5><span class='fas fa-hashtag'></span></h5>
</td> </td>
<td> <td>
<h5>{% trans "Serial Number" %}</h5> <h5 title='{% trans "This stock item is serialized. It has a unique serial number and the quantity cannot be adjusted" %}'>{% trans "Serial Number" %}</h5>
</td> </td>
<td><h5> <td><h5>
{{ item.serial }} {{ item.serial }}
@ -348,7 +348,11 @@
<h5><div class='fas fa-boxes'></div></h5> <h5><div class='fas fa-boxes'></div></h5>
</td> </td>
<td> <td>
{% if item.in_stock %}
<h5>{% trans "Available Quantity" %}</h5> <h5>{% trans "Available Quantity" %}</h5>
{% else %}
<h5>{% trans "Quantity" %}</h5>
{% endif %}
</td> </td>
<td> <td>
<h5>{% if item.quantity != available %}{% decimal available %} / {% endif %}{% decimal item.quantity %} {% include "part/part_units.html" with part=item.part %}</h5> <h5>{% if item.quantity != available %}{% decimal available %} / {% endif %}{% decimal item.quantity %} {% include "part/part_units.html" with part=item.part %}</h5>
@ -367,7 +371,7 @@
</tr> </tr>
{% elif item.sales_order %} {% elif item.sales_order %}
<tr> <tr>
<td><span class='fas fa-user-tie'></span></td> <td><span class='fas fa-th-list'></span></td>
<td>{% trans "Sales Order" %}</td> <td>{% trans "Sales Order" %}</td>
<td><a href="{% url 'so-detail' item.sales_order.id %}">{{ item.sales_order.reference }}</a> - <a href="{% url 'company-detail' item.sales_order.customer.id %}">{{ item.sales_order.customer.name }}</a></td> <td><a href="{% url 'so-detail' item.sales_order.id %}">{{ item.sales_order.reference }}</a> - <a href="{% url 'company-detail' item.sales_order.customer.id %}">{{ item.sales_order.customer.name }}</a></td>
</tr> </tr>

View File

@ -1387,6 +1387,7 @@ function loadSalesOrderAllocationTable(table, options={}) {
}, },
{ {
field: 'item', field: 'item',
switchable: false,
title: '{% trans "Stock Item" %}', title: '{% trans "Stock Item" %}',
formatter: function(value, row) { formatter: function(value, row) {
// Render a link to the particular stock item // Render a link to the particular stock item
@ -1409,6 +1410,18 @@ function loadSalesOrderAllocationTable(table, options={}) {
title: '{% trans "Quantity" %}', title: '{% trans "Quantity" %}',
sortable: true, sortable: true,
}, },
{
field: 'shipment_date',
title: '{% trans "Shipped" %}',
sortable: true,
formatter: function(value, row) {
if (value) {
return renderDate(value);
} else {
return `<em>{% trans "Not shipped" %}</em>`;
}
}
}
] ]
}); });
} }