mirror of
https://github.com/inventree/InvenTree.git
synced 2025-07-01 03:00:54 +00:00
Split dynamic javascript files into two separate directories
- One gets translated and is served statically - One does not get translated and is served dynamically - Add CI step
This commit is contained in:
196
InvenTree/templates/js/translated/api.js
Normal file
196
InvenTree/templates/js/translated/api.js
Normal file
@ -0,0 +1,196 @@
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
var jQuery = window.$;
|
||||
|
||||
$.urlParam = function(name){
|
||||
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
|
||||
if (results==null) {
|
||||
return null;
|
||||
}
|
||||
return decodeURI(results[1]) || 0;
|
||||
}
|
||||
|
||||
// using jQuery
|
||||
function getCookie(name) {
|
||||
var cookieValue = null;
|
||||
if (document.cookie && document.cookie != '') {
|
||||
var cookies = document.cookie.split(';');
|
||||
for (var i = 0; i < cookies.length; i++) {
|
||||
var cookie = jQuery.trim(cookies[i]);
|
||||
// Does this cookie string begin with the name we want?
|
||||
if (cookie.substring(0, name.length + 1) == (name + '=')) {
|
||||
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cookieValue;
|
||||
}
|
||||
|
||||
function inventreeGet(url, filters={}, options={}) {
|
||||
|
||||
// Middleware token required for data update
|
||||
//var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
|
||||
return $.ajax({
|
||||
beforeSend: function(xhr, settings) {
|
||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||
},
|
||||
url: url,
|
||||
type: 'GET',
|
||||
data: filters,
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
success: function(response) {
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
}
|
||||
},
|
||||
error: function(xhr, ajaxOptions, thrownError) {
|
||||
console.error('Error on GET at ' + url);
|
||||
console.error(thrownError);
|
||||
if (options.error) {
|
||||
options.error({
|
||||
error: thrownError
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function inventreeFormDataUpload(url, data, options={}) {
|
||||
/* Upload via AJAX using the FormData approach.
|
||||
*
|
||||
* Note that the following AJAX parameters are required for FormData upload
|
||||
*
|
||||
* processData: false
|
||||
* contentType: false
|
||||
*/
|
||||
|
||||
// CSRF cookie token
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
|
||||
return $.ajax({
|
||||
beforeSend: function(xhr, settings) {
|
||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||
},
|
||||
url: url,
|
||||
method: options.method || 'POST',
|
||||
data: data,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
success: function(data, status, xhr) {
|
||||
if (options.success) {
|
||||
options.success(data, status, xhr);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log('Form data upload failure: ' + status);
|
||||
|
||||
if (options.error) {
|
||||
options.error(xhr, status, error);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function inventreePut(url, data={}, options={}) {
|
||||
|
||||
var method = options.method || 'PUT';
|
||||
|
||||
// Middleware token required for data update
|
||||
//var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
|
||||
var csrftoken = getCookie('csrftoken');
|
||||
|
||||
return $.ajax({
|
||||
beforeSend: function(xhr, settings) {
|
||||
xhr.setRequestHeader('X-CSRFToken', csrftoken);
|
||||
},
|
||||
url: url,
|
||||
type: method,
|
||||
data: JSON.stringify(data),
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
success: function(response, status) {
|
||||
if (options.success) {
|
||||
options.success(response, status);
|
||||
}
|
||||
if (options.reloadOnSuccess) {
|
||||
location.reload();
|
||||
}
|
||||
},
|
||||
error: function(xhr, ajaxOptions, thrownError) {
|
||||
if (options.error) {
|
||||
options.error(xhr, ajaxOptions, thrownError);
|
||||
} else {
|
||||
console.error(`Error on ${method} to '${url}' - STATUS ${xhr.status}`);
|
||||
console.error(thrownError);
|
||||
}
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
if (options.complete) {
|
||||
options.complete(xhr, status);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function inventreeDelete(url, options={}) {
|
||||
/*
|
||||
* Delete a record
|
||||
*/
|
||||
|
||||
options = options || {};
|
||||
|
||||
options.method = 'DELETE';
|
||||
|
||||
return inventreePut(url, {}, options);
|
||||
}
|
||||
|
||||
|
||||
function showApiError(xhr) {
|
||||
|
||||
var title = null;
|
||||
var message = null;
|
||||
|
||||
switch (xhr.status) {
|
||||
case 0: // No response
|
||||
title = '{% trans "No Response" %}';
|
||||
message = '{% trans "No response from the InvenTree server" %}';
|
||||
break;
|
||||
case 400: // Bad request
|
||||
// Note: Normally error code 400 is handled separately,
|
||||
// and should now be shown here!
|
||||
title = '{% trans "Error 400: Bad request" %}';
|
||||
message = '{% trans "API request returned error code 400" %}';
|
||||
break;
|
||||
case 401: // Not authenticated
|
||||
title = '{% trans "Error 401: Not Authenticated" %}';
|
||||
message = '{% trans "Authentication credentials not supplied" %}';
|
||||
break;
|
||||
case 403: // Permission denied
|
||||
title = '{% trans "Error 403: Permission Denied" %}';
|
||||
message = '{% trans "You do not have the required permissions to access this function" %}';
|
||||
break;
|
||||
case 404: // Resource not found
|
||||
title = '{% trans "Error 404: Resource Not Found" %}';
|
||||
message = '{% trans "The requested resource could not be located on the server" %}';
|
||||
break;
|
||||
case 408: // Timeout
|
||||
title = '{% trans "Error 408: Timeout" %}';
|
||||
message = '{% trans "Connection timeout while requesting data from server" %}';
|
||||
break;
|
||||
default:
|
||||
title = '{% trans "Unhandled Error Code" %}';
|
||||
message = `{% trans "Error code" %}: ${xhr.status}`;
|
||||
break;
|
||||
}
|
||||
|
||||
message += "<hr>";
|
||||
message += renderErrorMessage(xhr);
|
||||
|
||||
showAlertDialog(title, message);
|
||||
}
|
86
InvenTree/templates/js/translated/attachment.js
Normal file
86
InvenTree/templates/js/translated/attachment.js
Normal file
@ -0,0 +1,86 @@
|
||||
{% load i18n %}
|
||||
|
||||
function reloadAttachmentTable() {
|
||||
|
||||
$('#attachment-table').bootstrapTable("refresh");
|
||||
}
|
||||
|
||||
|
||||
function loadAttachmentTable(url, options) {
|
||||
|
||||
var table = options.table || '#attachment-table';
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
name: options.name || 'attachments',
|
||||
formatNoMatches: function() { return '{% trans "No attachments found" %}'},
|
||||
sortable: true,
|
||||
search: false,
|
||||
queryParams: options.filters || {},
|
||||
onPostBody: function() {
|
||||
// Add callback for 'edit' button
|
||||
$(table).find('.button-attachment-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
if (options.onEdit) {
|
||||
options.onEdit(pk);
|
||||
}
|
||||
});
|
||||
|
||||
// Add callback for 'delete' button
|
||||
$(table).find('.button-attachment-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
if (options.onDelete) {
|
||||
options.onDelete(pk);
|
||||
}
|
||||
});
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'attachment',
|
||||
title: '{% trans "File" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
var split = value.split('/');
|
||||
|
||||
return renderLink(split[split.length - 1], value);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'comment',
|
||||
title: '{% trans "Comment" %}',
|
||||
},
|
||||
{
|
||||
field: 'upload_date',
|
||||
title: '{% trans "Upload Date" %}',
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
formatter: function(value, row) {
|
||||
var html = '';
|
||||
|
||||
html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton(
|
||||
'fa-edit icon-blue',
|
||||
'button-attachment-edit',
|
||||
row.pk,
|
||||
'{% trans "Edit attachment" %}',
|
||||
);
|
||||
|
||||
html += makeIconButton(
|
||||
'fa-trash-alt icon-red',
|
||||
'button-attachment-delete',
|
||||
row.pk,
|
||||
'{% trans "Delete attachment" %}',
|
||||
);
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
612
InvenTree/templates/js/translated/barcode.js
Normal file
612
InvenTree/templates/js/translated/barcode.js
Normal file
@ -0,0 +1,612 @@
|
||||
{% load i18n %}
|
||||
|
||||
function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
/*
|
||||
* Generate HTML for a barcode input
|
||||
*/
|
||||
|
||||
placeholderText = placeholderText || '{% trans "Scan barcode data here using wedge scanner" %}';
|
||||
|
||||
hintText = hintText || '{% trans "Enter barcode data" %}';
|
||||
|
||||
var html = `
|
||||
<div class='form-group'>
|
||||
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'>
|
||||
<span class='fas fa-qrcode'></span>
|
||||
</span>
|
||||
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||
</div>
|
||||
<div id='hint_barcode_data' class='help-block'>${hintText}</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
function makeNotesField(options={}) {
|
||||
|
||||
var tooltip = options.tooltip || '{% trans "Enter optional notes for stock transfer" %}';
|
||||
var placeholder = options.placeholder || '{% trans "Enter notes" %}';
|
||||
|
||||
return `
|
||||
<div class='form-group'>
|
||||
<label class='control-label' for='notes'>{% trans "Notes" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'>
|
||||
<span class='fas fa-sticky-note'></span>
|
||||
</span>
|
||||
<input id='notes' class='textinput textInput form-control' type='text' name='notes' placeholder='${placeholder}'>
|
||||
</div>
|
||||
<div id='hint_notes' class='help_block'>${tooltip}</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* POST data to the server, and handle standard responses.
|
||||
*/
|
||||
function postBarcodeData(barcode_data, options={}) {
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
|
||||
var url = options.url || '/api/barcode/';
|
||||
|
||||
var data = options.data || {};
|
||||
|
||||
data.barcode = barcode_data;
|
||||
|
||||
inventreePut(
|
||||
url,
|
||||
data,
|
||||
{
|
||||
method: 'POST',
|
||||
error: function() {
|
||||
enableBarcodeInput(modal, true);
|
||||
showBarcodeMessage(modal, '{% trans "Server error" %}');
|
||||
},
|
||||
success: function(response, status) {
|
||||
modalEnable(modal, false);
|
||||
enableBarcodeInput(modal, true);
|
||||
|
||||
if (status == 'success') {
|
||||
|
||||
if ('success' in response) {
|
||||
if (options.onScan) {
|
||||
options.onScan(response);
|
||||
}
|
||||
} else if ('error' in response) {
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
response.error,
|
||||
'warning'
|
||||
);
|
||||
} else {
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "Unknown response from server" %}',
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Invalid response returned from server
|
||||
showInvalidResponseError(modal, response, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function showBarcodeMessage(modal, message, style='danger') {
|
||||
|
||||
var html = `<div class='alert alert-block alert-${style}'>`;
|
||||
|
||||
html += message;
|
||||
|
||||
html += "</div>";
|
||||
|
||||
$(modal + ' #barcode-error-message').html(html);
|
||||
}
|
||||
|
||||
|
||||
function showInvalidResponseError(modal, response, status) {
|
||||
showBarcodeMessage(modal, `{% trans "Invalid server response" %}<br>{% trans "Status" %}: '${status}'`);
|
||||
}
|
||||
|
||||
|
||||
function enableBarcodeInput(modal, enabled=true) {
|
||||
|
||||
var barcode = $(modal + ' #barcode');
|
||||
|
||||
barcode.prop('disabled', !enabled);
|
||||
|
||||
modalEnable(modal, enabled);
|
||||
|
||||
barcode.focus();
|
||||
}
|
||||
|
||||
function getBarcodeData(modal) {
|
||||
|
||||
modal = modal || '#modal-form';
|
||||
|
||||
var el = $(modal + ' #barcode');
|
||||
|
||||
var barcode = el.val();
|
||||
|
||||
el.val('');
|
||||
el.focus();
|
||||
|
||||
return barcode.trim();
|
||||
}
|
||||
|
||||
|
||||
function barcodeDialog(title, options={}) {
|
||||
/*
|
||||
* Handle a barcode display dialog.
|
||||
*/
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
function sendBarcode() {
|
||||
var barcode = getBarcodeData(modal);
|
||||
|
||||
if (barcode && barcode.length > 0) {
|
||||
|
||||
postBarcodeData(barcode, options);
|
||||
}
|
||||
}
|
||||
|
||||
$(modal).on('shown.bs.modal', function() {
|
||||
$(modal + ' .modal-form-content').scrollTop(0);
|
||||
|
||||
var barcode = $(modal + ' #barcode');
|
||||
|
||||
// Handle 'enter' key on barcode
|
||||
barcode.keyup(function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
if (event.which == 10 || event.which == 13) {
|
||||
sendBarcode();
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure the barcode field has focus
|
||||
barcode.focus();
|
||||
|
||||
var form = $(modal).find('.js-modal-form');
|
||||
|
||||
// Override form submission
|
||||
form.submit(function() {
|
||||
return false;
|
||||
});
|
||||
|
||||
// Callback for when the "submit" button is pressed on the modal
|
||||
modalSubmit(modal, function() {
|
||||
if (options.onSubmit) {
|
||||
options.onSubmit();
|
||||
}
|
||||
});
|
||||
|
||||
if (options.onShow) {
|
||||
options.onShow();
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
modalSetTitle(modal, title);
|
||||
|
||||
if (options.onSubmit) {
|
||||
modalShowSubmitButton(modal, true);
|
||||
} else {
|
||||
modalShowSubmitButton(modal, false);
|
||||
}
|
||||
|
||||
var content = '';
|
||||
|
||||
content += `<div class='alert alert-info alert-block'>{% trans "Scan barcode data below" %}</div>`;
|
||||
|
||||
content += `<div id='barcode-error-message'></div>`;
|
||||
content += `<form class='js-modal-form' method='post'>`;
|
||||
|
||||
// Optional content before barcode input
|
||||
content += `<div class='container' id='barcode-header'>`;
|
||||
content += options.headerContent || '';
|
||||
content += `</div>`;
|
||||
|
||||
content += makeBarcodeInput();
|
||||
|
||||
if (options.extraFields) {
|
||||
content += options.extraFields;
|
||||
}
|
||||
|
||||
content += `</form>`;
|
||||
|
||||
// Optional content after barcode input
|
||||
content += `<div class='container' id='barcode-footer'>`;
|
||||
content += options.footerContent || '';
|
||||
content += '</div>';
|
||||
|
||||
modalSetContent(modal, content);
|
||||
|
||||
$(modal).modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
});
|
||||
|
||||
if (options.preShow) {
|
||||
options.preShow();
|
||||
}
|
||||
|
||||
$(modal).modal('show');
|
||||
}
|
||||
|
||||
|
||||
function barcodeScanDialog() {
|
||||
/*
|
||||
* Perform a barcode scan,
|
||||
* and (potentially) redirect the browser
|
||||
*/
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
barcodeDialog(
|
||||
"Scan Barcode",
|
||||
{
|
||||
onScan: function(response) {
|
||||
if ('url' in response) {
|
||||
$(modal).modal('hide');
|
||||
|
||||
// Redirect to the URL!
|
||||
window.location.href = response.url;
|
||||
} else {
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "No URL in response" %}',
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Dialog for linking a particular barcode to a stock item.
|
||||
*/
|
||||
function linkBarcodeDialog(stockitem, options={}) {
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
barcodeDialog(
|
||||
"{% trans 'Link Barcode to Stock Item' %}",
|
||||
{
|
||||
url: '/api/barcode/link/',
|
||||
data: {
|
||||
stockitem: stockitem,
|
||||
},
|
||||
onScan: function(response) {
|
||||
|
||||
$(modal).modal('hide');
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove barcode association from a device.
|
||||
*/
|
||||
function unlinkBarcode(stockitem) {
|
||||
|
||||
var html = `<b>{% trans "Unlink Barcode" %}</b><br>`;
|
||||
|
||||
html += "{% trans 'This will remove the association between this stock item and the barcode' %}";
|
||||
|
||||
showQuestionDialog(
|
||||
"{% trans 'Unlink Barcode' %}",
|
||||
html,
|
||||
{
|
||||
accept_text: "{% trans 'Unlink' %}",
|
||||
accept: function() {
|
||||
inventreePut(
|
||||
`/api/stock/${stockitem}/`,
|
||||
{
|
||||
// Clear the UID field
|
||||
uid: '',
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function(response, status) {
|
||||
location.reload();
|
||||
},
|
||||
},
|
||||
);
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display dialog to check multiple stock items in to a stock location.
|
||||
*/
|
||||
function barcodeCheckIn(location_id, options={}) {
|
||||
|
||||
var modal = '#modal-form';
|
||||
|
||||
// List of items we are going to checkin
|
||||
var items = [];
|
||||
|
||||
|
||||
function reloadTable() {
|
||||
|
||||
modalEnable(modal, false);
|
||||
|
||||
// Remove click listeners
|
||||
$(modal + ' .button-item-remove').unbind('click');
|
||||
|
||||
var table = $(modal + ' #items-table-div');
|
||||
|
||||
var html = `
|
||||
<table class='table table-condensed table-striped' id='items-table'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Location" %}</th>
|
||||
<th>{% trans "Quantity" %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>`;
|
||||
|
||||
items.forEach(function(item) {
|
||||
html += `
|
||||
<tr pk='${item.pk}'>
|
||||
<td>${imageHoverIcon(item.part_detail.thumbnail)} ${item.part_detail.name}</td>
|
||||
<td>${item.location_detail.name}</td>
|
||||
<td>${item.quantity}</td>
|
||||
<td>${makeIconButton('fa-times-circle icon-red', 'button-item-remove', item.pk, '{% trans "Remove stock item" %}')}</td>
|
||||
</tr>`;
|
||||
});
|
||||
|
||||
html += `
|
||||
</tbody>
|
||||
</table>`;
|
||||
|
||||
table.html(html);
|
||||
|
||||
modalEnable(modal, items.length > 0);
|
||||
|
||||
$(modal + ' #barcode').focus();
|
||||
|
||||
$(modal + ' .button-item-remove').unbind('click').on('mouseup', function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
var match = false;
|
||||
|
||||
for (var ii = 0; ii < items.length; ii++) {
|
||||
if (pk.toString() == items[ii].pk.toString()) {
|
||||
items.splice(ii, 1);
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (match) {
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
var table = `<div class='container' id='items-table-div' style='width: 80%; float: left;'></div>`;
|
||||
|
||||
// Extra form fields
|
||||
var extra = makeNotesField();
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Check Stock Items into Location" %}',
|
||||
{
|
||||
headerContent: table,
|
||||
preShow: function() {
|
||||
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||
modalEnable(modal, false);
|
||||
reloadTable();
|
||||
},
|
||||
onShow: function() {
|
||||
},
|
||||
extraFields: extra,
|
||||
onSubmit: function() {
|
||||
|
||||
// Called when the 'check-in' button is pressed
|
||||
|
||||
var data = {location: location_id};
|
||||
|
||||
// Extract 'notes' field
|
||||
data.notes = $(modal + ' #notes').val();
|
||||
|
||||
var entries = [];
|
||||
|
||||
items.forEach(function(item) {
|
||||
entries.push({
|
||||
pk: item.pk,
|
||||
quantity: item.quantity,
|
||||
});
|
||||
});
|
||||
|
||||
data.items = entries;
|
||||
|
||||
inventreePut(
|
||||
"{% url 'api-stock-transfer' %}",
|
||||
data,
|
||||
{
|
||||
method: 'POST',
|
||||
success: function(response, status) {
|
||||
// Hide the modal
|
||||
$(modal).modal('hide');
|
||||
if (status == 'success' && 'success' in response) {
|
||||
|
||||
showAlertOrCache('alert-success', response.success, true);
|
||||
location.reload();
|
||||
} else {
|
||||
showAlertOrCache('alert-success', '{% trans "Error transferring stock" %}', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
},
|
||||
onScan: function(response) {
|
||||
if ('stockitem' in response) {
|
||||
stockitem = response.stockitem;
|
||||
|
||||
var duplicate = false;
|
||||
|
||||
items.forEach(function(item) {
|
||||
if (item.pk == stockitem.pk) {
|
||||
duplicate = true;
|
||||
}
|
||||
});
|
||||
|
||||
if (duplicate) {
|
||||
showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', "warning");
|
||||
} else {
|
||||
|
||||
if (stockitem.location == location_id) {
|
||||
showBarcodeMessage(modal, '{% trans "Stock Item already in this location" %}');
|
||||
return;
|
||||
}
|
||||
|
||||
// Add this stock item to the list
|
||||
items.push(stockitem);
|
||||
|
||||
showBarcodeMessage(modal, '{% trans "Added stock item" %}', "success");
|
||||
|
||||
reloadTable();
|
||||
}
|
||||
|
||||
} else {
|
||||
// Barcode does not match a stock item
|
||||
showBarcodeMessage(modal, '{% trans "Barcode does not match Stock Item" %}', "warning");
|
||||
}
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display dialog to check a single stock item into a stock location
|
||||
*/
|
||||
function scanItemsIntoLocation(item_id_list, options={}) {
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
|
||||
var stock_location = null;
|
||||
|
||||
// Extra form fields
|
||||
var extra = makeNotesField();
|
||||
|
||||
// Header contentfor
|
||||
var header = `
|
||||
<div id='header-div'>
|
||||
</div>
|
||||
`;
|
||||
|
||||
function updateLocationInfo(location) {
|
||||
var div = $(modal + ' #header-div');
|
||||
|
||||
if (stock_location && stock_location.pk) {
|
||||
div.html(`
|
||||
<div class='alert alert-block alert-info'>
|
||||
<b>{% trans "Location" %}</b></br>
|
||||
${stock_location.name}<br>
|
||||
<i>${stock_location.description}</i>
|
||||
</div>
|
||||
`);
|
||||
} else {
|
||||
div.html('');
|
||||
}
|
||||
}
|
||||
|
||||
barcodeDialog(
|
||||
'{% trans "Check Into Location" %}',
|
||||
{
|
||||
headerContent: header,
|
||||
extraFields: extra,
|
||||
preShow: function() {
|
||||
modalSetSubmitText(modal, '{% trans "Check In" %}');
|
||||
modalEnable(modal, false);
|
||||
},
|
||||
onShow: function() {
|
||||
},
|
||||
onSubmit: function() {
|
||||
// Called when the 'check-in' button is pressed
|
||||
if (!stock_location) {
|
||||
return;
|
||||
}
|
||||
|
||||
var items = [];
|
||||
|
||||
item_id_list.forEach(function(pk) {
|
||||
items.push({
|
||||
pk: pk,
|
||||
});
|
||||
})
|
||||
|
||||
var data = {
|
||||
location: stock_location.pk,
|
||||
notes: $(modal + ' #notes').val(),
|
||||
items: items,
|
||||
};
|
||||
|
||||
// Send API request
|
||||
inventreePut(
|
||||
'{% url "api-stock-transfer" %}',
|
||||
data,
|
||||
{
|
||||
method: 'POST',
|
||||
success: function(response, status) {
|
||||
// First hide the modal
|
||||
$(modal).modal('hide');
|
||||
|
||||
if (status == 'success' && 'success' in response) {
|
||||
showAlertOrCache('alert-success', response.success, true);
|
||||
location.reload();
|
||||
} else {
|
||||
showAlertOrCache('alert-danger', '{% trans "Error transferring stock" %}', false);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
},
|
||||
onScan: function(response) {
|
||||
updateLocationInfo(null);
|
||||
if ('stocklocation' in response) {
|
||||
// Barcode corresponds to a StockLocation
|
||||
stock_location = response.stocklocation;
|
||||
|
||||
updateLocationInfo(stock_location);
|
||||
modalEnable(modal, true);
|
||||
|
||||
} else {
|
||||
// Barcode does *NOT* correspond to a StockLocation
|
||||
showBarcodeMessage(
|
||||
modal,
|
||||
'{% trans "Barcode does not match a valid location" %}',
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
560
InvenTree/templates/js/translated/bom.js
Normal file
560
InvenTree/templates/js/translated/bom.js
Normal file
@ -0,0 +1,560 @@
|
||||
{% load i18n %}
|
||||
|
||||
/* BOM management functions.
|
||||
* Requires follwing files to be loaded first:
|
||||
* - api.js
|
||||
* - part.js
|
||||
* - modals.js
|
||||
*/
|
||||
|
||||
|
||||
function reloadBomTable(table, options) {
|
||||
|
||||
table.bootstrapTable('refresh');
|
||||
}
|
||||
|
||||
|
||||
function removeRowFromBomWizard(e) {
|
||||
/* Remove a row from BOM upload wizard
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var table = $(src).closest('table');
|
||||
|
||||
// Which column was clicked?
|
||||
var row = $(src).closest('tr');
|
||||
|
||||
row.remove();
|
||||
|
||||
var rowNum = 1;
|
||||
var colNum = 0;
|
||||
|
||||
table.find('tr').each(function() {
|
||||
|
||||
colNum++;
|
||||
|
||||
if (colNum >= 3) {
|
||||
var cell = $(this).find('td:eq(1)');
|
||||
cell.text(rowNum++);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function removeColFromBomWizard(e) {
|
||||
/* Remove a column from BOM upload wizard
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
// Which column was clicked?
|
||||
var col = $(src).closest('th').index();
|
||||
|
||||
var table = $(src).closest('table');
|
||||
|
||||
table.find('tr').each(function() {
|
||||
this.removeChild(this.cells[col]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function newPartFromBomWizard(e) {
|
||||
/* Create a new part directly from the BOM wizard.
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var row = $(src).closest('tr');
|
||||
|
||||
launchModalForm('/part/new/', {
|
||||
data: {
|
||||
'description': row.attr('part-description'),
|
||||
'name': row.attr('part-name'),
|
||||
},
|
||||
success: function(response) {
|
||||
/* A new part has been created! Push it as an option.
|
||||
*/
|
||||
|
||||
var select = row.attr('part-select');
|
||||
|
||||
var option = new Option(response.text, response.pk, true, true);
|
||||
$(select).append(option).trigger('change');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadBomTable(table, options) {
|
||||
/* Load a BOM table with some configurable options.
|
||||
*
|
||||
* Following options are available:
|
||||
* editable - Should the BOM table be editable?
|
||||
* bom_url - Address to request BOM data from
|
||||
* part_url - Address to request Part data from
|
||||
* parent_id - Parent ID of the owning part
|
||||
*
|
||||
* BOM data are retrieved from the server via AJAX query
|
||||
*/
|
||||
|
||||
var params = {
|
||||
part: options.parent_id,
|
||||
ordering: 'name',
|
||||
}
|
||||
|
||||
if (options.part_detail) {
|
||||
params.part_detail = true;
|
||||
}
|
||||
|
||||
params.sub_part_detail = true;
|
||||
|
||||
var filters = {};
|
||||
|
||||
if (!options.disableFilters) {
|
||||
filters = loadTableFilters('bom');
|
||||
}
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList('bom', $(table));
|
||||
|
||||
// Construct the table columns
|
||||
|
||||
var cols = [];
|
||||
|
||||
if (options.editable) {
|
||||
cols.push({
|
||||
field: 'ID',
|
||||
title: '',
|
||||
checkbox: true,
|
||||
visible: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
// Disable checkbox if the row is defined for a *different* part!
|
||||
if (row.part != options.parent_id) {
|
||||
return {
|
||||
disabled: true,
|
||||
};
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Set the parent ID of the multi-level table.
|
||||
// We prepend this with the literal string value 'top-level-',
|
||||
// because otherwise the unfortunate situation where BomItem.pk == BomItem.part.pk
|
||||
// AND THIS BREAKS EVERYTHING
|
||||
var parent_id = `top-level-${options.parent_id}`;
|
||||
|
||||
// Part column
|
||||
cols.push(
|
||||
{
|
||||
field: 'sub_part',
|
||||
title: '{% trans "Part" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
var url = `/part/${row.sub_part}/`;
|
||||
var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url);
|
||||
|
||||
var sub_part = row.sub_part_detail;
|
||||
|
||||
html += makePartIcons(row.sub_part_detail);
|
||||
|
||||
// Display an extra icon if this part is an assembly
|
||||
if (sub_part.assembly) {
|
||||
var text = `<span title='{% trans "Open subassembly" %}' class='fas fa-stream label-right'></span>`;
|
||||
|
||||
html += renderLink(text, `/part/${row.sub_part}/bom/`);
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
// Part description
|
||||
cols.push(
|
||||
{
|
||||
field: 'sub_part_detail.description',
|
||||
title: '{% trans "Description" %}',
|
||||
}
|
||||
);
|
||||
|
||||
// Part reference
|
||||
cols.push({
|
||||
field: 'reference',
|
||||
title: '{% trans "Reference" %}',
|
||||
searchable: true,
|
||||
sortable: true,
|
||||
});
|
||||
|
||||
// Part quantity
|
||||
cols.push({
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
var text = value;
|
||||
|
||||
// The 'value' is a text string with (potentially) multiple trailing zeros
|
||||
// Let's make it a bit more pretty
|
||||
text = parseFloat(text);
|
||||
|
||||
if (row.optional) {
|
||||
text += ' ({% trans "Optional" %})';
|
||||
}
|
||||
|
||||
if (row.overage) {
|
||||
text += `<small> (${row.overage}) </small>`;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
});
|
||||
|
||||
cols.push(
|
||||
{
|
||||
field: 'sub_part_detail.stock',
|
||||
title: '{% trans "Available" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var url = `/part/${row.sub_part_detail.pk}/stock/`;
|
||||
var text = value;
|
||||
|
||||
if (value == null || value <= 0) {
|
||||
text = `<span class='label label-warning'>{% trans "No Stock" %}</span>`;
|
||||
}
|
||||
|
||||
return renderLink(text, url);
|
||||
}
|
||||
});
|
||||
|
||||
cols.push(
|
||||
{
|
||||
field: 'purchase_price_range',
|
||||
title: '{% trans "Purchase Price Range" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
});
|
||||
|
||||
cols.push(
|
||||
{
|
||||
field: 'purchase_price_avg',
|
||||
title: '{% trans "Purchase Price Average" %}',
|
||||
searchable: false,
|
||||
sortable: true,
|
||||
});
|
||||
|
||||
cols.push(
|
||||
{
|
||||
field: 'price_range',
|
||||
title: '{% trans "Buy Price" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
return value;
|
||||
} else {
|
||||
return "<span class='warning-msg'>{% trans 'No pricing available' %}</span>";
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cols.push({
|
||||
field: 'optional',
|
||||
title: '{% trans "Optional" %}',
|
||||
searchable: false,
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
}
|
||||
});
|
||||
|
||||
cols.push({
|
||||
field: 'allow_variants',
|
||||
title: '{% trans "Allow Variants" %}',
|
||||
formatter: function(value) {
|
||||
return yesNoLabel(value);
|
||||
}
|
||||
})
|
||||
|
||||
cols.push({
|
||||
field: 'inherited',
|
||||
title: '{% trans "Inherited" %}',
|
||||
searchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
// This BOM item *is* inheritable, but is defined for this BOM
|
||||
if (!row.inherited) {
|
||||
return yesNoLabel(false);
|
||||
} else if (row.part == options.parent_id) {
|
||||
return '{% trans "Inherited" %}';
|
||||
} else {
|
||||
// If this BOM item is inherited from a parent part
|
||||
return renderLink(
|
||||
'{% trans "View BOM" %}',
|
||||
`/part/${row.part}/bom/`,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
cols.push(
|
||||
{
|
||||
'field': 'can_build',
|
||||
'title': '{% trans "Can Build" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
var can_build = 0;
|
||||
|
||||
if (row.quantity > 0) {
|
||||
can_build = row.sub_part_detail.stock / row.quantity;
|
||||
}
|
||||
|
||||
return +can_build.toFixed(2);
|
||||
},
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
// Function to sort the "can build" quantity
|
||||
var cb_a = 0;
|
||||
var cb_b = 0;
|
||||
|
||||
if (rowA.quantity > 0) {
|
||||
cb_a = rowA.sub_part_detail.stock / rowA.quantity;
|
||||
}
|
||||
|
||||
if (rowB.quantity > 0) {
|
||||
cb_b = rowB.sub_part_detail.stock / rowB.quantity;
|
||||
}
|
||||
|
||||
return (cb_a > cb_b) ? 1 : -1;
|
||||
},
|
||||
sortable: true,
|
||||
}
|
||||
)
|
||||
|
||||
// Part notes
|
||||
cols.push(
|
||||
{
|
||||
field: 'note',
|
||||
title: '{% trans "Notes" %}',
|
||||
searchable: true,
|
||||
sortable: true,
|
||||
}
|
||||
);
|
||||
|
||||
if (options.editable) {
|
||||
|
||||
cols.push({
|
||||
title: '{% trans "Actions" %}',
|
||||
switchable: false,
|
||||
field: 'pk',
|
||||
visible: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
if (row.part == options.parent_id) {
|
||||
|
||||
var bValidate = `<button title='{% trans "Validate BOM Item" %}' class='bom-validate-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-check-circle icon-blue'/></button>`;
|
||||
|
||||
var bValid = `<span title='{% trans "This line has been validated" %}' class='fas fa-check-double icon-green'/>`;
|
||||
|
||||
var bEdit = `<button title='{% trans "Edit BOM Item" %}' class='bom-edit-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-edit'></span></button>`;
|
||||
|
||||
var bDelt = `<button title='{% trans "Delete BOM Item" %}' class='bom-delete-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-trash-alt icon-red'></span></button>`;
|
||||
|
||||
var html = "<div class='btn-group' role='group'>";
|
||||
|
||||
html += bEdit;
|
||||
html += bDelt;
|
||||
|
||||
if (!row.validated) {
|
||||
html += bValidate;
|
||||
} else {
|
||||
html += bValid;
|
||||
}
|
||||
|
||||
html += "</div>";
|
||||
|
||||
return html;
|
||||
} else {
|
||||
// Return a link to the external BOM
|
||||
|
||||
return renderLink(
|
||||
'{% trans "View BOM" %}',
|
||||
`/part/${row.part}/bom/`
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Function to request BOM data for sub-items
|
||||
// This function may be called recursively for multi-level BOMs
|
||||
function requestSubItems(bom_pk, part_pk) {
|
||||
|
||||
inventreeGet(
|
||||
options.bom_url,
|
||||
{
|
||||
part: part_pk,
|
||||
sub_part_detail: true,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
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);
|
||||
|
||||
table.treegrid('collapseAll');
|
||||
},
|
||||
error: function() {
|
||||
console.log('Error requesting BOM for part=' + part_pk);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
table.inventreeTable({
|
||||
treeEnable: !options.editable,
|
||||
rootParentId: parent_id,
|
||||
idField: 'pk',
|
||||
parentIdField: 'parentId',
|
||||
treeShowField: 'sub_part',
|
||||
showColumns: true,
|
||||
name: 'bom',
|
||||
sortable: true,
|
||||
search: true,
|
||||
rowStyle: function(row, index) {
|
||||
|
||||
var classes = [];
|
||||
|
||||
// Shade rows differently if they are for different parent parts
|
||||
if (row.part != options.parent_id) {
|
||||
classes.push('rowinherited');
|
||||
}
|
||||
|
||||
if (row.validated) {
|
||||
classes.push('rowvalid');
|
||||
} else {
|
||||
classes.push('rowinvalid');
|
||||
}
|
||||
|
||||
return {
|
||||
classes: classes.join(' '),
|
||||
};
|
||||
|
||||
},
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No BOM items found" %}';
|
||||
},
|
||||
clickToSelect: true,
|
||||
queryParams: filters,
|
||||
original: params,
|
||||
columns: cols,
|
||||
url: options.bom_url,
|
||||
onPostBody: function() {
|
||||
|
||||
if (!options.editable) {
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
onExpand: function() {
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
onLoadSuccess: function() {
|
||||
|
||||
if (options.editable) {
|
||||
table.bootstrapTable('uncheckAll');
|
||||
} else {
|
||||
|
||||
var data = table.bootstrapTable('getData');
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Set the parent ID of the top-level rows
|
||||
row.parentId = parent_id;
|
||||
|
||||
table.bootstrapTable('updateRow', idx, row, true);
|
||||
|
||||
if (row.sub_part_detail.assembly) {
|
||||
requestSubItems(row.pk, row.sub_part);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
// In editing mode, attached editables to the appropriate table elements
|
||||
if (options.editable) {
|
||||
|
||||
table.on('click', '.bom-delete-button', function() {
|
||||
|
||||
var pk = $(this).attr('pk');
|
||||
var url = `/part/bom/${pk}/delete/`;
|
||||
|
||||
constructForm(`/api/bom/${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete BOM Item" %}',
|
||||
onSuccess: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
table.on('click', '.bom-edit-button', function() {
|
||||
|
||||
var pk = $(this).attr('pk');
|
||||
var url = `/part/bom/${pk}/edit/`;
|
||||
|
||||
launchModalForm(
|
||||
url,
|
||||
{
|
||||
success: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
table.on('click', '.bom-validate-button', function() {
|
||||
|
||||
var pk = $(this).attr('pk');
|
||||
var url = `/api/bom/${pk}/validate/`;
|
||||
|
||||
inventreePut(
|
||||
url,
|
||||
{
|
||||
valid: true
|
||||
},
|
||||
{
|
||||
method: 'PATCH',
|
||||
success: function() {
|
||||
reloadBomTable(table);
|
||||
}
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
1257
InvenTree/templates/js/translated/build.js
Normal file
1257
InvenTree/templates/js/translated/build.js
Normal file
File diff suppressed because it is too large
Load Diff
25
InvenTree/templates/js/translated/calendar.js
Normal file
25
InvenTree/templates/js/translated/calendar.js
Normal file
@ -0,0 +1,25 @@
|
||||
{% load i18n %}
|
||||
|
||||
/**
|
||||
* Helper functions for calendar display
|
||||
*/
|
||||
|
||||
function startDate(calendar) {
|
||||
// Extract the first displayed date on the calendar
|
||||
return calendar.currentData.dateProfile.activeRange.start.toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
function endDate(calendar) {
|
||||
// Extract the last display date on the calendar
|
||||
return calendar.currentData.dateProfile.activeRange.end.toISOString().split("T")[0];
|
||||
}
|
||||
|
||||
function clearEvents(calendar) {
|
||||
// Remove all events from the calendar
|
||||
|
||||
var events = calendar.getEvents();
|
||||
|
||||
events.forEach(function(event) {
|
||||
event.remove();
|
||||
})
|
||||
}
|
800
InvenTree/templates/js/translated/company.js
Normal file
800
InvenTree/templates/js/translated/company.js
Normal file
@ -0,0 +1,800 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
function manufacturerPartFields() {
|
||||
|
||||
return {
|
||||
part: {},
|
||||
manufacturer: {},
|
||||
MPN: {
|
||||
icon: 'fa-hashtag',
|
||||
},
|
||||
description: {},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function createManufacturerPart(options={}) {
|
||||
|
||||
var fields = manufacturerPartFields();
|
||||
|
||||
if (options.part) {
|
||||
fields.part.value = options.part;
|
||||
fields.part.hidden = true;
|
||||
}
|
||||
|
||||
if (options.manufacturer) {
|
||||
fields.manufacturer.value = options.manufacturer;
|
||||
}
|
||||
|
||||
constructForm('{% url "api-manufacturer-part-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Manufacturer Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function editManufacturerPart(part, options={}) {
|
||||
|
||||
var url = `/api/company/part/manufacturer/${part}/`;
|
||||
|
||||
constructForm(url, {
|
||||
fields: manufacturerPartFields(),
|
||||
title: '{% trans "Edit Manufacturer Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
});
|
||||
}
|
||||
|
||||
function deleteManufacturerPart(part, options={}) {
|
||||
|
||||
constructForm(`/api/company/part/manufacturer/${part}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Manufacturer Part" %}',
|
||||
onSuccess: options.onSuccess,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function supplierPartFields() {
|
||||
|
||||
return {
|
||||
part: {},
|
||||
supplier: {},
|
||||
SKU: {
|
||||
icon: 'fa-hashtag',
|
||||
},
|
||||
manufacturer_part: {
|
||||
filters: {
|
||||
part_detail: true,
|
||||
manufacturer_detail: true,
|
||||
}
|
||||
},
|
||||
description: {},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
note: {
|
||||
icon: 'fa-pencil-alt',
|
||||
},
|
||||
packaging: {
|
||||
icon: 'fa-box',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Launch a form to create a new ManufacturerPart
|
||||
*/
|
||||
function createSupplierPart(options={}) {
|
||||
|
||||
var fields = supplierPartFields();
|
||||
|
||||
if (options.part) {
|
||||
fields.manufacturer_part.filters.part = options.part;
|
||||
fields.part.hidden = true;
|
||||
fields.part.value = options.part;
|
||||
}
|
||||
|
||||
if (options.supplier) {
|
||||
fields.supplier.value = options.supplier;
|
||||
}
|
||||
|
||||
if (options.manufacturer_part) {
|
||||
fields.manufacturer_part.value = options.manufacturer_part;
|
||||
}
|
||||
|
||||
constructForm('{% url "api-supplier-part-list" %}', {
|
||||
fields: fields,
|
||||
method: 'POST',
|
||||
title: '{% trans "Add Supplier Part" %}',
|
||||
onSuccess: options.onSuccess,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function editSupplierPart(part, options={}) {
|
||||
|
||||
constructForm(`/api/company/part/${part}/`, {
|
||||
fields: supplierPartFields(),
|
||||
title: '{% trans "Edit Supplier Part" %}',
|
||||
onSuccess: options.onSuccess
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteSupplierPart(part, options={}) {
|
||||
|
||||
constructForm(`/api/company/part/${part}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Supplier Part" %}',
|
||||
onSuccess: options.onSuccess,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Returns a default form-set for creating / editing a Company object
|
||||
function companyFormFields(options={}) {
|
||||
|
||||
return {
|
||||
name: {},
|
||||
description: {},
|
||||
website: {
|
||||
icon: 'fa-globe',
|
||||
},
|
||||
address: {
|
||||
icon: 'fa-envelope',
|
||||
},
|
||||
currency: {
|
||||
icon: 'fa-dollar-sign',
|
||||
},
|
||||
phone: {
|
||||
icon: 'fa-phone',
|
||||
},
|
||||
email: {
|
||||
icon: 'fa-at',
|
||||
},
|
||||
contact: {
|
||||
icon: 'fa-address-card',
|
||||
},
|
||||
is_supplier: {},
|
||||
is_manufacturer: {},
|
||||
is_customer: {}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
function editCompany(pk, options={}) {
|
||||
|
||||
var fields = options.fields || companyFormFields();
|
||||
|
||||
constructForm(
|
||||
`/api/company/${pk}/`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
fields: fields,
|
||||
reload: true,
|
||||
title: '{% trans "Edit Company" %}',
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* Launches a form to create a new company.
|
||||
* As this can be called from many different contexts,
|
||||
* we abstract it here!
|
||||
*/
|
||||
function createCompany(options={}) {
|
||||
|
||||
// Default field set
|
||||
var fields = options.fields || companyFormFields();
|
||||
|
||||
constructForm(
|
||||
'{% url "api-company-list" %}',
|
||||
{
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
follow: true,
|
||||
title: '{% trans "Add new Company" %}',
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function loadCompanyTable(table, url, options={}) {
|
||||
/*
|
||||
* Load company listing data into specified table.
|
||||
*
|
||||
* Args:
|
||||
* - table: Table element on the page
|
||||
* - url: Base URL for the API query
|
||||
* - options: table options.
|
||||
*/
|
||||
|
||||
// Query parameters
|
||||
var params = options.params || {};
|
||||
|
||||
var filters = loadTableFilters("company");
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList("company", $(table));
|
||||
|
||||
var columns = [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Company" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
var html = imageHoverIcon(row.image) + renderLink(value, row.url);
|
||||
|
||||
if (row.is_customer) {
|
||||
html += `<span title='{% trans "Customer" %}' class='fas fa-user-tie label-right'></span>`;
|
||||
}
|
||||
|
||||
if (row.is_manufacturer) {
|
||||
html += `<span title='{% trans "Manufacturer" %}' class='fas fa-industry label-right'></span>`;
|
||||
}
|
||||
|
||||
if (row.is_supplier) {
|
||||
html += `<span title='{% trans "Supplier" %}' class='fas fa-building label-right'></span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'website',
|
||||
title: '{% trans "Website" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
if (options.pagetype == 'suppliers') {
|
||||
columns.push({
|
||||
sortable: true,
|
||||
field: 'parts_supplied',
|
||||
title: '{% trans "Parts Supplied" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderLink(value, `/company/${row.pk}/parts/`);
|
||||
}
|
||||
});
|
||||
} else if (options.pagetype == 'manufacturers') {
|
||||
columns.push({
|
||||
sortable: true,
|
||||
field: 'parts_manufactured',
|
||||
title: '{% trans "Parts Manufactured" %}',
|
||||
formatter: function(value, row) {
|
||||
return renderLink(value, `/company/${row.pk}/parts/`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
method: 'get',
|
||||
queryParams: filters,
|
||||
groupBy: false,
|
||||
sidePagination: 'server',
|
||||
formatNoMatches: function() { return "{% trans "No company information found" %}"; },
|
||||
showColumns: true,
|
||||
name: options.pagetype || 'company',
|
||||
columns: columns,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function deleteManufacturerParts(selections, options={}) {
|
||||
|
||||
if (selections.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var parts = [];
|
||||
|
||||
var text = `
|
||||
<div class='alert alert-block alert-danger'>
|
||||
<p>{% trans "The following manufacturer parts will be deleted" %}:</p>
|
||||
<ul>`;
|
||||
|
||||
selections.forEach(function(item) {
|
||||
parts.push(item.pk);
|
||||
|
||||
text += `
|
||||
<li>
|
||||
<p>${item.MPN} - ${item.part_detail.full_name}</p>
|
||||
</li>`;
|
||||
});
|
||||
|
||||
text += `
|
||||
</ul>
|
||||
</div>`;
|
||||
|
||||
showQuestionDialog(
|
||||
'{% trans "Delete Manufacturer Parts" %}',
|
||||
text,
|
||||
{
|
||||
accept_text: '{% trans "Delete" %}',
|
||||
accept: function() {
|
||||
|
||||
// Delete each manufacturer part
|
||||
var requests = [];
|
||||
|
||||
parts.forEach(function(pk) {
|
||||
var url = `/api/company/part/manufacturer/${pk}`;
|
||||
|
||||
requests.push(inventreeDelete(url));
|
||||
});
|
||||
|
||||
// Wait for all the requests to complete
|
||||
$.when.apply($, requests).done(function() {
|
||||
|
||||
if (options.onSuccess) {
|
||||
options.onSuccess();
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function loadManufacturerPartTable(table, url, options) {
|
||||
/*
|
||||
* Load manufacturer part table
|
||||
*
|
||||
*/
|
||||
|
||||
// Query parameters
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters("manufacturer-part");
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList("manufacturer-part", $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
method: 'get',
|
||||
original: params,
|
||||
queryParams: filters,
|
||||
name: 'manufacturerparts',
|
||||
groupBy: false,
|
||||
formatNoMatches: function() { return '{% trans "No manufacturer parts found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
visible: params['part_detail'],
|
||||
switchable: params['part_detail'],
|
||||
sortable: true,
|
||||
field: 'part_detail.full_name',
|
||||
title: '{% trans "Part" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var url = `/part/${row.part}/`;
|
||||
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += `<span class='fas fa-clone label-right' title='{% trans "Template part" %}'></span>`;
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += `<span class='fas fa-tools label-right' title='{% trans "Assembled part" %}'></span>`;
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
html += `<span class='label label-warning label-right'>{% trans "Inactive" %}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'manufacturer',
|
||||
title: '{% trans "Manufacturer" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value && row.manufacturer_detail) {
|
||||
var name = row.manufacturer_detail.name;
|
||||
var url = `/company/${value}/`;
|
||||
var html = imageHoverIcon(row.manufacturer_detail.image) + renderLink(name, url);
|
||||
|
||||
return html;
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'MPN',
|
||||
title: '{% trans "MPN" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return renderLink(value, `/manufacturer-part/${row.pk}/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
sortable: false,
|
||||
switchable: true,
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-manufacturer-part-edit', pk, '{% trans "Edit manufacturer part" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-manufacturer-part-delete', pk, '{% trans "Delete manufacturer part" %}');
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
],
|
||||
onPostBody: function() {
|
||||
// Callbacks
|
||||
$(table).find('.button-manufacturer-part-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
editManufacturerPart(
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(table).find('.button-manufacturer-part-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
deleteManufacturerPart(
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadManufacturerPartParameterTable(table, url, options) {
|
||||
/*
|
||||
* Load table of ManufacturerPartParameter objects
|
||||
*/
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters("manufacturer-part-parameters");
|
||||
|
||||
// Overwrite explicit parameters
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
// setupFilterList("manufacturer-part-parameters", $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
method: 'get',
|
||||
original: params,
|
||||
queryParams: filters,
|
||||
name: 'manufacturerpartparameters',
|
||||
groupBy: false,
|
||||
formatNoMatches: function() { return '{% trans "No parameters found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
switchable: false,
|
||||
visible: true,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Name" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'value',
|
||||
title: '{% trans "Value" %}',
|
||||
switchable: false,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'units',
|
||||
title: '{% trans "Units" %}',
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '',
|
||||
switchable: false,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
var pk = row.pk;
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-parameter-edit', pk, '{% trans "Edit parameter" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-parameter-delete', pk, '{% trans "Delete parameter" %}');
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
],
|
||||
onPostBody: function() {
|
||||
// Setup callback functions
|
||||
$(table).find('.button-parameter-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/company/part/manufacturer/parameter/${pk}/`, {
|
||||
fields: {
|
||||
name: {},
|
||||
value: {},
|
||||
units: {},
|
||||
},
|
||||
title: '{% trans "Edit Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
$(table).find('.button-parameter-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/company/part/manufacturer/parameter/${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Parameter" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadSupplierPartTable(table, url, options) {
|
||||
/*
|
||||
* Load supplier part table
|
||||
*
|
||||
*/
|
||||
|
||||
// Query parameters
|
||||
var params = options.params || {};
|
||||
|
||||
// Load filters
|
||||
var filters = loadTableFilters("supplier-part");
|
||||
|
||||
for (var key in params) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
setupFilterList("supplier-part", $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
method: 'get',
|
||||
original: params,
|
||||
queryParams: filters,
|
||||
name: 'supplierparts',
|
||||
groupBy: false,
|
||||
formatNoMatches: function() { return '{% trans "No supplier parts found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
visible: params['part_detail'],
|
||||
switchable: params['part_detail'],
|
||||
sortable: true,
|
||||
field: 'part_detail.full_name',
|
||||
title: '{% trans "Part" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var url = `/part/${row.part}/`;
|
||||
|
||||
var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
|
||||
|
||||
if (row.part_detail.is_template) {
|
||||
html += `<span class='fas fa-clone label-right' title='{% trans "Template part" %}'></span>`;
|
||||
}
|
||||
|
||||
if (row.part_detail.assembly) {
|
||||
html += `<span class='fas fa-tools label-right' title='{% trans "Assembled part" %}'></span>`;
|
||||
}
|
||||
|
||||
if (!row.part_detail.active) {
|
||||
html += `<span class='label label-warning label-right'>{% trans "Inactive" %}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'supplier',
|
||||
title: '{% trans "Supplier" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
var name = row.supplier_detail.name;
|
||||
var url = `/company/${value}/`;
|
||||
var html = imageHoverIcon(row.supplier_detail.image) + renderLink(name, url);
|
||||
|
||||
return html;
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'SKU',
|
||||
title: '{% trans "Supplier Part" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return renderLink(value, `/supplier-part/${row.pk}/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
visible: params['manufacturer_detail'],
|
||||
switchable: params['manufacturer_detail'],
|
||||
sortable: true,
|
||||
field: 'manufacturer',
|
||||
title: '{% trans "Manufacturer" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value && row.manufacturer_detail) {
|
||||
var name = row.manufacturer_detail.name;
|
||||
var url = `/company/${value}/`;
|
||||
var html = imageHoverIcon(row.manufacturer_detail.image) + renderLink(name, url);
|
||||
|
||||
return html;
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
visible: params['manufacturer_detail'],
|
||||
switchable: params['manufacturer_detail'],
|
||||
sortable: true,
|
||||
field: 'MPN',
|
||||
title: '{% trans "MPN" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value && row.manufacturer_part) {
|
||||
return renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`);
|
||||
} else {
|
||||
return "-";
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '{% trans "Link" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
if (value) {
|
||||
return renderLink(value, value);
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'note',
|
||||
title: '{% trans "Notes" %}',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'packaging',
|
||||
title: '{% trans "Packaging" %}',
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '',
|
||||
sortable: false,
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
var pk = row.pk;
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', 'button-supplier-part-edit', pk, '{% trans "Edit supplier part" %}');
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-supplier-part-delete', pk, '{% trans "Delete supplier part" %}');
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
],
|
||||
onPostBody: function() {
|
||||
// Callbacks
|
||||
$(table).find('.button-supplier-part-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
editSupplierPart(
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(table).find('.button-supplier-part-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
deleteSupplierPart(
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
});
|
||||
}
|
419
InvenTree/templates/js/translated/filters.js
Normal file
419
InvenTree/templates/js/translated/filters.js
Normal file
@ -0,0 +1,419 @@
|
||||
{% load i18n %}
|
||||
|
||||
/**
|
||||
* Code for managing query filters / table options.
|
||||
*
|
||||
* Optional query filters are available to the user for various
|
||||
* tables display in the web interface.
|
||||
* These filters are saved to the web session, and should be
|
||||
* persistent for a given table type.
|
||||
*
|
||||
* This makes use of the 'inventreeSave' and 'inventreeLoad' functions
|
||||
* for writing to and reading from session storage.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
function defaultFilters() {
|
||||
return {
|
||||
stock: "cascade=1&in_stock=1",
|
||||
build: "",
|
||||
parts: "cascade=1",
|
||||
company: "",
|
||||
salesorder: "",
|
||||
purchaseorder: "",
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load table filters for the given table from session storage
|
||||
*
|
||||
* @param tableKey - String key for the particular table
|
||||
* @param defaults - Default filters for this table e.g. 'cascade=1&location=5'
|
||||
*/
|
||||
function loadTableFilters(tableKey) {
|
||||
|
||||
var lookup = "table-filters-" + tableKey.toLowerCase();
|
||||
|
||||
var defaults = defaultFilters()[tableKey] || '';
|
||||
|
||||
var filterstring = inventreeLoad(lookup, defaults);
|
||||
|
||||
var filters = {};
|
||||
|
||||
filterstring.split("&").forEach(function(item, index) {
|
||||
item = item.trim();
|
||||
|
||||
if (item.length > 0) {
|
||||
var f = item.split('=');
|
||||
|
||||
if (f.length == 2) {
|
||||
filters[f[0]] = f[1];
|
||||
} else {
|
||||
console.log(`Improperly formatted filter: ${item}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Save table filters to session storage
|
||||
*
|
||||
* @param {*} tableKey - string key for the given table
|
||||
* @param {*} filters - object of string:string pairs
|
||||
*/
|
||||
function saveTableFilters(tableKey, filters) {
|
||||
var lookup = "table-filters-" + tableKey.toLowerCase();
|
||||
|
||||
var strings = [];
|
||||
|
||||
for (var key in filters) {
|
||||
strings.push(`${key.trim()}=${String(filters[key]).trim()}`);
|
||||
}
|
||||
|
||||
var filterstring = strings.join('&');
|
||||
|
||||
inventreeSave(lookup, filterstring);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Remove a named filter parameter
|
||||
*/
|
||||
function removeTableFilter(tableKey, filterKey) {
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
delete filters[filterKey];
|
||||
|
||||
saveTableFilters(tableKey, filters);
|
||||
|
||||
// Return a copy of the updated filters
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
function addTableFilter(tableKey, filterKey, filterValue) {
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
filters[filterKey] = filterValue;
|
||||
|
||||
saveTableFilters(tableKey, filters);
|
||||
|
||||
// Return a copy of the updated filters
|
||||
return filters;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Clear all the custom filters for a given table
|
||||
*/
|
||||
function clearTableFilters(tableKey) {
|
||||
saveTableFilters(tableKey, {});
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a list of the "available" filters for a given table key.
|
||||
* A filter is "available" if it is not already being used to filter the table.
|
||||
* Once a filter is selected, it will not be returned here.
|
||||
*/
|
||||
function getRemainingTableFilters(tableKey) {
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
var remaining = getAvailableTableFilters(tableKey);
|
||||
|
||||
for (var key in filters) {
|
||||
// Delete the filter if it is already in use
|
||||
delete remaining[key];
|
||||
}
|
||||
|
||||
return remaining;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* Return the filter settings for a given table and key combination.
|
||||
* Return empty object if the combination does not exist.
|
||||
*/
|
||||
function getFilterSettings(tableKey, filterKey) {
|
||||
|
||||
return getAvailableTableFilters(tableKey)[filterKey] || {};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return a set of key:value options for the given filter.
|
||||
* If no options are specified (e.g. for a number field),
|
||||
* then a null object is returned.
|
||||
*/
|
||||
function getFilterOptionList(tableKey, filterKey) {
|
||||
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
|
||||
if (settings.type == 'bool') {
|
||||
return {
|
||||
'1': {
|
||||
key: '1',
|
||||
value: '{% trans "true" %}',
|
||||
},
|
||||
'0': {
|
||||
key: '0',
|
||||
value: '{% trans "false" %}',
|
||||
},
|
||||
};
|
||||
} else if ('options' in settings) {
|
||||
return settings.options;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate a list of <option> tags for the given table.
|
||||
*/
|
||||
function generateAvailableFilterList(tableKey) {
|
||||
|
||||
var remaining = getRemainingTableFilters(tableKey);
|
||||
|
||||
var id = 'filter-tag-' + tableKey.toLowerCase();
|
||||
|
||||
var html = `<select class='form-control filter-input' id='${id}' name='tag'>`;
|
||||
|
||||
html += "<option value=''>{% trans 'Select filter' %}</option>";
|
||||
|
||||
for (var opt in remaining) {
|
||||
var title = getFilterTitle(tableKey, opt);
|
||||
html += `<option value='${opt}'>${title}</option>`;
|
||||
}
|
||||
|
||||
html += `</select>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Generate an input for setting the value of a given filter.
|
||||
*/
|
||||
function generateFilterInput(tableKey, filterKey) {
|
||||
|
||||
var id = 'filter-value-' + tableKey.toLowerCase();
|
||||
|
||||
if (filterKey == null || filterKey.length == 0) {
|
||||
// Return an 'empty' element
|
||||
return `<div class='filter-input' id='${id}'></div>`;
|
||||
}
|
||||
|
||||
var options = getFilterOptionList(tableKey, filterKey);
|
||||
|
||||
var html = '';
|
||||
|
||||
// A 'null' options list means that a simple text-input dialog should be used
|
||||
if (options == null) {
|
||||
html = `<input class='form-control filter-input' id='${id}' name='value'></input>`;
|
||||
} else {
|
||||
// Return a 'select' input with the available values
|
||||
html = `<select class='form-control filter-input' id='${id}' name='value'>`;
|
||||
|
||||
for (var key in options) {
|
||||
option = options[key];
|
||||
html += `<option value='${key}'>${option.value}</option>`;
|
||||
}
|
||||
|
||||
html += `</select>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Configure a filter list for a given table
|
||||
*
|
||||
* @param {*} tableKey - string lookup key for filter settings
|
||||
* @param {*} table - bootstrapTable element to update
|
||||
* @param {*} target - name of target element on page
|
||||
*/
|
||||
function setupFilterList(tableKey, table, target) {
|
||||
|
||||
var addClicked = false;
|
||||
|
||||
if (target == null || target.length == 0) {
|
||||
target = `#filter-list-${tableKey}`;
|
||||
}
|
||||
|
||||
var tag = `filter-tag-${tableKey}`;
|
||||
var add = `filter-add-${tableKey}`;
|
||||
var clear = `filter-clear-${tableKey}`;
|
||||
var make = `filter-make-${tableKey}`;
|
||||
|
||||
var filters = loadTableFilters(tableKey);
|
||||
|
||||
var element = $(target);
|
||||
|
||||
// One blank slate, please
|
||||
element.empty();
|
||||
|
||||
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) {
|
||||
element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-default filter-tag'><span class='fas fa-trash-alt'></span></button>`);
|
||||
}
|
||||
|
||||
for (var key in filters) {
|
||||
var value = getFilterOptionValue(tableKey, key, filters[key]);
|
||||
var title = getFilterTitle(tableKey, key);
|
||||
var description = getFilterDescription(tableKey, key);
|
||||
|
||||
element.append(`<div title='${description}' class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
|
||||
}
|
||||
|
||||
// Add a callback for adding a new filter
|
||||
element.find(`#${add}`).click(function clicked() {
|
||||
|
||||
if (!addClicked) {
|
||||
|
||||
addClicked = true;
|
||||
|
||||
var html = '';
|
||||
|
||||
//`<div class='filter-input'>`;
|
||||
|
||||
html += generateAvailableFilterList(tableKey);
|
||||
html += generateFilterInput(tableKey);
|
||||
|
||||
html += `<button title='{% trans "Create filter" %}' class='btn btn-default filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
|
||||
//html += '</div>';
|
||||
|
||||
element.append(html);
|
||||
|
||||
// Add a callback for when the filter tag selection is changed
|
||||
element.find(`#filter-tag-${tableKey}`).on('change', function() {
|
||||
var list = element.find(`#filter-value-${tableKey}`);
|
||||
|
||||
list.replaceWith(generateFilterInput(tableKey, this.value));
|
||||
});
|
||||
|
||||
// Add a callback for when the new filter is created
|
||||
element.find(`#filter-make-${tableKey}`).click(function() {
|
||||
var tag = element.find(`#filter-tag-${tableKey}`).val();
|
||||
var val = element.find(`#filter-value-${tableKey}`).val();
|
||||
|
||||
// Only add the new filter if it is not empty!
|
||||
if (tag && tag.length > 0) {
|
||||
var filters = addTableFilter(tableKey, tag, val);
|
||||
reloadTableFilters(table, filters);
|
||||
|
||||
// Run this function again
|
||||
setupFilterList(tableKey, table, target);
|
||||
}
|
||||
|
||||
});
|
||||
} else {
|
||||
addClicked = false;
|
||||
|
||||
setupFilterList(tableKey, table, target);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
// Add a callback for clearing all the filters
|
||||
element.find(`#${clear}`).click(function() {
|
||||
var filters = clearTableFilters(tableKey);
|
||||
|
||||
reloadTableFilters(table, filters);
|
||||
|
||||
setupFilterList(tableKey, table, target);
|
||||
});
|
||||
|
||||
// Add callback for deleting each filter
|
||||
element.find(".close").click(function(event) {
|
||||
var me = $(this);
|
||||
|
||||
var filter = me.attr(`filter-tag-${tableKey}`);
|
||||
|
||||
var filters = removeTableFilter(tableKey, filter);
|
||||
|
||||
reloadTableFilters(table, filters);
|
||||
|
||||
// Run this function again!
|
||||
setupFilterList(tableKey, table, target);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the pretty title for the given table and filter selection.
|
||||
* If no title is provided, default to the key value.
|
||||
*
|
||||
*/
|
||||
function getFilterTitle(tableKey, filterKey) {
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
return settings.title || filterKey;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return the pretty description for the given table and filter selection
|
||||
*/
|
||||
function getFilterDescription(tableKey, filterKey) {
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
|
||||
return settings.title;
|
||||
}
|
||||
|
||||
/*
|
||||
* Return a description for the given table and filter selection.
|
||||
*/
|
||||
function getFilterDescription(tableKey, filterKey) {
|
||||
var settings = getFilterSettings(tableKey, filterKey);
|
||||
return settings.description || filterKey;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Return the display value for a particular option
|
||||
*/
|
||||
function getFilterOptionValue(tableKey, filterKey, valueKey) {
|
||||
|
||||
var filter = getFilterSettings(tableKey, filterKey);
|
||||
|
||||
var value = String(valueKey);
|
||||
|
||||
// Lookup for boolean options
|
||||
if (filter.type == 'bool') {
|
||||
if (value == '1') return '{% trans "true" %}';
|
||||
if (value == '0') return '{% trans "false" %}';
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
// Iterate through a list of options
|
||||
if ('options' in filter) {
|
||||
for (var key in filter.options) {
|
||||
|
||||
if (key == valueKey) {
|
||||
return filter.options[key].value;
|
||||
}
|
||||
}
|
||||
|
||||
// Could not find a match
|
||||
return value;
|
||||
}
|
||||
|
||||
// Cannot map to a display string - return the original text
|
||||
return value;
|
||||
}
|
||||
|
1674
InvenTree/templates/js/translated/forms.js
Normal file
1674
InvenTree/templates/js/translated/forms.js
Normal file
File diff suppressed because it is too large
Load Diff
249
InvenTree/templates/js/translated/label.js
Normal file
249
InvenTree/templates/js/translated/label.js
Normal file
@ -0,0 +1,249 @@
|
||||
{% load i18n %}
|
||||
|
||||
function printStockItemLabels(items, options={}) {
|
||||
/**
|
||||
* Print stock item labels for the given stock items
|
||||
*/
|
||||
|
||||
if (items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Items" %}',
|
||||
'{% trans "Stock item(s) must be selected before printing labels" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available labels from the server
|
||||
inventreeGet(
|
||||
'{% url "api-stockitem-label-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
items: items,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No labels found which match selected stock item(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label to print
|
||||
selectLabel(
|
||||
response,
|
||||
items,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/label/stock/${pk}/print/?`;
|
||||
|
||||
items.forEach(function(item) {
|
||||
href += `items[]=${item}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function printStockLocationLabels(locations, options={}) {
|
||||
|
||||
if (locations.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Locations" %}',
|
||||
'{% trans "Stock location(s) must be selected before printing labels" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available labels from the server
|
||||
inventreeGet(
|
||||
'{% url "api-stocklocation-label-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
locations: locations,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No labels found which match selected stock location(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label to print
|
||||
selectLabel(
|
||||
response,
|
||||
locations,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/label/location/${pk}/print/?`;
|
||||
|
||||
locations.forEach(function(location) {
|
||||
href += `locations[]=${location}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function printPartLabels(parts, options={}) {
|
||||
/**
|
||||
* Print labels for the provided parts
|
||||
*/
|
||||
|
||||
if (parts.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "Part(s) must be selected before printing labels" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available labels from the server
|
||||
inventreeGet(
|
||||
'{% url "api-part-label-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
parts: parts,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Labels Found" %}',
|
||||
'{% trans "No labels found which match the selected part(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select label to print
|
||||
selectLabel(
|
||||
response,
|
||||
parts,
|
||||
{
|
||||
success: function(pk) {
|
||||
var url = `/api/label/part/${pk}/print/?`;
|
||||
|
||||
parts.forEach(function(part) {
|
||||
url += `parts[]=${part}&`;
|
||||
});
|
||||
|
||||
window.location.href = url;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function selectLabel(labels, items, options={}) {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
// If only a single label template is provided,
|
||||
// just run with that!
|
||||
|
||||
if (labels.length == 1) {
|
||||
if (options.success) {
|
||||
options.success(labels[0].pk);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
);
|
||||
|
||||
// Construct form
|
||||
var html = '';
|
||||
|
||||
if (items.length > 0) {
|
||||
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
${items.length} {% trans "stock items 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" %}
|
||||
</label>
|
||||
<div class='controls'>
|
||||
<select id='id_label' class='select form-control name='label'>
|
||||
${label_list}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>`;
|
||||
|
||||
openModal({
|
||||
modal: modal,
|
||||
});
|
||||
|
||||
modalEnable(modal, true);
|
||||
modalSetTitle(modal, '{% trans "Select Label Template" %}');
|
||||
modalSetContent(modal, html);
|
||||
|
||||
attachSelect(modal);
|
||||
|
||||
modalSubmit(modal, function() {
|
||||
|
||||
var label = $(modal).find('#id_label');
|
||||
|
||||
var pk = label.val();
|
||||
|
||||
closeModal(modal);
|
||||
|
||||
if (options.success) {
|
||||
options.success(pk);
|
||||
}
|
||||
});
|
||||
}
|
1109
InvenTree/templates/js/translated/modals.js
Normal file
1109
InvenTree/templates/js/translated/modals.js
Normal file
File diff suppressed because it is too large
Load Diff
232
InvenTree/templates/js/translated/model_renderers.js
Normal file
232
InvenTree/templates/js/translated/model_renderers.js
Normal file
@ -0,0 +1,232 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
function blankImage() {
|
||||
return `/static/img/blank_image.png`;
|
||||
}
|
||||
|
||||
// Render a select2 thumbnail image
|
||||
function select2Thumbnail(image) {
|
||||
if (!image) {
|
||||
image = blankImage();
|
||||
}
|
||||
|
||||
return `<img src='${image}' class='select2-thumbnail'>`;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* This file contains functions for rendering various InvenTree database models,
|
||||
* in particular for displaying them in modal forms in a 'select2' context.
|
||||
*
|
||||
* Each renderer is provided with three arguments:
|
||||
*
|
||||
* - name: The 'name' of the model instance in the referring model
|
||||
* - data: JSON data which represents the model instance. Returned via a GET request.
|
||||
* - parameters: The field parameters provided via an OPTIONS request to the endpoint.
|
||||
* - options: User options provided by the client
|
||||
*/
|
||||
|
||||
|
||||
// Renderer for "Company" model
|
||||
function renderCompany(name, data, parameters, options) {
|
||||
|
||||
var html = select2Thumbnail(data.image);
|
||||
|
||||
html += `<span><b>${data.name}</b></span> - <i>${data.description}</i>`;
|
||||
|
||||
html += `<span class='float-right'>{% trans "Company ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "StockItem" model
|
||||
function renderStockItem(name, data, parameters, options) {
|
||||
|
||||
var image = data.part_detail.thumbnail || data.part_detail.image || blankImage();
|
||||
|
||||
var html = `<img src='${image}' class='select2-thumbnail'>`;
|
||||
|
||||
html += ` <span>${data.part_detail.full_name || data.part_detail.name}</span>`;
|
||||
|
||||
if (data.serial && data.quantity == 1) {
|
||||
html += ` - <i>{% trans "Serial Number" %}: ${data.serial}`;
|
||||
} else {
|
||||
html += ` - <i>{% trans "Quantity" %}: ${data.quantity}`;
|
||||
}
|
||||
|
||||
if (data.part_detail.description) {
|
||||
html += `<p><small>${data.part_detail.description}</small></p>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "StockLocation" model
|
||||
function renderStockLocation(name, data, parameters, options) {
|
||||
|
||||
var level = '- '.repeat(data.level);
|
||||
|
||||
var html = `<span>${level}${data.pathstring}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${data.description}</i>`;
|
||||
}
|
||||
|
||||
html += `<span class='float-right'>{% trans "Location ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
function renderBuild(name, data, parameters, options) {
|
||||
|
||||
var image = null;
|
||||
|
||||
if (data.part_detail && data.part_detail.thumbnail) {
|
||||
image = data.part_detail.thumbnail;
|
||||
}
|
||||
|
||||
var html = select2Thumbnail(image);
|
||||
|
||||
html += `<span><b>${data.reference}</b></span> - ${data.quantity} x ${data.part_detail.full_name}`;
|
||||
html += `<span class='float-right'>{% trans "Build ID" %}: ${data.pk}</span>`;
|
||||
|
||||
html += `<p><i>${data.title}</i></p>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "Part" model
|
||||
function renderPart(name, data, parameters, options) {
|
||||
|
||||
var html = select2Thumbnail(data.image);
|
||||
|
||||
html += ` <span>${data.full_name || data.name}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${data.description}</i>`;
|
||||
}
|
||||
|
||||
html += `<span class='float-right'>{% trans "Part ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
// Renderer for "User" model
|
||||
function renderUser(name, data, parameters, options) {
|
||||
|
||||
var html = `<span>${data.username}</span>`;
|
||||
|
||||
if (data.first_name && data.last_name) {
|
||||
html += ` - <i>${data.first_name} ${data.last_name}</i>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "Owner" model
|
||||
function renderOwner(name, data, parameters, options) {
|
||||
|
||||
var html = `<span>${data.name}</span>`;
|
||||
|
||||
switch (data.label) {
|
||||
case 'user':
|
||||
html += `<span class='float-right fas fa-user'></span>`;
|
||||
break;
|
||||
case 'group':
|
||||
html += `<span class='float-right fas fa-users'></span>`;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "PartCategory" model
|
||||
function renderPartCategory(name, data, parameters, options) {
|
||||
|
||||
var level = '- '.repeat(data.level);
|
||||
|
||||
var html = `<span>${level}${data.pathstring}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
html += ` - <i>${data.description}</i>`;
|
||||
}
|
||||
|
||||
html += `<span class='float-right'>{% trans "Category ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
function renderPartParameterTemplate(name, data, parameters, options) {
|
||||
|
||||
var html = `<span>${data.name} - [${data.units}]</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "ManufacturerPart" model
|
||||
function renderManufacturerPart(name, data, parameters, options) {
|
||||
|
||||
var manufacturer_image = null;
|
||||
var part_image = null;
|
||||
|
||||
if (data.manufacturer_detail) {
|
||||
manufacturer_image = data.manufacturer_detail.image;
|
||||
}
|
||||
|
||||
if (data.part_detail) {
|
||||
part_image = data.part_detail.thumbnail || data.part_detail.image;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
|
||||
html += select2Thumbnail(manufacturer_image);
|
||||
html += select2Thumbnail(part_image);
|
||||
|
||||
html += ` <span><b>${data.manufacturer_detail.name}</b> - ${data.MPN}</span>`;
|
||||
html += ` - <i>${data.part_detail.full_name}</i>`;
|
||||
|
||||
html += `<span class='float-right'>{% trans "Manufacturer Part ID" %}: ${data.pk}</span>`;
|
||||
|
||||
return html;
|
||||
}
|
||||
|
||||
|
||||
// Renderer for "SupplierPart" model
|
||||
function renderSupplierPart(name, data, parameters, options) {
|
||||
|
||||
var supplier_image = null;
|
||||
var part_image = null;
|
||||
|
||||
if (data.supplier_detail) {
|
||||
supplier_image = data.supplier_detail.image;
|
||||
}
|
||||
|
||||
if (data.part_detail) {
|
||||
part_image = data.part_detail.thumbnail || data.part_detail.image;
|
||||
}
|
||||
|
||||
var html = '';
|
||||
|
||||
html += select2Thumbnail(supplier_image);
|
||||
html += select2Thumbnail(part_image);
|
||||
|
||||
html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
|
||||
html += ` - <i>${data.part_detail.full_name}</i>`;
|
||||
|
||||
html += `<span class='float-right'>{% trans "Supplier Part ID" %}: ${data.pk}</span>`;
|
||||
|
||||
|
||||
return html;
|
||||
|
||||
}
|
84
InvenTree/templates/js/translated/nav.js
Normal file
84
InvenTree/templates/js/translated/nav.js
Normal file
@ -0,0 +1,84 @@
|
||||
|
||||
/*
|
||||
* Attach callbacks to navigation bar elements.
|
||||
*
|
||||
* Searches for elements with the class 'nav-toggle'.
|
||||
* A callback is added to each element,
|
||||
* to display the matching panel.
|
||||
*
|
||||
* The 'id' of the .nav-toggle element should be of the form "select-<x>",
|
||||
* and point to a matching "panel-<x>"
|
||||
*/
|
||||
function attachNavCallbacks(options={}) {
|
||||
|
||||
$('.nav-toggle').click(function() {
|
||||
var el = $(this);
|
||||
|
||||
// Find the matching "panel" element
|
||||
var panelName = el.attr('id').replace('select-', '');
|
||||
|
||||
activatePanel(panelName, options);
|
||||
});
|
||||
|
||||
var panelClass = options.name || 'unknown';
|
||||
|
||||
/* Look for a default panel to initialize
|
||||
* First preference = URL parameter e.g. ?display=part-stock
|
||||
* Second preference = localStorage
|
||||
* Third preference = default
|
||||
*/
|
||||
var defaultPanel = $.urlParam('display') || localStorage.getItem(`inventree-selected-panel-${panelClass}`) || options.default;
|
||||
|
||||
if (defaultPanel) {
|
||||
activatePanel(defaultPanel);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function activatePanel(panelName, options={}) {
|
||||
|
||||
var panelClass = options.name || 'unknown';
|
||||
|
||||
// First, cause any other panels to "fade out"
|
||||
$('.panel-visible').hide();
|
||||
$('.panel-visible').removeClass('panel-visible');
|
||||
|
||||
// Find the target panel
|
||||
var panel = `#panel-${panelName}`;
|
||||
var select = `#select-${panelName}`;
|
||||
|
||||
// Check that the selected panel (and select) exist
|
||||
if ($(panel).length && $(select).length) {
|
||||
// Yep, both are displayed
|
||||
} else {
|
||||
// Either the select or the panel are not displayed!
|
||||
// Iterate through the available 'select' elements until one matches
|
||||
panelName = null;
|
||||
|
||||
$('.nav-toggle').each(function(item) {
|
||||
var panel_name = $(this).attr('id').replace('select-', '');
|
||||
|
||||
if ($(`#panel-${panel_name}`).length && (panelName == null)) {
|
||||
panelName = panel_name;
|
||||
}
|
||||
|
||||
panel = `#panel-${panelName}`;
|
||||
select = `#select-${panelName}`;
|
||||
});
|
||||
}
|
||||
|
||||
// Save the selected panel
|
||||
localStorage.setItem(`inventree-selected-panel-${panelClass}`, panelName);
|
||||
|
||||
// Display the panel
|
||||
$(panel).addClass('panel-visible');
|
||||
$(panel).fadeIn(100);
|
||||
|
||||
// Un-select all selectors
|
||||
$('.list-group-item').removeClass('active');
|
||||
|
||||
// Find the associated selector
|
||||
var select = `#select-${panelName}`;
|
||||
|
||||
$(select).parent('.list-group-item').addClass('active');
|
||||
}
|
467
InvenTree/templates/js/translated/order.js
Normal file
467
InvenTree/templates/js/translated/order.js
Normal file
@ -0,0 +1,467 @@
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
|
||||
// Create a new SalesOrder
|
||||
function createSalesOrder(options={}) {
|
||||
|
||||
constructForm('{% url "api-so-list" %}', {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
reference: {
|
||||
prefix: '{% settings_value "SALESORDER_REFERENCE_PREFIX" %}',
|
||||
},
|
||||
customer: {
|
||||
value: options.customer,
|
||||
},
|
||||
customer_reference: {},
|
||||
description: {},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
responsible: {
|
||||
icon: 'fa-user',
|
||||
}
|
||||
},
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/sales-order/${data.pk}/`;
|
||||
},
|
||||
title: '{% trans "Create Sales Order" %}',
|
||||
});
|
||||
}
|
||||
|
||||
// Create a new PurchaseOrder
|
||||
function createPurchaseOrder(options={}) {
|
||||
|
||||
constructForm('{% url "api-po-list" %}', {
|
||||
method: 'POST',
|
||||
fields: {
|
||||
reference: {
|
||||
prefix: "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}",
|
||||
},
|
||||
supplier: {
|
||||
value: options.supplier,
|
||||
},
|
||||
supplier_reference: {},
|
||||
description: {},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
responsible: {
|
||||
icon: 'fa-user',
|
||||
}
|
||||
},
|
||||
onSuccess: function(data) {
|
||||
location.href = `/order/purchase-order/${data.pk}/`;
|
||||
},
|
||||
title: '{% trans "Create Purchase Order" %}',
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function removeOrderRowFromOrderWizard(e) {
|
||||
/* Remove a part selection from an order form. */
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var row = $(src).attr('row');
|
||||
|
||||
$('#' + row).remove();
|
||||
}
|
||||
|
||||
function newSupplierPartFromOrderWizard(e) {
|
||||
/* Create a new supplier part directly from an order form.
|
||||
* Launches a secondary modal and (if successful),
|
||||
* back-populates the selected row.
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.srcElement || e.target;
|
||||
|
||||
var part = $(src).attr('part');
|
||||
|
||||
console.log('part: ' + part);
|
||||
|
||||
if (!part) {
|
||||
part = $(src).closest('button').attr('part');
|
||||
console.log('parent: ' + part);
|
||||
}
|
||||
|
||||
launchModalForm("/supplier-part/new/", {
|
||||
modal: '#modal-form-secondary',
|
||||
data: {
|
||||
part: part,
|
||||
},
|
||||
success: function(response) {
|
||||
/* A new supplier part has been created! */
|
||||
|
||||
var dropdown = '#id_supplier_part_' + part;
|
||||
|
||||
var option = new Option(response.text, response.pk, true, true);
|
||||
|
||||
$('#modal-form').find(dropdown).append(option).trigger('change');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function newPurchaseOrderFromOrderWizard(e) {
|
||||
/* Create a new purchase order directly from an order form.
|
||||
* Launches a secondary modal and (if successful),
|
||||
* back-fills the newly created purchase order.
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var supplier = $(src).attr('supplierid');
|
||||
|
||||
launchModalForm("/order/purchase-order/new/", {
|
||||
modal: '#modal-form-secondary',
|
||||
data: {
|
||||
supplier: supplier,
|
||||
},
|
||||
success: function(response) {
|
||||
/* A new purchase order has been created! */
|
||||
|
||||
var dropdown = '#id-purchase-order-' + supplier;
|
||||
|
||||
var option = new Option(response.text, response.pk, true, true);
|
||||
|
||||
$('#modal-form').find(dropdown).append(option).trigger('change');
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function editPurchaseOrderLineItem(e) {
|
||||
|
||||
/* Edit a purchase order line item in a modal form.
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var url = $(src).attr('url');
|
||||
|
||||
launchModalForm(url, {
|
||||
reload: true,
|
||||
});
|
||||
}
|
||||
|
||||
function removePurchaseOrderLineItem(e) {
|
||||
|
||||
/* Delete a purchase order line item in a modal form
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var url = $(src).attr('url');
|
||||
|
||||
launchModalForm(url, {
|
||||
reload: true,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadPurchaseOrderTable(table, options) {
|
||||
/* Create a purchase-order table */
|
||||
|
||||
options.params = options.params || {};
|
||||
|
||||
options.params['supplier_detail'] = true;
|
||||
|
||||
var filters = loadTableFilters("purchaseorder");
|
||||
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
|
||||
options.url = options.url || '{% url "api-po-list" %}';
|
||||
|
||||
setupFilterList("purchaseorder", $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: options.url,
|
||||
queryParams: filters,
|
||||
name: 'purchaseorder',
|
||||
groupBy: false,
|
||||
sidePagination: 'server',
|
||||
original: options.params,
|
||||
formatNoMatches: function() { return '{% trans "No purchase orders found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
title: '',
|
||||
visible: true,
|
||||
checkbox: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'reference',
|
||||
title: '{% trans "Purchase Order" %}',
|
||||
sortable: true,
|
||||
switchable: false,
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var prefix = "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}";
|
||||
|
||||
if (prefix) {
|
||||
value = `${prefix}${value}`;
|
||||
}
|
||||
|
||||
var html = renderLink(value, `/order/purchase-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'supplier_detail',
|
||||
title: '{% trans "Supplier" %}',
|
||||
sortable: true,
|
||||
sortName: 'supplier__name',
|
||||
formatter: function(value, row, index, field) {
|
||||
return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'supplier_reference',
|
||||
title: '{% trans "Supplier Reference" %}',
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row, index, field) {
|
||||
return purchaseOrderStatusDisplay(row.status, row.status_text);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Date" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}',
|
||||
sortable: true,
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
function loadSalesOrderTable(table, options) {
|
||||
|
||||
options.params = options.params || {};
|
||||
options.params['customer_detail'] = true;
|
||||
|
||||
var filters = loadTableFilters("salesorder");
|
||||
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
|
||||
options.url = options.url || '{% url "api-so-list" %}';
|
||||
|
||||
setupFilterList("salesorder", $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: options.url,
|
||||
queryParams: filters,
|
||||
name: 'salesorder',
|
||||
groupBy: false,
|
||||
sidePagination: 'server',
|
||||
original: options.params,
|
||||
formatNoMatches: function() { return '{% trans "No sales orders found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
title: '',
|
||||
checkbox: true,
|
||||
visible: true,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'reference',
|
||||
title: '{% trans "Sales Order" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
|
||||
|
||||
if (prefix) {
|
||||
value = `${prefix}${value}`;
|
||||
}
|
||||
|
||||
var html = renderLink(value, `/order/sales-order/${row.pk}/`);
|
||||
|
||||
if (row.overdue) {
|
||||
html += makeIconBadge('fa-calendar-times icon-red', '{% trans "Order is overdue" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
},
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
sortName: 'customer__name',
|
||||
field: 'customer_detail',
|
||||
title: '{% trans "Customer" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
|
||||
if (!row.customer_detail) {
|
||||
return '{% trans "Invalid Customer" %}';
|
||||
}
|
||||
|
||||
return imageHoverIcon(row.customer_detail.image) + renderLink(row.customer_detail.name, `/company/${row.customer}/sales-orders/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'customer_reference',
|
||||
title: '{% trans "Customer Reference" %}',
|
||||
},
|
||||
{
|
||||
sortable: false,
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'status',
|
||||
title: '{% trans "Status" %}',
|
||||
formatter: function(value, row, index, field) {
|
||||
return salesOrderStatusDisplay(row.status, row.status_text);
|
||||
}
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'creation_date',
|
||||
title: '{% trans "Creation Date" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'target_date',
|
||||
title: '{% trans "Target Date" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'shipment_date',
|
||||
title: '{% trans "Shipment Date" %}',
|
||||
},
|
||||
{
|
||||
sortable: true,
|
||||
field: 'line_items',
|
||||
title: '{% trans "Items" %}'
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadSalesOrderAllocationTable(table, options={}) {
|
||||
/**
|
||||
* Load a table with SalesOrderAllocation items
|
||||
*/
|
||||
|
||||
options.params = options.params || {};
|
||||
|
||||
options.params['location_detail'] = true;
|
||||
options.params['part_detail'] = true;
|
||||
options.params['item_detail'] = true;
|
||||
options.params['order_detail'] = true;
|
||||
|
||||
var filters = loadTableFilters("salesorderallocation");
|
||||
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
|
||||
setupFilterList("salesorderallocation", $(table));
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-so-allocation-list" %}',
|
||||
queryParams: filters,
|
||||
name: 'salesorderallocation',
|
||||
groupBy: false,
|
||||
search: false,
|
||||
paginationVAlign: 'bottom',
|
||||
original: options.params,
|
||||
formatNoMatches: function() { return '{% trans "No sales order allocations found" %}'; },
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'order',
|
||||
switchable: false,
|
||||
title: '{% trans "Order" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
|
||||
|
||||
var ref = `${prefix}${row.order_detail.reference}`;
|
||||
|
||||
return renderLink(ref, `/order/sales-order/${row.order}/`);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'item',
|
||||
title: '{% trans "Stock Item" %}',
|
||||
formatter: function(value, row) {
|
||||
// Render a link to the particular stock item
|
||||
|
||||
var link = `/stock/item/${row.item}/`;
|
||||
var text = `{% trans "Stock Item" %} ${row.item}`;
|
||||
|
||||
return renderLink(text, link);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'location',
|
||||
title: '{% trans "Location" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (!value) {
|
||||
return '{% trans "Location not specified" %}';
|
||||
}
|
||||
|
||||
var link = `/stock/location/${value}`;
|
||||
var text = row.location_detail.description;
|
||||
|
||||
return renderLink(text, link);
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'quantity',
|
||||
title: '{% trans "Quantity" %}',
|
||||
sortable: true,
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
1201
InvenTree/templates/js/translated/part.js
Normal file
1201
InvenTree/templates/js/translated/part.js
Normal file
File diff suppressed because it is too large
Load Diff
357
InvenTree/templates/js/translated/report.js
Normal file
357
InvenTree/templates/js/translated/report.js
Normal file
@ -0,0 +1,357 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
function selectReport(reports, items, options={}) {
|
||||
/**
|
||||
* Present the user with the available reports,
|
||||
* and allow them to select which report to print.
|
||||
*
|
||||
* The intent is that the available report templates have been requested
|
||||
* (via AJAX) from the server.
|
||||
*/
|
||||
|
||||
// If there is only a single report available, just print!
|
||||
if (reports.length == 1) {
|
||||
if (options.success) {
|
||||
options.success(reports[0].pk);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var modal = options.modal || '#modal-form';
|
||||
|
||||
var report_list = makeOptionsList(
|
||||
reports,
|
||||
function(item) {
|
||||
var text = item.name;
|
||||
|
||||
if (item.description) {
|
||||
text += ` - ${item.description}`;
|
||||
}
|
||||
|
||||
return text;
|
||||
},
|
||||
function(item) {
|
||||
return item.pk;
|
||||
}
|
||||
);
|
||||
|
||||
// Construct form
|
||||
var html = '';
|
||||
|
||||
if (items.length > 0) {
|
||||
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
${items.length} {% trans "items 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_report'>
|
||||
{% trans "Select Report Template" %}
|
||||
</label>
|
||||
<div class='controls'>
|
||||
<select id='id_report' class='select form-control name='report'>
|
||||
${report_list}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</form>`;
|
||||
|
||||
openModal({
|
||||
modal: modal,
|
||||
});
|
||||
|
||||
modalEnable(modal, true);
|
||||
modalSetTitle(modal, '{% trans "Select Test Report Template" %}');
|
||||
modalSetContent(modal, html);
|
||||
|
||||
attachSelect(modal);
|
||||
|
||||
modalSubmit(modal, function() {
|
||||
|
||||
var label = $(modal).find('#id_report');
|
||||
|
||||
var pk = label.val();
|
||||
|
||||
closeModal(modal);
|
||||
|
||||
if (options.success) {
|
||||
options.success(pk);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
function printTestReports(items, options={}) {
|
||||
/**
|
||||
* Print test reports for the provided stock item(s)
|
||||
*/
|
||||
|
||||
if (items.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Stock Items" %}',
|
||||
'{% trans "Stock item(s) must be selected before printing reports" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available reports from the server
|
||||
inventreeGet(
|
||||
'{% url "api-stockitem-testreport-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
items: items,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected stock item(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select report template to print
|
||||
selectReport(
|
||||
response,
|
||||
items,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/test/${pk}/print/?`;
|
||||
|
||||
items.forEach(function(item) {
|
||||
href += `item=${item}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
function printBuildReports(builds, options={}) {
|
||||
/**
|
||||
* Print Build report for the provided build(s)
|
||||
*/
|
||||
|
||||
if (builds.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Builds" %}',
|
||||
'{% trans "Build(s) must be selected before printing reports" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
inventreeGet(
|
||||
'{% url "api-build-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
builds: builds,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected build(s)" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select which report to print
|
||||
selectReport(
|
||||
response,
|
||||
builds,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/build/${pk}/print/?`;
|
||||
|
||||
builds.forEach(function(build) {
|
||||
href += `build=${build}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function printBomReports(parts, options={}) {
|
||||
/**
|
||||
* Print BOM reports for the provided part(s)
|
||||
*/
|
||||
|
||||
if (parts.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "Part(s) must be selected before printing reports" %}'
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request available reports from the server
|
||||
inventreeGet(
|
||||
'{% url "api-bom-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
parts: parts,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected part(s)" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select which report to print
|
||||
selectReport(
|
||||
response,
|
||||
parts,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/bom/${pk}/print/?`;
|
||||
|
||||
parts.forEach(function(part) {
|
||||
href += `part=${part}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function printPurchaseOrderReports(orders, options={}) {
|
||||
/**
|
||||
* Print PO reports for the provided purchase order(s)
|
||||
*/
|
||||
|
||||
if (orders.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Purchase Orders" %}',
|
||||
'{% trans "Purchase Order(s) must be selected before printing report" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request avaiable report templates
|
||||
inventreeGet(
|
||||
'{% url "api-po-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
orders: orders,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected orders" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select report template
|
||||
selectReport(
|
||||
response,
|
||||
orders,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/po/${pk}/print/?`;
|
||||
|
||||
orders.forEach(function(order) {
|
||||
href += `order=${order}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
function printSalesOrderReports(orders, options={}) {
|
||||
/**
|
||||
* Print SO reports for the provided purchase order(s)
|
||||
*/
|
||||
|
||||
if (orders.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Sales Orders" %}',
|
||||
'{% trans "Sales Order(s) must be selected before printing report" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Request avaiable report templates
|
||||
inventreeGet(
|
||||
'{% url "api-so-report-list" %}',
|
||||
{
|
||||
enabled: true,
|
||||
orders: orders,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "No Reports Found" %}',
|
||||
'{% trans "No report templates found which match selected orders" %}',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Select report template
|
||||
selectReport(
|
||||
response,
|
||||
orders,
|
||||
{
|
||||
success: function(pk) {
|
||||
var href = `/api/report/so/${pk}/print/?`;
|
||||
|
||||
orders.forEach(function(order) {
|
||||
href += `order=${order}&`;
|
||||
});
|
||||
|
||||
window.location.href = href;
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
1781
InvenTree/templates/js/translated/stock.js
Normal file
1781
InvenTree/templates/js/translated/stock.js
Normal file
File diff suppressed because it is too large
Load Diff
363
InvenTree/templates/js/translated/table_filters.js
Normal file
363
InvenTree/templates/js/translated/table_filters.js
Normal file
@ -0,0 +1,363 @@
|
||||
{% load i18n %}
|
||||
{% load status_codes %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% include "status_codes.html" with label='stock' options=StockStatus.list %}
|
||||
{% include "status_codes.html" with label='stockHistory' options=StockHistoryCode.list %}
|
||||
{% include "status_codes.html" with label='build' options=BuildStatus.list %}
|
||||
{% include "status_codes.html" with label='purchaseOrder' options=PurchaseOrderStatus.list %}
|
||||
{% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %}
|
||||
|
||||
|
||||
function getAvailableTableFilters(tableKey) {
|
||||
|
||||
tableKey = tableKey.toLowerCase();
|
||||
|
||||
// Filters for "variant" table
|
||||
if (tableKey == "variants") {
|
||||
return {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
},
|
||||
template: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
},
|
||||
virtual: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Virtual" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for Bill of Materials table
|
||||
if (tableKey == "bom") {
|
||||
return {
|
||||
sub_part_trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable Part" %}'
|
||||
},
|
||||
sub_part_assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembled Part" %}',
|
||||
},
|
||||
validated: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Validated" %}',
|
||||
},
|
||||
inherited: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Inherited" %}',
|
||||
},
|
||||
allow_variants: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Allow Variant Stock" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "used in" table
|
||||
if (tableKey == 'usedin') {
|
||||
return {
|
||||
'part_active': {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for "stock location" table
|
||||
if (tableKey == "location") {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include sublocations" %}',
|
||||
description: '{% trans "Include locations" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for "part category" table
|
||||
if (tableKey == "category") {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include subcategories" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "customer stock" table (really a subset of "stock")
|
||||
if (tableKey == "customerstock") {
|
||||
return {
|
||||
serialized: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Is Serialized" %}',
|
||||
},
|
||||
serial_gte: {
|
||||
title: '{% trans "Serial number GTE" %}',
|
||||
description: '{% trans "Serial number greater than or equal to" %}'
|
||||
},
|
||||
serial_lte: {
|
||||
title: '{% trans "Serial number LTE" %}',
|
||||
description: '{% trans "Serial number less than or equal to" %}',
|
||||
},
|
||||
serial: {
|
||||
title: '{% trans "Serial number" %}',
|
||||
description: '{% trans "Serial number" %}'
|
||||
},
|
||||
batch: {
|
||||
title: '{% trans "Batch" %}',
|
||||
description: '{% trans "Batch code" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Stock" table
|
||||
if (tableKey == 'stock') {
|
||||
return {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active parts" %}',
|
||||
description: '{% trans "Show stock for active parts" %}',
|
||||
},
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembly" %}',
|
||||
description: '{% trans "Part is an assembly" %}',
|
||||
},
|
||||
allocated: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Is allocated" %}',
|
||||
description: '{% trans "Item has been allocated" %}',
|
||||
},
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include sublocations" %}',
|
||||
description: '{% trans "Include stock in sublocations" %}',
|
||||
},
|
||||
depleted: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Depleted" %}',
|
||||
description: '{% trans "Show stock items which are depleted" %}',
|
||||
},
|
||||
{% settings_value "STOCK_ENABLE_EXPIRY" as expiry %}
|
||||
{% if expiry %}
|
||||
expired: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Expired" %}',
|
||||
description: '{% trans "Show stock items which have expired" %}',
|
||||
},
|
||||
stale: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Stale" %}',
|
||||
description: '{% trans "Show stock which is close to expiring" %}',
|
||||
},
|
||||
{% endif %}
|
||||
in_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "In Stock" %}',
|
||||
description: '{% trans "Show items which are in stock" %}',
|
||||
},
|
||||
is_building: {
|
||||
type: 'bool',
|
||||
title: '{% trans "In Production" %}',
|
||||
description: '{% trans "Show items which are in production" %}',
|
||||
},
|
||||
include_variants: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include Variants" %}',
|
||||
description: '{% trans "Include stock items for variant parts" %}',
|
||||
},
|
||||
installed: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Installed" %}',
|
||||
description: '{% trans "Show stock items which are installed in another item" %}',
|
||||
},
|
||||
sent_to_customer: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Sent to customer" %}',
|
||||
description: '{% trans "Show items which have been assigned to a customer" %}',
|
||||
},
|
||||
serialized: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Is Serialized" %}',
|
||||
},
|
||||
serial: {
|
||||
title: '{% trans "Serial number" %}',
|
||||
description: '{% trans "Serial number" %}'
|
||||
},
|
||||
serial_gte: {
|
||||
title: '{% trans "Serial number GTE" %}',
|
||||
description: '{% trans "Serial number greater than or equal to" %}'
|
||||
},
|
||||
serial_lte: {
|
||||
title: '{% trans "Serial number LTE" %}',
|
||||
description: '{% trans "Serial number less than or equal to" %}',
|
||||
},
|
||||
status: {
|
||||
options: stockCodes,
|
||||
title: '{% trans "Stock status" %}',
|
||||
description: '{% trans "Stock status" %}',
|
||||
},
|
||||
batch: {
|
||||
title: '{% trans "Batch" %}',
|
||||
description: '{% trans "Batch code" %}',
|
||||
},
|
||||
has_purchase_price: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has purchase price" %}',
|
||||
description: '{% trans "Show stock items which have a purchase price set" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the 'stock test' table
|
||||
if (tableKey == 'stocktests') {
|
||||
return {
|
||||
result: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Test result" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the 'part test template' table
|
||||
if (tableKey == 'parttests') {
|
||||
return {
|
||||
required: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Required" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Build" table
|
||||
if (tableKey == 'build') {
|
||||
return {
|
||||
status: {
|
||||
title: '{% trans "Build status" %}',
|
||||
options: buildCodes,
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Order" table
|
||||
if (tableKey == "purchaseorder") {
|
||||
|
||||
return {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
options: purchaseOrderCodes,
|
||||
},
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (tableKey == "salesorder") {
|
||||
return {
|
||||
status: {
|
||||
title: '{% trans "Order status" %}',
|
||||
options: salesOrderCodes,
|
||||
},
|
||||
outstanding: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Outstanding" %}',
|
||||
},
|
||||
overdue: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Overdue" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
if (tableKey == 'supplier-part') {
|
||||
return {
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active parts" %}',
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "Parts" table
|
||||
if (tableKey == "parts") {
|
||||
return {
|
||||
cascade: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include parts in subcategories" %}',
|
||||
},
|
||||
has_ipn: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Has IPN" %}',
|
||||
description: '{% trans "Part has internal part number" %}',
|
||||
},
|
||||
active: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Active" %}',
|
||||
description: '{% trans "Show active parts" %}',
|
||||
},
|
||||
is_template: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Template" %}',
|
||||
},
|
||||
has_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Stock available" %}'
|
||||
},
|
||||
low_stock: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Low stock" %}',
|
||||
},
|
||||
assembly: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Assembly" %}',
|
||||
},
|
||||
component: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Component" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Starred" %}',
|
||||
},
|
||||
salable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Salable" %}',
|
||||
},
|
||||
trackable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Trackable" %}',
|
||||
},
|
||||
purchaseable: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Purchasable" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Finally, no matching key
|
||||
return {};
|
||||
}
|
375
InvenTree/templates/js/translated/tables.js
Normal file
375
InvenTree/templates/js/translated/tables.js
Normal file
@ -0,0 +1,375 @@
|
||||
{% load i18n %}
|
||||
|
||||
|
||||
function reloadtable(table) {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
|
||||
|
||||
function editButton(url, text='Edit') {
|
||||
return "<button class='btn btn-success edit-button btn-sm' type='button' url='" + url + "'>" + text + "</button>";
|
||||
}
|
||||
|
||||
|
||||
function deleteButton(url, text='Delete') {
|
||||
return "<button class='btn btn-danger delete-button btn-sm' type='button' url='" + url + "'>" + text + "</button>";
|
||||
}
|
||||
|
||||
|
||||
function renderLink(text, url, options={}) {
|
||||
if (url === null || url === undefined || url === '') {
|
||||
return text;
|
||||
}
|
||||
|
||||
var max_length = options.max_length || -1;
|
||||
|
||||
var remove_http = options.remove_http || false;
|
||||
|
||||
// Shorten the displayed length if required
|
||||
if ((max_length > 0) && (text.length > max_length)) {
|
||||
var slice_length = (max_length - 3) / 2;
|
||||
|
||||
var text_start = text.slice(0, slice_length);
|
||||
var text_end = text.slice(-slice_length);
|
||||
|
||||
text = `${text_start}...${text_end}`;
|
||||
}
|
||||
|
||||
return '<a href="' + url + '">' + text + '</a>';
|
||||
}
|
||||
|
||||
|
||||
function enableButtons(elements, enabled) {
|
||||
for (let item of elements) {
|
||||
$(item).prop('disabled', !enabled);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function linkButtonsToSelection(table, buttons) {
|
||||
/* Link a bootstrap-table object to one or more buttons.
|
||||
* The buttons will only be enabled if there is at least one row selected
|
||||
*/
|
||||
|
||||
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(row) {
|
||||
enableButtons(buttons, table.bootstrapTable('getSelections').length > 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function isNumeric(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Reload a table which has already been made into a bootstrap table.
|
||||
* New filters can be optionally provided, to change the query params.
|
||||
*/
|
||||
function reloadTableFilters(table, filters) {
|
||||
|
||||
// Simply perform a refresh
|
||||
if (filters == null) {
|
||||
table.bootstrapTable('refresh');
|
||||
return;
|
||||
}
|
||||
|
||||
// More complex refresh with new filters supplied
|
||||
var options = table.bootstrapTable('getOptions');
|
||||
|
||||
// Construct a new list of filters to use for the query
|
||||
var params = {};
|
||||
|
||||
for (var key in filters) {
|
||||
params[key] = filters[key];
|
||||
}
|
||||
|
||||
// Original query params will override
|
||||
if (options.original != null) {
|
||||
for (var key in options.original) {
|
||||
params[key] = options.original[key];
|
||||
}
|
||||
}
|
||||
|
||||
options.queryParams = function(tableParams) {
|
||||
return convertQueryParameters(tableParams, params);
|
||||
};
|
||||
|
||||
table.bootstrapTable('refreshOptions', options);
|
||||
table.bootstrapTable('refresh', filters);
|
||||
}
|
||||
|
||||
|
||||
function visibleColumnString(columns) {
|
||||
/* Generate a list of "visible" columns to save to file. */
|
||||
|
||||
var fields = [];
|
||||
|
||||
columns.forEach(function(column) {
|
||||
if (column.switchable && column.visible) {
|
||||
fields.push(column.field);
|
||||
}
|
||||
});
|
||||
|
||||
return fields.join(',');
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Convert bootstrap-table style parameters to "InvenTree" style
|
||||
*/
|
||||
function convertQueryParameters(params, filters) {
|
||||
|
||||
// Override the way that we ask the server to sort results
|
||||
// It seems bootstrap-table does not offer a "native" way to do this...
|
||||
if ('sort' in params) {
|
||||
var order = params['order'];
|
||||
|
||||
var ordering = params['sort'] || null;
|
||||
|
||||
if (ordering) {
|
||||
|
||||
if (order == 'desc') {
|
||||
ordering = `-${ordering}`;
|
||||
}
|
||||
|
||||
params['ordering'] = ordering;
|
||||
}
|
||||
|
||||
delete params['sort'];
|
||||
delete params['order'];
|
||||
|
||||
}
|
||||
|
||||
for (var key in filters) {
|
||||
params[key] = filters[key];
|
||||
}
|
||||
|
||||
// Add "order" back in (if it was originally specified by InvenTree)
|
||||
// Annoyingly, "order" shadows some field names in InvenTree...
|
||||
if ('order' in filters) {
|
||||
params['order'] = filters['order'];
|
||||
}
|
||||
|
||||
// Remove searchable[] array (generated by bootstrap-table)
|
||||
if ('searchable' in params) {
|
||||
delete params['searchable'];
|
||||
}
|
||||
|
||||
if ('sortable' in params) {
|
||||
delete params['sortable'];
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
||||
/* Wrapper function for bootstrapTable.
|
||||
* Sets some useful defaults, and manage persistent settings.
|
||||
*/
|
||||
$.fn.inventreeTable = function(options) {
|
||||
|
||||
var table = this;
|
||||
|
||||
var tableName = options.name || 'table';
|
||||
|
||||
var varName = tableName + '-pagesize';
|
||||
|
||||
// Pagingation options (can be server-side or client-side as specified by the caller)
|
||||
if (!options.disablePagination) {
|
||||
options.pagination = true;
|
||||
options.paginationVAlign = options.paginationVAlign || 'both';
|
||||
options.pageSize = inventreeLoad(varName, 25);
|
||||
options.pageList = [25, 50, 100, 250, 'all'];
|
||||
options.totalField = 'count';
|
||||
options.dataField = 'results';
|
||||
}
|
||||
|
||||
// Extract query params
|
||||
var filters = options.queryParams || options.filters || {};
|
||||
|
||||
options.queryParams = function(params) {
|
||||
return convertQueryParameters(params, filters);
|
||||
};
|
||||
|
||||
options.rememberOrder = true;
|
||||
|
||||
if (options.sortable == null) {
|
||||
options.sortable = true;
|
||||
}
|
||||
|
||||
if (options.search == null) {
|
||||
options.search = true;
|
||||
}
|
||||
|
||||
if (options.showColumns == null) {
|
||||
options.showColumns = true;
|
||||
}
|
||||
|
||||
// Callback to save pagination data
|
||||
options.onPageChange = function(number, size) {
|
||||
inventreeSave(varName, size);
|
||||
};
|
||||
|
||||
// Callback when a column is changed
|
||||
options.onColumnSwitch = function(field, checked) {
|
||||
|
||||
var columns = table.bootstrapTable('getVisibleColumns');
|
||||
|
||||
var text = visibleColumnString(columns);
|
||||
|
||||
// Save visible columns
|
||||
inventreeSave(`table_columns_${tableName}`, text);
|
||||
};
|
||||
|
||||
// Standard options for all tables
|
||||
table.bootstrapTable(options);
|
||||
|
||||
// Load visible column list from memory
|
||||
// Load visible column list
|
||||
var visibleColumns = inventreeLoad(`table_columns_${tableName}`, null);
|
||||
|
||||
// If a set of visible columns has been saved, load!
|
||||
if (visibleColumns) {
|
||||
var columns = visibleColumns.split(",");
|
||||
|
||||
// Which columns are currently visible?
|
||||
var visible = table.bootstrapTable('getVisibleColumns');
|
||||
|
||||
if (visible && Array.isArray(visible)) {
|
||||
visible.forEach(function(column) {
|
||||
|
||||
// Visible field should *not* be visible! (hide it!)
|
||||
if (column.switchable && !columns.includes(column.field)) {
|
||||
table.bootstrapTable('hideColumn', column.field);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
console.log('Could not get list of visible columns!');
|
||||
}
|
||||
}
|
||||
|
||||
// Optionally, link buttons to the table selection
|
||||
if (options.buttons) {
|
||||
linkButtonsToSelection(table, options.buttons);
|
||||
}
|
||||
}
|
||||
|
||||
function customGroupSorter(sortName, sortOrder, sortData) {
|
||||
|
||||
var order = sortOrder === 'desc' ? -1 : 1;
|
||||
|
||||
sortData.sort(function(a, b) {
|
||||
|
||||
// Extract default field values
|
||||
// Allow multi-level access if required
|
||||
// Ref: https://stackoverflow.com/a/6394168
|
||||
|
||||
function extract(obj, i) {
|
||||
return obj[i];
|
||||
}
|
||||
|
||||
var aa = sortName.split('.').reduce(extract, a);
|
||||
var bb = sortName.split('.').reduce(extract, b);
|
||||
|
||||
// Extract parent information
|
||||
var aparent = a._data && a._data['parent-index'];
|
||||
var bparent = b._data && b._data['parent-index'];
|
||||
|
||||
// If either of the comparisons are in a group
|
||||
if (aparent || bparent) {
|
||||
|
||||
// If the parents are different (or one item does not have a parent,
|
||||
// then we need to extract the parent value for the selected column.
|
||||
|
||||
if (aparent != bparent) {
|
||||
if (aparent) {
|
||||
aa = a._data['table'].options.groupByFormatter(sortName, 0, a._data['group-data']);
|
||||
}
|
||||
|
||||
if (bparent) {
|
||||
bb = b._data['table'].options.groupByFormatter(sortName, 0, b._data['group-data']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (aa === undefined || aa === null) {
|
||||
aa = '';
|
||||
}
|
||||
if (bb === undefined || bb === null) {
|
||||
bb = '';
|
||||
}
|
||||
|
||||
if (isNumeric(aa) && isNumeric(bb)) {
|
||||
if (aa < bb) {
|
||||
return order * -1;
|
||||
} else if (aa > bb) {
|
||||
return order;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
aa = aa.toString();
|
||||
bb = bb.toString();
|
||||
|
||||
var cmp = aa.localeCompare(bb);
|
||||
|
||||
if (cmp === -1) {
|
||||
return order * -1;
|
||||
} else if (cmp === 1) {
|
||||
return order;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Expose default bootstrap table string literals to translation layer
|
||||
(function ($) {
|
||||
'use strict';
|
||||
|
||||
$.fn.bootstrapTable.locales['en-US-custom'] = {
|
||||
formatLoadingMessage: function () {
|
||||
return '{% trans "Loading data" %}';
|
||||
},
|
||||
formatRecordsPerPage: function (pageNumber) {
|
||||
return `${pageNumber} {% trans "rows per page" %}`;
|
||||
},
|
||||
formatShowingRows: function (pageFrom, pageTo, totalRows) {
|
||||
return `{% trans "Showing" %} ${pageFrom} {% trans "to" %} ${pageTo} {% trans "of" %} ${totalRows} {% trans "rows" %}`;
|
||||
},
|
||||
formatSearch: function () {
|
||||
return '{% trans "Search" %}';
|
||||
},
|
||||
formatNoMatches: function () {
|
||||
return '{% trans "No matching results" %}';
|
||||
},
|
||||
formatPaginationSwitch: function () {
|
||||
return '{% trans "Hide/Show pagination" %}';
|
||||
},
|
||||
formatRefresh: function () {
|
||||
return '{% trans "Refresh" %}';
|
||||
},
|
||||
formatToggle: function () {
|
||||
return '{% trans "Toggle" %}';
|
||||
},
|
||||
formatColumns: function () {
|
||||
return '{% trans "Columns" %}';
|
||||
},
|
||||
formatAllRows: function () {
|
||||
return '{% trans "All" %}';
|
||||
}
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['en-US-custom']);
|
||||
|
||||
})(jQuery);
|
Reference in New Issue
Block a user