2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 13:05:42 +00:00

Merge branch 'master' of https://github.com/inventree/InvenTree into matmair/issue2694

This commit is contained in:
Matthias
2022-03-27 23:19:41 +02:00
25 changed files with 489 additions and 259 deletions

View File

@ -10,7 +10,7 @@
{% endblock %}
{% block actions %}
<div class='btn btn-secondary' type='button' id='mark-all' title='{% trans "Mark all as read" %}'>
<div class='btn btn-outline-secondary' type='button' id='mark-all' title='{% trans "Mark all as read" %}'>
<span class='fa fa-bookmark'></span> {% trans "Mark all as read" %}
</div>
<div class='btn btn-secondary' type='button' id='inbox-refresh' title='{% trans "Refresh Pending Notifications" %}'>

View File

@ -126,8 +126,12 @@ $("#mark-all").on('click', function() {
{
read: false,
},
{
success: function(response) {
updateNotificationTables();
}
}
);
updateNotificationTables();
});
loadNotificationTable("#history-table", {

View File

@ -22,6 +22,7 @@
{% include "InvenTree/settings/user_settings.html" %}
{% include "InvenTree/settings/user_homepage.html" %}
{% include "InvenTree/settings/user_search.html" %}
{% include "InvenTree/settings/user_notifications.html" %}
{% include "InvenTree/settings/user_labels.html" %}
{% include "InvenTree/settings/user_reports.html" %}
{% include "InvenTree/settings/user_display.html" %}

View File

@ -14,6 +14,8 @@
{% include "sidebar_item.html" with label='user-home' text=text icon="fa-home" %}
{% trans "Search Settings" as text %}
{% include "sidebar_item.html" with label='user-search' text=text icon="fa-search" %}
{% trans "Notifications" as text %}
{% include "sidebar_item.html" with label='user-notifications' text=text icon="fa-bell" %}
{% trans "Label Printing" as text %}
{% include "sidebar_item.html" with label='user-labels' text=text icon="fa-tag" %}
{% trans "Reporting" as text %}

View File

@ -0,0 +1,20 @@
{% extends "panel.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block label %}user-notifications{% endblock label %}
{% block heading %}{% trans "Notification Settings" %}{% endblock heading %}
{% block content %}
<div class='row'>
<table class='table table-striped table-condensed'>
<tbody>
{% include "InvenTree/settings/setting.html" with key="NOTIFICATION_SEND_EMAILS" icon='fa-envelope' user_setting=True %}
</tbody>
</table>
</div>
{% endblock content %}

View File

@ -213,7 +213,7 @@ function createBuildOutput(build_id, options) {
success: function(data) {
if (data.next) {
fields.serial_numbers.placeholder = `{% trans "Next available serial number" %}: ${data.next}`;
} else {
} else if (data.latest) {
fields.serial_numbers.placeholder = `{% trans "Latest serial number" %}: ${data.latest}`;
}
},
@ -1025,9 +1025,10 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
}
// Store the required quantity in the row data
row.required = quantity;
// Prevent weird rounding issues
row.required = parseFloat(quantity.toFixed(15));
return quantity;
return row.required;
}
function sumAllocations(row) {
@ -1043,9 +1044,9 @@ function loadBuildOutputAllocationTable(buildInfo, output, options={}) {
quantity += item.quantity;
});
row.allocated = quantity;
row.allocated = parseFloat(quantity.toFixed(15));
return quantity;
return row.allocated;
}
function setupCallbacks() {
@ -1642,6 +1643,9 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
remaining = 0;
}
// Ensure the quantity sent to the form field is correctly formatted
remaining = parseFloat(remaining.toFixed(15));
// We only care about entries which are not yet fully allocated
if (remaining > 0) {
table_entries += renderBomItemRow(bom_item, remaining);

View File

@ -14,11 +14,41 @@
*/
/* exported
printLabels,
printPartLabels,
printStockItemLabels,
printStockLocationLabels,
*/
/*
* Perform the "print" action.
*/
function printLabels(url, plugin=null) {
if (plugin) {
// If a plugin is provided, do not redirect the browser.
// Instead, perform an API request and display a message
url = url + `plugin=${plugin}`;
inventreeGet(url, {}, {
success: function(response) {
showMessage(
'{% trans "Labels sent to printer" %}',
{
style: 'success',
}
);
}
});
} else {
window.location.href = url;
}
}
function printStockItemLabels(items) {
/**
* Print stock item labels for the given stock items
@ -57,14 +87,17 @@ function printStockItemLabels(items) {
response,
items,
{
success: function(pk) {
success: function(data) {
var pk = data.label;
var href = `/api/label/stock/${pk}/print/?`;
items.forEach(function(item) {
href += `items[]=${item}&`;
});
window.location.href = href;
printLabels(href, data.plugin);
}
}
);
@ -73,6 +106,7 @@ function printStockItemLabels(items) {
);
}
function printStockLocationLabels(locations) {
if (locations.length == 0) {
@ -107,14 +141,17 @@ function printStockLocationLabels(locations) {
response,
locations,
{
success: function(pk) {
success: function(data) {
var pk = data.label;
var href = `/api/label/location/${pk}/print/?`;
locations.forEach(function(location) {
href += `locations[]=${location}&`;
});
window.location.href = href;
printLabels(href, data.plugin);
}
}
);
@ -162,14 +199,17 @@ function printPartLabels(parts) {
response,
parts,
{
success: function(pk) {
var url = `/api/label/part/${pk}/print/?`;
success: function(data) {
var pk = data.label;
var href = `/api/label/part/${pk}/print/?`;
parts.forEach(function(part) {
url += `parts[]=${part}&`;
href += `parts[]=${part}&`;
});
window.location.href = url;
printLabels(href, data.plugin);
}
}
);
@ -188,18 +228,51 @@ function selectLabel(labels, items, options={}) {
* (via AJAX) from the server.
*/
// If only a single label template is provided,
// just run with that!
// Array of available plugins for label printing
var plugins = [];
if (labels.length == 1) {
if (options.success) {
options.success(labels[0].pk);
// Request a list of available label printing plugins from the server
inventreeGet(
`/api/plugin/`,
{},
{
async: false,
success: function(response) {
response.forEach(function(plugin) {
// Look for active plugins which implement the 'labels' mixin class
if (plugin.active && plugin.mixins && plugin.mixins.labels) {
// This plugin supports label printing
plugins.push(plugin);
}
});
}
}
);
return;
var plugin_selection = '';
if (plugins.length > 0) {
plugin_selection =`
<div class='form-group'>
<label class='control-label requiredField' for='id_plugin'>
{% trans "Select Printer" %}
</label>
<div class='controls'>
<select id='id_plugin' class='select form-control' name='plugin'>
<option value='' title='{% trans "Export to PDF" %}'>{% trans "Export to PDF" %}</option>
`;
plugins.forEach(function(plugin) {
plugin_selection += `<option value='${plugin.key}' title='${plugin.meta.human_name}'>${plugin.name} - <small>${plugin.meta.human_name}</small></option>`;
});
plugin_selection += `
</select>
</div>
</div>
`;
}
var modal = options.modal || '#modal-form';
var label_list = makeOptionsList(
@ -233,14 +306,15 @@ function selectLabel(labels, items, options={}) {
<form method='post' action='' class='js-modal-form' enctype='multipart/form-data'>
<div class='form-group'>
<label class='control-label requiredField' for='id_label'>
{% trans "Select Label" %}
{% trans "Select Label Template" %}
</label>
<div class='controls'>
<select id='id_label' class='select form-control name='label'>
<select id='id_label' class='select form-control' name='label'>
${label_list}
</select>
</div>
</div>
${plugin_selection}
</form>`;
openModal({
@ -255,14 +329,17 @@ function selectLabel(labels, items, options={}) {
modalSubmit(modal, function() {
var label = $(modal).find('#id_label');
var pk = label.val();
var label = $(modal).find('#id_label').val();
var plugin = $(modal).find('#id_plugin').val();
closeModal(modal);
if (options.success) {
options.success(pk);
options.success({
// Return the selected label template and plugin
label: label,
plugin: plugin,
});
}
});
}

View File

@ -253,7 +253,7 @@ function openNotificationPanel() {
{
success: function(response) {
if (response.length == 0) {
html = `<p class='text-muted'>{% trans "No unread notifications" %}</p>`;
html = `<p class='text-muted'><em>{% trans "No unread notifications" %}</em><span class='fas fa-check-circle icon-green float-right'></span></p>`;
} else {
// build up items
response.forEach(function(item, index) {

View File

@ -1770,6 +1770,7 @@ function loadStockTable(table, options) {
col = {
field: 'location_detail.pathstring',
title: '{% trans "Location" %}',
sortName: 'location',
formatter: function(value, row) {
return locationDetail(row);
}
@ -1912,172 +1913,8 @@ function loadStockTable(table, options) {
original: original,
showColumns: true,
columns: columns,
{% if False %}
groupByField: options.groupByField || 'part',
groupBy: grouping,
groupByFormatter: function(field, id, data) {
var row = data[0];
if (field == 'part_detail.full_name') {
var html = imageHoverIcon(row.part_detail.thumbnail);
html += row.part_detail.full_name;
html += ` <i>(${data.length} {% trans "items" %})</i>`;
html += makePartIcons(row.part_detail);
return html;
} else if (field == 'part_detail.IPN') {
var ipn = row.part_detail.IPN;
if (ipn) {
return ipn;
} else {
return '-';
}
} else if (field == 'part_detail.description') {
return row.part_detail.description;
} else if (field == 'packaging') {
var packaging = [];
data.forEach(function(item) {
var pkg = item.packaging;
if (!pkg) {
pkg = '-';
}
if (!packaging.includes(pkg)) {
packaging.push(pkg);
}
});
if (packaging.length > 1) {
return "...";
} else if (packaging.length == 1) {
return packaging[0];
} else {
return "-";
}
} else if (field == 'quantity') {
var stock = 0;
var items = 0;
data.forEach(function(item) {
stock += parseFloat(item.quantity);
items += 1;
});
stock = +stock.toFixed(5);
return `${stock} (${items} {% trans "items" %})`;
} else if (field == 'status') {
var statii = [];
data.forEach(function(item) {
var status = String(item.status);
if (!status || status == '') {
status = '-';
}
if (!statii.includes(status)) {
statii.push(status);
}
});
// Multiple status codes
if (statii.length > 1) {
return "...";
} else if (statii.length == 1) {
return stockStatusDisplay(statii[0]);
} else {
return "-";
}
} else if (field == 'batch') {
var batches = [];
data.forEach(function(item) {
var batch = item.batch;
if (!batch || batch == '') {
batch = '-';
}
if (!batches.includes(batch)) {
batches.push(batch);
}
});
if (batches.length > 1) {
return "" + batches.length + " {% trans 'batches' %}";
} else if (batches.length == 1) {
if (batches[0]) {
return batches[0];
} else {
return '-';
}
} else {
return '-';
}
} else if (field == 'location_detail.pathstring') {
/* Determine how many locations */
var locations = [];
data.forEach(function(item) {
var detail = locationDetail(item);
if (!locations.includes(detail)) {
locations.push(detail);
}
});
if (locations.length == 1) {
// Single location, easy!
return locations[0];
} else if (locations.length > 1) {
return "In " + locations.length + " {% trans 'locations' %}";
} else {
return "<i>{% trans 'Undefined location' %}</i>";
}
} else if (field == 'notes') {
var notes = [];
data.forEach(function(item) {
var note = item.notes;
if (!note || note == '') {
note = '-';
}
if (!notes.includes(note)) {
notes.push(note);
}
});
if (notes.length > 1) {
return '...';
} else if (notes.length == 1) {
return notes[0] || '-';
} else {
return '-';
}
} else {
return '';
}
},
{% endif %}
});
/*
if (options.buttons) {
linkButtonsToSelection(table, options.buttons);
}
*/
var buttons = [
'#stock-print-options',
'#stock-options',
@ -2092,7 +1929,6 @@ function loadStockTable(table, options) {
buttons,
);
function stockAdjustment(action) {
var items = $(table).bootstrapTable('getSelections');