mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Table custom buttons (#5075)
* Add generic implementation for barcode actions - Commonize code against tables - Cleaner UI - Better code - Will make future react refactor easier * Add permissions.js - Separate .js file for dynamically checking permissions * Update stock table to use client-side actions * API endpoint for bulk category adjustment * Bug fix for purchase_order.js - Prevent some really strange API calls * Refactor actions for part table - Now done dynamically * Refactor actions for the attachment tables * Refactor actions for build output table * Increment API version * Cleanup janky button * Refactor supplier part table * Refactor manufacturer part table * Remove linkButtonsToSelection - no longer needed - Cleanup, yay! * Cleanup purchase order line table * Refactor BOM table buttons * JS linting * Further cleanup * Template cleanup - remove extra div elements * js linting * js fix
This commit is contained in:
@ -18,9 +18,7 @@
|
||||
{% block content %}
|
||||
|
||||
<div id='history-buttons'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="notifications-history" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="notifications-history" %}
|
||||
</div>
|
||||
|
||||
<div class='row'>
|
||||
|
@ -18,9 +18,7 @@
|
||||
{% block content %}
|
||||
|
||||
<div id='inbox-buttons'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="notifications-inbox" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="notifications-inbox" %}
|
||||
</div>
|
||||
|
||||
<div class='row'>
|
||||
|
@ -35,9 +35,7 @@
|
||||
|
||||
<div class='panel-content'>
|
||||
<div id='part-stocktake-report-toolbar'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="stocktakereport" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="stocktakereport" %}
|
||||
</div>
|
||||
<table class='table table-striped table-condensed' id='stocktake-report-table' data-toolbar='#part-stocktake-report-toolbar'></table>
|
||||
</div>
|
||||
|
@ -51,11 +51,7 @@
|
||||
{% endif %}
|
||||
|
||||
<div id='plugin-button-toolbar'>
|
||||
<div class='button-toolbar container-fluid'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="plugins" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="plugins" %}
|
||||
</div>
|
||||
<div class='table-responsive'>
|
||||
<table class='table table-striped table-condensed' id='plugin-table' data-toolbar='#plugin-button-toolbar'></table>
|
||||
|
@ -1,21 +1,7 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div id='attachment-buttons'>
|
||||
<div class='btn-group' role='group'>
|
||||
<div class='btn-group' id='multi-attachment-actions'>
|
||||
<button class='btn btn-primary dropdown-toggle' type='button' data-bs-toggle='dropdown' title='{% trans "Actions" %}'>
|
||||
<span class='fas fa-tools'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu'>
|
||||
<li>
|
||||
<a class='dropdown-item' href='#' id='multi-attachment-delete' title='{% trans "Delete selected attachments" %}'>
|
||||
<span class='fas fa-trash-alt icon-red'></span> {% trans "Delete Attachments" %}
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
{% include "filter_list.html" with id="attachments" %}
|
||||
</div>
|
||||
{% include "filter_list.html" with id="attachments" %}
|
||||
</div>
|
||||
|
||||
<div class='dropzone' id='attachment-dropzone'>
|
||||
|
@ -147,6 +147,7 @@
|
||||
<!-- dynamic javascript templates -->
|
||||
<script defer type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% url 'nav.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% url 'permissions.js' %}"></script>
|
||||
<script defer type='text/javascript' src="{% url 'settings.js' %}"></script>
|
||||
|
||||
<!-- translated javascript templates-->
|
||||
|
@ -1 +1,3 @@
|
||||
<div class='filter-list d-flex flex-row form-row' id='filter-list-{% if prefix %}{{ prefix }}{% endif %}{{ id }}'><!-- Empty div for table filters --></div>
|
||||
<div class='filter-list d-flex flex-row form-row' id='filter-list-{% if prefix %}{{ prefix }}{% endif %}{{ id }}'>
|
||||
<!-- Empty div for table filters -->
|
||||
</div>
|
||||
|
59
InvenTree/templates/js/dynamic/permissions.js
Normal file
59
InvenTree/templates/js/dynamic/permissions.js
Normal file
@ -0,0 +1,59 @@
|
||||
/*
|
||||
* globals
|
||||
inventreeGet,
|
||||
|
||||
/* exported
|
||||
checkPermission,
|
||||
*/
|
||||
|
||||
// Keep track of the current user permissions
|
||||
var user_roles = null;
|
||||
|
||||
|
||||
/*
|
||||
* Check if the user has the specified role and permission
|
||||
*/
|
||||
function checkPermission(role, permission='view') {
|
||||
|
||||
// Allow permission to be specified in dotted notation, e.g. 'part.add'
|
||||
if (role.indexOf('.') > 0) {
|
||||
let parts = role.split('.');
|
||||
role = parts[0];
|
||||
permission = parts[1];
|
||||
}
|
||||
|
||||
// Request user roles if we do not have them
|
||||
if (user_roles == null) {
|
||||
inventreeGet('{% url "api-user-roles" %}', {}, {
|
||||
async: false,
|
||||
success: function(response) {
|
||||
user_roles = response.roles || {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (user_roles == null) {
|
||||
console.error("Failed to fetch user roles");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(role in user_roles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let roles = user_roles[role];
|
||||
|
||||
if (!roles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let found = false;
|
||||
|
||||
user_roles[role].forEach(function(p) {
|
||||
if (String(p).valueOf() == String(permission).valueOf()) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
@ -187,7 +187,27 @@ function attachmentLink(filename) {
|
||||
let html = makeIcon(icon) + ` ${fn}`;
|
||||
|
||||
return renderLink(html, filename, {download: true});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of actions for an attachment table,
|
||||
* with the provided permission set
|
||||
*/
|
||||
function makeAttachmentActions(permissions, options) {
|
||||
|
||||
let actions = [];
|
||||
|
||||
if (permissions.delete) {
|
||||
actions.push({
|
||||
label: 'delete',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
title: '{% trans "Delete attachments" %}',
|
||||
callback: options.callback,
|
||||
});
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
|
||||
@ -225,7 +245,20 @@ function loadAttachmentTable(url, options) {
|
||||
}
|
||||
});
|
||||
|
||||
setupFilterList('attachments', $(table), '#filter-list-attachments');
|
||||
setupFilterList('attachments', $(table), '#filter-list-attachments', {
|
||||
custom_actions: [
|
||||
{
|
||||
label: 'attachments',
|
||||
icon: 'fa-tools',
|
||||
title: '{% trans "Attachment actions" %}',
|
||||
actions: makeAttachmentActions(permissions, {
|
||||
callback: function(attachments) {
|
||||
deleteAttachments(attachments, url, options);
|
||||
}
|
||||
}),
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
if (permissions.add) {
|
||||
addAttachmentButtonCallbacks(url, options.fields || {});
|
||||
@ -235,19 +268,6 @@ function loadAttachmentTable(url, options) {
|
||||
$('#new-attachment-link').hide();
|
||||
}
|
||||
|
||||
if (permissions.delete) {
|
||||
// Add callback for the 'multi delete' button
|
||||
$('#multi-attachment-delete').click(function() {
|
||||
var attachments = getTableData(table);
|
||||
|
||||
if (attachments.length > 0) {
|
||||
deleteAttachments(attachments, url, options);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#multi-attachment-actions').hide();
|
||||
}
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
name: options.name || 'attachments',
|
||||
@ -286,16 +306,6 @@ function loadAttachmentTable(url, options) {
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (permissions.delete) {
|
||||
// Add callback for 'delete' button
|
||||
$(table).find('.button-attachment-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
var attachment = $(table).bootstrapTable('getRowByUniqueId', pk);
|
||||
deleteAttachments([attachment], url, options);
|
||||
});
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
|
@ -763,7 +763,7 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
// Extra form fields
|
||||
var extra = makeNotesField();
|
||||
|
||||
// Header contentfor
|
||||
// Header content
|
||||
var header = `
|
||||
<div id='header-div'>
|
||||
</div>
|
||||
|
@ -33,6 +33,7 @@
|
||||
modalSetContent,
|
||||
partFields,
|
||||
partGroups,
|
||||
reloadBootstrapTable,
|
||||
renderLink,
|
||||
setupFilterList,
|
||||
shortenString,
|
||||
@ -817,7 +818,24 @@ function loadBomTable(table, options={}) {
|
||||
|
||||
Object.assign(filters, params);
|
||||
|
||||
setupFilterList('bom', $(table));
|
||||
setupFilterList('bom', $(table), '#filter-list-bom', {
|
||||
custom_actions: [{
|
||||
label: 'actions',
|
||||
actions: [{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete items" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'part.change',
|
||||
callback: function(data) {
|
||||
deleteBomItems(data, {
|
||||
success: function() {
|
||||
reloadBootstrapTable('#bom-table');
|
||||
}
|
||||
});
|
||||
}
|
||||
}]
|
||||
}]
|
||||
});
|
||||
|
||||
function availableQuantity(row) {
|
||||
|
||||
|
@ -23,7 +23,6 @@
|
||||
inventreeLoad,
|
||||
inventreePut,
|
||||
launchModalForm,
|
||||
linkButtonsToSelection,
|
||||
loadTableFilters,
|
||||
locationDetail,
|
||||
makeDeleteButton,
|
||||
@ -34,6 +33,7 @@
|
||||
makePartIcons,
|
||||
makeProgressBar,
|
||||
orderParts,
|
||||
reloadBootstrapTable,
|
||||
renderDate,
|
||||
renderLink,
|
||||
setupFilterList,
|
||||
@ -387,7 +387,7 @@ function createBuildOutput(build_id, options) {
|
||||
fields: fields,
|
||||
preFormContent: html,
|
||||
onSuccess: function(response) {
|
||||
location.reload();
|
||||
reloadBootstrapTable(options.table || '#build-output-table');
|
||||
},
|
||||
});
|
||||
|
||||
@ -995,6 +995,70 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of actions for the build output table
|
||||
*/
|
||||
function makeBuildOutputActions(build_info) {
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'complete',
|
||||
title: '{% trans "Complete outputs" %}',
|
||||
icon: 'fa-check-circle icon-green',
|
||||
permission: 'build.add',
|
||||
callback: function(data) {
|
||||
completeBuildOutputs(
|
||||
build_info.pk,
|
||||
data,
|
||||
{
|
||||
success: function() {
|
||||
$('#build-output-table').bootstrapTable('refresh'); // Reload the "in progress" table
|
||||
$('#build-stock-table').bootstrapTable('refresh'); // Reload the "completed" table
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'scrap',
|
||||
title: '{% trans "Scrap outputs" %}',
|
||||
icon: 'fa-times-circle icon-red',
|
||||
permission: 'build.change',
|
||||
callback: function(data) {
|
||||
scrapBuildOutputs(
|
||||
build_info.pk,
|
||||
data,
|
||||
{
|
||||
success: function() {
|
||||
$('#build-output-table').bootstrapTable('refresh'); // Reload the "in progress" table
|
||||
$('#build-stock-table').bootstrapTable('refresh'); // Reload the "completed" table
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete outputs" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'build.delete',
|
||||
callback: function(data) {
|
||||
deleteBuildOutputs(
|
||||
build_info.pk,
|
||||
data,
|
||||
{
|
||||
success: function() {
|
||||
$('#build-output-table').bootstrapTable('refresh'); // Reload the "in progress" table
|
||||
$('#build-stock-table').bootstrapTable('refresh'); // Reload the "completed" table
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display a "build output" table for a particular build.
|
||||
*
|
||||
@ -1035,6 +1099,12 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
},
|
||||
singular_name: '{% trans "build output" %}',
|
||||
plural_name: '{% trans "build outputs" %}',
|
||||
custom_actions: [{
|
||||
label: 'buildoutput',
|
||||
icon: 'fa-tools',
|
||||
title: '{% trans "Build output actions" %}',
|
||||
actions: makeBuildOutputActions(build_info),
|
||||
}]
|
||||
});
|
||||
|
||||
// Request list of required tests for the part being assembled
|
||||
@ -1383,25 +1453,6 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
);
|
||||
});
|
||||
|
||||
// Complete multiple outputs
|
||||
$('#multi-output-complete').click(function() {
|
||||
var outputs = getTableData(table);
|
||||
|
||||
completeBuildOutputs(
|
||||
build_info.pk,
|
||||
outputs,
|
||||
{
|
||||
success: function() {
|
||||
// Reload the "in progress" table
|
||||
$('#build-output-table').bootstrapTable('refresh');
|
||||
|
||||
// Reload the "completed" table
|
||||
$('#build-stock-table').bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Delete multiple build outputs
|
||||
$('#multi-output-delete').click(function() {
|
||||
var outputs = getTableData(table);
|
||||
@ -1421,25 +1472,6 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
);
|
||||
});
|
||||
|
||||
// Scrap multiple outputs
|
||||
$('#multi-output-scrap').click(function() {
|
||||
var outputs = getTableData(table);
|
||||
|
||||
scrapBuildOutputs(
|
||||
build_info.pk,
|
||||
outputs,
|
||||
{
|
||||
success: function() {
|
||||
// Reload the "in progress" table
|
||||
$('#build-output-table').bootstrapTable('refresh');
|
||||
|
||||
// Reload the "completed" table
|
||||
$('#build-stock-table').bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$('#outputs-expand').click(function() {
|
||||
$(table).bootstrapTable('expandAllRows');
|
||||
});
|
||||
@ -2180,13 +2212,6 @@ function loadBuildTable(table, options) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
linkButtonsToSelection(
|
||||
table,
|
||||
[
|
||||
'#build-print-options',
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
makeDeleteButton,
|
||||
makeEditButton,
|
||||
makeIconBadge,
|
||||
orderParts,
|
||||
renderClipboard,
|
||||
renderDate,
|
||||
renderLink,
|
||||
@ -1213,6 +1214,43 @@ function deleteManufacturerPartParameters(selections, options={}) {
|
||||
}
|
||||
|
||||
|
||||
// Construct a set of actions for the manufacturer part table
|
||||
function makeManufacturerPartActions(options={}) {
|
||||
return [
|
||||
{
|
||||
label: 'order',
|
||||
title: '{% trans "Order parts" %}',
|
||||
icon: 'fa-shopping-cart',
|
||||
permission: 'purchase_order.add',
|
||||
callback: function(data) {
|
||||
let parts = [];
|
||||
|
||||
data.forEach(function(item) {
|
||||
let part = item.part_detail;
|
||||
part.manufacturer_part = item.pk;
|
||||
parts.push(part);
|
||||
});
|
||||
|
||||
orderParts(parts);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete manufacturer parts" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'purchase_order.delete',
|
||||
callback: function(data) {
|
||||
deleteManufacturerParts(data, {
|
||||
success: function() {
|
||||
$('#manufacturer-part-table').bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load manufacturer part table
|
||||
*/
|
||||
@ -1226,7 +1264,18 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
|
||||
var filterTarget = options.filterTarget || '#filter-list-manufacturer-part';
|
||||
|
||||
setupFilterList('manufacturer-part', $(table), filterTarget);
|
||||
setupFilterList('manufacturer-part', $(table), filterTarget, {
|
||||
custom_actions: [
|
||||
{
|
||||
label: 'manufacturer-part',
|
||||
title: '{% trans "Manufacturer part actions" %}',
|
||||
icon: 'fa-tools',
|
||||
actions: makeManufacturerPartActions({
|
||||
manufacturer_id: options.params.manufacturer,
|
||||
})
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
@ -1453,6 +1502,43 @@ function loadManufacturerPartParameterTable(table, url, options) {
|
||||
}
|
||||
|
||||
|
||||
// Construct a set of actions for the supplier part table
|
||||
function makeSupplierPartActions(options={}) {
|
||||
return [
|
||||
{
|
||||
label: 'order',
|
||||
title: '{% trans "Order parts" %}',
|
||||
icon: 'fa-shopping-cart',
|
||||
permission: 'purchase_order.add',
|
||||
callback: function(data) {
|
||||
let parts = []
|
||||
|
||||
data.forEach(function(entry) {
|
||||
parts.push(entry.part_detail);
|
||||
});
|
||||
|
||||
orderParts(parts, {
|
||||
supplier: options.supplier_id,
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
title: '{% trans "Delete supplier parts" %}',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
permission: 'purchase_order.delete',
|
||||
callback: function(data) {
|
||||
deleteSupplierParts(data, {
|
||||
success: function() {
|
||||
$('#supplier-part-table').bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Load supplier part table
|
||||
*/
|
||||
@ -1464,7 +1550,18 @@ function loadSupplierPartTable(table, url, options) {
|
||||
// Load filters
|
||||
var filters = loadTableFilters('supplierpart', params);
|
||||
|
||||
setupFilterList('supplierpart', $(table));
|
||||
setupFilterList('supplierpart', $(table), '#filter-list-supplier-part', {
|
||||
custom_actions: [
|
||||
{
|
||||
label: 'supplier-part',
|
||||
title: '{% trans "Supplier part actions" %}',
|
||||
icon: 'fa-tools',
|
||||
actions: makeSupplierPartActions({
|
||||
supplier_id: options.params.supplier,
|
||||
}),
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
|
@ -1,6 +1,7 @@
|
||||
{% load i18n %}
|
||||
|
||||
/* globals
|
||||
checkPermission,
|
||||
downloadTableData,
|
||||
getAvailableTableFilters,
|
||||
getTableData,
|
||||
@ -266,6 +267,102 @@ function generateFilterInput(tableKey, filterKey) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a single action button based on the provided definition
|
||||
*/
|
||||
function makeFilterActionButton(button, options={}) {
|
||||
let prefix = options.prefix || 'action';
|
||||
|
||||
// Check for required permission (if specified)
|
||||
if (button.permission) {
|
||||
if (!checkPermission(button.permission)) {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
return `
|
||||
<li><a class='dropdown-item' href='#' id='action-${prefix}-${button.label}'>
|
||||
<span class='fas ${button.icon}'></span> ${button.title}
|
||||
</a></li>`;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of custom actions for a given table
|
||||
*/
|
||||
function makeCustomActionGroup(action_group, table) {
|
||||
|
||||
let buttons = [];
|
||||
let label = action_group.label || 'actions';
|
||||
let title = action_group.title || '{% trans "Actions" %}';
|
||||
let icon = action_group.icon || 'fa-tools';
|
||||
|
||||
// Construct the HTML for each button
|
||||
action_group.actions.forEach(function(action) {
|
||||
buttons.push(makeFilterActionButton(action, {prefix: label}));
|
||||
});
|
||||
|
||||
if (buttons.length == 0) {
|
||||
// Don't display anything if there are no buttons to show
|
||||
return '';
|
||||
}
|
||||
|
||||
let html = `
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='${label}-actions' title='${title}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas ${icon}'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
`;
|
||||
|
||||
buttons.forEach(function(button) {
|
||||
html += button;
|
||||
});
|
||||
|
||||
html += `</ul></div>`;
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of custom barcode actions for a given table
|
||||
*
|
||||
* To define barcode actions for a data table, use options.barcode_actions
|
||||
*/
|
||||
function makeBarcodeActions(barcode_actions, table) {
|
||||
|
||||
let html = `
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='barcode-actions' title='{% trans "Barcode actions" %}' class='btn btn-outline-secondary dropdown-toggle' type='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-qrcode'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu' role='menu'>
|
||||
`;
|
||||
|
||||
barcode_actions.forEach(function(action) {
|
||||
html += makeFilterActionButton(action, {prefix: 'barcode'});
|
||||
});
|
||||
|
||||
html += `</ul></div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Add callbacks for custom actions
|
||||
*/
|
||||
function addFilterActionCallbacks(element, label, table, actions) {
|
||||
actions.forEach(function(action) {
|
||||
let id = `action-${label}-${action.label}`;
|
||||
element.find(`#${id}`).click(function() {
|
||||
let data = getTableData(table);
|
||||
action.callback(data);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Helper function to make a 'filter' style button
|
||||
*/
|
||||
@ -315,6 +412,19 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
|
||||
let report_button = options.report && global_settings.REPORT_ENABLE;
|
||||
let labels_button = options.labels && global_settings.LABEL_ENABLE;
|
||||
let barcode_actions = options.barcode_actions && global_settings.BARCODE_ENABLE;
|
||||
|
||||
// Add in "custom" actions first (to the left of the table buttons)
|
||||
if (options.custom_actions) {
|
||||
options.custom_actions.forEach(function(action_group) {
|
||||
buttons += makeCustomActionGroup(action_group, table);
|
||||
});
|
||||
}
|
||||
|
||||
// Add in button for custom barcode actions
|
||||
if (barcode_actions) {
|
||||
buttons += makeBarcodeActions(options.barcode_actions, table);
|
||||
}
|
||||
|
||||
if (report_button || labels_button) {
|
||||
let print_buttons = `
|
||||
@ -394,6 +504,19 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
element.append(filter_tag);
|
||||
}
|
||||
|
||||
// Callback for custom actions
|
||||
if (options.custom_actions) {
|
||||
options.custom_actions.forEach(function(action_group) {
|
||||
let label = action_group.label || 'actions';
|
||||
addFilterActionCallbacks(element, label, table, action_group.actions);
|
||||
});
|
||||
}
|
||||
|
||||
// Callback for barcode actions
|
||||
if (barcode_actions) {
|
||||
addFilterActionCallbacks(element, 'barcode', table, options.barcode_actions);
|
||||
}
|
||||
|
||||
// Callback for printing reports
|
||||
if (options.report && global_settings.REPORT_ENABLE) {
|
||||
element.find(`#print-report-${tableKey}`).click(function() {
|
||||
|
@ -23,7 +23,6 @@
|
||||
inventreeLoad,
|
||||
inventreePut,
|
||||
inventreeSave,
|
||||
linkButtonsToSelection,
|
||||
loadTableFilters,
|
||||
makeDeleteButton,
|
||||
makeEditButton,
|
||||
@ -2159,6 +2158,69 @@ function partGridTile(part) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Update the category for a set of parts
|
||||
*/
|
||||
function setPartCategory(data, options={}) {
|
||||
|
||||
let parts = [];
|
||||
|
||||
data.forEach(function(item) {
|
||||
parts.push(item.pk);
|
||||
});
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Set the part category for the selected parts" %}
|
||||
</div>
|
||||
`;
|
||||
|
||||
constructForm('{% url "api-part-change-category" %}',{
|
||||
title: '{% trans "Set Part Category" %}',
|
||||
method: 'POST',
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
category: {},
|
||||
},
|
||||
processBeforeUpload: function(data) {
|
||||
data.parts = parts;
|
||||
return data;
|
||||
},
|
||||
onSuccess: function() {
|
||||
$(options.table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of custom actions for the part table
|
||||
*/
|
||||
function makePartActions(table) {
|
||||
|
||||
return [
|
||||
{
|
||||
label: 'set-category',
|
||||
title: '{% trans "Set category" %}',
|
||||
icon: 'fa-sitemap',
|
||||
permission: 'part.change',
|
||||
callback: function(data) {
|
||||
setPartCategory(data, {table: table});
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'order',
|
||||
title: '{% trans "Order parts" %}',
|
||||
icon: 'fa-shopping-cart',
|
||||
permission: 'purchase_order.add',
|
||||
callback: function(data) {
|
||||
orderParts(data);
|
||||
},
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
/* Load part listing data into specified table.
|
||||
*
|
||||
* Args:
|
||||
@ -2190,6 +2252,14 @@ function loadPartTable(table, url, options={}) {
|
||||
},
|
||||
singular_name: '{% trans "part" %}',
|
||||
plural_name: '{% trans "parts" %}',
|
||||
custom_actions: [
|
||||
{
|
||||
label: 'parts',
|
||||
icon: 'fa-tools',
|
||||
title: '{% trans "Part actions" %}',
|
||||
actions: makePartActions(table),
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
var columns = [
|
||||
@ -2438,97 +2508,6 @@ function loadPartTable(table, url, options={}) {
|
||||
return html;
|
||||
}
|
||||
});
|
||||
|
||||
if (options.buttons) {
|
||||
linkButtonsToSelection($(table), options.buttons);
|
||||
}
|
||||
|
||||
/* Button callbacks for part table buttons */
|
||||
|
||||
// Callback function for the "order parts" button
|
||||
$('#multi-part-order').click(function() {
|
||||
var selections = getTableData(table);
|
||||
|
||||
var parts = [];
|
||||
|
||||
selections.forEach(function(part) {
|
||||
parts.push(part);
|
||||
});
|
||||
|
||||
orderParts(parts, {});
|
||||
});
|
||||
|
||||
// Callback function for the "set category" button
|
||||
$('#multi-part-category').click(function() {
|
||||
var selections = getTableData(table);
|
||||
var parts = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
parts.push(item.pk);
|
||||
});
|
||||
|
||||
var html = `
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans "Set the part category for the selected parts" %}
|
||||
</div>
|
||||
`;
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Set Part Category" %}',
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
category: {
|
||||
label: '{% trans "Category" %}',
|
||||
help_text: '{% trans "Select Part Category" %}',
|
||||
required: true,
|
||||
type: 'related field',
|
||||
model: 'partcategory',
|
||||
api_url: '{% url "api-part-category-list" %}',
|
||||
}
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
var category = getFormFieldValue('category', fields['category'], opts);
|
||||
|
||||
if (category == null) {
|
||||
handleFormErrors(
|
||||
{
|
||||
'category': ['{% trans "Category is required" %}']
|
||||
},
|
||||
opts.fields,
|
||||
opts
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Set the category for each part in sequence
|
||||
function setCategory() {
|
||||
if (parts.length > 0) {
|
||||
var part = parts.shift();
|
||||
|
||||
inventreePut(
|
||||
`{% url "api-part-list" %}${part}/`,
|
||||
{
|
||||
category: category,
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
complete: setCategory,
|
||||
}
|
||||
);
|
||||
} else {
|
||||
// We are done!
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
}
|
||||
|
||||
// Start the ball rolling
|
||||
showModalSpinner(opts.modal);
|
||||
setCategory();
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
@ -31,7 +31,6 @@
|
||||
inventreeLoad,
|
||||
inventreePut,
|
||||
launchModalForm,
|
||||
linkButtonsToSelection,
|
||||
loadTableFilters,
|
||||
makeCopyButton,
|
||||
makeDeleteButton,
|
||||
@ -606,7 +605,7 @@ function newSupplierPartFromOrderWizard(e) {
|
||||
/*
|
||||
* Create a new form to order parts based on the list of provided parts.
|
||||
*/
|
||||
function orderParts(parts_list, options) {
|
||||
function orderParts(parts_list, options={}) {
|
||||
|
||||
var parts = [];
|
||||
|
||||
@ -786,7 +785,7 @@ function orderParts(parts_list, options) {
|
||||
supplier_part_filters.manufacturer_part = options.manufacturer_part;
|
||||
}
|
||||
|
||||
// Construct API filtres for the PurchaseOrder field
|
||||
// Construct API filters for the PurchaseOrder field
|
||||
var order_filters = {
|
||||
status: {{ PurchaseOrderStatus.PENDING }},
|
||||
supplier_detail: true,
|
||||
@ -822,6 +821,10 @@ function orderParts(parts_list, options) {
|
||||
|
||||
$(opts.modal).find(`#info-pack-size-${pk}`).remove();
|
||||
|
||||
if (typeof value === 'object') {
|
||||
value = value.pk;
|
||||
}
|
||||
|
||||
if (value != null) {
|
||||
inventreeGet(
|
||||
`/api/company/part/${value}/`,
|
||||
@ -1866,14 +1869,9 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
|
||||
var filters = loadTableFilters('purchaseorderlineitem', options.params);
|
||||
|
||||
setupFilterList(
|
||||
'purchaseorderlineitem',
|
||||
$(table),
|
||||
options.filter_target || '#filter-list-purchase-order-lines',
|
||||
{
|
||||
download: true
|
||||
}
|
||||
);
|
||||
setupFilterList('purchaseorderlineitem', $(table), options.filter_target || '#filter-list-purchase-order-lines', {
|
||||
download: true,
|
||||
});
|
||||
|
||||
function setupCallbacks() {
|
||||
if (options.allow_edit) {
|
||||
@ -2211,12 +2209,4 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
linkButtonsToSelection(
|
||||
table,
|
||||
[
|
||||
'#multi-select-options',
|
||||
]
|
||||
);
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
{% load i18n %}
|
||||
|
||||
/* globals
|
||||
checkPermission,
|
||||
getModelRenderer,
|
||||
inventreeGet,
|
||||
inventreePut,
|
||||
@ -22,40 +23,6 @@ function closeSearchPanel() {
|
||||
}
|
||||
|
||||
|
||||
// Keep track of the roles / permissions available to the current user
|
||||
var search_user_roles = null;
|
||||
|
||||
|
||||
/*
|
||||
* Check if the user has the specified role and permission
|
||||
*/
|
||||
function checkPermission(role, permission='view') {
|
||||
|
||||
if (!search_user_roles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!(role in search_user_roles)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var roles = search_user_roles[role];
|
||||
|
||||
if (!roles) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var found = false;
|
||||
|
||||
search_user_roles[role].forEach(function(p) {
|
||||
if (String(p).valueOf() == String(permission).valueOf()) {
|
||||
found = true;
|
||||
}
|
||||
});
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Callback when the search panel is opened.
|
||||
@ -71,15 +38,6 @@ function openSearchPanel() {
|
||||
|
||||
clearSearchResults();
|
||||
|
||||
// Request user roles if we do not have them
|
||||
if (search_user_roles == null) {
|
||||
inventreeGet('{% url "api-user-roles" %}', {}, {
|
||||
success: function(response) {
|
||||
search_user_roles = response.roles || {};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Callback for text input changed
|
||||
search_input.on('keyup change', searchTextChanged);
|
||||
|
||||
|
@ -30,7 +30,6 @@
|
||||
inventreePut,
|
||||
inventreeSave,
|
||||
launchModalForm,
|
||||
linkButtonsToSelection,
|
||||
loadTableFilters,
|
||||
makeDeleteButton,
|
||||
makeEditButton,
|
||||
@ -1707,6 +1706,121 @@ function locationDetail(row, showLink=true) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Construct a set of custom actions for the stock table
|
||||
*/
|
||||
function makeStockActions(table) {
|
||||
let actions = [
|
||||
{
|
||||
label: 'add',
|
||||
icon: 'fa-plus-circle icon-green',
|
||||
title: '{% trans "Add stock" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
stockAdjustment('add', data, table);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'remove',
|
||||
icon: 'fa-minus-circle icon-red',
|
||||
title: '{% trans "Remove stock" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
stockAdjustment('take', data, table);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'stocktake',
|
||||
icon: 'fa-check-circle icon-blue',
|
||||
title: '{% trans "Count stock" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
stockAdjustment('count', data, table);
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'move',
|
||||
icon: 'fa-exchange-alt icon-blue',
|
||||
title: '{% trans "Transfer stock" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
stockAdjustment('move', data, table);
|
||||
}
|
||||
},
|
||||
{
|
||||
label: 'status',
|
||||
icon: 'fa-info-circle icon-blue',
|
||||
title: '{% trans "Change stock status" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
setStockStatus(data, {table: table});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'merge',
|
||||
icon: 'fa-object-group',
|
||||
title: '{% trans "Merge stock" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
mergeStockItems(data, {
|
||||
success: function(response) {
|
||||
$(table).bootstrapTable('refresh');
|
||||
|
||||
showMessage('{% trans "Merged stock items" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'order',
|
||||
icon: 'fa-shopping-cart',
|
||||
title: '{% trans "Order stock" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
let parts = [];
|
||||
|
||||
data.forEach(function(item) {
|
||||
var part = item.part_detail;
|
||||
|
||||
if (part) {
|
||||
parts.push(part);
|
||||
}
|
||||
});
|
||||
|
||||
orderParts(parts, {});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'assign',
|
||||
icon: 'fa-user-tie',
|
||||
title: '{% trans "Assign to customer" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(data) {
|
||||
assignStockToCustomer(data, {
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
},
|
||||
},
|
||||
{
|
||||
label: 'delete',
|
||||
icon: 'fa-trash-alt icon-red',
|
||||
title: '{% trans "Delete stock" %}',
|
||||
permission: 'stock.delete',
|
||||
callback: function(data) {
|
||||
stockAdjustment('delete', data, table);
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
return actions;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Load data into a stock table with adjustable options.
|
||||
* Fetches data (via AJAX) and loads into a bootstrap table.
|
||||
* Also links in default button callbacks.
|
||||
@ -1746,7 +1860,26 @@ function loadStockTable(table, options) {
|
||||
key: 'item',
|
||||
},
|
||||
singular_name: '{% trans "stock item" %}',
|
||||
plural_name: '{% trans "stock items" %}'
|
||||
plural_name: '{% trans "stock items" %}',
|
||||
barcode_actions: [
|
||||
{
|
||||
icon: 'fa-sitemap',
|
||||
label: 'scantolocation',
|
||||
title: '{% trans "Scan to location" %}',
|
||||
permission: 'stock.change',
|
||||
callback: function(items) {
|
||||
scanItemsIntoLocation(items);
|
||||
}
|
||||
}
|
||||
],
|
||||
custom_actions: [
|
||||
{
|
||||
actions: makeStockActions(table),
|
||||
icon: 'fa-boxes',
|
||||
title: '{% trans "Stock Actions" %}',
|
||||
label: 'stock',
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Override the default values, or add new ones
|
||||
@ -2267,114 +2400,6 @@ function loadStockTable(table, options) {
|
||||
buttons.push('#stock-barcode-options');
|
||||
}
|
||||
|
||||
linkButtonsToSelection(
|
||||
table,
|
||||
buttons,
|
||||
);
|
||||
|
||||
function stockAdjustment(action) {
|
||||
var items = getTableData(table);
|
||||
|
||||
adjustStock(action, items, {
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Automatically link button callbacks
|
||||
if (global_settings.BARCODE_ENABLE) {
|
||||
$('#multi-item-barcode-scan-into-location').click(function() {
|
||||
var selections = getTableData(table);
|
||||
|
||||
var items = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
items.push(item);
|
||||
});
|
||||
|
||||
scanItemsIntoLocation(items);
|
||||
});
|
||||
}
|
||||
|
||||
// Callback for 'stocktake' button
|
||||
$('#multi-item-stocktake').click(function() {
|
||||
stockAdjustment('count');
|
||||
});
|
||||
|
||||
// Callback for 'remove stock' button
|
||||
$('#multi-item-remove').click(function() {
|
||||
stockAdjustment('take');
|
||||
});
|
||||
|
||||
// Callback for 'add stock' button
|
||||
$('#multi-item-add').click(function() {
|
||||
stockAdjustment('add');
|
||||
});
|
||||
|
||||
// Callback for 'move stock' button
|
||||
$('#multi-item-move').click(function() {
|
||||
stockAdjustment('move');
|
||||
});
|
||||
|
||||
// Callback for 'merge stock' button
|
||||
$('#multi-item-merge').click(function() {
|
||||
var items = getTableData(table);
|
||||
|
||||
mergeStockItems(items, {
|
||||
success: function(response) {
|
||||
$(table).bootstrapTable('refresh');
|
||||
|
||||
showMessage('{% trans "Merged stock items" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Callback for 'assign stock' button
|
||||
$('#multi-item-assign').click(function() {
|
||||
|
||||
var items = getTableData(table);
|
||||
|
||||
assignStockToCustomer(items, {
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Callback for 'un-assign stock' button
|
||||
$('#multi-item-order').click(function() {
|
||||
|
||||
var selections = getTableData(table);
|
||||
|
||||
var parts = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
var part = item.part_detail;
|
||||
|
||||
if (part) {
|
||||
parts.push(part);
|
||||
}
|
||||
});
|
||||
|
||||
orderParts(parts, {});
|
||||
});
|
||||
|
||||
// Callback for 'delete stock' button
|
||||
$('#multi-item-delete').click(function() {
|
||||
var selections = getTableData(table);
|
||||
|
||||
var stock = [];
|
||||
|
||||
selections.forEach(function(item) {
|
||||
stock.push(item.pk);
|
||||
});
|
||||
|
||||
stockAdjustment('delete');
|
||||
});
|
||||
|
||||
// Callback for 'change status' button
|
||||
$('#multi-item-status').click(function() {
|
||||
let selections = getTableData(table);
|
||||
@ -2384,35 +2409,9 @@ function loadStockTable(table, options) {
|
||||
items.push(item.pk);
|
||||
});
|
||||
|
||||
if (items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Items" %}',
|
||||
'{% trans "Select one or more stock items" %}'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-info alert-block>
|
||||
{% trans "Selected stock items" %}: ${items.length}
|
||||
</div>`;
|
||||
|
||||
constructForm('{% url "api-stock-change-status" %}', {
|
||||
title: '{% trans "Change Stock Status" %}',
|
||||
method: 'POST',
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
status: {},
|
||||
note: {},
|
||||
},
|
||||
processBeforeUpload: function(data) {
|
||||
data.items = items;
|
||||
return data;
|
||||
},
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
@ -2917,10 +2916,6 @@ function loadStockTrackingTable(table, options) {
|
||||
url: options.url,
|
||||
});
|
||||
|
||||
if (options.buttons) {
|
||||
linkButtonsToSelection(table, options.buttons);
|
||||
}
|
||||
|
||||
table.on('click', '.btn-entry-edit', function() {
|
||||
var button = $(this);
|
||||
|
||||
@ -3131,3 +3126,55 @@ function installStockItem(stock_item_id, part_id, options={}) {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// Perform the specified stock adjustment action against the selected items
|
||||
function stockAdjustment(action, items, table) {
|
||||
adjustStock(action, items, {
|
||||
success: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Set the status of the selected stock items
|
||||
*/
|
||||
function setStockStatus(items, options={}) {
|
||||
|
||||
if (items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Items" %}',
|
||||
'{% trans "Select one or more stock items" %}'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
let id_values = [];
|
||||
|
||||
items.forEach(function(item) {
|
||||
id_values.push(item.pk)
|
||||
});
|
||||
|
||||
let html = `
|
||||
<div class='alert alert-info alert-block>
|
||||
{% trans "Selected stock items" %}: ${items.length}
|
||||
</div>`;
|
||||
|
||||
constructForm('{% url "api-stock-change-status" %}', {
|
||||
title: '{% trans "Change Stock Status" %}',
|
||||
method: 'POST',
|
||||
preFormContent: html,
|
||||
fields: {
|
||||
status: {},
|
||||
note: {},
|
||||
},
|
||||
processBeforeUpload: function(data) {
|
||||
data.items = items;
|
||||
return data;
|
||||
},
|
||||
onSuccess: function() {
|
||||
$(options.table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -52,6 +52,12 @@ function constructHasProjectCodeFilter() {
|
||||
}
|
||||
|
||||
|
||||
// Reset a dictionary of filters for the attachment table
|
||||
function getAttachmentFilters() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// Return a dictionary of filters for the return order table
|
||||
function getReturnOrderFilters() {
|
||||
var filters = {
|
||||
@ -487,6 +493,11 @@ function getBuildTableFilters() {
|
||||
}
|
||||
|
||||
|
||||
function getBuildItemTableFilters() {
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
// Return a dictionary of filters for the "build lines" table
|
||||
function getBuildLineTableFilters() {
|
||||
return {
|
||||
@ -770,6 +781,16 @@ function getAvailableTableFilters(tableKey) {
|
||||
tableKey = tableKey.toLowerCase();
|
||||
|
||||
switch (tableKey) {
|
||||
case 'attachments':
|
||||
return getAttachmentFilters();
|
||||
case 'build':
|
||||
return getBuildTableFilters();
|
||||
case 'builditems':
|
||||
return getBuildItemTableFilters();
|
||||
case 'buildlines':
|
||||
return getBuildLineTableFilters();
|
||||
case 'bom':
|
||||
return getBOMTableFilters();
|
||||
case 'category':
|
||||
return getPartCategoryFilters();
|
||||
case 'company':
|
||||
@ -778,12 +799,6 @@ function getAvailableTableFilters(tableKey) {
|
||||
return getContactFilters();
|
||||
case 'customerstock':
|
||||
return getCustomerStockFilters();
|
||||
case 'bom':
|
||||
return getBOMTableFilters();
|
||||
case 'build':
|
||||
return getBuildTableFilters();
|
||||
case 'buildlines':
|
||||
return getBuildLineTableFilters();
|
||||
case 'location':
|
||||
return getStockLocationFilters();
|
||||
case 'parameters':
|
||||
|
@ -38,10 +38,10 @@ function reloadBootstrapTable(table) {
|
||||
if (tbl.exists()) {
|
||||
tbl.bootstrapTable('refresh');
|
||||
} else {
|
||||
console.error(`Invalid table name passed to reloadTable(): ${table}`);
|
||||
console.error(`Invalid table name passed to reloadBootstrapTable(): ${table}`);
|
||||
}
|
||||
} else {
|
||||
console.error(`Null value passed to reloadTable()`);
|
||||
console.error(`Null value passed to reloadBootstrapTable()`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,25 +220,6 @@ function enableButtons(elements, enabled) {
|
||||
}
|
||||
|
||||
|
||||
/* Link a bootstrap-table object to one or more buttons.
|
||||
* The buttons will only be enabled if there is at least one row selected
|
||||
*/
|
||||
function linkButtonsToSelection(table, buttons) {
|
||||
|
||||
if (typeof table === 'string') {
|
||||
table = $(table);
|
||||
}
|
||||
|
||||
// Initially set the enable state of the buttons
|
||||
enableButtons(buttons, table.bootstrapTable('getSelections').length > 0);
|
||||
|
||||
// Add a callback
|
||||
table.on('check.bs.table uncheck.bs.table check-some.bs.table uncheck-some.bs.table check-all.bs.table uncheck-all.bs.table', function() {
|
||||
enableButtons(buttons, table.bootstrapTable('getSelections').length > 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns true if the input looks like a valid number
|
||||
* @param {String} n
|
||||
@ -474,11 +455,6 @@ $.fn.inventreeTable = function(options) {
|
||||
console.error(`Could not get list of visible columns for table '${tableName}'`);
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally, link buttons to the table selection
|
||||
if (options.buttons) {
|
||||
linkButtonsToSelection(table, options.buttons);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
@ -9,46 +9,7 @@
|
||||
{% endif %}
|
||||
|
||||
<div id='{{ prefix }}button-toolbar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<div class='btn-group' role='group'>
|
||||
{% if barcodes %}
|
||||
<!-- Barcode actions menu -->
|
||||
<div class='btn-group' role='group'>
|
||||
<button id='stock-barcode-options' class='btn btn-primary dropdown-toggle' type='button' data-bs-toggle='dropdown' title='{% trans "Barcode Actions" %}'>
|
||||
<span class='fas fa-qrcode'></span> <span class='caret'></span>
|
||||
</button>
|
||||
<ul class='dropdown-menu'>
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-barcode-scan-into-location' title='{% trans "Scan to Location" %}'><span class='fas fa-sitemap'></span> {% trans "Scan to Location" %}</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% if not read_only %}
|
||||
{% if roles.stock.change or roles.stock.delete %}
|
||||
<div class="btn-group" role="group">
|
||||
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" title='{% trans "Stock Options" %}'>
|
||||
<span class='fas fa-boxes'></span> <span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if roles.stock.change %}
|
||||
<li><a class='dropdown-item' href="#" id='multi-item-add' title='{% trans "Add to selected stock items" %}'><span class='fas fa-plus-circle icon-green'></span> {% trans "Add stock" %}</a></li>
|
||||
<li><a class='dropdown-item' href="#" id='multi-item-remove' title='{% trans "Remove from selected stock items" %}'><span class='fas fa-minus-circle icon-red'></span> {% trans "Remove stock" %}</a></li>
|
||||
<li><a class='dropdown-item' href="#" id='multi-item-stocktake' title='{% trans "Stocktake selected stock items" %}'><span class='fas fa-check-circle icon-green'></span> {% trans "Count stock" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-move' title='{% trans "Move selected stock items" %}'><span class='fas fa-exchange-alt icon-blue'></span> {% trans "Transfer stock" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-status' title='{% trans "Change stock status" %}'><span class='fas fa-info-circle icon-blue'></span> {% trans "Change stock status" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-merge' title='{% trans "Merge selected stock items" %}'><span class='fas fa-object-group'></span> {% trans "Merge stock" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-order' title='{% trans "Order selected items" %}'><span class='fas fa-shopping-cart'></span> {% trans "Order stock" %}</a></li>
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-assign' title='{% trans "Assign to customer" %}'><span class='fas fa-user-tie'></span> {% trans "Assign to customer" %}</a></li>
|
||||
{% endif %}
|
||||
{% if roles.stock.delete %}
|
||||
<li><a class='dropdown-item' href='#' id='multi-item-delete' title='{% trans "Delete selected items" %}'><span class='fas fa-trash-alt icon-red'></span> {% trans "Delete stock" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% include "filter_list.html" with prefix=prefix id="stock" %}
|
||||
</div>
|
||||
</div>
|
||||
{% include "filter_list.html" with prefix=prefix id="stock" %}
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' data-toolbar='#{{ prefix }}button-toolbar' id='{{ prefix }}stock-table'>
|
||||
|
Reference in New Issue
Block a user