Now displaying item match table

This commit is contained in:
eeintech 2021-05-07 13:23:10 -04:00
parent 4f942fd9f7
commit 6e269ae41a
4 changed files with 133 additions and 49 deletions

View File

@ -20,8 +20,8 @@ class FileManager:
# Fields which are absolutely necessary for valid upload # Fields which are absolutely necessary for valid upload
REQUIRED_HEADERS = [] REQUIRED_HEADERS = []
# Fields which are used for part matching (only one of them is needed) # Fields which are used for item matching (only one of them is needed)
PART_MATCH_HEADERS = [] ITEM_MATCH_HEADERS = []
# Fields which would be helpful but are not required # Fields which would be helpful but are not required
OPTIONAL_HEADERS = [] OPTIONAL_HEADERS = []
@ -83,7 +83,7 @@ class FileManager:
def update_headers(self): def update_headers(self):
""" Update headers """ """ Update headers """
self.HEADERS = self.REQUIRED_HEADERS + self.PART_MATCH_HEADERS + self.OPTIONAL_HEADERS self.HEADERS = self.REQUIRED_HEADERS + self.ITEM_MATCH_HEADERS + self.OPTIONAL_HEADERS
def setup(self): def setup(self):
""" Setup headers depending on the file name """ """ Setup headers depending on the file name """
@ -96,7 +96,7 @@ class FileManager:
'Quantity', 'Quantity',
] ]
self.PART_MATCH_HEADERS = [ self.ITEM_MATCH_HEADERS = [
'Manufacturer_MPN', 'Manufacturer_MPN',
'Supplier_SKU', 'Supplier_SKU',
] ]

View File

@ -119,18 +119,40 @@ class MatchItem(forms.Form):
# Item selection # Item selection
for row in row_data: for row in row_data:
for col in row['data']: for col in row['data']:
print(f'{row["index"]=} | {col["column"]["guess"]=}') if col['column']['guess'] in file_manager.REQUIRED_HEADERS:
if col['column']['guess']: field_name = col['column']['guess'].lower() + '-' + str(row['index'] - 1)
if col['column']['guess'] in file_manager.PART_MATCH_HEADERS: if 'quantity' in col['column']['guess'].lower():
# Get item options self.fields[field_name] = forms.CharField(
item_options = row['item_options']
# Get item match
item_match = row['item_match']
field_name = col['column']['guess'].lower() + '_' + str(row['index'])
self.fields[field_name] = forms.ChoiceField(
choices=item_options,
required=True, required=True,
widget=forms.NumberInput(attrs={
'name': 'quantity' + str(row['index']),
'class': 'numberinput',
'type': 'number',
'min': '1',
'step': 'any',
'value': row['quantity'],
})
) )
if item_match: else:
self.fields[field_name].initial = item_match self.fields[field_name] = forms.Input(
required=True,
widget=forms.Select(attrs={
})
)
elif col['column']['guess'] in file_manager.ITEM_MATCH_HEADERS:
print(f'{row["index"]=} | {col["column"]["guess"]=} | {row.get("item_match", "No Match")}')
# Get item options
item_options = [(option.id, option) for option in row['item_options']]
# Get item match
item_match = row['item_match']
field_name = col['column']['guess'].lower() + '-' + str(row['index'] - 1)
self.fields[field_name] = forms.ChoiceField(
choices=[('', '-' * 10)] + item_options,
required=True,
widget=forms.Select(attrs={'class': 'bomselect'})
)
if item_match:
print(f'{item_match=}')
self.fields[field_name].initial = item_match.id

View File

