mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 13:05:42 +00:00
Printing options (#5786)
* Added backend changes to support printing options * Pass printing options seperatly via kwargs for easier api refactor later * Implemented printing options in CUI * Fix js linting * Use translations for printing dialog * Added docs * Remove plugin and template fields from send printing options * Fix docs * Added tests * Fix tests * Fix options response and added test for it * Fix tests * Bump api version * Update docs * Apply suggestions from code review * Fix api change date
This commit is contained in:
@ -32,6 +32,7 @@
|
||||
setFormGroupVisibility,
|
||||
showFormInput,
|
||||
selectImportFields,
|
||||
updateForm,
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -306,6 +307,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
|
||||
* - localOnly: If true, this field will only be rendered, but not send to the server
|
||||
* - 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
|
||||
@ -315,6 +317,7 @@ function constructDeleteForm(fields, options) {
|
||||
* - reload: Set to true to reload the current page after form success
|
||||
* - confirm: Set to true to require a "confirm" button
|
||||
* - confirmText: Text for confirm button (default = "Confirm")
|
||||
* - disableSuccessMessage: Set to true to suppress the success message if the response contains a success key by accident
|
||||
*
|
||||
*/
|
||||
function constructForm(url, options={}) {
|
||||
@ -720,6 +723,21 @@ function constructFormBody(fields, options) {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* This Method updates an existing form by replacing all form fields with the new ones
|
||||
* @param {*} options new form definition options
|
||||
*/
|
||||
function updateForm(options) {
|
||||
// merge already entered values in the newly constructed form
|
||||
options.data = extractFormData(options.fields, options);
|
||||
|
||||
// remove old submit handlers
|
||||
$(options.modal).off('click', '#modal-form-submit');
|
||||
|
||||
// construct new form
|
||||
constructFormBody(options.fields, options);
|
||||
}
|
||||
|
||||
|
||||
// Add a "confirm" checkbox to the modal
|
||||
// The "submit" button will be disabled unless "confirm" is checked
|
||||
@ -841,6 +859,7 @@ function submitFormData(fields, options) {
|
||||
|
||||
// Ignore visual fields
|
||||
if (field && field.type == 'candy') continue;
|
||||
if (field && field.localOnly === true) continue;
|
||||
|
||||
if (field) {
|
||||
|
||||
@ -1190,7 +1209,7 @@ function handleFormSuccess(response, options) {
|
||||
}
|
||||
|
||||
// Display any messages
|
||||
if (response && (response.success || options.successMessage)) {
|
||||
if (!options.disableSuccessMessage && response && (response.success || options.successMessage)) {
|
||||
showAlertOrCache(
|
||||
response.success || options.successMessage,
|
||||
cache,
|
||||
|
@ -4,6 +4,8 @@
|
||||
/* globals
|
||||
attachSelect,
|
||||
closeModal,
|
||||
constructForm,
|
||||
getFormFieldValue,
|
||||
inventreeGet,
|
||||
makeOptionsList,
|
||||
modalEnable,
|
||||
@ -13,7 +15,9 @@
|
||||
modalSubmit,
|
||||
openModal,
|
||||
showAlertDialog,
|
||||
showApiError,
|
||||
showMessage,
|
||||
updateForm,
|
||||
user_settings,
|
||||
*/
|
||||
|
||||
@ -21,137 +25,11 @@
|
||||
printLabels,
|
||||
*/
|
||||
|
||||
/**
|
||||
* Present the user with the available labels,
|
||||
* and allow them to select which label to print.
|
||||
*
|
||||
* The intent is that the available labels have been requested
|
||||
* (via AJAX) from the server.
|
||||
*/
|
||||
function selectLabel(labels, items, options={}) {
|
||||
// Array of available plugins for label printing
|
||||
var plugins = [];
|
||||
|
||||
// Request a list of available label printing plugins from the server
|
||||
inventreeGet(
|
||||
`/api/plugins/`,
|
||||
{
|
||||
mixin: 'labels',
|
||||
},
|
||||
{
|
||||
async: false,
|
||||
success: function(response) {
|
||||
plugins = response;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var plugin_selection = '';
|
||||
|
||||
if (plugins.length > 0) {
|
||||
plugin_selection =`
|
||||
<div class='form-group'>
|
||||
<label class='control-label requiredField' for='id_plugin'>
|
||||
{% trans "Select Printer" %}
|
||||
</label>
|
||||
<div class='controls'>
|
||||
<select id='id_plugin' class='select form-control' name='plugin'>
|
||||
`;
|
||||
|
||||
plugins.forEach(function(plugin) {
|
||||
var selected = '';
|
||||
if (user_settings['LABEL_DEFAULT_PRINTER'] == plugin.key) {
|
||||
selected = ' selected';
|
||||
}
|
||||
plugin_selection += `<option value='${plugin.key}' title='${plugin.meta.human_name}'${selected}>${plugin.name} - <small>${plugin.meta.human_name}</small></option>`;
|
||||
});
|
||||
|
||||
plugin_selection += `
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
var label_list = makeOptionsList(
|
||||
labels,
|
||||
function(item) {
|
||||
var text = item.name;
|
||||
|
||||
if (item.description) {
|
||||
text += ` - ${item.description}`;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
function(item) {
|
||||
return item.pk;
|
||||
},
|
||||
null,
|
||||
function(item) {
|
||||
if (options.key == 'part')
|
||||
return item.pk == user_settings.DEFAULT_PART_LABEL_TEMPLATE;
|
||||
else if (options.key == 'location')
|
||||
return item.pk == user_settings.DEFAULT_LOCATION_LABEL_TEMPLATE;
|
||||
else if (options.key == 'item')
|
||||
return item.pk == user_settings.DEFAULT_ITEM_LABEL_TEMPLATE;
|
||||
return '';
|
||||
}
|
||||
);
|
||||
|
||||
// Construct form
|
||||
var html = '';
|
||||
|
||||
if (items.length > 0) {
|
||||
let item_name = items.length == 1 ? options.singular_name : options.plural_name;
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
${items.length} ${item_name} {% trans "selected" %}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
html += `
|
||||
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
|
||||
<div class='form-group'>
|
||||
<label class='control-label requiredField' for='id_label'>
|
||||
{% trans "Select Label Template" %}
|
||||
</label>
|
||||
<div class='controls'>
|
||||
<select id='id_label' class='select form-control' name='label'>
|
||||
${label_list}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
${plugin_selection}
|
||||
</form>`;
|
||||
|
||||
openModal({
|
||||
modal: modal,
|
||||
});
|
||||
|
||||
modalEnable(modal, true);
|
||||
modalShowSubmitButton(modal, true);
|
||||
modalSetTitle(modal, '{% trans "Select Label Template" %}');
|
||||
modalSetContent(modal, html);
|
||||
|
||||
attachSelect(modal);
|
||||
|
||||
modalSubmit(modal, function() {
|
||||
|
||||
var label = $(modal).find('#id_label').val();
|
||||
var plugin = $(modal).find('#id_plugin').val();
|
||||
|
||||
closeModal(modal);
|
||||
|
||||
if (options.success) {
|
||||
options.success({
|
||||
// Return the selected label template and plugin
|
||||
label: label,
|
||||
plugin: plugin,
|
||||
});
|
||||
}
|
||||
});
|
||||
const defaultLabelTemplates = {
|
||||
part: user_settings.DEFAULT_PART_LABEL_TEMPLATE,
|
||||
location: user_settings.DEFAULT_LOCATION_LABEL_TEMPLATE,
|
||||
item: user_settings.DEFAULT_ITEM_LABEL_TEMPLATE,
|
||||
line: user_settings.DEFAULT_LINE_LABEL_TEMPLATE,
|
||||
}
|
||||
|
||||
|
||||
@ -166,6 +44,7 @@ function selectLabel(labels, items, options={}) {
|
||||
* - url: The list URL for the particular template type
|
||||
* - items: The list of items to be printed
|
||||
* - key: The key to use in the query parameters
|
||||
* - plural_name: The plural name of the item type
|
||||
*/
|
||||
function printLabels(options) {
|
||||
|
||||
@ -183,9 +62,11 @@ function printLabels(options) {
|
||||
|
||||
params[options.key] = options.items;
|
||||
|
||||
// Request a list of available label templates
|
||||
// Request a list of available label templates from the server
|
||||
let labelTemplates = [];
|
||||
inventreeGet(options.url, params, {
|
||||
success: function(response) {
|
||||
async: false,
|
||||
success: function (response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
@ -194,34 +75,121 @@ function printLabels(options) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label template for printing
|
||||
selectLabel(response, options.items, {
|
||||
success: function(data) {
|
||||
let href = `${options.url}${data.label}/print/?`;
|
||||
|
||||
options.items.forEach(function(item) {
|
||||
href += `${options.key}=${item}&`;
|
||||
});
|
||||
|
||||
href += `plugin=${data.plugin}`;
|
||||
|
||||
inventreeGet(href, {}, {
|
||||
success: function(response) {
|
||||
if (response.file) {
|
||||
// Download the generated file
|
||||
window.open(response.file);
|
||||
} else {
|
||||
showMessage('{% trans "Labels sent to printer" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
plural_name: options.plural_name,
|
||||
singular_name: options.singular_name,
|
||||
key: options.key,
|
||||
});
|
||||
labelTemplates = response;
|
||||
}
|
||||
});
|
||||
|
||||
// Request a list of available label printing plugins from the server
|
||||
let plugins = [];
|
||||
inventreeGet(`/api/plugins/`, { mixin: 'labels' }, {
|
||||
async: false,
|
||||
success: function (response) {
|
||||
plugins = response;
|
||||
}
|
||||
});
|
||||
|
||||
let header_html = "";
|
||||
|
||||
// show how much items are selected if there is more than one item selected
|
||||
if (options.items.length > 1) {
|
||||
header_html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
${options.items.length} ${options.plural_name} {% trans "selected" %}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
const updateFormUrl = (formOptions) => {
|
||||
const plugin = getFormFieldValue("_plugin", formOptions.fields._plugin, formOptions);
|
||||
const labelTemplate = getFormFieldValue("_label_template", formOptions.fields._label_template, formOptions);
|
||||
const params = $.param({ plugin, [options.key]: options.items })
|
||||
formOptions.url = `${options.url}${labelTemplate ?? "1"}/print/?${params}`;
|
||||
}
|
||||
|
||||
const updatePrintingOptions = (formOptions) => {
|
||||
let printingOptionsRes = null;
|
||||
$.ajax({
|
||||
url: formOptions.url,
|
||||
type: "OPTIONS",
|
||||
contentType: "application/json",
|
||||
dataType: "json",
|
||||
accepts: { json: "application/json" },
|
||||
async: false,
|
||||
success: (res) => { printingOptionsRes = res },
|
||||
error: (xhr) => showApiError(xhr, formOptions.url)
|
||||
});
|
||||
|
||||
const printingOptions = printingOptionsRes.actions.POST || {};
|
||||
|
||||
// clear all other options
|
||||
formOptions.fields = {
|
||||
_label_template: formOptions.fields._label_template,
|
||||
_plugin: formOptions.fields._plugin,
|
||||
}
|
||||
|
||||
if (Object.keys(printingOptions).length > 0) {
|
||||
formOptions.fields = {
|
||||
...formOptions.fields,
|
||||
divider: { type: "candy", html: `<hr/><h5>{% trans "Printing Options" %}</h5>` },
|
||||
...printingOptions,
|
||||
};
|
||||
}
|
||||
|
||||
// update form
|
||||
updateForm(formOptions);
|
||||
}
|
||||
|
||||
const printingFormOptions = {
|
||||
title: options.items.length === 1 ? `{% trans "Print label" %}` : `{% trans "Print labels" %}`,
|
||||
submitText: `{% trans "Print" %}`,
|
||||
method: "POST",
|
||||
disableSuccessMessage: true,
|
||||
header_html,
|
||||
fields: {
|
||||
_label_template: {
|
||||
label: `{% trans "Select label template" %}`,
|
||||
type: "choice",
|
||||
localOnly: true,
|
||||
value: defaultLabelTemplates[options.key],
|
||||
choices: labelTemplates.map(t => ({
|
||||
value: t.pk,
|
||||
display_name: `${t.name} - <small>${t.description}</small>`,
|
||||
})),
|
||||
onEdit: (_value, _name, _field, formOptions) => {
|
||||
updateFormUrl(formOptions);
|
||||
}
|
||||
},
|
||||
_plugin: {
|
||||
label: `{% trans "Select plugin" %}`,
|
||||
type: "choice",
|
||||
localOnly: true,
|
||||
value: user_settings.LABEL_DEFAULT_PRINTER || plugins[0].key,
|
||||
choices: plugins.map(p => ({
|
||||
value: p.key,
|
||||
display_name: `${p.name} - <small>${p.meta.human_name}</small>`,
|
||||
})),
|
||||
onEdit: (_value, _name, _field, formOptions) => {
|
||||
updateFormUrl(formOptions);
|
||||
updatePrintingOptions(formOptions);
|
||||
}
|
||||
},
|
||||
},
|
||||
onSuccess: (response) => {
|
||||
if (response.file) {
|
||||
// Download the generated file
|
||||
window.open(response.file);
|
||||
} else {
|
||||
showMessage('{% trans "Labels sent to printer" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// construct form
|
||||
constructForm(null, printingFormOptions);
|
||||
|
||||
// fetch the options for the default plugin
|
||||
updateFormUrl(printingFormOptions);
|
||||
updatePrintingOptions(printingFormOptions);
|
||||
}
|
||||
|
Reference in New Issue
Block a user