diff --git a/InvenTree/part/api.py b/InvenTree/part/api.py index 00b5dfa7de..403934d3d9 100644 --- a/InvenTree/part/api.py +++ b/InvenTree/part/api.py @@ -42,7 +42,7 @@ from build.models import Build from . import serializers as part_serializers -from InvenTree.helpers import str2bool, isNull +from InvenTree.helpers import str2bool, isNull, increment from InvenTree.api import AttachmentMixin from InvenTree.status_codes import BuildStatus @@ -410,6 +410,33 @@ class PartThumbsUpdate(generics.RetrieveUpdateAPIView): ] +class PartSerialNumberDetail(generics.RetrieveAPIView): + """ + API endpoint for returning extra serial number information about a particular part + """ + + queryset = Part.objects.all() + + def retrieve(self, request, *args, **kwargs): + + part = self.get_object() + + # Calculate the "latest" serial number + latest = part.getLatestSerialNumber() + + data = { + 'latest': latest, + } + + if latest is not None: + next = increment(latest) + + if next != increment: + data['next'] = next + + return Response(data) + + class PartDetail(generics.RetrieveUpdateDestroyAPIView): """ API endpoint for detail view of a single Part object """ @@ -1532,7 +1559,14 @@ part_api_urls = [ url(r'^(?P<pk>\d+)/?', PartThumbsUpdate.as_view(), name='api-part-thumbs-update'), ])), - url(r'^(?P<pk>\d+)/', PartDetail.as_view(), name='api-part-detail'), + url(r'^(?P<pk>\d+)/', include([ + + # Endpoint for extra serial number information + url(r'^serial-numbers/', PartSerialNumberDetail.as_view(), name='api-part-serial-number-detail'), + + # Part detail endpoint + url(r'^.*$', PartDetail.as_view(), name='api-part-detail'), + ])), url(r'^.*$', PartList.as_view(), name='api-part-list'), ] diff --git a/InvenTree/stock/templates/stock/item_base.html b/InvenTree/stock/templates/stock/item_base.html index 0f8d81203a..111007cd71 100644 --- a/InvenTree/stock/templates/stock/item_base.html +++ b/InvenTree/stock/templates/stock/item_base.html @@ -433,6 +433,7 @@ $("#stock-serialize").click(function() { serializeStockItem({{ item.pk }}, { + part: {{ item.part.pk }}, reload: true, data: { quantity: {{ item.quantity }}, diff --git a/InvenTree/templates/js/translated/api.js b/InvenTree/templates/js/translated/api.js index 735ce0a676..d9c23f035f 100644 --- a/InvenTree/templates/js/translated/api.js +++ b/InvenTree/templates/js/translated/api.js @@ -54,6 +54,7 @@ function inventreeGet(url, filters={}, options={}) { data: filters, dataType: 'json', contentType: 'application/json', + async: (options.async == false) ? false : true, success: function(response) { if (options.success) { options.success(response); diff --git a/InvenTree/templates/js/translated/forms.js b/InvenTree/templates/js/translated/forms.js index fd1668cc77..5af85d382e 100644 --- a/InvenTree/templates/js/translated/forms.js +++ b/InvenTree/templates/js/translated/forms.js @@ -28,6 +28,7 @@ disableFormInput, enableFormInput, hideFormInput, + setFormInputPlaceholder, setFormGroupVisibility, showFormInput, */ @@ -1276,6 +1277,11 @@ function initializeGroups(fields, options) { } } +// Set the placeholder value for a field +function setFormInputPlaceholder(name, placeholder, options) { + $(options.modal).find(`#id_${name}`).attr('placeholder', placeholder); +} + // Clear a form input function clearFormInput(name, options) { updateFieldValue(name, null, {}, options); diff --git a/InvenTree/templates/js/translated/stock.js b/InvenTree/templates/js/translated/stock.js index c624278c93..5e92299f03 100644 --- a/InvenTree/templates/js/translated/stock.js +++ b/InvenTree/templates/js/translated/stock.js @@ -80,6 +80,20 @@ function serializeStockItem(pk, options={}) { notes: {}, }; + if (options.part) { + // Work out the next available serial number + inventreeGet(`/api/part/${options.part}/serial-numbers/`, {}, { + success: function(data) { + if (data.next) { + options.fields.serial_numbers.placeholder = `{% trans "Next available serial number" %}: ${data.next}`; + } else if (data.latest) { + options.fields.serial_numbers.placeholder = `{% trans "Latest serial number" %}: ${data.latest}`; + } + }, + async: false, + }); + } + constructForm(url, options); } @@ -144,10 +158,26 @@ function stockItemFields(options={}) { // If a "trackable" part is selected, enable serial number field if (data.trackable) { enableFormInput('serial_numbers', opts); - // showFormInput('serial_numbers', opts); + + // Request part serial number information from the server + inventreeGet(`/api/part/${data.pk}/serial-numbers/`, {}, { + success: function(data) { + var placeholder = ''; + if (data.next) { + placeholder = `{% trans "Next available serial number" %}: ${data.next}`; + } else if (data.latest) { + placeholder = `{% trans "Latest serial number" %}: ${data.latest}`; + } + + setFormInputPlaceholder('serial_numbers', placeholder, opts); + } + }); + } else { clearFormInput('serial_numbers', opts); disableFormInput('serial_numbers', opts); + + setFormInputPlaceholder('serial_numbers', '{% trans "This part cannot be serialized" %}', opts); } // Enable / disable fields based on purchaseable status