From e3231bbedb780ff4a2be86662ec2fbce935701be Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Nov 2020 20:58:18 +1100 Subject: [PATCH 01/10] Hide "pricing" information in the BOM table --- InvenTree/part/serializers.py | 4 ++-- InvenTree/templates/js/bom.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/serializers.py b/InvenTree/part/serializers.py index 7bec904ce0..0eebe6617d 100644 --- a/InvenTree/part/serializers.py +++ b/InvenTree/part/serializers.py @@ -336,7 +336,7 @@ class PartStarSerializer(InvenTreeModelSerializer): class BomItemSerializer(InvenTreeModelSerializer): """ Serializer for BomItem object """ - price_range = serializers.CharField(read_only=True) + # price_range = serializers.CharField(read_only=True) quantity = serializers.FloatField() @@ -387,7 +387,7 @@ class BomItemSerializer(InvenTreeModelSerializer): 'sub_part_detail', 'quantity', 'reference', - 'price_range', + # 'price_range', 'optional', 'overage', 'note', diff --git a/InvenTree/templates/js/bom.js b/InvenTree/templates/js/bom.js index c55429faba..9bbf8127e8 100644 --- a/InvenTree/templates/js/bom.js +++ b/InvenTree/templates/js/bom.js @@ -228,6 +228,12 @@ function loadBomTable(table, options) { } }); + /* + + // TODO - Re-introduce the pricing column at a later stage, + // once the pricing has been "fixed" + // O.W. 2020-11-24 + cols.push( { field: 'price_range', @@ -241,6 +247,7 @@ function loadBomTable(table, options) { } } }); + */ } // Part notes From 28333c1a217c71aa02eac9e92bc37e8a9ffaa370 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Nov 2020 21:18:00 +1100 Subject: [PATCH 02/10] Add a simple "shell" task --- tasks.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tasks.py b/tasks.py index 30641c7c81..69c53b83d4 100644 --- a/tasks.py +++ b/tasks.py @@ -102,6 +102,16 @@ def install(c): print("Config file 'config.yaml' does not exist - copying from template.") copyfile(CONFIG_TEMPLATE_FILE, CONFIG_FILE) + +@task +def shell(c): + """ + Open a python shell with access to the InvenTree database models. + """ + + manage(c, 'shell', pty=True) + + @task def superuser(c): """ From 083d7671d0ab5e7f0b8343d8220666dcaa4caa2f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 24 Nov 2020 21:19:19 +1100 Subject: [PATCH 03/10] Bug fix for BOM table If the BOM for part included a BomItem with the same PK as the top-level part, the bootstrap-tree-grid library borked Probably for good reason, too! So we now ensure that the top-level key is unique --- InvenTree/templates/js/bom.js | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/InvenTree/templates/js/bom.js b/InvenTree/templates/js/bom.js index 9bbf8127e8..299045cfa5 100644 --- a/InvenTree/templates/js/bom.js +++ b/InvenTree/templates/js/bom.js @@ -140,6 +140,12 @@ function loadBomTable(table, options) { }); } + // Set the parent ID of the multi-level table. + // We prepend this with the literal string value 'top-level-', + // because otherwise the unfortunate situation where BomItem.pk == BomItem.part.pk + // AND THIS BREAKS EVERYTHING + var parent_id = `top-level-${options.parent_id}`; + // Part column cols.push( { @@ -197,11 +203,11 @@ function loadBomTable(table, options) { text = parseFloat(text); if (row.optional) { - text += " ({% trans "Optional" %})"; + text += ' ({% trans "Optional" %})'; } if (row.overage) { - text += " (+" + row.overage + ") "; + text += ` (${row.overage}) `; } return text; @@ -233,7 +239,7 @@ function loadBomTable(table, options) { // TODO - Re-introduce the pricing column at a later stage, // once the pricing has been "fixed" // O.W. 2020-11-24 - + cols.push( { field: 'price_range', @@ -334,9 +340,8 @@ function loadBomTable(table, options) { table.inventreeTable({ treeEnable: !options.editable, - rootParentId: options.parent_id, + rootParentId: parent_id, idField: 'pk', - //uniqueId: 'pk', parentIdField: 'parentId', treeShowField: 'sub_part', showColumns: true, @@ -345,12 +350,18 @@ function loadBomTable(table, options) { search: true, rowStyle: function(row, index) { if (row.validated) { - return {classes: 'rowvalid'}; + return { + classes: 'rowvalid' + }; } else { - return {classes: 'rowinvalid'}; + return { + classes: 'rowinvalid' + }; } }, - formatNoMatches: function() { return '{% trans "No BOM items found" %}'; }, + formatNoMatches: function() { + return '{% trans "No BOM items found" %}'; + }, clickToSelect: true, queryParams: filters, original: params, @@ -383,7 +394,7 @@ function loadBomTable(table, options) { } // Set the parent ID of the top-level rows - row.parentId = options.parent_id; + row.parentId = parent_id; table.bootstrapTable('updateRow', idx, row, true); From 56f05e260455df01050a9175ea1fe9eae855081f Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 10:42:01 +1100 Subject: [PATCH 04/10] Fixes for 'non field' errors in forms - Fixes issue where non-model fields would not show error text --- InvenTree/InvenTree/forms.py | 6 +----- InvenTree/templates/modal_form.html | 16 +++------------- 2 files changed, 4 insertions(+), 18 deletions(-) diff --git a/InvenTree/InvenTree/forms.py b/InvenTree/InvenTree/forms.py index 4be8a28acb..56a1a116da 100644 --- a/InvenTree/InvenTree/forms.py +++ b/InvenTree/InvenTree/forms.py @@ -28,6 +28,7 @@ class HelperForm(forms.ModelForm): self.helper = FormHelper() self.helper.form_tag = False + self.helper.form_show_errors = True """ Create a default 'layout' for this form. @@ -43,11 +44,6 @@ class HelperForm(forms.ModelForm): valid = super(HelperForm, self).is_valid() - # Check for errors from model validation - # If none, disable crispy form errors - if not self.errors: - self.helper.form_show_errors = False - return valid def rebuild_layout(self): diff --git a/InvenTree/templates/modal_form.html b/InvenTree/templates/modal_form.html index 0a5ae59916..94bf032579 100644 --- a/InvenTree/templates/modal_form.html +++ b/InvenTree/templates/modal_form.html @@ -1,3 +1,5 @@ +{% load i18n %} +
{% if form.pre_form_info %} {% endif %} -{% block non_field_error %} -{% if form.non_field_errors %} - -{% endif %} -{% endblock %}
{% block pre_form_content %} @@ -41,4 +31,4 @@ {% endblock %} {% block post_form_content %} -{% endblock %} \ No newline at end of file +{% endblock %} From 5f8f0232a9d556b6c6622ce6efa9488efd7eed68 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 11:17:55 +1100 Subject: [PATCH 05/10] Add extra context to SupplierPartCreate form --- .../company/supplier_part_create.html | 17 ++++++++++++++ InvenTree/company/views.py | 23 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 InvenTree/company/templates/company/supplier_part_create.html diff --git a/InvenTree/company/templates/company/supplier_part_create.html b/InvenTree/company/templates/company/supplier_part_create.html new file mode 100644 index 0000000000..21c23f9075 --- /dev/null +++ b/InvenTree/company/templates/company/supplier_part_create.html @@ -0,0 +1,17 @@ +{% extends "modal_form.html" %} + +{% load i18n %} + +{% block pre_form_content %} +{{ block.super }} + +{% if part %} +
+ {% include "hover_image.html" with image=part.image %} + {{ part.full_name}} +
+ {{ part.description }} +
+{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index 2720f4ccac..e863fa1d72 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -291,7 +291,7 @@ class SupplierPartCreate(AjaxCreateView): model = SupplierPart form_class = EditSupplierPartForm - ajax_template_name = 'modal_form.html' + ajax_template_name = 'company/supplier_part_create.html' ajax_form_title = _('Create new Supplier Part') context_object_name = 'part' role_required = 'purchase_order.add' @@ -304,6 +304,27 @@ class SupplierPartCreate(AjaxCreateView): # TODO - What validation steps can be performed on the single_pricing field? pass + def get_context_data(self): + """ + Supply context data to the form + """ + + ctx = super().get_context_data() + + # Add 'part' object + form = self.get_form() + + part = form['part'].value() + + try: + part = Part.objects.get(pk=part) + except (ValueError, Part.DoesNotExist): + part = None + + ctx['part'] = part + + return ctx + def save(self, form): """ If single_pricing is defined, add a price break for quantity=1 From 607cc90ce03cd5b39d3cb8c96fd45ac7b7d85dd6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 11:17:55 +1100 Subject: [PATCH 06/10] Add extra context to SupplierPartCreate form (cherry picked from commit 5f8f0232a9d556b6c6622ce6efa9488efd7eed68) --- .../company/supplier_part_create.html | 17 ++++++++++++++ InvenTree/company/views.py | 23 ++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 InvenTree/company/templates/company/supplier_part_create.html diff --git a/InvenTree/company/templates/company/supplier_part_create.html b/InvenTree/company/templates/company/supplier_part_create.html new file mode 100644 index 0000000000..21c23f9075 --- /dev/null +++ b/InvenTree/company/templates/company/supplier_part_create.html @@ -0,0 +1,17 @@ +{% extends "modal_form.html" %} + +{% load i18n %} + +{% block pre_form_content %} +{{ block.super }} + +{% if part %} +
+ {% include "hover_image.html" with image=part.image %} + {{ part.full_name}} +
+ {{ part.description }} +
+{% endif %} + +{% endblock %} \ No newline at end of file diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index 2720f4ccac..e863fa1d72 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -291,7 +291,7 @@ class SupplierPartCreate(AjaxCreateView): model = SupplierPart form_class = EditSupplierPartForm - ajax_template_name = 'modal_form.html' + ajax_template_name = 'company/supplier_part_create.html' ajax_form_title = _('Create new Supplier Part') context_object_name = 'part' role_required = 'purchase_order.add' @@ -304,6 +304,27 @@ class SupplierPartCreate(AjaxCreateView): # TODO - What validation steps can be performed on the single_pricing field? pass + def get_context_data(self): + """ + Supply context data to the form + """ + + ctx = super().get_context_data() + + # Add 'part' object + form = self.get_form() + + part = form['part'].value() + + try: + part = Part.objects.get(pk=part) + except (ValueError, Part.DoesNotExist): + part = None + + ctx['part'] = part + + return ctx + def save(self, form): """ If single_pricing is defined, add a price break for quantity=1 From 7068f7081120bb37d6dc0fd2a8d450c3e1ed7eb9 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 11:18:45 +1100 Subject: [PATCH 07/10] Fixes for 'order parts' form - Sometimes the part pk was not being retrieved properly --- .../order/order_wizard/select_parts.html | 8 ++++---- InvenTree/templates/js/order.js | 17 ++++++++++++----- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/InvenTree/order/templates/order/order_wizard/select_parts.html b/InvenTree/order/templates/order/order_wizard/select_parts.html index 9c7c90ac26..c93e26e363 100644 --- a/InvenTree/order/templates/order/order_wizard/select_parts.html +++ b/InvenTree/order/templates/order/order_wizard/select_parts.html @@ -11,11 +11,11 @@ {% if parts|length > 0 %} {% else %} {% endif %} @@ -39,8 +39,8 @@ {{ part.full_name }} {{ part.description }} - diff --git a/InvenTree/templates/js/order.js b/InvenTree/templates/js/order.js index 41a1b4c046..69d4f584d9 100644 --- a/InvenTree/templates/js/order.js +++ b/InvenTree/templates/js/order.js @@ -21,9 +21,16 @@ function newSupplierPartFromOrderWizard(e) { e = e || window.event; - var src = e.target || e.srcElement; + var src = e.srcElement || e.target; - var part = $(src).attr('part-id'); + var part = $(src).attr('part'); + + console.log('part: ' + part); + + if (!part) { + part = $(src).closest('button').attr('part'); + console.log('parent: ' + part); + } launchModalForm("/supplier-part/new/", { modal: '#modal-form-secondary', @@ -125,7 +132,7 @@ function loadPurchaseOrderTable(table, options) { name: 'purchaseorder', groupBy: false, original: options.params, - formatNoMatches: function() { return "{% trans "No purchase orders found" %}"; }, + formatNoMatches: function() { return '{% trans "No purchase orders found" %}'; }, columns: [ { field: 'pk', @@ -208,7 +215,7 @@ function loadSalesOrderTable(table, options) { name: 'salesorder', groupBy: false, original: options.params, - formatNoMatches: function() { return "{% trans "No sales orders found" %}"; }, + formatNoMatches: function() { return '{% trans "No sales orders found" %}'; }, columns: [ { field: 'pk', @@ -265,7 +272,7 @@ function loadSalesOrderTable(table, options) { { sortable: true, field: 'shipment_date', - title: "{% trans "Shipment Date" %}", + title: '{% trans "Shipment Date" %}', }, { sortable: true, From 6c68197e617d6a1a0d95a7ec96402d1140197917 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 11:18:58 +1100 Subject: [PATCH 08/10] Allow part ordering from build view --- InvenTree/templates/js/build.js | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/InvenTree/templates/js/build.js b/InvenTree/templates/js/build.js index ab6f0e4e0a..a3c7bd5186 100644 --- a/InvenTree/templates/js/build.js +++ b/InvenTree/templates/js/build.js @@ -243,6 +243,22 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { }); }); + // Callback for 'buy' button + $(table).find('.button-buy').click(function() { + var pk = $(this).attr('pk'); + + var idx = $(this).closest('tr').attr('data-index'); + var row = $(table).bootstrapTable('getData')[idx]; + + launchModalForm('{% url "order-parts" %}', { + data: { + parts: [ + pk, + ] + } + }); + }); + // Callback for 'build' button $(table).find('.button-build').click(function() { var pk = $(this).attr('pk'); @@ -563,7 +579,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) { } if (row.sub_part_detail.purchaseable) { - html += makeIconButton('fa-shopping-cart icon-blue', 'button-buy', row.sub_part, '{% trans "Order stock" %}', {disabled: true}); + html += makeIconButton('fa-shopping-cart icon-blue', 'button-buy', row.sub_part, '{% trans "Order stock" %}'); } html += makeIconButton('fa-sign-in-alt icon-green', 'button-add', row.sub_part, '{% trans "Allocate stock" %}'); From 50a88e482605fd539322bc6d3f971a160a12cde6 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 11:19:16 +1100 Subject: [PATCH 09/10] Revert "Add extra context to SupplierPartCreate form" This reverts commit 5f8f0232a9d556b6c6622ce6efa9488efd7eed68. --- .../company/supplier_part_create.html | 17 -------------- InvenTree/company/views.py | 23 +------------------ 2 files changed, 1 insertion(+), 39 deletions(-) delete mode 100644 InvenTree/company/templates/company/supplier_part_create.html diff --git a/InvenTree/company/templates/company/supplier_part_create.html b/InvenTree/company/templates/company/supplier_part_create.html deleted file mode 100644 index 21c23f9075..0000000000 --- a/InvenTree/company/templates/company/supplier_part_create.html +++ /dev/null @@ -1,17 +0,0 @@ -{% extends "modal_form.html" %} - -{% load i18n %} - -{% block pre_form_content %} -{{ block.super }} - -{% if part %} -
- {% include "hover_image.html" with image=part.image %} - {{ part.full_name}} -
- {{ part.description }} -
-{% endif %} - -{% endblock %} \ No newline at end of file diff --git a/InvenTree/company/views.py b/InvenTree/company/views.py index e863fa1d72..2720f4ccac 100644 --- a/InvenTree/company/views.py +++ b/InvenTree/company/views.py @@ -291,7 +291,7 @@ class SupplierPartCreate(AjaxCreateView): model = SupplierPart form_class = EditSupplierPartForm - ajax_template_name = 'company/supplier_part_create.html' + ajax_template_name = 'modal_form.html' ajax_form_title = _('Create new Supplier Part') context_object_name = 'part' role_required = 'purchase_order.add' @@ -304,27 +304,6 @@ class SupplierPartCreate(AjaxCreateView): # TODO - What validation steps can be performed on the single_pricing field? pass - def get_context_data(self): - """ - Supply context data to the form - """ - - ctx = super().get_context_data() - - # Add 'part' object - form = self.get_form() - - part = form['part'].value() - - try: - part = Part.objects.get(pk=part) - except (ValueError, Part.DoesNotExist): - part = None - - ctx['part'] = part - - return ctx - def save(self, form): """ If single_pricing is defined, add a price break for quantity=1 From ea2f5009c8386aceab69301a1b6deed07956ee73 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Fri, 27 Nov 2020 14:40:30 +1100 Subject: [PATCH 10/10] Vastly improved speed of BOM upload - Was calculating the stock levels for *every* part, for *every* drop down - Many many many calls were being made - Just remove stock count entirely from the drop-down menus --- InvenTree/part/templates/part/bom_upload/select_fields.html | 2 +- InvenTree/part/templates/part/bom_upload/select_parts.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/InvenTree/part/templates/part/bom_upload/select_fields.html b/InvenTree/part/templates/part/bom_upload/select_fields.html index bd0c222881..60f0efcfad 100644 --- a/InvenTree/part/templates/part/bom_upload/select_fields.html +++ b/InvenTree/part/templates/part/bom_upload/select_fields.html @@ -59,7 +59,7 @@ {% endfor %} {% if col.duplicate %} -

Duplicate column selection

+

{% trans "Duplicate column selection" %}

{% endif %} {% endfor %} diff --git a/InvenTree/part/templates/part/bom_upload/select_parts.html b/InvenTree/part/templates/part/bom_upload/select_parts.html index ede92c6c30..1ee5e8821b 100644 --- a/InvenTree/part/templates/part/bom_upload/select_parts.html +++ b/InvenTree/part/templates/part/bom_upload/select_parts.html @@ -63,7 +63,7 @@ {% for part in row.part_options %} {% endfor %}