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

Merge branch 'inventree:master' into trans-improv

This commit is contained in:
Matthias Mair
2021-08-05 08:17:21 +02:00
committed by GitHub
28 changed files with 616 additions and 1056 deletions

View File

@ -8,6 +8,26 @@
*/
function bomItemFields() {
return {
part: {
hidden: true,
},
sub_part: {
},
quantity: {},
reference: {},
overage: {},
note: {},
allow_variants: {},
inherited: {},
optional: {},
};
}
function reloadBomTable(table, options) {
table.bootstrapTable('refresh');
@ -262,13 +282,13 @@ function loadBomTable(table, options) {
cols.push(
{
field: 'price_range',
title: '{% trans "Buy Price" %}',
title: '{% trans "Supplier Cost" %}',
sortable: true,
formatter: function(value, row, index, field) {
if (value) {
return value;
} else {
return "<span class='warning-msg'>{% trans 'No pricing available' %}</span>";
return "<span class='warning-msg'>{% trans 'No supplier pricing available' %}</span>";
}
}
});
@ -528,14 +548,15 @@ function loadBomTable(table, options) {
var pk = $(this).attr('pk');
var url = `/part/bom/${pk}/edit/`;
launchModalForm(
url,
{
success: function() {
reloadBomTable(table);
}
var fields = bomItemFields();
constructForm(`/api/bom/${pk}/`, {
fields: fields,
title: '{% trans "Edit BOM Item" %}',
onSuccess: function() {
reloadBomTable(table);
}
);
});
});
table.on('click', '.bom-validate-button', function() {

View File

@ -927,7 +927,7 @@ function loadBuildTable(table, options) {
},
{
field: 'responsible',
title: '{% trans "Resposible" %}',
title: '{% trans "Responsible" %}',
sortable: true,
formatter: function(value, row, index, field) {
if (value)

View File

@ -265,6 +265,8 @@ function setupFilterList(tableKey, table, target) {
// One blank slate, please
element.empty();
element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-default filter-tag'><span class='fas fa-redo-alt'></span></button>`);
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
if (Object.keys(filters).length > 0) {
@ -279,6 +281,11 @@ function setupFilterList(tableKey, table, target) {
element.append(`<div title='${description}' class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
}
// Callback for reloading the table
element.find(`#reload-${tableKey}`).click(function() {
$(table).bootstrapTable('refresh');
});
// Add a callback for adding a new filter
element.find(`#${add}`).click(function clicked() {

View File

@ -240,6 +240,7 @@ function constructDeleteForm(fields, options) {
* - hidden: Set to true to hide the field
* - icon: font-awesome icon to display before the field
* - prefix: Custom HTML prefix to display before the field
* - data: map of data to fill out field values with
* - focus: Name of field to focus on when modal is displayed
* - preventClose: Set to true to prevent form from closing on success
* - onSuccess: callback function when form action is successful
@ -263,6 +264,11 @@ function constructForm(url, options) {
// Default HTTP method
options.method = options.method || 'PATCH';
// Construct an "empty" data object if not provided
if (!options.data) {
options.data = {};
}
// Request OPTIONS endpoint from the API
getApiEndpointOptions(url, function(OPTIONS) {
@ -346,10 +352,19 @@ function constructFormBody(fields, options) {
// otherwise *all* fields will be displayed
var displayed_fields = options.fields || fields;
// Handle initial data overrides
if (options.data) {
for (const field in options.data) {
if (field in fields) {
fields[field].value = options.data[field];
}
}
}
// Provide each field object with its own name
for(field in fields) {
fields[field].name = field;
// If any "instance_filters" are defined for the endpoint, copy them across (overwrite)
if (fields[field].instance_filters) {
@ -366,6 +381,10 @@ function constructFormBody(fields, options) {
// TODO: Refactor the following code with Object.assign (see above)
// "before" and "after" renders
fields[field].before = field_options.before;
fields[field].after = field_options.after;
// Secondary modal options
fields[field].secondary = field_options.secondary;
@ -560,10 +579,15 @@ function submitFormData(fields, options) {
var has_files = false;
// Extract values for each field
options.field_names.forEach(function(name) {
for (var idx = 0; idx < options.field_names.length; idx++) {
var name = options.field_names[idx];
var field = fields[name] || null;
// Ignore visual fields
if (field && field.type == 'candy') continue;
if (field) {
var value = getFormFieldValue(name, field, options);
@ -593,7 +617,7 @@ function submitFormData(fields, options) {
} else {
console.log(`WARNING: Could not find field matching '${name}'`);
}
});
}
var upload_func = inventreePut;
@ -1279,6 +1303,11 @@ function renderModelData(name, model, data, parameters, options) {
*/
function constructField(name, parameters, options) {
// Shortcut for simple visual fields
if (parameters.type == 'candy') {
return constructCandyInput(name, parameters, options);
}
var field_name = `id_${name}`;
// Hidden inputs are rendered without label / help text / etc
@ -1292,7 +1321,14 @@ function constructField(name, parameters, options) {
form_classes += ' has-error';
}
var html = `<div id='div_${field_name}' class='${form_classes}'>`;
var html = '';
// Optional content to render before the field
if (parameters.before) {
html += parameters.before;
}
html += `<div id='div_${field_name}' class='${form_classes}'>`;
// Add a label
html += constructLabel(name, parameters);
@ -1352,6 +1388,10 @@ function constructField(name, parameters, options) {
html += `</div>`; // controls
html += `</div>`; // form-group
if (parameters.after) {
html += parameters.after;
}
return html;
}
@ -1430,6 +1470,9 @@ function constructInput(name, parameters, options) {
case 'date':
func = constructDateInput;
break;
case 'candy':
func = constructCandyInput;
break;
default:
// Unsupported field type!
break;
@ -1658,6 +1701,17 @@ function constructDateInput(name, parameters, options) {
}
/*
* Construct a "candy" field input
* No actual field data!
*/
function constructCandyInput(name, parameters, options) {
return parameters.html;
}
/*
* Construct a 'help text' div based on the field parameters
*

View File

@ -13,91 +13,213 @@ function yesNoLabel(value) {
}
}
// Construct fieldset for part forms
function partFields(options={}) {
var fields = {
category: {},
name: {},
IPN: {},
revision: {},
description: {},
variant_of: {},
keywords: {
icon: 'fa-key',
},
units: {},
link: {
icon: 'fa-link',
},
default_location: {},
default_supplier: {},
default_expiry: {
icon: 'fa-calendar-alt',
},
minimum_stock: {
icon: 'fa-boxes',
},
attributes: {
type: 'candy',
html: `<hr><h4><i>{% trans "Part Attributes" %}</i></h4><hr>`
},
component: {
value: global_settings.PART_COMPONENT,
},
assembly: {
value: global_settings.PART_ASSEMBLY,
},
is_template: {
value: global_settings.PART_TEMPLATE,
},
trackable: {
value: global_settings.PART_TRACKABLE,
},
purchaseable: {
value: global_settings.PART_PURCHASEABLE,
},
salable: {
value: global_settings.PART_SALABLE,
},
virtual: {
value: global_settings.PART_VIRTUAL,
},
};
// If editing a part, we can set the "active" status
if (options.edit) {
fields.active = {};
}
// Pop expiry field
if (!global_settings.STOCK_ENABLE_EXPIRY) {
delete fields["default_expiry"];
}
// Additional fields when "creating" a new part
if (options.create) {
// No supplier parts available yet
delete fields["default_supplier"];
fields.create = {
type: 'candy',
html: `<hr><h4><i>{% trans "Part Creation Options" %}</i></h4><hr>`,
};
if (global_settings.PART_CREATE_INITIAL) {
fields.initial_stock = {
type: 'decimal',
label: '{% trans "Initial Stock Quantity" %}',
help_text: '{% trans "Initialize part stock with specified quantity" %}',
};
}
fields.copy_category_parameters = {
type: 'boolean',
label: '{% trans "Copy Category Parameters" %}',
help_text: '{% trans "Copy parameter templates from selected part category" %}',
value: global_settings.PART_CATEGORY_PARAMETERS,
};
}
// Additional fields when "duplicating" a part
if (options.duplicate) {
fields.duplicate = {
type: 'candy',
html: `<hr><h4><i>{% trans "Part Duplication Options" %}</i></h4><hr>`,
};
fields.copy_from = {
type: 'integer',
hidden: true,
value: options.duplicate,
},
fields.copy_image = {
type: 'boolean',
label: '{% trans "Copy Image" %}',
help_text: '{% trans "Copy image from original part" %}',
value: true,
},
fields.copy_bom = {
type: 'boolean',
label: '{% trans "Copy BOM" %}',
help_text: '{% trans "Copy bill of materials from original part" %}',
value: global_settings.PART_COPY_BOM,
};
fields.copy_parameters = {
type: 'boolean',
label: '{% trans "Copy Parameters" %}',
help_text: '{% trans "Copy parameter data from original part" %}',
value: global_settings.PART_COPY_PARAMETERS,
};
}
return fields;
}
function categoryFields() {
return {
parent: {
help_text: '{% trans "Parent part category" %}',
},
name: {},
description: {},
default_location: {},
default_keywords: {
icon: 'fa-key',
}
};
}
// Edit a PartCategory via the API
function editCategory(pk, options={}) {
var url = `/api/part/category/${pk}/`;
var fields = categoryFields();
constructForm(url, {
fields: fields,
title: '{% trans "Edit Part Category" %}',
reload: true,
});
}
function editPart(pk, options={}) {
var url = `/api/part/${pk}/`;
var fields = {
category: {
/*
secondary: {
label: '{% trans "New Category" %}',
title: '{% trans "Create New Part Category" %}',
api_url: '{% url "api-part-category-list" %}',
method: 'POST',
fields: {
name: {},
description: {},
parent: {
secondary: {
title: '{% trans "New Parent" %}',
api_url: '{% url "api-part-category-list" %}',
method: 'POST',
fields: {
name: {},
description: {},
parent: {},
}
}
},
}
},
*/
},
name: {
placeholder: 'part name',
},
IPN: {},
description: {},
revision: {},
keywords: {
icon: 'fa-key',
},
variant_of: {},
link: {
icon: 'fa-link',
},
default_location: {
/*
secondary: {
label: '{% trans "New Location" %}',
title: '{% trans "Create new stock location" %}',
},
*/
},
default_supplier: {
filters: {
part: pk,
part_detail: true,
manufacturer_detail: true,
supplier_detail: true,
},
/*
secondary: {
label: '{% trans "New Supplier Part" %}',
title: '{% trans "Create new supplier part" %}',
}
*/
},
units: {},
minimum_stock: {},
virtual: {},
is_template: {},
assembly: {},
component: {},
trackable: {},
purchaseable: {},
salable: {},
active: {},
};
var fields = partFields({
edit: true
});
constructForm(url, {
fields: fields,
title: '{% trans "Edit Part" %}',
reload: true,
});
}
// Launch form to duplicate a part
function duplicatePart(pk, options={}) {
// First we need all the part information
inventreeGet(`/api/part/${pk}/`, {}, {
success: function(data) {
var fields = partFields({
duplicate: pk,
});
// If we are making a "variant" part
if (options.variant) {
// Override the "variant_of" field
data.variant_of = pk;
}
constructForm('{% url "api-part-list" %}', {
method: 'POST',
fields: fields,
title: '{% trans "Duplicate Part" %}',
data: data,
onSuccess: function(data) {
// Follow the new part
location.href = `/part/${data.pk}/`;
}
});
}
});
}

View File

@ -187,7 +187,7 @@ $.fn.inventreeTable = function(options) {
if (!options.disablePagination) {
options.pagination = true;
options.paginationVAlign = options.paginationVAlign || 'both';
options.pageSize = inventreeLoad(varName, 25);
options.pageSize = options.pageSize || inventreeLoad(varName, 25);
options.pageList = [25, 50, 100, 250, 'all'];
options.totalField = 'count';
options.dataField = 'results';