mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 04:55:44 +00:00
fix trailing
This commit is contained in:
@ -14,7 +14,7 @@
|
||||
{% block body_class %}login-screen{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<!--
|
||||
<!--
|
||||
Background Image Attribution: https://unsplash.com/photos/Ixvv3YZkd7w
|
||||
-->
|
||||
<div class='container-fluid'>
|
||||
|
@ -224,7 +224,7 @@
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
enableSidebar(
|
||||
'search',
|
||||
|
@ -46,7 +46,7 @@
|
||||
<div class='btn-group' role='group'>
|
||||
<button class='btn btn-success' id='import-part'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "Import Part" %}
|
||||
</button>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -36,7 +36,7 @@
|
||||
<div class='btn-group' role='group'>
|
||||
{% url 'admin:plugin_pluginconfig_changelist' as url %}
|
||||
{% include "admin_button.html" with url=url %}
|
||||
<button class="btn btn-success" id="install-plugin" title="{% trans 'Install Plugin' %}"><span class='fas fa-plus-circle'></span> {% trans "Install Plugin" %}</button>
|
||||
<button class="btn btn-success" id="install-plugin" title="{% trans 'Install Plugin' %}"><span class='fas fa-plus-circle'></span> {% trans "Install Plugin" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -52,7 +52,7 @@
|
||||
<th>{% trans "Version" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
||||
<tbody>
|
||||
{% plugin_list as pl_list %}
|
||||
{% for plugin_key, plugin in pl_list.items %}
|
||||
@ -133,7 +133,7 @@
|
||||
<th>{% trans "Message" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
|
||||
<tbody>
|
||||
{% for stage, errors in pl_errors.items %}
|
||||
{% for error_detail in errors %}
|
||||
|
@ -70,7 +70,7 @@
|
||||
<div class='alert alert-block alert-info'>
|
||||
{% trans 'The code information is pulled from the latest git commit for this plugin. It might not reflect official version numbers or information but the actual code running.' %}
|
||||
</div>
|
||||
|
||||
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="col">
|
||||
|
@ -99,7 +99,7 @@ $('table').find('.boolean-setting').change(function() {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
});
|
||||
|
||||
// Callback for when non-boolean settings are edited
|
||||
|
@ -76,7 +76,7 @@
|
||||
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
|
||||
{% if ALL_LANG or use_lang %}
|
||||
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
||||
{{ language.name_local }} ({{ lang_code }})
|
||||
{{ language.name_local }} ({{ lang_code }})
|
||||
{% if lang_translated %}
|
||||
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
||||
{% else %}
|
||||
@ -105,4 +105,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@ -25,11 +25,11 @@
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_PURCHASE_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_SHOW_SALES_ORDERS" user_setting=True icon='fa-truck' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_EXCLUDE_INACTIVE_SALES_ORDERS" user_setting=True icon='fa-eye-slash' %}
|
||||
|
||||
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_RESULTS" user_setting=True icon='fa-search' %}
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@ -50,7 +50,7 @@
|
||||
</head>
|
||||
|
||||
<body class='login-screen'>
|
||||
<!--
|
||||
<!--
|
||||
Background Image Attribution: https://unsplash.com/photos/Ixvv3YZkd7w
|
||||
-->
|
||||
<div class='container-fluid'>
|
||||
@ -130,4 +130,4 @@ $(document).ready(function () {
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -65,7 +65,7 @@
|
||||
|
||||
<title>
|
||||
{% block page_title %}
|
||||
{% inventree_title %}
|
||||
{% inventree_title %}
|
||||
{% endblock %}
|
||||
</title>
|
||||
</head>
|
||||
@ -91,7 +91,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<main class='col ps-md-2 pt-2 pe-2'>
|
||||
|
||||
|
||||
{% block alerts %}
|
||||
<div class='notification-area' id='alerts'>
|
||||
<!-- Div for displayed alerts -->
|
||||
@ -164,7 +164,7 @@
|
||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/chartjs-adapter-moment.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'easymde/easymde.min.js' %}"></script>
|
||||
|
||||
|
||||
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/qr-scanner.umd.min.js' %}"></script>
|
||||
@ -246,4 +246,4 @@ $(document).ready(function () {
|
||||
{% endblock %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
@ -68,7 +68,7 @@ function activatePanel(label, panel_name, options={}) {
|
||||
|
||||
function onPanelLoad(panel, callback) {
|
||||
// One-time callback when a panel is first displayed
|
||||
// Used to implement lazy-loading, rather than firing
|
||||
// Used to implement lazy-loading, rather than firing
|
||||
// multiple AJAX queries when the page is first loaded.
|
||||
|
||||
var panelId = `#panel-${panel}`;
|
||||
@ -130,12 +130,12 @@ function enableSidebar(label, options={}) {
|
||||
|
||||
// By default, the menu is "expanded"
|
||||
var state = localStorage.getItem(`inventree-menu-state-${label}`) || 'expanded';
|
||||
|
||||
|
||||
// We wish to "toggle" the state!
|
||||
setSidebarState(label, state == 'expanded' ? 'collapsed' : 'expanded');
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// Set the initial state (default = expanded)
|
||||
var state = localStorage.getItem(`inventree-menu-state-${label}`) || 'expanded';
|
||||
|
||||
@ -204,7 +204,7 @@ function enableBreadcrumbTree(options) {
|
||||
node = nodes[node.parent];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
roots.push(node);
|
||||
}
|
||||
@ -227,7 +227,7 @@ function enableBreadcrumbTree(options) {
|
||||
|
||||
// Toggle treeview visibilty
|
||||
$('#breadcrumb-tree-collapse').toggle();
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
@ -58,13 +58,13 @@ function editSetting(key, options={}) {
|
||||
// First, read the settings object from the server
|
||||
inventreeGet(url, {}, {
|
||||
success: function(response) {
|
||||
|
||||
|
||||
if (response.choices && response.choices.length > 0) {
|
||||
response.type = 'choice';
|
||||
reload_required = true;
|
||||
}
|
||||
|
||||
// Construct the field
|
||||
// Construct the field
|
||||
var fields = {
|
||||
value: {
|
||||
label: response.name,
|
||||
@ -77,7 +77,7 @@ function editSetting(key, options={}) {
|
||||
|
||||
// Foreign key lookup available!
|
||||
if (response.type == 'related field') {
|
||||
|
||||
|
||||
if (response.model_name && response.api_url) {
|
||||
fields.value.type = 'related field';
|
||||
fields.value.model = response.model_name.split('.').at(-1);
|
||||
|
@ -14,11 +14,11 @@
|
||||
$.urlParam = function(name) {
|
||||
// eslint-disable-next-line no-useless-escape
|
||||
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
|
||||
|
||||
|
||||
if (results == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
return decodeURI(results[1]) || 0;
|
||||
};
|
||||
|
||||
@ -80,9 +80,9 @@ function inventreeGet(url, filters={}, options={}) {
|
||||
|
||||
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
|
||||
*/
|
||||
|
@ -14,7 +14,7 @@
|
||||
|
||||
/*
|
||||
* Add callbacks to buttons for creating new attachments.
|
||||
*
|
||||
*
|
||||
* Note: Attachments can also be external links!
|
||||
*/
|
||||
function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
@ -46,7 +46,7 @@ function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
};
|
||||
|
||||
Object.assign(link_fields, fields);
|
||||
|
||||
|
||||
constructForm(url, {
|
||||
fields: link_fields,
|
||||
method: 'POST',
|
||||
@ -88,7 +88,7 @@ function loadAttachmentTable(url, options) {
|
||||
constructForm(`${url}${pk}/`, {
|
||||
fields: {
|
||||
link: {},
|
||||
comment: {},
|
||||
comment: {},
|
||||
},
|
||||
processResults: function(data, fields, opts) {
|
||||
// Remove the "link" field if the attachment is a file!
|
||||
@ -100,7 +100,7 @@ function loadAttachmentTable(url, options) {
|
||||
title: '{% trans "Edit Attachment" %}',
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
// Add callback for 'delete' button
|
||||
$(table).find('.button-attachment-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
@ -324,7 +324,7 @@ function barcodeDialog(title, options={}) {
|
||||
function barcodeScanDialog() {
|
||||
/*
|
||||
* Perform a barcode scan,
|
||||
* and (potentially) redirect the browser
|
||||
* and (potentially) redirect the browser
|
||||
*/
|
||||
|
||||
var modal = '#modal-form';
|
||||
@ -345,9 +345,9 @@ function barcodeScanDialog() {
|
||||
'warning'
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -647,7 +647,7 @@ function scanItemsIntoLocation(item_list, options={}) {
|
||||
item_list.forEach(function(item) {
|
||||
items.push({
|
||||
pk: item.pk || item.id,
|
||||
quantity: item.quantity,
|
||||
quantity: item.quantity,
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -95,7 +95,7 @@ function constructBomUploadTable(data, options={}) {
|
||||
|
||||
// Handle any errors raised by initial data import
|
||||
if (row.data.errors.part) {
|
||||
addFieldErrorMessage(`items_sub_part_${idx}`, row.data.errors.part);
|
||||
addFieldErrorMessage(`items_sub_part_${idx}`, row.data.errors.part);
|
||||
}
|
||||
|
||||
if (row.data.errors.quantity) {
|
||||
@ -156,7 +156,7 @@ function constructBomUploadTable(data, options={}) {
|
||||
|
||||
// Request API endpoint options
|
||||
getApiEndpointOptions('{% url "api-bom-list" %}', function(response) {
|
||||
|
||||
|
||||
var fields = response.actions.POST;
|
||||
|
||||
data.rows.forEach(function(row, idx) {
|
||||
@ -543,7 +543,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
${part_thumb} ${part_name} - <em>${part_desc}</em>
|
||||
</div>
|
||||
`;
|
||||
|
||||
|
||||
// Add a table of individual rows
|
||||
html += `
|
||||
<table class='table table-striped table-condensed' id='substitute-table'>
|
||||
@ -643,7 +643,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
|
||||
// Re-enable the "submit" button
|
||||
$(opts.modal).find('#modal-form-submit').prop('disabled', false);
|
||||
|
||||
|
||||
// Reload the parent BOM table
|
||||
reloadParentTable();
|
||||
}
|
||||
@ -659,7 +659,7 @@ function deleteBomItems(items, options={}) {
|
||||
|
||||
var sub_part = item.sub_part_detail;
|
||||
var thumb = thumbnailImage(sub_part.thumbnail || sub_part.image);
|
||||
|
||||
|
||||
var html = `
|
||||
<tr>
|
||||
<td>${thumb} ${sub_part.full_name}</td>
|
||||
@ -709,7 +709,7 @@ function deleteBomItems(items, options={}) {
|
||||
function deleteNextBomItem() {
|
||||
|
||||
if (items.length > 0) {
|
||||
|
||||
|
||||
var item = items.shift();
|
||||
|
||||
inventreeDelete(`/api/bom/${item.pk}/`,
|
||||
@ -735,13 +735,13 @@ function deleteBomItems(items, options={}) {
|
||||
|
||||
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
|
||||
*/
|
||||
|
||||
@ -831,10 +831,10 @@ function loadBomTable(table, options={}) {
|
||||
var html = '';
|
||||
|
||||
var sub_part = row.sub_part_detail;
|
||||
|
||||
|
||||
// Display an extra icon if this part is an assembly
|
||||
if (sub_part.assembly) {
|
||||
|
||||
|
||||
if (row.sub_assembly_received) {
|
||||
// Data received, ignore
|
||||
} else if (row.sub_assembly_requested) {
|
||||
@ -895,7 +895,7 @@ function loadBomTable(table, options={}) {
|
||||
text = parseFloat(text);
|
||||
|
||||
if (row.optional) {
|
||||
text += ' ({% trans "Optional" %})';
|
||||
text += ' ({% trans "Optional" %})';
|
||||
}
|
||||
|
||||
if (row.overage) {
|
||||
@ -920,7 +920,7 @@ function loadBomTable(table, options={}) {
|
||||
var variant_stock = row.allow_variants ? (row.available_variant_stock || 0) : 0;
|
||||
|
||||
var available_stock = availableQuantity(row);
|
||||
|
||||
|
||||
var text = `${available_stock}`;
|
||||
|
||||
if (available_stock <= 0) {
|
||||
@ -957,7 +957,7 @@ function loadBomTable(table, options={}) {
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (show_pricing) {
|
||||
cols.push({
|
||||
field: 'purchase_price_range',
|
||||
@ -1139,7 +1139,7 @@ function loadBomTable(table, options={}) {
|
||||
row.sub_assembly_received = true;
|
||||
|
||||
$(table).bootstrapTable('updateByUniqueId', bom_pk, row, true);
|
||||
|
||||
|
||||
table.bootstrapTable('append', response);
|
||||
},
|
||||
error: function(xhr) {
|
||||
@ -1302,10 +1302,10 @@ function loadBomTable(table, options={}) {
|
||||
* Arguments:
|
||||
* - table: The ID string of the table element e.g. '#used-in-table'
|
||||
* - part_id: The ID (PK) of the part we are interested in
|
||||
*
|
||||
*
|
||||
* Options:
|
||||
* -
|
||||
*
|
||||
* -
|
||||
*
|
||||
* The following "options" are available.
|
||||
*/
|
||||
function loadUsedInTable(table, part_id, options={}) {
|
||||
@ -1343,14 +1343,14 @@ function loadUsedInTable(table, part_id, options={}) {
|
||||
// Iterate through each variant item
|
||||
for (var jj = 0; jj < variantData.length; jj++) {
|
||||
variantData[jj].parent = row.pk;
|
||||
|
||||
|
||||
var variant = variantData[jj];
|
||||
|
||||
// Add this variant to the table, augmented
|
||||
$(table).bootstrapTable('append', [{
|
||||
// Point the parent to the "master" assembly row
|
||||
// Point the parent to the "master" assembly row
|
||||
parent: row.pk,
|
||||
part: variant.pk,
|
||||
part: variant.pk,
|
||||
part_detail: variant,
|
||||
sub_part: row.sub_part,
|
||||
sub_part_detail: row.sub_part_detail,
|
||||
|
@ -126,7 +126,7 @@ function newBuildOrder(options={}) {
|
||||
|
||||
/* Construct a form to cancel a build order */
|
||||
function cancelBuildOrder(build_id, options={}) {
|
||||
|
||||
|
||||
constructForm(
|
||||
`/api/build/${build_id}/cancel/`,
|
||||
{
|
||||
@ -304,7 +304,7 @@ function createBuildOutput(build_id, options) {
|
||||
* Construct a set of output buttons for a particular build output
|
||||
*/
|
||||
function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
// Tracked parts? Must be individually allocated
|
||||
@ -355,7 +355,7 @@ function makeBuildOutputButtons(output_id, build_info, options={}) {
|
||||
|
||||
/*
|
||||
* Unallocate stock against a particular build order
|
||||
*
|
||||
*
|
||||
* Options:
|
||||
* - output: pk value for a stock item "build output"
|
||||
* - bom_item: pk value for a particular BOMItem (build item)
|
||||
@ -401,7 +401,7 @@ function unallocateStock(build_id, options={}) {
|
||||
* Launch a modal form to complete selected build outputs
|
||||
*/
|
||||
function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
|
||||
|
||||
if (outputs.length == 0) {
|
||||
showAlertDialog(
|
||||
'{% trans "Select Build Outputs" %}',
|
||||
@ -487,7 +487,7 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
});
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
|
||||
// Extract data elements from the form
|
||||
var data = {
|
||||
outputs: [],
|
||||
@ -631,7 +631,7 @@ function deleteBuildOutputs(build_id, outputs, options={}) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
$(opts.modal).find(`#output_row_${pk}`).remove();
|
||||
});
|
||||
});
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
var data = {
|
||||
@ -753,7 +753,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
|
||||
if (!value) {
|
||||
return '{% trans "Location not specified" %}';
|
||||
}
|
||||
|
||||
|
||||
var link = `/stock/location/${value}`;
|
||||
var text = row.location_detail.description;
|
||||
|
||||
@ -803,7 +803,7 @@ function sumAllocationsForBomRow(bom_row, allocations) {
|
||||
* Display a "build output" table for a particular build.
|
||||
*
|
||||
* This displays a list of "active" (i.e. "in production") build outputs for a given build
|
||||
*
|
||||
*
|
||||
*/
|
||||
function loadBuildOutputTable(build_info, options={}) {
|
||||
|
||||
@ -825,7 +825,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
setupFilterList('builditems', $(table), options.filterTarget || '#filter-list-incompletebuilditems');
|
||||
|
||||
function setupBuildOutputButtonCallbacks() {
|
||||
|
||||
|
||||
// Callback for the "allocate" button
|
||||
$(table).find('.button-output-allocate').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
@ -997,7 +997,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
|
||||
// Check how many BOM lines have been completely allocated for this build output
|
||||
bom_items.forEach(function(bom_item) {
|
||||
|
||||
|
||||
var required_quantity = bom_item.quantity * row.quantity;
|
||||
|
||||
if (sumAllocationsForBomRow(bom_item, row.allocations) >= required_quantity) {
|
||||
@ -1281,7 +1281,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
$(table).on('expand-row.bs.table', function(detail, index, row) {
|
||||
$(`#button-output-allocate-${row.pk}`).prop('disabled', false);
|
||||
});
|
||||
|
||||
|
||||
// Disable the "allocate" button when the sub-table is collapsed
|
||||
$(table).on('collapse-row.bs.table', function(detail, index, row) {
|
||||
$(`#button-output-allocate-${row.pk}`).prop('disabled', true);
|
||||
@ -1364,9 +1364,9 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
|
||||
/*
|
||||
* Display the "allocation table" for a particular build output.
|
||||
*
|
||||
*
|
||||
* This displays a table of required allocations for a particular build output
|
||||
*
|
||||
*
|
||||
* Args:
|
||||
* - buildId: The PK of the Build object
|
||||
* - partId: The PK of the Part object
|
||||
@ -1375,7 +1375,7 @@ function loadBuildOutputTable(build_info, options={}) {
|
||||
* -- table: The #id of the table (will be auto-calculated if not provided)
|
||||
*/
|
||||
function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
|
||||
|
||||
var buildId = buildInfo.pk;
|
||||
var partId = buildInfo.part;
|
||||
|
||||
@ -1628,7 +1628,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
$(table).inventreeTable({
|
||||
data: bom_items,
|
||||
disablePagination: true,
|
||||
formatNoMatches: function() {
|
||||
formatNoMatches: function() {
|
||||
return '{% trans "No BOM items found" %}';
|
||||
},
|
||||
name: 'build-allocation',
|
||||
@ -1715,7 +1715,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
field: 'actions',
|
||||
formatter: function(value, row) {
|
||||
/* Actions available for a particular stock item allocation:
|
||||
*
|
||||
*
|
||||
* - Edit the allocation quantity
|
||||
* - Delete the allocation
|
||||
*/
|
||||
@ -1812,15 +1812,15 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
var variant_stock = row.allow_variants ? (row.available_variant_stock || 0) : 0;
|
||||
|
||||
var available_stock = availableQuantity(row);
|
||||
|
||||
|
||||
var required = requiredQuantity(row);
|
||||
|
||||
var text = '';
|
||||
|
||||
|
||||
if (available_stock > 0) {
|
||||
text += `${available_stock}`;
|
||||
}
|
||||
|
||||
|
||||
if (available_stock < required) {
|
||||
text += `<span class='fas fa-times-circle icon-red float-right' title='{% trans "Insufficient stock available" %}'></span>`;
|
||||
} else {
|
||||
@ -1838,12 +1838,12 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
} else if (substitute_stock > 0) {
|
||||
extra = '{% trans "Includes substitute stock" %}';
|
||||
}
|
||||
|
||||
|
||||
if (extra) {
|
||||
text += `<span title='${extra}' class='fas fa-info-circle float-right icon-blue'></span>`;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return renderLink(text, url);
|
||||
},
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
@ -1862,7 +1862,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
},
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
// Custom sorting function for progress bars
|
||||
|
||||
|
||||
var aA = allocatedQuantity(rowA);
|
||||
var aB = allocatedQuantity(rowB);
|
||||
|
||||
@ -1932,12 +1932,12 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
|
||||
|
||||
/**
|
||||
* Allocate stock items to a build
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - buildId: ID / PK value for the build
|
||||
* - partId: ID / PK value for the part being built
|
||||
* - bom_items: A list of BomItem objects to be allocated
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - output: ID / PK of the associated build output (or null for untracked items)
|
||||
* - source_location: ID / PK of the top-level StockLocation to source stock from (or null)
|
||||
@ -2109,7 +2109,7 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
|
||||
</tbody>
|
||||
</table>
|
||||
`;
|
||||
|
||||
|
||||
constructForm(`/api/build/${build_id}/allocate/`, {
|
||||
method: 'POST',
|
||||
fields: {},
|
||||
@ -2193,7 +2193,7 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
|
||||
|
||||
filters.location = location;
|
||||
filters.cascade = true;
|
||||
|
||||
|
||||
return filters;
|
||||
},
|
||||
noResults: function(query) {
|
||||
@ -2371,7 +2371,7 @@ function loadBuildTable(table, options) {
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
title: 'ID',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
|
@ -10,7 +10,7 @@
|
||||
setupFilterList,
|
||||
showQuestionDialog,
|
||||
*/
|
||||
|
||||
|
||||
/* exported
|
||||
createCompany,
|
||||
createManufacturerPart,
|
||||
@ -26,7 +26,7 @@
|
||||
|
||||
/**
|
||||
* Construct a set of form fields for creating / editing a ManufacturerPart
|
||||
* @returns
|
||||
* @returns
|
||||
*/
|
||||
function manufacturerPartFields() {
|
||||
|
||||
@ -46,7 +46,7 @@ function manufacturerPartFields() {
|
||||
|
||||
/**
|
||||
* Launches a form to create a new ManufacturerPart
|
||||
* @param {object} options
|
||||
* @param {object} options
|
||||
*/
|
||||
function createManufacturerPart(options={}) {
|
||||
|
||||
@ -67,7 +67,7 @@ function createManufacturerPart(options={}) {
|
||||
var company_fields = companyFormFields();
|
||||
|
||||
company_fields.is_manufacturer.value = true;
|
||||
|
||||
|
||||
return company_fields;
|
||||
}
|
||||
};
|
||||
@ -84,7 +84,7 @@ function createManufacturerPart(options={}) {
|
||||
/**
|
||||
* Launches a form to edit a ManufacturerPart
|
||||
* @param {integer} part - ID of a ManufacturerPart
|
||||
* @param {object} options
|
||||
* @param {object} options
|
||||
*/
|
||||
function editManufacturerPart(part, options={}) {
|
||||
|
||||
@ -414,7 +414,7 @@ function deleteManufacturerParts(selections, options={}) {
|
||||
<p>${item.MPN} - ${item.part_detail.full_name}</p>
|
||||
</li>`;
|
||||
});
|
||||
|
||||
|
||||
text += `
|
||||
</ul>
|
||||
</div>`;
|
||||
@ -577,7 +577,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
editManufacturerPart(
|
||||
pk,
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
@ -590,7 +590,7 @@ function loadManufacturerPartTable(table, url, options) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
deleteManufacturerPart(
|
||||
pk,
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
@ -775,7 +775,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
formatter: function(value, row) {
|
||||
if (value) {
|
||||
var name = row.supplier_detail.name;
|
||||
var url = `/company/${value}/`;
|
||||
var url = `/company/${value}/`;
|
||||
var html = imageHoverIcon(row.supplier_detail.image) + renderLink(name, url);
|
||||
|
||||
return html;
|
||||
@ -875,7 +875,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
editSupplierPart(
|
||||
pk,
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
@ -888,7 +888,7 @@ function loadSupplierPartTable(table, url, options) {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
deleteSupplierPart(
|
||||
pk,
|
||||
pk,
|
||||
{
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
|
@ -6,22 +6,22 @@
|
||||
inventreeSave,
|
||||
reloadTableFilters,
|
||||
*/
|
||||
|
||||
|
||||
/* exported
|
||||
setupFilterList,
|
||||
*/
|
||||
|
||||
/**
|
||||
* 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
|
||||
* 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.
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
@ -39,7 +39,7 @@ function defaultFilters() {
|
||||
|
||||
/**
|
||||
* 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'
|
||||
*/
|
||||
@ -73,7 +73,7 @@ function loadTableFilters(tableKey) {
|
||||
|
||||
/**
|
||||
* Save table filters to session storage
|
||||
*
|
||||
*
|
||||
* @param {*} tableKey - string key for the given table
|
||||
* @param {*} filters - object of string:string pairs
|
||||
*/
|
||||
@ -251,7 +251,7 @@ function generateFilterInput(tableKey, filterKey) {
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -410,7 +410,7 @@ function setupFilterList(tableKey, table, target, options={}) {
|
||||
/**
|
||||
* 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);
|
||||
@ -460,4 +460,3 @@ function getFilterOptionValue(tableKey, filterKey, valueKey) {
|
||||
// Cannot map to a display string - return the original text
|
||||
return value;
|
||||
}
|
||||
|
||||
|
@ -62,7 +62,7 @@ $.fn.select2.defaults.set('theme', 'bootstrap-5');
|
||||
* can perform a GET method at the endpoint.
|
||||
*/
|
||||
function canView(OPTIONS) {
|
||||
|
||||
|
||||
if ('actions' in OPTIONS) {
|
||||
return ('GET' in OPTIONS.actions);
|
||||
} else {
|
||||
@ -90,7 +90,7 @@ function canCreate(OPTIONS) {
|
||||
* can perform a PUT or PATCH method at the endpoint
|
||||
*/
|
||||
function canChange(OPTIONS) {
|
||||
|
||||
|
||||
if ('actions' in OPTIONS) {
|
||||
return ('PUT' in OPTIONS.actions || 'PATCH' in OPTIONS.actions);
|
||||
} else {
|
||||
@ -147,25 +147,25 @@ function getApiEndpointOptions(url, callback) {
|
||||
|
||||
/*
|
||||
* Construct a 'creation' (POST) form, to create a new model in the database.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - fields: The 'actions' object provided by the OPTIONS endpoint
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* -
|
||||
* -
|
||||
*/
|
||||
function constructCreateForm(fields, options) {
|
||||
|
||||
// Check if default values were provided for any fields
|
||||
for (const name in fields) {
|
||||
|
||||
|
||||
var field = fields[name];
|
||||
|
||||
var field_options = options.fields[name] || {};
|
||||
|
||||
// If a 'value' is not provided for the field,
|
||||
if (field.value == null) {
|
||||
|
||||
|
||||
if ('value' in field_options) {
|
||||
// Client has specified the default value for the field
|
||||
field.value = field_options.value;
|
||||
@ -183,12 +183,12 @@ function constructCreateForm(fields, options) {
|
||||
|
||||
/*
|
||||
* Construct a 'change' (PATCH) form, to create a new model in the database.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - fields: The 'actions' object provided by the OPTIONS endpoint
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* -
|
||||
* -
|
||||
*/
|
||||
function constructChangeForm(fields, options) {
|
||||
|
||||
@ -203,12 +203,12 @@ function constructChangeForm(fields, options) {
|
||||
json: 'application/json',
|
||||
},
|
||||
success: function(data) {
|
||||
|
||||
|
||||
// An optional function can be provided to process the returned results,
|
||||
// before they are rendered to the form
|
||||
if (options.processResults) {
|
||||
var processed = options.processResults(data, fields, options);
|
||||
|
||||
|
||||
// If the processResults function returns data, it will be stored
|
||||
if (processed) {
|
||||
data = processed;
|
||||
@ -225,7 +225,7 @@ function constructChangeForm(fields, options) {
|
||||
|
||||
// Store the entire data object
|
||||
options.instance = data;
|
||||
|
||||
|
||||
constructFormBody(fields, options);
|
||||
},
|
||||
error: function(xhr) {
|
||||
@ -240,7 +240,7 @@ function constructChangeForm(fields, options) {
|
||||
|
||||
/*
|
||||
* Construct a 'delete' form, to remove a model instance from the database.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - fields: The 'actions' object provided by the OPTIONS request
|
||||
* - options: The 'options' object provided by the client
|
||||
@ -282,7 +282,7 @@ function constructDeleteForm(fields, options) {
|
||||
/*
|
||||
* Request API OPTIONS data from the server,
|
||||
* and construct a modal form based on the response.
|
||||
*
|
||||
*
|
||||
* url: API URL which defines form data
|
||||
* options:
|
||||
* - method: The HTTP method e.g. 'PUT', 'POST', 'DELETE' (default='PATCH')
|
||||
@ -310,7 +310,7 @@ function constructDeleteForm(fields, options) {
|
||||
* - reload: Set to true to reload the current page after form success
|
||||
* - confirm: Set to true to require a "confirm" button
|
||||
* - confirmText: Text for confirm button (default = "Confirm")
|
||||
*
|
||||
*
|
||||
*/
|
||||
function constructForm(url, options) {
|
||||
|
||||
@ -321,7 +321,7 @@ function constructForm(url, options) {
|
||||
|
||||
options.fields = options.fields || {};
|
||||
|
||||
// Save the URL
|
||||
// Save the URL
|
||||
options.url = url;
|
||||
|
||||
// Default HTTP method
|
||||
@ -345,7 +345,7 @@ function constructForm(url, options) {
|
||||
/*
|
||||
* Determine what "type" of form we want to construct,
|
||||
* based on the requested action.
|
||||
*
|
||||
*
|
||||
* First we must determine if the user has the correct permissions!
|
||||
*/
|
||||
|
||||
@ -360,7 +360,7 @@ function constructForm(url, options) {
|
||||
details: '{% trans "Create operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
|
||||
console.warn(`'POST action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -375,7 +375,7 @@ function constructForm(url, options) {
|
||||
details: '{% trans "Update operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
|
||||
console.warn(`${options.method} action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -389,7 +389,7 @@ function constructForm(url, options) {
|
||||
details: '{% trans "Delete operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
|
||||
console.warn(`DELETE action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -403,7 +403,7 @@ function constructForm(url, options) {
|
||||
details: '{% trans "View operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
|
||||
console.warn(`GET action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -417,7 +417,7 @@ function constructForm(url, options) {
|
||||
|
||||
/*
|
||||
* Construct a modal form based on the provided options
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - fields: The endpoint description returned from the OPTIONS request
|
||||
* - options: form options object provided by the client.
|
||||
@ -450,7 +450,7 @@ function constructFormBody(fields, options) {
|
||||
// Provide each field object with its own name
|
||||
for (field in fields) {
|
||||
fields[field].name = field;
|
||||
|
||||
|
||||
// If any "instance_filters" are defined for the endpoint, copy them across (overwrite)
|
||||
if (fields[field].instance_filters) {
|
||||
fields[field].filters = Object.assign(fields[field].filters || {}, fields[field].instance_filters);
|
||||
@ -512,7 +512,7 @@ function constructFormBody(fields, options) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
html += constructField(field_name, field, options);
|
||||
}
|
||||
|
||||
@ -529,7 +529,7 @@ function constructFormBody(fields, options) {
|
||||
var modal = options.modal;
|
||||
|
||||
modalEnable(modal, true);
|
||||
|
||||
|
||||
// Insert generated form content
|
||||
$(modal).find('#form-content').html(html);
|
||||
|
||||
@ -547,7 +547,7 @@ function constructFormBody(fields, options) {
|
||||
if (options.postFormContent) {
|
||||
$(modal).find('#post-form-content').html(options.postFormContent);
|
||||
}
|
||||
|
||||
|
||||
// Clear any existing buttons from the modal
|
||||
$(modal).find('#modal-footer-buttons').html('');
|
||||
|
||||
@ -659,7 +659,7 @@ function extractFormData(fields, options) {
|
||||
var data = {};
|
||||
|
||||
for (var idx = 0; idx < options.field_names.length; idx++) {
|
||||
|
||||
|
||||
var name = options.field_names[idx];
|
||||
|
||||
var field = fields[name] || null;
|
||||
@ -677,7 +677,7 @@ function extractFormData(fields, options) {
|
||||
|
||||
/*
|
||||
* Submit form data to the server.
|
||||
*
|
||||
*
|
||||
*/
|
||||
function submitFormData(fields, options) {
|
||||
|
||||
@ -712,7 +712,7 @@ function submitFormData(fields, options) {
|
||||
case 'decimal':
|
||||
if (!validateFormField(name, options)) {
|
||||
data_valid = false;
|
||||
|
||||
|
||||
data_errors[name] = ['{% trans "Enter a valid number" %}'];
|
||||
}
|
||||
break;
|
||||
@ -734,7 +734,7 @@ function submitFormData(fields, options) {
|
||||
var file = field_files[0];
|
||||
|
||||
form_data.append(name, file);
|
||||
|
||||
|
||||
has_files = true;
|
||||
}
|
||||
} else {
|
||||
@ -780,7 +780,7 @@ function submitFormData(fields, options) {
|
||||
handleFormSuccess(response, options);
|
||||
},
|
||||
error: function(xhr) {
|
||||
|
||||
|
||||
$(options.modal).find('#modal-progress-spinner').hide();
|
||||
|
||||
switch (xhr.status) {
|
||||
@ -808,7 +808,7 @@ function submitFormData(fields, options) {
|
||||
*
|
||||
*/
|
||||
function updateFieldValues(fields, options) {
|
||||
|
||||
|
||||
for (var idx = 0; idx < options.field_names.length; idx++) {
|
||||
|
||||
var name = options.field_names[idx];
|
||||
@ -896,7 +896,7 @@ function getFormFieldElement(name, options) {
|
||||
* An invalid number is expunged at the client side by the getFormFieldValue() function,
|
||||
* which means that an empty string '' is sent to the server if the number is not valud.
|
||||
* This can result in confusing error messages displayed under the form field.
|
||||
*
|
||||
*
|
||||
* So, we can invalid numbers and display errors *before* the form is submitted!
|
||||
*/
|
||||
function validateFormField(name, options) {
|
||||
@ -962,7 +962,7 @@ function getFormFieldValue(name, field={}, options={}) {
|
||||
|
||||
/*
|
||||
* Handle successful form posting
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - response: The JSON response object from the server
|
||||
* - options: The original options object provided by the client
|
||||
@ -1004,7 +1004,7 @@ function handleFormSuccess(response, options) {
|
||||
target: msg_target,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (response && response.info) {
|
||||
showAlertOrCache(response.info, cache, {style: 'info'});
|
||||
}
|
||||
@ -1020,7 +1020,7 @@ function handleFormSuccess(response, options) {
|
||||
if (persist) {
|
||||
// Instead of closing the form and going somewhere else,
|
||||
// reload (empty) the form so the user can input more data
|
||||
|
||||
|
||||
// Reset the status of the "submit" button
|
||||
if (options.modal) {
|
||||
$(options.modal).find('#modal-form-submit').prop('disabled', false);
|
||||
@ -1065,7 +1065,7 @@ function clearFormErrors(options={}) {
|
||||
if (options && options.modal) {
|
||||
// Remove the individual error messages
|
||||
$(options.modal).find('.form-error-message').remove();
|
||||
|
||||
|
||||
$(options.modal).find('.modal-content').removeClass('modal-error');
|
||||
|
||||
// Remove the "has error" class
|
||||
@ -1086,12 +1086,12 @@ function clearFormErrors(options={}) {
|
||||
*
|
||||
* We need to know the unique ID of each item in the array,
|
||||
* and the array length must equal the length of the array returned from the server
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - response: The JSON error response from the server
|
||||
* - parent: The name of the parent field e.g. "items"
|
||||
* - options: The global options struct
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - nested: A map of nested ID values for the "parent" field
|
||||
* e.g.
|
||||
@ -1102,7 +1102,7 @@ function clearFormErrors(options={}) {
|
||||
* 12
|
||||
* ]
|
||||
* }
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
function handleNestedErrors(errors, field_name, options={}) {
|
||||
@ -1115,7 +1115,7 @@ function handleNestedErrors(errors, field_name, options={}) {
|
||||
}
|
||||
|
||||
var nest_list = nest_list = options['nested'][field_name];
|
||||
|
||||
|
||||
// Nest list must be provided!
|
||||
if (!nest_list) {
|
||||
console.warn(`handleNestedErrors missing nesting options for field '${fieldName}'`);
|
||||
@ -1123,9 +1123,9 @@ function handleNestedErrors(errors, field_name, options={}) {
|
||||
}
|
||||
|
||||
for (var idx = 0; idx < error_list.length; idx++) {
|
||||
|
||||
|
||||
var error_item = error_list[idx];
|
||||
|
||||
|
||||
if (idx >= nest_list.length) {
|
||||
console.warn(`handleNestedErrors returned greater number of errors (${error_list.length}) than could be handled (${nest_list.length})`);
|
||||
break;
|
||||
@ -1133,7 +1133,7 @@ function handleNestedErrors(errors, field_name, options={}) {
|
||||
|
||||
// Extract the particular ID of the nested item
|
||||
var nest_id = nest_list[idx];
|
||||
|
||||
|
||||
// Here, error_item is a map of field names to error messages
|
||||
for (sub_field_name in error_item) {
|
||||
|
||||
@ -1158,7 +1158,7 @@ function handleNestedErrors(errors, field_name, options={}) {
|
||||
|
||||
row.after(html);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
// Find the target (nested) field
|
||||
@ -1178,7 +1178,7 @@ function handleNestedErrors(errors, field_name, options={}) {
|
||||
|
||||
/*
|
||||
* Display form error messages as returned from the server.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - errors: The JSON error response from the server
|
||||
* - fields: The form data object
|
||||
@ -1195,7 +1195,7 @@ function handleFormErrors(errors, fields={}, options={}) {
|
||||
clearFormErrors(options);
|
||||
|
||||
var non_field_errors = null;
|
||||
|
||||
|
||||
if (options.modal) {
|
||||
non_field_errors = $(options.modal).find('#non-field-errors');
|
||||
} else {
|
||||
@ -1310,13 +1310,13 @@ function isFieldVisible(field, options) {
|
||||
/*
|
||||
* Attach callbacks to specified fields,
|
||||
* triggered after the field value is edited.
|
||||
*
|
||||
*
|
||||
* Callback function is called with arguments (name, field, options)
|
||||
*/
|
||||
function addFieldCallbacks(fields, options) {
|
||||
|
||||
for (var idx = 0; idx < options.field_names.length; idx++) {
|
||||
|
||||
|
||||
var name = options.field_names[idx];
|
||||
|
||||
var field = fields[name];
|
||||
@ -1581,7 +1581,7 @@ function addSecondaryModal(field, fields, options) {
|
||||
|
||||
/*
|
||||
* Initialize a single related-field
|
||||
*
|
||||
*
|
||||
* argument:
|
||||
* - modal: DOM identifier for the modal window
|
||||
* - name: name of the field e.g. 'location'
|
||||
@ -1657,7 +1657,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
query.search = params.term;
|
||||
query.offset = offset;
|
||||
query.limit = pageSize;
|
||||
|
||||
|
||||
// Allow custom run-time filter augmentation
|
||||
if ('adjustFilters' in field) {
|
||||
query = field.adjustFilters(query, options);
|
||||
@ -1707,7 +1707,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
// Extract 'instance' data passed through from an initial value
|
||||
// Or, use the raw 'item' data as a backup
|
||||
var data = item;
|
||||
|
||||
|
||||
if (item.element && item.element.instance) {
|
||||
data = item.element.instance;
|
||||
}
|
||||
@ -1732,7 +1732,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
// Extract 'instance' data passed through from an initial value
|
||||
// Or, use the raw 'item' data as a backup
|
||||
var data = item;
|
||||
|
||||
|
||||
if (item.element && item.element.instance) {
|
||||
data = item.element.instance;
|
||||
}
|
||||
@ -1761,7 +1761,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
|
||||
// If a 'value' is already defined, grab the model info from the server
|
||||
if (field.value) {
|
||||
|
||||
|
||||
var pk = field.value;
|
||||
var url = `${field.api_url}/${pk}/`.replace('//', '/');
|
||||
|
||||
@ -1806,7 +1806,7 @@ function initializeRelatedField(field, fields, options={}) {
|
||||
/*
|
||||
* Set the value of a select2 instace for a "related field",
|
||||
* e.g. with data returned from a secondary modal
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - name: The name of the field
|
||||
* - data: JSON data representing the model instance
|
||||
@ -1852,7 +1852,7 @@ function searching() {
|
||||
/*
|
||||
* Render a "foreign key" model reference in a select2 instance.
|
||||
* Allows custom rendering with access to the entire serialized object.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - name: The name of the field e.g. 'location'
|
||||
* - model: The name of the InvenTree model e.g. 'stockitem'
|
||||
@ -1872,7 +1872,7 @@ function renderModelData(name, model, data, parameters, options) {
|
||||
|
||||
var renderer = null;
|
||||
|
||||
// Find a custom renderer
|
||||
// Find a custom renderer
|
||||
switch (model) {
|
||||
case 'company':
|
||||
renderer = renderCompany;
|
||||
@ -1919,7 +1919,7 @@ function renderModelData(name, model, data, parameters, options) {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (renderer != null) {
|
||||
html = renderer(name, data, parameters, options);
|
||||
}
|
||||
@ -1954,16 +1954,16 @@ function getFieldName(name, options={}) {
|
||||
|
||||
/*
|
||||
* Construct a single form 'field' for rendering in a form.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - name: The 'name' of the field
|
||||
* - parameters: The field parameters supplied by the DRF OPTIONS method
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* -
|
||||
*
|
||||
* -
|
||||
*
|
||||
* The function constructs a fieldset which mostly replicates django "crispy" forms:
|
||||
*
|
||||
*
|
||||
* - Field name
|
||||
* - Field <input> (depends on specified field type)
|
||||
* - Field description (help text)
|
||||
@ -2012,7 +2012,7 @@ function constructField(name, parameters, options={}) {
|
||||
if (group_options.collapsible) {
|
||||
html += `
|
||||
<div data-bs-toggle='collapse' data-bs-target='#form-panel-content-${group_id}'>
|
||||
<a href='#'><span id='group-icon-${group_id}' class='fas fa-angle-up'></span>
|
||||
<a href='#'><span id='group-icon-${group_id}' class='fas fa-angle-up'></span>
|
||||
`;
|
||||
} else {
|
||||
html += `<div>`;
|
||||
@ -2044,7 +2044,7 @@ function constructField(name, parameters, options={}) {
|
||||
if (parameters.before) {
|
||||
html += parameters.before;
|
||||
}
|
||||
|
||||
|
||||
var hover_title = '';
|
||||
|
||||
if (parameters.help_text) {
|
||||
@ -2062,7 +2062,7 @@ function constructField(name, parameters, options={}) {
|
||||
|
||||
// Does this input deserve "extra" decorators?
|
||||
var extra = (parameters.icon != null) || (parameters.prefix != null) || (parameters.prefixRaw != null);
|
||||
|
||||
|
||||
// Some fields can have 'clear' inputs associated with them
|
||||
if (!parameters.required && !parameters.read_only) {
|
||||
switch (parameters.type) {
|
||||
@ -2080,10 +2080,10 @@ function constructField(name, parameters, options={}) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (extra) {
|
||||
html += `<div class='input-group'>`;
|
||||
|
||||
|
||||
if (parameters.prefix) {
|
||||
html += `<span class='input-group-text'>${parameters.prefix}</span>`;
|
||||
} else if (parameters.prefixRaw) {
|
||||
@ -2120,7 +2120,7 @@ function constructField(name, parameters, options={}) {
|
||||
|
||||
html += `</div>`; // controls
|
||||
html += `</div>`; // form-group
|
||||
|
||||
|
||||
if (parameters.after) {
|
||||
html += parameters.after;
|
||||
}
|
||||
@ -2145,17 +2145,17 @@ function constructLabel(name, parameters) {
|
||||
}
|
||||
|
||||
var html = `<label class='${label_classes}' for='id_${name}'>`;
|
||||
|
||||
|
||||
if (parameters.label) {
|
||||
html += `${parameters.label}`;
|
||||
} else {
|
||||
html += `${name}`;
|
||||
}
|
||||
|
||||
|
||||
if (parameters.required) {
|
||||
html += `<span class='asteriskField'>*</span>`;
|
||||
}
|
||||
|
||||
|
||||
html += `</label>`;
|
||||
|
||||
return html;
|
||||
@ -2164,11 +2164,11 @@ function constructLabel(name, parameters) {
|
||||
|
||||
/*
|
||||
* Construct a form input based on the field parameters
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - name: The name of the field
|
||||
* - parameters: Field parameters returned by the OPTIONS method
|
||||
*
|
||||
*
|
||||
*/
|
||||
function constructInput(name, parameters, options={}) {
|
||||
|
||||
@ -2212,7 +2212,7 @@ function constructInput(name, parameters, options={}) {
|
||||
// Unsupported field type!
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if (func != null) {
|
||||
html = func(name, parameters, options);
|
||||
} else {
|
||||
@ -2499,14 +2499,14 @@ function constructRawInput(name, parameters) {
|
||||
|
||||
/*
|
||||
* Construct a 'help text' div based on the field parameters
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - name: The name of the field
|
||||
* - parameters: Field parameters returned by the OPTIONS method
|
||||
*
|
||||
*
|
||||
*/
|
||||
function constructHelpText(name, parameters) {
|
||||
|
||||
|
||||
var html = `<div id='hint_id_${name}' class='help-block'><i>${parameters.help_text}</i></div>`;
|
||||
|
||||
return html;
|
||||
@ -2567,7 +2567,7 @@ function selectImportFields(url, data={}, options={}) {
|
||||
var headers = `<tr><th>{% trans "File Column" %}</th><th>{% trans "Field Name" %}</th></tr>`;
|
||||
|
||||
var html = '';
|
||||
|
||||
|
||||
if (options.preamble) {
|
||||
html += options.preamble;
|
||||
}
|
||||
@ -2580,7 +2580,7 @@ function selectImportFields(url, data={}, options={}) {
|
||||
fields: {},
|
||||
preFormContent: html,
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
|
||||
var columns = [];
|
||||
|
||||
for (var idx = 0; idx < field_names.length; idx++) {
|
||||
@ -2605,16 +2605,16 @@ function selectImportFields(url, data={}, options={}) {
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
|
||||
|
||||
$(opts.modal).find('#modal-progress-spinner').hide();
|
||||
|
||||
|
||||
switch (xhr.status) {
|
||||
case 400:
|
||||
handleFormErrors(xhr.responseJSON, fields, opts);
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
|
||||
console.error(`upload error at ${opts.url}`);
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
|
@ -60,7 +60,7 @@ function imageHoverIcon(url) {
|
||||
|
||||
/**
|
||||
* Renders a simple thumbnail image
|
||||
* @param {String} url is the image URL
|
||||
* @param {String} url is the image URL
|
||||
* @returns html <img> tag
|
||||
*/
|
||||
function thumbnailImage(url, options={}) {
|
||||
@ -131,7 +131,7 @@ function makeIconButton(icon, cls, pk, title, options={}) {
|
||||
|
||||
/*
|
||||
* Render a progessbar!
|
||||
*
|
||||
*
|
||||
* @param value is the current value of the progress bar
|
||||
* @param maximum is the maximum value of the progress bar
|
||||
*/
|
||||
@ -164,7 +164,7 @@ function makeProgressBar(value, maximum, opts={}) {
|
||||
var style = options.style || '';
|
||||
|
||||
var text = options.text;
|
||||
|
||||
|
||||
if (!text) {
|
||||
if (style == 'percent') {
|
||||
// Display e.g. "50%"
|
||||
@ -247,13 +247,13 @@ function setupNotesField(element, url, options={}) {
|
||||
if (editable) {
|
||||
// Heading icons
|
||||
toolbar_icons.push('heading-1', 'heading-2', 'heading-3', '|');
|
||||
|
||||
|
||||
// Font style
|
||||
toolbar_icons.push('bold', 'italic', 'strikethrough', '|');
|
||||
|
||||
|
||||
// Text formatting
|
||||
toolbar_icons.push('unordered-list', 'ordered-list', 'code', 'quote', '|');
|
||||
|
||||
|
||||
// Elements
|
||||
toolbar_icons.push('table', 'link', 'image');
|
||||
}
|
||||
@ -275,11 +275,11 @@ function setupNotesField(element, url, options={}) {
|
||||
if (!editable) {
|
||||
// Set readonly
|
||||
mde.codemirror.setOption('readOnly', true);
|
||||
|
||||
|
||||
// Hide the "edit" and "save" buttons
|
||||
$('#edit-notes').hide();
|
||||
$('#save-notes').hide();
|
||||
|
||||
|
||||
} else {
|
||||
mde.togglePreview();
|
||||
|
||||
@ -289,7 +289,7 @@ function setupNotesField(element, url, options={}) {
|
||||
$('#save-notes').show();
|
||||
|
||||
// Show the toolbar
|
||||
$(`#${element}`).next('.EasyMDEContainer').find('.editor-toolbar').show();
|
||||
$(`#${element}`).next('.EasyMDEContainer').find('.editor-toolbar').show();
|
||||
|
||||
mde.togglePreview();
|
||||
});
|
||||
@ -313,4 +313,3 @@ function setupNotesField(element, url, options={}) {
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -224,7 +224,7 @@ 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.
|
||||
*/
|
||||
@ -249,7 +249,7 @@ function selectLabel(labels, items, options={}) {
|
||||
}
|
||||
|
||||
var plugin_selection = '';
|
||||
|
||||
|
||||
if (plugins_enabled && plugins.length > 0) {
|
||||
plugin_selection =`
|
||||
<div class='form-group'>
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
/*
|
||||
* Create and display a new modal dialog
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - title: Form title to render
|
||||
* - submitText: Text to render on 'submit' button (default = "Submit")
|
||||
@ -94,7 +94,7 @@ function createNewModal(options={}) {
|
||||
if (options.focus) {
|
||||
getFieldByName(modal_name, options.focus).focus();
|
||||
}
|
||||
|
||||
|
||||
// Steal keyboard focus
|
||||
$(modal_name).focus();
|
||||
|
||||
@ -122,7 +122,7 @@ function createNewModal(options={}) {
|
||||
// Simulate a click on the 'Submit' button
|
||||
$(modal_name).find('#modal-form-submit').click();
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
}
|
||||
});
|
||||
@ -163,10 +163,10 @@ function makeOptionsList(elements, textFunc, valueFunc, titleFunc) {
|
||||
* from the (assumed array) of elements.
|
||||
* For each element, we pass the element to the supplied functions,
|
||||
* which (in turn) generate display / value / title values.
|
||||
*
|
||||
*
|
||||
* Args:
|
||||
* - elements: List of elements
|
||||
* - textFunc: Function which takes an element and generates the text to be displayed
|
||||
* - textFunc: Function which takes an element and generates the text to be displayed
|
||||
* - valueFunc: optional function which takes an element and generates the value
|
||||
* - titleFunc: optional function which takes an element and generates a title
|
||||
*/
|
||||
@ -214,7 +214,7 @@ function setFieldOptions(fieldName, optionList, options={}) {
|
||||
|
||||
var addEmptyOption = options.addEmptyOption || true;
|
||||
|
||||
// If not appending, clear out the field...
|
||||
// If not appending, clear out the field...
|
||||
if (!append) {
|
||||
field.find('option').remove();
|
||||
}
|
||||
@ -246,7 +246,7 @@ function reloadFieldOptions(fieldName, options) {
|
||||
*
|
||||
* Args:
|
||||
* - fieldName: The name of the field
|
||||
* - options:
|
||||
* - options:
|
||||
* -- url: Query url
|
||||
* -- params: Query params
|
||||
* -- value: A function which takes a returned option and returns the 'value' (if not specified, the `pk` field is used)
|
||||
@ -289,7 +289,7 @@ function reloadFieldOptions(fieldName, options) {
|
||||
|
||||
function enableField(fieldName, enabled, options={}) {
|
||||
/* Enable (or disable) a particular field in a modal.
|
||||
*
|
||||
*
|
||||
* Args:
|
||||
* - fieldName: The name of the field
|
||||
* - enabled: boolean enabled / disabled status
|
||||
@ -366,7 +366,7 @@ function partialMatcher(params, data) {
|
||||
|
||||
|
||||
function attachSelect(modal) {
|
||||
/* Attach 'select2' functionality to any drop-down list in the modal.
|
||||
/* Attach 'select2' functionality to any drop-down list in the modal.
|
||||
* Provides search filtering for dropdown items
|
||||
*/
|
||||
|
||||
@ -391,7 +391,7 @@ function attachBootstrapCheckbox(modal) {
|
||||
|
||||
|
||||
function loadingMessageContent() {
|
||||
/* Render a 'loading' message to display in a form
|
||||
/* Render a 'loading' message to display in a form
|
||||
* when waiting for a response from the server
|
||||
*/
|
||||
|
||||
@ -404,7 +404,7 @@ function afterForm(response, options) {
|
||||
/* afterForm is called after a form is successfully submitted,
|
||||
* and the form is dismissed.
|
||||
* Used for general purpose functionality after form submission:
|
||||
*
|
||||
*
|
||||
* - Display a bootstrap alert (success / info / warning / danger)
|
||||
* - Run a supplied success callback function
|
||||
* - Redirect the browser to a different URL
|
||||
@ -424,11 +424,11 @@ function afterForm(response, options) {
|
||||
if (response.info) {
|
||||
showAlertOrCache(response.info, cache, {style: 'info'});
|
||||
}
|
||||
|
||||
|
||||
if (response.warning) {
|
||||
showAlertOrCache(response.warning, cache, {style: 'warning'});
|
||||
}
|
||||
|
||||
|
||||
if (response.danger) {
|
||||
showAlertOrCache(response.danger, cache, {style: 'danger'});
|
||||
}
|
||||
@ -467,7 +467,7 @@ function modalEnable(modal, enable=true) {
|
||||
|
||||
|
||||
function modalSetTitle(modal, title='') {
|
||||
/* Update the title of a modal form
|
||||
/* Update the title of a modal form
|
||||
*/
|
||||
$(modal + ' #modal-title').html(title);
|
||||
}
|
||||
@ -496,7 +496,7 @@ function modalSetCloseText(modal, text) {
|
||||
|
||||
function modalSetButtonText(modal, submit_text, close_text) {
|
||||
/* Set the button text for a modal form
|
||||
*
|
||||
*
|
||||
* submit_text - text for the form submit button
|
||||
* close_text - text for the form dismiss button
|
||||
*/
|
||||
@ -575,8 +575,8 @@ function renderErrorMessage(xhr) {
|
||||
|
||||
function showAlertDialog(title, content, options={}) {
|
||||
/* Display a modal dialog message box.
|
||||
*
|
||||
* title - Title text
|
||||
*
|
||||
* title - Title text
|
||||
* content - HTML content of the dialog window
|
||||
*/
|
||||
|
||||
@ -600,7 +600,7 @@ function showAlertDialog(title, content, options={}) {
|
||||
|
||||
function showQuestionDialog(title, content, options={}) {
|
||||
/* Display a modal dialog for user input (Yes/No confirmation dialog)
|
||||
*
|
||||
*
|
||||
* title - Title text
|
||||
* content - HTML content of the dialog window
|
||||
* options:
|
||||
@ -609,7 +609,7 @@ function showQuestionDialog(title, content, options={}) {
|
||||
* cancel_text - Text for the cancel button (default = 'Cancel')
|
||||
* accept - Function to run if the user presses 'Accept'
|
||||
* cancel - Functino to run if the user presses 'Cancel'
|
||||
*/
|
||||
*/
|
||||
|
||||
var modal = createNewModal({
|
||||
title: title,
|
||||
@ -632,9 +632,9 @@ function showQuestionDialog(title, content, options={}) {
|
||||
|
||||
function openModal(options) {
|
||||
/* Open a modal form, and perform some action based on the provided options object:
|
||||
*
|
||||
*
|
||||
* options can contain:
|
||||
*
|
||||
*
|
||||
* modal - ID of the modal form element (default = '#modal-form')
|
||||
* title - Custom title for the form
|
||||
* content - Default content for the form panel
|
||||
@ -743,7 +743,7 @@ function attachSecondaryModal(modal, options) {
|
||||
/* Attach a secondary modal form to the primary modal form.
|
||||
* Inserts a button into the primary form which, when clicked,
|
||||
* will launch the secondary modal to do /something/ and then return a result.
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* field: Name of the field to attach to
|
||||
* label: Button text
|
||||
@ -834,7 +834,7 @@ function attachButtons(modal, buttons) {
|
||||
function attachFieldCallback(modal, callback) {
|
||||
/* Attach a 'callback' function to a given field in the modal form.
|
||||
* When the value of that field is changed, the callback function is performed.
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - field: The name of the field to attach to
|
||||
* - action: A function to perform
|
||||
@ -867,7 +867,7 @@ function attachCallbacks(modal, callbacks) {
|
||||
function handleModalForm(url, options) {
|
||||
/* Update a modal form after data are received from the server.
|
||||
* Manages POST requests until the form is successfully submitted.
|
||||
*
|
||||
*
|
||||
* The server should respond with a JSON object containing a boolean value 'form_valid'
|
||||
* Form submission repeats (after user interaction) until 'form_valid' = true
|
||||
*/
|
||||
@ -952,8 +952,8 @@ function handleModalForm(url, options) {
|
||||
error: function(xhr) {
|
||||
// There was an error submitting form data via POST
|
||||
|
||||
$(modal).modal('hide');
|
||||
showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr));
|
||||
$(modal).modal('hide');
|
||||
showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr));
|
||||
},
|
||||
complete: function() {
|
||||
// TODO
|
||||
@ -965,15 +965,15 @@ function handleModalForm(url, options) {
|
||||
|
||||
function launchModalForm(url, options = {}) {
|
||||
/* Launch a modal form, and request data from the server to fill the form
|
||||
* If the form data is returned from the server, calls handleModalForm()
|
||||
* If the form data is returned from the server, calls handleModalForm()
|
||||
*
|
||||
* A successful request will return a JSON object with, at minimum,
|
||||
* an object called 'html_form'
|
||||
*
|
||||
*
|
||||
* If the request is NOT successful, displays an appropriate error message.
|
||||
*
|
||||
*
|
||||
* options:
|
||||
*
|
||||
*
|
||||
* modal - Name of the modal (default = '#modal-form')
|
||||
* data - Data to pass through to the AJAX request to fill the form
|
||||
* submit_text - Text for the submit button (default = 'Submit')
|
||||
|
@ -21,9 +21,9 @@
|
||||
/*
|
||||
* 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.
|
||||
@ -40,7 +40,7 @@ function renderId(title, pk, parameters={}) {
|
||||
if ('render_pk' in parameters) {
|
||||
render = parameters['render_pk'];
|
||||
}
|
||||
|
||||
|
||||
if (render) {
|
||||
return `<span class='float-right'><small>${title}: ${pk}</small></span>`;
|
||||
} else {
|
||||
@ -52,7 +52,7 @@ function renderId(title, pk, parameters={}) {
|
||||
// Renderer for "Company" model
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderCompany(name, data, parameters={}, options={}) {
|
||||
|
||||
|
||||
var html = select2Thumbnail(data.image);
|
||||
|
||||
html += `<span><b>${data.name}</b></span> - <i>${data.description}</i>`;
|
||||
@ -68,11 +68,11 @@ function renderCompany(name, data, parameters={}, options={}) {
|
||||
function renderStockItem(name, data, parameters={}, options={}) {
|
||||
|
||||
var image = blankImage();
|
||||
|
||||
|
||||
if (data.part_detail) {
|
||||
image = data.part_detail.thumbnail || data.part_detail.image || blankImage();
|
||||
}
|
||||
|
||||
|
||||
var render_part_detail = true;
|
||||
|
||||
if ('render_part_detail' in parameters) {
|
||||
@ -151,12 +151,12 @@ function renderStockLocation(name, data, parameters={}, options={}) {
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
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);
|
||||
|
||||
@ -173,7 +173,7 @@ function renderBuild(name, data, parameters={}, options={}) {
|
||||
function renderPart(name, data, parameters={}, options={}) {
|
||||
|
||||
var html = select2Thumbnail(data.image);
|
||||
|
||||
|
||||
html += ` <span>${data.full_name || data.name}</span>`;
|
||||
|
||||
if (data.description) {
|
||||
@ -245,9 +245,9 @@ function renderPurchaseOrder(name, data, parameters={}, options={}) {
|
||||
|
||||
var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
|
||||
var html = `<span>${prefix}${data.reference}</span>`;
|
||||
|
||||
|
||||
var thumbnail = null;
|
||||
|
||||
|
||||
if (data.supplier_detail) {
|
||||
thumbnail = data.supplier_detail.thumbnail || data.supplier_detail.image;
|
||||
|
||||
@ -268,10 +268,10 @@ function renderPurchaseOrder(name, data, parameters={}, options={}) {
|
||||
// Renderer for "SalesOrder" model
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
function renderSalesOrder(name, data, parameters={}, options={}) {
|
||||
|
||||
|
||||
var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
|
||||
var html = `<span>${prefix}${data.reference}</span>`;
|
||||
|
||||
|
||||
var thumbnail = null;
|
||||
|
||||
if (data.customer_detail) {
|
||||
@ -335,7 +335,7 @@ function renderPartParameterTemplate(name, data, parameters={}, options={}) {
|
||||
if (data.units) {
|
||||
units = ` [${data.units}]`;
|
||||
}
|
||||
|
||||
|
||||
var html = `<span>${data.name}${units}</span>`;
|
||||
|
||||
return html;
|
||||
@ -377,7 +377,7 @@ function renderSupplierPart(name, data, parameters={}, options={}) {
|
||||
|
||||
var supplier_image = null;
|
||||
var part_image = null;
|
||||
|
||||
|
||||
if (data.supplier_detail) {
|
||||
supplier_image = data.supplier_detail.image;
|
||||
}
|
||||
@ -387,13 +387,13 @@ function renderSupplierPart(name, data, parameters={}, options={}) {
|
||||
}
|
||||
|
||||
var html = '';
|
||||
|
||||
|
||||
html += select2Thumbnail(supplier_image);
|
||||
|
||||
|
||||
if (data.part_detail) {
|
||||
html += select2Thumbnail(part_image);
|
||||
}
|
||||
|
||||
|
||||
if (data.supplier_detail) {
|
||||
html += ` <span><b>${data.supplier_detail.name}</b> - ${data.SKU}</span>`;
|
||||
}
|
||||
|
@ -74,14 +74,14 @@ function showCachedAlerts() {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* Display an alert message at the top of the screen.
|
||||
* The message will contain a "close" button,
|
||||
* and also dismiss automatically after a certain amount of time.
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - message: Text / HTML content to display
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - style: alert style e.g. 'success' / 'warning'
|
||||
* - timeout: Time (in milliseconds) after which the message will be dismissed
|
||||
@ -119,7 +119,7 @@ function showMessage(message, options={}) {
|
||||
${icon}
|
||||
<b>${message}</b>
|
||||
${details}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
`;
|
||||
|
||||
@ -152,7 +152,7 @@ var notificationUpdateTic = 0;
|
||||
/**
|
||||
* The notification checker is initiated when the document is loaded. It checks if there are unread notifications
|
||||
* if unread messages exist the notification indicator is updated
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - force: set true to force an update now (if you got in focus for example)
|
||||
**/
|
||||
@ -182,10 +182,10 @@ function notificationCheck(force = false) {
|
||||
|
||||
/**
|
||||
* handles read / unread buttons and UI rebuilding
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - btn: element that got clicked / fired the event -> must contain pk and target as attributes
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - panel_caller: this button was clicked in the notification panel
|
||||
**/
|
||||
@ -220,7 +220,7 @@ function updateNotificationReadState(btn, panel_caller=false) {
|
||||
|
||||
/**
|
||||
* Returns the html for a read / unread button
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - pk: primary key of the notification
|
||||
* - state: current state of the notification (read / unread) -> just pass what you were handed by the api
|
||||
|
@ -190,7 +190,7 @@ function completePurchaseOrder(order_id, options={}) {
|
||||
* Launches a modal form to mark a PurchaseOrder as 'cancelled'
|
||||
*/
|
||||
function cancelPurchaseOrder(order_id, options={}) {
|
||||
|
||||
|
||||
constructForm(
|
||||
`/api/order/po/${order_id}/cancel/`,
|
||||
{
|
||||
@ -343,7 +343,7 @@ function createSalesOrder(options={}) {
|
||||
title: '{% trans "Add Customer" %}',
|
||||
fields: function() {
|
||||
var fields = companyFormFields();
|
||||
|
||||
|
||||
fields.is_customer.value = true;
|
||||
|
||||
return fields;
|
||||
@ -531,7 +531,7 @@ function newSupplierPartFromOrderWizard(e) {
|
||||
createSupplierPart({
|
||||
part: part,
|
||||
onSuccess: function(data) {
|
||||
|
||||
|
||||
// TODO: 2021-08-23 - This whole form wizard needs to be refactored.
|
||||
// In the future, use the API forms functionality to add the new item
|
||||
// For now, this hack will have to do...
|
||||
@ -568,9 +568,9 @@ function newSupplierPartFromOrderWizard(e) {
|
||||
|
||||
/**
|
||||
* Export an order (PurchaseOrder or SalesOrder)
|
||||
*
|
||||
*
|
||||
* - Display a simple form which presents the user with export options
|
||||
*
|
||||
*
|
||||
*/
|
||||
function exportOrder(redirect_url, options={}) {
|
||||
|
||||
@ -828,7 +828,7 @@ function orderParts(parts_list, options={}) {
|
||||
filters: supplier_part_filters,
|
||||
noResults: function(query) {
|
||||
return '{% trans "No matching supplier parts" %}';
|
||||
}
|
||||
}
|
||||
}, null, opts);
|
||||
|
||||
// Configure the "purchase order" field
|
||||
@ -971,26 +971,26 @@ function newPurchaseOrderFromOrderWizard(e) {
|
||||
var dropdown = `#id-purchase-order-${supplier}`;
|
||||
|
||||
var option = new Option(text, pk, true, true);
|
||||
|
||||
|
||||
$('#modal-form').find(dropdown).append(option).trigger('change');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Receive stock items against a PurchaseOrder
|
||||
* Uses the PurchaseOrderReceive API endpoint
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - order_id, ID / PK for the PurchaseOrder instance
|
||||
* - line_items: A list of PurchaseOrderLineItems objects to be allocated
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* -
|
||||
* -
|
||||
*/
|
||||
function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
|
||||
@ -1012,7 +1012,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
var thumb = thumbnailImage(line_item.part_detail.thumbnail);
|
||||
|
||||
var quantity = (line_item.quantity || 0) - (line_item.received || 0);
|
||||
|
||||
|
||||
if (quantity < 0) {
|
||||
quantity = 0;
|
||||
}
|
||||
@ -1022,14 +1022,14 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
<span class='input-group-text' title='{% trans "Add batch code" %}' data-bs-toggle='collapse' href='#div-batch-${pk}'>
|
||||
<span class='fas fa-layer-group'></span>
|
||||
</span>
|
||||
`;
|
||||
`;
|
||||
|
||||
var toggle_serials = `
|
||||
<span class='input-group-text' title='{% trans "Add serial numbers" %}' data-bs-toggle='collapse' href='#div-serials-${pk}'>
|
||||
<span class='fas fa-hashtag'></span>
|
||||
</span>
|
||||
`;
|
||||
|
||||
|
||||
// Quantity to Receive
|
||||
var quantity_input = constructField(
|
||||
`items_quantity_${pk}`,
|
||||
@ -1363,7 +1363,7 @@ function editPurchaseOrderLineItem(e) {
|
||||
|
||||
function removePurchaseOrderLineItem(e) {
|
||||
|
||||
/* Delete a purchase order line item in a modal form
|
||||
/* Delete a purchase order line item in a modal form
|
||||
*/
|
||||
|
||||
e = e || window.event;
|
||||
@ -1371,7 +1371,7 @@ function removePurchaseOrderLineItem(e) {
|
||||
var src = e.target || e.srcElement;
|
||||
|
||||
var url = $(src).attr('url');
|
||||
|
||||
|
||||
// TODO: Migrate this to the API forms
|
||||
launchModalForm(url, {
|
||||
reload: true,
|
||||
@ -1437,7 +1437,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'supplier_detail',
|
||||
title: '{% trans "Supplier" %}',
|
||||
@ -1491,11 +1491,11 @@ function loadPurchaseOrderTable(table, options) {
|
||||
switchable: true,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
|
||||
if (!row.responsible_detail) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
|
||||
var html = row.responsible_detail.name;
|
||||
|
||||
if (row.responsible_detail.label == 'group') {
|
||||
@ -1514,7 +1514,7 @@ function loadPurchaseOrderTable(table, options) {
|
||||
|
||||
/**
|
||||
* Load a table displaying line items for a particular PurchasesOrder
|
||||
* @param {String} table - HTML ID tag e.g. '#table'
|
||||
* @param {String} table - HTML ID tag e.g. '#table'
|
||||
* @param {Object} options - options which must provide:
|
||||
* - order (integer PK)
|
||||
* - supplier (integer PK)
|
||||
@ -1533,7 +1533,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
|
||||
|
||||
var target = options.filter_target || '#filter-list-purchase-order-lines';
|
||||
|
||||
setupFilterList('purchaseorderlineitem', $(table), target, {download: true});
|
||||
@ -1651,7 +1651,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
formatter: function(value, row, index, field) {
|
||||
if (row.part) {
|
||||
return imageHoverIcon(row.part_detail.thumbnail) + renderLink(row.part_detail.full_name, `/part/${row.part_detail.pk}/`);
|
||||
} else {
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
},
|
||||
@ -1752,7 +1752,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
currency: currency
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return formatter.format(total);
|
||||
}
|
||||
},
|
||||
@ -1789,14 +1789,14 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
});
|
||||
},
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
|
||||
|
||||
if (rowA.received == 0 && rowB.received == 0) {
|
||||
return (rowA.quantity > rowB.quantity) ? 1 : -1;
|
||||
}
|
||||
|
||||
|
||||
var progressA = parseFloat(rowA.received) / rowA.quantity;
|
||||
var progressB = parseFloat(rowB.received) / rowB.quantity;
|
||||
|
||||
|
||||
return (progressA < progressB) ? 1 : -1;
|
||||
}
|
||||
},
|
||||
@ -1821,9 +1821,9 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
title: '',
|
||||
formatter: function(value, row, index, field) {
|
||||
var html = `<div class='btn-group' role='group'>`;
|
||||
|
||||
|
||||
var pk = row.pk;
|
||||
|
||||
|
||||
if (options.allow_receive && row.received < row.quantity) {
|
||||
html += makeIconButton('fa-sign-in-alt icon-green', 'button-line-receive', pk, '{% trans "Receive line item" %}');
|
||||
}
|
||||
@ -1835,7 +1835,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
}
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
|
||||
return html;
|
||||
},
|
||||
}
|
||||
@ -1847,7 +1847,7 @@ function loadPurchaseOrderLineItemTable(table, options={}) {
|
||||
|
||||
/**
|
||||
* Load a table displaying lines for a particular PurchaseOrder
|
||||
*
|
||||
*
|
||||
* @param {String} table : HTML ID tag e.g. '#table'
|
||||
* @param {Object} options : object which contains:
|
||||
* - order {integer} : pk of the PurchaseOrder
|
||||
@ -1872,7 +1872,7 @@ function loadPurchaseOrderExtraLineTable(table, options={}) {
|
||||
options.params.order = options.order;
|
||||
options.params.part_detail = true;
|
||||
options.params.allocations = true;
|
||||
|
||||
|
||||
var filters = loadTableFilters('purchaseorderextraline');
|
||||
|
||||
for (var key in options.params) {
|
||||
@ -1948,15 +1948,15 @@ function loadPurchaseOrderExtraLineTable(table, options={}) {
|
||||
}, 0);
|
||||
|
||||
var currency = (data.slice(-1)[0] && data.slice(-1)[0].price_currency) || 'USD';
|
||||
|
||||
|
||||
var formatter = new Intl.NumberFormat(
|
||||
'en-US',
|
||||
{
|
||||
style: 'currency',
|
||||
style: 'currency',
|
||||
currency: currency
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return formatter.format(total);
|
||||
}
|
||||
}
|
||||
@ -2359,21 +2359,21 @@ function loadSalesOrderShipmentTable(table, options={}) {
|
||||
|
||||
/**
|
||||
* Allocate stock items against a SalesOrder
|
||||
*
|
||||
*
|
||||
* arguments:
|
||||
* - order_id: The ID / PK value for the SalesOrder
|
||||
* - lines: A list of SalesOrderLineItem objects to be allocated
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - source_location: ID / PK of the top-level StockLocation to source stock from (or null)
|
||||
*/
|
||||
function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
|
||||
|
||||
function renderLineItemRow(line_item, quantity) {
|
||||
// Function to render a single line_item row
|
||||
|
||||
var pk = line_item.pk;
|
||||
|
||||
|
||||
var part = line_item.part_detail;
|
||||
|
||||
var thumb = thumbnailImage(part.thumbnail || part.image);
|
||||
@ -2449,7 +2449,7 @@ function allocateStockToSalesOrder(order_id, line_items, options={}) {
|
||||
'{% trans "Select Parts" %}',
|
||||
'{% trans "You must select at least one part to allocate" %}',
|
||||
);
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -2757,12 +2757,12 @@ function loadSalesOrderAllocationTable(table, options={}) {
|
||||
|
||||
/**
|
||||
* Display an "allocations" sub table, showing stock items allocated againt a sales order
|
||||
* @param {*} index
|
||||
* @param {*} row
|
||||
* @param {*} element
|
||||
* @param {*} index
|
||||
* @param {*} row
|
||||
* @param {*} element
|
||||
*/
|
||||
function showAllocationSubTable(index, row, element, options) {
|
||||
|
||||
|
||||
// Construct a sub-table element
|
||||
var html = `
|
||||
<div class='sub-table'>
|
||||
@ -2798,7 +2798,7 @@ function showAllocationSubTable(index, row, element, options) {
|
||||
// Add callbacks for 'delete' buttons
|
||||
table.find('.button-allocation-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
|
||||
constructForm(
|
||||
`/api/order/so-allocation/${pk}/`,
|
||||
{
|
||||
@ -2894,7 +2894,7 @@ function showFulfilledSubTable(index, row, element, options) {
|
||||
}
|
||||
|
||||
var id = `fulfilled-table-${row.pk}`;
|
||||
|
||||
|
||||
var html = `
|
||||
<div class='sub-table'>
|
||||
<table class='table table-striped table-condensed' id='${id}'>
|
||||
@ -2976,7 +2976,7 @@ function reloadTotal() {
|
||||
|
||||
/**
|
||||
* Load a table displaying line items for a particular SalesOrder
|
||||
*
|
||||
*
|
||||
* @param {String} table : HTML ID tag e.g. '#table'
|
||||
* @param {Object} options : object which contains:
|
||||
* - order {integer} : pk of the SalesOrder
|
||||
@ -3001,7 +3001,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
options.params.order = options.order;
|
||||
options.params.part_detail = true;
|
||||
options.params.allocations = true;
|
||||
|
||||
|
||||
var filters = loadTableFilters('salesorderlineitem');
|
||||
|
||||
for (var key in options.params) {
|
||||
@ -3107,15 +3107,15 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
}, 0);
|
||||
|
||||
var currency = (data.slice(-1)[0] && data.slice(-1)[0].sale_price_currency) || 'USD';
|
||||
|
||||
|
||||
var formatter = new Intl.NumberFormat(
|
||||
'en-US',
|
||||
{
|
||||
style: 'currency',
|
||||
style: 'currency',
|
||||
currency: currency
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return formatter.format(total);
|
||||
}
|
||||
},
|
||||
@ -3138,7 +3138,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
return `<em>${renderDate(row.order_detail.target_date)}</em>`;
|
||||
} else {
|
||||
return '-';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
];
|
||||
@ -3166,17 +3166,17 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
});
|
||||
},
|
||||
sorter: function(valA, valB, rowA, rowB) {
|
||||
|
||||
|
||||
var A = rowA.allocated;
|
||||
var B = rowB.allocated;
|
||||
|
||||
|
||||
if (A == 0 && B == 0) {
|
||||
return (rowA.quantity > rowB.quantity) ? 1 : -1;
|
||||
}
|
||||
|
||||
|
||||
var progressA = parseFloat(A) / rowA.quantity;
|
||||
var progressB = parseFloat(B) / rowB.quantity;
|
||||
|
||||
|
||||
return (progressA < progressB) ? 1 : -1;
|
||||
}
|
||||
},
|
||||
@ -3391,11 +3391,11 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
// Extract the row data from the table!
|
||||
var idx = $(this).closest('tr').attr('data-index');
|
||||
|
||||
|
||||
var row = $(table).bootstrapTable('getData')[idx];
|
||||
|
||||
|
||||
var quantity = 1;
|
||||
|
||||
|
||||
if (row.allocated < row.quantity) {
|
||||
quantity = row.quantity - row.allocated;
|
||||
}
|
||||
@ -3489,7 +3489,7 @@ function loadSalesOrderLineItemTable(table, options={}) {
|
||||
|
||||
/**
|
||||
* Load a table displaying lines for a particular SalesOrder
|
||||
*
|
||||
*
|
||||
* @param {String} table : HTML ID tag e.g. '#table'
|
||||
* @param {Object} options : object which contains:
|
||||
* - order {integer} : pk of the SalesOrder
|
||||
@ -3514,7 +3514,7 @@ function loadSalesOrderExtraLineTable(table, options={}) {
|
||||
options.params.order = options.order;
|
||||
options.params.part_detail = true;
|
||||
options.params.allocations = true;
|
||||
|
||||
|
||||
var filters = loadTableFilters('salesorderextraline');
|
||||
|
||||
for (var key in options.params) {
|
||||
@ -3590,15 +3590,15 @@ function loadSalesOrderExtraLineTable(table, options={}) {
|
||||
}, 0);
|
||||
|
||||
var currency = (data.slice(-1)[0] && data.slice(-1)[0].price_currency) || 'USD';
|
||||
|
||||
|
||||
var formatter = new Intl.NumberFormat(
|
||||
'en-US',
|
||||
{
|
||||
style: 'currency',
|
||||
style: 'currency',
|
||||
currency: currency
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
return formatter.format(total);
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,7 @@ function partFields(options={}) {
|
||||
help_text: '{% trans "Create initial supplier data for this part" %}',
|
||||
group: 'supplier',
|
||||
};
|
||||
|
||||
|
||||
fields.supplier = {
|
||||
type: 'related field',
|
||||
model: 'company',
|
||||
@ -221,14 +221,14 @@ function partFields(options={}) {
|
||||
api_url: '{% url "api-company-list" %}',
|
||||
group: 'supplier',
|
||||
};
|
||||
|
||||
|
||||
fields.SKU = {
|
||||
type: 'string',
|
||||
label: '{% trans "SKU" %}',
|
||||
label: '{% trans "SKU" %}',
|
||||
help_text: '{% trans "Supplier stock keeping unit" %}',
|
||||
group: 'supplier',
|
||||
};
|
||||
|
||||
|
||||
fields.manufacturer = {
|
||||
type: 'related field',
|
||||
model: 'company',
|
||||
@ -240,7 +240,7 @@ function partFields(options={}) {
|
||||
api_url: '{% url "api-company-list" %}',
|
||||
group: 'supplier',
|
||||
};
|
||||
|
||||
|
||||
fields.MPN = {
|
||||
type: 'string',
|
||||
label: '{% trans "MPN" %}',
|
||||
@ -357,7 +357,7 @@ function duplicatePart(pk, options={}) {
|
||||
inventreeGet(`/api/part/${pk}/`, {}, {
|
||||
|
||||
success: function(data) {
|
||||
|
||||
|
||||
var fields = partFields({
|
||||
duplicate: pk,
|
||||
});
|
||||
@ -378,7 +378,7 @@ function duplicatePart(pk, options={}) {
|
||||
// By default, disable "is_template" when making a variant *of* a template
|
||||
data.is_template = false;
|
||||
}
|
||||
|
||||
|
||||
constructForm('{% url "api-part-list" %}', {
|
||||
method: 'POST',
|
||||
fields: fields,
|
||||
@ -397,7 +397,7 @@ function duplicatePart(pk, options={}) {
|
||||
|
||||
/* Toggle the 'starred' status of a part.
|
||||
* Performs AJAX queries and updates the display on the button.
|
||||
*
|
||||
*
|
||||
* options:
|
||||
* - button: ID of the button (default = '#part-star-icon')
|
||||
* - URL: API url of the object
|
||||
@ -578,7 +578,7 @@ function makePartIcons(part) {
|
||||
}
|
||||
|
||||
if (!part.active) {
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span> `;
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span> `;
|
||||
}
|
||||
|
||||
return html;
|
||||
@ -655,7 +655,7 @@ function loadPartVariantTable(table, partId, options={}) {
|
||||
}
|
||||
|
||||
if (!row.active) {
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
@ -839,7 +839,7 @@ function loadPartParameterTable(table, url, options) {
|
||||
|
||||
/*
|
||||
* Construct a table showing a list of purchase orders for a given part.
|
||||
*
|
||||
*
|
||||
* This requests API data from the PurchaseOrderLineItem endpoint
|
||||
*/
|
||||
function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
@ -850,7 +850,7 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
options.params.base_part = part_id;
|
||||
options.params.part_detail = true;
|
||||
options.params.order_detail = true;
|
||||
|
||||
|
||||
var filters = loadTableFilters('purchaseorderlineitem');
|
||||
|
||||
for (var key in options.params) {
|
||||
@ -1014,7 +1014,7 @@ function loadPartPurchaseOrderTable(table, part_id, options={}) {
|
||||
title: '',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
|
||||
if (row.received >= row.quantity) {
|
||||
// Already recevied
|
||||
return `<span class='badge bg-success rounded-pill'>{% trans "Received" %}</span>`;
|
||||
@ -1089,7 +1089,7 @@ function loadRelatedPartsTable(table, part_id, options={}) {
|
||||
title: '',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-related-delete', row.pk, '{% trans "Delete part relationship" %}');
|
||||
@ -1234,7 +1234,7 @@ function partGridTile(part) {
|
||||
}
|
||||
|
||||
var html = `
|
||||
|
||||
|
||||
<div class='card product-card borderless'>
|
||||
<div class='panel product-card-panel'>
|
||||
<div class='panel-heading'>
|
||||
@ -1258,7 +1258,7 @@ function partGridTile(part) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
return html;
|
||||
@ -1267,7 +1267,7 @@ function partGridTile(part) {
|
||||
|
||||
function loadPartTable(table, url, options={}) {
|
||||
/* Load part listing data into specified table.
|
||||
*
|
||||
*
|
||||
* Args:
|
||||
* - table: HTML reference to the table
|
||||
* - url: Base URL for API query
|
||||
@ -1340,7 +1340,7 @@ function loadPartTable(table, url, options={}) {
|
||||
|
||||
display += makePartIcons(row);
|
||||
|
||||
return display;
|
||||
return display;
|
||||
}
|
||||
};
|
||||
|
||||
@ -1373,7 +1373,7 @@ function loadPartTable(table, url, options={}) {
|
||||
} else {
|
||||
return '{% trans "No category" %}';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if (!options.params.ordering) {
|
||||
@ -1386,7 +1386,7 @@ function loadPartTable(table, url, options={}) {
|
||||
field: 'unallocated_stock',
|
||||
title: '{% trans "Stock" %}',
|
||||
searchable: false,
|
||||
formatter: function(value, row) {
|
||||
formatter: function(value, row) {
|
||||
var link = '?display=part-stock';
|
||||
|
||||
if (row.in_stock) {
|
||||
@ -1538,7 +1538,7 @@ function loadPartTable(table, url, options={}) {
|
||||
html = `<div class='row full-height'>`;
|
||||
|
||||
data.forEach(function(row, index) {
|
||||
|
||||
|
||||
// Force a new row every 5 columns
|
||||
if ((index > 0) && (index % 5 == 0) && (index < data.length)) {
|
||||
html += `</div><div class='row full-height'>`;
|
||||
@ -1552,7 +1552,7 @@ function loadPartTable(table, url, options={}) {
|
||||
return html;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if (options.buttons) {
|
||||
linkButtonsToSelection($(table), options.buttons);
|
||||
}
|
||||
@ -1624,11 +1624,11 @@ function loadPartCategoryTable(table, options) {
|
||||
filters = loadTableFilters(filterKey);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var tree_view = options.allowTreeView && inventreeLoad('category-tree-view') == 1;
|
||||
|
||||
if (tree_view) {
|
||||
params.cascade = true;
|
||||
params.cascade = true;
|
||||
}
|
||||
|
||||
var original = {};
|
||||
@ -1652,7 +1652,7 @@ function loadPartCategoryTable(table, options) {
|
||||
queryParams: filters,
|
||||
disablePagination: tree_view,
|
||||
sidePagination: tree_view ? 'client' : 'server',
|
||||
serverSort: !tree_view,
|
||||
serverSort: !tree_view,
|
||||
search: !tree_view,
|
||||
name: 'category',
|
||||
original: original,
|
||||
@ -1707,14 +1707,14 @@ function loadPartCategoryTable(table, options) {
|
||||
|
||||
$('#view-category-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-category-tree').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
|
||||
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
onChange: function() {
|
||||
table.bootstrapTable('resetView');
|
||||
},
|
||||
onExpand: function() {
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -1862,7 +1862,7 @@ function loadPartTestTemplateTable(table, options) {
|
||||
} else {
|
||||
var text = '{% trans "This test is defined for a parent part" %}';
|
||||
|
||||
return renderLink(text, `/part/${row.part}/tests/`);
|
||||
return renderLink(text, `/part/${row.part}/tests/`);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1871,9 +1871,9 @@ function loadPartTestTemplateTable(table, options) {
|
||||
|
||||
table.find('.button-test-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
|
||||
var url = `/api/part/test-template/${pk}/`;
|
||||
|
||||
|
||||
constructForm(url, {
|
||||
fields: {
|
||||
test_name: {},
|
||||
@ -1891,9 +1891,9 @@ function loadPartTestTemplateTable(table, options) {
|
||||
|
||||
table.find('.button-test-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
|
||||
var url = `/api/part/test-template/${pk}/`;
|
||||
|
||||
|
||||
constructForm(url, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Test Result Template" %}',
|
||||
@ -1975,14 +1975,14 @@ function loadPriceBreakTable(table, options) {
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
var html = value;
|
||||
|
||||
|
||||
html += `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-edit icon-blue', `button-${name}-edit`, row.pk, `{% trans "Edit ${human_name}" %}`);
|
||||
html += makeIconButton('fa-trash-alt icon-red', `button-${name}-delete`, row.pk, `{% trans "Delete ${human_name}" %}`);
|
||||
|
||||
|
||||
html += `</div>`;
|
||||
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
|
@ -25,7 +25,7 @@ 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.
|
||||
*/
|
||||
|
@ -34,11 +34,11 @@ function openSearchPanel() {
|
||||
|
||||
// Prevent this button from actually submitting the form
|
||||
event.preventDefault();
|
||||
|
||||
|
||||
panel.find('#search-input').val('');
|
||||
clearSearchResults();
|
||||
});
|
||||
|
||||
|
||||
// Callback for the "close search" button
|
||||
panel.find('#search-close').click(function(event) {
|
||||
// Prevent this button from actually submitting the form
|
||||
@ -67,11 +67,11 @@ function updateSearch() {
|
||||
}
|
||||
|
||||
clearSearchResults();
|
||||
|
||||
|
||||
if (searchText.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
searchTextCurrent = searchText;
|
||||
|
||||
// Cancel any previous AJAX requests
|
||||
@ -83,7 +83,7 @@ function updateSearch() {
|
||||
|
||||
// Show the "searching" text
|
||||
$('#offcanvas-search').find('#search-pending').show();
|
||||
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_PARTS) {
|
||||
|
||||
var params = {};
|
||||
@ -122,7 +122,7 @@ function updateSearch() {
|
||||
|
||||
if (user_settings.SEARCH_PREVIEW_SHOW_STOCK) {
|
||||
// Search for matching stock items
|
||||
|
||||
|
||||
var filters = {
|
||||
part_detail: true,
|
||||
location_detail: true,
|
||||
@ -220,7 +220,7 @@ function updateSearch() {
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
// Wait until all the pending queries are completed
|
||||
$.when.apply($, searchQueries).done(function() {
|
||||
$('#offcanvas-search').find('#search-pending').hide();
|
||||
@ -231,13 +231,13 @@ function updateSearch() {
|
||||
function clearSearchResults() {
|
||||
|
||||
var panel = $('#offcanvas-search');
|
||||
|
||||
|
||||
// Ensure the 'no results found' element is visible
|
||||
panel.find('#search-no-results').show();
|
||||
|
||||
// Ensure that the 'searching' element is hidden
|
||||
panel.find('#search-pending').hide();
|
||||
|
||||
|
||||
// Delete any existing search results
|
||||
panel.find('#search-results').empty();
|
||||
|
||||
@ -287,7 +287,7 @@ function addSearchQuery(key, title, query_url, query_params, render_func, render
|
||||
|
||||
// Add a group of results to the list
|
||||
function addSearchResults(key, results, title, renderFunc, renderParams={}) {
|
||||
|
||||
|
||||
if (results.length == 0) {
|
||||
// Do not display this group, as there are no results
|
||||
return;
|
||||
@ -297,7 +297,7 @@ function addSearchResults(key, results, title, renderFunc, renderParams={}) {
|
||||
|
||||
// Ensure the 'no results found' element is hidden
|
||||
panel.find('#search-no-results').hide();
|
||||
|
||||
|
||||
panel.find(`#search-results-wrapper-${key}`).append(`
|
||||
<div class='search-result-group' id='search-results-${key}'>
|
||||
<div class='search-result-header' style='display: flex;'>
|
||||
|
@ -202,7 +202,7 @@ function stockItemFields(options={}) {
|
||||
} else {
|
||||
clearFormInput('supplier_part', opts);
|
||||
clearFormInput('purchase_price', opts);
|
||||
|
||||
|
||||
disableFormInput('supplier_part', opts);
|
||||
disableFormInput('purchase_price', opts);
|
||||
disableFormInput('purchase_price_currency', opts);
|
||||
@ -315,11 +315,11 @@ function duplicateStockItem(pk, options) {
|
||||
delete data['serial'];
|
||||
|
||||
options.data = data;
|
||||
|
||||
|
||||
options.create = true;
|
||||
options.fields = stockItemFields(options);
|
||||
options.groups = stockItemGroups(options);
|
||||
|
||||
|
||||
options.method = 'POST';
|
||||
options.title = '{% trans "Duplicate Stock Item" %}';
|
||||
|
||||
@ -342,7 +342,7 @@ function editStockItem(pk, options={}) {
|
||||
options.groups = stockItemGroups(options);
|
||||
|
||||
options.title = '{% trans "Edit Stock Item" %}';
|
||||
|
||||
|
||||
// Query parameters for retrieving stock item data
|
||||
options.params = {
|
||||
part_detail: true,
|
||||
@ -443,7 +443,7 @@ function findStockItemBySerialNumber(part_id) {
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
var serial = getFormFieldValue('serial', fields['serial'], opts);
|
||||
|
||||
|
||||
serial = serial.toString().trim();
|
||||
|
||||
if (!serial) {
|
||||
@ -540,7 +540,7 @@ function assignStockToCustomer(items, options={}) {
|
||||
for (var idx = 0; idx < items.length; idx++) {
|
||||
|
||||
var item = items[idx];
|
||||
|
||||
|
||||
var pk = item.pk;
|
||||
|
||||
var part = item.part_detail;
|
||||
@ -1069,7 +1069,7 @@ function adjustStock(action, items, options={}) {
|
||||
}
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
|
||||
// Extract data elements from the form
|
||||
var data = {
|
||||
items: [],
|
||||
@ -1094,7 +1094,7 @@ function adjustStock(action, items, options={}) {
|
||||
if (row.exists()) {
|
||||
|
||||
item_pk_values.push(pk);
|
||||
|
||||
|
||||
var quantity = getFormFieldValue(`items_quantity_${pk}`, {}, opts);
|
||||
|
||||
data.items.push({
|
||||
@ -1514,9 +1514,9 @@ function loadStockTestResultsTable(table, options) {
|
||||
|
||||
|
||||
function locationDetail(row, showLink=true) {
|
||||
/*
|
||||
/*
|
||||
* Function to display a "location" of a StockItem.
|
||||
*
|
||||
*
|
||||
* Complicating factors: A StockItem may not actually *be* in a location!
|
||||
* - Could be at a customer
|
||||
* - Could be installed in another stock item
|
||||
@ -1568,7 +1568,7 @@ function loadStockTable(table, options) {
|
||||
/* Load data into a stock table with adjustable options.
|
||||
* Fetches data (via AJAX) and loads into a bootstrap table.
|
||||
* Also links in default button callbacks.
|
||||
*
|
||||
*
|
||||
* Options:
|
||||
* url - URL for the stock query
|
||||
* params - query params for augmenting stock data request
|
||||
@ -1657,7 +1657,7 @@ function loadStockTable(table, options) {
|
||||
return html;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (!options.params.ordering) {
|
||||
col['sortable'] = true;
|
||||
}
|
||||
@ -1711,7 +1711,7 @@ function loadStockTable(table, options) {
|
||||
|
||||
if (row.is_building) {
|
||||
html += makeIconBadge('fa-tools', '{% trans "Stock item is in production" %}');
|
||||
}
|
||||
}
|
||||
|
||||
if (row.sales_order) {
|
||||
// Stock item has been assigned to a sales order
|
||||
@ -1760,7 +1760,7 @@ function loadStockTable(table, options) {
|
||||
return html;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
columns.push(col);
|
||||
|
||||
col = {
|
||||
@ -1870,7 +1870,7 @@ function loadStockTable(table, options) {
|
||||
});
|
||||
|
||||
col = {
|
||||
|
||||
|
||||
field: 'supplier_part',
|
||||
title: '{% trans "Supplier Part" %}',
|
||||
visible: params['supplier_part_detail'] || false,
|
||||
@ -1987,7 +1987,7 @@ function loadStockTable(table, options) {
|
||||
});
|
||||
|
||||
if (global_settings.BARCODE_ENABLE) {
|
||||
$('#multi-item-barcode-scan-into-location').click(function() {
|
||||
$('#multi-item-barcode-scan-into-location').click(function() {
|
||||
var selections = $(table).bootstrapTable('getSelections');
|
||||
|
||||
var items = [];
|
||||
@ -2158,7 +2158,7 @@ function loadStockTable(table, options) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
/*
|
||||
* Display a table of stock locations
|
||||
*/
|
||||
function loadStockLocationTable(table, options) {
|
||||
@ -2220,14 +2220,14 @@ function loadStockLocationTable(table, options) {
|
||||
|
||||
$('#view-location-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-location-tree').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
|
||||
|
||||
table.treegrid({
|
||||
treeColumn: 1,
|
||||
onChange: function() {
|
||||
table.bootstrapTable('resetView');
|
||||
},
|
||||
onExpand: function() {
|
||||
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -2664,7 +2664,7 @@ function uninstallStockItem(installed_item_id, options={}) {
|
||||
},
|
||||
preFormContent: function(opts) {
|
||||
var html = '';
|
||||
|
||||
|
||||
if (installed_item_id == null) {
|
||||
html += `
|
||||
<div class='alert alert-block alert-info'>
|
||||
|
@ -15,7 +15,7 @@
|
||||
|
||||
/**
|
||||
* Reload a named table
|
||||
* @param table
|
||||
* @param table
|
||||
*/
|
||||
function reloadtable(table) {
|
||||
$(table).bootstrapTable('refresh');
|
||||
@ -25,7 +25,7 @@ function reloadtable(table) {
|
||||
/**
|
||||
* Download data from a table, via the API.
|
||||
* This requires a number of conditions to be met:
|
||||
*
|
||||
*
|
||||
* - The API endpoint supports data download (on the server side)
|
||||
* - The table is "flat" (does not support multi-level loading, etc)
|
||||
* - The table has been loaded using the inventreeTable() function, not bootstrapTable()
|
||||
@ -60,16 +60,16 @@ function downloadTableData(table, opts={}) {
|
||||
},
|
||||
onSubmit: function(fields, form_options) {
|
||||
var format = getFormFieldValue('format', fields['format'], form_options);
|
||||
|
||||
|
||||
// Hide the modal
|
||||
$(form_options.modal).modal('hide');
|
||||
|
||||
for (const [key, value] of Object.entries(query_params)) {
|
||||
url += `${key}=${value}&`;
|
||||
}
|
||||
|
||||
|
||||
url += `export=${format}`;
|
||||
|
||||
|
||||
location.href = url;
|
||||
}
|
||||
});
|
||||
@ -80,9 +80,9 @@ function downloadTableData(table, opts={}) {
|
||||
|
||||
/**
|
||||
* Render a URL for display
|
||||
* @param {String} text
|
||||
* @param {String} url
|
||||
* @param {object} options
|
||||
* @param {String} text
|
||||
* @param {String} url
|
||||
* @param {object} options
|
||||
* @returns link text
|
||||
*/
|
||||
function renderLink(text, url, options={}) {
|
||||
@ -134,8 +134,8 @@ function linkButtonsToSelection(table, buttons) {
|
||||
|
||||
/**
|
||||
* Returns true if the input looks like a valid number
|
||||
* @param {String} n
|
||||
* @returns
|
||||
* @param {String} n
|
||||
* @returns
|
||||
*/
|
||||
function isNumeric(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
@ -251,7 +251,7 @@ function convertQueryParameters(params, filters) {
|
||||
|
||||
delete params['original_search'];
|
||||
}
|
||||
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
|
@ -95,9 +95,9 @@
|
||||
{% include "spacer.html" %}
|
||||
{% include "spacer.html" %}
|
||||
{% endif %}
|
||||
|
||||
|
||||
<ul class='navbar-nav flex-row'>
|
||||
|
||||
|
||||
<li class='nav-item me-2'>
|
||||
<button data-bs-toggle='offcanvas' data-bs-target="#offcanvas-search" class='btn position-relative' title='{% trans "Search" %}'>
|
||||
<span class='fas fa-search'></span>
|
||||
|
@ -21,7 +21,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% block page_info %}
|
||||
<div class='panel-content'>
|
||||
{% block details_above %}
|
||||
@ -57,7 +57,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
{% block page_content %}
|
||||
|
@ -40,4 +40,3 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -5,13 +5,13 @@
|
||||
{% for provider in socialaccount_providers %}
|
||||
{% if provider.id == "openid" %}
|
||||
{% for brand in provider.get_brands %}
|
||||
<a title="{{brand.name}}"
|
||||
class="btn btn-primary socialaccount_provider {{provider.id}} {{brand.id}}"
|
||||
<a title="{{brand.name}}"
|
||||
class="btn btn-primary socialaccount_provider {{provider.id}} {{brand.id}}"
|
||||
href="{% provider_login_url provider.id openid=brand.openid_url process=process %}"
|
||||
><span class='brand-icon' brand_name='{{provider.id}}'></span> {{brand.name}}</a>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<a title="{{provider.name}}" class="btn btn-primary socialaccount_provider {{provider.id}}"
|
||||
<a title="{{provider.name}}" class="btn btn-primary socialaccount_provider {{provider.id}}"
|
||||
href="{% provider_login_url provider.id process=process scope=scope auth_params=auth_params %}"
|
||||
><span class='brand-icon' brand_name='{{provider.id}}'></span> {{provider.name}}</a>
|
||||
{% endfor %}
|
||||
|
@ -10,7 +10,7 @@ const {{ label }}Codes = {
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
/*
|
||||
/*
|
||||
* Render the status for a {{ label }} object.
|
||||
* Uses the values specified in "status_codes.py"
|
||||
* This function is generated by the "status_codes.html" template
|
||||
|
Reference in New Issue
Block a user