mirror of
				https://github.com/inventree/InvenTree.git
				synced 2025-11-04 07:05:41 +00:00 
			
		
		
		
	Merge branch 'master' into partial-shipment
# Conflicts: # InvenTree/build/templates/build/build_base.html # InvenTree/order/templates/order/order_base.html # InvenTree/order/templates/order/sales_order_base.html # InvenTree/order/templates/order/sales_order_detail.html # InvenTree/order/templates/order/so_navbar.html
This commit is contained in:
		@@ -27,7 +27,7 @@ function loadAttachmentTable(url, options) {
 | 
			
		||||
            return '{% trans "No attachments found" %}';
 | 
			
		||||
        },
 | 
			
		||||
        sortable: true,
 | 
			
		||||
        search: false,
 | 
			
		||||
        search: true,
 | 
			
		||||
        queryParams: options.filters || {},
 | 
			
		||||
        onPostBody: function() {
 | 
			
		||||
            // Add callback for 'edit' button
 | 
			
		||||
@@ -58,12 +58,16 @@ function loadAttachmentTable(url, options) {
 | 
			
		||||
 | 
			
		||||
                    var fn = value.toLowerCase();
 | 
			
		||||
 | 
			
		||||
                    if (fn.endsWith('.pdf')) {
 | 
			
		||||
                    if (fn.endsWith('.csv')) {
 | 
			
		||||
                        icon = 'fa-file-csv';
 | 
			
		||||
                    } else if (fn.endsWith('.pdf')) {
 | 
			
		||||
                        icon = 'fa-file-pdf';
 | 
			
		||||
                    } else if (fn.endsWith('.xls') || fn.endsWith('.xlsx')) {
 | 
			
		||||
                        icon = 'fa-file-excel';
 | 
			
		||||
                    } else if (fn.endsWith('.doc') || fn.endsWith('.docx')) {
 | 
			
		||||
                        icon = 'fa-file-word';
 | 
			
		||||
                    } else if (fn.endsWith('.zip') || fn.endsWith('.7z')) {
 | 
			
		||||
                        icon = 'fa-file-archive';
 | 
			
		||||
                    } else {
 | 
			
		||||
                        var images = ['.png', '.jpg', '.bmp', '.gif', '.svg', '.tif'];
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -407,7 +407,7 @@ function loadBomTable(table, options) {
 | 
			
		||||
 | 
			
		||||
                // Display an extra icon if this part is an assembly
 | 
			
		||||
                if (sub_part.assembly) {
 | 
			
		||||
                    var text = `<span title='{% trans "Open subassembly" %}' class='fas fa-stream label-right'></span>`;
 | 
			
		||||
                    var text = `<span title='{% trans "Open subassembly" %}' class='fas fa-stream float-right'></span>`;
 | 
			
		||||
 | 
			
		||||
                    html += renderLink(text, `/part/${row.sub_part}/bom/`);
 | 
			
		||||
                }
 | 
			
		||||
@@ -470,7 +470,7 @@ function loadBomTable(table, options) {
 | 
			
		||||
            var text = value;
 | 
			
		||||
 | 
			
		||||
            if (value == null || value <= 0) {
 | 
			
		||||
                text = `<span class='label label-warning'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
                text = `<span class='badge rounded-pill bg-danger'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return renderLink(text, url);
 | 
			
		||||
@@ -612,7 +612,7 @@ function loadBomTable(table, options) {
 | 
			
		||||
 | 
			
		||||
                    var bValidate = makeIconButton('fa-check-circle icon-green', 'bom-validate-button', row.pk, '{% trans "Validate BOM Item" %}');
 | 
			
		||||
 | 
			
		||||
                    var bValid = `<span title='{% trans "This line has been validated" %}' class='fas fa-check-double icon-green'/>`;
 | 
			
		||||
                    var bValid = makeIconButton('fa-check-double icon-green', 'bom-valid-button', row.pk, '{% trans "This line has been validated" %}', {disabled: true});
 | 
			
		||||
 | 
			
		||||
                    var bSubs = makeIconButton('fa-exchange-alt icon-blue', 'bom-substitutes-button', row.pk, '{% trans "Edit substitute parts" %}');
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -34,8 +34,8 @@ function buildFormFields() {
 | 
			
		||||
        reference: {
 | 
			
		||||
            prefix: global_settings.BUILDORDER_REFERENCE_PREFIX,
 | 
			
		||||
        },
 | 
			
		||||
        title: {},
 | 
			
		||||
        part: {},
 | 
			
		||||
        title: {},
 | 
			
		||||
        quantity: {},
 | 
			
		||||
        parent: {
 | 
			
		||||
            filters: {
 | 
			
		||||
@@ -937,7 +937,10 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
 | 
			
		||||
 | 
			
		||||
                                var progress = makeProgressBar(
 | 
			
		||||
                                    allocatedLines,
 | 
			
		||||
                                    totalLines
 | 
			
		||||
                                    totalLines,
 | 
			
		||||
                                    {
 | 
			
		||||
                                        max_width: '150px',
 | 
			
		||||
                                    }
 | 
			
		||||
                                );
 | 
			
		||||
    
 | 
			
		||||
                                build_progress.html(progress);
 | 
			
		||||
 
 | 
			
		||||
@@ -325,15 +325,15 @@ function loadCompanyTable(table, url, options={}) {
 | 
			
		||||
                var html = imageHoverIcon(row.image) + renderLink(value, row.url);
 | 
			
		||||
 | 
			
		||||
                if (row.is_customer) {
 | 
			
		||||
                    html += `<span title='{% trans "Customer" %}' class='fas fa-user-tie label-right'></span>`;
 | 
			
		||||
                    html += `<span title='{% trans "Customer" %}' class='fas fa-user-tie float-right'></span>`;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (row.is_manufacturer) {
 | 
			
		||||
                    html += `<span title='{% trans "Manufacturer" %}' class='fas fa-industry label-right'></span>`;
 | 
			
		||||
                    html += `<span title='{% trans "Manufacturer" %}' class='fas fa-industry float-right'></span>`;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (row.is_supplier) {
 | 
			
		||||
                    html += `<span title='{% trans "Supplier" %}' class='fas fa-building label-right'></span>`;
 | 
			
		||||
                    html += `<span title='{% trans "Supplier" %}' class='fas fa-building float-right'></span>`;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return html;
 | 
			
		||||
@@ -493,15 +493,15 @@ function loadManufacturerPartTable(table, url, options) {
 | 
			
		||||
                    var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
 | 
			
		||||
 | 
			
		||||
                    if (row.part_detail.is_template) {
 | 
			
		||||
                        html += `<span class='fas fa-clone label-right' title='{% trans "Template part" %}'></span>`;
 | 
			
		||||
                        html += `<span class='fas fa-clone float-right' title='{% trans "Template part" %}'></span>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (row.part_detail.assembly) {
 | 
			
		||||
                        html += `<span class='fas fa-tools label-right' title='{% trans "Assembled part" %}'></span>`;
 | 
			
		||||
                        html += `<span class='fas fa-tools float-right' title='{% trans "Assembled part" %}'></span>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!row.part_detail.active) {
 | 
			
		||||
                        html += `<span class='label label-warning label-right'>{% trans "Inactive" %}</span>`;
 | 
			
		||||
                        html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return html;
 | 
			
		||||
@@ -750,15 +750,15 @@ function loadSupplierPartTable(table, url, options) {
 | 
			
		||||
                    var html = imageHoverIcon(row.part_detail.thumbnail) + renderLink(value, url);
 | 
			
		||||
 | 
			
		||||
                    if (row.part_detail.is_template) {
 | 
			
		||||
                        html += `<span class='fas fa-clone label-right' title='{% trans "Template part" %}'></span>`;
 | 
			
		||||
                        html += `<span class='fas fa-clone float-right' title='{% trans "Template part" %}'></span>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (row.part_detail.assembly) {
 | 
			
		||||
                        html += `<span class='fas fa-tools label-right' title='{% trans "Assembled part" %}'></span>`;
 | 
			
		||||
                        html += `<span class='fas fa-tools float-right' title='{% trans "Assembled part" %}'></span>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (!row.part_detail.active) {
 | 
			
		||||
                        html += `<span class='label label-warning label-right'>{% trans "Inactive" %}</span>`;
 | 
			
		||||
                        html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return html;
 | 
			
		||||
 
 | 
			
		||||
@@ -281,7 +281,7 @@ function setupFilterList(tableKey, table, target) {
 | 
			
		||||
    // One blank slate, please
 | 
			
		||||
    element.empty();
 | 
			
		||||
 | 
			
		||||
    element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-default filter-tag'><span class='fas fa-redo-alt'></span></button>`);
 | 
			
		||||
    element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-tag'><span class='fas fa-redo-alt'></span></button>`);
 | 
			
		||||
 | 
			
		||||
    // Callback for reloading the table
 | 
			
		||||
    element.find(`#reload-${tableKey}`).click(function() {
 | 
			
		||||
@@ -294,10 +294,10 @@ function setupFilterList(tableKey, table, target) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // If there are filters currently "in use", add them in!
 | 
			
		||||
    element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-default filter-tag'><span class='fas fa-filter'></span></button>`);
 | 
			
		||||
    element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-tag'><span class='fas fa-filter'></span></button>`);
 | 
			
		||||
 | 
			
		||||
    if (Object.keys(filters).length > 0) {
 | 
			
		||||
        element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-default filter-tag'><span class='fas fa-backspace icon-red'></span></button>`);
 | 
			
		||||
        element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-tag'><span class='fas fa-backspace icon-red'></span></button>`);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    for (var key in filters) {
 | 
			
		||||
@@ -320,7 +320,7 @@ function setupFilterList(tableKey, table, target) {
 | 
			
		||||
            html += generateAvailableFilterList(tableKey);
 | 
			
		||||
            html += generateFilterInput(tableKey);
 | 
			
		||||
 | 
			
		||||
            html += `<button title='{% trans "Create filter" %}' class='btn btn-default filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
 | 
			
		||||
            html += `<button title='{% trans "Create filter" %}' class='btn btn-outline-secondary filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
 | 
			
		||||
 | 
			
		||||
            element.append(html);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,6 @@
 | 
			
		||||
{% load inventree_extras %}
 | 
			
		||||
 | 
			
		||||
/* globals
 | 
			
		||||
    attachToggle,
 | 
			
		||||
    createNewModal,
 | 
			
		||||
    inventreeFormDataUpload,
 | 
			
		||||
    inventreeGet,
 | 
			
		||||
@@ -49,6 +48,9 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// Set global default theme for select2
 | 
			
		||||
$.fn.select2.defaults.set('theme', 'bootstrap-5');
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Return true if the OPTIONS specify that the user
 | 
			
		||||
 * can perform a GET method at the endpoint.
 | 
			
		||||
@@ -519,11 +521,6 @@ function constructFormBody(fields, options) {
 | 
			
		||||
    // Attach clear callbacks (if required)
 | 
			
		||||
    addClearCallbacks(fields, options);
 | 
			
		||||
 | 
			
		||||
    attachToggle(modal);
 | 
			
		||||
 | 
			
		||||
    $(modal + ' .select2-container').addClass('select-full-width');
 | 
			
		||||
    $(modal + ' .select2-container').css('width', '100%');
 | 
			
		||||
 | 
			
		||||
    modalShowSubmitButton(modal, true);
 | 
			
		||||
 | 
			
		||||
    $(modal).on('click', '#modal-form-submit', function() {
 | 
			
		||||
@@ -563,13 +560,14 @@ function insertConfirmButton(options) {
 | 
			
		||||
 | 
			
		||||
    var message = options.confirmMessage || '{% trans "Confirm" %}';
 | 
			
		||||
 | 
			
		||||
    var confirm = `
 | 
			
		||||
    <span style='float: left;'>
 | 
			
		||||
        ${message}
 | 
			
		||||
        <input id='modal-confirm' name='confirm' type='checkbox'>
 | 
			
		||||
    </span>`;
 | 
			
		||||
    var html = `
 | 
			
		||||
    <div class="form-check form-switch">
 | 
			
		||||
        <input class="form-check-input" type="checkbox" id="modal-confirm">
 | 
			
		||||
        <label class="form-check-label" for="modal-confirm">${message}</label>
 | 
			
		||||
    </div>
 | 
			
		||||
    `;
 | 
			
		||||
 | 
			
		||||
    $(options.modal).find('#modal-footer-buttons').append(confirm);
 | 
			
		||||
    $(options.modal).find('#modal-footer-buttons').append(html);
 | 
			
		||||
 | 
			
		||||
    // Disable the 'submit' button
 | 
			
		||||
    $(options.modal).find('#modal-form-submit').prop('disabled', true);
 | 
			
		||||
@@ -930,7 +928,7 @@ function clearFormErrors(options) {
 | 
			
		||||
    $(options.modal).find('.form-error-message').remove();
 | 
			
		||||
 | 
			
		||||
    // Remove the "has error" class
 | 
			
		||||
    $(options.modal).find('.has-error').removeClass('has-error');
 | 
			
		||||
    $(options.modal).find('.form-field-error').removeClass('form-field-error');
 | 
			
		||||
 | 
			
		||||
    // Hide the 'non field errors'
 | 
			
		||||
    $(options.modal).find('#non-field-errors').html('');
 | 
			
		||||
@@ -1103,8 +1101,8 @@ function handleFormErrors(errors, fields, options) {
 | 
			
		||||
 */
 | 
			
		||||
function addFieldErrorMessage(field_name, error_text, error_idx, options) {
 | 
			
		||||
 | 
			
		||||
    // Add the 'has-error' class
 | 
			
		||||
    $(options.modal).find(`#div_id_${field_name}`).addClass('has-error');
 | 
			
		||||
    // Add the 'form-field-error' class
 | 
			
		||||
    $(options.modal).find(`#div_id_${field_name}`).addClass('form-field-error');
 | 
			
		||||
 | 
			
		||||
    var field_dom = $(options.modal).find(`#errors-${field_name}`);
 | 
			
		||||
 | 
			
		||||
@@ -1299,7 +1297,7 @@ function addSecondaryModal(field, fields, options) {
 | 
			
		||||
 | 
			
		||||
    var html = `
 | 
			
		||||
    <span style='float: right;'>
 | 
			
		||||
        <div type='button' class='btn btn-primary btn-secondary' title='${secondary.title || secondary.label}' id='btn-new-${name}'>
 | 
			
		||||
        <div type='button' class='btn btn-primary btn-secondary btn-form-secondary' title='${secondary.title || secondary.label}' id='btn-new-${name}'>
 | 
			
		||||
            ${secondary.label || secondary.title}
 | 
			
		||||
        </div>
 | 
			
		||||
    </span>`;
 | 
			
		||||
@@ -1585,7 +1583,6 @@ function initializeChoiceField(field, fields, options) {
 | 
			
		||||
    select.select2({
 | 
			
		||||
        dropdownAutoWidth: false,
 | 
			
		||||
        dropdownParent: $(options.modal),
 | 
			
		||||
        width: '100%',
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1734,7 +1731,7 @@ function constructField(name, parameters, options) {
 | 
			
		||||
                <div class='panel-heading form-panel-heading' id='form-panel-heading-${group}'>`;
 | 
			
		||||
            if (group_options.collapsible) {
 | 
			
		||||
                html += `
 | 
			
		||||
                <div data-toggle='collapse' data-target='#form-panel-content-${group}'>
 | 
			
		||||
                <div data-bs-toggle='collapse' data-bs-target='#form-panel-content-${group}'>
 | 
			
		||||
                    <a href='#'><span id='group-icon-${group}' class='fas fa-angle-up'></span> 
 | 
			
		||||
                `;
 | 
			
		||||
            } else {
 | 
			
		||||
@@ -1760,7 +1757,7 @@ function constructField(name, parameters, options) {
 | 
			
		||||
    var form_classes = 'form-group';
 | 
			
		||||
 | 
			
		||||
    if (parameters.errors) {
 | 
			
		||||
        form_classes += ' has-error';
 | 
			
		||||
        form_classes += ' form-field-error';
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    // Optional content to render before the field
 | 
			
		||||
@@ -1802,7 +1799,7 @@ function constructField(name, parameters, options) {
 | 
			
		||||
        html += `<div class='input-group'>`;
 | 
			
		||||
    
 | 
			
		||||
        if (parameters.prefix) {
 | 
			
		||||
            html += `<span class='input-group-addon'>${parameters.prefix}</span>`;
 | 
			
		||||
            html += `<span class='input-group-text'>${parameters.prefix}</span>`;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -1812,7 +1809,7 @@ function constructField(name, parameters, options) {
 | 
			
		||||
 | 
			
		||||
        if (!parameters.required) {
 | 
			
		||||
            html += `
 | 
			
		||||
            <span class='input-group-addon form-clear' id='clear_${name}' title='{% trans "Clear input" %}'>
 | 
			
		||||
            <span class='input-group-text form-clear' id='clear_${name}' title='{% trans "Clear input" %}'>
 | 
			
		||||
                <span class='icon-red fas fa-backspace'></span>
 | 
			
		||||
            </span>`;
 | 
			
		||||
        }
 | 
			
		||||
@@ -1821,7 +1818,11 @@ function constructField(name, parameters, options) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (parameters.help_text && !options.hideLabels) {
 | 
			
		||||
        html += constructHelpText(name, parameters, options);
 | 
			
		||||
 | 
			
		||||
        // Boolean values are handled differently!
 | 
			
		||||
        if (parameters.type != 'boolean') {
 | 
			
		||||
            html += constructHelpText(name, parameters, options);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Div for error messages
 | 
			
		||||
@@ -1996,7 +1997,6 @@ function constructInputOptions(name, classes, type, parameters) {
 | 
			
		||||
 | 
			
		||||
    switch (parameters.type) {
 | 
			
		||||
    case 'boolean':
 | 
			
		||||
        opts.push(`style='display: inline-block; width: 20px; margin-right: 20px;'`);
 | 
			
		||||
        break;
 | 
			
		||||
    case 'integer':
 | 
			
		||||
    case 'float':
 | 
			
		||||
@@ -2009,6 +2009,15 @@ function constructInputOptions(name, classes, type, parameters) {
 | 
			
		||||
 | 
			
		||||
    if (parameters.multiline) {
 | 
			
		||||
        return `<textarea ${opts.join(' ')}></textarea>`;
 | 
			
		||||
    } else if (parameters.type == 'boolean') {
 | 
			
		||||
        return `
 | 
			
		||||
        <div class='form-check form-switch'>
 | 
			
		||||
            <input ${opts.join(' ')}>
 | 
			
		||||
            <label class='form-check-label' for=''>
 | 
			
		||||
                <em><small>${parameters.help_text}</small></em>
 | 
			
		||||
            </label>
 | 
			
		||||
        </div>
 | 
			
		||||
        `;
 | 
			
		||||
    } else {
 | 
			
		||||
        return `<input ${opts.join(' ')}>`;
 | 
			
		||||
    }
 | 
			
		||||
@@ -2032,7 +2041,7 @@ function constructCheckboxInput(name, parameters) {
 | 
			
		||||
 | 
			
		||||
    return constructInputOptions(
 | 
			
		||||
        name,
 | 
			
		||||
        'checkboxinput',
 | 
			
		||||
        'form-check-input',
 | 
			
		||||
        'checkbox',
 | 
			
		||||
        parameters
 | 
			
		||||
    );
 | 
			
		||||
@@ -2117,7 +2126,7 @@ function constructChoiceInput(name, parameters) {
 | 
			
		||||
 */
 | 
			
		||||
function constructRelatedFieldInput(name) {
 | 
			
		||||
 | 
			
		||||
    var html = `<select id='id_${name}' class='select form-control' name='${name}' style='width: 100%;'></select>`;
 | 
			
		||||
    var html = `<select id='id_${name}' class='select form-control' name='${name}'></select>`;
 | 
			
		||||
 | 
			
		||||
    // Don't load any options - they will be filled via an AJAX request
 | 
			
		||||
 | 
			
		||||
@@ -2191,13 +2200,7 @@ function constructRawInput(name, parameters) {
 | 
			
		||||
 */
 | 
			
		||||
function constructHelpText(name, parameters) {
 | 
			
		||||
    
 | 
			
		||||
    var style = '';
 | 
			
		||||
 | 
			
		||||
    if (parameters.type == 'boolean') {
 | 
			
		||||
        style = `style='display: inline-block; margin-left: 25px' `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var html = `<div id='hint_id_${name}' ${style}class='help-block'><i>${parameters.help_text}</i></div>`;
 | 
			
		||||
    var html = `<div id='hint_id_${name}' class='help-block'><i>${parameters.help_text}</i></div>`;
 | 
			
		||||
 | 
			
		||||
    return html;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -16,9 +16,9 @@
 | 
			
		||||
 | 
			
		||||
function yesNoLabel(value) {
 | 
			
		||||
    if (value) {
 | 
			
		||||
        return `<span class='label label-green'>{% trans "YES" %}</span>`;
 | 
			
		||||
        return `<span class='badge rounded-pill bg-success'>{% trans "YES" %}</span>`;
 | 
			
		||||
    } else {
 | 
			
		||||
        return `<span class='label label-yellow'>{% trans "NO" %}</span>`;
 | 
			
		||||
        return `<span class='badge rounded-pill bg-warning'>{% trans "NO" %}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -92,7 +92,7 @@ function select2Thumbnail(image) {
 | 
			
		||||
 */
 | 
			
		||||
function makeIconBadge(icon, title) {
 | 
			
		||||
 | 
			
		||||
    var html = `<span class='fas ${icon} label-right' title='${title}'></span>`;
 | 
			
		||||
    var html = `<span class='icon-badge fas ${icon} float-right' title='${title}'></span>`;
 | 
			
		||||
 | 
			
		||||
    return html;
 | 
			
		||||
}
 | 
			
		||||
@@ -103,7 +103,7 @@ function makeIconBadge(icon, title) {
 | 
			
		||||
 */
 | 
			
		||||
function makeIconButton(icon, cls, pk, title, options={}) {
 | 
			
		||||
 | 
			
		||||
    var classes = `btn btn-default btn-glyph ${cls}`;
 | 
			
		||||
    var classes = `btn btn-outline-secondary ${cls}`;
 | 
			
		||||
 | 
			
		||||
    var id = `${cls}-${pk}`;
 | 
			
		||||
 | 
			
		||||
@@ -182,8 +182,14 @@ function makeProgressBar(value, maximum, opts={}) {
 | 
			
		||||
 | 
			
		||||
    var id = options.id || 'progress-bar';
 | 
			
		||||
 | 
			
		||||
    var style = '';
 | 
			
		||||
 | 
			
		||||
    if (opts.max_width) {
 | 
			
		||||
        style += `max-width: ${options.max_width}; `;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return `
 | 
			
		||||
    <div id='${id}' class='progress'>
 | 
			
		||||
    <div id='${id}' class='progress' style='${style}'>
 | 
			
		||||
        <div class='progress-bar ${extraclass}' role='progressbar' aria-valuenow='${percent}' aria-valuemin='0' aria-valuemax='100' style='width:${percent}%'></div>
 | 
			
		||||
        <div class='progress-value'>${text}</div>
 | 
			
		||||
    </div>
 | 
			
		||||
 
 | 
			
		||||
@@ -47,14 +47,12 @@ function createNewModal(options={}) {
 | 
			
		||||
        <div class='modal-dialog'>
 | 
			
		||||
            <div class='modal-content'>
 | 
			
		||||
                <div class="modal-header">
 | 
			
		||||
                    <button type="button" class="close" data-dismiss="modal" aria-label='{% trans "Close" %}'>
 | 
			
		||||
                        <span aria-hidden="true">×</span>
 | 
			
		||||
                    </button>
 | 
			
		||||
                    <h3 id='modal-title'>
 | 
			
		||||
                    <h4 id='modal-title' class='modal-title'>
 | 
			
		||||
                        <!-- Form title to be injected here -->
 | 
			
		||||
                    </h3>
 | 
			
		||||
                    </h4>
 | 
			
		||||
                    <button type='button' class='btn-close' data-bs-dismiss='modal' aria-label='{% trans "Close" %}'></button>
 | 
			
		||||
                </div>
 | 
			
		||||
                <div class='modal-form-content-wrapper'>
 | 
			
		||||
                <div class='modal-body modal-form-content-wrapper'>
 | 
			
		||||
                    <div id='non-field-errors'>
 | 
			
		||||
                        <!-- Form error messages go here -->
 | 
			
		||||
                    </div>
 | 
			
		||||
@@ -73,7 +71,8 @@ function createNewModal(options={}) {
 | 
			
		||||
                    <div id='modal-footer-buttons'>
 | 
			
		||||
                        <!-- Extra buttons can be inserted here -->
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <button type='button' class='btn btn-default' id='modal-form-close' data-dismiss='modal'>{% trans "Cancel" %}</button>
 | 
			
		||||
                    <span class='flex-item' style='flex-grow: 1;'></span>
 | 
			
		||||
                    <button type='button' class='btn btn-secondary' id='modal-form-close' data-bs-dismiss='modal'>{% trans "Cancel" %}</button>
 | 
			
		||||
                    <button type='button' class='btn btn-primary' id='modal-form-submit'>{% trans "Submit" %}</button>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
@@ -355,22 +354,6 @@ function partialMatcher(params, data) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function attachToggle(modal) {
 | 
			
		||||
    /* Attach 'bootstrap-toggle' functionality to any checkbox in the modal.
 | 
			
		||||
     * This is simple for visual improvement, 
 | 
			
		||||
     * and also larger toggle style buttons are easier to press!
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    $(modal).find(`input[type='checkbox']`).each(function() {
 | 
			
		||||
        $(this).bootstrapToggle({
 | 
			
		||||
            size: 'small',
 | 
			
		||||
            onstyle: 'success',
 | 
			
		||||
            offstyle: 'warning',
 | 
			
		||||
        });
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function attachSelect(modal) {
 | 
			
		||||
    /* Attach 'select2' functionality to any drop-down list in the modal. 
 | 
			
		||||
     * Provides search filtering for dropdown items
 | 
			
		||||
@@ -550,14 +533,14 @@ function renderErrorMessage(xhr) {
 | 
			
		||||
 | 
			
		||||
    html += `
 | 
			
		||||
    <div class='panel-group'>
 | 
			
		||||
        <div class='panel panel-default'>
 | 
			
		||||
        <div class='panel'>
 | 
			
		||||
            <div class='panel panel-heading'>
 | 
			
		||||
                <div class='panel-title'>
 | 
			
		||||
                    <a data-toggle='collapse' href="#collapse-error-info">{% trans "Show Error Information" %}</a>
 | 
			
		||||
                    <a data-bs-toggle='collapse' href="#collapse-error-info">{% trans "Show Error Information" %}</a>
 | 
			
		||||
                </div>
 | 
			
		||||
            </div>
 | 
			
		||||
            <div class='panel-collapse collapse' id='collapse-error-info'>
 | 
			
		||||
                <div class='panel-body'>`;
 | 
			
		||||
                <div class='panel-content'>`;
 | 
			
		||||
 | 
			
		||||
    html += xhr.responseText;
 | 
			
		||||
 | 
			
		||||
@@ -698,7 +681,6 @@ function injectModalForm(modal, form_html) {
 | 
			
		||||
     */
 | 
			
		||||
    $(modal).find('.modal-form-content').html(form_html);
 | 
			
		||||
    attachSelect(modal);
 | 
			
		||||
    attachToggle(modal);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -806,7 +788,7 @@ function insertActionButton(modal, options) {
 | 
			
		||||
    if (already_present == false) {
 | 
			
		||||
        var html = `
 | 
			
		||||
        <span style='float: right;'>
 | 
			
		||||
            <button name='${options.name}' type='submit' class='btn btn-default modal-form-button' value='${options.name}'>
 | 
			
		||||
            <button name='${options.name}' type='submit' class='btn btn-outline-secondary modal-form-button' value='${options.name}'>
 | 
			
		||||
                ${options.title}
 | 
			
		||||
            </button>
 | 
			
		||||
        </span>`;
 | 
			
		||||
 
 | 
			
		||||
@@ -94,7 +94,7 @@ function renderStockItem(name, data, parameters, options) {
 | 
			
		||||
    if (data.serial && data.quantity == 1) {
 | 
			
		||||
        stock_detail = `{% trans "Serial Number" %}: ${data.serial}`;
 | 
			
		||||
    } else if (data.quantity == 0) {
 | 
			
		||||
        stock_detail = `<span class='label-form label-red'>{% trans "No Stock"% }</span>`;
 | 
			
		||||
        stock_detail = `<span class='badge rounded-pill bg-danger'>{% trans "No Stock"% }</span>`;
 | 
			
		||||
    } else {
 | 
			
		||||
        stock_detail = `{% trans "Quantity" %}: ${data.quantity}`;
 | 
			
		||||
    }
 | 
			
		||||
@@ -172,7 +172,7 @@ function renderPart(name, data, parameters, options) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!data.active) {
 | 
			
		||||
        extra += `<span class='label-form label-red'>{% trans "Inactive" %}</span>`;
 | 
			
		||||
        extra += `<span class='badge badge-right rounded-pill bg-danger'>{% trans "Inactive" %}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    html += `
 | 
			
		||||
 
 | 
			
		||||
@@ -118,6 +118,7 @@ function createPurchaseOrder(options={}) {
 | 
			
		||||
                prefix: global_settings.PURCHASEORDER_REFERENCE_PREFIX,
 | 
			
		||||
            },
 | 
			
		||||
            supplier: {
 | 
			
		||||
                icon: 'fa-building',
 | 
			
		||||
                value: options.supplier,
 | 
			
		||||
                secondary: {
 | 
			
		||||
                    title: '{% trans "Add Supplier" %}',
 | 
			
		||||
 
 | 
			
		||||
@@ -410,14 +410,12 @@ function toggleStar(options) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function partStockLabel(part, options={}) {
 | 
			
		||||
 | 
			
		||||
    var label_class = options.label_class || 'label-form';
 | 
			
		||||
function partStockLabel(part) {
 | 
			
		||||
 | 
			
		||||
    if (part.in_stock) {
 | 
			
		||||
        return `<span class='label ${label_class} label-green'>{% trans "Stock" %}: ${part.in_stock}</span>`;
 | 
			
		||||
        return `<span class='badge rounded-pill bg-success'>{% trans "Stock" %}: ${part.in_stock}</span>`;
 | 
			
		||||
    } else {
 | 
			
		||||
        return `<span class='label ${label_class} label-red'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
        return `<span class='badge rounded-pill bg-danger'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -453,7 +451,7 @@ function makePartIcons(part) {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (!part.active) {
 | 
			
		||||
        html += `<span class='label label-warning label-right'>{% trans "Inactive" %}</span>`; 
 | 
			
		||||
        html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return html;
 | 
			
		||||
@@ -530,7 +528,7 @@ function loadPartVariantTable(table, partId, options={}) {
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                if (!row.active) {
 | 
			
		||||
                    html += `<span class='label label-warning label-right'>{% trans "Inactive" %}</span>`; 
 | 
			
		||||
                    html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`; 
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                return html;
 | 
			
		||||
@@ -769,14 +767,10 @@ function partGridTile(part) {
 | 
			
		||||
    // Rows for table view
 | 
			
		||||
    var rows = '';
 | 
			
		||||
 | 
			
		||||
    if (part.IPN) {
 | 
			
		||||
        rows += `<tr><td><b>{% trans "IPN" %}</b></td><td>${part.IPN}</td></tr>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var stock = `${part.in_stock}`;
 | 
			
		||||
 | 
			
		||||
    if (!part.in_stock) {
 | 
			
		||||
        stock = `<span class='label label-red'>{% trans "No Stock" %}</label>`;
 | 
			
		||||
        stock = `<span class='badge rounded-pill bg-danger'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rows += `<tr><td><b>{% trans "Stock" %}</b></td><td>${stock}</td></tr>`;
 | 
			
		||||
@@ -791,8 +785,8 @@ function partGridTile(part) {
 | 
			
		||||
 | 
			
		||||
    var html = `
 | 
			
		||||
    
 | 
			
		||||
    <div class='product-card card'>
 | 
			
		||||
        <div class='panel panel-default panel-inventree product-card-panel'>
 | 
			
		||||
    <div class='card product-card borderless'>
 | 
			
		||||
        <div class='panel product-card-panel'>
 | 
			
		||||
            <div class='panel-heading'>
 | 
			
		||||
                <a href='/part/${part.pk}/'>
 | 
			
		||||
                    <b>${part.full_name}</b>
 | 
			
		||||
@@ -949,20 +943,20 @@ function loadPartTable(table, url, options={}) {
 | 
			
		||||
 | 
			
		||||
                // Is stock "low" (below the 'minimum_stock' quantity)?
 | 
			
		||||
                if (row.minimum_stock && row.minimum_stock > value) {
 | 
			
		||||
                    value += `<span class='label label-right label-warning'>{% trans "Low stock" %}</span>`;
 | 
			
		||||
                    value += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Low stock" %}</span>`;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            } else if (row.on_order) {
 | 
			
		||||
                // There is no stock available, but stock is on order
 | 
			
		||||
                value = `0<span class='label label-right label-primary'>{% trans "On Order" %}: ${row.on_order}</span>`;
 | 
			
		||||
                value = `0<span class='badge badge-right rounded-pill bg-info'>{% trans "On Order" %}: ${row.on_order}</span>`;
 | 
			
		||||
                link = '?display=purchase-orders';
 | 
			
		||||
            } else if (row.building) {
 | 
			
		||||
                // There is no stock available, but stock is being built
 | 
			
		||||
                value = `0<span class='label label-right label-info'>{% trans "Building" %}: ${row.building}</span>`;
 | 
			
		||||
                value = `0<span class='badge badge-right rounded-pill bg-info'>{% trans "Building" %}: ${row.building}</span>`;
 | 
			
		||||
                link = '?display=build-orders';
 | 
			
		||||
            } else {
 | 
			
		||||
                // There is no stock available
 | 
			
		||||
                value = `0<span class='label label-right label-danger'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
                value = `0<span class='badge badge-right rounded-pill bg-danger'>{% trans "No Stock" %}</span>`;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return renderLink(value, `/part/${row.pk}/${link}`);
 | 
			
		||||
@@ -989,6 +983,8 @@ function loadPartTable(table, url, options={}) {
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    var grid_view = inventreeLoad('part-grid-view') == 1;
 | 
			
		||||
 | 
			
		||||
    $(table).inventreeTable({
 | 
			
		||||
        url: url,
 | 
			
		||||
        method: 'get',
 | 
			
		||||
@@ -1003,8 +999,52 @@ function loadPartTable(table, url, options={}) {
 | 
			
		||||
        },
 | 
			
		||||
        columns: columns,
 | 
			
		||||
        showColumns: true,
 | 
			
		||||
        showCustomView: false,
 | 
			
		||||
        showCustomView: grid_view,
 | 
			
		||||
        showCustomViewButton: false,
 | 
			
		||||
        onPostBody: function() {
 | 
			
		||||
            grid_view = inventreeLoad('part-grid-view') == 1;
 | 
			
		||||
            if (grid_view) {
 | 
			
		||||
                $('#view-part-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
 | 
			
		||||
                $('#view-part-grid').removeClass('btn-outline-secondary').addClass('btn-secondary');
 | 
			
		||||
            } else {
 | 
			
		||||
                $('#view-part-grid').removeClass('btn-secondary').addClass('btn-outline-secondary');
 | 
			
		||||
                $('#view-part-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        buttons: [
 | 
			
		||||
            {
 | 
			
		||||
                icon: 'fas fa-bars',
 | 
			
		||||
                attributes: {
 | 
			
		||||
                    title: '{% trans "Display as list" %}',
 | 
			
		||||
                    id: 'view-part-list',
 | 
			
		||||
                },
 | 
			
		||||
                event: () => {
 | 
			
		||||
                    inventreeSave('part-grid-view', 0);
 | 
			
		||||
                    $(table).bootstrapTable(
 | 
			
		||||
                        'refreshOptions',
 | 
			
		||||
                        {
 | 
			
		||||
                            showCustomView: false,
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                icon: 'fas fa-th',
 | 
			
		||||
                attributes: {
 | 
			
		||||
                    title: '{% trans "Display as grid" %}',
 | 
			
		||||
                    id: 'view-part-grid',
 | 
			
		||||
                },
 | 
			
		||||
                event: () => {
 | 
			
		||||
                    inventreeSave('part-grid-view', 1);
 | 
			
		||||
                    $(table).bootstrapTable(
 | 
			
		||||
                        'refreshOptions',
 | 
			
		||||
                        {
 | 
			
		||||
                            showCustomView: true,
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        customView: function(data) {
 | 
			
		||||
 | 
			
		||||
            var html = '';
 | 
			
		||||
@@ -1117,14 +1157,88 @@ function loadPartCategoryTable(table, options) {
 | 
			
		||||
 | 
			
		||||
    setupFilterList(filterKey, table, filterListElement);
 | 
			
		||||
 | 
			
		||||
    var tree_view = inventreeLoad('category-tree-view') == 1;
 | 
			
		||||
 | 
			
		||||
    table.inventreeTable({
 | 
			
		||||
        treeEnable: tree_view,
 | 
			
		||||
        rootParentId: options.params.parent,
 | 
			
		||||
        uniqueId: 'pk',
 | 
			
		||||
        idField: 'pk',
 | 
			
		||||
        treeShowField: 'name',
 | 
			
		||||
        parentIdField: 'parent',
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: options.url || '{% url "api-part-category-list" %}',
 | 
			
		||||
        queryParams: filters,
 | 
			
		||||
        sidePagination: 'server',
 | 
			
		||||
        disablePagination: tree_view,
 | 
			
		||||
        sidePagination: tree_view ? 'client' : 'server',
 | 
			
		||||
        serverSort: !tree_view, 
 | 
			
		||||
        search: !tree_view,
 | 
			
		||||
        name: 'category',
 | 
			
		||||
        original: original,
 | 
			
		||||
        showColumns: true,
 | 
			
		||||
        buttons: [
 | 
			
		||||
            {
 | 
			
		||||
                icon: 'fas fa-bars',
 | 
			
		||||
                attributes: {
 | 
			
		||||
                    title: '{% trans "Display as list" %}',
 | 
			
		||||
                    id: 'view-category-list',
 | 
			
		||||
                },
 | 
			
		||||
                event: () => {
 | 
			
		||||
                    inventreeSave('category-tree-view', 0);
 | 
			
		||||
                    table.bootstrapTable(
 | 
			
		||||
                        'refreshOptions',
 | 
			
		||||
                        {
 | 
			
		||||
                            treeEnable: false,
 | 
			
		||||
                            serverSort: true,
 | 
			
		||||
                            search: true,
 | 
			
		||||
                            pagination: true,
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                icon: 'fas fa-sitemap',
 | 
			
		||||
                attributes: {
 | 
			
		||||
                    title: '{% trans "Display as tree" %}',
 | 
			
		||||
                    id: 'view-category-tree',
 | 
			
		||||
                },
 | 
			
		||||
                event: () => {
 | 
			
		||||
                    inventreeSave('category-tree-view', 1);
 | 
			
		||||
                    table.bootstrapTable(
 | 
			
		||||
                        'refreshOptions',
 | 
			
		||||
                        {
 | 
			
		||||
                            treeEnable: true,
 | 
			
		||||
                            serverSort: false,
 | 
			
		||||
                            search: false,
 | 
			
		||||
                            pagination: false,
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        onPostBody: function() {
 | 
			
		||||
 | 
			
		||||
            tree_view = inventreeLoad('category-tree-view') == 1;
 | 
			
		||||
 | 
			
		||||
            if (tree_view) {
 | 
			
		||||
 | 
			
		||||
                $('#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 {
 | 
			
		||||
                $('#view-category-tree').removeClass('btn-secondary').addClass('btn-outline-secondary');
 | 
			
		||||
                $('#view-category-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        columns: [
 | 
			
		||||
            {
 | 
			
		||||
                checkbox: true,
 | 
			
		||||
@@ -1154,7 +1268,8 @@ function loadPartCategoryTable(table, options) {
 | 
			
		||||
            {
 | 
			
		||||
                field: 'pathstring',
 | 
			
		||||
                title: '{% trans "Path" %}',
 | 
			
		||||
                switchable: true,
 | 
			
		||||
                switchable: !tree_view,
 | 
			
		||||
                visible: !tree_view,
 | 
			
		||||
                sortable: false,
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
 
 | 
			
		||||
@@ -450,17 +450,17 @@ function removeStockRow(e) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
function passFailBadge(result, align='float-right') {
 | 
			
		||||
function passFailBadge(result) {
 | 
			
		||||
 | 
			
		||||
    if (result) {
 | 
			
		||||
        return `<span class='label label-green ${align}'>{% trans "PASS" %}</span>`;
 | 
			
		||||
        return `<span class='badge badge-right rounded-pill bg-success'>{% trans "PASS" %}</span>`;
 | 
			
		||||
    } else {
 | 
			
		||||
        return `<span class='label label-red ${align}'>{% trans "FAIL" %}</span>`;
 | 
			
		||||
        return `<span class='badge badge-right rounded-pill bg-danger'>{% trans "FAIL" %}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function noResultBadge(align='float-right') {
 | 
			
		||||
    return `<span class='label label-blue ${align}'>{% trans "NO RESULT" %}</span>`;
 | 
			
		||||
function noResultBadge() {
 | 
			
		||||
    return `<span class='badge badge-right rounded-pill bg-info'>{% trans "NO RESULT" %}</span>`;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function formatDate(row) {
 | 
			
		||||
@@ -468,11 +468,7 @@ function formatDate(row) {
 | 
			
		||||
    var html = row.date;
 | 
			
		||||
 | 
			
		||||
    if (row.user_detail) {
 | 
			
		||||
        html += `<span class='badge'>${row.user_detail.username}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (row.attachment) {
 | 
			
		||||
        html += `<a href='${row.attachment}'><span class='fas fa-file-alt label-right'></span></a>`;
 | 
			
		||||
        html += `<span class='badge badge-right rounded-pill bg-secondary'>${row.user_detail.username}</span>`;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return html;
 | 
			
		||||
@@ -553,6 +549,15 @@ function loadStockTestResultsTable(table, options) {
 | 
			
		||||
            {
 | 
			
		||||
                field: 'value',
 | 
			
		||||
                title: '{% trans "Value" %}',
 | 
			
		||||
                formatter: function(value, row) {
 | 
			
		||||
                    var html = value;
 | 
			
		||||
 | 
			
		||||
                    if (row.attachment) {
 | 
			
		||||
                        html += `<a href='${row.attachment}'><span class='fas fa-file-alt float-right'></span></a>`;
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    return html;
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                field: 'notes',
 | 
			
		||||
@@ -878,16 +883,12 @@ function loadStockTable(table, options) {
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if (row.quantity <= 0) {
 | 
			
		||||
                html += `<span class='label label-right label-danger'>{% trans "Depleted" %}</span>`;
 | 
			
		||||
                html += `<span class='badge rounded-pill bg-danger'>{% trans "Depleted" %}</span>`;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return html;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    if (!options.params.ordering) {
 | 
			
		||||
        col['sortable'] = true;
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    columns.push(col);
 | 
			
		||||
 | 
			
		||||
@@ -1442,7 +1443,19 @@ function loadStockLocationTable(table, options) {
 | 
			
		||||
        filters[key] = params[key];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    var tree_view = inventreeLoad('location-tree-view') == 1;
 | 
			
		||||
 | 
			
		||||
    table.inventreeTable({
 | 
			
		||||
        treeEnable: tree_view,
 | 
			
		||||
        rootParentId: options.params.parent,
 | 
			
		||||
        uniqueId: 'pk',
 | 
			
		||||
        idField: 'pk',
 | 
			
		||||
        treeShowField: 'name',
 | 
			
		||||
        parentIdField: 'parent',
 | 
			
		||||
        disablePagination: tree_view,
 | 
			
		||||
        sidePagination: tree_view ? 'client' : 'server',
 | 
			
		||||
        serverSort: !tree_view,
 | 
			
		||||
        search: !tree_view,
 | 
			
		||||
        method: 'get',
 | 
			
		||||
        url: options.url || '{% url "api-location-list" %}',
 | 
			
		||||
        queryParams: filters,
 | 
			
		||||
@@ -1450,6 +1463,69 @@ function loadStockLocationTable(table, options) {
 | 
			
		||||
        name: 'location',
 | 
			
		||||
        original: original,
 | 
			
		||||
        showColumns: true,
 | 
			
		||||
        onPostBody: function() {
 | 
			
		||||
 | 
			
		||||
            tree_view = inventreeLoad('location-tree-view') == 1;
 | 
			
		||||
 | 
			
		||||
            if (tree_view) {
 | 
			
		||||
 | 
			
		||||
                $('#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 {
 | 
			
		||||
                $('#view-location-tree').removeClass('btn-secondary').addClass('btn-outline-secondary');
 | 
			
		||||
                $('#view-location-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
 | 
			
		||||
            }
 | 
			
		||||
        },
 | 
			
		||||
        buttons: [
 | 
			
		||||
            {
 | 
			
		||||
                icon: 'fas fa-bars',
 | 
			
		||||
                attributes: {
 | 
			
		||||
                    title: '{% trans "Display as list" %}',
 | 
			
		||||
                    id: 'view-location-list',
 | 
			
		||||
                },
 | 
			
		||||
                event: () => {
 | 
			
		||||
                    inventreeSave('location-tree-view', 0);
 | 
			
		||||
                    table.bootstrapTable(
 | 
			
		||||
                        'refreshOptions',
 | 
			
		||||
                        {
 | 
			
		||||
                            treeEnable: false,
 | 
			
		||||
                            serverSort: true,
 | 
			
		||||
                            search: true,
 | 
			
		||||
                            pagination: true,
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
                icon: 'fas fa-sitemap',
 | 
			
		||||
                attributes: {
 | 
			
		||||
                    title: '{% trans "Display as tree" %}',
 | 
			
		||||
                    id: 'view-location-tree',
 | 
			
		||||
                },
 | 
			
		||||
                event: () => {
 | 
			
		||||
                    inventreeSave('location-tree-view', 1);
 | 
			
		||||
                    table.bootstrapTable(
 | 
			
		||||
                        'refreshOptions',
 | 
			
		||||
                        {
 | 
			
		||||
                            treeEnable: true,
 | 
			
		||||
                            serverSort: false,
 | 
			
		||||
                            search: false,
 | 
			
		||||
                            pagination: false,
 | 
			
		||||
                        }
 | 
			
		||||
                    );
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        ],
 | 
			
		||||
        columns: [
 | 
			
		||||
            {
 | 
			
		||||
                checkbox: true,
 | 
			
		||||
@@ -1684,8 +1760,8 @@ function loadStockTrackingTable(table, options) {
 | 
			
		||||
        formatter: function(value, row, index, field) {
 | 
			
		||||
            // Manually created entries can be edited or deleted
 | 
			
		||||
            if (false && !row.system) {
 | 
			
		||||
                var bEdit = "<button title='{% trans 'Edit tracking entry' %}' class='btn btn-entry-edit btn-default btn-glyph' type='button' url='/stock/track/" + row.pk + "/edit/'><span class='fas fa-edit'/></button>";
 | 
			
		||||
                var bDel = "<button title='{% trans 'Delete tracking entry' %}' class='btn btn-entry-delete btn-default btn-glyph' type='button' url='/stock/track/" + row.pk + "/delete/'><span class='fas fa-trash-alt icon-red'/></button>";
 | 
			
		||||
                var bEdit = "<button title='{% trans 'Edit tracking entry' %}' class='btn btn-entry-edit btn-outline-secondary' type='button' url='/stock/track/" + row.pk + "/edit/'><span class='fas fa-edit'/></button>";
 | 
			
		||||
                var bDel = "<button title='{% trans 'Delete tracking entry' %}' class='btn btn-entry-delete btn-outline-secondary' type='button' url='/stock/track/" + row.pk + "/delete/'><span class='fas fa-trash-alt icon-red'/></button>";
 | 
			
		||||
 | 
			
		||||
                return "<div class='btn-group' role='group'>" + bEdit + bDel + "</div>";
 | 
			
		||||
            } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -214,6 +214,8 @@ $.fn.inventreeTable = function(options) {
 | 
			
		||||
        options.pageList = [25, 50, 100, 250, 'all'];
 | 
			
		||||
        options.totalField = 'count';
 | 
			
		||||
        options.dataField = 'results';
 | 
			
		||||
    } else {
 | 
			
		||||
        options.pagination = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Extract query params
 | 
			
		||||
@@ -397,3 +399,8 @@ function customGroupSorter(sortName, sortOrder, sortData) {
 | 
			
		||||
    $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['en-US-custom']);
 | 
			
		||||
 | 
			
		||||
})(jQuery);
 | 
			
		||||
 | 
			
		||||
$.extend($.fn.treegrid.defaults, {
 | 
			
		||||
    expanderExpandedClass: 'treegrid-expander-expanded',
 | 
			
		||||
    expanderCollapsedClass: 'treegrid-expander-collapsed'
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user