diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py
index c7914827e7..3443c9982f 100644
--- a/InvenTree/stock/api.py
+++ b/InvenTree/stock/api.py
@@ -22,7 +22,10 @@ from part.models import Part, PartCategory
from part.serializers import PartBriefSerializer
from company.models import SupplierPart
-from company.serializers import SupplierPartSerializer
+from company.serializers import CompanySerializer, SupplierPartSerializer
+
+from order.models import PurchaseOrder
+from order.serializers import POSerializer
import common.settings
import common.models
@@ -992,6 +995,59 @@ class StockTrackingList(generics.ListAPIView):
return self.serializer_class(*args, **kwargs)
+ def list(self, request, *args, **kwargs):
+
+ queryset = self.filter_queryset(self.get_queryset())
+
+ serializer = self.get_serializer(queryset, many=True)
+
+ data = serializer.data
+
+ # Attempt to add extra context information to the historical data
+ for item in data:
+ deltas = item['deltas']
+
+ # Add location detail
+ if 'location' in deltas:
+ try:
+ location = StockLocation.objects.get(pk=deltas['location'])
+ serializer = LocationSerializer(location)
+ deltas['location_detail'] = serializer.data
+ except:
+ pass
+
+ # Add stockitem detail
+ if 'stockitem' in deltas:
+ try:
+ stockitem = StockItem.objects.get(pk=deltas['stockitem'])
+ serializer = StockItemSerializer(stockitem)
+ deltas['stockitem_detail'] = serializer.data
+ except:
+ pass
+
+ # Add customer detail
+ if 'customer' in deltas:
+ try:
+ customer = Company.objects.get(pk=deltas['customer'])
+ serializer = CompanySerializer(location)
+ deltas['customer_detail'] = serializer.data
+ except:
+ pass
+
+ # Add purchaseorder detail
+ if 'purchaseorder' in deltas:
+ try:
+ order = PurchaseOrder.objects.get(pk=deltas['purchaseorder'])
+ serializer = POSerializer(order)
+ deltas['purchaseorder_detail'] = serializer.data
+ except:
+ pass
+
+ if request.is_ajax():
+ return JsonResponse(data, safe=False)
+ else:
+ return Response(data)
+
def create(self, request, *args, **kwargs):
""" Create a new StockItemTracking object
diff --git a/InvenTree/templates/js/stock.js b/InvenTree/templates/js/stock.js
index 4e5d78d8cd..f3f1c7a6bd 100644
--- a/InvenTree/templates/js/stock.js
+++ b/InvenTree/templates/js/stock.js
@@ -976,42 +976,28 @@ function loadStockLocationTable(table, options) {
function loadStockTrackingTable(table, options) {
- var cols = [
- {
- field: 'pk',
- visible: false,
- },
- {
- field: 'date',
- title: '{% trans "Date" %}',
- sortable: true,
- formatter: function(value, row, index, field) {
- var m = moment(value);
- if (m.isValid()) {
- var html = m.format('dddd MMMM Do YYYY'); // + '
' + m.format('h:mm a');
- return html;
- }
+ var cols = [];
- return 'N/A';
- }
- },
- ];
+ // Date
+ cols.push({
+ field: 'date',
+ title: '{% trans "Date" %}',
+ sortable: true,
+ formatter: function(value, row, index, field) {
+ var m = moment(value);
- // If enabled, provide a link to the referenced StockItem
- if (options.partColumn) {
- cols.push({
- field: 'item',
- title: '{% trans "Stock Item" %}',
- sortable: true,
- formatter: function(value, row, index, field) {
- return renderLink(value.part_name, value.url);
+ if (m.isValid()) {
+ var html = m.format('dddd MMMM Do YYYY'); // + '
' + m.format('h:mm a');
+ return html;
}
- });
- }
+
+ return '{% trans "Invalid date" %}';
+ }
+ });
// Stock transaction description
cols.push({
- field: 'title',
+ field: 'label',
title: '{% trans "Description" %}',
formatter: function(value, row, index, field) {
var html = "" + value + "";
@@ -1020,20 +1006,129 @@ function loadStockTrackingTable(table, options) {
html += "
" + row.notes + "";
}
- if (row.link) {
- html += "
" + row.link + "";
- }
-
return html;
}
});
+ // Stock transaction details
cols.push({
- field: 'quantity',
- title: '{% trans "Quantity" %}',
- formatter: function(value, row, index, field) {
- return parseFloat(value);
- },
+ field: 'deltas',
+ title: '{% trans "Details" %}',
+ formatter: function(details, row, index, field) {
+ var html = `
{% trans "Location" %} | `; + + html += ''; + + if (details.location_detail) { + // A valid location is provided + + html += renderLink( + details.location_detail.pathstring, + details.location_detail.url, + ); + } else { + // An invalid location (may have been deleted?) + html += `{% trans "Location no longer exists" %}`; + } + + html += ' |
---|---|
{% trans "Purchase Order" %}`; + + html += ' | '; + + if (details.purchaseorder_detail) { + html += renderLink( + details.purchaseorder_detail.reference, + `/order/purchase-order/${details.purchaseorder}/` + ); + } else { + html += `{% trans "Purchase order no longer exists" %}`; + } + + html += ' |
{% trans "Customer" %}`; + + html += ' | '; + + if (details.customer_detail) { + html += renderLink( + details.customer_detail.name, + details.customer_detail.url + ); + } else { + html += `{% trans "Customer no longer exists" %}`; + } + + html += ' |
{% trans "Stock Item" %}'; + + html += ' | '; + + if (details.stockitem_detail) { + html += renderLink( + details.stockitem, + `/stock/item/${details.stockitem}/` + ); + } else { + html += `{% trans "Stock item no longer exists" %}`; + } + + html += ' |
{% trans "Added" %} | '; + + html += `${details.added} | `; + + html += '
{% trans "Removed" %} | '; + + html += `${details.removed} | `; + + html += '
{% trans "Quantity" %} | '; + + html += `${details.quantity} | `; + + html += '