diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py
index fe28d780c7..0a9d39225b 100644
--- a/InvenTree/InvenTree/version.py
+++ b/InvenTree/InvenTree/version.py
@@ -12,11 +12,14 @@ import common.models
INVENTREE_SW_VERSION = "0.7.0 dev"
# InvenTree API version
-INVENTREE_API_VERSION = 35
+INVENTREE_API_VERSION = 36
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v36 -> 2022-04-03
+ - Adds ability to filter part list endpoint by unallocated_stock argument
+
v35 -> 2022-04-01 : https://github.com/inventree/InvenTree/pull/2797
- Adds stock allocation information to the Part API
- Adds calculated field for "unallocated_quantity"
diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py
index 3a2bb6eeb3..f7bb81520d 100644
--- a/InvenTree/part/api.py
+++ b/InvenTree/part/api.py
@@ -798,6 +798,20 @@ class PartFilter(rest_filters.FilterSet):
return queryset
+ # unallocated_stock filter
+ unallocated_stock = rest_filters.BooleanFilter(label='Unallocated stock', method='filter_unallocated_stock')
+
+ def filter_unallocated_stock(self, queryset, name, value):
+
+ value = str2bool(value)
+
+ if value:
+ queryset = queryset.filter(Q(unallocated_stock__gt=0))
+ else:
+ queryset = queryset.filter(Q(unallocated_stock__lte=0))
+
+ return queryset
+
is_template = rest_filters.BooleanFilter()
assembly = rest_filters.BooleanFilter()
diff --git a/InvenTree/templates/js/translated/part.js b/InvenTree/templates/js/translated/part.js
index b0283a1b35..1c66c32b6e 100644
--- a/InvenTree/templates/js/translated/part.js
+++ b/InvenTree/templates/js/translated/part.js
@@ -494,10 +494,40 @@ function duplicateBom(part_id, options={}) {
function partStockLabel(part, options={}) {
if (part.in_stock) {
- return `{% trans "Stock" %}: ${part.in_stock}`;
+ // There IS stock available for this part
+
+ // Is stock "low" (below the 'minimum_stock' quantity)?
+ if (part.minimum_stock && part.minimum_stock > part.in_stock) {
+ return `{% trans "Low stock" %}: ${part.in_stock}${part.units}`;
+ } else if (part.unallocated_stock == 0) {
+ if (part.ordering) {
+ // There is no available stock, but stock is on order
+ return `{% trans "On Order" %}: ${part.ordering}${part.units}`;
+ } else if (part.building) {
+ // There is no available stock, but stock is being built
+ return `{% trans "Building" %}: ${part.building}${part.units}`;
+ } else {
+ // There is no available stock
+ return `{% trans "Available" %}: 0/${part.in_stock}${part.units}`;
+ }
+ } else {
+ return `{% trans "Available" %}: ${part.unallocated_stock}/${part.in_stock}${part.units}`;
+ }
} else {
- return `{% trans "No Stock" %}`;
+ // There IS NO stock available for this part
+
+ if (part.ordering) {
+ // There is no stock, but stock is on order
+ return `{% trans "On Order" %}: ${part.ordering}${part.units}`;
+ } else if (part.building) {
+ // There is no stock, but stock is being built
+ return `{% trans "Building" %}: ${part.building}${part.units}`;
+ } else {
+ // There is no stock
+ return `{% trans "No Stock" %}`;
+ }
}
+
}
@@ -1160,12 +1190,14 @@ function partGridTile(part) {
if (!part.in_stock) {
stock = `{% trans "No Stock" %}`;
+ } else if (!part.unallocated_stock) {
+ stock = `{% trans "Not available" %}`;
}
rows += `
{% trans "Stock" %} | ${stock} |
`;
- if (part.on_order) {
- rows += `{$ trans "On Order" %} | ${part.on_order} |
`;
+ if (part.ordering) {
+ rows += `{% trans "On Order" %} | ${part.ordering} |
`;
}
if (part.building) {
@@ -1322,31 +1354,47 @@ function loadPartTable(table, url, options={}) {
columns.push(col);
col = {
- field: 'in_stock',
- title: '{% trans "Stock" %}',
+ field: 'unallocated_stock',
+ title: '{% trans "Available" %}',
searchable: false,
formatter: function(value, row) {
var link = '?display=part-stock';
- if (value) {
+ if (row.in_stock) {
// There IS stock available for this part
// Is stock "low" (below the 'minimum_stock' quantity)?
- if (row.minimum_stock && row.minimum_stock > value) {
+ if (row.minimum_stock && row.minimum_stock > row.in_stock) {
value += `{% trans "Low stock" %}`;
+ } else if (value == 0) {
+ if (row.ordering) {
+ // There is no available stock, but stock is on order
+ value = `0{% trans "On Order" %}: ${row.ordering}`;
+ link = '?display=purchase-orders';
+ } else if (row.building) {
+ // There is no available stock, but stock is being built
+ value = `0{% trans "Building" %}: ${row.building}`;
+ link = '?display=build-orders';
+ } else {
+ // There is no available stock
+ value = `0{% trans "Not available" %}`;
+ }
}
-
- } else if (row.on_order) {
- // There is no stock available, but stock is on order
- value = `0{% trans "On Order" %}: ${row.on_order}`;
- link = '?display=purchase-orders';
- } else if (row.building) {
- // There is no stock available, but stock is being built
- value = `0{% trans "Building" %}: ${row.building}`;
- link = '?display=build-orders';
} else {
- // There is no stock available
- value = `0{% trans "No Stock" %}`;
+ // There IS NO stock available for this part
+
+ if (row.ordering) {
+ // There is no stock, but stock is on order
+ value = `0{% trans "On Order" %}: ${row.ordering}`;
+ link = '?display=purchase-orders';
+ } else if (row.building) {
+ // There is no stock, but stock is being built
+ value = `0{% trans "Building" %}: ${row.building}`;
+ link = '?display=build-orders';
+ } else {
+ // There is no stock
+ value = `0{% trans "No Stock" %}`;
+ }
}
return renderLink(value, `/part/${row.pk}/${link}`);
diff --git a/InvenTree/templates/js/translated/table_filters.js b/InvenTree/templates/js/translated/table_filters.js
index 81d43d2c3f..6212568950 100644
--- a/InvenTree/templates/js/translated/table_filters.js
+++ b/InvenTree/templates/js/translated/table_filters.js
@@ -427,12 +427,16 @@ function getAvailableTableFilters(tableKey) {
},
has_stock: {
type: 'bool',
- title: '{% trans "Stock available" %}',
+ title: '{% trans "In stock" %}',
},
low_stock: {
type: 'bool',
title: '{% trans "Low stock" %}',
},
+ unallocated_stock: {
+ type: 'bool',
+ title: '{% trans "Available stock" %}',
+ },
assembly: {
type: 'bool',
title: '{% trans "Assembly" %}',