2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-20 22:06:28 +00:00

Merge branch 'master' into feature-non-int-serial

This commit is contained in:
Ben Charlton
2020-08-28 17:14:02 +01:00
17 changed files with 412 additions and 108 deletions

View File

@ -7,6 +7,8 @@ from rapidfuzz import fuzz
import tablib
import os
from collections import OrderedDict
from django.utils.translation import gettext_lazy as _
from django.core.exceptions import ValidationError
@ -47,7 +49,7 @@ def MakeBomTemplate(fmt):
return DownloadFile(data, filename)
def ExportBom(part, fmt='csv', cascade=False, max_levels=None, supplier_data=False):
def ExportBom(part, fmt='csv', cascade=False, max_levels=None, parameter_data=False, stock_data=False, supplier_data=False):
""" Export a BOM (Bill of Materials) for a given part.
Args:
@ -92,9 +94,75 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, supplier_data=Fal
dataset = BomItemResource().export(queryset=bom_items, cascade=cascade)
def add_columns_to_dataset(columns, column_size):
try:
for header, column_dict in columns.items():
# Construct column tuple
col = tuple(column_dict.get(c_idx, '') for c_idx in range(column_size))
# Add column to dataset
dataset.append_col(col, header=header)
except AttributeError:
pass
if parameter_data:
"""
If requested, add extra columns for each PartParameter associated with each line item
"""
parameter_cols = {}
for b_idx, bom_item in enumerate(bom_items):
# Get part parameters
parameters = bom_item.sub_part.get_parameters()
# Add parameters to columns
if parameters:
for parameter in parameters:
name = parameter.template.name
value = parameter.data
try:
parameter_cols[name].update({b_idx: value})
except KeyError:
parameter_cols[name] = {b_idx: value}
# Add parameter columns to dataset
parameter_cols_ordered = OrderedDict(sorted(parameter_cols.items(), key=lambda x: x[0]))
add_columns_to_dataset(parameter_cols_ordered, len(bom_items))
if stock_data:
"""
If requested, add extra columns for stock data associated with each line item
"""
stock_headers = [
_('Default Location'),
_('Available Stock'),
]
stock_cols = {}
for b_idx, bom_item in enumerate(bom_items):
stock_data = []
# Get part default location
try:
stock_data.append(bom_item.sub_part.get_default_location().name)
except AttributeError:
stock_data.append('')
# Get part current stock
stock_data.append(bom_item.sub_part.available_stock)
for s_idx, header in enumerate(stock_headers):
try:
stock_cols[header].update({b_idx: stock_data[s_idx]})
except KeyError:
stock_cols[header] = {b_idx: stock_data[s_idx]}
# Add stock columns to dataset
add_columns_to_dataset(stock_cols, len(bom_items))
if supplier_data:
"""
If requested, add extra columns for each SupplierPart associated with the each line item
If requested, add extra columns for each SupplierPart associated with each line item
"""
# Expand dataset with manufacturer parts
@ -150,11 +218,7 @@ def ExportBom(part, fmt='csv', cascade=False, max_levels=None, supplier_data=Fal
manufacturer_cols[k_mpn] = {b_idx: manufacturer_mpn}
# Add manufacturer columns to dataset
for header, col_dict in manufacturer_cols.items():
# Construct column tuple
col = tuple(col_dict.get(c_idx, '') for c_idx in range(len(bom_items)))
# Add column to dataset
dataset.append_col(col, header=header)
add_columns_to_dataset(manufacturer_cols, len(bom_items))
data = dataset.export(fmt)

View File

@ -58,7 +58,11 @@ class BomExportForm(forms.Form):
levels = forms.IntegerField(label=_("Levels"), required=True, initial=0, help_text=_("Select maximum number of BOM levels to export (0 = all levels)"))
supplier_data = forms.BooleanField(label=_("Include Supplier Data"), required=False, initial=True, help_text=_("Include supplier part data in exported BOM"))
parameter_data = forms.BooleanField(label=_("Include Parameter Data"), required=False, initial=False, help_text=_("Include part parameters data in exported BOM"))
stock_data = forms.BooleanField(label=_("Include Stock Data"), required=False, initial=False, help_text=_("Include part stock data in exported BOM"))
supplier_data = forms.BooleanField(label=_("Include Supplier Data"), required=False, initial=True, help_text=_("Include part supplier data in exported BOM"))
def get_choices(self):
""" BOM export format choices """
@ -196,11 +200,19 @@ class EditCategoryForm(HelperForm):
]
class PartModelChoiceField(forms.ModelChoiceField):
""" Extending string representation of Part instance with available stock """
def label_from_instance(self, part):
return f'{part} - {part.available_stock}'
class EditBomItemForm(HelperForm):
""" Form for editing a BomItem object """
quantity = RoundingDecimalFormField(max_digits=10, decimal_places=5)
sub_part = PartModelChoiceField(queryset=Part.objects.all())
class Meta:
model = BomItem
fields = [

View File

@ -268,7 +268,7 @@ class Part(MPTTModel):
super().save(*args, **kwargs)
def __str__(self):
return "{n} - {d}".format(n=self.full_name, d=self.description)
return f"{self.full_name} - {self.description}"
def checkAddToBOM(self, parent):
"""

