mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-18 21:15:41 +00:00
merge
This commit is contained in:
@ -250,18 +250,18 @@ $("#param-table").inventreeTable({
|
||||
columns: [
|
||||
{
|
||||
field: 'pk',
|
||||
title: 'ID',
|
||||
title: '{% trans "ID" %}',
|
||||
visible: false,
|
||||
switchable: false,
|
||||
},
|
||||
{
|
||||
field: 'name',
|
||||
title: 'Name',
|
||||
title: '{% trans "Name" %}',
|
||||
sortable: 'true',
|
||||
},
|
||||
{
|
||||
field: 'units',
|
||||
title: 'Units',
|
||||
title: '{% trans "Units" %}',
|
||||
sortable: 'true',
|
||||
},
|
||||
{
|
||||
|
@ -2,29 +2,48 @@
|
||||
{% load static %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% include "sidebar_header.html" with text="User Settings" icon='fa-user' %}
|
||||
{% trans "User Settings" as text %}
|
||||
{% include "sidebar_header.html" with text=text icon='fa-user' %}
|
||||
|
||||
{% include "sidebar_item.html" with label='account' text="Account Settings" icon="fa-cog" %}
|
||||
{% include "sidebar_item.html" with label='user-display' text="Display Settings" icon="fa-desktop" %}
|
||||
{% include "sidebar_item.html" with label='user-home' text="Home Page" icon="fa-home" %}
|
||||
{% include "sidebar_item.html" with label='user-search' text="Search Settings" icon="fa-search" %}
|
||||
{% include "sidebar_item.html" with label='user-labels' text="Label Printing" icon="fa-tag" %}
|
||||
{% include "sidebar_item.html" with label='user-reports' text="Reporting" icon="fa-file-pdf" %}
|
||||
{% trans "Account Settings" as text %}
|
||||
{% include "sidebar_item.html" with label='account' text=text icon="fa-cog" %}
|
||||
{% trans "Display Settings" as text %}
|
||||
{% include "sidebar_item.html" with label='user-display' text=text icon="fa-desktop" %}
|
||||
{% trans "Home Page" as text %}
|
||||
{% 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 "Label Printing" as text %}
|
||||
{% include "sidebar_item.html" with label='user-labels' text=text icon="fa-tag" %}
|
||||
{% trans "Reporting" as text %}
|
||||
{% include "sidebar_item.html" with label='user-reports' text=text icon="fa-file-pdf" %}
|
||||
|
||||
{% if user.is_staff %}
|
||||
|
||||
{% include "sidebar_header.html" with text="Global Settings" icon='fa-cogs' %}
|
||||
{% trans "Global Settings" as text %}
|
||||
{% include "sidebar_header.html" with text=text icon='fa-cogs' %}
|
||||
|
||||
{% include "sidebar_item.html" with label='server' text="Server Configuration" icon="fa-server" %}
|
||||
{% include "sidebar_item.html" with label='login' text="Login Settings" icon="fa-fingerprint" %}
|
||||
{% include "sidebar_item.html" with label='barcodes' text="Barcode Support" icon="fa-qrcode" %}
|
||||
{% include "sidebar_item.html" with label='currencies' text="Currencies" icon="fa-dollar-sign" %}
|
||||
{% include "sidebar_item.html" with label='reporting' text="Reporting" icon="fa-file-pdf" %}
|
||||
{% include "sidebar_item.html" with label='parts' text="Parts" icon="fa-shapes" %}
|
||||
{% include "sidebar_item.html" with label='category' text="Categories" icon="fa-sitemap" %}
|
||||
{% include "sidebar_item.html" with label='stock' text="Stock" icon="fa-boxes" %}
|
||||
{% include "sidebar_item.html" with label='build-order' text="Build Orders" icon="fa-tools" %}
|
||||
{% include "sidebar_item.html" with label='purchase-order' text="Purchase Orders" icon="fa-shopping-cart" %}
|
||||
{% include "sidebar_item.html" with label='sales-order' text="Sales Orders" icon="fa-truck" %}
|
||||
{% trans "Server Configuration" as text %}
|
||||
{% include "sidebar_item.html" with label='server' text=text icon="fa-server" %}
|
||||
{% trans "Login Settings" as text %}
|
||||
{% include "sidebar_item.html" with label='login' text=text icon="fa-fingerprint" %}
|
||||
{% trans "Barcode Support" as text %}
|
||||
{% include "sidebar_item.html" with label='barcodes' text=text icon="fa-qrcode" %}
|
||||
{% trans "Currencies" as text %}
|
||||
{% include "sidebar_item.html" with label='currencies' text=text icon="fa-dollar-sign" %}
|
||||
{% trans "Reporting" as text %}
|
||||
{% include "sidebar_item.html" with label='reporting' text=text icon="fa-file-pdf" %}
|
||||
{% trans "Parts" as text %}
|
||||
{% include "sidebar_item.html" with label='parts' text=text icon="fa-shapes" %}
|
||||
{% trans "Categories" as text %}
|
||||
{% include "sidebar_item.html" with label='category' text=text icon="fa-sitemap" %}
|
||||
{% trans "Stock" as text %}
|
||||
{% include "sidebar_item.html" with label='stock' text=text icon="fa-boxes" %}
|
||||
{% trans "Build Orders" as text %}
|
||||
{% include "sidebar_item.html" with label='build-order' text=text icon="fa-tools" %}
|
||||
{% trans "Purchase Orders" as text %}
|
||||
{% include "sidebar_item.html" with label='purchase-order' text=text icon="fa-shopping-cart" %}
|
||||
{% trans "Sales Orders" as text %}
|
||||
{% include "sidebar_item.html" with label='sales-order' text=text icon="fa-truck" %}
|
||||
|
||||
{% endif %}
|
@ -4,6 +4,7 @@
|
||||
{% load inventree_extras %}
|
||||
{% load socialaccount %}
|
||||
{% load crispy_forms_tags %}
|
||||
{% load user_sessions i18n %}
|
||||
|
||||
{% block label %}account{% endblock %}
|
||||
|
||||
@ -14,12 +15,12 @@
|
||||
{% block actions %}
|
||||
{% inventree_demo_mode as demo %}
|
||||
{% if not demo %}
|
||||
<div class='btn btn-outline-primary' type='button' id='edit-password' title='{% trans "Change Password" %}'>
|
||||
<span class='fas fa-key'></span> {% trans "Set Password" %}
|
||||
</div>
|
||||
<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 %}
|
||||
|
||||
@ -238,6 +239,7 @@
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
@ -294,6 +296,51 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>{% trans "Active Sessions" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% if session_list.count > 1 %}
|
||||
<form method="post" action="{% url 'session_delete_other' %}">
|
||||
{% csrf_token %}
|
||||
<button type="submit" class="btn btn-sm btn-default btn-danger" title='{% trans "Log out active sessions (except this one)" %}'>
|
||||
{% trans "Log Out Active Sessions" %}
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% trans "<em>unknown on unknown</em>" as unknown_on_unknown %}
|
||||
{% trans "<em>unknown</em>" as unknown %}
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "IP Address" %}</th>
|
||||
<th>{% trans "Device" %}</th>
|
||||
<th>{% trans "Last Activity" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
{% for object in session_list %}
|
||||
<tr {% if object.session_key == session_key %}class="active"{% endif %}>
|
||||
<td>{{ object.ip }}</td>
|
||||
<td>{{ object.user_agent|device|default_if_none:unknown_on_unknown|safe }}</td>
|
||||
<td>
|
||||
{% if object.session_key == session_key %}
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago (this session){% endblocktrans %}
|
||||
{% else %}
|
||||
{% blocktrans with time=object.last_activity|timesince %}{{ time }} ago{% endblocktrans %}
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block js_ready %}
|
||||
|
@ -50,4 +50,57 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Language Settings" %}</h4>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{% url 'settings' %}">
|
||||
<label for='language' class=' requiredField'>
|
||||
{% trans "Select language" %}
|
||||
</label>
|
||||
<div class='form-group input-group mb-3'>
|
||||
<select name="language" class="select form-control w-25">
|
||||
{% get_current_language as LANGUAGE_CODE %}
|
||||
{% get_available_languages as LANGUAGES %}
|
||||
{% get_language_info_list for LANGUAGES as languages %}
|
||||
{% if 'alllang' in request.GET %}{% define True as ALL_LANG %}{% endif %}
|
||||
{% for language in languages %}
|
||||
{% define language.code as lang_code %}
|
||||
{% define locale_stats|keyvalue:lang_code as lang_translated %}
|
||||
{% if lang_translated > 10 or lang_code == 'en' or lang_code == LANGUAGE_CODE %}{% define True as use_lang %}{% else %}{% define False as use_lang %}{% endif %}
|
||||
{% if ALL_LANG or use_lang %}
|
||||
<option value="{{ lang_code }}"{% if lang_code == LANGUAGE_CODE %} selected{% endif %}>
|
||||
{{ language.name_local }} ({{ lang_code }})
|
||||
{% if lang_translated %}
|
||||
{% blocktrans %}{{ lang_translated }}% translated{% endblocktrans %}
|
||||
{% else %}
|
||||
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
|
||||
{% endif %}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||
</div>
|
||||
</div>
|
||||
<p>{% trans "Some languages are not complete" %}
|
||||
{% if ALL_LANG %}
|
||||
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
|
||||
{% else %}
|
||||
{% trans "and hidden." %} <a href="?alllang">{% trans "Show them too" %}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h4>{% trans "Help the translation efforts!" %}</h4>
|
||||
<p>{% blocktrans with link="https://crowdin.com/project/inventree" %}Native language translation of the InvenTree web application is <a href="{{link}}">community contributed via crowdin</a>. Contributions are welcomed and encouraged.{% endblocktrans %}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
@ -1,5 +1,8 @@
|
||||
{% load i18n %}
|
||||
|
||||
<button type='button' class='btn btn-outline-success' id='new-attachment-link'>
|
||||
<span class='fas fa-link'></span> {% trans "Add Link" %}
|
||||
</button>
|
||||
<button type='button' class='btn btn-success' id='new-attachment'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "Add Attachment" %}
|
||||
</button>
|
@ -40,8 +40,8 @@
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/group-by-v2/bootstrap-table-group-by.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.css' %}">
|
||||
<link rel='stylesheet' href='{% static "treegrid/css/jquery.treegrid.css" %}'>
|
||||
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2/css/select2.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'select2/css/select2-bootstrap-5-theme.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fullcalendar/main.css' %}">
|
||||
@ -180,9 +180,9 @@
|
||||
<script type='text/javascript' src="{% i18n_static 'tables.js' %}"></script>
|
||||
<script type='text/javascript' src="{% i18n_static 'table_filters.js' %}"></script>
|
||||
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/solid.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/brands.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/fontawesome.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/solid.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/brands.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'fontawesome/js/fontawesome.min.js' %}"></script>
|
||||
|
||||
{% block js_load %}
|
||||
{% endblock %}
|
||||
|
@ -54,6 +54,7 @@ function inventreeGet(url, filters={}, options={}) {
|
||||
data: filters,
|
||||
dataType: 'json',
|
||||
contentType: 'application/json',
|
||||
async: (options.async == false) ? false : true,
|
||||
success: function(response) {
|
||||
if (options.success) {
|
||||
options.success(response);
|
||||
|
@ -6,10 +6,57 @@
|
||||
*/
|
||||
|
||||
/* exported
|
||||
addAttachmentButtonCallbacks,
|
||||
loadAttachmentTable,
|
||||
reloadAttachmentTable,
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Add callbacks to buttons for creating new attachments.
|
||||
*
|
||||
* Note: Attachments can also be external links!
|
||||
*/
|
||||
function addAttachmentButtonCallbacks(url, fields={}) {
|
||||
|
||||
// Callback for 'new attachment' button
|
||||
$('#new-attachment').click(function() {
|
||||
|
||||
var file_fields = {
|
||||
attachment: {},
|
||||
comment: {},
|
||||
};
|
||||
|
||||
Object.assign(file_fields, fields);
|
||||
|
||||
constructForm(url, {
|
||||
fields: file_fields,
|
||||
method: 'POST',
|
||||
onSuccess: reloadAttachmentTable,
|
||||
title: '{% trans "Add Attachment" %}',
|
||||
});
|
||||
});
|
||||
|
||||
// Callback for 'new link' button
|
||||
$('#new-attachment-link').click(function() {
|
||||
|
||||
var link_fields = {
|
||||
link: {},
|
||||
comment: {},
|
||||
};
|
||||
|
||||
Object.assign(link_fields, fields);
|
||||
|
||||
constructForm(url, {
|
||||
fields: link_fields,
|
||||
method: 'POST',
|
||||
onSuccess: reloadAttachmentTable,
|
||||
title: '{% trans "Add Link" %}',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function reloadAttachmentTable() {
|
||||
|
||||
$('#attachment-table').bootstrapTable('refresh');
|
||||
@ -20,6 +67,8 @@ function loadAttachmentTable(url, options) {
|
||||
|
||||
var table = options.table || '#attachment-table';
|
||||
|
||||
addAttachmentButtonCallbacks(url, options.fields || {});
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
name: options.name || 'attachments',
|
||||
@ -34,56 +83,77 @@ function loadAttachmentTable(url, options) {
|
||||
$(table).find('.button-attachment-edit').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
if (options.onEdit) {
|
||||
options.onEdit(pk);
|
||||
}
|
||||
constructForm(`${url}${pk}/`, {
|
||||
fields: {
|
||||
link: {},
|
||||
comment: {},
|
||||
},
|
||||
processResults: function(data, fields, opts) {
|
||||
// Remove the "link" field if the attachment is a file!
|
||||
if (data.attachment) {
|
||||
delete opts.fields.link;
|
||||
}
|
||||
},
|
||||
onSuccess: reloadAttachmentTable,
|
||||
title: '{% trans "Edit Attachment" %}',
|
||||
});
|
||||
});
|
||||
|
||||
// Add callback for 'delete' button
|
||||
$(table).find('.button-attachment-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
if (options.onDelete) {
|
||||
options.onDelete(pk);
|
||||
}
|
||||
constructForm(`${url}${pk}/`, {
|
||||
method: 'DELETE',
|
||||
confirmMessage: '{% trans "Confirm Delete" %}',
|
||||
title: '{% trans "Delete Attachment" %}',
|
||||
onSuccess: reloadAttachmentTable,
|
||||
});
|
||||
});
|
||||
},
|
||||
columns: [
|
||||
{
|
||||
field: 'attachment',
|
||||
title: '{% trans "File" %}',
|
||||
formatter: function(value) {
|
||||
title: '{% trans "Attachment" %}',
|
||||
formatter: function(value, row) {
|
||||
|
||||
var icon = 'fa-file-alt';
|
||||
if (row.attachment) {
|
||||
var icon = 'fa-file-alt';
|
||||
|
||||
var fn = value.toLowerCase();
|
||||
var fn = value.toLowerCase();
|
||||
|
||||
if (fn.endsWith('.csv')) {
|
||||
icon = 'fa-file-csv';
|
||||
} else if (fn.endsWith('.pdf')) {
|
||||
icon = 'fa-file-pdf';
|
||||
} else if (fn.endsWith('.xls') || fn.endsWith('.xlsx')) {
|
||||
icon = 'fa-file-excel';
|
||||
} else if (fn.endsWith('.doc') || fn.endsWith('.docx')) {
|
||||
icon = 'fa-file-word';
|
||||
} else if (fn.endsWith('.zip') || fn.endsWith('.7z')) {
|
||||
icon = 'fa-file-archive';
|
||||
if (fn.endsWith('.csv')) {
|
||||
icon = 'fa-file-csv';
|
||||
} else if (fn.endsWith('.pdf')) {
|
||||
icon = 'fa-file-pdf';
|
||||
} else if (fn.endsWith('.xls') || fn.endsWith('.xlsx')) {
|
||||
icon = 'fa-file-excel';
|
||||
} else if (fn.endsWith('.doc') || fn.endsWith('.docx')) {
|
||||
icon = 'fa-file-word';
|
||||
} else if (fn.endsWith('.zip') || fn.endsWith('.7z')) {
|
||||
icon = 'fa-file-archive';
|
||||
} else {
|
||||
var images = ['.png', '.jpg', '.bmp', '.gif', '.svg', '.tif'];
|
||||
|
||||
images.forEach(function(suffix) {
|
||||
if (fn.endsWith(suffix)) {
|
||||
icon = 'fa-file-image';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var split = value.split('/');
|
||||
var filename = split[split.length - 1];
|
||||
|
||||
var html = `<span class='fas ${icon}'></span> ${filename}`;
|
||||
|
||||
return renderLink(html, value);
|
||||
} else if (row.link) {
|
||||
var html = `<span class='fas fa-link'></span> ${row.link}`;
|
||||
return renderLink(html, row.link);
|
||||
} else {
|
||||
var images = ['.png', '.jpg', '.bmp', '.gif', '.svg', '.tif'];
|
||||
|
||||
images.forEach(function(suffix) {
|
||||
if (fn.endsWith(suffix)) {
|
||||
icon = 'fa-file-image';
|
||||
}
|
||||
});
|
||||
return '-';
|
||||
}
|
||||
|
||||
var split = value.split('/');
|
||||
var filename = split[split.length - 1];
|
||||
|
||||
var html = `<span class='fas ${icon}'></span> ${filename}`;
|
||||
|
||||
return renderLink(html, value);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -192,6 +192,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
</a>
|
||||
</td>
|
||||
<td id='description-${pk}'><em>${part.description}</em></td>
|
||||
<td id='stock-${pk}'><em>${part.stock}</em></td>
|
||||
<td>${buttons}</td>
|
||||
</tr>
|
||||
`;
|
||||
@ -212,6 +213,7 @@ function bomSubstitutesDialog(bom_item_id, substitutes, options={}) {
|
||||
<tr>
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Description" %}</th>
|
||||
<th>{% trans "Stock" %}</th>
|
||||
<th><!-- Actions --></th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
@ -124,6 +124,7 @@ function supplierPartFields() {
|
||||
part_detail: true,
|
||||
manufacturer_detail: true,
|
||||
},
|
||||
auto_fill: true,
|
||||
},
|
||||
description: {},
|
||||
link: {
|
||||
|
@ -273,7 +273,7 @@ function setupFilterList(tableKey, table, target) {
|
||||
|
||||
var element = $(target);
|
||||
|
||||
if (!element) {
|
||||
if (!element || !element.exists()) {
|
||||
console.log(`WARNING: setupFilterList could not find target '${target}'`);
|
||||
return;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
disableFormInput,
|
||||
enableFormInput,
|
||||
hideFormInput,
|
||||
setFormInputPlaceholder,
|
||||
setFormGroupVisibility,
|
||||
showFormInput,
|
||||
*/
|
||||
@ -1276,6 +1277,11 @@ function initializeGroups(fields, options) {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the placeholder value for a field
|
||||
function setFormInputPlaceholder(name, placeholder, options) {
|
||||
$(options.modal).find(`#id_${name}`).attr('placeholder', placeholder);
|
||||
}
|
||||
|
||||
// Clear a form input
|
||||
function clearFormInput(name, options) {
|
||||
updateFieldValue(name, null, {}, options);
|
||||
|
@ -695,6 +695,28 @@ function loadPurchaseOrderTable(table, options) {
|
||||
title: '{% trans "Items" %}',
|
||||
sortable: true,
|
||||
},
|
||||
{
|
||||
field: 'responsible',
|
||||
title: '{% trans "Responsible" %}',
|
||||
switchable: true,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
if (!row.responsible_detail) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
var html = row.responsible_detail.name;
|
||||
|
||||
if (row.responsible_detail.label == 'group') {
|
||||
html += `<span class='float-right fas fa-users'></span>`;
|
||||
} else {
|
||||
html += `<span class='float-right fas fa-user'></span>`;
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
|
@ -32,6 +32,7 @@
|
||||
loadPartTable,
|
||||
loadPartTestTemplateTable,
|
||||
loadPartVariantTable,
|
||||
loadRelatedPartsTable,
|
||||
loadSellPricingChart,
|
||||
loadSimplePartTable,
|
||||
loadStockPricingChart,
|
||||
@ -152,12 +153,7 @@ function partFields(options={}) {
|
||||
delete fields['default_expiry'];
|
||||
}
|
||||
|
||||
// Additional fields when "creating" a new part
|
||||
if (options.create) {
|
||||
|
||||
// No supplier parts available yet
|
||||
delete fields['default_supplier'];
|
||||
|
||||
if (options.create || options.duplicate) {
|
||||
if (global_settings.PART_CREATE_INITIAL) {
|
||||
|
||||
fields.initial_stock = {
|
||||
@ -186,6 +182,13 @@ function partFields(options={}) {
|
||||
group: 'create',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Additional fields when "creating" a new part
|
||||
if (options.create) {
|
||||
|
||||
// No supplier parts available yet
|
||||
delete fields['default_supplier'];
|
||||
|
||||
fields.copy_category_parameters = {
|
||||
type: 'boolean',
|
||||
@ -348,6 +351,10 @@ function duplicatePart(pk, options={}) {
|
||||
duplicate: pk,
|
||||
});
|
||||
|
||||
if (fields.initial_stock_location) {
|
||||
fields.initial_stock_location.value = data.default_location;
|
||||
}
|
||||
|
||||
// Remove "default_supplier" field
|
||||
delete fields['default_supplier'];
|
||||
|
||||
@ -705,6 +712,97 @@ function loadPartParameterTable(table, url, options) {
|
||||
}
|
||||
|
||||
|
||||
function loadRelatedPartsTable(table, part_id, options={}) {
|
||||
/*
|
||||
* Load table of "related" parts
|
||||
*/
|
||||
|
||||
options.params = options.params || {};
|
||||
|
||||
options.params.part = part_id;
|
||||
|
||||
var filters = {};
|
||||
|
||||
for (var key in options.params) {
|
||||
filters[key] = options.params[key];
|
||||
}
|
||||
|
||||
setupFilterList('related', $(table), options.filterTarget);
|
||||
|
||||
function getPart(row) {
|
||||
if (row.part_1 == part_id) {
|
||||
return row.part_2_detail;
|
||||
} else {
|
||||
return row.part_1_detail;
|
||||
}
|
||||
}
|
||||
|
||||
var columns = [
|
||||
{
|
||||
field: 'name',
|
||||
title: '{% trans "Part" %}',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
var part = getPart(row);
|
||||
|
||||
var html = imageHoverIcon(part.thumbnail) + renderLink(part.full_name, `/part/${part.pk}/`);
|
||||
|
||||
html += makePartIcons(part);
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'description',
|
||||
title: '{% trans "Description" %}',
|
||||
formatter: function(value, row) {
|
||||
return getPart(row).description;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'actions',
|
||||
title: '',
|
||||
switchable: false,
|
||||
formatter: function(value, row) {
|
||||
|
||||
var html = `<div class='btn-group float-right' role='group'>`;
|
||||
|
||||
html += makeIconButton('fa-trash-alt icon-red', 'button-related-delete', row.pk, '{% trans "Delete part relationship" %}');
|
||||
|
||||
html += '</div>';
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: '{% url "api-part-related-list" %}',
|
||||
groupBy: false,
|
||||
name: 'related',
|
||||
original: options.params,
|
||||
queryParams: filters,
|
||||
columns: columns,
|
||||
showColumns: false,
|
||||
search: true,
|
||||
onPostBody: function() {
|
||||
$(table).find('.button-related-delete').click(function() {
|
||||
var pk = $(this).attr('pk');
|
||||
|
||||
constructForm(`/api/part/related/${pk}/`, {
|
||||
method: 'DELETE',
|
||||
title: '{% trans "Delete Part Relationship" %}',
|
||||
onSuccess: function() {
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
});
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function loadParametricPartTable(table, options={}) {
|
||||
/* Load parametric table for part parameters
|
||||
*
|
||||
@ -836,6 +934,7 @@ function loadPartTable(table, url, options={}) {
|
||||
* query: extra query params for API request
|
||||
* buttons: If provided, link buttons to selection status of this table
|
||||
* disableFilters: If true, disable custom filters
|
||||
* actions: Provide a callback function to construct an "actions" column
|
||||
*/
|
||||
|
||||
// Ensure category detail is included
|
||||
@ -878,7 +977,7 @@ function loadPartTable(table, url, options={}) {
|
||||
|
||||
col = {
|
||||
field: 'IPN',
|
||||
title: 'IPN',
|
||||
title: '{% trans "IPN" %}',
|
||||
};
|
||||
|
||||
if (!options.params.ordering) {
|
||||
@ -895,7 +994,7 @@ function loadPartTable(table, url, options={}) {
|
||||
|
||||
var name = row.full_name;
|
||||
|
||||
var display = imageHoverIcon(row.thumbnail) + renderLink(name, '/part/' + row.pk + '/');
|
||||
var display = imageHoverIcon(row.thumbnail) + renderLink(name, `/part/${row.pk}/`);
|
||||
|
||||
display += makePartIcons(row);
|
||||
|
||||
@ -993,6 +1092,21 @@ function loadPartTable(table, url, options={}) {
|
||||
}
|
||||
});
|
||||
|
||||
// Push an "actions" column
|
||||
if (options.actions) {
|
||||
columns.push({
|
||||
field: 'actions',
|
||||
title: '',
|
||||
switchable: false,
|
||||
visible: true,
|
||||
searchable: false,
|
||||
sortable: false,
|
||||
formatter: function(value, row) {
|
||||
return options.actions(value, row);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var grid_view = options.gridView && inventreeLoad('part-grid-view') == 1;
|
||||
|
||||
$(table).inventreeTable({
|
||||
@ -1020,6 +1134,10 @@ function loadPartTable(table, url, options={}) {
|
||||
$('#view-part-grid').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-part-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
}
|
||||
|
||||
if (options.onPostBody) {
|
||||
options.onPostBody();
|
||||
}
|
||||
},
|
||||
buttons: options.gridView ? [
|
||||
{
|
||||
|
@ -44,6 +44,7 @@
|
||||
editStockItem,
|
||||
editStockLocation,
|
||||
exportStock,
|
||||
findStockItemBySerialNumber,
|
||||
loadInstalledInTable,
|
||||
loadStockLocationTable,
|
||||
loadStockTable,
|
||||
@ -80,6 +81,20 @@ function serializeStockItem(pk, options={}) {
|
||||
notes: {},
|
||||
};
|
||||
|
||||
if (options.part) {
|
||||
// Work out the next available serial number
|
||||
inventreeGet(`/api/part/${options.part}/serial-numbers/`, {}, {
|
||||
success: function(data) {
|
||||
if (data.next) {
|
||||
options.fields.serial_numbers.placeholder = `{% trans "Next available serial number" %}: ${data.next}`;
|
||||
} else if (data.latest) {
|
||||
options.fields.serial_numbers.placeholder = `{% trans "Latest serial number" %}: ${data.latest}`;
|
||||
}
|
||||
},
|
||||
async: false,
|
||||
});
|
||||
}
|
||||
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
@ -144,10 +159,26 @@ function stockItemFields(options={}) {
|
||||
// If a "trackable" part is selected, enable serial number field
|
||||
if (data.trackable) {
|
||||
enableFormInput('serial_numbers', opts);
|
||||
// showFormInput('serial_numbers', opts);
|
||||
|
||||
// Request part serial number information from the server
|
||||
inventreeGet(`/api/part/${data.pk}/serial-numbers/`, {}, {
|
||||
success: function(data) {
|
||||
var placeholder = '';
|
||||
if (data.next) {
|
||||
placeholder = `{% trans "Next available serial number" %}: ${data.next}`;
|
||||
} else if (data.latest) {
|
||||
placeholder = `{% trans "Latest serial number" %}: ${data.latest}`;
|
||||
}
|
||||
|
||||
setFormInputPlaceholder('serial_numbers', placeholder, opts);
|
||||
}
|
||||
});
|
||||
|
||||
} else {
|
||||
clearFormInput('serial_numbers', opts);
|
||||
disableFormInput('serial_numbers', opts);
|
||||
|
||||
setFormInputPlaceholder('serial_numbers', '{% trans "This part cannot be serialized" %}', opts);
|
||||
}
|
||||
|
||||
// Enable / disable fields based on purchaseable status
|
||||
@ -364,6 +395,87 @@ function createNewStockItem(options={}) {
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
/*
|
||||
* Launch a modal form to find a particular stock item by serial number.
|
||||
* Arguments:
|
||||
* - part: ID (PK) of the part in question
|
||||
*/
|
||||
|
||||
function findStockItemBySerialNumber(part_id) {
|
||||
|
||||
constructFormBody({}, {
|
||||
title: '{% trans "Find Serial Number" %}',
|
||||
fields: {
|
||||
serial: {
|
||||
label: '{% trans "Serial Number" %}',
|
||||
help_text: '{% trans "Enter serial number" %}',
|
||||
placeholder: '{% trans "Enter serial number" %}',
|
||||
required: true,
|
||||
type: 'string',
|
||||
value: '',
|
||||
}
|
||||
},
|
||||
onSubmit: function(fields, opts) {
|
||||
|
||||
var serial = getFormFieldValue('serial', fields['serial'], opts);
|
||||
|
||||
serial = serial.toString().trim();
|
||||
|
||||
if (!serial) {
|
||||
handleFormErrors(
|
||||
{
|
||||
'serial': [
|
||||
'{% trans "Enter a serial number" %}',
|
||||
]
|
||||
}, fields, opts
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
inventreeGet(
|
||||
'{% url "api-stock-list" %}',
|
||||
{
|
||||
part_tree: part_id,
|
||||
serial: serial,
|
||||
},
|
||||
{
|
||||
success: function(response) {
|
||||
if (response.length == 0) {
|
||||
// No results!
|
||||
handleFormErrors(
|
||||
{
|
||||
'serial': [
|
||||
'{% trans "No matching serial number" %}',
|
||||
]
|
||||
}, fields, opts
|
||||
);
|
||||
} else if (response.length > 1) {
|
||||
// Too many results!
|
||||
handleFormErrors(
|
||||
{
|
||||
'serial': [
|
||||
'{% trans "More than one matching result found" %}',
|
||||
]
|
||||
}, fields, opts
|
||||
);
|
||||
} else {
|
||||
$(opts.modal).modal('hide');
|
||||
|
||||
// Redirect
|
||||
var pk = response[0].pk;
|
||||
location.href = `/stock/item/${pk}/`;
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
showApiError(xhr, opts.url);
|
||||
$(opts.modal).modal('hide');
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/* Stock API functions
|
||||
* Requires api.js to be loaded first
|
||||
@ -1101,7 +1213,7 @@ function loadStockTable(table, options) {
|
||||
|
||||
col = {
|
||||
field: 'part_detail.IPN',
|
||||
title: 'IPN',
|
||||
title: '{% trans "IPN" %}',
|
||||
sortName: 'part__IPN',
|
||||
visible: params['part_detail'],
|
||||
switchable: params['part_detail'],
|
||||
|
@ -74,6 +74,12 @@ function getAvailableTableFilters(tableKey) {
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "related parts" table
|
||||
if (tableKey == 'related') {
|
||||
return {
|
||||
};
|
||||
}
|
||||
|
||||
// Filters for the "used in" table
|
||||
if (tableKey == 'usedin') {
|
||||
return {
|
||||
|
@ -24,29 +24,35 @@
|
||||
|
||||
{% block page_info %}
|
||||
<div class='panel-content'>
|
||||
<div class='row'>
|
||||
<div class='col-sm-6' id='detail-panel-left'>
|
||||
<div class='card'>
|
||||
{% block details_left %}
|
||||
<div class='row g-0'>
|
||||
<div class='col-md-4'>
|
||||
{% block thumbnail %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
<div class='col-md-8'>
|
||||
{% block details %}
|
||||
{% endblock %}
|
||||
{% block details_above %}
|
||||
{% endblock details_above %}
|
||||
<div class='container' style='max-width: 100%; padding: 5px;'>
|
||||
<div class='row'>
|
||||
<div class='col' id='detail-panel-left'>
|
||||
<div class='card'>
|
||||
{% block details_left %}
|
||||
<div class='row'>
|
||||
<div class='col' style='max-width: 220px;'>
|
||||
{% block thumbnail %}
|
||||
{% endblock thumbnail %}
|
||||
</div>
|
||||
<div class='col'>
|
||||
{% block details %}
|
||||
{% endblock details %}
|
||||
</div>
|
||||
</div>
|
||||
{% endblock details_left %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
<div class='col-sm-6' id='detail-panel-right'>
|
||||
<div class='card'>
|
||||
{% block details_right %}
|
||||
block details_right
|
||||
{% endblock %}
|
||||
<div class='col' id='detail-panel-right'>
|
||||
<div class='card'>
|
||||
{% block details_right %}
|
||||
block details_right
|
||||
{% endblock details_right %}
|
||||
</div>
|
||||
</div>
|
||||
{% block details_below %}
|
||||
{% endblock details_below %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,8 +1,8 @@
|
||||
{% load i18n %}
|
||||
<span title='{% trans text %}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate bg-light" data-bs-parent="#sidebar">
|
||||
<span title='{{ text }}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate bg-light" data-bs-parent="#sidebar">
|
||||
<h6>
|
||||
<i class="bi bi-bootstrap"></i>
|
||||
{% if icon %}<span class='sidebar-item-icon fas {{ icon }}'></span>{% endif %}
|
||||
{% if text %}<span class='sidebar-item-text' style='display: none;'>{% trans text %}</span>{% endif %}
|
||||
{% if text %}<span class='sidebar-item-text' style='display: none;'>{{ text }}</span>{% endif %}
|
||||
</h6>
|
||||
</span>
|
||||
</span>
|
@ -1,8 +1,8 @@
|
||||
{% load i18n %}
|
||||
<a href="#" id='select-{{ label }}' title='{% trans text %}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate sidebar-selector" data-bs-parent="#sidebar">
|
||||
<a href="#" id='select-{{ label }}' title='{{ text }}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate sidebar-selector" data-bs-parent="#sidebar">
|
||||
<i class="bi bi-bootstrap"></i>
|
||||
<span class='sidebar-item-icon fas {{ icon|default:"fa-circle" }}'></span>
|
||||
<span class='sidebar-item-text' style='display: none;'>{% trans text %}</span>
|
||||
<span class='sidebar-item-text' style='display: none;'>{{ text }}</span>
|
||||
{% if badge %}
|
||||
<span id='sidebar-badge-{{ label }}' class='sidebar-item-badge badge rounded-pill badge-right bg-dark'>
|
||||
<span class='fas fa-spin fa-spinner'></span>
|
||||
|
@ -1,4 +1,4 @@
|
||||
{% load i18n %}
|
||||
<a href="{{ url }}" class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate" data-bs-parent="#sidebar">
|
||||
<i class="bi bi-bootstrap"></i><span class='sidebar-item-icon fas {{ icon }}'></span><span class='sidebar-item-text' style='display: none;'>{% trans text %}</span>
|
||||
<i class="bi bi-bootstrap"></i><span class='sidebar-item-icon fas {{ icon }}'></span><span class='sidebar-item-text' style='display: none;'>{{ text }}</span>
|
||||
</a>
|
||||
|
Reference in New Issue
Block a user