mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Create new file bom.py for BOM helper functions
- New class for managing BOM upload
This commit is contained in:
parent
65c7454124
commit
45d16f2c42
@ -92,33 +92,6 @@ def MakeBarcode(object_type, object_id, object_url, data={}):
|
||||
return json.dumps(data, sort_keys=True)
|
||||
|
||||
|
||||
def IsValidSpreadsheetFormat(fmt):
|
||||
""" Test if a file format specifier is in the valid list of spreadsheet file formats """
|
||||
|
||||
return fmt.lower() in ['csv', 'xls', 'xlsx', 'tsv']
|
||||
|
||||
|
||||
def MakeBomTemplate(fmt):
|
||||
""" Generate a Bill of Materials upload template file (for user download) """
|
||||
|
||||
if not IsValidSpreadsheetFormat(fmt):
|
||||
fmt = 'csv'
|
||||
|
||||
fields = [
|
||||
'Part',
|
||||
'Quantity',
|
||||
'Overage',
|
||||
'Reference',
|
||||
'Notes'
|
||||
]
|
||||
|
||||
data = tablib.Dataset(headers=fields).export(fmt)
|
||||
|
||||
filename = 'InvenTree_BOM_Template.' + fmt
|
||||
|
||||
return DownloadFile(data, filename)
|
||||
|
||||
|
||||
def DownloadFile(data, filename, content_type='application/text'):
|
||||
""" Create a dynamic file for the user to download.
|
||||
|
||||
|
92
InvenTree/part/bom.py
Normal file
92
InvenTree/part/bom.py
Normal file
@ -0,0 +1,92 @@
|
||||
"""
|
||||
Functionality for Bill of Material (BOM) management.
|
||||
Primarily BOM upload tools.
|
||||
"""
|
||||
|
||||
from fuzzywuzzy import fuzz
|
||||
import tablib
|
||||
import os
|
||||
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from .models import Part, BomItem
|
||||
|
||||
from InvenTree.helpers import DownloadFile
|
||||
|
||||
|
||||
def IsValidBOMFormat(fmt):
|
||||
""" Test if a file format specifier is in the valid list of BOM file formats """
|
||||
|
||||
return fmt.strip().lower() in ['csv', 'xls', 'xlsx', 'tsv']
|
||||
|
||||
|
||||
def MakeBomTemplate(fmt):
|
||||
""" Generate a Bill of Materials upload template file (for user download) """
|
||||
|
||||
fmt = fmt.strip().lower()
|
||||
|
||||
if not IsValidBOMFormat(fmt):
|
||||
fmt = 'csv'
|
||||
|
||||
fields = [
|
||||
'Part',
|
||||
'Quantity',
|
||||
'Overage',
|
||||
'Reference',
|
||||
'Notes'
|
||||
]
|
||||
|
||||
data = tablib.Dataset(headers=fields).export(fmt)
|
||||
|
||||
filename = 'InvenTree_BOM_Template.' + fmt
|
||||
|
||||
return DownloadFile(data, filename)
|
||||
|
||||
|
||||
class BomUploadManager:
|
||||
""" Class for managing an uploaded BOM file """
|
||||
|
||||
# Fields which are absolutely necessary for valid upload
|
||||
REQUIRED_HEADERS = [
|
||||
'Part',
|
||||
'Quantity',
|
||||
]
|
||||
|
||||
# Fields which are not necessary but can be populated
|
||||
USEFUL_HEADERS = [
|
||||
'REFERENCE',
|
||||
'OVERAGE',
|
||||
'NOTES'
|
||||
]
|
||||
|
||||
def __init__(self, bom_file, starting_row=2):
|
||||
""" Initialize the BomUpload class with a user-uploaded file object """
|
||||
self.starting_row = starting_row
|
||||
print("Starting on row", starting_row)
|
||||
self.process(bom_file)
|
||||
|
||||
def process(self, bom_file):
|
||||
""" Process a BOM file """
|
||||
|
||||
ext = os.path.splitext(bom_file.name)[-1].lower()
|
||||
|
||||
if ext in ['.csv', '.tsv', ]:
|
||||
# These file formats need string decoding
|
||||
raw_data = bom_file.read().decode('utf-8')
|
||||
elif ext in ['.xls', '.xlsx']:
|
||||
raw_data = bom_file.read()
|
||||
else:
|
||||
raise ValidationError({'bom_file': _('Unsupported file format: {f}'.format(f=ext))})
|
||||
|
||||
try:
|
||||
bom_data = tablib.Dataset().load(raw_data)
|
||||
except tablib.UnsupportedFormat:
|
||||
raise ValidationError({'bom_file': _('Error reading BOM file (invalid data)')})
|
||||
|
||||
# Now we have BOM data in memory!
|
||||
|
||||
headers = [h.lower() for h in bom_data.headers]
|
||||
|
||||
for header in self.REQUIRED_HEADERS:
|
||||
if not header.lower() in headers:
|
||||
raise ValidationError({'bom_file': _("Missing required field '{f}'".format(f=header))})
|
@ -8,6 +8,7 @@ from __future__ import unicode_literals
|
||||
import tablib
|
||||
import os
|
||||
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.shortcuts import get_object_or_404
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.urls import reverse_lazy
|
||||
@ -23,11 +24,12 @@ from .models import match_part_names
|
||||
from company.models import SupplierPart
|
||||
|
||||
from . import forms as part_forms
|
||||
from .bom import MakeBomTemplate, BomUploadManager
|
||||
|
||||
from InvenTree.views import AjaxView, AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
from InvenTree.views import QRCodeView
|
||||
|
||||
from InvenTree.helpers import DownloadFile, str2bool, MakeBomTemplate
|
||||
from InvenTree.helpers import DownloadFile, str2bool
|
||||
from InvenTree.status_codes import OrderStatus
|
||||
|
||||
|
||||
@ -679,35 +681,15 @@ class BomUpload(AjaxView, FormMixin):
|
||||
'form_valid': False
|
||||
}
|
||||
|
||||
# Extract the file format data
|
||||
ext = os.path.splitext(bom_file.name)[-1].lower()
|
||||
|
||||
if ext in ['.csv', '.tsv', ]:
|
||||
# These file formats need string decoding
|
||||
raw_data = bom_file.read().decode('utf-8')
|
||||
elif ext in ['.xls', '.xlsx']:
|
||||
raw_data = bom_file.read()
|
||||
else:
|
||||
self.form.errors['bom_file'] = ['Unsupported file format: ' + ext]
|
||||
return self.renderJsonResponse(self.request, self.form, data)
|
||||
|
||||
# Now try to read the data
|
||||
# Create a BomUploadManager object - will perform initial data validation
|
||||
# (and raise a ValidationError if there is something wrong with the file)
|
||||
try:
|
||||
bom_data = tablib.Dataset().load(raw_data)
|
||||
manager = BomUploadManager(bom_file, self.form['starting_row'].value())
|
||||
except ValidationError as e:
|
||||
errors = e.error_dict
|
||||
|
||||
headers = [h.lower() for h in bom_data.headers]
|
||||
|
||||
# Minimal set of required fields
|
||||
for header in ['part', 'quantity', 'reference']:
|
||||
if not header in headers:
|
||||
self.form.errors['bom_file'] = [_("Missing required field '{f}'".format(f=header))]
|
||||
break
|
||||
|
||||
except tablib.UnsupportedFormat:
|
||||
valid = False
|
||||
self.form.errors['bom_file'] = [
|
||||
"Error reading '{f}' (Unsupported file format)".format(f=str(bom_file)),
|
||||
]
|
||||
for k,v in errors.items():
|
||||
self.form.errors[k] = v
|
||||
|
||||
return self.renderJsonResponse(self.request, self.form, data=data)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user