2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-06-18 21:15:41 +00:00

Merge branch 'master' of https://github.com/inventree/InvenTree into plugin-2037

This commit is contained in:
Matthias
2021-11-18 11:37:11 +01:00
25 changed files with 409 additions and 93 deletions

View File

@ -12,12 +12,15 @@
{% endblock %}
{% block actions %}
{% inventree_demo_mode as demo %}
{% if not demo %}
<div class='btn btn-primary' type='button' id='edit-user' title='{% trans "Edit User Information" %}'>
<span class='fas fa-user-cog'></span> {% trans "Edit" %}
</div>
<div class='btn btn-primary' type='button' id='edit-password' title='{% trans "Change Password" %}'>
<span class='fas fa-key'></span> {% trans "Set Password" %}
</div>
{% endif %}
{% endblock %}
{% block content %}

View File

@ -1,5 +1,6 @@
{% extends "account/base.html" %}
{% load inventree_extras %}
{% load i18n account socialaccount crispy_forms_tags inventree_extras %}
{% block head_title %}{% trans "Sign In" %}{% endblock %}
@ -10,6 +11,7 @@
{% settings_value 'LOGIN_ENABLE_PWD_FORGOT' as enable_pwd_forgot %}
{% settings_value 'LOGIN_ENABLE_SSO' as enable_sso %}
{% mail_configured as mail_conf %}
{% inventree_demo_mode as demo %}
<h1>{% trans "Sign In" %}</h1>
@ -36,9 +38,16 @@ for a account and sign in below:{% endblocktrans %}</p>
<div class="btn-group float-right" role="group">
<button class="btn btn-success" type="submit">{% trans "Sign In" %}</button>
</div>
{% if mail_conf and enable_pwd_forgot %}
{% if mail_conf and enable_pwd_forgot and not demo %}
<a class="" href="{% url 'account_reset_password' %}"><small>{% trans "Forgot Password?" %}</small></a>
{% endif %}
{% if demo %}
<p>
<h6>
{% trans "InvenTree demo instance" %} - <a href='https://inventree.readthedocs.io/en/latest/demo/'>{% trans "Click here for login details" %}</a>
</h6>
</p>
{% endif %}
</form>
{% if enable_sso %}

View File

@ -86,25 +86,20 @@
</div>
</div>
<main class='col ps-md-2 pt-2 pe-2'>
{% if server_restart_required %}
<div class='notification-area' id='restart-required'>
{% block alerts %}
<div class='notification-area' id='alerts'>
<!-- Div for displayed alerts -->
{% if server_restart_required %}
<div id='alert-restart-server' class='alert alert-danger' role='alert'>
<span class='fas fa-server'></span>
<b>{% trans "Server Restart Required" %}</b>
<small>
<br>
{% trans "A configuration option has been changed which requires a server restart" %}.
<br>
{% trans "Contact your system administrator for further information" %}
{% trans "A configuration option has been changed which requires a server restart" %}. {% trans "Contact your system administrator for further information" %}
</small>
</div>
</div>
{% endif %}
{% block alerts %}
<div class='notification-area' id='alerts'>
<!-- Div for displayed alerts -->
{% endif %}
</div>
{% endblock %}

View File