@ -23,7 +23,7 @@ from . import models
from . import forms from . import forms
from .files import FileManager from .files import FileManager
from part.models import SupplierPart from company.models import ManufacturerPart, SupplierPart
class SettingEdit(AjaxUpdateView): class SettingEdit(AjaxUpdateView):
@ -195,12 +195,41 @@ class FileManagementFormView(MultiStepFormView):
def get_context_data(self, form, **kwargs): def get_context_data(self, form, **kwargs):
context = super().get_context_data(form=form, **kwargs) context = super().get_context_data(form=form, **kwargs)
if self.steps.current == 'fields': if self.steps.current == 'fields' or self.steps.current == 'items':
# Get columns and row data # Get columns and row data
columns = self.file_manager.columns() columns = self.file_manager.columns()
rows = self.file_manager.rows() rows = self.file_manager.rows()
key_item_select = ''
key_quantity_select = ''
if self.steps.current == 'items':
# Get file manager
self.getFileManager()
# Find column key for item selection
for item in self.file_manager.ITEM_MATCH_HEADERS:
item = item.lower()
for key in form.fields.keys():
print(f'{item=} is in {key=} ?')
if item in key:
key_item_select = item
break
break
# Find column key for quantity selection
key_quantity_select = 'quantity'
# Optimize for template # Optimize for template
for row in rows: for row in rows:
# Add item select field
if key_item_select:
row['item_select'] = key_item_select + '-' + str(row['index'])
print(f'{row["item_select"]}')
# Add quantity select field
if key_quantity_select:
row['quantity_select'] = key_quantity_select + '-' + str(row['index'])
row_data = row['data'] row_data = row['data']
data = [] data = []
@ -209,12 +238,16 @@ class FileManagementFormView(MultiStepFormView):
data.append({ data.append({
'cell': item, 'cell': item,
'idx': idx, 'idx': idx,
'column': columns[idx] 'column': columns[idx],
}) })
row['data'] = data row['data'] = data
print(f'\n{row=}')
context.update({'rows': rows}) context.update({'rows': rows})
if self.steps.current == 'items':
context.update({'columns': columns})
# Load extra context data # Load extra context data
print(f'{self.extra_context_data=}') print(f'{self.extra_context_data=}')
@ -393,14 +426,14 @@ class FileManagementFormView(MultiStepFormView):
# Check that at least one of the part match field is present # Check that at least one of the part match field is present
part_match_found = False part_match_found = False
for col in self.file_manager.PART_MATCH_HEADERS: for col in self.file_manager.ITEM_MATCH_HEADERS:
if col in self.column_selections.values(): if col in self.column_selections.values():
part_match_found = True part_match_found = True
break break
# If not, notify user # If not, notify user
if not part_match_found: if not part_match_found:
for col in self.file_manager.PART_MATCH_HEADERS: for col in self.file_manager.ITEM_MATCH_HEADERS:
missing_columns.append(col) missing_columns.append(col)
# Store extra context data # Store extra context data
@ -425,15 +458,16 @@ class FileManagementFormView(MultiStepFormView):
The pre-fill data are then passed through to the part selection form. The pre-fill data are then passed through to the part selection form.
""" """
match_supplier = False
match_manufacturer = False
# Fields prefixed with "Part_" can be used to do "smart matching" against Part objects in the database # Fields prefixed with "Part_" can be used to do "smart matching" against Part objects in the database
q_idx = self.getColumnIndex('Quantity') q_idx = self.getColumnIndex('Quantity')
s_idx = self.getColumnIndex('Supplier_SKU') s_idx = self.getColumnIndex('Supplier_SKU')
# m_idx = self.getColumnIndex('Manufacturer_MPN') m_idx = self.getColumnIndex('Manufacturer_MPN')
# p_idx = self.getColumnIndex('Unit_Price') # p_idx = self.getColumnIndex('Unit_Price')
# e_idx = self.getColumnIndex('Extended_Price') # e_idx = self.getColumnIndex('Extended_Price')
self.allowed_items = SupplierPart.objects.all()
for row in self.rows: for row in self.rows:
# Initially use a quantity of zero # Initially use a quantity of zero
@ -442,9 +476,6 @@ class FileManagementFormView(MultiStepFormView):
# Initially we do not have a part to reference # Initially we do not have a part to reference
exact_match_part = None exact_match_part = None
# A list of potential Part matches
item_options = self.allowed_items
# Check if there is a column corresponding to "quantity" # Check if there is a column corresponding to "quantity"
if q_idx >= 0: if q_idx >= 0:
q_val = row['data'][q_idx]['cell'] q_val = row['data'][q_idx]['cell']
@ -464,7 +495,10 @@ class FileManagementFormView(MultiStepFormView):
# Check if there is a column corresponding to "Supplier SKU" # Check if there is a column corresponding to "Supplier SKU"
if s_idx >= 0: if s_idx >= 0:
sku = row['data'][s_idx] sku = row['data'][s_idx]['cell']
# Match for supplier
match_supplier = True
try: try:
# Attempt SupplierPart lookup based on SKU value # Attempt SupplierPart lookup based on SKU value
@ -473,17 +507,27 @@ class FileManagementFormView(MultiStepFormView):
exact_match_part = None exact_match_part = None
# Check if there is a column corresponding to "Manufacturer MPN" # Check if there is a column corresponding to "Manufacturer MPN"
# if m_idx >= 0: if m_idx >= 0:
# row['part_mpn'] = row['data'][m_idx] mpn = row['data'][m_idx]['cell']
# try: # Match for manufacturer
# # Attempt ManufacturerPart lookup based on MPN value if not match_supplier:
# exact_match_part = ManufacturerPart.objects.get(MPN=row['part_mpn']) match_manufacturer = True
# except (ValueError, ManufacturerPart.DoesNotExist):
# exact_match_part = None try:
# Attempt ManufacturerPart lookup based on MPN value
exact_match_part = ManufacturerPart.objects.get(MPN__contains=mpn)
except (ValueError, ManufacturerPart.DoesNotExist, ManufacturerPart.MultipleObjectsReturned):
exact_match_part = None
# Check if matching for supplier or manufacturer parts
if match_supplier:
self.allowed_items = SupplierPart.objects.all()
elif match_manufacturer:
self.allowed_items = ManufacturerPart.objects.all()
# Supply list of part options for each row, sorted by how closely they match the part name # Supply list of part options for each row, sorted by how closely they match the part name
row['item_options'] = item_options row['item_options'] = self.allowed_items
# Unless found, the 'part_match' is blank # Unless found, the 'part_match' is blank
row['item_match'] = None row['item_match'] = None
@ -502,6 +546,16 @@ class FileManagementFormView(MultiStepFormView):
return valid return valid
def checkPartSelection(self, form):
""" Check part matching """
# Extract form data
self.getFormTableData(form.data)
valid = len(self.extra_context_data.get('missing_columns', [])) == 0 and not self.extra_context_data.get('duplicates', [])
return valid
def validate(self, step, form): def validate(self, step, form):
""" Validate forms """ """ Validate forms """
@ -519,11 +573,10 @@ class FileManagementFormView(MultiStepFormView):
form.add_error(None, 'Fields matching failed') form.add_error(None, 'Fields matching failed')
elif step == 'items': elif step == 'items':
# valid = self.checkPartSelection(form) valid = self.checkPartSelection(form)
# if not valid: if not valid:
# form.add_error(None, 'Items matching failed') form.add_error(None, 'Items matching failed')
pass
return valid return valid

