diff --git a/InvenTree/InvenTree/static/css/inventree.css b/InvenTree/InvenTree/static/css/inventree.css
index cd9ce410de..a61696f547 100644
--- a/InvenTree/InvenTree/static/css/inventree.css
+++ b/InvenTree/InvenTree/static/css/inventree.css
@@ -781,6 +781,7 @@ input[type="submit"] {
.btn-small {
padding: 3px;
padding-left: 5px;
+ padding-right: 5px;
}
.btn-remove {
diff --git a/InvenTree/InvenTree/version.py b/InvenTree/InvenTree/version.py
index e0e64f5525..bbf0174453 100644
--- a/InvenTree/InvenTree/version.py
+++ b/InvenTree/InvenTree/version.py
@@ -12,11 +12,15 @@ import common.models
INVENTREE_SW_VERSION = "0.6.0 dev"
# InvenTree API version
-INVENTREE_API_VERSION = 18
+INVENTREE_API_VERSION = 19
"""
Increment this API version number whenever there is a significant change to the API that any clients need to know about
+v19 -> 2021-12-02
+ - Adds the ability to filter the StockItem API by "part_tree"
+ - Returns only stock items which match a particular part.tree_id field
+
v18 -> 2021-11-15
- Adds the ability to filter BomItem API by "uses" field
- This returns a list of all BomItems which "use" the specified part
diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py
index 403934d3d9..dbc1140214 100644
--- a/InvenTree/part/api.py
+++ b/InvenTree/part/api.py
@@ -1075,6 +1075,7 @@ class PartList(generics.ListCreateAPIView):
'revision',
'keywords',
'category__name',
+ 'manufacturer_parts__MPN',
]
diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html
index 994eefe94e..d2505a57f7 100644
--- a/InvenTree/part/templates/part/part_base.html
+++ b/InvenTree/part/templates/part/part_base.html
@@ -322,7 +322,14 @@
|
{% trans "Latest Serial Number" %} |
- {{ part.getLatestSerialNumber }}{% include "clip.html"%} |
+
+ {{ part.getLatestSerialNumber }}
+
+ |
{% endif %}
{% if part.default_location %}
@@ -577,4 +584,8 @@
$('#collapse-part-details').collapse('show');
}
+ $('#serial-number-search').click(function() {
+ findStockItemBySerialNumber({{ part.pk }});
+ });
+
{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py
index fcfa58d01a..8385041209 100644
--- a/InvenTree/stock/api.py
+++ b/InvenTree/stock/api.py
@@ -313,7 +313,7 @@ class StockFilter(rest_filters.FilterSet):
# Serial number filtering
serial_gte = rest_filters.NumberFilter(label='Serial number GTE', field_name='serial', lookup_expr='gte')
serial_lte = rest_filters.NumberFilter(label='Serial number LTE', field_name='serial', lookup_expr='lte')
- serial = rest_filters.NumberFilter(label='Serial number', field_name='serial', lookup_expr='exact')
+ serial = rest_filters.CharFilter(label='Serial number', field_name='serial', lookup_expr='exact')
serialized = rest_filters.BooleanFilter(label='Has serial number', method='filter_serialized')
@@ -703,6 +703,18 @@ class StockList(generics.ListCreateAPIView):
except (ValueError, StockItem.DoesNotExist):
pass
+ # Filter by "part tree" - only allow parts within a given variant tree
+ part_tree = params.get('part_tree', None)
+
+ if part_tree is not None:
+ try:
+ part = Part.objects.get(pk=part_tree)
+
+ if part.tree_id is not None:
+ queryset = queryset.filter(part__tree_id=part.tree_id)
+ except:
+ pass
+
# Filter by 'allocated' parts?
allocated = params.get('allocated', None)
diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html
index 5f22076d9a..0d5ab272d6 100644
--- a/InvenTree/stock/templates/stock/item_base.html
+++ b/InvenTree/stock/templates/stock/item_base.html
@@ -148,17 +148,24 @@
|
{% trans "Serial Number" %} |
- {% if previous %}
-
- {{ previous.serial }} ‹
-
- {% endif %}
- {{ item.serial }}
- {% if next %}
-
- › {{ next.serial }}
-
- {% endif %}
+ {{ item.serial }}
+
|
{% else %}
@@ -592,4 +599,8 @@ $("#stock-return-from-customer").click(function() {
{% endif %}
+$('#serial-number-search').click(function() {
+ findStockItemBySerialNumber({{ item.part.pk }});
+});
+
{% endblock %}
diff --git a/InvenTree/templates/base.html b/InvenTree/templates/base.html
index 5bce256647..c253dfe8ef 100644
--- a/InvenTree/templates/base.html
+++ b/InvenTree/templates/base.html
@@ -181,10 +181,10 @@
-
-
-
-
+
+
+
+
{% block js_load %}
{% endblock %}
diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js
index 5e92299f03..02ea3c2c3b 100644
--- a/InvenTree/templates/js/translated/stock.js
+++ b/InvenTree/templates/js/translated/stock.js
@@ -44,6 +44,7 @@
editStockItem,
editStockLocation,
exportStock,
+ findStockItemBySerialNumber,
loadInstalledInTable,
loadStockLocationTable,
loadStockTable,
@@ -394,6 +395,87 @@ function createNewStockItem(options={}) {
constructForm(url, options);
}
+/*
+ * Launch a modal form to find a particular stock item by serial number.
+ * Arguments:
+ * - part: ID (PK) of the part in question
+ */
+
+function findStockItemBySerialNumber(part_id) {
+
+ constructFormBody({}, {
+ title: '{% trans "Find Serial Number" %}',
+ fields: {
+ serial: {
+ label: '{% trans "Serial Number" %}',
+ help_text: '{% trans "Enter serial number" %}',
+ placeholder: '{% trans "Enter serial number" %}',
+ required: true,
+ type: 'string',
+ value: '',
+ }
+ },
+ onSubmit: function(fields, opts) {
+
+ var serial = getFormFieldValue('serial', fields['serial'], opts);
+
+ serial = serial.toString().trim();
+
+ if (!serial) {
+ handleFormErrors(
+ {
+ 'serial': [
+ '{% trans "Enter a serial number" %}',
+ ]
+ }, fields, opts
+ );
+ return;
+ }
+
+ inventreeGet(
+ '{% url "api-stock-list" %}',
+ {
+ part_tree: part_id,
+ serial: serial,
+ },
+ {
+ success: function(response) {
+ if (response.length == 0) {
+ // No results!
+ handleFormErrors(
+ {
+ 'serial': [
+ '{% trans "No matching serial number" %}',
+ ]
+ }, fields, opts
+ );
+ } else if (response.length > 1) {
+ // Too many results!
+ handleFormErrors(
+ {
+ 'serial': [
+ '{% trans "More than one matching result found" %}',
+ ]
+ }, fields, opts
+ );
+ } else {
+ $(opts.modal).modal('hide');
+
+ // Redirect
+ var pk = response[0].pk;
+ location.href = `/stock/item/${pk}/`;
+ }
+ },
+ error: function(xhr) {
+ showApiError(xhr, opts.url);
+ $(opts.modal).modal('hide');
+ }
+ }
+ );
+ }
+ });
+}
+
/* Stock API functions
* Requires api.js to be loaded first