From 476cc5f6619a4c5f53203b086a90488f0fbd0436 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 18 Aug 2020 11:47:27 +1000 Subject: [PATCH 1/4] BOM upload: improve generation of "import" template - Better field naming - Remove some fields which are not required here - Better description of what is going to happen --- InvenTree/part/admin.py | 61 ++++++++++++++++++- InvenTree/part/bom.py | 8 ++- .../part/bom_upload/upload_file.html | 11 +++- 3 files changed, 73 insertions(+), 7 deletions(-) diff --git a/InvenTree/part/admin.py b/InvenTree/part/admin.py index de1b7d1fae..2912d04a32 100644 --- a/InvenTree/part/admin.py +++ b/InvenTree/part/admin.py @@ -139,16 +139,71 @@ class BomItemResource(ModelResource): bom_id = Field(attribute='pk') + # ID of the parent part parent_part_id = Field(attribute='part', widget=widgets.ForeignKeyWidget(Part)) - parent_part_name = Field(attribute='part__full_name', readonly=True) + # IPN of the parent part + parent_part_ipn = Field(attribute='part__IPN', readonly=True) - sub_part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part)) + # Name of the parent part + parent_part_name = Field(attribute='part__name', readonly=True) - sub_part_name = Field(attribute='sub_part__full_name', readonly=True) + # ID of the sub-part + part_id = Field(attribute='sub_part', widget=widgets.ForeignKeyWidget(Part)) + # IPN of the sub-part + part_ipn = Field(attribute='sub_part__IPN', readonly=True) + + # Name of the sub-part + part_name = Field(attribute='sub_part__name', readonly=True) + + # Description of the sub-part + part_description = Field(attribute='sub_part__description', readonly=True) + + # Is the sub-part itself an assembly? sub_assembly = Field(attribute='sub_part__assembly', readonly=True) + def before_export(self, queryset, *args, **kwargs): + + self.is_importing = kwargs.get('importing', False) + + def get_fields(self, **kwargs): + """ + If we are exporting for the purposes of generating + a 'bom-import' template, there are some fields which + we are not interested in. + """ + + fields = super().get_fields(**kwargs) + + # If we are not generating an "import" template, + # just return the complete list of fields + if not self.is_importing: + return fields + + # Otherwise, remove some fields we are not interested in + + idx = 0 + + to_remove = [ + 'level', + 'bom_id', + 'parent_part_id', + 'parent_part_ipn', + 'parent_part_name', + 'part_description', + 'sub_assembly' + ] + + while idx < len(fields): + + if fields[idx].column_name.lower() in to_remove: + del fields[idx] + else: + idx += 1 + + return fields + class Meta: model = BomItem skip_unchanged = True diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 8702d9d867..3daeedcebb 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -30,8 +30,14 @@ def MakeBomTemplate(fmt): if not IsValidBOMFormat(fmt): fmt = 'csv' + # Create an "empty" queryset, essentially. + # This will then export just the row headers! query = BomItem.objects.filter(pk=None) - dataset = BomItemResource().export(queryset=query) + + dataset = BomItemResource().export( + queryset=query, + importing=True + ) data = dataset.export(fmt) diff --git a/InvenTree/part/templates/part/bom_upload/upload_file.html b/InvenTree/part/templates/part/bom_upload/upload_file.html index 37c5801b5c..d503268fb7 100644 --- a/InvenTree/part/templates/part/bom_upload/upload_file.html +++ b/InvenTree/part/templates/part/bom_upload/upload_file.html @@ -1,18 +1,23 @@ {% extends "part/part_base.html" %} {% load static %} +{% load i18n %} {% load inventree_extras %} {% block details %} {% include "part/tabs.html" with tab='bom' %} -

Upload Bill of Materials

+

{% trans "Upload Bill of Materials" %}


-

Step 1 - Select BOM File

+

{% trans "Step 1 - Select BOM File" %}

-

The BOM file must contain the required named columns as provided in the BOM Upload Template

+ {% trans "Requirements for BOM upload" %}: +
From 68fb599c733863c08eaea7dc4feffb3a4a97e097 Mon Sep 17 00:00:00 2001 From: Oliver Walters Date: Tue, 18 Aug 2020 14:01:01 +1000 Subject: [PATCH 2/4] Major improvements to BOM upload - Column headings now match the values provided in BOM import template - Add a new column for part selection, while displaying all imported data - Better code documentation - Improve data validation - Allow decimal quantity (not just integer!) - Better matching logic --- InvenTree/part/bom.py | 20 ++- .../part/bom_upload/select_fields.html | 13 +- .../part/bom_upload/select_parts.html | 50 +++--- InvenTree/part/views.py | 158 +++++++++++++----- 4 files changed, 157 insertions(+), 84 deletions(-) diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py index 3daeedcebb..8f1241de01 100644 --- a/InvenTree/part/bom.py +++ b/InvenTree/part/bom.py @@ -102,26 +102,23 @@ class BomUploadManager: # Fields which are absolutely necessary for valid upload REQUIRED_HEADERS = [ - 'Part', + 'Part_Name', 'Quantity' ] # Fields which would be helpful but are not required OPTIONAL_HEADERS = [ + 'Part_IPN', + 'Part_ID', 'Reference', - 'Notes', + 'Note', 'Overage', - 'Description', - 'Category', - 'Supplier', - 'Manufacturer', - 'MPN', - 'IPN', ] EDITABLE_HEADERS = [ 'Reference', - 'Notes' + 'Note', + 'Overage' ] HEADERS = REQUIRED_HEADERS + OPTIONAL_HEADERS @@ -171,6 +168,11 @@ class BomUploadManager: if h.lower() == header.lower(): return h + # Try for a case-insensitive match with space replacement + for h in self.HEADERS: + if h.lower() == header.lower().replace(' ', '_'): + return h + # Finally, look for a close match using fuzzy matching matches = [] diff --git a/InvenTree/part/templates/part/bom_upload/select_fields.html b/InvenTree/part/templates/part/bom_upload/select_fields.html index d49b1c12ce..bd0c222881 100644 --- a/InvenTree/part/templates/part/bom_upload/select_fields.html +++ b/InvenTree/part/templates/part/bom_upload/select_fields.html @@ -1,17 +1,18 @@ {% extends "part/part_base.html" %} {% load static %} +{% load i18n %} {% load inventree_extras %} {% block details %} {% include "part/tabs.html" with tab='bom' %} -

Upload Bill of Materials

+

{% trans "Upload Bill of Materials" %}

-

Step 2 - Select Fields

+

{% trans "Step 2 - Select Fields" %}


{% if missing_columns and missing_columns|length > 0 %}