diff --git a/InvenTree/part/bom.py b/InvenTree/part/bom.py
index 6841de2146..e2d870805a 100644
--- a/InvenTree/part/bom.py
+++ b/InvenTree/part/bom.py
@@ -59,15 +59,8 @@ class BomUploadManager:
'Notes'
]
- def __init__(self, bom_file, starting_row=2):
+ def __init__(self, bom_file):
""" Initialize the BomUpload class with a user-uploaded file object """
- try:
- start = int(starting_row) - 1
- if start < 0:
- start = 0
- self.starting_row = start
- except ValueError:
- self.starting_row = 1
self.process(bom_file)
@@ -154,6 +147,20 @@ class BomUploadManager:
return None
+
+ def get_headers(self):
+ """ Return a list of headers for the thingy """
+ headers = []
+
+ return headers
+
+
+ def col_count(self):
+ if self.data is None:
+ return 0
+
+ return len(self.data.headers)
+
def row_count(self):
""" Return the number of rows in the file.
Ignored the top rows as indicated by 'starting row'
@@ -162,16 +169,30 @@ class BomUploadManager:
if self.data is None:
return 0
- # Limit the number of BOM lines to be sensible
- count = min(len(self.data) - self.starting_row, 1000)
+ return len(self.data)
- return count
+ def rows(self):
+ """ Return a list of all rows """
+ rows = []
- def get_row(self, index):
+ for i in range(self.row_count()):
+ row = self.get_row_data(i)
+
+ if row:
+ rows.append(row)
+
+ return rows
+
+ def get_row_data(self, index):
+ """ Retrieve row data at a particular index """
+ if self.data is None or index >= len(self.data):
+ return None
+
+ return self.data[index]
+
+ def get_row_dict(self, index):
""" Retrieve a dict object representing the data row at a particular offset """
- index += self.starting_row
-
if self.data is None or index >= len(self.data):
return None
diff --git a/InvenTree/part/forms.py b/InvenTree/part/forms.py
index b0dcaabfb0..cb6ae9c70b 100644
--- a/InvenTree/part/forms.py
+++ b/InvenTree/part/forms.py
@@ -39,25 +39,29 @@ class BomValidateForm(HelperForm):
]
-class BomImportForm(HelperForm):
+class BomUploadSelectFile(HelperForm):
""" Form for importing a BOM. Provides a file input box for upload """
bom_file = forms.FileField(label='BOM file', required=True, help_text="Select BOM file to upload")
- starting_row = forms.IntegerField(
- required=True,
- initial=2,
- help_text='First row containing valid BOM data',
- validators=[
- MinValueValidator(1)
- ]
- )
-
class Meta:
model = Part
fields = [
'bom_file',
+ ]
+
+
+class BomUploadSelectFields(HelperForm):
+ """ Form for selecting BOM fields """
+
+ starting_row = forms.IntegerField(required=True, initial=2, help_text='Index of starting row', validators=[MinValueValidator(1)])
+ row_count = forms.IntegerField(required=True, help_text='Number of rows to process', validators=[MinValueValidator(0)])
+
+ class Meta:
+ model = Part
+ fields = [
'starting_row',
+ 'row_count',
]
diff --git a/InvenTree/part/templates/part/bom_upload/select_fields.html b/InvenTree/part/templates/part/bom_upload/select_fields.html
new file mode 100644
index 0000000000..0656a957a5
--- /dev/null
+++ b/InvenTree/part/templates/part/bom_upload/select_fields.html
@@ -0,0 +1,38 @@
+{% extends "modal_form.html" %}
+{% load inventree_extras %}
+
+{% block form %}
+
+
Step 2 of 3 - Select BOM Fields
+
+
+
+BOM Rows: {{ bom.row_count }}
+
+{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/part/templates/part/bom_upload.html b/InvenTree/part/templates/part/bom_upload/select_file.html
similarity index 57%
rename from InvenTree/part/templates/part/bom_upload.html
rename to InvenTree/part/templates/part/bom_upload/select_file.html
index 50ff5cdf87..68380dbfd5 100644
--- a/InvenTree/part/templates/part/bom_upload.html
+++ b/InvenTree/part/templates/part/bom_upload/select_file.html
@@ -2,9 +2,13 @@
{% block pre_form_content %}
+Step 1 of 3 - Select BOM File
+
{{ block.super }}
-Select a BOM file to upload for {{ part.name }} - {{ part.description }}.
+Select a BOM file to upload for:
+ {{ part.name }} - {{ part.description }}
+
The BOM file must contain the required named columns as provided in the BOM Upload Template
@@ -14,4 +18,8 @@
Maximum of 1000 lines per BOM
+{% endblock %}
+
+{% block form_data %}
+
{% endblock %}
\ No newline at end of file
diff --git a/InvenTree/part/templatetags/inventree_extras.py b/InvenTree/part/templatetags/inventree_extras.py
index b17c5bf9e8..493a1bb740 100644
--- a/InvenTree/part/templatetags/inventree_extras.py
+++ b/InvenTree/part/templatetags/inventree_extras.py
@@ -8,6 +8,11 @@ from InvenTree import version
register = template.Library()
+@register.simple_tag()
+def inrange(n, *args, **kwargs):
+ """ Return range(n) for iterating through a numeric quantity """
+ return range(n)
+
@register.simple_tag()
def multiply(x, y, *args, **kwargs):
""" Multiply two numbers together """
diff --git a/InvenTree/part/views.py b/InvenTree/part/views.py
index 11669b464b..f5d689875a 100644
--- a/InvenTree/part/views.py
+++ b/InvenTree/part/views.py
@@ -650,8 +650,17 @@ class BomUpload(AjaxView, FormMixin):
"""
ajax_form_title = 'Upload Bill of Materials'
- ajax_template_name = 'part/bom_upload.html'
- form_class = part_forms.BomImportForm
+ ajax_template_name = 'part/bom_upload/select_file.html'
+
+ def get_form_class(self):
+
+ form_step = self.request.POST.get('form_step', None)
+
+ if form_step == 'select_fields':
+ return part_forms.BomUploadSelectFields
+ else:
+ # Default form is the starting point
+ return part_forms.BomUploadSelectFile
def get_context_data(self):
ctx = {
@@ -674,24 +683,54 @@ class BomUpload(AjaxView, FormMixin):
return self.renderJsonResponse(request, self.form)
- def handleBomFileUpload(self, bom_file):
-
+ def handleBomFileUpload(self):
+
+ bom_file = self.request.FILES.get('bom_file', None)
+
+ manager = None
+ bom_file_valid = False
+
+ if bom_file is None:
+ self.form.errors['bom_file'] = [_('No BOM file provided')]
+ else:
+ # Create a BomUploadManager object - will perform initial data validation
+ # (and raise a ValidationError if there is something wrong with the file)
+ try:
+ manager = BomUploadManager(bom_file)
+ bom_file_valid = True
+ except ValidationError as e:
+ errors = e.error_dict
+
+ for k,v in errors.items():
+ self.form.errors[k] = v
+
data = {
- # TODO - Validate the form if there isn't actually an error!
'form_valid': False
}
- # Create a BomUploadManager object - will perform initial data validation
- # (and raise a ValidationError if there is something wrong with the file)
- try:
- manager = BomUploadManager(bom_file, self.form['starting_row'].value())
- except ValidationError as e:
- errors = e.error_dict
+ ctx = {}
- for k,v in errors.items():
- self.form.errors[k] = v
+ if bom_file_valid:
+ # BOM file is valid? Proceed to the next step!
+ form = part_forms.BomUploadSelectFields
+ self.ajax_template_name = 'part/bom_upload/select_fields.html'
+ ctx['bom'] = manager
+ else:
+ form = self.form
- return self.renderJsonResponse(self.request, self.form, data=data)
+ return self.renderJsonResponse(self.request, form, data=data, context=ctx)
+
+ def handleFieldSelection(self):
+
+ data = {
+ 'form_valid': False,
+ }
+
+ self.ajax_template_name = 'part/bom_upload/select_fields.html'
+
+ ctx = {}
+
+ return self.renderJsonResponse(self.request, form=self.get_form(), data=data, context=ctx)
def post(self, request, *args, **kwargs):
""" Perform the various 'POST' requests required.
@@ -703,10 +742,14 @@ class BomUpload(AjaxView, FormMixin):
self.form = self.get_form()
# Did the user POST a file named bom_file?
- bom_file = request.FILES.get('bom_file', None)
+
- if bom_file:
- return self.handleBomFileUpload(bom_file)
+ form_step = request.POST.get('form_step', None)
+
+ if form_step == 'select_file':
+ return self.handleBomFileUpload()
+ elif form_step == 'select_fields':
+ return self.handleFieldSelection()
data = {
'form_valid': False,