View File

@ -63,7 +63,7 @@
<option value=''>--- {% trans "Select Part" %} ---</option>
{% for part in row.part_options %}
<option value='{{ part.id }}' {% if part.id == row.part.id %} selected='selected' {% elif part.id == row.part_match.id %} selected='selected' {% endif %}>
{{ part.full_name }} - {{ part.description }}
{{ part }} - {{ part.available_stock }}
</option>
{% endfor %}
</select>

View File

@ -25,14 +25,12 @@
{{ block.super }}
$('#add-stock-item').click(function () {
launchModalForm(
"{% url 'stock-item-create' %}",
{
reload: true,
data: {
part: {{ part.id }}
}
});
createNewStockItem({
reload: true,
data: {
part: {{ part.id }},
}
});
});
loadStockTable($("#stock-table"), {
@ -64,37 +62,12 @@
});
$('#item-create').click(function () {
launchModalForm("{% url 'stock-item-create' %}", {
createNewStockItem({
reload: true,
data: {
part: {{ part.id }}
},
secondary: [
{
field: 'part',
label: '{% trans "New Part" %}',
title: '{% trans "Create New Part" %}',
url: "{% url 'part-create' %}",
},
{
field: 'supplier_part',
label: '{% trans "New Supplier Part" %}',
title: '{% trans "Create new Supplier Part" %}',
url: "{% url 'supplier-part-create' %}",
data: {
part: {{ part.id }}
},
},
{
field: 'location',
label: '{% trans "New Location" %}',
title: '{% trans "Create New Location" %}',
url: "{% url 'stock-location-create' %}",
}
]
part: {{ part.id }},
}
});
return false;
});
{% endblock %}

View File

@ -1499,6 +1499,10 @@ class BomDownload(AjaxView):
cascade = str2bool(request.GET.get('cascade', False))
parameter_data = str2bool(request.GET.get('parameter_data', False))
stock_data = str2bool(request.GET.get('stock_data', False))
supplier_data = str2bool(request.GET.get('supplier_data', False))
levels = request.GET.get('levels', None)
@ -1516,7 +1520,13 @@ class BomDownload(AjaxView):
if not IsValidBOMFormat(export_format):
export_format = 'csv'
return ExportBom(part, fmt=export_format, cascade=cascade, max_levels=levels, supplier_data=supplier_data)
return ExportBom(part,
fmt=export_format,
cascade=cascade,
max_levels=levels,
parameter_data=parameter_data,
stock_data=stock_data,
supplier_data=supplier_data)
def get_data(self):
return {
@ -1541,6 +1551,8 @@ class BomExport(AjaxView):
fmt = request.POST.get('file_format', 'csv').lower()
cascade = str2bool(request.POST.get('cascading', False))
levels = request.POST.get('levels', None)
parameter_data = str2bool(request.POST.get('parameter_data', False))
stock_data = str2bool(request.POST.get('stock_data', False))
supplier_data = str2bool(request.POST.get('supplier_data', False))
try:
@ -1556,6 +1568,8 @@ class BomExport(AjaxView):
url += '?file_format=' + fmt
url += '&cascade=' + str(cascade)
url += '&parameter_data=' + str(parameter_data)
url += '&stock_data=' + str(stock_data)
url += '&supplier_data=' + str(supplier_data)
if levels: