mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge branch 'master' of https://github.com/inventree/InvenTree into api-mixin
This commit is contained in:
commit
0ee36b360d
5
.github/workflows/qc_checks.yaml
vendored
5
.github/workflows/qc_checks.yaml
vendored
@ -12,7 +12,7 @@ on:
|
|||||||
- l10*
|
- l10*
|
||||||
|
|
||||||
env:
|
env:
|
||||||
python_version: 3.7
|
python_version: 3.8
|
||||||
node_version: 16
|
node_version: 16
|
||||||
|
|
||||||
server_start_sleep: 60
|
server_start_sleep: 60
|
||||||
@ -229,6 +229,7 @@ jobs:
|
|||||||
cache: 'pip'
|
cache: 'pip'
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
|
sudo apt-get update
|
||||||
sudo apt-get install libpq-dev
|
sudo apt-get install libpq-dev
|
||||||
pip3 install invoke
|
pip3 install invoke
|
||||||
pip3 install psycopg2
|
pip3 install psycopg2
|
||||||
@ -283,7 +284,7 @@ jobs:
|
|||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install mysql-server libmysqlclient-dev
|
sudo apt-get install libmysqlclient-dev
|
||||||
pip3 install invoke
|
pip3 install invoke
|
||||||
pip3 install mysqlclient
|
pip3 install mysqlclient
|
||||||
invoke install
|
invoke install
|
||||||
|
@ -11,7 +11,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
from mptt.fields import TreeNodeChoiceField
|
from mptt.fields import TreeNodeChoiceField
|
||||||
|
|
||||||
from InvenTree.forms import HelperForm
|
from InvenTree.forms import HelperForm
|
||||||
from InvenTree.helpers import GetExportFormats, clean_decimal
|
from InvenTree.helpers import clean_decimal
|
||||||
from InvenTree.fields import RoundingDecimalFormField
|
from InvenTree.fields import RoundingDecimalFormField
|
||||||
|
|
||||||
import common.models
|
import common.models
|
||||||
@ -55,36 +55,6 @@ class PartImageDownloadForm(HelperForm):
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class BomExportForm(forms.Form):
|
|
||||||
""" Simple form to let user set BOM export options,
|
|
||||||
before exporting a BOM (bill of materials) file.
|
|
||||||
"""
|
|
||||||
|
|
||||||
file_format = forms.ChoiceField(label=_("File Format"), help_text=_("Select output file format"))
|
|
||||||
|
|
||||||
cascading = forms.BooleanField(label=_("Cascading"), required=False, initial=True, help_text=_("Download cascading / multi-level BOM"))
|
|
||||||
|
|
||||||
levels = forms.IntegerField(label=_("Levels"), required=True, initial=0, help_text=_("Select maximum number of BOM levels to export (0 = all levels)"))
|
|
||||||
|
|
||||||
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"))
|
|
||||||
|
|
||||||
manufacturer_data = forms.BooleanField(label=_("Include Manufacturer Data"), required=False, initial=True, help_text=_("Include part manufacturer 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 """
|
|
||||||
|
|
||||||
return [(x, x.upper()) for x in GetExportFormats()]
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
|
||||||
super().__init__(*args, **kwargs)
|
|
||||||
|
|
||||||
self.fields['file_format'].choices = self.get_choices()
|
|
||||||
|
|
||||||
|
|
||||||
class BomDuplicateForm(HelperForm):
|
class BomDuplicateForm(HelperForm):
|
||||||
"""
|
"""
|
||||||
Simple confirmation form for BOM duplication.
|
Simple confirmation form for BOM duplication.
|
||||||
|
@ -32,7 +32,7 @@
|
|||||||
<div class='alert alert-info alert-block'>
|
<div class='alert alert-info alert-block'>
|
||||||
<strong>{% trans "Requirements for BOM upload" %}:</strong>
|
<strong>{% trans "Requirements for BOM upload" %}:</strong>
|
||||||
<ul>
|
<ul>
|
||||||
<li>{% trans "The BOM file must contain the required named columns as provided in the " %} <strong><a href="/part/bom_template/">{% trans "BOM Upload Template" %}</a></strong></li>
|
<li>{% trans "The BOM file must contain the required named columns as provided in the " %} <strong><a href='#' id='bom-template-download'>{% trans "BOM Upload Template" %}</a></strong></li>
|
||||||
<li>{% trans "Each part must already exist in the database" %}</li>
|
<li>{% trans "Each part must already exist in the database" %}</li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
@ -60,4 +60,8 @@
|
|||||||
|
|
||||||
enableSidebar('bom-upload');
|
enableSidebar('bom-upload');
|
||||||
|
|
||||||
|
$('#bom-template-download').click(function() {
|
||||||
|
downloadBomTemplate();
|
||||||
|
});
|
||||||
|
|
||||||
{% endblock js_ready %}
|
{% endblock js_ready %}
|
@ -620,13 +620,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
$("#download-bom").click(function () {
|
$("#download-bom").click(function () {
|
||||||
launchModalForm("{% url 'bom-export' part.id %}",
|
exportBom({{ part.id }});
|
||||||
{
|
|
||||||
success: function(response) {
|
|
||||||
location.href = response.url;
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
{% if report_enabled %}
|
{% if report_enabled %}
|
||||||
|
@ -1192,14 +1192,10 @@ class BomExport(AjaxView):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
model = Part
|
model = Part
|
||||||
form_class = part_forms.BomExportForm
|
|
||||||
ajax_form_title = _("Export Bill of Materials")
|
ajax_form_title = _("Export Bill of Materials")
|
||||||
|
|
||||||
role_required = 'part.view'
|
role_required = 'part.view'
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
return self.renderJsonResponse(request, self.form_class())
|
|
||||||
|
|
||||||
def post(self, request, *args, **kwargs):
|
def post(self, request, *args, **kwargs):
|
||||||
|
|
||||||
# Extract POSTed form data
|
# Extract POSTed form data
|
||||||
|
@ -175,7 +175,6 @@ function enableBreadcrumbTree(options) {
|
|||||||
|
|
||||||
for (var i = 0; i < data.length; i++) {
|
for (var i = 0; i < data.length; i++) {
|
||||||
node = data[i];
|
node = data[i];
|
||||||
node.nodes = [];
|
|
||||||
nodes[node.pk] = node;
|
nodes[node.pk] = node;
|
||||||
node.selectable = false;
|
node.selectable = false;
|
||||||
|
|
||||||
@ -193,10 +192,17 @@ function enableBreadcrumbTree(options) {
|
|||||||
node = data[i];
|
node = data[i];
|
||||||
|
|
||||||
if (node.parent != null) {
|
if (node.parent != null) {
|
||||||
|
if (nodes[node.parent].nodes) {
|
||||||
nodes[node.parent].nodes.push(node);
|
nodes[node.parent].nodes.push(node);
|
||||||
|
} else {
|
||||||
|
nodes[node.parent].nodes = [node];
|
||||||
|
}
|
||||||
|
|
||||||
if (node.state.expanded) {
|
if (node.state.expanded) {
|
||||||
|
while (node.parent != null) {
|
||||||
nodes[node.parent].state.expanded = true;
|
nodes[node.parent].state.expanded = true;
|
||||||
|
node = nodes[node.parent];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
@ -212,7 +218,6 @@ function enableBreadcrumbTree(options) {
|
|||||||
collapseIcon: 'fa fa-chevron-down',
|
collapseIcon: 'fa fa-chevron-down',
|
||||||
});
|
});
|
||||||
|
|
||||||
setBreadcrumbTreeState(label, state);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@ -220,26 +225,11 @@ function enableBreadcrumbTree(options) {
|
|||||||
$('#breadcrumb-tree-toggle').click(function() {
|
$('#breadcrumb-tree-toggle').click(function() {
|
||||||
// Add callback to "collapse" and "expand" the sidebar
|
// Add callback to "collapse" and "expand" the sidebar
|
||||||
|
|
||||||
// By default, the menu is "expanded"
|
// Toggle treeview visibilty
|
||||||
var state = localStorage.getItem(`inventree-tree-state-${label}`) || 'expanded';
|
$('#breadcrumb-tree-collapse').toggle();
|
||||||
|
|
||||||
// We wish to "toggle" the state!
|
|
||||||
setBreadcrumbTreeState(label, state == 'expanded' ? 'collapsed' : 'expanded');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Set the initial state (default = expanded)
|
|
||||||
var state = localStorage.getItem(`inventree-tree-state-${label}`) || 'expanded';
|
|
||||||
|
|
||||||
function setBreadcrumbTreeState(label, state) {
|
|
||||||
|
|
||||||
if (state == 'collapsed') {
|
|
||||||
$('#breadcrumb-tree-collapse').hide(100);
|
|
||||||
} else {
|
|
||||||
$('#breadcrumb-tree-collapse').show(100);
|
|
||||||
}
|
|
||||||
|
|
||||||
localStorage.setItem(`inventree-tree-state-${label}`, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
/* globals
|
/* globals
|
||||||
constructForm,
|
constructForm,
|
||||||
|
exportFormatOptions,
|
||||||
imageHoverIcon,
|
imageHoverIcon,
|
||||||
inventreeGet,
|
inventreeGet,
|
||||||
inventreePut,
|
inventreePut,
|
||||||
@ -14,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
/* exported
|
/* exported
|
||||||
|
downloadBomTemplate,
|
||||||
|
exportBom,
|
||||||
newPartFromBomWizard,
|
newPartFromBomWizard,
|
||||||
loadBomTable,
|
loadBomTable,
|
||||||
loadUsedInTable,
|
loadUsedInTable,
|
||||||
@ -21,12 +24,121 @@
|
|||||||
removeColFromBomWizard,
|
removeColFromBomWizard,
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* BOM management functions.
|
function downloadBomTemplate(options={}) {
|
||||||
* Requires follwing files to be loaded first:
|
|
||||||
* - api.js
|
var format = options.format;
|
||||||
* - part.js
|
|
||||||
* - modals.js
|
if (!format) {
|
||||||
|
format = inventreeLoad('bom-export-format', 'csv');
|
||||||
|
}
|
||||||
|
|
||||||
|
constructFormBody({}, {
|
||||||
|
title: '{% trans "Download BOM Template" %}',
|
||||||
|
fields: {
|
||||||
|
format: {
|
||||||
|
label: '{% trans "Format" %}',
|
||||||
|
help_text: '{% trans "Select file format" %}',
|
||||||
|
required: true,
|
||||||
|
type: 'choice',
|
||||||
|
value: format,
|
||||||
|
choices: exportFormatOptions(),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSubmit: function(fields, opts) {
|
||||||
|
var format = getFormFieldValue('format', fields['format'], opts);
|
||||||
|
|
||||||
|
// Save the format for next time
|
||||||
|
inventreeSave('bom-export-format', format);
|
||||||
|
|
||||||
|
// Hide the modal
|
||||||
|
$(opts.modal).modal('hide');
|
||||||
|
|
||||||
|
// Download the file
|
||||||
|
location.href = `{% url "bom-upload-template" %}?format=${format}`;
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Export BOM (Bill of Materials) for the specified Part instance
|
||||||
*/
|
*/
|
||||||
|
function exportBom(part_id, options={}) {
|
||||||
|
|
||||||
|
constructFormBody({}, {
|
||||||
|
title: '{% trans "Export BOM" %}',
|
||||||
|
fields: {
|
||||||
|
format: {
|
||||||
|
label: '{% trans "Format" %}',
|
||||||
|
help_text: '{% trans "Select file format" %}',
|
||||||
|
required: true,
|
||||||
|
type: 'choice',
|
||||||
|
value: inventreeLoad('bom-export-format', 'csv'),
|
||||||
|
choices: exportFormatOptions(),
|
||||||
|
},
|
||||||
|
cascading: {
|
||||||
|
label: '{% trans "Cascading" %}',
|
||||||
|
help_text: '{% trans "Download cascading / multi-level BOM" %}',
|
||||||
|
type: 'boolean',
|
||||||
|
value: inventreeLoad('bom-export-cascading', true),
|
||||||
|
},
|
||||||
|
levels: {
|
||||||
|
label: '{% trans "Levels" %}',
|
||||||
|
help_text: '{% trans "Select maximum number of BOM levels to export (0 = all levels)" %}',
|
||||||
|
type: 'integer',
|
||||||
|
value: 0,
|
||||||
|
min_value: 0,
|
||||||
|
},
|
||||||
|
parameter_data: {
|
||||||
|
label: '{% trans "Include Parameter Data" %}',
|
||||||
|
help_text: '{% trans "Include part parameter data in exported BOM" %}',
|
||||||
|
type: 'boolean',
|
||||||
|
value: inventreeLoad('bom-export-parameter_data', false),
|
||||||
|
},
|
||||||
|
stock_data: {
|
||||||
|
label: '{% trans "Include Stock Data" %}',
|
||||||
|
help_text: '{% trans "Include part stock data in exported BOM" %}',
|
||||||
|
type: 'boolean',
|
||||||
|
value: inventreeLoad('bom-export-stock_data', false),
|
||||||
|
},
|
||||||
|
manufacturer_data: {
|
||||||
|
label: '{% trans "Include Manufacturer Data" %}',
|
||||||
|
help_text: '{% trans "Include part manufacturer data in exported BOM" %}',
|
||||||
|
type: 'boolean',
|
||||||
|
value: inventreeLoad('bom-export-manufacturer_data', false),
|
||||||
|
},
|
||||||
|
supplier_data: {
|
||||||
|
label: '{% trans "Include Supplier Data" %}',
|
||||||
|
help_text: '{% trans "Include part supplier data in exported BOM" %}',
|
||||||
|
type: 'boolean',
|
||||||
|
value: inventreeLoad('bom-export-supplier_data', false),
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSubmit: function(fields, opts) {
|
||||||
|
|
||||||
|
// Extract values from the form
|
||||||
|
var field_names = ['format', 'cascading', 'levels', 'parameter_data', 'stock_data', 'manufacturer_data', 'supplier_data'];
|
||||||
|
|
||||||
|
var url = `/part/${part_id}/bom-download/?`;
|
||||||
|
|
||||||
|
field_names.forEach(function(fn) {
|
||||||
|
var val = getFormFieldValue(fn, fields[fn], opts);
|
||||||
|
|
||||||
|
// Update user preferences
|
||||||
|
inventreeSave(`bom-export-${fn}`, val);
|
||||||
|
|
||||||
|
url += `${fn}=${val}&`;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(opts.modal).modal('hide');
|
||||||
|
|
||||||
|
// Redirect to the BOM file download
|
||||||
|
location.href = url;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function bomItemFields() {
|
function bomItemFields() {
|
||||||
|
@ -811,7 +811,9 @@ function updateFieldValue(name, value, field, options) {
|
|||||||
|
|
||||||
switch (field.type) {
|
switch (field.type) {
|
||||||
case 'boolean':
|
case 'boolean':
|
||||||
el.prop('checked', value);
|
if (value == true || value.toString().toLowerCase() == 'true') {
|
||||||
|
el.prop('checked');
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
case 'related field':
|
case 'related field':
|
||||||
// Clear?
|
// Clear?
|
||||||
@ -2034,8 +2036,15 @@ function constructInputOptions(name, classes, type, parameters) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (parameters.value != null) {
|
if (parameters.value != null) {
|
||||||
|
if (parameters.type == 'boolean') {
|
||||||
|
// Special consideration of a boolean (checkbox) value
|
||||||
|
if (parameters.value == true || parameters.value.toString().toLowerCase() == 'true') {
|
||||||
|
opts.push('checked');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
// Existing value?
|
// Existing value?
|
||||||
opts.push(`value='${parameters.value}'`);
|
opts.push(`value='${parameters.value}'`);
|
||||||
|
}
|
||||||
} else if (parameters.default != null) {
|
} else if (parameters.default != null) {
|
||||||
// Otherwise, a defualt value?
|
// Otherwise, a defualt value?
|
||||||
opts.push(`value='${parameters.default}'`);
|
opts.push(`value='${parameters.default}'`);
|
||||||
|
Loading…
Reference in New Issue
Block a user