View File

@ -39,7 +39,7 @@
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for row in form %} {% for row in rows %}
<tr {% if row.errors %} style='background: #ffeaea;'{% endif %} part-name='{{ row.part_name }}' part-description='{{ row.description }}' part-select='#select_part_{{ row.index }}'> <tr {% if row.errors %} style='background: #ffeaea;'{% endif %} part-name='{{ row.part_name }}' part-description='{{ row.description }}' part-select='#select_part_{{ row.index }}'>
<td> <td>
<button class='btn btn-default btn-remove' onClick='removeRowFromBomWizard()' id='del_row_{{ forloop.counter }}' style='display: inline; float: right;' title='{% trans "Remove row" %}'> <button class='btn btn-default btn-remove' onClick='removeRowFromBomWizard()' id='del_row_{{ forloop.counter }}' style='display: inline; float: right;' title='{% trans "Remove row" %}'>
@ -48,13 +48,18 @@
</td> </td>
<td></td> <td></td>
<td> <td>
{% comment %} {% add row.index 1 %} {% endcomment %} {% add row.index 1 %}
</td> </td>
<td> <td>
{{ row }} <button class='btn btn-default btn-create' onClick='newPartFromBomWizard()' id='new_part_row_{{ row.index }}' title='{% trans "Create new part" %}' type='button'>
{% comment %} <button class='btn btn-default btn-create' onClick='newPartFromBomWizard()' id='new_part_row_{{ row.index }}' title='{% trans "Create new part" %}' type='button'>
<span row_id='{{ row.index }}' class='fas fa-plus icon-green'/> <span row_id='{{ row.index }}' class='fas fa-plus icon-green'/>
</button> </button>
{% for field in form.visible_fields %}
{% if field.name == row.item_select %}
{{ field }}
{% endif %}
{% endfor %}
{% comment %}
<select class='select bomselect' id='select_part_{{ row.index }}' name='part_{{ row.index }}'> <select class='select bomselect' id='select_part_{{ row.index }}' name='part_{{ row.index }}'>
<option value=''>--- {% trans "Select Part" %} ---</option> <option value=''>--- {% trans "Select Part" %} ---</option>
{% for part in row.part_options %} {% for part in row.part_options %}
@ -69,16 +74,20 @@
</td> </td>
{% for item in row.data %} {% for item in row.data %}
<td> <td>
{% comment %}
{% if item.column.guess == 'Quantity' %} {% if item.column.guess == 'Quantity' %}
<input name='quantity_{{ row.index }}' class='numberinput' type='number' min='1' step='any' value='{% decimal row.quantity %}'/> {% for field in form.visible_fields %}
{% if field.name == row.quantity_select %}
{{ field }}
{% endif %}
{% endfor %}
{% comment %} <input name='quantity_{{ row.index }}' class='numberinput' type='number' min='1' step='any' value='{% decimal row.quantity %}'/> {% endcomment %}
{% if row.errors.quantity %} {% if row.errors.quantity %}
<p class='help-inline'>{{ row.errors.quantity }}</p> <p class='help-inline'>{{ row.errors.quantity }}</p>
{% endif %} {% endif %}
{% else %} {% else %}
{{ item.cell }} {{ item.cell }}
{% endif %} {% endif %}
<input type='hidden' name='row_{{ row.index }}_col_{{ forloop.counter0 }}' value='{{ item.cell }}'/> {% endcomment %} <input type='hidden' name='row_{{ row.index }}_col_{{ forloop.counter0 }}' value='{{ item.cell }}'/>
</td> </td>
{% endfor %} {% endfor %}
</tr> </tr>