Merge pull request #2026 from inventree/js-linting

Js linting
This commit is contained in:
Oliver 2021-09-03 23:35:14 +10:00 committed by GitHub
commit c275bf8d98
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
30 changed files with 1748 additions and 1093 deletions

25
.eslintrc.yml Normal file
View File

@ -0,0 +1,25 @@
env:
commonjs: false
browser: true
es2021: true
jquery: true
extends:
- google
parserOptions:
ecmaVersion: 12
rules:
no-var: off
guard-for-in: off
no-trailing-spaces: off
camelcase: off
padded-blocks: off
prefer-const: off
max-len: off
require-jsdoc: off
valid-jsdoc: off
no-multiple-empty-lines: off
comma-dangle: off
prefer-spread: off
indent:
- error
- 4

View File

@ -18,11 +18,33 @@ jobs:
env: env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
INVENTREE_DB_ENGINE: sqlite3
INVENTREE_DB_NAME: inventree
INVENTREE_MEDIA_ROOT: ./media
INVENTREE_STATIC_ROOT: ./static
steps: steps:
- name: Install node.js
uses: actions/setup-node@v2
- run: npm install
- name: Checkout Code - name: Checkout Code
uses: actions/checkout@v2 uses: actions/checkout@v2
- name: Check Files - name: Setup Python
uses: actions/setup-python@v2
with:
python-version: 3.7
- name: Install Dependencies
run: |
sudo apt-get update
sudo apt-get install gettext
pip3 install invoke
invoke install
invoke static
- name: Check Templated Files
run: | run: |
cd ci cd ci
python check_js_templates.py python check_js_templates.py
- name: Lint Javascript Files
run: |
npm install eslint eslint-config-google
invoke render-js-files
npx eslint js_tmp/*.js

8
.gitignore vendored
View File

@ -67,8 +67,16 @@ secret_key.txt
.coverage .coverage
htmlcov/ htmlcov/
# Temporary javascript files (used for testing)
js_tmp/
# Development files # Development files
dev/ dev/
# Locale stats file # Locale stats file
locale_stats.json locale_stats.json
# node.js
package-lock.json
package.json
node_modules/

View File

@ -0,0 +1,100 @@
"""
Pull rendered copies of the templated
"""
from django.http import response
from django.test import TestCase, testcases
from django.contrib.auth import get_user_model
import os
import pathlib
class RenderJavascriptFiles(TestCase):
"""
A unit test to "render" javascript files.
The server renders templated javascript files,
we need the fully-rendered files for linting and static tests.
"""
def setUp(self):
user = get_user_model()
self.user = user.objects.create_user(
username='testuser',
password='testpassword',
email='user@gmail.com',
)
self.client.login(username='testuser', password='testpassword')
def download_file(self, filename, prefix):
url = os.path.join(prefix, filename)
response = self.client.get(url)
here = os.path.abspath(os.path.dirname(__file__))
output_dir = os.path.join(
here,
'..',
'..',
'js_tmp',
)
output_dir = os.path.abspath(output_dir)
if not os.path.exists(output_dir):
os.mkdir(output_dir)
output_file = os.path.join(
output_dir,
filename,
)
with open(output_file, 'wb') as output:
output.write(response.content)
def download_files(self, subdir, prefix):
here = os.path.abspath(os.path.dirname(__file__))
js_template_dir = os.path.join(
here,
'..',
'templates',
'js',
)
directory = os.path.join(js_template_dir, subdir)
directory = os.path.abspath(directory)
js_files = pathlib.Path(directory).rglob('*.js')
n = 0
for f in js_files:
js = os.path.basename(f)
self.download_file(js, prefix)
n += 1
return n
def test_render_files(self):
"""
Look for all javascript files
"""
n = 0
print("Rendering javascript files...")
n += self.download_files('translated', '/js/i18n')
n += self.download_files('dynamic', '/js/dynamic')
print(f"Rendered {n} javascript files.")

View File

@ -111,6 +111,7 @@ translated_javascript_urls = [
url(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'), url(r'^company.js', DynamicJsView.as_view(template_name='js/translated/company.js'), name='company.js'),
url(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'), url(r'^filters.js', DynamicJsView.as_view(template_name='js/translated/filters.js'), name='filters.js'),
url(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'), url(r'^forms.js', DynamicJsView.as_view(template_name='js/translated/forms.js'), name='forms.js'),
url(r'^helpers.js', DynamicJsView.as_view(template_name='js/translated/helpers.js'), name='helpers.js'),
url(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'), url(r'^label.js', DynamicJsView.as_view(template_name='js/translated/label.js'), name='label.js'),
url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'), url(r'^model_renderers.js', DynamicJsView.as_view(template_name='js/translated/model_renderers.js'), name='model_renderers.js'),
url(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'), url(r'^modals.js', DynamicJsView.as_view(template_name='js/translated/modals.js'), name='modals.js'),

View File

@ -160,6 +160,7 @@
<script type='text/javascript' src="{% i18n_static 'company.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'company.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'filters.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'filters.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'forms.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'helpers.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'label.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'label.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'modals.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'modals.js' %}"></script>
<script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script> <script type='text/javascript' src="{% i18n_static 'model_renderers.js' %}"></script>

View File

@ -1,17 +1,26 @@
{% load i18n %} {% load i18n %}
/* globals
*/
/* exported
clearEvents,
endDate,
startDate,
*/
/** /**
* Helper functions for calendar display * Helper functions for calendar display
*/ */
function startDate(calendar) { function startDate(calendar) {
// Extract the first displayed date on the calendar // Extract the first displayed date on the calendar
return calendar.currentData.dateProfile.activeRange.start.toISOString().split("T")[0]; return calendar.currentData.dateProfile.activeRange.start.toISOString().split('T')[0];
} }
function endDate(calendar) { function endDate(calendar) {
// Extract the last display date on the calendar // Extract the last display date on the calendar
return calendar.currentData.dateProfile.activeRange.end.toISOString().split("T")[0]; return calendar.currentData.dateProfile.activeRange.end.toISOString().split('T')[0];
} }
function clearEvents(calendar) { function clearEvents(calendar) {
@ -21,5 +30,5 @@ function clearEvents(calendar) {
events.forEach(function(event) { events.forEach(function(event) {
event.remove(); event.remove();
}) });
} }

View File

@ -1,5 +1,20 @@
{% load inventree_extras %} {% load inventree_extras %}
/* globals
ClipboardJS,
inventreeFormDataUpload,
launchModalForm,
user_settings,
*/
/* exported
attachClipboard,
enableDragAndDrop,
inventreeDocReady,
inventreeLoad,
inventreeSave,
*/
function attachClipboard(selector, containerselector, textElement) { function attachClipboard(selector, containerselector, textElement) {
// set container // set container
if (containerselector) { if (containerselector) {
@ -8,19 +23,22 @@ function attachClipboard(selector, containerselector, textElement) {
containerselector = document.body; containerselector = document.body;
} }
var text = null;
// set text-function // set text-function
if (textElement) { if (textElement) {
text = function() { text = function() {
return document.getElementById(textElement).textContent; return document.getElementById(textElement).textContent;
} };
} else { } else {
text = function(trigger) { text = function(trigger) {
var content = trigger.parentElement.parentElement.textContent; var content = trigger.parentElement.parentElement.textContent;
return content.trim(); return content.trim();
} };
} }
// create Clipboard // create Clipboard
// eslint-disable-next-line no-unused-vars
var cis = new ClipboardJS(selector, { var cis = new ClipboardJS(selector, {
text: text, text: text,
container: containerselector container: containerselector
@ -33,12 +51,12 @@ function inventreeDocReady() {
* This will be called for every page that extends "base.html" * This will be called for every page that extends "base.html"
*/ */
window.addEventListener("dragover",function(e){ window.addEventListener('dragover', function(e) {
e = e || event; e = e || event;
e.preventDefault(); e.preventDefault();
}, false); }, false);
window.addEventListener("drop",function(e){ window.addEventListener('drop', function(e) {
e = e || event; e = e || event;
e.preventDefault(); e.preventDefault();
}, false); }, false);
@ -51,12 +69,13 @@ function inventreeDocReady() {
// TODO - Only indicate that a drop event will occur if a file is being dragged // TODO - Only indicate that a drop event will occur if a file is being dragged
var transfer = event.originalEvent.dataTransfer; var transfer = event.originalEvent.dataTransfer;
// eslint-disable-next-line no-constant-condition
if (true || isFileTransfer(transfer)) { if (true || isFileTransfer(transfer)) {
$(this).addClass('dragover'); $(this).addClass('dragover');
} }
}); });
$('.dropzone').on('dragleave drop', function(event) { $('.dropzone').on('dragleave drop', function() {
$(this).removeClass('dragover'); $(this).removeClass('dragover');
}); });
@ -74,18 +93,18 @@ function inventreeDocReady() {
// Callback to launch the 'Database Stats' window // Callback to launch the 'Database Stats' window
$('#launch-stats').click(function() { $('#launch-stats').click(function() {
launchModalForm("/stats/", { launchModalForm('/stats/', {
no_post: true, no_post: true,
}); });
}); });
// Initialize clipboard-buttons // Initialize clipboard-buttons
attachClipboard('.clip-btn'); attachClipboard('.clip-btn');
attachClipboard('.clip-btn', 'modal-about'); // modals attachClipboard('.clip-btn', 'modal-about');
attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text'); // version-text attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text');
// Add autocomplete to the search-bar // Add autocomplete to the search-bar
$("#search-bar" ).autocomplete({ $('#search-bar').autocomplete({
source: function(request, response) { source: function(request, response) {
$.ajax({ $.ajax({
url: '/api/part/', url: '/api/part/',
@ -128,7 +147,9 @@ function inventreeDocReady() {
window.location = '/part/' + ui.item.id + '/'; window.location = '/part/' + ui.item.id + '/';
}, },
minLength: 2, minLength: 2,
classes: {'ui-autocomplete': 'dropdown-menu search-menu'}, classes: {
'ui-autocomplete': 'dropdown-menu search-menu',
},
}); });
} }
@ -140,124 +161,6 @@ function isFileTransfer(transfer) {
} }
function isOnlineTransfer(transfer) {
/* Determine if a drag-and-drop transfer is from another website.
* e.g. dragged from another browser window
*/
return transfer.items.length > 0;
}
function getImageUrlFromTransfer(transfer) {
/* Extract external image URL from a drag-and-dropped image
*/
var url = transfer.getData('text/html').match(/src\s*=\s*"(.+?)"/)[1];
console.log('Image URL: ' + url);
return url;
}
function makeIconBadge(icon, title) {
// Construct an 'icon badge' which floats to the right of an object
var html = `<span class='fas ${icon} label-right' title='${title}'></span>`;
return html;
}
function makeIconButton(icon, cls, pk, title, options={}) {
// Construct an 'icon button' using the fontawesome set
var classes = `btn btn-default btn-glyph ${cls}`;
var id = `${cls}-${pk}`;
var html = '';
var extraProps = '';
if (options.disabled) {
extraProps += "disabled='true' ";
}
html += `<button pk='${pk}' id='${id}' class='${classes}' title='${title}' ${extraProps}>`;
html += `<span class='fas ${icon}'></span>`;
html += `</button>`;
return html;
}
function makeProgressBar(value, maximum, opts={}) {
/*
* Render a progessbar!
*
* @param value is the current value of the progress bar
* @param maximum is the maximum value of the progress bar
*/
var options = opts || {};
value = parseFloat(value);
var percent = 100;
// Prevent div-by-zero or null value
if (maximum && maximum > 0) {
maximum = parseFloat(maximum);
percent = parseInt(value / maximum * 100);
}
if (percent > 100) {
percent = 100;
}
var extraclass = '';
if (value > maximum) {
extraclass='progress-bar-over';
} else if (value < maximum) {
extraclass = 'progress-bar-under';
}
var style = options.style || '';
var text = '';
if (style == 'percent') {
// Display e.g. "50%"
text = `${percent}%`;
} else if (style == 'max') {
// Display just the maximum value
text = `${maximum}`;
} else if (style == 'value') {
// Display just the current value
text = `${value}`;
} else if (style == 'blank') {
// No display!
text = '';
} else {
/* Default style
* Display e.g. "5 / 10"
*/
text = `${value} / ${maximum}`;
}
var id = options.id || 'progress-bar';
return `
<div id='${id}' class='progress'>
<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>
`;
}
function enableDragAndDrop(element, url, options) { function enableDragAndDrop(element, url, options) {
/* Enable drag-and-drop file uploading for a given element. /* Enable drag-and-drop file uploading for a given element.
@ -272,7 +175,7 @@ function enableDragAndDrop(element, url, options) {
method - HTTP method method - HTTP method
*/ */
data = options.data || {}; var data = options.data || {};
$(element).on('drop', function(event) { $(element).on('drop', function(event) {
@ -315,40 +218,28 @@ function enableDragAndDrop(element, url, options) {
}); });
} }
function imageHoverIcon(url) {
/* Render a small thumbnail icon for an image.
* On mouseover, display a full-size version of the image
*/
if (!url) { /**
url = '/static/img/blank_image.png';
}
var html = `
<a class='hover-icon'>
<img class='hover-img-thumb' src='` + url + `'>
<img class='hover-img-large' src='` + url + `'>
</a>
`;
return html;
}
function inventreeSave(name, value) {
/*
* Save a key:value pair to local storage * Save a key:value pair to local storage
* @param {String} name - settting key
* @param {String} value - setting value
*/ */
function inventreeSave(name, value) {
var key = "inventree-" + name; var key = `inventree-${name}`;
localStorage.setItem(key, value); localStorage.setItem(key, value);
} }
function inventreeLoad(name, defaultValue) {
/*
* Retrieve a key:value pair from local storage
*/
var key = "inventree-" + name; /**
* Retrieve a key:value pair from local storage
* @param {*} name - setting key
* @param {*} defaultValue - default value (returned if no matching key:value pair is found)
* @returns
*/
function inventreeLoad(name, defaultValue) {
var key = `inventree-${name}`;
var value = localStorage.getItem(key); var value = localStorage.getItem(key);
@ -358,27 +249,3 @@ function inventreeLoad(name, defaultValue) {
return value; return value;
} }
} }
function inventreeLoadInt(name) {
/*
* Retrieve a value from local storage, and attempt to cast to integer
*/
var data = inventreeLoad(name);
return parseInt(data, 10);
}
function inventreeLoadFloat(name) {
var data = inventreeLoad(name);
return parseFloat(data);
}
function inventreeDel(name) {
var key = 'inventree-' + name;
localStorage.removeItem(key);
}

View File

@ -1,3 +1,10 @@
/* globals
*/
/* exported
attachNavCallbacks,
onPanelLoad,
*/
/* /*
* Attach callbacks to navigation bar elements. * Attach callbacks to navigation bar elements.
@ -55,7 +62,7 @@ function activatePanel(panelName, options={}) {
// Iterate through the available 'select' elements until one matches // Iterate through the available 'select' elements until one matches
panelName = null; panelName = null;
$('.nav-toggle').each(function(item) { $('.nav-toggle').each(function() {
var panel_name = $(this).attr('id').replace('select-', ''); var panel_name = $(this).attr('id').replace('select-', '');
if ($(`#panel-${panel_name}`).length && (panelName == null)) { if ($(`#panel-${panel_name}`).length && (panelName == null)) {
@ -83,9 +90,9 @@ function activatePanel(panelName, options={}) {
$('.list-group-item').removeClass('active'); $('.list-group-item').removeClass('active');
// Find the associated selector // Find the associated selector
var select = `#select-${panelName}`; var selectElement = `#select-${panelName}`;
$(select).parent('.list-group-item').addClass('active'); $(selectElement).parent('.list-group-item').addClass('active');
} }
@ -96,7 +103,7 @@ function onPanelLoad(panel, callback) {
var panelId = `#panel-${panel}`; var panelId = `#panel-${panel}`;
$(panelId).on('fadeInStarted', function(e) { $(panelId).on('fadeInStarted', function() {
// Trigger the callback // Trigger the callback
callback(); callback();

View File

@ -1,17 +1,19 @@
{% load inventree_extras %} {% load inventree_extras %}
// InvenTree settings
/* exported
user_settings,
global_settings,
*/
{% user_settings request.user as USER_SETTINGS %} {% user_settings request.user as USER_SETTINGS %}
const user_settings = {
var user_settings = {
{% for key, value in USER_SETTINGS.items %} {% for key, value in USER_SETTINGS.items %}
{{ key }}: {% primitive_to_javascript value %}, {{ key }}: {% primitive_to_javascript value %},
{% endfor %} {% endfor %}
}; };
{% global_settings as GLOBAL_SETTINGS %} {% global_settings as GLOBAL_SETTINGS %}
const global_settings = {
var global_settings = {
{% for key, value in GLOBAL_SETTINGS.items %} {% for key, value in GLOBAL_SETTINGS.items %}
{{ key }}: {% primitive_to_javascript value %}, {{ key }}: {% primitive_to_javascript value %},
{% endfor %} {% endfor %}

View File

@ -1,15 +1,29 @@
{% load i18n %} {% load i18n %}
{% load inventree_extras %} {% load inventree_extras %}
var jQuery = window.$; /* globals
renderErrorMessage,
showAlertDialog,
*/
/* exported
inventreeGet,
inventreeDelete,
inventreeFormDataUpload,
showApiError,
*/
$.urlParam = function(name) { $.urlParam = function(name) {
// eslint-disable-next-line no-useless-escape
var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href); var results = new RegExp('[\?&]' + name + '=([^&#]*)').exec(window.location.href);
if (results == null) { if (results == null) {
return null; return null;
} }
return decodeURI(results[1]) || 0; return decodeURI(results[1]) || 0;
} };
// using jQuery // using jQuery
function getCookie(name) { function getCookie(name) {
@ -31,11 +45,10 @@ function getCookie(name) {
function inventreeGet(url, filters={}, options={}) { function inventreeGet(url, filters={}, options={}) {
// Middleware token required for data update // Middleware token required for data update
//var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
var csrftoken = getCookie('csrftoken'); var csrftoken = getCookie('csrftoken');
return $.ajax({ return $.ajax({
beforeSend: function(xhr, settings) { beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', csrftoken); xhr.setRequestHeader('X-CSRFToken', csrftoken);
}, },
url: url, url: url,
@ -73,7 +86,7 @@ function inventreeFormDataUpload(url, data, options={}) {
var csrftoken = getCookie('csrftoken'); var csrftoken = getCookie('csrftoken');
return $.ajax({ return $.ajax({
beforeSend: function(xhr, settings) { beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', csrftoken); xhr.setRequestHeader('X-CSRFToken', csrftoken);
}, },
url: url, url: url,
@ -101,11 +114,10 @@ function inventreePut(url, data={}, options={}) {
var method = options.method || 'PUT'; var method = options.method || 'PUT';
// Middleware token required for data update // Middleware token required for data update
//var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
var csrftoken = getCookie('csrftoken'); var csrftoken = getCookie('csrftoken');
return $.ajax({ return $.ajax({
beforeSend: function(xhr, settings) { beforeSend: function(xhr) {
xhr.setRequestHeader('X-CSRFToken', csrftoken); xhr.setRequestHeader('X-CSRFToken', csrftoken);
}, },
url: url, url: url,
@ -157,29 +169,35 @@ function showApiError(xhr) {
var message = null; var message = null;
switch (xhr.status) { switch (xhr.status) {
case 0: // No response // No response
case 0:
title = '{% trans "No Response" %}'; title = '{% trans "No Response" %}';
message = '{% trans "No response from the InvenTree server" %}'; message = '{% trans "No response from the InvenTree server" %}';
break; break;
case 400: // Bad request // Bad request
case 400:
// Note: Normally error code 400 is handled separately, // Note: Normally error code 400 is handled separately,
// and should now be shown here! // and should now be shown here!
title = '{% trans "Error 400: Bad request" %}'; title = '{% trans "Error 400: Bad request" %}';
message = '{% trans "API request returned error code 400" %}'; message = '{% trans "API request returned error code 400" %}';
break; break;
case 401: // Not authenticated // Not authenticated
case 401:
title = '{% trans "Error 401: Not Authenticated" %}'; title = '{% trans "Error 401: Not Authenticated" %}';
message = '{% trans "Authentication credentials not supplied" %}'; message = '{% trans "Authentication credentials not supplied" %}';
break; break;
case 403: // Permission denied // Permission denied
case 403:
title = '{% trans "Error 403: Permission Denied" %}'; title = '{% trans "Error 403: Permission Denied" %}';
message = '{% trans "You do not have the required permissions to access this function" %}'; message = '{% trans "You do not have the required permissions to access this function" %}';
break; break;
case 404: // Resource not found // Resource not found
case 404:
title = '{% trans "Error 404: Resource Not Found" %}'; title = '{% trans "Error 404: Resource Not Found" %}';
message = '{% trans "The requested resource could not be located on the server" %}'; message = '{% trans "The requested resource could not be located on the server" %}';
break; break;
case 408: // Timeout // Timeout
case 408:
title = '{% trans "Error 408: Timeout" %}'; title = '{% trans "Error 408: Timeout" %}';
message = '{% trans "Connection timeout while requesting data from server" %}'; message = '{% trans "Connection timeout while requesting data from server" %}';
break; break;
@ -189,7 +207,7 @@ function showApiError(xhr) {
break; break;
} }
message += "<hr>"; message += '<hr>';
message += renderErrorMessage(xhr); message += renderErrorMessage(xhr);
showAlertDialog(title, message); showAlertDialog(title, message);

View File

@ -1,8 +1,18 @@
{% load i18n %} {% load i18n %}
/* globals
makeIconButton,
renderLink,
*/
/* exported
loadAttachmentTable,
reloadAttachmentTable,
*/
function reloadAttachmentTable() { function reloadAttachmentTable() {
$('#attachment-table').bootstrapTable("refresh"); $('#attachment-table').bootstrapTable('refresh');
} }
@ -13,7 +23,9 @@ function loadAttachmentTable(url, options) {
$(table).inventreeTable({ $(table).inventreeTable({
url: url, url: url,
name: options.name || 'attachments', name: options.name || 'attachments',
formatNoMatches: function() { return '{% trans "No attachments found" %}'}, formatNoMatches: function() {
return '{% trans "No attachments found" %}';
},
sortable: true, sortable: true,
search: false, search: false,
queryParams: options.filters || {}, queryParams: options.filters || {},
@ -40,7 +52,7 @@ function loadAttachmentTable(url, options) {
{ {
field: 'attachment', field: 'attachment',
title: '{% trans "File" %}', title: '{% trans "File" %}',
formatter: function(value, row) { formatter: function(value) {
var icon = 'fa-file-alt'; var icon = 'fa-file-alt';

View File

@ -1,5 +1,27 @@
{% load i18n %} {% load i18n %}
/* globals
imageHoverIcon,
inventreePut,
makeIconButton,
modalEnable,
modalSetContent,
modalSetTitle,
modalSetSubmitText,
modalShowSubmitButton,
modalSubmit,
showAlertOrCache,
showQuestionDialog,
*/
/* exported
barcodeCheckIn,
barcodeScanDialog,
linkBarcodeDialog,
scanItemsIntoLocation,
unlinkBarcode,
*/
function makeBarcodeInput(placeholderText='', hintText='') { function makeBarcodeInput(placeholderText='', hintText='') {
/* /*
* Generate HTML for a barcode input * Generate HTML for a barcode input
@ -99,7 +121,7 @@ function postBarcodeData(barcode_data, options={}) {
} }
} }
} }
) );
} }
@ -109,7 +131,7 @@ function showBarcodeMessage(modal, message, style='danger') {
html += message; html += message;
html += "</div>"; html += '</div>';
$(modal + ' #barcode-error-message').html(html); $(modal + ' #barcode-error-message').html(html);
} }
@ -256,7 +278,7 @@ function barcodeScanDialog() {
var modal = '#modal-form'; var modal = '#modal-form';
barcodeDialog( barcodeDialog(
"Scan Barcode", '{% trans "Scan Barcode" %}',
{ {
onScan: function(response) { onScan: function(response) {
if ('url' in response) { if ('url' in response) {
@ -280,18 +302,18 @@ function barcodeScanDialog() {
/* /*
* Dialog for linking a particular barcode to a stock item. * Dialog for linking a particular barcode to a stock item.
*/ */
function linkBarcodeDialog(stockitem, options={}) { function linkBarcodeDialog(stockitem) {
var modal = '#modal-form'; var modal = '#modal-form';
barcodeDialog( barcodeDialog(
"{% trans 'Link Barcode to Stock Item' %}", '{% trans "Link Barcode to Stock Item" %}',
{ {
url: '/api/barcode/link/', url: '/api/barcode/link/',
data: { data: {
stockitem: stockitem, stockitem: stockitem,
}, },
onScan: function(response) { onScan: function() {
$(modal).modal('hide'); $(modal).modal('hide');
location.reload(); location.reload();
@ -308,13 +330,13 @@ function unlinkBarcode(stockitem) {
var html = `<b>{% trans "Unlink Barcode" %}</b><br>`; var html = `<b>{% trans "Unlink Barcode" %}</b><br>`;
html += "{% trans 'This will remove the association between this stock item and the barcode' %}"; html += '{% trans "This will remove the association between this stock item and the barcode" %}';
showQuestionDialog( showQuestionDialog(
"{% trans 'Unlink Barcode' %}", '{% trans "Unlink Barcode" %}',
html, html,
{ {
accept_text: "{% trans 'Unlink' %}", accept_text: '{% trans "Unlink" %}',
accept: function() { accept: function() {
inventreePut( inventreePut(
`/api/stock/${stockitem}/`, `/api/stock/${stockitem}/`,
@ -324,7 +346,7 @@ function unlinkBarcode(stockitem) {
}, },
{ {
method: 'PATCH', method: 'PATCH',
success: function(response, status) { success: function() {
location.reload(); location.reload();
}, },
}, },
@ -338,7 +360,7 @@ function unlinkBarcode(stockitem) {
/* /*
* Display dialog to check multiple stock items in to a stock location. * Display dialog to check multiple stock items in to a stock location.
*/ */
function barcodeCheckIn(location_id, options={}) { function barcodeCheckIn(location_id) {
var modal = '#modal-form'; var modal = '#modal-form';
@ -430,7 +452,9 @@ function barcodeCheckIn(location_id, options={}) {
// Called when the 'check-in' button is pressed // Called when the 'check-in' button is pressed
var data = {location: location_id}; var data = {
location: location_id
};
// Extract 'notes' field // Extract 'notes' field
data.notes = $(modal + ' #notes').val(); data.notes = $(modal + ' #notes').val();
@ -447,7 +471,7 @@ function barcodeCheckIn(location_id, options={}) {
data.items = entries; data.items = entries;
inventreePut( inventreePut(
"{% url 'api-stock-transfer' %}", '{% url "api-stock-transfer" %}',
data, data,
{ {
method: 'POST', method: 'POST',
@ -467,7 +491,7 @@ function barcodeCheckIn(location_id, options={}) {
}, },
onScan: function(response) { onScan: function(response) {
if ('stockitem' in response) { if ('stockitem' in response) {
stockitem = response.stockitem; var stockitem = response.stockitem;
var duplicate = false; var duplicate = false;
@ -478,7 +502,7 @@ function barcodeCheckIn(location_id, options={}) {
}); });
if (duplicate) { if (duplicate) {
showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', "warning"); showBarcodeMessage(modal, '{% trans "Stock Item already scanned" %}', 'warning');
} else { } else {
if (stockitem.location == location_id) { if (stockitem.location == location_id) {
@ -489,14 +513,14 @@ function barcodeCheckIn(location_id, options={}) {
// Add this stock item to the list // Add this stock item to the list
items.push(stockitem); items.push(stockitem);
showBarcodeMessage(modal, '{% trans "Added stock item" %}', "success"); showBarcodeMessage(modal, '{% trans "Added stock item" %}', 'success');
reloadTable(); reloadTable();
} }
} else { } else {
// Barcode does not match a stock item // Barcode does not match a stock item
showBarcodeMessage(modal, '{% trans "Barcode does not match Stock Item" %}', "warning"); showBarcodeMessage(modal, '{% trans "Barcode does not match Stock Item" %}', 'warning');
} }
}, },
} }
@ -525,12 +549,12 @@ function scanItemsIntoLocation(item_id_list, options={}) {
function updateLocationInfo(location) { function updateLocationInfo(location) {
var div = $(modal + ' #header-div'); var div = $(modal + ' #header-div');
if (stock_location && stock_location.pk) { if (location && location.pk) {
div.html(` div.html(`
<div class='alert alert-block alert-info'> <div class='alert alert-block alert-info'>
<b>{% trans "Location" %}</b></br> <b>{% trans "Location" %}</b></br>
${stock_location.name}<br> ${location.name}<br>
<i>${stock_location.description}</i> <i>${location.description}</i>
</div> </div>
`); `);
} else { } else {
@ -561,7 +585,7 @@ function scanItemsIntoLocation(item_id_list, options={}) {
items.push({ items.push({
pk: pk, pk: pk,
}); });
}) });
var data = { var data = {
location: stock_location.pk, location: stock_location.pk,
@ -587,7 +611,7 @@ function scanItemsIntoLocation(item_id_list, options={}) {
} }
} }
} }
) );
}, },
onScan: function(response) { onScan: function(response) {
updateLocationInfo(null); updateLocationInfo(null);
@ -603,10 +627,10 @@ function scanItemsIntoLocation(item_id_list, options={}) {
showBarcodeMessage( showBarcodeMessage(
modal, modal,
'{% trans "Barcode does not match a valid location" %}', '{% trans "Barcode does not match a valid location" %}',
"warning", 'warning',
); );
} }
} }
} }
) );
} }

View File

@ -1,5 +1,25 @@
{% load i18n %} {% load i18n %}
/* globals
constructForm,
imageHoverIcon,
inventreeGet,
inventreePut,
launchModalForm,
loadTableFilters,
makePartIcons,
renderLink,
setupFilterList,
yesNoLabel,
*/
/* exported
newPartFromBomWizard,
loadBomTable,
removeRowFromBomWizard,
removeColFromBomWizard,
*/
/* BOM management functions. /* BOM management functions.
* Requires follwing files to be loaded first: * Requires follwing files to be loaded first:
* - api.js * - api.js
@ -28,7 +48,7 @@ function bomItemFields() {
} }
function reloadBomTable(table, options) { function reloadBomTable(table) {
table.bootstrapTable('refresh'); table.bootstrapTable('refresh');
} }
@ -126,7 +146,7 @@ function loadBomTable(table, options) {
var params = { var params = {
part: options.parent_id, part: options.parent_id,
ordering: 'name', ordering: 'name',
} };
if (options.part_detail) { if (options.part_detail) {
params.part_detail = true; params.part_detail = true;
@ -157,7 +177,7 @@ function loadBomTable(table, options) {
checkbox: true, checkbox: true,
visible: true, visible: true,
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
// Disable checkbox if the row is defined for a *different* part! // Disable checkbox if the row is defined for a *different* part!
if (row.part != options.parent_id) { if (row.part != options.parent_id) {
return { return {
@ -182,7 +202,7 @@ function loadBomTable(table, options) {
field: 'sub_part', field: 'sub_part',
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/part/${row.sub_part}/`; var url = `/part/${row.sub_part}/`;
var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url); var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url);
@ -225,7 +245,7 @@ function loadBomTable(table, options) {
title: '{% trans "Quantity" %}', title: '{% trans "Quantity" %}',
searchable: false, searchable: false,
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var text = value; var text = value;
// The 'value' is a text string with (potentially) multiple trailing zeros // The 'value' is a text string with (potentially) multiple trailing zeros
@ -244,13 +264,12 @@ function loadBomTable(table, options) {
}, },
}); });
cols.push( cols.push({
{
field: 'sub_part_detail.stock', field: 'sub_part_detail.stock',
title: '{% trans "Available" %}', title: '{% trans "Available" %}',
searchable: false, searchable: false,
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/part/${row.sub_part_detail.pk}/?display=stock`; var url = `/part/${row.sub_part_detail.pk}/?display=stock`;
var text = value; var text = value;
@ -263,32 +282,29 @@ function loadBomTable(table, options) {
} }
}); });
cols.push( cols.push({
{
field: 'purchase_price_range', field: 'purchase_price_range',
title: '{% trans "Purchase Price Range" %}', title: '{% trans "Purchase Price Range" %}',
searchable: false, searchable: false,
sortable: true, sortable: true,
}); });
cols.push( cols.push({
{
field: 'purchase_price_avg', field: 'purchase_price_avg',
title: '{% trans "Purchase Price Average" %}', title: '{% trans "Purchase Price Average" %}',
searchable: false, searchable: false,
sortable: true, sortable: true,
}); });
cols.push( cols.push({
{
field: 'price_range', field: 'price_range',
title: '{% trans "Supplier Cost" %}', title: '{% trans "Supplier Cost" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value) {
if (value) { if (value) {
return value; return value;
} else { } else {
return "<span class='warning-msg'>{% trans 'No supplier pricing available' %}</span>"; return `<span class='warning-msg'>{% trans 'No supplier pricing available' %}</span>`;
} }
} }
}); });
@ -308,13 +324,13 @@ function loadBomTable(table, options) {
formatter: function(value) { formatter: function(value) {
return yesNoLabel(value); return yesNoLabel(value);
} }
}) });
cols.push({ cols.push({
field: 'inherited', field: 'inherited',
title: '{% trans "Inherited" %}', title: '{% trans "Inherited" %}',
searchable: false, searchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
// This BOM item *is* inheritable, but is defined for this BOM // This BOM item *is* inheritable, but is defined for this BOM
if (!row.inherited) { if (!row.inherited) {
return yesNoLabel(false); return yesNoLabel(false);
@ -332,9 +348,9 @@ function loadBomTable(table, options) {
cols.push( cols.push(
{ {
'field': 'can_build', field: 'can_build',
'title': '{% trans "Can Build" %}', title: '{% trans "Can Build" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var can_build = 0; var can_build = 0;
if (row.quantity > 0) { if (row.quantity > 0) {
@ -360,7 +376,7 @@ function loadBomTable(table, options) {
}, },
sortable: true, sortable: true,
} }
) );
// Part notes // Part notes
cols.push( cols.push(
@ -379,7 +395,7 @@ function loadBomTable(table, options) {
switchable: false, switchable: false,
field: 'pk', field: 'pk',
visible: true, visible: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (row.part == options.parent_id) { if (row.part == options.parent_id) {
@ -391,7 +407,7 @@ function loadBomTable(table, options) {
var bDelt = `<button title='{% trans "Delete BOM Item" %}' class='bom-delete-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-trash-alt icon-red'></span></button>`; var bDelt = `<button title='{% trans "Delete BOM Item" %}' class='bom-delete-button btn btn-default btn-glyph' type='button' pk='${row.pk}'><span class='fas fa-trash-alt icon-red'></span></button>`;
var html = "<div class='btn-group' role='group'>"; var html = `<div class='btn-group' role='group'>`;
html += bEdit; html += bEdit;
html += bDelt; html += bDelt;
@ -402,7 +418,7 @@ function loadBomTable(table, options) {
html += bValid; html += bValid;
} }
html += "</div>"; html += `</div>`;
return html; return html;
} else { } else {
@ -434,7 +450,7 @@ function loadBomTable(table, options) {
response[idx].parentId = bom_pk; response[idx].parentId = bom_pk;
if (response[idx].sub_part_detail.assembly) { if (response[idx].sub_part_detail.assembly) {
requestSubItems(response[idx].pk, response[idx].sub_part) requestSubItems(response[idx].pk, response[idx].sub_part);
} }
} }
@ -446,7 +462,7 @@ function loadBomTable(table, options) {
console.log('Error requesting BOM for part=' + part_pk); console.log('Error requesting BOM for part=' + part_pk);
} }
} }
) );
} }
table.inventreeTable({ table.inventreeTable({
@ -459,7 +475,7 @@ function loadBomTable(table, options) {
name: 'bom', name: 'bom',
sortable: true, sortable: true,
search: true, search: true,
rowStyle: function(row, index) { rowStyle: function(row) {
var classes = []; var classes = [];
@ -532,7 +548,6 @@ function loadBomTable(table, options) {
table.on('click', '.bom-delete-button', function() { table.on('click', '.bom-delete-button', function() {
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
var url = `/part/bom/${pk}/delete/`;
constructForm(`/api/bom/${pk}/`, { constructForm(`/api/bom/${pk}/`, {
method: 'DELETE', method: 'DELETE',
@ -546,7 +561,6 @@ function loadBomTable(table, options) {
table.on('click', '.bom-edit-button', function() { table.on('click', '.bom-edit-button', function() {
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
var url = `/part/bom/${pk}/edit/`;
var fields = bomItemFields(); var fields = bomItemFields();

View File

@ -1,6 +1,33 @@
{% load i18n %} {% load i18n %}
{% load inventree_extras %} {% load inventree_extras %}
/* globals
buildStatusDisplay,
constructForm,
getFieldByName,
global_settings,
imageHoverIcon,
inventreeGet,
launchModalForm,
linkButtonsToSelection,
loadTableFilters,
makeIconBadge,
makeIconButton,
makePartIcons,
makeProgressBar,
renderLink,
setupFilterList,
*/
/* exported
editBuildOrder,
loadAllocationTable,
loadBuildOrderAllocationTable,
loadBuildOutputAllocationTable,
loadBuildPartsTable,
loadBuildTable,
*/
function buildFormFields() { function buildFormFields() {
return { return {
@ -32,7 +59,7 @@ function buildFormFields() {
} }
function editBuildOrder(pk, options={}) { function editBuildOrder(pk) {
var fields = buildFormFields(); var fields = buildFormFields();
@ -76,10 +103,10 @@ function makeBuildOutputActionButtons(output, buildInfo, lines) {
var buildId = buildInfo.pk; var buildId = buildInfo.pk;
var outputId = 'untracked';
if (output) { if (output) {
outputId = output.pk; outputId = output.pk;
} else {
outputId = 'untracked';
} }
var panel = `#allocation-panel-${outputId}`; var panel = `#allocation-panel-${outputId}`;
@ -202,13 +229,13 @@ function loadBuildOrderAllocationTable(table, options={}) {
options.params['build_detail'] = true; options.params['build_detail'] = true;
options.params['location_detail'] = true; options.params['location_detail'] = true;
var filters = loadTableFilters("buildorderallocation"); var filters = loadTableFilters('buildorderallocation');
for (var key in options.params) { for (var key in options.params) {
filters[key] = options.params[key]; filters[key] = options.params[key];
} }
setupFilterList("buildorderallocation", $(table)); setupFilterList('buildorderallocation', $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: '{% url "api-build-item-list" %}', url: '{% url "api-build-item-list" %}',
@ -219,7 +246,7 @@ function loadBuildOrderAllocationTable(table, options={}) {
paginationVAlign: 'bottom', paginationVAlign: 'bottom',
original: options.params, original: options.params,
formatNoMatches: function() { formatNoMatches: function() {
return '{% trans "No build order allocations found" %}' return '{% trans "No build order allocations found" %}';
}, },
columns: [ columns: [
{ {
@ -345,13 +372,13 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
// Register button callbacks once table data are loaded // Register button callbacks once table data are loaded
// Callback for 'allocate' button // Callback for 'allocate' button
$(table).find(".button-add").click(function() { $(table).find('.button-add').click(function() {
// Primary key of the 'sub_part' // Primary key of the 'sub_part'
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
// Launch form to allocate new stock against this output // Launch form to allocate new stock against this output
launchModalForm("{% url 'build-item-create' %}", { launchModalForm('{% url "build-item-create" %}', {
success: reloadTable, success: reloadTable,
data: { data: {
part: pk, part: pk,
@ -391,7 +418,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
} }
} }
} }
) );
} }
} }
] ]
@ -400,10 +427,8 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
// Callback for 'buy' button // Callback for 'buy' button
$(table).find('.button-buy').click(function() { $(table).find('.button-buy').click(function() {
var pk = $(this).attr('pk');
var idx = $(this).closest('tr').attr('data-index'); var pk = $(this).attr('pk');
var row = $(table).bootstrapTable('getData')[idx];
launchModalForm('{% url "order-parts" %}', { launchModalForm('{% url "order-parts" %}', {
data: { data: {
@ -447,7 +472,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
// Load table of BOM items // Load table of BOM items
$(table).inventreeTable({ $(table).inventreeTable({
url: "{% url 'api-bom-list' %}", url: '{% url "api-bom-list" %}',
queryParams: { queryParams: {
part: partId, part: partId,
sub_part_detail: true, sub_part_detail: true,
@ -467,7 +492,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
build: buildId, build: buildId,
part_detail: true, part_detail: true,
location_detail: true, location_detail: true,
} };
if (output) { if (output) {
params.sub_part_trackable = true; params.sub_part_trackable = true;
@ -496,7 +521,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
var key = parseInt(part); var key = parseInt(part);
if (!(key in allocations)) { if (!(key in allocations)) {
allocations[key] = new Array(); allocations[key] = [];
} }
allocations[key].push(item); allocations[key].push(item);
@ -573,8 +598,6 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
element.html(html); element.html(html);
var lineItem = row;
var subTable = $(`#${subTableId}`); var subTable = $(`#${subTableId}`);
subTable.bootstrapTable({ subTable.bootstrapTable({
@ -595,7 +618,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
width: '50%', width: '50%',
field: 'quantity', field: 'quantity',
title: '{% trans "Assigned Stock" %}', title: '{% trans "Assigned Stock" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var text = ''; var text = '';
var url = ''; var url = '';
@ -618,7 +641,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
{ {
field: 'location', field: 'location',
title: '{% trans "Location" %}', title: '{% trans "Location" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (row.stock_item_detail.location) { if (row.stock_item_detail.location) {
var text = row.stock_item_detail.location_name; var text = row.stock_item_detail.location_name;
var url = `/stock/location/${row.stock_item_detail.location}/`; var url = `/stock/location/${row.stock_item_detail.location}/`;
@ -631,7 +654,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
}, },
{ {
field: 'actions', field: 'actions',
formatter: function(value, row, index, field) { formatter: function(value, row) {
/* Actions available for a particular stock item allocation: /* Actions available for a particular stock item allocation:
* *
* - Edit the allocation quantity * - Edit the allocation quantity
@ -678,7 +701,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
field: 'sub_part_detail.full_name', field: 'sub_part_detail.full_name',
title: '{% trans "Required Part" %}', title: '{% trans "Required Part" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/part/${row.sub_part}/`; var url = `/part/${row.sub_part}/`;
var thumb = row.sub_part_detail.thumbnail; var thumb = row.sub_part_detail.thumbnail;
var name = row.sub_part_detail.full_name; var name = row.sub_part_detail.full_name;
@ -709,7 +732,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
field: 'allocated', field: 'allocated',
title: '{% trans "Allocated" %}', title: '{% trans "Allocated" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var allocated = 0; var allocated = 0;
if (row.allocations) { if (row.allocations) {
@ -757,7 +780,7 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
{ {
field: 'actions', field: 'actions',
title: '{% trans "Actions" %}', title: '{% trans "Actions" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
// Generate action buttons for this build output // Generate action buttons for this build output
var html = `<div class='btn-group float-right' role='group'>`; var html = `<div class='btn-group float-right' role='group'>`;
@ -804,7 +827,7 @@ function loadBuildTable(table, options) {
params['part_detail'] = true; params['part_detail'] = true;
if (!options.disableFilters) { if (!options.disableFilters) {
filters = loadTableFilters("build"); filters = loadTableFilters('build');
} }
for (var key in params) { for (var key in params) {
@ -815,7 +838,7 @@ function loadBuildTable(table, options) {
var filterTarget = options.filterTarget || null; var filterTarget = options.filterTarget || null;
setupFilterList("build", table, filterTarget); setupFilterList('build', table, filterTarget);
$(table).inventreeTable({ $(table).inventreeTable({
method: 'get', method: 'get',
@ -846,7 +869,7 @@ function loadBuildTable(table, options) {
title: '{% trans "Build" %}', title: '{% trans "Build" %}',
sortable: true, sortable: true,
switchable: true, switchable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX; var prefix = global_settings.BUILDORDER_REFERENCE_PREFIX;
@ -873,7 +896,7 @@ function loadBuildTable(table, options) {
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
sortable: true, sortable: true,
sortName: 'part__name', sortName: 'part__name',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var html = imageHoverIcon(row.part_detail.thumbnail); var html = imageHoverIcon(row.part_detail.thumbnail);
@ -887,7 +910,7 @@ function loadBuildTable(table, options) {
field: 'quantity', field: 'quantity',
title: '{% trans "Completed" %}', title: '{% trans "Completed" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
return makeProgressBar( return makeProgressBar(
row.completed, row.completed,
row.quantity, row.quantity,
@ -901,7 +924,7 @@ function loadBuildTable(table, options) {
field: 'status', field: 'status',
title: '{% trans "Status" %}', title: '{% trans "Status" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value) {
return buildStatusDisplay(value); return buildStatusDisplay(value);
}, },
}, },
@ -914,13 +937,10 @@ function loadBuildTable(table, options) {
field: 'issued_by', field: 'issued_by',
title: '{% trans "Issued by" %}', title: '{% trans "Issued by" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value) if (value) {
{
return row.issued_by_detail.username; return row.issued_by_detail.username;
} } else {
else
{
return `<i>{% trans "No user information" %}</i>`; return `<i>{% trans "No user information" %}</i>`;
} }
} }
@ -929,13 +949,10 @@ function loadBuildTable(table, options) {
field: 'responsible', field: 'responsible',
title: '{% trans "Responsible" %}', title: '{% trans "Responsible" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value) if (value) {
{
return row.responsible_detail.name; return row.responsible_detail.name;
} } else {
else
{
return '{% trans "No information" %}'; return '{% trans "No information" %}';
} }
} }
@ -968,7 +985,7 @@ function updateAllocationTotal(id, count, required) {
$('#allocation-total-'+id).html(count); $('#allocation-total-'+id).html(count);
var el = $("#allocation-panel-" + id); var el = $('#allocation-panel-' + id);
el.removeClass('part-allocation-pass part-allocation-underallocated part-allocation-overallocated'); el.removeClass('part-allocation-pass part-allocation-underallocated part-allocation-overallocated');
if (count < required) { if (count < required) {
@ -986,32 +1003,39 @@ function loadAllocationTable(table, part_id, part, url, required, button) {
table.bootstrapTable({ table.bootstrapTable({
url: url, url: url,
sortable: false, sortable: false,
formatNoMatches: function() { return '{% trans "No parts allocated for" %} ' + part; }, formatNoMatches: function() {
return '{% trans "No parts allocated for" %} ' + part;
},
columns: [ columns: [
{ {
field: 'stock_item_detail', field: 'stock_item_detail',
title: '{% trans "Stock Item" %}', title: '{% trans "Stock Item" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
return '' + parseFloat(value.quantity) + ' x ' + value.part_name + ' @ ' + value.location_name; return '' + parseFloat(value.quantity) + ' x ' + value.part_name + ' @ ' + value.location_name;
} }
}, },
{ {
field: 'stock_item_detail.quantity', field: 'stock_item_detail.quantity',
title: '{% trans "Available" %}', title: '{% trans "Available" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
return parseFloat(value); return parseFloat(value);
} }
}, },
{ {
field: 'quantity', field: 'quantity',
title: '{% trans "Allocated" %}', title: '{% trans "Allocated" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var html = parseFloat(value); var html = parseFloat(value);
var bEdit = "<button class='btn item-edit-button btn-sm' type='button' title='{% trans "Edit stock allocation" %}' url='/build/item/" + row.pk + "/edit/'><span class='fas fa-edit'></span></button>"; var bEdit = `<button class='btn item-edit-button btn-sm' type='button' title='{% trans "Edit stock allocation" %}' url='/build/item/${row.pk}/edit/'><span class='fas fa-edit'></span></button>`;
var bDel = "<button class='btn item-del-button btn-sm' type='button' title='{% trans "Delete stock allocation" %}' url='/build/item/" + row.pk + "/delete/'><span class='fas fa-trash-alt icon-red'></span></button>"; var bDel = `<button class='btn item-del-button btn-sm' type='button' title='{% trans "Delete stock allocation" %}' url='/build/item/${row.pk}/delete/'><span class='fas fa-trash-alt icon-red'></span></button>`;
html += "<div class='btn-group' style='float: right;'>" + bEdit + bDel + "</div>"; html += `
<div class='btn-group' style='float: right;'>
${bEdit}
${bDel}
</div>
`;
return html; return html;
} }
@ -1028,7 +1052,7 @@ function loadAllocationTable(table, part_id, part, url, required, button) {
}); });
}); });
table.on('load-success.bs.table', function(data) { table.on('load-success.bs.table', function() {
// Extract table data // Extract table data
var results = table.bootstrapTable('getData'); var results = table.bootstrapTable('getData');
@ -1106,9 +1130,6 @@ function loadBuildPartsTable(table, options={}) {
$(table).find('.button-buy').click(function() { $(table).find('.button-buy').click(function() {
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
var idx = $(this).closest('tr').attr('data-index');
var row = $(table).bootstrapTable('getData')[idx];
launchModalForm('{% url "order-parts" %}', { launchModalForm('{% url "order-parts" %}', {
data: { data: {
parts: [ parts: [
@ -1122,10 +1143,6 @@ function loadBuildPartsTable(table, options={}) {
$(table).find('.button-build').click(function() { $(table).find('.button-build').click(function() {
var pk = $(this).attr('pk'); var pk = $(this).attr('pk');
// Extract row data from the table
var idx = $(this).closest('tr').attr('data-index');
var row = $(table).bootstrapTable('getData')[idx];
newBuildOrder({ newBuildOrder({
part: pk, part: pk,
parent: options.build, parent: options.build,
@ -1139,7 +1156,7 @@ function loadBuildPartsTable(table, options={}) {
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
switchable: false, switchable: false,
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/part/${row.sub_part}/`; var url = `/part/${row.sub_part}/`;
var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url); var html = imageHoverIcon(row.sub_part_detail.thumbnail) + renderLink(row.sub_part_detail.full_name, url);
@ -1177,7 +1194,7 @@ function loadBuildPartsTable(table, options={}) {
switchable: false, switchable: false,
field: 'sub_part_detail.stock', field: 'sub_part_detail.stock',
title: '{% trans "Available" %}', title: '{% trans "Available" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
return makeProgressBar( return makeProgressBar(
value, value,
row.quantity * options.build_remaining, row.quantity * options.build_remaining,
@ -1201,7 +1218,7 @@ function loadBuildPartsTable(table, options={}) {
field: 'actions', field: 'actions',
title: '{% trans "Actions" %}', title: '{% trans "Actions" %}',
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
// Generate action buttons against the part // Generate action buttons against the part
var html = `<div class='btn-group float-right' role='group'>`; var html = `<div class='btn-group float-right' role='group'>`;
@ -1228,7 +1245,7 @@ function loadBuildPartsTable(table, options={}) {
sortable: true, sortable: true,
search: true, search: true,
onPostBody: setupTableCallbacks, onPostBody: setupTableCallbacks,
rowStyle: function(row, index) { rowStyle: function(row) {
var classes = []; var classes = [];
// Shade rows differently if they are for different parent parts // Shade rows differently if they are for different parent parts

View File

@ -1,6 +1,33 @@
{% load i18n %} {% load i18n %}
/* globals
constructForm,
imageHoverIcon,
inventreeDelete,
loadTableFilters,
makeIconButton,
renderLink,
setupFilterList,
showQuestionDialog,
*/
/* exported
createCompany,
createManufacturerPart,
createSupplierPart,
deleteManufacturerParts,
editCompany,
loadCompanyTable,
loadManufacturerPartTable,
loadManufacturerPartParameterTable,
loadSupplierPartTable,
*/
/**
* Construct a set of form fields for creating / editing a ManufacturerPart
* @returns
*/
function manufacturerPartFields() { function manufacturerPartFields() {
return { return {
@ -17,6 +44,10 @@ function manufacturerPartFields() {
} }
/**
* Launches a form to create a new ManufacturerPart
* @param {object} options
*/
function createManufacturerPart(options={}) { function createManufacturerPart(options={}) {
var fields = manufacturerPartFields(); var fields = manufacturerPartFields();
@ -32,14 +63,14 @@ function createManufacturerPart(options={}) {
fields.manufacturer.secondary = { fields.manufacturer.secondary = {
title: '{% trans "Add Manufacturer" %}', title: '{% trans "Add Manufacturer" %}',
fields: function(data) { fields: function() {
var company_fields = companyFormFields(); var company_fields = companyFormFields();
company_fields.is_manufacturer.value = true; company_fields.is_manufacturer.value = true;
return company_fields; return company_fields;
} }
} };
constructForm('{% url "api-manufacturer-part-list" %}', { constructForm('{% url "api-manufacturer-part-list" %}', {
fields: fields, fields: fields,
@ -50,6 +81,11 @@ function createManufacturerPart(options={}) {
} }
/**
* Launches a form to edit a ManufacturerPart
* @param {integer} part - ID of a ManufacturerPart
* @param {object} options
*/
function editManufacturerPart(part, options={}) { function editManufacturerPart(part, options={}) {
var url = `/api/company/part/manufacturer/${part}/`; var url = `/api/company/part/manufacturer/${part}/`;
@ -126,7 +162,7 @@ function createSupplierPart(options={}) {
// Add a secondary modal for the supplier // Add a secondary modal for the supplier
fields.supplier.secondary = { fields.supplier.secondary = {
title: '{% trans "Add Supplier" %}', title: '{% trans "Add Supplier" %}',
fields: function(data) { fields: function() {
var company_fields = companyFormFields(); var company_fields = companyFormFields();
company_fields.is_supplier.value = true; company_fields.is_supplier.value = true;
@ -185,7 +221,7 @@ function deleteSupplierPart(part, options={}) {
// Returns a default form-set for creating / editing a Company object // Returns a default form-set for creating / editing a Company object
function companyFormFields(options={}) { function companyFormFields() {
return { return {
name: {}, name: {},
@ -228,7 +264,7 @@ function editCompany(pk, options={}) {
title: '{% trans "Edit Company" %}', title: '{% trans "Edit Company" %}',
} }
); );
}; }
/* /*
* Launches a form to create a new company. * Launches a form to create a new company.
@ -265,13 +301,13 @@ function loadCompanyTable(table, url, options={}) {
// Query parameters // Query parameters
var params = options.params || {}; var params = options.params || {};
var filters = loadTableFilters("company"); var filters = loadTableFilters('company');
for (var key in params) { for (var key in params) {
filters[key] = params[key]; filters[key] = params[key];
} }
setupFilterList("company", $(table)); setupFilterList('company', $(table));
var columns = [ var columns = [
{ {
@ -285,7 +321,7 @@ function loadCompanyTable(table, url, options={}) {
title: '{% trans "Company" %}', title: '{% trans "Company" %}',
sortable: true, sortable: true,
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var html = imageHoverIcon(row.image) + renderLink(value, row.url); var html = imageHoverIcon(row.image) + renderLink(value, row.url);
if (row.is_customer) { if (row.is_customer) {
@ -310,7 +346,7 @@ function loadCompanyTable(table, url, options={}) {
{ {
field: 'website', field: 'website',
title: '{% trans "Website" %}', title: '{% trans "Website" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
if (value) { if (value) {
return renderLink(value, value); return renderLink(value, value);
} }
@ -345,7 +381,9 @@ function loadCompanyTable(table, url, options={}) {
queryParams: filters, queryParams: filters,
groupBy: false, groupBy: false,
sidePagination: 'server', sidePagination: 'server',
formatNoMatches: function() { return "{% trans "No company information found" %}"; }, formatNoMatches: function() {
return '{% trans "No company information found" %}';
},
showColumns: true, showColumns: true,
name: options.pagetype || 'company', name: options.pagetype || 'company',
columns: columns, columns: columns,
@ -401,7 +439,7 @@ function deleteManufacturerParts(selections, options={}) {
if (options.onSuccess) { if (options.onSuccess) {
options.onSuccess(); options.onSuccess();
} }
}) });
} }
} }
); );
@ -418,13 +456,13 @@ function loadManufacturerPartTable(table, url, options) {
var params = options.params || {}; var params = options.params || {};
// Load filters // Load filters
var filters = loadTableFilters("manufacturer-part"); var filters = loadTableFilters('manufacturer-part');
for (var key in params) { for (var key in params) {
filters[key] = params[key]; filters[key] = params[key];
} }
setupFilterList("manufacturer-part", $(table)); setupFilterList('manufacturer-part', $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: url, url: url,
@ -433,7 +471,9 @@ function loadManufacturerPartTable(table, url, options) {
queryParams: filters, queryParams: filters,
name: 'manufacturerparts', name: 'manufacturerparts',
groupBy: false, groupBy: false,
formatNoMatches: function() { return '{% trans "No manufacturer parts found" %}'; }, formatNoMatches: function() {
return '{% trans "No manufacturer parts found" %}';
},
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@ -445,7 +485,7 @@ function loadManufacturerPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'part_detail.full_name', field: 'part_detail.full_name',
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/part/${row.part}/`; var url = `/part/${row.part}/`;
@ -470,7 +510,7 @@ function loadManufacturerPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'manufacturer', field: 'manufacturer',
title: '{% trans "Manufacturer" %}', title: '{% trans "Manufacturer" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value && row.manufacturer_detail) { if (value && row.manufacturer_detail) {
var name = row.manufacturer_detail.name; var name = row.manufacturer_detail.name;
var url = `/company/${value}/`; var url = `/company/${value}/`;
@ -478,7 +518,7 @@ function loadManufacturerPartTable(table, url, options) {
return html; return html;
} else { } else {
return "-"; return '-';
} }
} }
}, },
@ -486,14 +526,14 @@ function loadManufacturerPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'MPN', field: 'MPN',
title: '{% trans "MPN" %}', title: '{% trans "MPN" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
return renderLink(value, `/manufacturer-part/${row.pk}/`); return renderLink(value, `/manufacturer-part/${row.pk}/`);
} }
}, },
{ {
field: 'link', field: 'link',
title: '{% trans "Link" %}', title: '{% trans "Link" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
if (value) { if (value) {
return renderLink(value, value); return renderLink(value, value);
} else { } else {
@ -537,7 +577,8 @@ function loadManufacturerPartTable(table, url, options) {
onSuccess: function() { onSuccess: function() {
$(table).bootstrapTable('refresh'); $(table).bootstrapTable('refresh');
} }
}); }
);
}); });
$(table).find('.button-manufacturer-part-delete').click(function() { $(table).find('.button-manufacturer-part-delete').click(function() {
@ -549,8 +590,9 @@ function loadManufacturerPartTable(table, url, options) {
onSuccess: function() { onSuccess: function() {
$(table).bootstrapTable('refresh'); $(table).bootstrapTable('refresh');
} }
}
);
}); });
})
} }
}); });
} }
@ -564,7 +606,7 @@ function loadManufacturerPartParameterTable(table, url, options) {
var params = options.params || {}; var params = options.params || {};
// Load filters // Load filters
var filters = loadTableFilters("manufacturer-part-parameters"); var filters = loadTableFilters('manufacturer-part-parameters');
// Overwrite explicit parameters // Overwrite explicit parameters
for (var key in params) { for (var key in params) {
@ -580,7 +622,9 @@ function loadManufacturerPartParameterTable(table, url, options) {
queryParams: filters, queryParams: filters,
name: 'manufacturerpartparameters', name: 'manufacturerpartparameters',
groupBy: false, groupBy: false,
formatNoMatches: function() { return '{% trans "No parameters found" %}'; }, formatNoMatches: function() {
return '{% trans "No parameters found" %}';
},
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@ -668,13 +712,13 @@ function loadSupplierPartTable(table, url, options) {
var params = options.params || {}; var params = options.params || {};
// Load filters // Load filters
var filters = loadTableFilters("supplier-part"); var filters = loadTableFilters('supplier-part');
for (var key in params) { for (var key in params) {
filters[key] = params[key]; filters[key] = params[key];
} }
setupFilterList("supplier-part", $(table)); setupFilterList('supplier-part', $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: url, url: url,
@ -683,7 +727,9 @@ function loadSupplierPartTable(table, url, options) {
queryParams: filters, queryParams: filters,
name: 'supplierparts', name: 'supplierparts',
groupBy: false, groupBy: false,
formatNoMatches: function() { return '{% trans "No supplier parts found" %}'; }, formatNoMatches: function() {
return '{% trans "No supplier parts found" %}';
},
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@ -695,7 +741,7 @@ function loadSupplierPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'part_detail.full_name', field: 'part_detail.full_name',
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/part/${row.part}/`; var url = `/part/${row.part}/`;
@ -720,7 +766,7 @@ function loadSupplierPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'supplier', field: 'supplier',
title: '{% trans "Supplier" %}', title: '{% trans "Supplier" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value) { if (value) {
var name = row.supplier_detail.name; var name = row.supplier_detail.name;
var url = `/company/${value}/`; var url = `/company/${value}/`;
@ -728,7 +774,7 @@ function loadSupplierPartTable(table, url, options) {
return html; return html;
} else { } else {
return "-"; return '-';
} }
}, },
}, },
@ -736,7 +782,7 @@ function loadSupplierPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'SKU', field: 'SKU',
title: '{% trans "Supplier Part" %}', title: '{% trans "Supplier Part" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
return renderLink(value, `/supplier-part/${row.pk}/`); return renderLink(value, `/supplier-part/${row.pk}/`);
} }
}, },
@ -746,7 +792,7 @@ function loadSupplierPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'manufacturer_detail.name', field: 'manufacturer_detail.name',
title: '{% trans "Manufacturer" %}', title: '{% trans "Manufacturer" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value && row.manufacturer_detail) { if (value && row.manufacturer_detail) {
var name = value; var name = value;
var url = `/company/${row.manufacturer_detail.pk}/`; var url = `/company/${row.manufacturer_detail.pk}/`;
@ -754,7 +800,7 @@ function loadSupplierPartTable(table, url, options) {
return html; return html;
} else { } else {
return "-"; return '-';
} }
} }
}, },
@ -764,18 +810,18 @@ function loadSupplierPartTable(table, url, options) {
sortable: true, sortable: true,
field: 'manufacturer_part_detail.MPN', field: 'manufacturer_part_detail.MPN',
title: '{% trans "MPN" %}', title: '{% trans "MPN" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value && row.manufacturer_part) { if (value && row.manufacturer_part) {
return renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`); return renderLink(value, `/manufacturer-part/${row.manufacturer_part}/`);
} else { } else {
return "-"; return '-';
} }
} }
}, },
{ {
field: 'link', field: 'link',
title: '{% trans "Link" %}', title: '{% trans "Link" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
if (value) { if (value) {
return renderLink(value, value); return renderLink(value, value);
} else { } else {
@ -828,7 +874,8 @@ function loadSupplierPartTable(table, url, options) {
onSuccess: function() { onSuccess: function() {
$(table).bootstrapTable('refresh'); $(table).bootstrapTable('refresh');
} }
}); }
);
}); });
$(table).find('.button-supplier-part-delete').click(function() { $(table).find('.button-supplier-part-delete').click(function() {
@ -840,8 +887,9 @@ function loadSupplierPartTable(table, url, options) {
onSuccess: function() { onSuccess: function() {
$(table).bootstrapTable('refresh'); $(table).bootstrapTable('refresh');
} }
}
);
}); });
})
} }
}); });
} }

View File

@ -1,5 +1,16 @@
{% load i18n %} {% load i18n %}
/* globals
getAvailableTableFilters,
inventreeLoad,
inventreeSave,
reloadTableFilters,
*/
/* exported
setupFilterList,
*/
/** /**
* Code for managing query filters / table options. * Code for managing query filters / table options.
* *
@ -16,12 +27,12 @@
function defaultFilters() { function defaultFilters() {
return { return {
stock: "cascade=1&in_stock=1", stock: 'cascade=1&in_stock=1',
build: "", build: '',
parts: "cascade=1", parts: 'cascade=1',
company: "", company: '',
salesorder: "", salesorder: '',
purchaseorder: "", purchaseorder: '',
}; };
} }
@ -34,7 +45,7 @@ function defaultFilters() {
*/ */
function loadTableFilters(tableKey) { function loadTableFilters(tableKey) {
var lookup = "table-filters-" + tableKey.toLowerCase(); var lookup = 'table-filters-' + tableKey.toLowerCase();
var defaults = defaultFilters()[tableKey] || ''; var defaults = defaultFilters()[tableKey] || '';
@ -42,7 +53,7 @@ function loadTableFilters(tableKey) {
var filters = {}; var filters = {};
filterstring.split("&").forEach(function(item, index) { filterstring.split('&').forEach(function(item) {
item = item.trim(); item = item.trim();
if (item.length > 0) { if (item.length > 0) {
@ -67,7 +78,7 @@ function loadTableFilters(tableKey) {
* @param {*} filters - object of string:string pairs * @param {*} filters - object of string:string pairs
*/ */
function saveTableFilters(tableKey, filters) { function saveTableFilters(tableKey, filters) {
var lookup = "table-filters-" + tableKey.toLowerCase(); var lookup = 'table-filters-' + tableKey.toLowerCase();
var strings = []; var strings = [];
@ -190,7 +201,7 @@ function generateAvailableFilterList(tableKey) {
var html = `<select class='form-control filter-input' id='${id}' name='tag'>`; var html = `<select class='form-control filter-input' id='${id}' name='tag'>`;
html += "<option value=''>{% trans 'Select filter' %}</option>"; html += `<option value=''>{% trans 'Select filter' %}</option>`;
for (var opt in remaining) { for (var opt in remaining) {
var title = getFilterTitle(tableKey, opt); var title = getFilterTitle(tableKey, opt);
@ -227,7 +238,7 @@ function generateFilterInput(tableKey, filterKey) {
html = `<select class='form-control filter-input' id='${id}' name='value'>`; html = `<select class='form-control filter-input' id='${id}' name='value'>`;
for (var key in options) { for (var key in options) {
option = options[key]; var option = options[key];
html += `<option value='${key}'>${option.value}</option>`; html += `<option value='${key}'>${option.value}</option>`;
} }
@ -295,15 +306,11 @@ function setupFilterList(tableKey, table, target) {
var html = ''; var html = '';
//`<div class='filter-input'>`;
html += generateAvailableFilterList(tableKey); html += generateAvailableFilterList(tableKey);
html += generateFilterInput(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-default filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
//html += '</div>';
element.append(html); element.append(html);
// Add a callback for when the filter tag selection is changed // Add a callback for when the filter tag selection is changed
@ -346,7 +353,7 @@ function setupFilterList(tableKey, table, target) {
}); });
// Add callback for deleting each filter // Add callback for deleting each filter
element.find(".close").click(function(event) { element.find('.close').click(function() {
var me = $(this); var me = $(this);
var filter = me.attr(`filter-tag-${tableKey}`); var filter = me.attr(`filter-tag-${tableKey}`);
@ -372,15 +379,6 @@ function getFilterTitle(tableKey, filterKey) {
} }
/**
* Return the pretty description for the given table and filter selection
*/
function getFilterDescription(tableKey, filterKey) {
var settings = getFilterSettings(tableKey, filterKey);
return settings.title;
}
/* /*
* Return a description for the given table and filter selection. * Return a description for the given table and filter selection.
*/ */

View File

@ -1,6 +1,34 @@
{% load i18n %} {% load i18n %}
{% load inventree_extras %} {% load inventree_extras %}
/* globals
attachToggle,
createNewModal,
inventreeFormDataUpload,
inventreeGet,
inventreePut,
modalEnable,
modalShowSubmitButton,
renderBuild,
renderCompany,
renderManufacturerPart,
renderOwner,
renderPart,
renderPartCategory,
renderPartParameterTemplate,
renderStockItem,
renderStockLocation,
renderSupplierPart,
renderUser,
showAlertDialog,
showAlertOrCache,
showApiError,
*/
/* exported
setFormGroupVisibility
*/
/** /**
* *
* This file contains code for rendering (and managing) HTML forms * This file contains code for rendering (and managing) HTML forms
@ -81,7 +109,7 @@ function canDelete(OPTIONS) {
* Get the API endpoint options at the provided URL, * Get the API endpoint options at the provided URL,
* using a HTTP options request. * using a HTTP options request.
*/ */
function getApiEndpointOptions(url, callback, options) { function getApiEndpointOptions(url, callback) {
// Return the ajax request object // Return the ajax request object
$.ajax({ $.ajax({
@ -93,7 +121,7 @@ function getApiEndpointOptions(url, callback, options) {
json: 'application/json', json: 'application/json',
}, },
success: callback, success: callback,
error: function(request, status, error) { error: function() {
// TODO: Handle error // TODO: Handle error
console.log(`ERROR in getApiEndpointOptions at '${url}'`); console.log(`ERROR in getApiEndpointOptions at '${url}'`);
} }
@ -172,7 +200,7 @@ function constructChangeForm(fields, options) {
constructFormBody(fields, options); constructFormBody(fields, options);
}, },
error: function(request, status, error) { error: function() {
// TODO: Handle error here // TODO: Handle error here
console.log(`ERROR in constructChangeForm at '${options.url}'`); console.log(`ERROR in constructChangeForm at '${options.url}'`);
} }
@ -211,7 +239,7 @@ function constructDeleteForm(fields, options) {
constructFormBody(fields, options); constructFormBody(fields, options);
}, },
error: function(request, status, error) { error: function() {
// TODO: Handle error here // TODO: Handle error here
console.log(`ERROR in constructDeleteForm at '${options.url}`); console.log(`ERROR in constructDeleteForm at '${options.url}`);
} }
@ -427,9 +455,9 @@ function constructFormBody(fields, options) {
for (var idx = 0; idx < field_names.length; idx++) { for (var idx = 0; idx < field_names.length; idx++) {
var name = field_names[idx]; var field_name = field_names[idx];
var field = fields[name]; var field = fields[field_name];
switch (field.type) { switch (field.type) {
// Skip field types which are simply not supported // Skip field types which are simply not supported
@ -439,7 +467,7 @@ function constructFormBody(fields, options) {
break; break;
} }
html += constructField(name, field, options); html += constructField(field_name, field, options);
} }
if (options.current_group) { if (options.current_group) {
@ -640,13 +668,13 @@ function submitFormData(fields, options) {
data, data,
{ {
method: options.method, method: options.method,
success: function(response, status) { success: function(response) {
handleFormSuccess(response, options); handleFormSuccess(response, options);
}, },
error: function(xhr, status, thrownError) { error: function(xhr) {
switch (xhr.status) { switch (xhr.status) {
case 400: // Bad request case 400:
handleFormErrors(xhr.responseJSON, fields, options); handleFormErrors(xhr.responseJSON, fields, options);
break; break;
default: default:
@ -675,7 +703,9 @@ function updateFieldValues(fields, options) {
var field = fields[name] || null; var field = fields[name] || null;
if (field == null) { continue; } if (field == null) {
continue;
}
var value = field.value; var value = field.value;
@ -683,7 +713,9 @@ function updateFieldValues(fields, options) {
value = field.default; value = field.default;
} }
if (value == null) { continue; } if (value == null) {
continue;
}
updateFieldValue(name, value, field, options); updateFieldValue(name, value, field, options);
} }
@ -735,7 +767,7 @@ function getFormFieldValue(name, field, options) {
switch (field.type) { switch (field.type) {
case 'boolean': case 'boolean':
value = el.is(":checked"); value = el.is(':checked');
break; break;
case 'date': case 'date':
case 'datetime': case 'datetime':
@ -776,19 +808,19 @@ function handleFormSuccess(response, options) {
// Display any messages // Display any messages
if (response && response.success) { if (response && response.success) {
showAlertOrCache("alert-success", response.success, cache); showAlertOrCache('alert-success', response.success, cache);
} }
if (response && response.info) { if (response && response.info) {
showAlertOrCache("alert-info", response.info, cache); showAlertOrCache('alert-info', response.info, cache);
} }
if (response && response.warning) { if (response && response.warning) {
showAlertOrCache("alert-warning", response.warning, cache); showAlertOrCache('alert-warning', response.warning, cache);
} }
if (response && response.danger) { if (response && response.danger) {
showAlertOrCache("alert-danger", response.danger, cache); showAlertOrCache('alert-danger', response.danger, cache);
} }
if (options.onSuccess) { if (options.onSuccess) {
@ -872,7 +904,7 @@ function handleFormErrors(errors, fields, options) {
var first_error_field = null; var first_error_field = null;
for (field_name in errors) { for (var field_name in errors) {
// Add the 'has-error' class // Add the 'has-error' class
$(options.modal).find(`#div_id_${field_name}`).addClass('has-error'); $(options.modal).find(`#div_id_${field_name}`).addClass('has-error');
@ -886,16 +918,16 @@ function handleFormErrors(errors, fields, options) {
} }
// Add an entry for each returned error message // Add an entry for each returned error message
for (var idx = field_errors.length-1; idx >= 0; idx--) { for (var ii = field_errors.length-1; ii >= 0; ii--) {
var error_text = field_errors[idx]; var error_text = field_errors[ii];
var html = ` var error_html = `
<span id='error_${idx+1}_id_${field_name}' class='help-block form-error-message'> <span id='error_${ii+1}_id_${field_name}' class='help-block form-error-message'>
<strong>${error_text}</strong> <strong>${error_text}</strong>
</span>`; </span>`;
field_dom.append(html); field_dom.append(error_html);
} }
} }
@ -1009,9 +1041,9 @@ function initializeGroups(fields, options) {
var group_options = options.groups[group]; var group_options = options.groups[group];
if (group_options.collapsed) { if (group_options.collapsed) {
$(modal).find(`#form-panel-content-${group}`).collapse("hide"); $(modal).find(`#form-panel-content-${group}`).collapse('hide');
} else { } else {
$(modal).find(`#form-panel-content-${group}`).collapse("show"); $(modal).find(`#form-panel-content-${group}`).collapse('show');
} }
if (group_options.hidden) { if (group_options.hidden) {
@ -1058,6 +1090,8 @@ function initializeRelatedFields(fields, options) {
case 'choice': case 'choice':
initializeChoiceField(field, fields, options); initializeChoiceField(field, fields, options);
break; break;
default:
break;
} }
} }
} }
@ -1096,14 +1130,14 @@ function addSecondaryModal(field, fields, options) {
if (secondary.fields instanceof Function) { if (secondary.fields instanceof Function) {
// Extract form values at time of button press // Extract form values at time of button press
var data = extractFormData(fields, options) var data = extractFormData(fields, options);
secondary.fields = secondary.fields(data); secondary.fields = secondary.fields(data);
} }
// If no onSuccess function is defined, provide a default one // If no onSuccess function is defined, provide a default one
if (!secondary.onSuccess) { if (!secondary.onSuccess) {
secondary.onSuccess = function(data, opts) { secondary.onSuccess = function(data) {
// Force refresh from the API, to get full detail // Force refresh from the API, to get full detail
inventreeGet(`${url}${data.pk}/`, {}, { inventreeGet(`${url}${data.pk}/`, {}, {
@ -1169,6 +1203,8 @@ function initializeRelatedField(field, fields, options) {
cache: true, cache: true,
data: function(params) { data: function(params) {
var offset = 0;
if (!params.page) { if (!params.page) {
offset = 0; offset = 0;
} else { } else {
@ -1222,7 +1258,7 @@ function initializeRelatedField(field, fields, options) {
return results; return results;
}, },
}, },
templateResult: function(item, container) { templateResult: function(item) {
// Extract 'instance' data passed through from an initial value // Extract 'instance' data passed through from an initial value
// Or, use the raw 'item' data as a backup // Or, use the raw 'item' data as a backup
@ -1247,7 +1283,7 @@ function initializeRelatedField(field, fields, options) {
return `${name} - ${item.id}`; return `${name} - ${item.id}`;
} }
}, },
templateSelection: function(item, container) { templateSelection: function(item) {
// Extract 'instance' data passed through from an initial value // Extract 'instance' data passed through from an initial value
// Or, use the raw 'item' data as a backup // Or, use the raw 'item' data as a backup
@ -1259,7 +1295,6 @@ function initializeRelatedField(field, fields, options) {
if (!data.pk) { if (!data.pk) {
return field.placeholder || ''; return field.placeholder || '';
return $(searching());
} }
// Custom formatting for selected item // Custom formatting for selected item
@ -1739,7 +1774,7 @@ function constructInputOptions(name, classes, type, parameters) {
// Construct a "hidden" input // Construct a "hidden" input
function constructHiddenInput(name, parameters, options) { function constructHiddenInput(name, parameters) {
return constructInputOptions( return constructInputOptions(
name, name,
@ -1751,7 +1786,7 @@ function constructHiddenInput(name, parameters, options) {
// Construct a "checkbox" input // Construct a "checkbox" input
function constructCheckboxInput(name, parameters, options) { function constructCheckboxInput(name, parameters) {
return constructInputOptions( return constructInputOptions(
name, name,
@ -1763,7 +1798,7 @@ function constructCheckboxInput(name, parameters, options) {
// Construct a "text" input // Construct a "text" input
function constructTextInput(name, parameters, options) { function constructTextInput(name, parameters) {
var classes = ''; var classes = '';
var type = ''; var type = '';
@ -1793,7 +1828,7 @@ function constructTextInput(name, parameters, options) {
// Construct a "number" field // Construct a "number" field
function constructNumberInput(name, parameters, options) { function constructNumberInput(name, parameters) {
return constructInputOptions( return constructInputOptions(
name, name,
@ -1805,7 +1840,7 @@ function constructNumberInput(name, parameters, options) {
// Construct a "choice" input // Construct a "choice" input
function constructChoiceInput(name, parameters, options) { function constructChoiceInput(name, parameters) {
var html = `<select id='id_${name}' class='select form-control' name='${name}'>`; var html = `<select id='id_${name}' class='select form-control' name='${name}'>`;
@ -1838,7 +1873,7 @@ function constructChoiceInput(name, parameters, options) {
* be converted into a select2 input. * be converted into a select2 input.
* This will then be served custom data from the API (as required)... * This will then be served custom data from the API (as required)...
*/ */
function constructRelatedFieldInput(name, parameters, options) { function constructRelatedFieldInput(name) {
var html = `<select id='id_${name}' class='select form-control' name='${name}'></select>`; var html = `<select id='id_${name}' class='select form-control' name='${name}'></select>`;
@ -1851,7 +1886,7 @@ function constructRelatedFieldInput(name, parameters, options) {
/* /*
* Construct a field for file upload * Construct a field for file upload
*/ */
function constructFileUploadInput(name, parameters, options) { function constructFileUploadInput(name, parameters) {
var cls = 'clearablefileinput'; var cls = 'clearablefileinput';
@ -1871,7 +1906,7 @@ function constructFileUploadInput(name, parameters, options) {
/* /*
* Construct a field for a date input * Construct a field for a date input
*/ */
function constructDateInput(name, parameters, options) { function constructDateInput(name, parameters) {
return constructInputOptions( return constructInputOptions(
name, name,
@ -1886,7 +1921,7 @@ function constructDateInput(name, parameters, options) {
* Construct a "candy" field input * Construct a "candy" field input
* No actual field data! * No actual field data!
*/ */
function constructCandyInput(name, parameters, options) { function constructCandyInput(name, parameters) {
return parameters.html; return parameters.html;
@ -1901,7 +1936,7 @@ function constructCandyInput(name, parameters, options) {
* - parameters: Field parameters returned by the OPTIONS method * - parameters: Field parameters returned by the OPTIONS method
* *
*/ */
function constructHelpText(name, parameters, options) { function constructHelpText(name, parameters) {
var style = ''; var style = '';

View File

@ -0,0 +1,187 @@
{% load i18n %}
/* exported
blankImage,
deleteButton,
editButton,
imageHoverIcon,
makeIconBadge,
makeIconButton,
makeProgressBar,
renderLink,
select2Thumbnail,
yesNoLabel,
*/
function yesNoLabel(value) {
if (value) {
return `<span class='label label-green'>{% trans "YES" %}</span>`;
} else {
return `<span class='label label-yellow'>{% trans "NO" %}</span>`;
}
}
function editButton(url, text='{% trans "Edit" %}') {
return `<button class='btn btn-success edit-button btn-sm' type='button' url='${url}'>${text}</button>`;
}
function deleteButton(url, text='{% trans "Delete" %}') {
return `<button class='btn btn-danger delete-button btn-sm' type='button' url='${url}'>${text}</button>`;
}
function blankImage() {
return `/static/img/blank_image.png`;
}
/* Render a small thumbnail icon for an image.
* On mouseover, display a full-size version of the image
*/
function imageHoverIcon(url) {
if (!url) {
url = blankImage();
}
var html = `
<a class='hover-icon'>
<img class='hover-img-thumb' src='${url}'>
<img class='hover-img-large' src='${url}'>
</a>
`;
return html;
}
// Render a select2 thumbnail image
function select2Thumbnail(image) {
if (!image) {
image = blankImage();
}
return `<img src='${image}' class='select2-thumbnail'>`;
}
function makeIconBadge(icon, title) {
// Construct an 'icon badge' which floats to the right of an object
var html = `<span class='fas ${icon} label-right' title='${title}'></span>`;
return html;
}
function makeIconButton(icon, cls, pk, title, options={}) {
// Construct an 'icon button' using the fontawesome set
var classes = `btn btn-default btn-glyph ${cls}`;
var id = `${cls}-${pk}`;
var html = '';
var extraProps = '';
if (options.disabled) {
extraProps += `disabled='true' `;
}
html += `<button pk='${pk}' id='${id}' class='${classes}' title='${title}' ${extraProps}>`;
html += `<span class='fas ${icon}'></span>`;
html += `</button>`;
return html;
}
/*
* Render a progessbar!
*
* @param value is the current value of the progress bar
* @param maximum is the maximum value of the progress bar
*/
function makeProgressBar(value, maximum, opts={}) {
var options = opts || {};
value = parseFloat(value);
var percent = 100;
// Prevent div-by-zero or null value
if (maximum && maximum > 0) {
maximum = parseFloat(maximum);
percent = parseInt(value / maximum * 100);
}
if (percent > 100) {
percent = 100;
}
var extraclass = '';
if (value > maximum) {
extraclass='progress-bar-over';
} else if (value < maximum) {
extraclass = 'progress-bar-under';
}
var style = options.style || '';
var text = '';
if (style == 'percent') {
// Display e.g. "50%"
text = `${percent}%`;
} else if (style == 'max') {
// Display just the maximum value
text = `${maximum}`;
} else if (style == 'value') {
// Display just the current value
text = `${value}`;
} else if (style == 'blank') {
// No display!
text = '';
} else {
/* Default style
* Display e.g. "5 / 10"
*/
text = `${value} / ${maximum}`;
}
var id = options.id || 'progress-bar';
return `
<div id='${id}' class='progress'>
<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>
`;
}
function renderLink(text, url, options={}) {
if (url === null || url === undefined || url === '') {
return text;
}
var max_length = options.max_length || -1;
// Shorten the displayed length if required
if ((max_length > 0) && (text.length > max_length)) {
var slice_length = (max_length - 3) / 2;
var text_start = text.slice(0, slice_length);
var text_end = text.slice(-slice_length);
text = `${text_start}...${text_end}`;
}
return `<a href="${url}">${text}</a>`;
}

View File

@ -1,6 +1,25 @@
{% load i18n %} {% load i18n %}
function printStockItemLabels(items, options={}) { /* globals
attachSelect,
closeModal,
inventreeGet,
makeOptionsList,
modalEnable,
modalSetContent,
modalSetTitle,
modalSubmit,
openModal,
showAlertDialog,
*/
/* exported
printPartLabels,
printStockItemLabels,
printStockLocationLabels,
*/
function printStockItemLabels(items) {
/** /**
* Print stock item labels for the given stock items * Print stock item labels for the given stock items
*/ */
@ -54,7 +73,7 @@ function printStockItemLabels(items, options={}) {
); );
} }
function printStockLocationLabels(locations, options={}) { function printStockLocationLabels(locations) {
if (locations.length == 0) { if (locations.length == 0) {
showAlertDialog( showAlertDialog(
@ -101,11 +120,11 @@ function printStockLocationLabels(locations, options={}) {
); );
} }
} }
) );
} }
function printPartLabels(parts, options={}) { function printPartLabels(parts) {
/** /**
* Print labels for the provided parts * Print labels for the provided parts
*/ */

View File

@ -1,5 +1,22 @@
{% load i18n %} {% load i18n %}
/* globals
inventreeGet,
showAlertOrCache,
*/
/* exported
attachSecondaryModal,
clearField,
clearFieldOptions,
closeModal,
enableField,
getFieldValue,
reloadFieldOptions,
showModalImage,
removeRowFromModalForm,
showQuestionDialog,
*/
/* /*
* Create and display a new modal dialog * Create and display a new modal dialog
@ -77,7 +94,7 @@ function createNewModal(options={}) {
}); });
// Automatically remove the modal when it is deleted! // Automatically remove the modal when it is deleted!
$(modal_name).on('hidden.bs.modal', function(e) { $(modal_name).on('hidden.bs.modal', function() {
$(modal_name).remove(); $(modal_name).remove();
}); });
@ -86,7 +103,7 @@ function createNewModal(options={}) {
if (event.keyCode == 13) { if (event.keyCode == 13) {
event.preventDefault(); event.preventDefault();
// Simulate a click on the 'Submit' button // Simulate a click on the 'Submit' button
$(modal_name).find("#modal-form-submit").click(); $(modal_name).find('#modal-form-submit').click();
return false; return false;
} }
@ -253,8 +270,8 @@ function reloadFieldOptions(fieldName, options) {
// Update the target field with the new options // Update the target field with the new options
setFieldOptions(fieldName, opts); setFieldOptions(fieldName, opts);
}, },
error: function(response) { error: function() {
console.log("Error GETting field options"); console.log('Error GETting field options');
} }
}); });
} }
@ -273,7 +290,7 @@ function enableField(fieldName, enabled, options={}) {
var field = getFieldByName(modal, fieldName); var field = getFieldByName(modal, fieldName);
field.prop("disabled", !enabled); field.prop('disabled', !enabled);
} }
function clearField(fieldName, options={}) { function clearField(fieldName, options={}) {
@ -344,7 +361,7 @@ function attachToggle(modal) {
* and also larger toggle style buttons are easier to press! * and also larger toggle style buttons are easier to press!
*/ */
$(modal).find("input[type='checkbox']").each(function(x) { $(modal).find(`input[type='checkbox']`).each(function() {
$(this).bootstrapToggle({ $(this).bootstrapToggle({
size: 'small', size: 'small',
onstyle: 'success', onstyle: 'success',
@ -377,7 +394,7 @@ function loadingMessageContent() {
*/ */
// TODO - This can be made a lot better // TODO - This can be made a lot better
return "<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> {% trans 'Waiting for server...' %}"; return `<span class='glyphicon glyphicon-refresh glyphicon-refresh-animate'></span> {% trans 'Waiting for server...' %}`;
} }
@ -399,29 +416,29 @@ function afterForm(response, options) {
// Display any messages // Display any messages
if (response.success) { if (response.success) {
showAlertOrCache("alert-success", response.success, cache); showAlertOrCache('alert-success', response.success, cache);
} }
if (response.info) { if (response.info) {
showAlertOrCache("alert-info", response.info, cache); showAlertOrCache('alert-info', response.info, cache);
} }
if (response.warning) { if (response.warning) {
showAlertOrCache("alert-warning", response.warning, cache); showAlertOrCache('alert-warning', response.warning, cache);
} }
if (response.danger) { if (response.danger) {
showAlertOrCache("alert-danger", response.danger, cache); showAlertOrCache('alert-danger', response.danger, cache);
} }
// Was a callback provided? // Was a callback provided?
if (options.success) { if (options.success) {
options.success(response); options.success(response);
} } else if (options.follow && response.url) {
else if (options.follow && response.url) {
window.location.href = response.url; window.location.href = response.url;
} } else if (options.redirect) {
else if (options.redirect) {
window.location.href = options.redirect; window.location.href = options.redirect;
} } else if (options.reload) {
else if (options.reload) {
location.reload(); location.reload();
} }
} }
@ -554,7 +571,7 @@ function renderErrorMessage(xhr) {
} }
function showAlertDialog(title, content, options={}) { function showAlertDialog(title, content) {
/* Display a modal dialog message box. /* Display a modal dialog message box.
* *
* title - Title text * title - Title text
@ -595,7 +612,7 @@ function showQuestionDialog(title, content, options={}) {
modalSetContent(modal, content); modalSetContent(modal, content);
$(modal).on('click', "#modal-form-submit", function() { $(modal).on('click', '#modal-form-submit', function() {
$(modal).modal('hide'); $(modal).modal('hide');
if (options.accept) { if (options.accept) {
@ -636,7 +653,7 @@ function openModal(options) {
event.preventDefault(); event.preventDefault();
// Simulate a click on the 'Submit' button // Simulate a click on the 'Submit' button
$(modal).find("#modal-form-submit").click(); $(modal).find('#modal-form-submit').click();
return false; return false;
} }
@ -698,17 +715,17 @@ function insertNewItemButton(modal, options) {
* Inserts a button at the end of this lael element. * Inserts a button at the end of this lael element.
*/ */
var html = "<span style='float: right;'>"; var html = `<span style='float: right;'>`;
html += "<div type='button' class='btn btn-primary btn-secondary'"; html += `<div type='button' class='btn btn-primary btn-secondary'`;
if (options.title) { if (options.title) {
html += " title='" + options.title + "'"; html += ` title='${ options.title}'`;
} }
html += " id='btn-new-" + options.field + "'>" + options.label + "</div>"; html += ` id='btn-new-${options.field}'>${options.label}</div>`;
html += "</span>"; html += '</span>';
$(modal).find('label[for="id_'+ options.field + '"]').append(html); $(modal).find('label[for="id_'+ options.field + '"]').append(html);
} }
@ -733,7 +750,7 @@ function attachSecondaryModal(modal, options) {
var data = options.data || {}; var data = options.data || {};
// Add a callback to the button // Add a callback to the button
$(modal).find("#btn-new-" + options.field).on('click', function() { $(modal).find('#btn-new-' + options.field).on('click', function() {
// Launch the secondary modal // Launch the secondary modal
launchModalForm( launchModalForm(
@ -762,24 +779,26 @@ function attachSecondaryModal(modal, options) {
} }
// eslint-disable-next-line no-unused-vars
function attachSecondaries(modal, secondaries) { function attachSecondaries(modal, secondaries) {
/* Attach a provided list of secondary modals */ /* Attach a provided list of secondary modals */
// 2021-07-18 - Secondary modals will be disabled for now, until they are re-implemented in the "API forms" architecture // 2021-07-18 - Secondary modals will be disabled for now, until they are re-implemented in the "API forms" architecture
return;
for (var i = 0; i < secondaries.length; i++) { // for (var i = 0; i < secondaries.length; i++) {
attachSecondaryModal(modal, secondaries[i]); // attachSecondaryModal(modal, secondaries[i]);
} // }
} }
function insertActionButton(modal, options) { function insertActionButton(modal, options) {
/* Insert a custom submition button */ /* Insert a custom submission button */
var html = "<span style='float: right;'>"; var html = `
html += "<button name='" + options.name + "' type='submit' class='btn btn-default modal-form-button'"; <span style='float: right;'>
html += " value='" + options.name + "'>" + options.title + "</button>"; <button name='${options.name}' type='submit' class='btn btn-default modal-form-button' value='${options.name}'>
html += "</span>"; ${options.title}
</button>
</span>`;
$(modal).find('#modal-footer-buttons').append(html); $(modal).find('#modal-footer-buttons').append(html);
} }
@ -838,8 +857,6 @@ function handleModalForm(url, options) {
var form = $(modal).find('.js-modal-form'); var form = $(modal).find('.js-modal-form');
var _form = $(modal).find(".js-modal-form");
form.ajaxForm({ form.ajaxForm({
url: url, url: url,
dataType: 'json', dataType: 'json',
@ -860,7 +877,7 @@ function handleModalForm(url, options) {
modalEnable(modal, false); modalEnable(modal, false);
}, },
// POST was successful // POST was successful
success: function(response, status, xhr, f) { success: function(response) {
// Re-enable the modal // Re-enable the modal
modalEnable(modal, true); modalEnable(modal, true);
if ('form_valid' in response) { if ('form_valid' in response) {
@ -868,9 +885,8 @@ function handleModalForm(url, options) {
if (response.form_valid) { if (response.form_valid) {
$(modal).modal('hide'); $(modal).modal('hide');
afterForm(response, options); afterForm(response, options);
} } else {
// Form was returned, invalid! // Form was returned, invalid!
else {
// Disable error message with option or response // Disable error message with option or response
if (!options.hideErrorMessage && !response.hideErrorMessage) { if (!options.hideErrorMessage && !response.hideErrorMessage) {
@ -901,25 +917,23 @@ function handleModalForm(url, options) {
if (response.buttons) { if (response.buttons) {
attachButtons(modal, response.buttons); attachButtons(modal, response.buttons);
} }
} } else {
else {
$(modal).modal('hide'); $(modal).modal('hide');
showAlertDialog('{% trans "Invalid response from server" %}', '{% trans "Form data missing from server response" %}'); showAlertDialog('{% trans "Invalid response from server" %}', '{% trans "Form data missing from server response" %}');
} }
} }
} } else {
else {
$(modal).modal('hide'); $(modal).modal('hide');
afterForm(response, options); afterForm(response, options);
} }
}, },
error: function(xhr, ajaxOptions, thrownError) { error: function(xhr) {
// There was an error submitting form data via POST // There was an error submitting form data via POST
$(modal).modal('hide'); $(modal).modal('hide');
showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr)); showAlertDialog('{% trans "Error posting form data" %}', renderErrorMessage(xhr));
}, },
complete: function(xhr) { complete: function() {
// TODO // TODO
} }
}); });
@ -960,7 +974,7 @@ function launchModalForm(url, options = {}) {
$(modal).find('#modal-footer-buttons').html(''); $(modal).find('#modal-footer-buttons').html('');
// Form the ajax request to retrieve the django form data // Form the ajax request to retrieve the django form data
ajax_data = { var ajax_data = {
url: url, url: url,
type: 'get', type: 'get',
dataType: 'json', dataType: 'json',
@ -1017,7 +1031,7 @@ function launchModalForm(url, options = {}) {
showAlertDialog('{% trans "Invalid server response" %}', '{% trans "JSON response missing form data" %}'); showAlertDialog('{% trans "Invalid server response" %}', '{% trans "JSON response missing form data" %}');
} }
}, },
error: function (xhr, ajaxOptions, thrownError) { error: function(xhr) {
$(modal).modal('hide'); $(modal).modal('hide');
@ -1056,8 +1070,8 @@ function launchModalForm(url, options = {}) {
showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr)); showAlertDialog('{% trans "Error requesting form data" %}', renderErrorMessage(xhr));
} }
console.log("Modal form error: " + xhr.status); console.log('Modal form error: ' + xhr.status);
console.log("Message: " + xhr.responseText); console.log('Message: ' + xhr.responseText);
} }
}; };

View File

@ -1,18 +1,19 @@
{% load i18n %} {% load i18n %}
/* globals
blankImage,
select2Thumbnail
*/
function blankImage() { /* exported
return `/static/img/blank_image.png`; renderBuild,
} renderCompany,
renderManufacturerPart,
// Render a select2 thumbnail image renderOwner,
function select2Thumbnail(image) { renderPartCategory,
if (!image) { renderStockLocation,
image = blankImage(); renderSupplierPart,
} */
return `<img src='${image}' class='select2-thumbnail'>`;
}
/* /*
@ -29,6 +30,7 @@ function select2Thumbnail(image) {
// Renderer for "Company" model // Renderer for "Company" model
// eslint-disable-next-line no-unused-vars
function renderCompany(name, data, parameters, options) { function renderCompany(name, data, parameters, options) {
var html = select2Thumbnail(data.image); var html = select2Thumbnail(data.image);
@ -42,6 +44,7 @@ function renderCompany(name, data, parameters, options) {
// Renderer for "StockItem" model // Renderer for "StockItem" model
// eslint-disable-next-line no-unused-vars
function renderStockItem(name, data, parameters, options) { function renderStockItem(name, data, parameters, options) {
var image = data.part_detail.thumbnail || data.part_detail.image || blankImage(); var image = data.part_detail.thumbnail || data.part_detail.image || blankImage();
@ -65,6 +68,7 @@ function renderStockItem(name, data, parameters, options) {
// Renderer for "StockLocation" model // Renderer for "StockLocation" model
// eslint-disable-next-line no-unused-vars
function renderStockLocation(name, data, parameters, options) { function renderStockLocation(name, data, parameters, options) {
var level = '- '.repeat(data.level); var level = '- '.repeat(data.level);
@ -80,7 +84,7 @@ function renderStockLocation(name, data, parameters, options) {
return html; return html;
} }
// eslint-disable-next-line no-unused-vars
function renderBuild(name, data, parameters, options) { function renderBuild(name, data, parameters, options) {
var image = null; var image = null;
@ -101,6 +105,7 @@ function renderBuild(name, data, parameters, options) {
// Renderer for "Part" model // Renderer for "Part" model
// eslint-disable-next-line no-unused-vars
function renderPart(name, data, parameters, options) { function renderPart(name, data, parameters, options) {
var html = select2Thumbnail(data.image); var html = select2Thumbnail(data.image);
@ -117,6 +122,7 @@ function renderPart(name, data, parameters, options) {
} }
// Renderer for "User" model // Renderer for "User" model
// eslint-disable-next-line no-unused-vars
function renderUser(name, data, parameters, options) { function renderUser(name, data, parameters, options) {
var html = `<span>${data.username}</span>`; var html = `<span>${data.username}</span>`;
@ -130,6 +136,7 @@ function renderUser(name, data, parameters, options) {
// Renderer for "Owner" model // Renderer for "Owner" model
// eslint-disable-next-line no-unused-vars
function renderOwner(name, data, parameters, options) { function renderOwner(name, data, parameters, options) {
var html = `<span>${data.name}</span>`; var html = `<span>${data.name}</span>`;
@ -150,6 +157,7 @@ function renderOwner(name, data, parameters, options) {
// Renderer for "PartCategory" model // Renderer for "PartCategory" model
// eslint-disable-next-line no-unused-vars
function renderPartCategory(name, data, parameters, options) { function renderPartCategory(name, data, parameters, options) {
var level = '- '.repeat(data.level); var level = '- '.repeat(data.level);
@ -165,7 +173,7 @@ function renderPartCategory(name, data, parameters, options) {
return html; return html;
} }
// eslint-disable-next-line no-unused-vars
function renderPartParameterTemplate(name, data, parameters, options) { function renderPartParameterTemplate(name, data, parameters, options) {
var html = `<span>${data.name} - [${data.units}]</span>`; var html = `<span>${data.name} - [${data.units}]</span>`;
@ -175,6 +183,7 @@ function renderPartParameterTemplate(name, data, parameters, options) {
// Renderer for "ManufacturerPart" model // Renderer for "ManufacturerPart" model
// eslint-disable-next-line no-unused-vars
function renderManufacturerPart(name, data, parameters, options) { function renderManufacturerPart(name, data, parameters, options) {
var manufacturer_image = null; var manufacturer_image = null;
@ -203,6 +212,7 @@ function renderManufacturerPart(name, data, parameters, options) {
// Renderer for "SupplierPart" model // Renderer for "SupplierPart" model
// eslint-disable-next-line no-unused-vars
function renderSupplierPart(name, data, parameters, options) { function renderSupplierPart(name, data, parameters, options) {
var supplier_image = null; var supplier_image = null;

View File

@ -1,6 +1,33 @@
{% load i18n %} {% load i18n %}
{% load inventree_extras %} {% load inventree_extras %}
/* globals
companyFormFields,
constructForm,
createSupplierPart,
global_settings,
imageHoverIcon,
inventreeGet,
launchModalForm,
loadTableFilters,
makeIconBadge,
purchaseOrderStatusDisplay,
renderLink,
salesOrderStatusDisplay,
setupFilterList,
*/
/* exported
createSalesOrder,
editPurchaseOrderLineItem,
loadPurchaseOrderTable,
loadSalesOrderAllocationTable,
loadSalesOrderTable,
newPurchaseOrderFromOrderWizard,
newSupplierPartFromOrderWizard,
removeOrderRowFromOrderWizard,
removePurchaseOrderLineItem,
*/
// Create a new SalesOrder // Create a new SalesOrder
function createSalesOrder(options={}) { function createSalesOrder(options={}) {
@ -15,7 +42,7 @@ function createSalesOrder(options={}) {
value: options.customer, value: options.customer,
secondary: { secondary: {
title: '{% trans "Add Customer" %}', title: '{% trans "Add Customer" %}',
fields: function(data) { fields: function() {
var fields = companyFormFields(); var fields = companyFormFields();
fields.is_customer.value = true; fields.is_customer.value = true;
@ -56,7 +83,7 @@ function createPurchaseOrder(options={}) {
value: options.supplier, value: options.supplier,
secondary: { secondary: {
title: '{% trans "Add Supplier" %}', title: '{% trans "Add Supplier" %}',
fields: function(data) { fields: function() {
var fields = companyFormFields(); var fields = companyFormFields();
fields.is_supplier.value = true; fields.is_supplier.value = true;
@ -143,7 +170,7 @@ function newSupplierPartFromOrderWizard(e) {
if (response.supplier_detail) { if (response.supplier_detail) {
text += response.supplier_detail.name; text += response.supplier_detail.name;
text += " | "; text += ' | ';
} }
text += response.SKU; text += response.SKU;
@ -153,8 +180,7 @@ function newSupplierPartFromOrderWizard(e) {
$('#modal-form').find(dropdown).append(option).trigger('change'); $('#modal-form').find(dropdown).append(option).trigger('change');
} }
} }
) );
} }
}); });
} }
@ -203,7 +229,7 @@ function newPurchaseOrderFromOrderWizard(e) {
$('#modal-form').find(dropdown).append(option).trigger('change'); $('#modal-form').find(dropdown).append(option).trigger('change');
} }
} }
) );
} }
}); });
} }
@ -248,7 +274,7 @@ function loadPurchaseOrderTable(table, options) {
options.params['supplier_detail'] = true; options.params['supplier_detail'] = true;
var filters = loadTableFilters("purchaseorder"); var filters = loadTableFilters('purchaseorder');
for (var key in options.params) { for (var key in options.params) {
filters[key] = options.params[key]; filters[key] = options.params[key];
@ -256,7 +282,7 @@ function loadPurchaseOrderTable(table, options) {
options.url = options.url || '{% url "api-po-list" %}'; options.url = options.url || '{% url "api-po-list" %}';
setupFilterList("purchaseorder", $(table)); setupFilterList('purchaseorder', $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: options.url, url: options.url,
@ -265,7 +291,9 @@ function loadPurchaseOrderTable(table, options) {
groupBy: false, groupBy: false,
sidePagination: 'server', sidePagination: 'server',
original: options.params, original: options.params,
formatNoMatches: function() { return '{% trans "No purchase orders found" %}'; }, formatNoMatches: function() {
return '{% trans "No purchase orders found" %}';
},
columns: [ columns: [
{ {
title: '', title: '',
@ -278,7 +306,7 @@ function loadPurchaseOrderTable(table, options) {
title: '{% trans "Purchase Order" %}', title: '{% trans "Purchase Order" %}',
sortable: true, sortable: true,
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX; var prefix = global_settings.PURCHASEORDER_REFERENCE_PREFIX;
@ -300,7 +328,7 @@ function loadPurchaseOrderTable(table, options) {
title: '{% trans "Supplier" %}', title: '{% trans "Supplier" %}',
sortable: true, sortable: true,
sortName: 'supplier__name', sortName: 'supplier__name',
formatter: function(value, row, index, field) { formatter: function(value, row) {
return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`); return imageHoverIcon(row.supplier_detail.image) + renderLink(row.supplier_detail.name, `/company/${row.supplier}/purchase-orders/`);
} }
}, },
@ -316,7 +344,7 @@ function loadPurchaseOrderTable(table, options) {
field: 'status', field: 'status',
title: '{% trans "Status" %}', title: '{% trans "Status" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value, row) {
return purchaseOrderStatusDisplay(row.status, row.status_text); return purchaseOrderStatusDisplay(row.status, row.status_text);
} }
}, },
@ -344,7 +372,7 @@ function loadSalesOrderTable(table, options) {
options.params = options.params || {}; options.params = options.params || {};
options.params['customer_detail'] = true; options.params['customer_detail'] = true;
var filters = loadTableFilters("salesorder"); var filters = loadTableFilters('salesorder');
for (var key in options.params) { for (var key in options.params) {
filters[key] = options.params[key]; filters[key] = options.params[key];
@ -352,7 +380,7 @@ function loadSalesOrderTable(table, options) {
options.url = options.url || '{% url "api-so-list" %}'; options.url = options.url || '{% url "api-so-list" %}';
setupFilterList("salesorder", $(table)); setupFilterList('salesorder', $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: options.url, url: options.url,
@ -361,7 +389,9 @@ function loadSalesOrderTable(table, options) {
groupBy: false, groupBy: false,
sidePagination: 'server', sidePagination: 'server',
original: options.params, original: options.params,
formatNoMatches: function() { return '{% trans "No sales orders found" %}'; }, formatNoMatches: function() {
return '{% trans "No sales orders found" %}';
},
columns: [ columns: [
{ {
title: '', title: '',
@ -373,7 +403,7 @@ function loadSalesOrderTable(table, options) {
sortable: true, sortable: true,
field: 'reference', field: 'reference',
title: '{% trans "Sales Order" %}', title: '{% trans "Sales Order" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var prefix = global_settings.SALESORDER_REFERENCE_PREFIX; var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;
@ -395,7 +425,7 @@ function loadSalesOrderTable(table, options) {
sortName: 'customer__name', sortName: 'customer__name',
field: 'customer_detail', field: 'customer_detail',
title: '{% trans "Customer" %}', title: '{% trans "Customer" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (!row.customer_detail) { if (!row.customer_detail) {
return '{% trans "Invalid Customer" %}'; return '{% trans "Invalid Customer" %}';
@ -418,7 +448,7 @@ function loadSalesOrderTable(table, options) {
sortable: true, sortable: true,
field: 'status', field: 'status',
title: '{% trans "Status" %}', title: '{% trans "Status" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
return salesOrderStatusDisplay(row.status, row.status_text); return salesOrderStatusDisplay(row.status, row.status_text);
} }
}, },
@ -459,13 +489,13 @@ function loadSalesOrderAllocationTable(table, options={}) {
options.params['item_detail'] = true; options.params['item_detail'] = true;
options.params['order_detail'] = true; options.params['order_detail'] = true;
var filters = loadTableFilters("salesorderallocation"); var filters = loadTableFilters('salesorderallocation');
for (var key in options.params) { for (var key in options.params) {
filters[key] = options.params[key]; filters[key] = options.params[key];
} }
setupFilterList("salesorderallocation", $(table)); setupFilterList('salesorderallocation', $(table));
$(table).inventreeTable({ $(table).inventreeTable({
url: '{% url "api-so-allocation-list" %}', url: '{% url "api-so-allocation-list" %}',
@ -475,7 +505,9 @@ function loadSalesOrderAllocationTable(table, options={}) {
search: false, search: false,
paginationVAlign: 'bottom', paginationVAlign: 'bottom',
original: options.params, original: options.params,
formatNoMatches: function() { return '{% trans "No sales order allocations found" %}'; }, formatNoMatches: function() {
return '{% trans "No sales order allocations found" %}';
},
columns: [ columns: [
{ {
field: 'pk', field: 'pk',
@ -486,7 +518,6 @@ function loadSalesOrderAllocationTable(table, options={}) {
field: 'order', field: 'order',
switchable: false, switchable: false,
title: '{% trans "Order" %}', title: '{% trans "Order" %}',
switchable: false,
formatter: function(value, row) { formatter: function(value, row) {
var prefix = global_settings.SALESORDER_REFERENCE_PREFIX; var prefix = global_settings.SALESORDER_REFERENCE_PREFIX;

View File

@ -1,20 +1,48 @@
{% load i18n %} {% load i18n %}
{% load inventree_extras %} {% load inventree_extras %}
/* globals
Chart,
constructForm,
global_settings,
imageHoverIcon,
inventreeGet,
inventreePut,
launchModalForm,
linkButtonsToSelection,
loadTableFilters,
makeIconBadge,
makeIconButton,
printPartLabels,
renderLink,
setFormGroupVisibility,
setupFilterList,
yesNoLabel,
*/
/* exported
duplicatePart,
editCategory,
editPart,
initPriceBreakSet,
loadBomChart,
loadParametricPartTable,
loadPartCategoryTable,
loadPartParameterTable,
loadPartTable,
loadPartTestTemplateTable,
loadPartVariantTable,
loadSellPricingChart,
loadSimplePartTable,
loadStockPricingChart,
toggleStar,
*/
/* Part API functions /* Part API functions
* Requires api.js to be loaded first * Requires api.js to be loaded first
*/ */
function yesNoLabel(value) { function partGroups() {
if (value) {
return `<span class='label label-green'>{% trans "YES" %}</span>`;
} else {
return `<span class='label label-yellow'>{% trans "NO" %}</span>`;
}
}
function partGroups(options={}) {
return { return {
attributes: { attributes: {
@ -34,9 +62,9 @@ function partGroups(options={}) {
collapsible: true, collapsible: true,
hidden: !global_settings.PART_PURCHASEABLE, hidden: !global_settings.PART_PURCHASEABLE,
} }
};
} }
}
// Construct fieldset for part forms // Construct fieldset for part forms
function partFields(options={}) { function partFields(options={}) {
@ -45,7 +73,7 @@ function partFields(options={}) {
category: { category: {
secondary: { secondary: {
title: '{% trans "Add Part Category" %}', title: '{% trans "Add Part Category" %}',
fields: function(data) { fields: function() {
var fields = categoryFields(); var fields = categoryFields();
return fields; return fields;
@ -115,14 +143,14 @@ function partFields(options={}) {
// Pop expiry field // Pop expiry field
if (!global_settings.STOCK_ENABLE_EXPIRY) { if (!global_settings.STOCK_ENABLE_EXPIRY) {
delete fields["default_expiry"]; delete fields['default_expiry'];
} }
// Additional fields when "creating" a new part // Additional fields when "creating" a new part
if (options.create) { if (options.create) {
// No supplier parts available yet // No supplier parts available yet
delete fields["default_supplier"]; delete fields['default_supplier'];
if (global_settings.PART_CREATE_INITIAL) { if (global_settings.PART_CREATE_INITIAL) {
@ -264,7 +292,7 @@ function categoryFields() {
// Edit a PartCategory via the API // Edit a PartCategory via the API
function editCategory(pk, options={}) { function editCategory(pk) {
var url = `/api/part/category/${pk}/`; var url = `/api/part/category/${pk}/`;
@ -279,7 +307,7 @@ function editCategory(pk, options={}) {
} }
function editPart(pk, options={}) { function editPart(pk) {
var url = `/api/part/${pk}/`; var url = `/api/part/${pk}/`;
@ -291,7 +319,7 @@ function editPart(pk, options={}) {
constructForm(url, { constructForm(url, {
fields: fields, fields: fields,
groups: partGroups(), groups: groups,
title: '{% trans "Edit Part" %}', title: '{% trans "Edit Part" %}',
reload: true, reload: true,
}); });
@ -370,7 +398,7 @@ function toggleStar(options) {
} }
function makePartIcons(part, options={}) { function makePartIcons(part) {
/* Render a set of icons for the given part. /* Render a set of icons for the given part.
*/ */
@ -397,7 +425,7 @@ function makePartIcons(part, options={}) {
} }
if (part.salable) { if (part.salable) {
html += makeIconBadge('fa-dollar-sign', title='{% trans "Salable part" %}'); html += makeIconBadge('fa-dollar-sign', '{% trans "Salable part" %}');
} }
if (!part.active) { if (!part.active) {
@ -418,13 +446,13 @@ function loadPartVariantTable(table, partId, options={}) {
params.ancestor = partId; params.ancestor = partId;
// Load filters // Load filters
var filters = loadTableFilters("variants"); var filters = loadTableFilters('variants');
for (var key in params) { for (var key in params) {
filters[key] = params[key]; filters[key] = params[key];
} }
setupFilterList("variants", $(table)); setupFilterList('variants', $(table));
var cols = [ var cols = [
{ {
@ -437,7 +465,7 @@ function loadPartVariantTable(table, partId, options={}) {
field: 'name', field: 'name',
title: '{% trans "Name" %}', title: '{% trans "Name" %}',
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var html = ''; var html = '';
var name = ''; var name = '';
@ -506,12 +534,14 @@ function loadPartVariantTable(table, partId, options={}) {
]; ];
table.inventreeTable({ table.inventreeTable({
url: "{% url 'api-part-list' %}", url: '{% url "api-part-list" %}',
name: 'partvariants', name: 'partvariants',
showColumns: true, showColumns: true,
original: params, original: params,
queryParams: filters, queryParams: filters,
formatNoMatches: function() { return '{% trans "No variants found" %}'; }, formatNoMatches: function() {
return '{% trans "No variants found" %}';
},
columns: cols, columns: cols,
treeEnable: true, treeEnable: true,
rootParentId: partId, rootParentId: partId,
@ -545,7 +575,7 @@ function loadPartParameterTable(table, url, options) {
var params = options.params || {}; var params = options.params || {};
// Load filters // Load filters
var filters = loadTableFilters("part-parameters"); var filters = loadTableFilters('part-parameters');
for (var key in params) { for (var key in params) {
filters[key] = params[key]; filters[key] = params[key];
@ -559,7 +589,9 @@ function loadPartParameterTable(table, url, options) {
queryParams: filters, queryParams: filters,
name: 'partparameters', name: 'partparameters',
groupBy: false, groupBy: false,
formatNoMatches: function() { return '{% trans "No parameters found" %}'; }, formatNoMatches: function() {
return '{% trans "No parameters found" %}';
},
columns: [ columns: [
{ {
checkbox: true, checkbox: true,
@ -650,19 +682,19 @@ function loadParametricPartTable(table, options={}) {
* - table_data: Parameters data * - table_data: Parameters data
*/ */
var table_headers = options.headers var table_headers = options.headers;
var table_data = options.data var table_data = options.data;
var columns = []; var columns = [];
for (header of table_headers) { for (var header of table_headers) {
if (header === 'part') { if (header === 'part') {
columns.push({ columns.push({
field: header, field: header,
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
sortable: true, sortable: true,
sortName: 'name', sortName: 'name',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var name = ''; var name = '';
@ -687,8 +719,6 @@ function loadParametricPartTable(table, options={}) {
title: header, title: header,
sortable: true, sortable: true,
filterControl: 'input', filterControl: 'input',
/* TODO: Search icons are not displayed */
/*clear: 'fa-times icon-red',*/
}); });
} }
} }
@ -698,7 +728,9 @@ function loadParametricPartTable(table, options={}) {
queryParams: table_headers, queryParams: table_headers,
groupBy: false, groupBy: false,
name: options.name || 'parametric', name: options.name || 'parametric',
formatNoMatches: function() { return '{% trans "No parts found" %}'; }, formatNoMatches: function() {
return '{% trans "No parts found" %}';
},
columns: columns, columns: columns,
showColumns: true, showColumns: true,
data: table_data, data: table_data,
@ -785,15 +817,17 @@ function loadPartTable(table, url, options={}) {
var filters = {}; var filters = {};
var col = null;
if (!options.disableFilters) { if (!options.disableFilters) {
filters = loadTableFilters("parts"); filters = loadTableFilters('parts');
} }
for (var key in params) { for (var key in params) {
filters[key] = params[key]; filters[key] = params[key];
} }
setupFilterList("parts", $(table), options.filterTarget || null); setupFilterList('parts', $(table), options.filterTarget || null);
var columns = [ var columns = [
{ {
@ -818,16 +852,18 @@ function loadPartTable(table, url, options={}) {
field: 'IPN', field: 'IPN',
title: 'IPN', title: 'IPN',
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'name', field: 'name',
title: '{% trans "Part" %}', title: '{% trans "Part" %}',
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var name = ''; var name = '';
@ -854,18 +890,20 @@ function loadPartTable(table, url, options={}) {
return display; return display;
} }
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
columns.push({ columns.push({
field: 'description', field: 'description',
title: '{% trans "Description" %}', title: '{% trans "Description" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (row.is_template) { if (row.is_template) {
value = '<i>' + value + '</i>'; value = `<i>${value}</i>`;
} }
return value; return value;
@ -876,60 +914,63 @@ function loadPartTable(table, url, options={}) {
sortName: 'category', sortName: 'category',
field: 'category_detail', field: 'category_detail',
title: '{% trans "Category" %}', title: '{% trans "Category" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (row.category) { if (row.category) {
return renderLink(value.pathstring, "/part/category/" + row.category + "/"); return renderLink(value.pathstring, `/part/category/${row.category}/`);
} } else {
else {
return '{% trans "No category" %}'; return '{% trans "No category" %}';
} }
} }
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'in_stock', field: 'in_stock',
title: '{% trans "Stock" %}', title: '{% trans "Stock" %}',
searchable: false, searchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row) {
var link = "stock"; var link = 'stock';
if (value) { if (value) {
// There IS stock available for this part // There IS stock available for this part
// Is stock "low" (below the 'minimum_stock' quantity)? // Is stock "low" (below the 'minimum_stock' quantity)?
if (row.minimum_stock && row.minimum_stock > value) { if (row.minimum_stock && row.minimum_stock > value) {
value += "<span class='label label-right label-warning'>{% trans "Low stock" %}</span>"; value += `<span class='label label-right label-warning'>{% trans "Low stock" %}</span>`;
} }
} else if (row.on_order) { } else if (row.on_order) {
// There is no stock available, but stock is 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='label label-right label-primary'>{% trans "On Order" %}: ${row.on_order}</span>`;
link = "orders"; link = 'orders';
} else if (row.building) { } else if (row.building) {
// There is no stock available, but stock is being built // 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='label label-right label-info'>{% trans "Building" %}: ${row.building}</span>`;
link = "builds"; link = 'builds';
} else { } else {
// There is no stock available // There is no stock available
value = "0<span class='label label-right label-danger'>{% trans "No Stock" %}</span>"; value = `0<span class='label label-right label-danger'>{% trans "No Stock" %}</span>`;
} }
return renderLink(value, '/part/' + row.pk + "/" + link + "/"); return renderLink(value, `/part/${row.pk}/${link}/`);
} }
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
columns.push({ columns.push({
field: 'link', field: 'link',
title: '{% trans "Link" %}', title: '{% trans "Link" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
return renderLink( return renderLink(
value, value, value, value,
{ {
@ -949,7 +990,9 @@ function loadPartTable(table, url, options={}) {
original: params, original: params,
sidePagination: 'server', sidePagination: 'server',
pagination: 'true', pagination: 'true',
formatNoMatches: function() { return '{% trans "No parts found" %}'; }, formatNoMatches: function() {
return '{% trans "No parts found" %}';
},
columns: columns, columns: columns,
showColumns: true, showColumns: true,
showCustomView: false, showCustomView: false,
@ -982,8 +1025,8 @@ function loadPartTable(table, url, options={}) {
/* Button callbacks for part table buttons */ /* Button callbacks for part table buttons */
$("#multi-part-order").click(function() { $('#multi-part-order').click(function() {
var selections = $(table).bootstrapTable("getSelections"); var selections = $(table).bootstrapTable('getSelections');
var parts = []; var parts = [];
@ -991,15 +1034,15 @@ function loadPartTable(table, url, options={}) {
parts.push(item.pk); parts.push(item.pk);
}); });
launchModalForm("/order/purchase-order/order-parts/", { launchModalForm('/order/purchase-order/order-parts/', {
data: { data: {
parts: parts, parts: parts,
}, },
}); });
}); });
$("#multi-part-category").click(function() { $('#multi-part-category').click(function() {
var selections = $(table).bootstrapTable("getSelections"); var selections = $(table).bootstrapTable('getSelections');
var parts = []; var parts = [];
@ -1007,7 +1050,7 @@ function loadPartTable(table, url, options={}) {
parts.push(item.pk); parts.push(item.pk);
}); });
launchModalForm("/part/set-category/", { launchModalForm('/part/set-category/', {
data: { data: {
parts: parts, parts: parts,
}, },
@ -1028,7 +1071,7 @@ function loadPartTable(table, url, options={}) {
}); });
$('#multi-part-export').click(function() { $('#multi-part-export').click(function() {
var selections = $(table).bootstrapTable("getSelections"); var selections = $(table).bootstrapTable('getSelections');
var parts = ''; var parts = '';
@ -1127,15 +1170,15 @@ function loadPartTestTemplateTable(table, options) {
var filterListElement = options.filterList || '#filter-list-parttests'; var filterListElement = options.filterList || '#filter-list-parttests';
var filters = loadTableFilters("parttests"); var filters = loadTableFilters('parttests');
var original = {}; var original = {};
for (var key in params) { for (var k in params) {
original[key] = params[key]; original[k] = params[k];
} }
setupFilterList("parttests", table, filterListElement); setupFilterList('parttests', table, filterListElement);
// Override the default values, or add new ones // Override the default values, or add new ones
for (var key in params) { for (var key in params) {
@ -1147,7 +1190,7 @@ function loadPartTestTemplateTable(table, options) {
formatNoMatches: function() { formatNoMatches: function() {
return '{% trans "No test templates matching query" %}'; return '{% trans "No test templates matching query" %}';
}, },
url: "{% url 'api-part-test-template-list' %}", url: '{% url "api-part-test-template-list" %}',
queryParams: filters, queryParams: filters,
name: 'testtemplate', name: 'testtemplate',
original: original, original: original,
@ -1168,7 +1211,7 @@ function loadPartTestTemplateTable(table, options) {
}, },
{ {
field: 'required', field: 'required',
title: "{% trans 'Required' %}", title: '{% trans "Required" %}',
sortable: true, sortable: true,
formatter: function(value) { formatter: function(value) {
return yesNoLabel(value); return yesNoLabel(value);
@ -1235,11 +1278,11 @@ function loadPriceBreakTable(table, options) {
onLoadSuccess: function(tableData) { onLoadSuccess: function(tableData) {
if (linkedGraph) { if (linkedGraph) {
// sort array // sort array
tableData = tableData.sort((a,b)=>a.quantity-b.quantity); tableData = tableData.sort((a, b) => (a.quantity - b.quantity));
// split up for graph definition // split up for graph definition
var graphLabels = Array.from(tableData, x => x.quantity); var graphLabels = Array.from(tableData, (x) => (x.quantity));
var graphData = Array.from(tableData, x => x.price); var graphData = Array.from(tableData, (x) => (x.price));
// destroy chart if exists // destroy chart if exists
if (chart) { if (chart) {
@ -1256,7 +1299,8 @@ function loadPriceBreakTable(table, options) {
borderColor: 'rgb(255, 206, 86)', borderColor: 'rgb(255, 206, 86)',
stepped: true, stepped: true,
fill: true, fill: true,
},] },
],
} }
); );
} }
@ -1277,10 +1321,10 @@ function loadPriceBreakTable(table, options) {
field: 'price', field: 'price',
title: '{% trans "Price" %}', title: '{% trans "Price" %}',
sortable: true, sortable: true,
formatter: function(value, row, index) { formatter: function(value, row) {
var html = value; var html = value;
html += `<div class='btn-group float-right' role='group'>` 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-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 += makeIconButton('fa-trash-alt icon-red', `button-${name}-delete`, row.pk, `{% trans "Delete ${human_name}" %}`);
@ -1331,7 +1375,7 @@ function initPriceBreakSet(table, options) {
); );
function reloadPriceBreakTable() { function reloadPriceBreakTable() {
table.bootstrapTable("refresh"); table.bootstrapTable('refresh');
} }
pb_new_btn.click(function() { pb_new_btn.click(function() {
@ -1419,12 +1463,26 @@ function loadBomChart(context, data) {
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
plugins: {legend: {position: 'bottom'}, plugins: {
scales: {xAxes: [{beginAtZero: true, ticks: {autoSkip: false}}]}} legend: {
position: 'bottom',
},
scales: {
xAxes: [
{
beginAtZero: true,
ticks: {
autoSkip: false,
}
}
]
}
}
} }
}); });
} }
function loadSellPricingChart(context, data) { function loadSellPricingChart(context, data) {
return new Chart(context, { return new Chart(context, {
type: 'line', type: 'line',
@ -1432,21 +1490,29 @@ function loadSellPricingChart(context, data) {
options: { options: {
responsive: true, responsive: true,
maintainAspectRatio: false, maintainAspectRatio: false,
plugins: {legend: {position: 'bottom'}}, plugins: {
legend: {
position: 'bottom'
}
},
scales: { scales: {
y: { y: {
type: 'linear', type: 'linear',
position: 'left', position: 'left',
grid: {display: false}, grid: {
display: false
},
title: { title: {
display: true, display: true,
text: '{% trans "Unit Price" %}' text: '{% trans "Unit Price" %}',
} }
}, },
y1: { y1: {
type: 'linear', type: 'linear',
position: 'right', position: 'right',
grid: {display: false}, grid: {
display: false
},
titel: { titel: {
display: true, display: true,
text: '{% trans "Quantity" %}', text: '{% trans "Quantity" %}',

View File

@ -1,5 +1,25 @@
{% load i18n %} {% load i18n %}
/* globals
attachSelect,
closeModal,
inventreeGet,
openModal,
makeOptionsList,
modalEnable,
modalSetContent,
modalSetTitle,
modalSubmit,
showAlertDialog,
*/
/* exported
printBomReports,
printBuildReports,
printPurchaseOrderReports,
printSalesOrderReports,
printTestReports,
*/
function selectReport(reports, items, options={}) { function selectReport(reports, items, options={}) {
/** /**
@ -88,7 +108,7 @@ function selectReport(reports, items, options={}) {
} }
function printTestReports(items, options={}) { function printTestReports(items) {
/** /**
* Print test reports for the provided stock item(s) * Print test reports for the provided stock item(s)
*/ */
@ -142,7 +162,7 @@ function printTestReports(items, options={}) {
} }
function printBuildReports(builds, options={}) { function printBuildReports(builds) {
/** /**
* Print Build report for the provided build(s) * Print Build report for the provided build(s)
*/ */
@ -188,14 +208,14 @@ function printBuildReports(builds, options={}) {
window.location.href = href; window.location.href = href;
} }
} }
) );
} }
} }
) );
} }
function printBomReports(parts, options={}) { function printBomReports(parts) {
/** /**
* Print BOM reports for the provided part(s) * Print BOM reports for the provided part(s)
*/ */
@ -245,11 +265,11 @@ function printBomReports(parts, options={}) {
); );
} }
} }
) );
} }
function printPurchaseOrderReports(orders, options={}) { function printPurchaseOrderReports(orders) {
/** /**
* Print PO reports for the provided purchase order(s) * Print PO reports for the provided purchase order(s)
*/ */
@ -296,14 +316,14 @@ function printPurchaseOrderReports(orders, options={}) {
window.location.href = href; window.location.href = href;
} }
} }
) );
} }
} }
) );
} }
function printSalesOrderReports(orders, options={}) { function printSalesOrderReports(orders) {
/** /**
* Print SO reports for the provided purchase order(s) * Print SO reports for the provided purchase order(s)
*/ */
@ -350,8 +370,8 @@ function printSalesOrderReports(orders, options={}) {
window.location.href = href; window.location.href = href;
} }
} }
) );
} }
} }
) );
} }

View File

@ -2,6 +2,63 @@
{% load inventree_extras %} {% load inventree_extras %}
{% load status_codes %} {% load status_codes %}
/* globals
attachSelect,
attachToggle,
blankImage,
enableField,
clearField,
clearFieldOptions,
closeModal,
constructFormBody,
constructNumberInput,
createNewModal,
getFormFieldValue,
global_settings,
handleFormErrors,
imageHoverIcon,
inventreeDelete,
inventreeGet,
inventreePut,
launchModalForm,
linkButtonsToSelection,
loadTableFilters,
makeIconBadge,
makeIconButton,
makeOptionsList,
makePartIcons,
modalEnable,
modalSetContent,
modalSetTitle,
modalSubmit,
moment,
openModal,
printStockItemLabels,
printTestReports,
renderLink,
reloadFieldOptions,
scanItemsIntoLocation,
showAlertDialog,
setFieldValue,
setupFilterList,
showApiError,
stockStatusDisplay,
*/
/* exported
createNewStockItem,
exportStock,
loadInstalledInTable,
loadStockLocationTable,
loadStockTable,
loadStockTestResultsTable,
loadStockTrackingTable,
loadTableFilters,
locationFields,
removeStockRow,
stockStatusCodes,
*/
function locationFields() { function locationFields() {
return { return {
@ -23,7 +80,7 @@ function stockStatusCodes() {
{% for code in StockStatus.list %} {% for code in StockStatus.list %}
{ {
key: {{ code.key }}, key: {{ code.key }},
text: "{{ code.value }}", text: '{{ code.value }}',
}, },
{% endfor %} {% endfor %}
]; ];
@ -45,11 +102,23 @@ function exportStock(params={}) {
type: 'choice', type: 'choice',
value: 'csv', value: 'csv',
choices: [ choices: [
{ value: 'csv', display_name: 'CSV' }, {
{ value: 'tsv', display_name: 'TSV' }, value: 'csv',
{ value: 'xls', display_name: 'XLS' }, display_name: 'CSV',
{ value: 'xlsx', display_name: 'XLSX' }, },
] {
value: 'tsv',
display_name: 'TSV',
},
{
value: 'xls',
display_name: 'XLS',
},
{
value: 'xlsx',
display_name: 'XLSX',
},
],
}, },
sublocations: { sublocations: {
label: '{% trans "Include Sublocations" %}', label: '{% trans "Include Sublocations" %}',
@ -208,8 +277,8 @@ function adjustStock(action, items, options={}) {
read_only: readonly, read_only: readonly,
title: readonly ? '{% trans "Quantity cannot be adjusted for serialized stock" %}' : '{% trans "Specify stock quantity" %}', title: readonly ? '{% trans "Quantity cannot be adjusted for serialized stock" %}' : '{% trans "Specify stock quantity" %}',
} }
) );
}; }
var buttons = `<div class='btn-group float-right' role='group'>`; var buttons = `<div class='btn-group float-right' role='group'>`;
@ -283,7 +352,7 @@ function adjustStock(action, items, options={}) {
confirm: true, confirm: true,
confirmMessage: '{% trans "Confirm stock adjustment" %}', confirmMessage: '{% trans "Confirm stock adjustment" %}',
modal: modal, modal: modal,
onSubmit: function(fields, opts) { onSubmit: function(fields) {
// "Delete" action gets handled differently // "Delete" action gets handled differently
if (action == 'delete') { if (action == 'delete') {
@ -295,7 +364,7 @@ function adjustStock(action, items, options={}) {
inventreeDelete( inventreeDelete(
`/api/stock/${item.pk}/`, `/api/stock/${item.pk}/`,
) )
) );
}); });
// Wait for *all* the requests to complete // Wait for *all* the requests to complete
@ -327,7 +396,7 @@ function adjustStock(action, items, options={}) {
}); });
// Add in extra field data // Add in extra field data
for (field_name in extraFields) { for (var field_name in extraFields) {
data[field_name] = getFormFieldValue( data[field_name] = getFormFieldValue(
field_name, field_name,
fields[field_name], fields[field_name],
@ -342,7 +411,7 @@ function adjustStock(action, items, options={}) {
data, data,
{ {
method: 'POST', method: 'POST',
success: function(response, status) { success: function() {
// Destroy the modal window // Destroy the modal window
$(modal).modal('hide'); $(modal).modal('hide');
@ -362,7 +431,7 @@ function adjustStock(action, items, options={}) {
{ {
modal: modal, modal: modal,
} }
) );
break; break;
default: default:
@ -446,15 +515,15 @@ function loadStockTestResultsTable(table, options) {
html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}'); html += makeIconButton('fa-trash-alt icon-red', 'button-test-delete', pk, '{% trans "Delete test result" %}');
} }
html += "</div>"; html += '</div>';
return html; return html;
} }
var parent_node = "parent node"; var parent_node = 'parent node';
table.inventreeTable({ table.inventreeTable({
url: "{% url 'api-part-test-template-list' %}", url: '{% url "api-part-test-template-list" %}',
method: 'get', method: 'get',
name: 'testresult', name: 'testresult',
treeEnable: true, treeEnable: true,
@ -473,7 +542,7 @@ function loadStockTestResultsTable(table, options) {
table.treegrid({ table.treegrid({
treeColumn: 0, treeColumn: 0,
}); });
table.treegrid("collapseAll"); table.treegrid('collapseAll');
}, },
columns: [ columns: [
{ {
@ -539,12 +608,12 @@ function loadStockTestResultsTable(table, options) {
stock_item: options.stock_item, stock_item: options.stock_item,
user_detail: true, user_detail: true,
attachment_detail: true, attachment_detail: true,
ordering: "-date", ordering: '-date',
}, },
{ {
success: function(data) { success: function(data) {
// Iterate through the returned test data // Iterate through the returned test data
data.forEach(function(item, index) { data.forEach(function(item) {
var match = false; var match = false;
var override = false; var override = false;
@ -589,13 +658,12 @@ function loadStockTestResultsTable(table, options) {
}); });
// Push data back into the table // Push data back into the table
table.bootstrapTable("load", tableData); table.bootstrapTable('load', tableData);
} }
} }
) );
} }
}); });
} }
@ -671,11 +739,11 @@ function loadStockTable(table, options) {
var params = options.params || {}; var params = options.params || {};
var filterListElement = options.filterList || "#filter-list-stock"; var filterListElement = options.filterList || '#filter-list-stock';
var filters = {}; var filters = {};
var filterKey = options.filterKey || options.name || "stock"; var filterKey = options.filterKey || options.name || 'stock';
if (!options.disableFilters) { if (!options.disableFilters) {
filters = loadTableFilters(filterKey); filters = loadTableFilters(filterKey);
@ -683,8 +751,8 @@ function loadStockTable(table, options) {
var original = {}; var original = {};
for (var key in params) { for (var k in params) {
original[key] = params[key]; original[k] = params[k];
} }
setupFilterList(filterKey, table, filterListElement); setupFilterList(filterKey, table, filterListElement);
@ -700,10 +768,13 @@ function loadStockTable(table, options) {
grouping = options.grouping; grouping = options.grouping;
} }
var col = null;
// Explicitly disable part grouping functionality // Explicitly disable part grouping functionality
// Might be able to add this in later on, // Might be able to add this in later on,
// but there is a bug which makes this crash if paginating on the server side. // but there is a bug which makes this crash if paginating on the server side.
// Ref: https://github.com/wenzhixin/bootstrap-table/issues/3250 // Ref: https://github.com/wenzhixin/bootstrap-table/issues/3250
// eslint-disable-next-line no-unused-vars
grouping = false; grouping = false;
var columns = [ var columns = [
@ -727,22 +798,24 @@ function loadStockTable(table, options) {
sortName: 'part__name', sortName: 'part__name',
visible: params['part_detail'], visible: params['part_detail'],
switchable: params['part_detail'], switchable: params['part_detail'],
formatter: function(value, row, index, field) { formatter: function(value, row) {
var url = `/stock/item/${row.pk}/`; var url = `/stock/item/${row.pk}/`;
var thumb = row.part_detail.thumbnail; var thumb = row.part_detail.thumbnail;
var name = row.part_detail.full_name; var name = row.part_detail.full_name;
html = imageHoverIcon(thumb) + renderLink(name, url); var html = imageHoverIcon(thumb) + renderLink(name, url);
html += makePartIcons(row.part_detail); html += makePartIcons(row.part_detail);
return html; return html;
} }
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
@ -751,13 +824,15 @@ function loadStockTable(table, options) {
sortName: 'part__IPN', sortName: 'part__IPN',
visible: params['part_detail'], visible: params['part_detail'],
switchable: params['part_detail'], switchable: params['part_detail'],
formatter: function(value, row, index, field) { formatter: function(value, row) {
return row.part_detail.IPN; return row.part_detail.IPN;
}, },
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
columns.push({ columns.push({
@ -765,7 +840,7 @@ function loadStockTable(table, options) {
title: '{% trans "Description" %}', title: '{% trans "Description" %}',
visible: params['part_detail'], visible: params['part_detail'],
switchable: params['part_detail'], switchable: params['part_detail'],
formatter: function(value, row, index, field) { formatter: function(value, row) {
return row.part_detail.description; return row.part_detail.description;
} }
}); });
@ -773,7 +848,7 @@ function loadStockTable(table, options) {
col = { col = {
field: 'quantity', field: 'quantity',
title: '{% trans "Stock" %}', title: '{% trans "Stock" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var val = parseFloat(value); var val = parseFloat(value);
@ -817,13 +892,9 @@ function loadStockTable(table, options) {
// REJECTED // REJECTED
if (row.status == {{ StockStatus.REJECTED }}) { if (row.status == {{ StockStatus.REJECTED }}) {
html += makeIconBadge('fa-times-circle icon-red', '{% trans "Stock item has been rejected" %}'); html += makeIconBadge('fa-times-circle icon-red', '{% trans "Stock item has been rejected" %}');
} } else if (row.status == {{ StockStatus.LOST }}) {
// LOST
else if (row.status == {{ StockStatus.LOST }}) {
html += makeIconBadge('fa-question-circle', '{% trans "Stock item is lost" %}'); html += makeIconBadge('fa-question-circle', '{% trans "Stock item is lost" %}');
} } else if (row.status == {{ StockStatus.DESTROYED }}) {
else if (row.status == {{ StockStatus.DESTROYED }}) {
html += makeIconBadge('fa-skull-crossbones', '{% trans "Stock item is destroyed" %}'); html += makeIconBadge('fa-skull-crossbones', '{% trans "Stock item is destroyed" %}');
} }
@ -834,51 +905,61 @@ function loadStockTable(table, options) {
return html; return html;
} }
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'status', field: 'status',
title: '{% trans "Status" %}', title: '{% trans "Status" %}',
formatter: function(value, row, index, field) { formatter: function(value) {
return stockStatusDisplay(value); return stockStatusDisplay(value);
}, },
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'batch', field: 'batch',
title: '{% trans "Batch" %}', title: '{% trans "Batch" %}',
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'location_detail.pathstring', field: 'location_detail.pathstring',
title: '{% trans "Location" %}', title: '{% trans "Location" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
return locationDetail(row); return locationDetail(row);
} }
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'stocktake_date', field: 'stocktake_date',
title: '{% trans "Stocktake" %}', title: '{% trans "Stocktake" %}',
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
@ -887,18 +968,22 @@ function loadStockTable(table, options) {
visible: global_settings.STOCK_ENABLE_EXPIRY, visible: global_settings.STOCK_ENABLE_EXPIRY,
switchable: global_settings.STOCK_ENABLE_EXPIRY, switchable: global_settings.STOCK_ENABLE_EXPIRY,
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
col = { col = {
field: 'updated', field: 'updated',
title: '{% trans "Last Updated" %}', title: '{% trans "Last Updated" %}',
}; };
if (!options.params.ordering) { if (!options.params.ordering) {
col['sortable'] = true; col['sortable'] = true;
}; }
columns.push(col); columns.push(col);
columns.push({ columns.push({
@ -963,7 +1048,7 @@ function loadStockTable(table, options) {
if (!options.params.ordering) { if (!options.params.ordering) {
col.sortable = true; col.sortable = true;
col.sortName = 'purchase_price'; col.sortName = 'purchase_price';
}; }
columns.push(col); columns.push(col);
@ -981,7 +1066,7 @@ function loadStockTable(table, options) {
formatNoMatches: function() { formatNoMatches: function() {
return '{% trans "No stock items matching query" %}'; return '{% trans "No stock items matching query" %}';
}, },
url: options.url || "{% url 'api-stock-list' %}", url: options.url || '{% url "api-stock-list" %}',
queryParams: filters, queryParams: filters,
sidePagination: 'server', sidePagination: 'server',
name: 'stock', name: 'stock',
@ -1048,7 +1133,7 @@ function loadStockTable(table, options) {
stock = +stock.toFixed(5); stock = +stock.toFixed(5);
return stock + " (" + items + " items)"; return `${stock} (${items} {% trans "items" %})`;
} else if (field == 'status') { } else if (field == 'status') {
var statii = []; var statii = [];
@ -1170,7 +1255,7 @@ function loadStockTable(table, options) {
function stockAdjustment(action) { function stockAdjustment(action) {
var items = $(table).bootstrapTable("getSelections"); var items = $(table).bootstrapTable('getSelections');
adjustStock(action, items, { adjustStock(action, items, {
onSuccess: function() { onSuccess: function() {
@ -1203,7 +1288,7 @@ function loadStockTable(table, options) {
}); });
printTestReports(items); printTestReports(items);
}) });
if (global_settings.BARCODE_ENABLE) { if (global_settings.BARCODE_ENABLE) {
$('#multi-item-barcode-scan-into-location').click(function() { $('#multi-item-barcode-scan-into-location').click(function() {
@ -1213,7 +1298,7 @@ function loadStockTable(table, options) {
selections.forEach(function(item) { selections.forEach(function(item) {
items.push(item.pk); items.push(item.pk);
}) });
scanItemsIntoLocation(items); scanItemsIntoLocation(items);
}); });
@ -1231,12 +1316,12 @@ function loadStockTable(table, options) {
stockAdjustment('add'); stockAdjustment('add');
}); });
$("#multi-item-move").click(function() { $('#multi-item-move').click(function() {
stockAdjustment('move'); stockAdjustment('move');
}); });
$("#multi-item-order").click(function() { $('#multi-item-order').click(function() {
var selections = $(table).bootstrapTable("getSelections"); var selections = $(table).bootstrapTable('getSelections');
var stock = []; var stock = [];
@ -1244,14 +1329,14 @@ function loadStockTable(table, options) {
stock.push(item.pk); stock.push(item.pk);
}); });
launchModalForm("/order/purchase-order/order-parts/", { launchModalForm('/order/purchase-order/order-parts/', {
data: { data: {
stock: stock, stock: stock,
}, },
}); });
}); });
$("#multi-item-set-status").click(function() { $('#multi-item-set-status').click(function() {
// Select and set the STATUS field for selected stock items // Select and set the STATUS field for selected stock items
var selections = $(table).bootstrapTable('getSelections'); var selections = $(table).bootstrapTable('getSelections');
@ -1335,11 +1420,11 @@ function loadStockTable(table, options) {
$.when.apply($, requests).done(function() { $.when.apply($, requests).done(function() {
$(table).bootstrapTable('refresh'); $(table).bootstrapTable('refresh');
}); });
}) });
}); });
$("#multi-item-delete").click(function() { $('#multi-item-delete').click(function() {
var selections = $(table).bootstrapTable("getSelections"); var selections = $(table).bootstrapTable('getSelections');
var stock = []; var stock = [];
@ -1368,8 +1453,8 @@ function loadStockLocationTable(table, options) {
var original = {}; var original = {};
for (var key in params) { for (var k in params) {
original[key] = params[key]; original[k] = params[k];
} }
setupFilterList(filterKey, table, filterListElement); setupFilterList(filterKey, table, filterListElement);
@ -1437,7 +1522,7 @@ function loadStockTrackingTable(table, options) {
field: 'date', field: 'date',
title: '{% trans "Date" %}', title: '{% trans "Date" %}',
sortable: true, sortable: true,
formatter: function(value, row, index, field) { formatter: function(value) {
var m = moment(value); var m = moment(value);
if (m.isValid()) { if (m.isValid()) {
@ -1453,11 +1538,11 @@ function loadStockTrackingTable(table, options) {
cols.push({ cols.push({
field: 'label', field: 'label',
title: '{% trans "Description" %}', title: '{% trans "Description" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
var html = "<b>" + value + "</b>"; var html = '<b>' + value + '</b>';
if (row.notes) { if (row.notes) {
html += "<br><i>" + row.notes + "</i>"; html += '<br><i>' + row.notes + '</i>';
} }
return html; return html;
@ -1468,7 +1553,7 @@ function loadStockTrackingTable(table, options) {
cols.push({ cols.push({
field: 'deltas', field: 'deltas',
title: '{% trans "Details" %}', title: '{% trans "Details" %}',
formatter: function(details, row, index, field) { formatter: function(details, row) {
var html = `<table class='table table-condensed' id='tracking-table-${row.pk}'>`; var html = `<table class='table table-condensed' id='tracking-table-${row.pk}'>`;
if (!details) { if (!details) {
@ -1603,14 +1688,11 @@ function loadStockTrackingTable(table, options) {
cols.push({ cols.push({
field: 'user', field: 'user',
title: '{% trans "User" %}', title: '{% trans "User" %}',
formatter: function(value, row, index, field) { formatter: function(value, row) {
if (value) if (value) {
{
// TODO - Format the user's first and last names // TODO - Format the user's first and last names
return row.user_detail.username; return row.user_detail.username;
} } else {
else
{
return `<i>{% trans "No user information" %}</i>`; return `<i>{% trans "No user information" %}</i>`;
} }
} }
@ -1692,7 +1774,7 @@ function createNewStockItem(options) {
reloadFieldOptions( reloadFieldOptions(
'supplier_part', 'supplier_part',
{ {
url: "{% url 'api-supplier-part-list' %}", url: '{% url "api-supplier-part-list" %}',
params: { params: {
part: value, part: value,
pretty: true, pretty: true,
@ -1723,7 +1805,7 @@ function createNewStockItem(options) {
} else { } else {
var expiry = moment().add(response.default_expiry, 'days'); var expiry = moment().add(response.default_expiry, 'days');
setFieldValue('expiry_date', expiry.format("YYYY-MM-DD")); setFieldValue('expiry_date', expiry.format('YYYY-MM-DD'));
} }
} }
} }
@ -1732,7 +1814,7 @@ function createNewStockItem(options) {
}, },
]; ];
launchModalForm("{% url 'stock-item-create' %}", options); launchModalForm('{% url "stock-item-create" %}', options);
} }
@ -1741,28 +1823,8 @@ function loadInstalledInTable(table, options) {
* Display a table showing the stock items which are installed in this stock item. * Display a table showing the stock items which are installed in this stock item.
*/ */
function updateCallbacks() {
// Setup callback functions when buttons are pressed
table.find('.button-install').click(function() {
var pk = $(this).attr('pk');
launchModalForm(
`/stock/item/${options.stock_item}/install/`,
{
data: {
part: pk,
},
success: function() {
// Refresh entire table!
table.bootstrapTable('refresh');
}
}
);
});
}
table.inventreeTable({ table.inventreeTable({
url: "{% url 'api-stock-list' %}", url: '{% url "api-stock-list" %}',
queryParams: { queryParams: {
installed_in: options.stock_item, installed_in: options.stock_item,
part_detail: true, part_detail: true,
@ -1802,7 +1864,7 @@ function loadInstalledInTable(table, options) {
{ {
field: 'status', field: 'status',
title: '{% trans "Status" %}', title: '{% trans "Status" %}',
formatter: function(value, row) { formatter: function(value) {
return stockStatusDisplay(value); return stockStatusDisplay(value);
} }
}, },
@ -1841,7 +1903,7 @@ function loadInstalledInTable(table, options) {
table.bootstrapTable('refresh'); table.bootstrapTable('refresh');
} }
} }
) );
}); });
} }
}); });

View File

@ -8,13 +8,26 @@
{% include "status_codes.html" with label='purchaseOrder' options=PurchaseOrderStatus.list %} {% include "status_codes.html" with label='purchaseOrder' options=PurchaseOrderStatus.list %}
{% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %} {% include "status_codes.html" with label='salesOrder' options=SalesOrderStatus.list %}
/* globals
global_settings
*/
/* exported
buildStatusDisplay,
getAvailableTableFilters,
purchaseOrderStatusDisplay,
salesOrderStatusDisplay,
stockHistoryStatusDisplay,
stockStatusDisplay,
*/
function getAvailableTableFilters(tableKey) { function getAvailableTableFilters(tableKey) {
tableKey = tableKey.toLowerCase(); tableKey = tableKey.toLowerCase();
// Filters for "variant" table // Filters for "variant" table
if (tableKey == "variants") { if (tableKey == 'variants') {
return { return {
active: { active: {
type: 'bool', type: 'bool',
@ -36,11 +49,11 @@ function getAvailableTableFilters(tableKey) {
} }
// Filters for Bill of Materials table // Filters for Bill of Materials table
if (tableKey == "bom") { if (tableKey == 'bom') {
return { return {
sub_part_trackable: { sub_part_trackable: {
type: 'bool', type: 'bool',
title: '{% trans "Trackable Part" %}' title: '{% trans "Trackable Part" %}',
}, },
sub_part_assembly: { sub_part_assembly: {
type: 'bool', type: 'bool',
@ -57,7 +70,7 @@ function getAvailableTableFilters(tableKey) {
allow_variants: { allow_variants: {
type: 'bool', type: 'bool',
title: '{% trans "Allow Variant Stock" %}', title: '{% trans "Allow Variant Stock" %}',
} },
}; };
} }
@ -72,29 +85,29 @@ function getAvailableTableFilters(tableKey) {
} }
// Filters for "stock location" table // Filters for "stock location" table
if (tableKey == "location") { if (tableKey == 'location') {
return { return {
cascade: { cascade: {
type: 'bool', type: 'bool',
title: '{% trans "Include sublocations" %}', title: '{% trans "Include sublocations" %}',
description: '{% trans "Include locations" %}', description: '{% trans "Include locations" %}',
} },
}; };
} }
// Filters for "part category" table // Filters for "part category" table
if (tableKey == "category") { if (tableKey == 'category') {
return { return {
cascade: { cascade: {
type: 'bool', type: 'bool',
title: '{% trans "Include subcategories" %}', title: '{% trans "Include subcategories" %}',
description: '{% trans "Include subcategories" %}', description: '{% trans "Include subcategories" %}',
} },
}; };
} }
// Filters for the "customer stock" table (really a subset of "stock") // Filters for the "customer stock" table (really a subset of "stock")
if (tableKey == "customerstock") { if (tableKey == 'customerstock') {
return { return {
serialized: { serialized: {
type: 'bool', type: 'bool',
@ -102,7 +115,7 @@ function getAvailableTableFilters(tableKey) {
}, },
serial_gte: { serial_gte: {
title: '{% trans "Serial number GTE" %}', title: '{% trans "Serial number GTE" %}',
description: '{% trans "Serial number greater than or equal to" %}' description: '{% trans "Serial number greater than or equal to" %}',
}, },
serial_lte: { serial_lte: {
title: '{% trans "Serial number LTE" %}', title: '{% trans "Serial number LTE" %}',
@ -110,7 +123,7 @@ function getAvailableTableFilters(tableKey) {
}, },
serial: { serial: {
title: '{% trans "Serial number" %}', title: '{% trans "Serial number" %}',
description: '{% trans "Serial number" %}' description: '{% trans "Serial number" %}',
}, },
batch: { batch: {
title: '{% trans "Batch" %}', title: '{% trans "Batch" %}',
@ -179,11 +192,11 @@ function getAvailableTableFilters(tableKey) {
}, },
serial: { serial: {
title: '{% trans "Serial number" %}', title: '{% trans "Serial number" %}',
description: '{% trans "Serial number" %}' description: '{% trans "Serial number" %}',
}, },
serial_gte: { serial_gte: {
title: '{% trans "Serial number GTE" %}', title: '{% trans "Serial number GTE" %}',
description: '{% trans "Serial number greater than or equal to" %}' description: '{% trans "Serial number greater than or equal to" %}',
}, },
serial_lte: { serial_lte: {
title: '{% trans "Serial number LTE" %}', title: '{% trans "Serial number LTE" %}',
@ -239,7 +252,7 @@ function getAvailableTableFilters(tableKey) {
required: { required: {
type: 'bool', type: 'bool',
title: '{% trans "Required" %}', title: '{% trans "Required" %}',
} },
}; };
} }
@ -262,7 +275,7 @@ function getAvailableTableFilters(tableKey) {
} }
// Filters for the "Order" table // Filters for the "Order" table
if (tableKey == "purchaseorder") { if (tableKey == 'purchaseorder') {
return { return {
status: { status: {
@ -280,7 +293,7 @@ function getAvailableTableFilters(tableKey) {
}; };
} }
if (tableKey == "salesorder") { if (tableKey == 'salesorder') {
return { return {
status: { status: {
title: '{% trans "Order status" %}', title: '{% trans "Order status" %}',
@ -302,12 +315,12 @@ function getAvailableTableFilters(tableKey) {
active: { active: {
type: 'bool', type: 'bool',
title: '{% trans "Active parts" %}', title: '{% trans "Active parts" %}',
} },
}; };
} }
// Filters for the "Parts" table // Filters for the "Parts" table
if (tableKey == "parts") { if (tableKey == 'parts') {
return { return {
cascade: { cascade: {
type: 'bool', type: 'bool',
@ -330,7 +343,7 @@ function getAvailableTableFilters(tableKey) {
}, },
has_stock: { has_stock: {
type: 'bool', type: 'bool',
title: '{% trans "Stock available" %}' title: '{% trans "Stock available" %}',
}, },
low_stock: { low_stock: {
type: 'bool', type: 'bool',

View File

@ -1,21 +1,33 @@
{% load i18n %} {% load i18n %}
/* global
inventreeLoad,
inventreeSave,
*/
/* exported
customGroupSorter,
reloadtable,
renderLink,
reloadTableFilters,
*/
/**
* Reload a named table
* @param table
*/
function reloadtable(table) { function reloadtable(table) {
$(table).bootstrapTable('refresh'); $(table).bootstrapTable('refresh');
} }
function editButton(url, text='Edit') { /**
return "<button class='btn btn-success edit-button btn-sm' type='button' url='" + url + "'>" + text + "</button>"; * Render a URL for display
} * @param {String} text
* @param {String} url
* @param {object} options
function deleteButton(url, text='Delete') { * @returns link text
return "<button class='btn btn-danger delete-button btn-sm' type='button' url='" + url + "'>" + text + "</button>"; */
}
function renderLink(text, url, options={}) { function renderLink(text, url, options={}) {
if (url === null || url === undefined || url === '') { if (url === null || url === undefined || url === '') {
return text; return text;
@ -23,8 +35,6 @@ function renderLink(text, url, options={}) {
var max_length = options.max_length || -1; var max_length = options.max_length || -1;
var remove_http = options.remove_http || false;
// Shorten the displayed length if required // Shorten the displayed length if required
if ((max_length > 0) && (text.length > max_length)) { if ((max_length > 0) && (text.length > max_length)) {
var slice_length = (max_length - 3) / 2; var slice_length = (max_length - 3) / 2;
@ -59,12 +69,17 @@ function linkButtonsToSelection(table, buttons) {
enableButtons(buttons, table.bootstrapTable('getSelections').length > 0); enableButtons(buttons, table.bootstrapTable('getSelections').length > 0);
// Add a callback // Add a callback
table.on('check.bs.table uncheck.bs.table check-some.bs.table uncheck-some.bs.table check-all.bs.table uncheck-all.bs.table', function(row) { table.on('check.bs.table uncheck.bs.table check-some.bs.table uncheck-some.bs.table check-all.bs.table uncheck-all.bs.table', function() {
enableButtons(buttons, table.bootstrapTable('getSelections').length > 0); enableButtons(buttons, table.bootstrapTable('getSelections').length > 0);
}); });
} }
/**
* Returns true if the input looks like a valid number
* @param {String} n
* @returns
*/
function isNumeric(n) { function isNumeric(n) {
return !isNaN(parseFloat(n)) && isFinite(n); return !isNaN(parseFloat(n)) && isFinite(n);
} }
@ -88,8 +103,8 @@ function reloadTableFilters(table, filters) {
// Construct a new list of filters to use for the query // Construct a new list of filters to use for the query
var params = {}; var params = {};
for (var key in filters) { for (var k in filters) {
params[key] = filters[key]; params[k] = filters[k];
} }
// Original query params will override // Original query params will override
@ -136,7 +151,6 @@ function convertQueryParameters(params, filters) {
var ordering = params['sort'] || null; var ordering = params['sort'] || null;
if (ordering) { if (ordering) {
if (order == 'desc') { if (order == 'desc') {
ordering = `-${ordering}`; ordering = `-${ordering}`;
} }
@ -220,7 +234,7 @@ $.fn.inventreeTable = function(options) {
}; };
// Callback when a column is changed // Callback when a column is changed
options.onColumnSwitch = function(field, checked) { options.onColumnSwitch = function() {
var columns = table.bootstrapTable('getVisibleColumns'); var columns = table.bootstrapTable('getVisibleColumns');
@ -239,7 +253,7 @@ $.fn.inventreeTable = function(options) {
// If a set of visible columns has been saved, load! // If a set of visible columns has been saved, load!
if (visibleColumns) { if (visibleColumns) {
var columns = visibleColumns.split(","); var columns = visibleColumns.split(',');
// Which columns are currently visible? // Which columns are currently visible?
var visible = table.bootstrapTable('getVisibleColumns'); var visible = table.bootstrapTable('getVisibleColumns');
@ -253,7 +267,7 @@ $.fn.inventreeTable = function(options) {
} }
}); });
} else { } else {
console.log('Could not get list of visible columns!'); console.log(`Could not get list of visible columns for column '${tableName}'`);
} }
} }
@ -261,7 +275,8 @@ $.fn.inventreeTable = function(options) {
if (options.buttons) { if (options.buttons) {
linkButtonsToSelection(table, options.buttons); linkButtonsToSelection(table, options.buttons);
} }
} };
function customGroupSorter(sortName, sortOrder, sortData) { function customGroupSorter(sortName, sortOrder, sortData) {
@ -367,7 +382,7 @@ function customGroupSorter(sortName, sortOrder, sortData) {
}, },
formatAllRows: function() { formatAllRows: function() {
return '{% trans "All" %}'; return '{% trans "All" %}';
} },
}; };
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['en-US-custom']); $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['en-US-custom']);

View File

@ -1,12 +1,13 @@
/* /*
* Status codes for the {{ label }} model. * Status codes for the {{ label }} model.
*/ */
var {{ label }}Codes = { const {{ label }}Codes = {
{% for opt in options %}'{{ opt.key }}': { {% for opt in options %}'{{ opt.key }}': {
key: '{{ opt.key }}', key: '{{ opt.key }}',
value: '{{ opt.value }}',{% if opt.color %} value: '{{ opt.value }}',{% if opt.color %}
label: 'label-{{ opt.color }}',{% endif %} label: 'label-{{ opt.color }}',{% endif %}
},{% endfor %} },
{% endfor %}
}; };
/* /*

View File

@ -457,3 +457,12 @@ def server(c, address="127.0.0.1:8000"):
""" """
manage(c, "runserver {address}".format(address=address), pty=True) manage(c, "runserver {address}".format(address=address), pty=True)
@task
def render_js_files(c):
"""
Render templated javascript files (used for static testing).
"""
manage(c, "test InvenTree.ci_render_js")