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

Merge remote-tracking branch 'inventree/master' into scheduling

# Conflicts:
#	InvenTree/InvenTree/version.py

(Update API version)
This commit is contained in:
Oliver
2022-03-08 12:25:36 +11:00
63 changed files with 20987 additions and 19004 deletions

View File

@ -20,6 +20,7 @@
/* exported
allocateStockToBuild,
autoAllocateStockToBuild,
completeBuildOrder,
createBuildOutput,
editBuildOrder,
@ -754,7 +755,7 @@ function loadBuildOutputTable(build_info, options={}) {
filters[key] = params[key];
}
// TODO: Initialize filter list
setupFilterList('builditems', $(table), options.filterTarget || '#filter-list-incompletebuilditems');
function setupBuildOutputButtonCallbacks() {
@ -999,7 +1000,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
filters[key] = params[key];
}
setupFilterList('builditems', $(table), options.filterTarget || null);
setupFilterList('builditems', $(table), options.filterTarget);
// If an "output" is specified, then only "trackable" parts are allocated
// Otherwise, only "untrackable" parts are allowed
@ -1512,6 +1513,16 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
*/
function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
if (bom_items.length == 0) {
showAlertDialog(
'{% trans "Select Parts" %}',
'{% trans "You must select at least one part to allocate" %}',
);
return;
}
// ID of the associated "build output" (or null)
var output_id = options.output || null;
@ -1626,8 +1637,8 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
if (table_entries.length == 0) {
showAlertDialog(
'{% trans "Select Parts" %}',
'{% trans "You must select at least one part to allocate" %}',
'{% trans "All Parts Allocated" %}',
'{% trans "All selected parts have been fully allocated" %}',
);
return;
@ -1844,6 +1855,48 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
}
/**
* Automatically allocate stock items to a build
*/
function autoAllocateStockToBuild(build_id, bom_items=[], options={}) {
var html = `
<div class='alert alert-block alert-info'>
<strong>{% trans "Automatic Stock Allocation" %}</strong><br>
{% trans "Stock items will be automatically allocated to this build order, according to the provided guidelines" %}:
<ul>
<li>{% trans "If a location is specifed, stock will only be allocated from that location" %}</li>
<li>{% trans "If stock is considered interchangeable, it will be allocated from the first location it is found" %}</li>
<li>{% trans "If substitute stock is allowed, it will be used where stock of the primary part cannot be found" %}</li>
</ul>
</div>
`;
var fields = {
location: {
value: options.location,
},
interchangeable: {
value: true,
},
substitutes: {
value: true,
},
};
constructForm(`/api/build/${build_id}/auto-allocate/`, {
method: 'POST',
fields: fields,
title: '{% trans "Allocate Stock Items" %}',
confirm: true,
preFormContent: html,
onSuccess: function(response) {
$('#allocation-table-untracked').bootstrapTable('refresh');
}
});
}
/*
* Display a table of Build orders
*/

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),
@ -899,7 +959,7 @@ function loadPurchaseOrderTable(table, options) {
sortable: true,
sortName: 'supplier__name',
formatter: function(value, row) {
return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`);
return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/?display=purchase-orders`);
}
},
{
@ -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

@ -1219,7 +1219,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'>