From ae4bfd07dac4b5e4f3a518ff1b21d794ebdb9040 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:31:08 +1000 Subject: [PATCH 1/9] StockItemCreate form now has better logic for auto-detecting the part --- InvenTree/stock/views.py | 117 ++++++++++++++++++++++----------------- 1 file changed, 67 insertions(+), 50 deletions(-) diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index ae6f593717..0f2f69345c 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -1036,6 +1036,37 @@ class StockItemCreate(AjaxCreateView): ajax_template_name = 'modal_form.html' ajax_form_title = _('Create new Stock Item') + def get_part(self, form=None): + """ + Attempt to get the "part" associted with this new stockitem. + + - May be passed to the form as a query parameter (e.g. ?part=) + - May be passed via the form field itself. + """ + + # Try to extract from the URL query + part_id = self.request.GET.get('part', None) + + if part_id: + try: + part = Part.objects.get(pk=part_id) + return part + except (Part.DoesNotExist, ValueError): + pass + + # Try to get from the form + if form: + try: + part_id = form['part'].value() + part = Part.objects.get(pk=part_id) + return part + except (Part.DoesNotExist, ValueError): + pass + + # Could not extract a part object + return None + + def get_form(self): """ Get form for StockItem creation. Overrides the default get_form() method to intelligently limit @@ -1044,53 +1075,46 @@ class StockItemCreate(AjaxCreateView): form = super().get_form() - part = None + part = self.get_part(form=form) - # If the user has selected a Part, limit choices for SupplierPart - if form['part'].value(): - part_id = form['part'].value() + if part is not None: + sn = part.getNextSerialNumber() + form.field_placeholder['serial_numbers'] = _('Next available serial number is') + ' ' + str(sn) - try: - part = Part.objects.get(id=part_id) - - sn = part.getNextSerialNumber() - form.field_placeholder['serial_numbers'] = _('Next available serial number is') + ' ' + str(sn) + print("Placeholder:", sn) - form.rebuild_layout() + form.rebuild_layout() - # Hide the 'part' field (as a valid part is selected) - form.fields['part'].widget = HiddenInput() + # Hide the 'part' field (as a valid part is selected) + form.fields['part'].widget = HiddenInput() - # trackable parts get special consideration - if part.trackable: - form.fields['delete_on_deplete'].widget = HiddenInput() - form.fields['delete_on_deplete'].initial = False - else: - form.fields.pop('serial_numbers') + # trackable parts get special consideration + if part.trackable: + form.fields['delete_on_deplete'].widget = HiddenInput() + form.fields['delete_on_deplete'].initial = False + else: + form.fields.pop('serial_numbers') - # If the part is NOT purchaseable, hide the supplier_part field - if not part.purchaseable: - form.fields['supplier_part'].widget = HiddenInput() - else: - # Pre-select the allowable SupplierPart options - parts = form.fields['supplier_part'].queryset - parts = parts.filter(part=part.id) + # If the part is NOT purchaseable, hide the supplier_part field + if not part.purchaseable: + form.fields['supplier_part'].widget = HiddenInput() + else: + # Pre-select the allowable SupplierPart options + parts = form.fields['supplier_part'].queryset + parts = parts.filter(part=part.id) - form.fields['supplier_part'].queryset = parts + form.fields['supplier_part'].queryset = parts - # If there is one (and only one) supplier part available, pre-select it - all_parts = parts.all() + # If there is one (and only one) supplier part available, pre-select it + all_parts = parts.all() - if len(all_parts) == 1: + if len(all_parts) == 1: - # TODO - This does NOT work for some reason? Ref build.views.BuildItemCreate - form.fields['supplier_part'].initial = all_parts[0].id - - except Part.DoesNotExist: - pass + # TODO - This does NOT work for some reason? Ref build.views.BuildItemCreate + form.fields['supplier_part'].initial = all_parts[0].id # Otherwise if the user has selected a SupplierPart, we know what Part they meant! - elif form['supplier_part'].value() is not None: + if form['supplier_part'].value() is not None: pass return form @@ -1113,27 +1137,20 @@ class StockItemCreate(AjaxCreateView): else: initials = super(StockItemCreate, self).get_initial().copy() - part_id = self.request.GET.get('part', None) + part = self.get_part() + loc_id = self.request.GET.get('location', None) sup_part_id = self.request.GET.get('supplier_part', None) - part = None location = None supplier_part = None - # Part field has been specified - if part_id: - try: - part = Part.objects.get(pk=part_id) - - # Check that the supplied part is 'valid' - if not part.is_template and part.active and not part.virtual: - initials['part'] = part - initials['location'] = part.get_default_location() - initials['supplier_part'] = part.default_supplier - - except (ValueError, Part.DoesNotExist): - pass + if part is not None: + # Check that the supplied part is 'valid' + if not part.is_template and part.active and not part.virtual: + initials['part'] = part + initials['location'] = part.get_default_location() + initials['supplier_part'] = part.default_supplier # SupplierPart field has been specified # It must match the Part, if that has been supplied From 27ca84fd2afc4891e887707cd23d99b3383458e2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:34:11 +1000 Subject: [PATCH 2/9] Part view shows next available serial number --- InvenTree/part/templates/part/detail.html | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/InvenTree/part/templates/part/detail.html b/InvenTree/part/templates/part/detail.html index e9e09959fb..1e7d36e127 100644 --- a/InvenTree/part/templates/part/detail.html +++ b/InvenTree/part/templates/part/detail.html @@ -33,6 +33,13 @@ {{ part.revision }} {% endif %} + {% if part.trackable %} + + + {% trans "Next Serial Number" %} + {{ part.getNextSerialNumber }} + + {% endif %} {% trans "Description" %} From 47a4ab2ed88bb2072296ddcae2f34f8eadb24f7e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:38:17 +1000 Subject: [PATCH 3/9] Template changes --- InvenTree/part/templates/part/part_base.html | 7 +++++-- InvenTree/part/templates/part/tabs.html | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/part_base.html b/InvenTree/part/templates/part/part_base.html index 0bb3687fc8..d9f80edf44 100644 --- a/InvenTree/part/templates/part/part_base.html +++ b/InvenTree/part/templates/part/part_base.html @@ -6,11 +6,14 @@ {% block content %} +{% if part.virtual %} +
+ {% trans "This part is a virtual part" %} +
+{% endif %} {% if part.is_template %}
{% trans "This part is a template part." %} -
- {% trans "It is not a real part, but real parts can be based on this template." %}
{% endif %} {% if part.variant_of %} diff --git a/InvenTree/part/templates/part/tabs.html b/InvenTree/part/templates/part/tabs.html index 28aa2cbb4d..ecec2796d6 100644 --- a/InvenTree/part/templates/part/tabs.html +++ b/InvenTree/part/templates/part/tabs.html @@ -13,9 +13,11 @@ {% trans "Variants" %} {{ part.variants.count }} {% endif %} + {% if not part.virtual %} {% trans "Stock" %} {% decimal part.total_stock %} + {% endif %} {% if part.component or part.used_in_count > 0 %} {% trans "Allocated" %} {% decimal part.allocation_count %} From 046a00026c1c182dfa829fa73ddc0b21beaff393 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:42:30 +1000 Subject: [PATCH 4/9] remove debug print --- InvenTree/stock/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/InvenTree/stock/views.py b/InvenTree/stock/views.py index 0f2f69345c..8d3e5ebbf1 100644 --- a/InvenTree/stock/views.py +++ b/InvenTree/stock/views.py @@ -1066,7 +1066,6 @@ class StockItemCreate(AjaxCreateView): # Could not extract a part object return None - def get_form(self): """ Get form for StockItem creation. Overrides the default get_form() method to intelligently limit @@ -1081,8 +1080,6 @@ class StockItemCreate(AjaxCreateView): sn = part.getNextSerialNumber() form.field_placeholder['serial_numbers'] = _('Next available serial number is') + ' ' + str(sn) - print("Placeholder:", sn) - form.rebuild_layout() # Hide the 'part' field (as a valid part is selected) From 4cbf2099fafeba52ba2cf8fd0c2b6fec5fa8a298 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:49:48 +1000 Subject: [PATCH 5/9] Add stock item filtering by serial number range --- InvenTree/stock/api.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/InvenTree/stock/api.py b/InvenTree/stock/api.py index 516edc5040..23d836b1e1 100644 --- a/InvenTree/stock/api.py +++ b/InvenTree/stock/api.py @@ -487,7 +487,20 @@ class StockList(generics.ListCreateAPIView): if serial_number is not None: queryset = queryset.filter(serial=serial_number) + + # Filter by range of serial numbers? + serial_number_gte = params.get('serial_gte', None) + serial_number_lte = params.get('serial_lte', None) + + if serial_number_gte is not None or serial_number_lte is not None: + queryset = queryset.exclude(serial=None) + + if serial_number_gte is not None: + queryset = queryset.filter(serial__gte=serial_number_gte) + if serial_number_lte is not None: + queryset = queryset.filter(serial__lte=serial_number_lte) + in_stock = params.get('in_stock', None) if in_stock is not None: From 669a76c9219698d3b58dc45307ced3f9642996d2 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:54:23 +1000 Subject: [PATCH 6/9] Add serial number range filtering options for the stock table --- InvenTree/templates/js/table_filters.html | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/InvenTree/templates/js/table_filters.html b/InvenTree/templates/js/table_filters.html index 84b517e8e7..298a034517 100644 --- a/InvenTree/templates/js/table_filters.html +++ b/InvenTree/templates/js/table_filters.html @@ -34,6 +34,14 @@ function getAvailableTableFilters(tableKey) { title: '{% trans "Is allocated" %}', description: '{% trans "Item has been alloacted" %}', }, + serial_gte: { + title: "{% trans "Serial number GTE" %}", + description: "{% trans "Serial number greater than or equal to" %}" + }, + serial_lte: { + title: "{% trans "Serial number LTE" %}", + description: "{% trans "Serial number less than or equal to" %}", + }, }; } From e63622341fc8167c8af328db776666c3f81f296f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 11:57:35 +1000 Subject: [PATCH 7/9] Add description field to table filters --- .../InvenTree/static/script/inventree/filters.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/InvenTree/InvenTree/static/script/inventree/filters.js b/InvenTree/InvenTree/static/script/inventree/filters.js index 8c9ebbec6d..3209ba3beb 100644 --- a/InvenTree/InvenTree/static/script/inventree/filters.js +++ b/InvenTree/InvenTree/static/script/inventree/filters.js @@ -272,8 +272,9 @@ function setupFilterList(tableKey, table, target) { for (var key in filters) { var value = getFilterOptionValue(tableKey, key, filters[key]); var title = getFilterTitle(tableKey, key); + var description = getFilterDescription(tableKey, key); - element.append(`
${title} = ${value}x
`); + element.append(`
${title} = ${value}x
`); } // Add a callback for adding a new filter @@ -362,6 +363,15 @@ function getFilterTitle(tableKey, filterKey) { } +/** + * Return the pretty description for the given table and filter selection + */ +function getFilterDescription(tableKey, filterKey) { + var settings = getFilterSettings(tableKey, filterKey); + + return settings.title; +} + /* * Return a description for the given table and filter selection. */ From c92bb78ae845b9121cd7aad8b009c876edf4ff8e Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 12:06:38 +1000 Subject: [PATCH 8/9] Stock table display fixes --- InvenTree/templates/js/stock.html | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/InvenTree/templates/js/stock.html b/InvenTree/templates/js/stock.html index fd2dd61746..c0737dc250 100644 --- a/InvenTree/templates/js/stock.html +++ b/InvenTree/templates/js/stock.html @@ -337,12 +337,21 @@ function loadStockTable(table, options) { } else { return '-'; } - } else if (field == 'location__path') { + } else if (field == 'location_detail.pathstring') { /* Determine how many locations */ var locations = []; data.forEach(function(item) { - var loc = item.location; + + var loc = null; + + if (item.location_detail) { + loc = item.location_detail.pathstring; + } else { + loc = "{% trans "Undefined location" %}"; + } + + console.log("Location: " + loc); if (!locations.includes(loc)) { locations.push(loc); @@ -353,7 +362,11 @@ function loadStockTable(table, options) { return "In " + locations.length + " locations"; } else { // A single location! - return renderLink(row.location__path, '/stock/location/' + row.location + '/') + if (row.location_detail) { + return renderLink(row.location_detail.pathstring, `/stock/location/${row.location}/`); + } else { + return "{% trans "Undefined location" %}"; + } } } else if (field == 'notes') { var notes = []; From c0f1966a2c607a1720917709a2f3249836e5e601 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 26 May 2020 12:08:00 +1000 Subject: [PATCH 9/9] Decimal filter on StockItem adjustment form --- InvenTree/stock/templates/stock/stock_adjust.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/InvenTree/stock/templates/stock/stock_adjust.html b/InvenTree/stock/templates/stock/stock_adjust.html index 566640544c..a72407f735 100644 --- a/InvenTree/stock/templates/stock/stock_adjust.html +++ b/InvenTree/stock/templates/stock/stock_adjust.html @@ -32,7 +32,7 @@ + value='{% decimal item.new_quantity %}' type='number' name='stock-id-{{ item.id }}' id='stock-id-{{ item.id }}'/> {% if item.error %}
{{ item.error }} {% endif %}