@ -217,8 +217,10 @@ function showApiError(xhr, url) {
break;
}
message += '<hr>';
message += `URL: ${url}`;
if (url) {
message += '<hr>';
message += `URL: ${url}`;
}
showMessage(title, {
style: 'danger',

View File

@ -16,6 +16,7 @@
/* exported
newPartFromBomWizard,
loadBomTable,
loadUsedInTable,
removeRowFromBomWizard,
removeColFromBomWizard,
*/
@ -311,7 +312,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
}
function loadBomTable(table, options) {
function loadBomTable(table, options={}) {
/* Load a BOM table with some configurable options.
*
* Following options are available:
@ -395,7 +396,7 @@ function loadBomTable(table, options) {
var sub_part = row.sub_part_detail;
html += makePartIcons(row.sub_part_detail);
html += makePartIcons(sub_part);
if (row.substitutes && row.substitutes.length > 0) {
html += makeIconBadge('fa-exchange-alt', '{% trans "Substitutes Available" %}');
@ -672,8 +673,9 @@ function loadBomTable(table, options) {
table.treegrid('collapseAll');
},
error: function() {
error: function(xhr) {
console.log('Error requesting BOM for part=' + part_pk);
showApiError(xhr);
}
}
);
@ -835,3 +837,166 @@ function loadBomTable(table, options) {
});
}
}
/*
* Load a table which shows the assemblies which "require" a certain part.
*
* Arguments:
* - table: The ID string of the table element e.g. '#used-in-table'
* - part_id: The ID (PK) of the part we are interested in
*
* Options:
* -
*
* The following "options" are available.
*/
function loadUsedInTable(table, part_id, options={}) {
var params = options.params || {};
params.uses = part_id;
params.part_detail = true;
params.sub_part_detail = true,
params.show_pricing = global_settings.PART_SHOW_PRICE_IN_BOM;
var filters = {};
if (!options.disableFilters) {
filters = loadTableFilters('usedin');
}
for (var key in params) {
filters[key] = params[key];
}
setupFilterList('usedin', $(table), options.filterTarget || '#filter-list-usedin');
function loadVariantData(row) {
// Load variants information for inherited BOM rows
inventreeGet(
'{% url "api-part-list" %}',
{
assembly: true,
ancestor: row.part,
},
{
success: function(variantData) {
// Iterate through each variant item
for (var jj = 0; jj < variantData.length; jj++) {
variantData[jj].parent = row.pk;
var variant = variantData[jj];
// Add this variant to the table, augmented
$(table).bootstrapTable('append', [{
// Point the parent to the "master" assembly row
parent: row.pk,
part: variant.pk,
part_detail: variant,
sub_part: row.sub_part,
sub_part_detail: row.sub_part_detail,
quantity: row.quantity,
}]);
}
},
error: function(xhr) {
showApiError(xhr);
}
}
);
}
$(table).inventreeTable({
url: options.url || '{% url "api-bom-list" %}',
name: options.table_name || 'usedin',
sortable: true,
search: true,
showColumns: true,
queryParams: filters,
original: params,
rootParentId: 'top-level-item',
idField: 'pk',
uniqueId: 'pk',
parentIdField: 'parent',
treeShowField: 'part',
onLoadSuccess: function(tableData) {
// Once the initial data are loaded, check if there are any "inherited" BOM lines
for (var ii = 0; ii < tableData.length; ii++) {
var row = tableData[ii];
// This is a "top level" item in the table
row.parent = 'top-level-item';
// Ignore this row as it is not "inherited" by variant parts
if (!row.inherited) {
continue;
}
loadVariantData(row);
}
},
onPostBody: function() {
$(table).treegrid({
treeColumn: 0,
});
},
columns: [
{
field: 'pk',
title: 'ID',
visible: false,
switchable: false,
},
{
field: 'part',
title: '{% trans "Assembly" %}',
switchable: false,
sortable: true,
formatter: function(value, row) {
var url = `/part/${value}/?display=bom`;
var html = '';
var part = row.part_detail;
html += imageHoverIcon(part.thumbnail);
html += renderLink(part.full_name, url);
html += makePartIcons(part);
return html;
}
},
{
field: 'sub_part',
title: '{% trans "Required Part" %}',
sortable: true,
formatter: function(value, row) {
var url = `/part/${value}/`;
var html = '';
var sub_part = row.sub_part_detail;
html += imageHoverIcon(sub_part.thumbnail);
html += renderLink(sub_part.full_name, url);
html += makePartIcons(sub_part);
return html;
}
},
{
field: 'quantity',
title: '{% trans "Required Quantity" %}',
formatter: function(value, row) {
var html = value;
if (row.parent && row.parent != 'top-level-item') {
html += ` <em>({% trans "Inherited from parent BOM" %})</em>`;
}
return html;
}
}
]
});
}

View File

@ -281,23 +281,24 @@ function setupFilterList(tableKey, table, target) {
// One blank slate, please
element.empty();
element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`);
var buttons = '';
// Callback for reloading the table
element.find(`#reload-${tableKey}`).click(function() {
$(table).bootstrapTable('refresh');
});
buttons += `<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`;
// If there are no filters defined for this table, exit now
if (jQuery.isEmptyObject(getAvailableTableFilters(tableKey))) {
return;
// If there are filters defined for this table, add more buttons
if (!jQuery.isEmptyObject(getAvailableTableFilters(tableKey))) {
buttons += `<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-filter'></span></button>`;
if (Object.keys(filters).length > 0) {
buttons += `<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-backspace icon-red'></span></button>`;
}
}
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-filter'></span></button>`);
if (Object.keys(filters).length > 0) {
element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-backspace icon-red'></span></button>`);
}
element.html(`
<div class='btn-group' role='group'>
${buttons}
</div>
`);
for (var key in filters) {
var value = getFilterOptionValue(tableKey, key, filters[key]);
@ -307,6 +308,11 @@ function setupFilterList(tableKey, table, target) {
element.append(`<div title='${description}' class='filter-tag'>${title} = ${value}<span ${tag}='${key}' class='close'>x</span></div>`);
}
// Callback for reloading the table
element.find(`#reload-${tableKey}`).click(function() {
$(table).bootstrapTable('refresh');
});
// Add a callback for adding a new filter
element.find(`#${add}`).click(function clicked() {
@ -316,10 +322,12 @@ function setupFilterList(tableKey, table, target) {
var html = '';
html += `<div class='input-group'>`;
html += generateAvailableFilterList(tableKey);
html += generateFilterInput(tableKey);
html += `<button title='{% trans "Create filter" %}' class='btn btn-outline-secondary filter-button' id='${make}'><span class='fas fa-plus'></span></button>`;
html += `</div>`;
element.append(html);

View File

@ -924,8 +924,8 @@ function handleFormSuccess(response, options) {
var cache = (options.follow && response.url) || options.redirect || options.reload;
// Display any messages
if (response && response.success) {
showAlertOrCache(response.success, cache, {style: 'success'});
if (response && (response.success || options.successMessage)) {
showAlertOrCache(response.success || options.successMessage, cache, {style: 'success'});
}
if (response && response.info) {

View File

@ -331,6 +331,7 @@ function editPart(pk) {
groups: groups,
title: '{% trans "Edit Part" %}',
reload: true,
successMessage: '{% trans "Part edited" %}',
});
}

View File

@ -77,10 +77,22 @@ function getAvailableTableFilters(tableKey) {
// Filters for the "used in" table
if (tableKey == 'usedin') {
return {
'inherited': {
type: 'bool',
title: '{% trans "Inherited" %}',
},
'optional': {
type: 'bool',
title: '{% trans "Optional" %}',
},
'part_active': {
type: 'bool',
title: '{% trans "Active" %}',
},
'part_trackable': {
type: 'bool',
title: '{% trans "Trackable" %}',
},
};
}

View File

@ -6,6 +6,7 @@
{% settings_value 'BARCODE_ENABLE' as barcodes %}
{% settings_value 'STICKY_HEADER' user=request.user as sticky %}
{% navigation_enabled as plugin_nav %}
{% inventree_demo_mode as demo %}
<nav class="navbar {% if sticky %}fixed-top{% endif %} navbar-expand-lg navbar-light">
<div class="container-fluid">
@ -83,6 +84,9 @@
</ul>
</div>
{% if demo %}
{% include "navbar_demo.html" %}
{% endif %}
{% include "search_form.html" %}
<ul class='navbar-nav flex-row'>
{% if barcodes %}
@ -103,7 +107,7 @@
</a>
<ul class='dropdown-menu dropdown-menu-end inventree-navbar-menu'>
{% if user.is_authenticated %}
{% if user.is_staff %}
{% if user.is_staff and not demo %}
<li><a class='dropdown-item' href="/admin/"><span class="fas fa-user"></span> {% trans "Admin" %}</a></li>
{% endif %}
<li><a class='dropdown-item' href="{% url 'account_logout' %}"><span class="fas fa-sign-out-alt"></span> {% trans "Logout" %}</a></li>

View File

@ -0,0 +1,12 @@
{% load i18n %}
{% include "spacer.html" %}
<div class='flex'>
<h6>
{% trans "InvenTree demo mode" %}
<a href='https://inventree.readthedocs.io/en/latest/demo/'>
<span class='fas fa-info-circle'></span>
</a>
</h6>
</div>
{% include "spacer.html" %}
{% include "spacer.html" %}

View File

@ -2,8 +2,10 @@
<form class="d-flex" action="{% url 'search' %}" method='post'>
{% csrf_token %}
<input type="text" name='search' class="form-control" aria-label='{% trans "Search" %}' id="search-bar" placeholder="{% trans 'Search' %}"{% if query_text %} value="{{ query }}"{% endif %}>
<button type="submit" id='search-submit' class="btn btn-secondary" title='{% trans "Search" %}'>
<span class='fas fa-search'></span>
</button>
<div class='input-group'>
<input type="text" name='search' class="form-control" aria-label='{% trans "Search" %}' id="search-bar" placeholder="{% trans 'Search' %}"{% if query_text %} value="{{ query }}"{% endif %}>
<button type="submit" id='search-submit' class="btn btn-secondary" title='{% trans "Search" %}'>
<span class='fas fa-search'></span>
</button>
</div>
</form>

View File

@ -27,6 +27,9 @@ function {{ label }}StatusDisplay(key, options={}) {
label = {{ label }}Codes[key].label;
}
// Fallback option for label
label = label || 'bg-dark';
if (value == null || value.length == 0) {
value = key;
label = '';