2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 13:05:42 +00:00

Merge branch 'inventree:master' into matmair/issue2279

This commit is contained in:
Matthias Mair
2022-03-03 23:55:04 +01:00
committed by GitHub
49 changed files with 24986 additions and 23619 deletions

View File

@ -671,9 +671,7 @@ function loadBomTable(table, options={}) {
// Do we show part pricing in the BOM table?
var show_pricing = global_settings.PART_SHOW_PRICE_IN_BOM;
if (!show_pricing) {
params.include_pricing = false;
}
params.include_pricing = show_pricing == true;
if (options.part_detail) {
params.part_detail = true;
@ -989,32 +987,40 @@ function loadBomTable(table, options={}) {
// Function to request BOM data for sub-items
// This function may be called recursively for multi-level BOMs
function requestSubItems(bom_pk, part_pk) {
function requestSubItems(bom_pk, part_pk, depth=0) {
// TODO: 2022-02-03 Currently, multi-level BOMs are not actually displayed.
// Prevent multi-level recursion
const MAX_BOM_DEPTH = 25;
// Re-enable this function once multi-level display has been re-deployed
return;
if (depth >= MAX_BOM_DEPTH) {
console.log(`Maximum BOM depth (${MAX_BOM_DEPTH}) reached!`);
return;
}
inventreeGet(
options.bom_url,
{
part: part_pk,
sub_part_detail: true,
include_pricing: show_pricing == true,
},
{
success: function(response) {
// Add the returned sub-items to the table
for (var idx = 0; idx < response.length; idx++) {
response[idx].parentId = bom_pk;
if (response[idx].sub_part_detail.assembly) {
requestSubItems(response[idx].pk, response[idx].sub_part);
}
}
table.bootstrapTable('append', response);
// Next, re-iterate and check if the new items also have sub items
response.forEach(function(bom_item) {
if (bom_item.sub_part_detail.assembly) {
requestSubItems(bom_item.pk, bom_item.sub_part, depth + 1);
}
});
table.treegrid('collapseAll');
},
error: function(xhr) {
@ -1026,7 +1032,7 @@ function loadBomTable(table, options={}) {
}
table.inventreeTable({
treeEnable: !options.editable,
treeEnable: true,
rootParentId: parent_id,
idField: 'pk',
uniqueId: 'pk',
@ -1066,38 +1072,37 @@ function loadBomTable(table, options={}) {
url: options.bom_url,
onPostBody: function() {
if (!options.editable) {
table.treegrid({
treeColumn: 0,
onExpand: function() {
}
});
}
table.treegrid({
treeColumn: 1,
onExpand: function() {
}
});
table.treegrid('collapseAll');
},
onLoadSuccess: function() {
if (options.editable) {
table.bootstrapTable('uncheckAll');
} else {
}
var data = table.bootstrapTable('getData');
var data = table.bootstrapTable('getData');
for (var idx = 0; idx < data.length; idx++) {
var row = data[idx];
for (var idx = 0; idx < data.length; idx++) {
var row = data[idx];
// If a row already has a parent ID set, it's already been updated!
if (row.parentId) {
continue;
}
// If a row already has a parent ID set, it's already been updated!
if (row.parentId) {
continue;
}
// Set the parent ID of the top-level rows
row.parentId = parent_id;
// Set the parent ID of the top-level rows
row.parentId = parent_id;
table.bootstrapTable('updateRow', idx, row, true);
table.bootstrapTable('updateRow', idx, row, true);
if (row.sub_part_detail.assembly) {
requestSubItems(row.pk, row.sub_part);
}
if (row.sub_part_detail.assembly) {
requestSubItems(row.pk, row.sub_part);
}
}
},

View File

@ -256,7 +256,7 @@ function generateFilterInput(tableKey, filterKey) {
* @param {*} table - bootstrapTable element to update
* @param {*} target - name of target element on page
*/
function setupFilterList(tableKey, table, target) {
function setupFilterList(tableKey, table, target, options={}) {
var addClicked = false;
@ -283,6 +283,11 @@ function setupFilterList(tableKey, table, target) {
var buttons = '';
// Add download button
if (options.download) {
buttons += `<button id='download-${tableKey}' title='{% trans "Download data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-download'></span></button>`;
}
buttons += `<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`;
// If there are filters defined for this table, add more buttons
@ -295,7 +300,7 @@ function setupFilterList(tableKey, table, target) {
}
element.html(`
<div class='btn-group' role='group'>
<div class='btn-group filter-group' role='group'>
${buttons}
</div>
`);
@ -322,6 +327,13 @@ function setupFilterList(tableKey, table, target) {
$(table).bootstrapTable('refresh');
});
// Add a callback for downloading table data
if (options.download) {
element.find(`#download-${tableKey}`).click(function() {
downloadTableData($(table));
});
}
// Add a callback for adding a new filter
element.find(`#${add}`).click(function clicked() {
@ -358,14 +370,14 @@ function setupFilterList(tableKey, table, target) {
reloadTableFilters(table, filters);
// Run this function again
setupFilterList(tableKey, table, target);
setupFilterList(tableKey, table, target, options);
}
});
} else {
addClicked = false;
setupFilterList(tableKey, table, target);
setupFilterList(tableKey, table, target, options);
}
});
@ -376,7 +388,7 @@ function setupFilterList(tableKey, table, target) {
reloadTableFilters(table, filters);
setupFilterList(tableKey, table, target);
setupFilterList(tableKey, table, target, options);
});
// Add callback for deleting each filter
@ -390,7 +402,7 @@ function setupFilterList(tableKey, table, target) {
reloadTableFilters(table, filters);
// Run this function again!
setupFilterList(tableKey, table, target);
setupFilterList(tableKey, table, target, options);
});
}

View File

@ -281,6 +281,65 @@ function createPurchaseOrder(options={}) {
}
/* Construct a set of fields for the SalesOrderLineItem form */
function soLineItemFields(options={}) {
var fields = {
order: {
hidden: true,
},
part: {},
quantity: {},
reference: {},
sale_price: {},
sale_price_currency: {},
target_date: {},
notes: {},
};
if (options.order) {
fields.order.value = options.order;
}
return fields;
}
/* Construct a set of fields for the PurchaseOrderLineItem form */
function poLineItemFields(options={}) {
var fields = {
order: {
hidden: true,
},
part: {
filters: {
part_detail: true,
supplier_detail: true,
supplier: options.supplier,
}
},
quantity: {},
reference: {},
purchase_price: {},
purchase_price_currency: {},
target_date: {},
destination: {},
notes: {},
};
if (options.order) {
fields.order.value = options.order;
}
if (options.currency) {
fields.purchase_price_currency.value = options.currency;
}
return fields;
}
function removeOrderRowFromOrderWizard(e) {
/* Remove a part selection from an order form. */
@ -293,6 +352,7 @@ function removeOrderRowFromOrderWizard(e) {
$('#' + row).remove();
}
function newSupplierPartFromOrderWizard(e) {
/* Create a new supplier part directly from an order form.
* Launches a secondary modal and (if successful),
@ -991,10 +1051,36 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
var target = options.filter_target || '#filter-list-purchase-order-lines';
setupFilterList('purchaseorderlineitem', $(table), target);
setupFilterList('purchaseorderlineitem', $(table), target, {download: true});
function setupCallbacks() {
if (options.allow_edit) {
// Callback for "duplicate" button
$(table).find('.button-line-duplicate').click(function() {
var pk = $(this).attr('pk');
inventreeGet(`/api/order/po-line/${pk}/`, {}, {
success: function(data) {
var fields = poLineItemFields({
supplier: options.supplier,
});
constructForm('{% url "api-po-line-list" %}', {
method: 'POST',
fields: fields,
data: data,
title: '{% trans "Duplicate Line Item" %}',
onSuccess: function(response) {
$(table).bootstrapTable('refresh');
}
});
}
});
});
// Callback for "edit" button
$(table).find('.button-line-edit').click(function() {
var pk = $(this).attr('pk');
@ -1022,6 +1108,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
});
});
// Callback for "delete" button
$(table).find('.button-line-delete').click(function() {
var pk = $(this).attr('pk');
@ -1270,6 +1357,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
}
if (options.allow_edit) {
html += makeIconButton('fa-clone', 'button-line-duplicate', pk, '{% trans "Duplicate line item" %}');
html += makeIconButton('fa-edit icon-blue', 'button-line-edit', pk, '{% trans "Edit line item" %}');
html += makeIconButton('fa-trash-alt icon-red', 'button-line-delete', pk, '{% trans "Delete line item" %}');
}
@ -2449,6 +2537,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
html += makeIconButton('fa-dollar-sign icon-green', 'button-price', pk, '{% trans "Calculate price" %}');
}
html += makeIconButton('fa-clone', 'button-duplicate', pk, '{% trans "Duplicate line item" %}');
html += makeIconButton('fa-edit icon-blue', 'button-edit', pk, '{% trans "Edit line item" %}');
var delete_disabled = false;
@ -2480,6 +2569,28 @@ function loadSalesOrderLineItemTable(table, options={}) {
// Configure callback functions once the table is loaded
function setupCallbacks() {
// Callback for duplicating line items
$(table).find('.button-duplicate').click(function() {
var pk = $(this).attr('pk');
inventreeGet(`/api/order/so-line/${pk}/`, {}, {
success: function(data) {
var fields = soLineItemFields();
constructForm('{% url "api-so-line-list" %}', {
method: 'POST',
fields: fields,
data: data,
title: '{% trans "Duplicate Line Item" %}',
onSuccess: function(response) {
$(table).bootstrapTable('refresh');
}
});
}
});
});
// Callback for editing line items
$(table).find('.button-edit').click(function() {
var pk = $(this).attr('pk');

View File

@ -474,6 +474,7 @@ function duplicateBom(part_id, options={}) {
}
},
include_inherited: {},
copy_substitutes: {},
remove_existing: {},
skip_invalid: {},
},
@ -1217,7 +1218,7 @@ function loadPartTable(table, url, options={}) {
filters[key] = params[key];
}
setupFilterList('parts', $(table), options.filterTarget || null);
setupFilterList('parts', $(table), options.filterTarget, {download: true});
var columns = [
{

View File

@ -43,7 +43,6 @@
duplicateStockItem,
editStockItem,
editStockLocation,
exportStock,
findStockItemBySerialNumber,
installStockItem,
loadInstalledInTable,
@ -506,49 +505,6 @@ function stockStatusCodes() {
}
/*
* Export stock table
*/
function exportStock(params={}) {
constructFormBody({}, {
title: '{% trans "Export Stock" %}',
fields: {
format: {
label: '{% trans "Format" %}',
help_text: '{% trans "Select file format" %}',
required: true,
type: 'choice',
value: 'csv',
choices: exportFormatOptions(),
},
sublocations: {
label: '{% trans "Include Sublocations" %}',
help_text: '{% trans "Include stock items in sublocations" %}',
type: 'boolean',
value: 'true',
}
},
onSubmit: function(fields, form_options) {
var format = getFormFieldValue('format', fields['format'], form_options);
var cascade = getFormFieldValue('sublocations', fields['sublocations'], form_options);
// Hide the modal
$(form_options.modal).modal('hide');
var url = `{% url "stock-export" %}?format=${format}&cascade=${cascade}`;
for (var key in params) {
url += `&${key}=${params[key]}`;
}
location.href = url;
}
});
}
/**
* Assign multiple stock items to a customer
*/
@ -1615,7 +1571,7 @@ function loadStockTable(table, options) {
original[k] = params[k];
}
setupFilterList(filterKey, table, filterTarget);
setupFilterList(filterKey, table, filterTarget, {download: true});
// Override the default values, or add new ones
for (var key in params) {

View File

@ -7,6 +7,7 @@
/* exported
customGroupSorter,
downloadTableData,
reloadtable,
renderLink,
reloadTableFilters,
@ -21,6 +22,62 @@ function reloadtable(table) {
}
/**
* Download data from a table, via the API.
* This requires a number of conditions to be met:
*
* - The API endpoint supports data download (on the server side)
* - The table is "flat" (does not support multi-level loading, etc)
* - The table has been loaded using the inventreeTable() function, not bootstrapTable()
* (Refer to the "reloadTableFilters" function to see why!)
*/
function downloadTableData(table, opts={}) {
// Extract table configuration options
var table_options = table.bootstrapTable('getOptions');
var url = table_options.url;
if (!url) {
console.log('Error: downloadTableData could not find "url" parameter.');
}
var query_params = table_options.query_params || {};
url += '?';
constructFormBody({}, {
title: opts.title || '{% trans "Export Table Data" %}',
fields: {
format: {
label: '{% trans "Format" %}',
help_text: '{% trans "Select File Format" %}',
required: true,
type: 'choice',
value: 'csv',
choices: exportFormatOptions(),
}
},
onSubmit: function(fields, form_options) {
var format = getFormFieldValue('format', fields['format'], form_options);
// Hide the modal
$(form_options.modal).modal('hide');
for (const [key, value] of Object.entries(query_params)) {
url += `${key}=${value}&`;
}
url += `export=${format}`;
location.href = url;
}
});
}
/**
* Render a URL for display
* @param {String} text
@ -114,6 +171,10 @@ function reloadTableFilters(table, filters) {
}
}
// Store the total set of query params
// This is necessary for the "downloadTableData" function to work
options.query_params = params;
options.queryParams = function(tableParams) {
return convertQueryParameters(tableParams, params);
};
@ -221,7 +282,11 @@ $.fn.inventreeTable = function(options) {
// Extract query params
var filters = options.queryParams || options.filters || {};
// Store the total set of query params
options.query_params = filters;
options.queryParams = function(params) {
// Update the query parameters callback with the *new* filters
return convertQueryParameters(params, filters);
};

View File

@ -11,9 +11,6 @@
<div id='{{ prefix }}button-toolbar'>
<div class='button-toolbar container-fluid' style='float: right;'>
<div class='btn-group' role='group'>
<button class='btn btn-outline-secondary' id='stock-export' title='{% trans "Export Stock Information" %}'>
<span class='fas fa-download'></span>
</button>
{% if barcodes %}
<!-- Barcode actions menu -->
<div class='btn-group' role='group'>