mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-19 05:25:42 +00:00
Merge remote-tracking branch 'inventree/master' into partial-shipment
# Conflicts: # InvenTree/InvenTree/version.py # InvenTree/order/models.py
This commit is contained in:
@ -7,6 +7,8 @@
|
||||
{% inventree_title %} | {% trans "Index" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_list %}
|
||||
{% endblock %}
|
||||
|
||||
{% block sidebar %}
|
||||
<!-- Sidebar data is filled dynamically for the index page-->
|
||||
@ -74,6 +76,7 @@ function addHeaderAction(label, title, icon, options) {
|
||||
}
|
||||
|
||||
{% settings_value 'HOMEPAGE_PART_STARRED' user=request.user as setting_part_starred %}
|
||||
{% settings_value 'HOMEPAGE_CATEGORY_STARRED' user=request.user as setting_category_starred %}
|
||||
{% settings_value 'HOMEPAGE_PART_LATEST' user=request.user as setting_part_latest %}
|
||||
{% settings_value 'HOMEPAGE_BOM_VALIDATION' user=request.user as setting_bom_validation %}
|
||||
{% to_list setting_part_starred setting_part_latest setting_bom_validation as settings_list_part %}
|
||||
@ -82,15 +85,25 @@ function addHeaderAction(label, title, icon, options) {
|
||||
addHeaderTitle('{% trans "Parts" %}');
|
||||
|
||||
{% if setting_part_starred %}
|
||||
addHeaderAction('starred-parts', '{% trans "Starred Parts" %}', 'fa-star');
|
||||
addHeaderAction('starred-parts', '{% trans "Subscribed Parts" %}', 'fa-bell');
|
||||
loadSimplePartTable("#table-starred-parts", "{% url 'api-part-list' %}", {
|
||||
params: {
|
||||
"starred": true,
|
||||
starred: true,
|
||||
},
|
||||
name: 'starred_parts',
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if setting_category_starred %}
|
||||
addHeaderAction('starred-categories', '{% trans "Subscribed Categories" %}', 'fa-bell');
|
||||
loadPartCategoryTable($('#table-starred-categories'), {
|
||||
params: {
|
||||
starred: true,
|
||||
},
|
||||
name: 'starred_categories'
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
{% if setting_part_latest %}
|
||||
addHeaderAction('latest-parts', '{% trans "Latest Parts" %}', 'fa-newspaper');
|
||||
loadSimplePartTable("#table-latest-parts", "{% url 'api-part-list' %}", {
|
||||
@ -126,8 +139,7 @@ loadSimplePartTable("#table-bom-validation", "{% url 'api-part-list' %}", {
|
||||
{% to_list setting_stock_recent setting_stock_low setting_stock_depleted setting_stock_needed as settings_list_stock %}
|
||||
{% endif %}
|
||||
|
||||
{% if roles.stock.view and True in settings_list_stock %}
|
||||
addHeaderTitle('{% trans "Stock" %}');
|
||||
{% if roles.stock.view %}
|
||||
|
||||
{% if setting_stock_recent %}
|
||||
addHeaderAction('recently-updated-stock', '{% trans "Recently Updated" %}', 'fa-clock');
|
||||
@ -143,7 +155,7 @@ loadStockTable($('#table-recently-updated-stock'), {
|
||||
{% endif %}
|
||||
|
||||
{% if setting_stock_low %}
|
||||
addHeaderAction('low-stock', '{% trans "Low Stock" %}', 'fa-shopping-cart');
|
||||
addHeaderAction('low-stock', '{% trans "Low Stock" %}', 'fa-flag');
|
||||
loadSimplePartTable("#table-low-stock", "{% url 'api-part-list' %}", {
|
||||
params: {
|
||||
low_stock: true,
|
||||
|
@ -8,6 +8,9 @@
|
||||
{% inventree_title %} | {% trans "Search Results" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_list %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class='panel panel-inventree'>
|
||||
|
@ -7,6 +7,12 @@
|
||||
{% trans "Category Settings" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<button class='btn btn-success' id='new-cat-param' disabled=''>
|
||||
<div class='fas fa-plus-circle'></div> {% trans "New Parameter" %}
|
||||
</button>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class='row'>
|
||||
@ -21,12 +27,6 @@
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div id='cat-param-buttons'>
|
||||
<button class='btn btn-success' id='new-cat-param' disabled=''>
|
||||
<div class='fas fa-plus-circle'></div> {% trans "New Parameter" %}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='cat-param-table' data-toolbar='#cat-param-buttons'>
|
||||
</table>
|
||||
|
||||
|
@ -13,29 +13,31 @@
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="INVENTREE_DEFAULT_CURRENCY" icon="fa-globe" %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td></td>
|
||||
<th>{% trans "Base Currency" %}</th>
|
||||
<th>{{ base_currency }}</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th colspan='2'>{% trans "Exchange Rates" %}</th>
|
||||
<td></td>
|
||||
<th colspan='4'>{% trans "Exchange Rates" %}</th>
|
||||
</tr>
|
||||
{% for rate in rates %}
|
||||
<tr>
|
||||
<td>{{ rate.currency }}</td>
|
||||
<td></td>
|
||||
<td>{{ rate.value }}</td>
|
||||
<td>{{ rate.currency }}</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
<tr>
|
||||
<th></th>
|
||||
<th>
|
||||
{% trans "Last Update" %}
|
||||
</th>
|
||||
<td>
|
||||
<td colspan="3">
|
||||
{% if rates_updated %}
|
||||
{{ rates_updated }}
|
||||
{% else %}
|
||||
@ -44,7 +46,7 @@
|
||||
<form action='{% url "settings-currencies-refresh" %}' method='post'>
|
||||
<div id='refresh-rates-form'>
|
||||
{% csrf_token %}
|
||||
<button type='submit' id='update-rates' class='btn btn-outline-secondary float-right'>{% trans "Update Now" %}</button>
|
||||
<button type='submit' id='update-rates' class='btn btn-primary float-right'>{% trans "Update Now" %}</button>
|
||||
</div>
|
||||
</form>
|
||||
</td>
|
||||
|
@ -17,7 +17,7 @@
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_PWD_FORGOT" icon="fa-info-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_MAIL_REQUIRED" icon="fa-info-circle" %}
|
||||
<tr>
|
||||
<td>{% trans 'Signup' %}</td>
|
||||
<th><h5>{% trans 'Signup' %}</h5></th>
|
||||
<td colspan='4'></td>
|
||||
</tr>
|
||||
{% include "InvenTree/settings/setting.html" with key="LOGIN_ENABLE_REG" icon="fa-info-circle" %}
|
||||
|
@ -9,8 +9,6 @@
|
||||
|
||||
{% block content %}
|
||||
|
||||
<h4>{% trans "Part Options" %}</h4>
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="PART_IPN_REGEX" %}
|
||||
@ -40,12 +38,17 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4>{% trans "Part Import" %}</h4>
|
||||
|
||||
<button class='btn btn-success' id='import-part'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "Import Part" %}
|
||||
</button>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-span'>
|
||||
<h4>{% trans "Part Import" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
<button class='btn btn-success' id='import-part'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "Import Part" %}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
@ -53,14 +56,16 @@
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
<h4>{% trans "Part Parameter Templates" %}</h4>
|
||||
|
||||
<div id='param-buttons'>
|
||||
<button class='btn btn-success' id='new-param'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
|
||||
</button>
|
||||
<div class='panel-heading'>
|
||||
<span class='d-flex flex-span'>
|
||||
<h4>{% trans "Part Parameter Templates" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
<button class='btn btn-success' id='new-param'>
|
||||
<span class='fas fa-plus-circle'></span> {% trans "New Parameter" %}
|
||||
</button>
|
||||
</div>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed' id='param-table' data-toolbar='#param-buttons'>
|
||||
|
@ -12,6 +12,7 @@
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="REPORT_DEFAULT_PAGE_SIZE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="REPORT_DEBUG_MODE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="REPORT_ENABLE_TEST_REPORT" %}
|
||||
|
@ -21,15 +21,13 @@
|
||||
</div>
|
||||
{% else %}
|
||||
<div id='setting-{{ setting.pk }}'>
|
||||
<strong>
|
||||
<span id='setting-value-{{ setting.key.upper }}' fieldname='{{ setting.key.upper }}'>
|
||||
{% if setting.value %}
|
||||
{{ setting.value }}
|
||||
<strong>{{ setting.value }}</strong>
|
||||
{% else %}
|
||||
<em>{% trans "No value set" %}</em>
|
||||
<em style='color: #855;'>{% trans "No value set" %}</em>
|
||||
{% endif %}
|
||||
</span>
|
||||
</strong>
|
||||
{{ setting.units }}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
@ -4,6 +4,9 @@
|
||||
{% load static %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block breadcrumb_list %}
|
||||
{% endblock %}
|
||||
|
||||
{% block page_title %}
|
||||
{% inventree_title %} | {% trans "Settings" %}
|
||||
{% endblock %}
|
||||
@ -50,26 +53,17 @@
|
||||
$('table').find('.btn-edit-setting').click(function() {
|
||||
var setting = $(this).attr('setting');
|
||||
var pk = $(this).attr('pk');
|
||||
var url = `/settings/${pk}/edit/`;
|
||||
|
||||
var is_global = true;
|
||||
|
||||
if ($(this).attr('user')){
|
||||
url += `user/`;
|
||||
is_global = false;
|
||||
}
|
||||
|
||||
launchModalForm(
|
||||
url,
|
||||
{
|
||||
success: function(response) {
|
||||
|
||||
if (response.is_bool) {
|
||||
var enabled = response.value.toLowerCase() == 'true';
|
||||
$(`#setting-value-${setting}`).prop('checked', enabled);
|
||||
} else {
|
||||
$(`#setting-value-${setting}`).html(response.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
editSetting(pk, {
|
||||
global: is_global,
|
||||
title: is_global ? '{% trans "Edit Global Setting" %}' : '{% trans "Edit User Setting" %}',
|
||||
});
|
||||
});
|
||||
|
||||
$("#edit-user").on('click', function() {
|
||||
|
@ -11,18 +11,18 @@
|
||||
{% trans "Account Settings" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block actions %}
|
||||
<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>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% mail_configured as mail_conf %}
|
||||
|
||||
<div class='btn-group' style='float: right;'>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tr>
|
||||
<td>{% trans "Username" %}</td>
|
||||
@ -39,61 +39,81 @@
|
||||
</table>
|
||||
|
||||
<div class='panel-heading'>
|
||||
<h4>{% trans "Email" %}</h4>
|
||||
<div class='d-flex flex-span'>
|
||||
<h4>{% trans "Email" %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{% if user.emailaddress_set.all %}
|
||||
<p>{% trans 'The following email addresses are associated with your account:' %}</p>
|
||||
<div class='row'>
|
||||
<div class='col-sm-6'>
|
||||
{% if user.emailaddress_set.all %}
|
||||
<p>{% trans 'The following email addresses are associated with your account:' %}</p>
|
||||
|
||||
<form action="{% url 'account_email' %}" class="email_list" method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset class="blockLabels">
|
||||
<form action="{% url 'account_email' %}" class="email_list" method="post">
|
||||
{% csrf_token %}
|
||||
<fieldset class="blockLabels">
|
||||
|
||||
{% for emailaddress in user.emailaddress_set.all %}
|
||||
<div class="ctrlHolder">
|
||||
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
|
||||
|
||||
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked"{%endif %} value="{{emailaddress.email}}"/>
|
||||
|
||||
{{ emailaddress.email }}
|
||||
{% if emailaddress.verified %}
|
||||
<span class="verified">{% trans "Verified" %}</span>
|
||||
{% else %}
|
||||
<span class="unverified">{% trans "Unverified" %}</span>
|
||||
{% endif %}
|
||||
{% if emailaddress.primary %}<span class="primary">{% trans "Primary" %}</span>{% endif %}
|
||||
</label>
|
||||
{% for emailaddress in user.emailaddress_set.all %}
|
||||
<div>
|
||||
<div class="ctrlHolder">
|
||||
<label for="email_radio_{{forloop.counter}}" class="{% if emailaddress.primary %}primary_email{%endif%}">
|
||||
|
||||
<input id="email_radio_{{forloop.counter}}" type="radio" name="email" {% if emailaddress.primary or user.emailaddress_set.count == 1 %}checked="checked"{%endif %} value="{{emailaddress.email}}"/>
|
||||
|
||||
{% if emailaddress.primary %}
|
||||
<b>{{ emailaddress.email }}</b>
|
||||
{% else %}
|
||||
{{ emailaddress.email }}
|
||||
{% endif %}
|
||||
</label>
|
||||
{% if emailaddress.verified %}
|
||||
<span class='badge badge-right rounded-pill bg-success'>{% trans "Verified" %}</span>
|
||||
{% else %}
|
||||
<span class='badge badge-right rounded-pill bg-warning'>{% trans "Unverified" %}</span>
|
||||
{% endif %}
|
||||
{% if emailaddress.primary %}<span class='badge badge-right rounded-pill bg-primary'>{% trans "Primary" %}</span>{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="buttonHolder">
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_send" {% if not mail_conf %}disabled{% endif %}>{% trans 'Re-send Verification' %}</button>
|
||||
<button class="btn btn-primary primaryAction" type="submit" name="action_remove" >{% trans 'Remove' %}</button>
|
||||
</div>
|
||||
<div class="buttonHolder">
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_primary" >{% trans 'Make Primary' %}</button>
|
||||
<button class="btn btn-primary secondaryAction" type="submit" name="action_send" {% if not mail_conf %}disabled{% endif %}>{% trans 'Re-send Verification' %}</button>
|
||||
<button class="btn btn-primary primaryAction" type="submit" name="action_remove" >{% trans 'Remove' %}</button>
|
||||
</div>
|
||||
|
||||
</fieldset>
|
||||
</form>
|
||||
</fieldset>
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<p><strong>{% trans 'Warning:'%}</strong>
|
||||
{% trans "You currently do not have any email address set up. You should really add an email address so you can receive notifications, reset your password, etc." %}
|
||||
</p>
|
||||
{% else %}
|
||||
<p><strong>{% trans 'Warning:'%}</strong>
|
||||
{% trans "You currently do not have any email address set up. You should really add an email address so you can receive notifications, reset your password, etc." %}
|
||||
</p>
|
||||
|
||||
{% endif %}
|
||||
|
||||
{% if can_add_email %}
|
||||
<br>
|
||||
<h4>{% trans "Add Email Address" %}</h4>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class='col-sm-6'>
|
||||
{% if can_add_email %}
|
||||
<h5>{% trans "Add Email Address" %}</h5>
|
||||
|
||||
<form method="post" action="{% url 'account_email' %}" class="add_email">
|
||||
{% csrf_token %}
|
||||
{{ add_email_form|crispy }}
|
||||
<button class="btn btn-primary" name="action_add" type="submit">{% trans "Add Email" %}</button>
|
||||
|
||||
<label for="id_email" class=" requiredField">
|
||||
E-mail<span class="asteriskField">*</span>
|
||||
</label>
|
||||
<div id="div_id_email" class="form-group input-group mb-3">
|
||||
<div class='input-group-prepend'><span class='input-group-text'>@</span></div>
|
||||
<input type="email" name="email" placeholder='{% trans "Enter e-mail address" %}' class="textinput textInput form-control" required="" id="id_email">
|
||||
<div class='input-group-append'>
|
||||
<button class="btn btn-primary" name="action_add" type="submit">{% trans "Add Email" %}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
{% endif %}
|
||||
<br>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='panel-heading'>
|
||||
@ -135,7 +155,9 @@
|
||||
</form>
|
||||
|
||||
{% else %}
|
||||
<p>{% trans 'You currently have no social network accounts connected to this account.' %}</p>
|
||||
<div class='alert alert-block alert-warning'>
|
||||
{% trans "There are no social network accounts connected to your InvenTree account" %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<br>
|
||||
@ -155,26 +177,26 @@
|
||||
|
||||
<div class='row'>
|
||||
|
||||
<form action='{% url "settings-appearance" %}' method='post'>
|
||||
{% csrf_token %}
|
||||
<input name='next' type='hidden' value='{% url "settings" %}'>
|
||||
<div class="col-sm-6" style="width: 200px;">
|
||||
<div id="div_id_themes" class="form-group">
|
||||
<div class="controls ">
|
||||
<select name='theme' class='select form-control'>
|
||||
{% get_available_themes as themes %}
|
||||
{% for theme in themes %}
|
||||
<option value='{{ theme.key }}'>{{ theme.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class='col-sm-6'>
|
||||
<form action='{% url "settings-appearance" %}' method='post'>
|
||||
{% csrf_token %}
|
||||
<input name='next' type='hidden' value='{% url "settings" %}'>
|
||||
<label for='theme' class=' requiredField'>
|
||||
{% trans "Select theme" %}
|
||||
</label>
|
||||
<div class='form-group input-group mb-3'>
|
||||
<select id='theme' name='theme' class='select form-control'>
|
||||
{% get_available_themes as themes %}
|
||||
{% for theme in themes %}
|
||||
<option value='{{ theme.key }}'>{{ theme.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<div class='input-group-append'>
|
||||
<input type="submit" value="{% trans 'Set Theme' %}" class="btn btn-primary">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6" style="width: auto;">
|
||||
<input type="submit" value="{% trans 'Set Theme' %}" class="btn btn btn-primary">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='panel-heading'>
|
||||
@ -186,29 +208,43 @@
|
||||
<form action="{% url 'set_language' %}" method="post">
|
||||
{% csrf_token %}
|
||||
<input name="next" type="hidden" value="{% url 'settings' %}">
|
||||
<div class="col-sm-6" style="width: 200px;"><div id="div_id_language" class="form-group"><div class="controls ">
|
||||
<select name="language" class="select form-control">
|
||||
<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 %}
|
||||
{% trans 'No translations available' %}
|
||||
{% if lang_code == 'en' %}-{% else %}{% trans 'No translations available' %}{% endif %}
|
||||
{% endif %}
|
||||
</option>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div></div></div>
|
||||
<div class="col-sm-6" style="width: auto;">
|
||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||
<div class='input-group-append'>
|
||||
<input type="submit" value="{% trans 'Set Language' %}" class="btn btn btn-primary">
|
||||
</div>
|
||||
<p>{% trans "Some languages are not complete" %}
|
||||
{% if ALL_LANG %}
|
||||
. <a href="{% url 'settings' %}">{% trans "Show only sufficent" %}</a>
|
||||
{% else %}
|
||||
and hidden. <a href="?alllang">{% trans "Show them too" %}</a>
|
||||
{% endif %}
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<h4>{% trans "Help the translation efforts!" %}</h4>
|
||||
|
@ -14,7 +14,8 @@
|
||||
<div class='row'>
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PART_STARRED" icon='fa-star' user_setting=True %}
|
||||
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PART_STARRED" icon='fa-bell' user_setting=True %}
|
||||
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_CATEGORY_STARRED" icon='fa-bell' user_setting=True %}
|
||||
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_PART_LATEST" icon='fa-history' user_setting=True %}
|
||||
{% include "InvenTree/settings/setting.html" with key="PART_RECENT_COUNT" icon="fa-clock" user_setting=True %}
|
||||
{% include "InvenTree/settings/setting.html" with key="HOMEPAGE_BOM_VALIDATION" user_setting=True %}
|
||||
|
@ -16,6 +16,7 @@
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_PREVIEW_RESULTS" user_setting=True icon='fa-search' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_SHOW_STOCK_LEVELS" user_setting=True icon='fa-boxes' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SEARCH_HIDE_INACTIVE_PARTS" user_setting=True icon='fa-eye-slash' %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
@ -22,12 +22,12 @@
|
||||
<a href="https://github.com/inventree/InvenTree/releases">{% inventree_version %}</a>{% include "clip.html" %}
|
||||
{% inventree_is_development as dev %}
|
||||
{% if dev %}
|
||||
<span class='badge rounded-pill bg-primary'>{% trans "Development Version" %}</span>
|
||||
<span class='badge badge-right rounded-pill bg-primary'>{% trans "Development Version" %}</span>
|
||||
{% else %}
|
||||
{% if up_to_date %}
|
||||
<span class='badge rounded-pill bg-success'>{% trans "Up to Date" %}</span>
|
||||
<span class='badge badge-right rounded-pill bg-success'>{% trans "Up to Date" %}</span>
|
||||
{% else %}
|
||||
<span class='badge rounded-pill bg-info'>{% trans "Update Available" %}</span>
|
||||
<span class='badge badge-right rounded-pill bg-info'>{% trans "Update Available" %}</span>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</td>
|
||||
|
@ -10,10 +10,30 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
|
||||
<!-- Favicon -->
|
||||
<link rel="apple-touch-icon" sizes="57x57" href="{% static 'img/favicon/apple-icon-57x57.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="60x60" href="{% static 'img/favicon/apple-icon-60x60.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="72x72" href="{% static 'img/favicon/apple-icon-72x72.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="76x76" href="{% static 'img/favicon/apple-icon-76x76.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="114x114" href="{% static 'img/favicon/apple-icon-114x114.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="120x120" href="{% static 'img/favicon/apple-icon-120x120.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="144x144" href="{% static 'img/favicon/apple-icon-144x144.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="152x152" href="{% static 'img/favicon/apple-icon-152x152.png' %}">
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="{% static 'img/favicon/apple-icon-180x180.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="192x192" href="{% static 'img/favicon/android-icon-192x192.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="{% static 'img/favicon/favicon-32x32.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="96x96" href="{% static 'img/favicon/favicon-96x96.png' %}">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="{% static 'img/favicon/favicon-16x16.png' %}">
|
||||
<link rel="manifest" href="{% static 'img/favicon/manifest.json' %}">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="msapplication-TileImage" content="{% static 'img/favicon/ms-icon-144x144.png' %}">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
|
||||
|
||||
<!-- CSS -->
|
||||
<link rel="stylesheet" href="{% static 'fontawesome/css/brands.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'fontawesome/css/solid.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'css/bootstrap_3.3.7_css_bootstrap.min.css' %}">
|
||||
<link rel="stylesheet" href="{% static 'bootstrap/css/bootstrap.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 'css/inventree.css' %}">
|
||||
@ -33,41 +53,60 @@
|
||||
<!--
|
||||
Background Image Attribution: https://unsplash.com/photos/Ixvv3YZkd7w
|
||||
-->
|
||||
<div class='container-fluid'>
|
||||
<div class='notification-area' id='alerts'>
|
||||
<!-- Div for displayed alerts -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class='main body-wrapper login-screen d-flex'>
|
||||
|
||||
<div class='main body-wrapper login-screen'>
|
||||
|
||||
<div class='login-container'>
|
||||
<div class="row">
|
||||
<div class='container-fluid'>
|
||||
|
||||
<div class='clearfix content-heading login-header'>
|
||||
<div class='clearfix content-heading login-header d-flex flex-wrap'>
|
||||
<img class="pull-left" src="{% static 'img/inventree.png' %}" width="60" height="60"/>
|
||||
<span><h3>{% inventree_title %}</h3></span>
|
||||
{% include "spacer.html" %}
|
||||
<span class='float-right'><h3>{% inventree_title %}</h3></span>
|
||||
</div>
|
||||
<hr>
|
||||
<div class='container-fluid'>{% block content %}{% endblock %}</div>
|
||||
</div>
|
||||
<div class='container-fluid'>
|
||||
<hr>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% block extra_body %}
|
||||
{% endblock %}
|
||||
|
||||
{% include 'notification.html' %}
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
<script type="text/javascript" src="{% static 'script/jquery_3.3.1_jquery.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/jquery-ui/jquery-ui.min.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'bootstrap/js/bootstrap.bundle.min.js' %}"></script>
|
||||
|
||||
<!-- general InvenTree -->
|
||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
|
||||
<!-- dynamic javascript templates -->
|
||||
<script type='text/javascript' src="{% url 'inventree.js' %}"></script>
|
||||
|
||||
<!-- fontawesome -->
|
||||
<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>
|
||||
|
||||
<!-- 3rd party general js -->
|
||||
<script type="text/javascript" src="{% static 'fullcalendar/main.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'fullcalendar/locales-all.js' %}"></script>
|
||||
<script type="text/javascript" src="{% static 'select2/js/select2.full.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/moment.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/chart.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/clipboard.min.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/randomColor.min.js' %}"></script>
|
||||
|
||||
|
||||
<script type='text/javascript'>
|
||||
|
||||
@ -75,12 +114,16 @@ $(document).ready(function () {
|
||||
// notifications
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
showAlertOrCache('alert-info', '{{message}}', true);
|
||||
showAlertOrCache(
|
||||
'{{ message }}',
|
||||
true,
|
||||
{
|
||||
style: 'info',
|
||||
}
|
||||
);
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
showCachedAlerts();
|
||||
|
||||
inventreeDocReady();
|
||||
});
|
||||
|
||||
|
@ -32,12 +32,13 @@ for a account and sign in below:{% endblocktrans %}</p>
|
||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}" />
|
||||
{% endif %}
|
||||
|
||||
<div class="btn-toolbar">
|
||||
<button class="btn btn-primary col-md-8" type="submit">{% trans "Sign In" %}</button>
|
||||
{% if mail_conf and enable_pwd_forgot %}
|
||||
<a class="btn btn-primary" href="{% url 'account_reset_password' %}">{% trans "Forgot Password?" %}</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
<hr>
|
||||
<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 %}
|
||||
<a class="" href="{% url 'account_reset_password' %}"><small>{% trans "Forgot Password?" %}</small></a>
|
||||
{% endif %}
|
||||
</form>
|
||||
|
||||
{% if enable_sso %}
|
||||
|
@ -14,7 +14,11 @@
|
||||
{% if redirect_field_value %}
|
||||
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
|
||||
{% endif %}
|
||||
<button type="submit" class="btn btn-primary btn-block">{% trans 'Sign Out' %}</button>
|
||||
<hr>
|
||||
<div class='btn-group float-right' role='group'>
|
||||
<a type='button' class='btn btn-secondary' href='{% url "index" %}'><span class='fas fa-undo-alt'></span> {% trans "Back to Site" %}</a>
|
||||
<button type="submit" class="btn btn-danger btn-block">{% trans 'Sign Out' %}</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
@ -14,7 +14,9 @@
|
||||
<form method="POST" action="{{ action_url }}">
|
||||
{% csrf_token %}
|
||||
{{ form|crispy }}
|
||||
<input type="submit" name="action" class="btn btn-primary btn-block" value="{% trans 'change password' %}"/>
|
||||
<div class='btn-group float-right' role='group'>
|
||||
<input type="submit" name="action" class="btn btn-success" value="{% trans 'Change password' %}"/>
|
||||
</div>
|
||||
</form>
|
||||
{% else %}
|
||||
<p>{% trans 'Your password is now changed.' %}</p>
|
||||
|
@ -1,10 +1,8 @@
|
||||
{% load i18n %}
|
||||
|
||||
<div id='attachment-buttons'>
|
||||
<div class='btn-group'>
|
||||
<div class='filter-list' id='filter-list-related'>
|
||||
<!-- An empty div in which the filter list will be constructed -->
|
||||
</div>
|
||||
<div class='btn-group' role='group'>
|
||||
{% include "filter_list.html" with id="related" %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -4,6 +4,7 @@
|
||||
|
||||
{% settings_value 'BARCODE_ENABLE' as barcodes %}
|
||||
{% settings_value 'REPORT_ENABLE_TEST_REPORT' as test_report_enabled %}
|
||||
{% settings_value "REPORT_ENABLE" as report_enabled %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
@ -83,7 +84,14 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<main class='col ps-md-2 pt-2'>
|
||||
<main class='col ps-md-2 pt-2 pe-2'>
|
||||
|
||||
{% block alerts %}
|
||||
<div class='notification-area' id='alerts'>
|
||||
<!-- Div for displayed alerts -->
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block breadcrumb_list %}
|
||||
<div class='container-fluid navigation'>
|
||||
<nav aria-label='breadcrumb'>
|
||||
@ -102,7 +110,6 @@
|
||||
</div>
|
||||
{% include 'modals.html' %}
|
||||
{% include 'about.html' %}
|
||||
{% include 'notification.html' %}
|
||||
</div>
|
||||
|
||||
<!-- Scripts -->
|
||||
@ -135,9 +142,9 @@
|
||||
|
||||
<!-- general InvenTree -->
|
||||
<script type='text/javascript' src="{% static 'script/inventree/notification.js' %}"></script>
|
||||
<script type='text/javascript' src="{% static 'script/inventree/inventree.js' %}"></script>
|
||||
|
||||
<!-- dynamic javascript templates -->
|
||||
<script type='text/javascript' src="{% url 'inventree.js' %}"></script>
|
||||
<script type='text/javascript' src="{% url 'calendar.js' %}"></script>
|
||||
<script type='text/javascript' src="{% url 'nav.js' %}"></script>
|
||||
<script type='text/javascript' src="{% url 'settings.js' %}"></script>
|
||||
@ -177,15 +184,25 @@ $(document).ready(function () {
|
||||
|
||||
inventreeDocReady();
|
||||
|
||||
showCachedAlerts();
|
||||
|
||||
{% if barcodes %}
|
||||
$('#barcode-scan').click(function() {
|
||||
barcodeScanDialog();
|
||||
});
|
||||
{% endif %}
|
||||
|
||||
moment.locale('{{request.LANGUAGE_CODE}}');
|
||||
moment.locale('{{ request.LANGUAGE_CODE }}');
|
||||
|
||||
// Account notifications
|
||||
{% if messages %}
|
||||
{% for message in messages %}
|
||||
showMessage(
|
||||
'{{ message }}',
|
||||
{
|
||||
style: 'info',
|
||||
}
|
||||
);
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
39
InvenTree/templates/email/build_order_required_stock.html
Normal file
39
InvenTree/templates/email/build_order_required_stock.html
Normal file
@ -0,0 +1,39 @@
|
||||
{% extends "email/email.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block title %}
|
||||
{% trans "Stock is required for the following build order" %}<br>
|
||||
{% blocktrans with build=build.reference part=part.full_name quantity=build.quantity %}Build order {{ build }} - building {{ quantity }} x {{ part }}{% endblocktrans %}
|
||||
<br>
|
||||
<p>{% trans "Click on the following link to view this build order" %}: <a href='{{ link }}'>{{ link }}</a></p>
|
||||
{% endblock title %}
|
||||
|
||||
{% block body %}
|
||||
<tr colspan='100%' style='height: 2rem; text-align: center;'>{% trans "The following parts are low on required stock" %}</tr>
|
||||
|
||||
<tr style="height: 3rem; border-bottom: 1px solid">
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Required Quantity" %}</th>
|
||||
<th>{% trans "Available" %}</th>
|
||||
</tr>
|
||||
|
||||
{% for line in lines %}
|
||||
<tr style="height: 2.5rem; border-bottom: 1px solid">
|
||||
<td style='padding-left: 1em;'>
|
||||
<a href='{{ line.link }}'>{{ line.part.full_name }}</a>{% if part.description %} - <em>{{ part.description }}</em>{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">
|
||||
{% decimal line.required %} {% if line.part.units %}{{ line.part.units }}{% endif %}
|
||||
</td>
|
||||
<td style="text-align: center;">{% decimal line.available %} {% if line.part.units %}{{ line.part.units }}{% endif %}</td>
|
||||
</tr>
|
||||
|
||||
{% endfor %}
|
||||
|
||||
{% endblock body %}
|
||||
|
||||
{% block footer_prefix %}
|
||||
<p><em>{% blocktrans with part=part.name %}You are receiving this email because you are subscribed to notifications for this part {% endblocktrans %}.</em></p>
|
||||
{% endblock footer_prefix %}
|
43
InvenTree/templates/email/email.html
Normal file
43
InvenTree/templates/email/email.html
Normal file
@ -0,0 +1,43 @@
|
||||
{% load i18n %}
|
||||
{% load static %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
<table style='border-collapse: collapse; width: 85%; margin-left: 10%; font-size: 1rem; border: 1px solid #68686a; border-radius: 2px;'>
|
||||
|
||||
{% block header %}
|
||||
<tr style='background: #eef3f7; height: 4rem; text-align: center;'>
|
||||
<th colspan="100%" style="padding-bottom: 1rem; color: #68686a;">
|
||||
{% block header_row %}
|
||||
<p style='font-size: 1.25rem;'>{% block title %}<!-- email title goes here -->{% endblock %}</p>
|
||||
{% block subtitle %}
|
||||
<!-- email subtitle goes here -->
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
</th>
|
||||
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<tr style="height: 3rem; border-bottom: 1px solid #68686a;">
|
||||
{% block body_row %}
|
||||
<!-- email body goes here -->
|
||||
{% endblock %}
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
||||
{% block footer %}
|
||||
<tr style='background: #eef3f7; height: 2rem;'>
|
||||
<td colspan="100%" style="padding-top:1rem; text-align: center">
|
||||
{% block footer_prefix %}
|
||||
<!-- Custom footer information goes here -->
|
||||
{% endblock %}
|
||||
<p><em><small>{% trans "InvenTree version" %}: {% inventree_version %} - <a href='https://inventree.readthedocs.io'>inventree.readthedocs.io</a></small></em></p>
|
||||
{% block footer_suffix %}
|
||||
<!-- Custom footer information goes here -->
|
||||
{% endblock %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endblock %}
|
||||
|
||||
</table>
|
32
InvenTree/templates/email/low_stock_notification.html
Normal file
32
InvenTree/templates/email/low_stock_notification.html
Normal file
@ -0,0 +1,32 @@
|
||||
{% extends "email/email.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load inventree_extras %}
|
||||
|
||||
{% block title %}
|
||||
{% blocktrans with part=part.name %} The available stock for {{ part }} has fallen below the configured minimum level{% endblocktrans %}
|
||||
{% if link %}
|
||||
<p>{% trans "Click on the following link to view this part" %}: <a href="{{ link }}">{{ link }}</a></p>
|
||||
{% endif %}
|
||||
{% endblock title %}
|
||||
|
||||
|
||||
{% block body %}
|
||||
<tr style="height: 3rem; border-bottom: 1px solid">
|
||||
<th>{% trans "Part" %}</th>
|
||||
<th>{% trans "Total Stock" %}</th>
|
||||
<th>{% trans "Available" %}</th>
|
||||
<th>{% trans "Minimum Quantity" %}</th>
|
||||
</tr>
|
||||
|
||||
<tr style="height: 3rem">
|
||||
<td style="text-align: center;">{{ part.full_name }}</td>
|
||||
<td style="text-align: center;">{% decimal part.total_stock %}</td>
|
||||
<td style="text-align: center;">{% decimal part.available_stock %}</td>
|
||||
<td style="text-align: center;">{% decimal part.minimum_stock %}</td>
|
||||
</tr>
|
||||
{% endblock body %}
|
||||
|
||||
{% block footer_prefix %}
|
||||
<p><em>{% blocktrans with part=part.name %}You are receiving this email because you are subscribed to notifications for this part {% endblocktrans %}.</em></p>
|
||||
{% endblock footer_prefix %}
|
1
InvenTree/templates/filter_list.html
Normal file
1
InvenTree/templates/filter_list.html
Normal file
@ -0,0 +1 @@
|
||||
<div class='filter-list d-flex flex-row form-row' id='filter-list-{{ id }}'><!-- Empty div for table filters --></div>
|
@ -1,312 +0,0 @@
|
||||
{% load inventree_extras %}
|
||||
|
||||
/* globals
|
||||
ClipboardJS,
|
||||
inventreeFormDataUpload,
|
||||
launchModalForm,
|
||||
user_settings,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
attachClipboard,
|
||||
enableDragAndDrop,
|
||||
exportFormatOptions,
|
||||
inventreeDocReady,
|
||||
inventreeLoad,
|
||||
inventreeSave,
|
||||
*/
|
||||
|
||||
function attachClipboard(selector, containerselector, textElement) {
|
||||
// set container
|
||||
if (containerselector) {
|
||||
containerselector = document.getElementById(containerselector);
|
||||
} else {
|
||||
containerselector = document.body;
|
||||
}
|
||||
|
||||
var text = null;
|
||||
|
||||
// set text-function
|
||||
if (textElement) {
|
||||
text = function() {
|
||||
return document.getElementById(textElement).textContent;
|
||||
};
|
||||
} else {
|
||||
text = function(trigger) {
|
||||
var content = trigger.parentElement.parentElement.textContent;
|
||||
return content.trim();
|
||||
};
|
||||
}
|
||||
|
||||
// create Clipboard
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
var cis = new ClipboardJS(selector, {
|
||||
text: text,
|
||||
container: containerselector
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a standard list of export format options *
|
||||
*/
|
||||
function exportFormatOptions() {
|
||||
return [
|
||||
{
|
||||
value: 'csv',
|
||||
display_name: 'CSV',
|
||||
},
|
||||
{
|
||||
value: 'tsv',
|
||||
display_name: 'TSV',
|
||||
},
|
||||
{
|
||||
value: 'xls',
|
||||
display_name: 'XLS',
|
||||
},
|
||||
{
|
||||
value: 'xlsx',
|
||||
display_name: 'XLSX',
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
function inventreeDocReady() {
|
||||
/* Run this function when the HTML document is loaded.
|
||||
* This will be called for every page that extends "base.html"
|
||||
*/
|
||||
|
||||
window.addEventListener('dragover', function(e) {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
window.addEventListener('drop', function(e) {
|
||||
e = e || event;
|
||||
e.preventDefault();
|
||||
}, false);
|
||||
|
||||
/* Add drag-n-drop functionality to any element
|
||||
* marked with the class 'dropzone'
|
||||
*/
|
||||
$('.dropzone').on('dragenter', function(event) {
|
||||
|
||||
// TODO - Only indicate that a drop event will occur if a file is being dragged
|
||||
var transfer = event.originalEvent.dataTransfer;
|
||||
|
||||
// eslint-disable-next-line no-constant-condition
|
||||
if (true || isFileTransfer(transfer)) {
|
||||
$(this).addClass('dragover');
|
||||
}
|
||||
});
|
||||
|
||||
$('.dropzone').on('dragleave drop', function() {
|
||||
$(this).removeClass('dragover');
|
||||
});
|
||||
|
||||
// Callback to launch the 'About' window
|
||||
$('#launch-about').click(function() {
|
||||
var modal = $('#modal-about');
|
||||
|
||||
modal.modal({
|
||||
backdrop: 'static',
|
||||
keyboard: true,
|
||||
});
|
||||
|
||||
modal.modal('show');
|
||||
});
|
||||
|
||||
// Callback to launch the 'Database Stats' window
|
||||
$('#launch-stats').click(function() {
|
||||
launchModalForm('/stats/', {
|
||||
no_post: true,
|
||||
});
|
||||
});
|
||||
|
||||
// Initialize clipboard-buttons
|
||||
attachClipboard('.clip-btn');
|
||||
attachClipboard('.clip-btn', 'modal-about');
|
||||
attachClipboard('.clip-btn-version', 'modal-about', 'about-copy-text');
|
||||
|
||||
// Add autocomplete to the search-bar
|
||||
$('#search-bar').autocomplete({
|
||||
source: function(request, response) {
|
||||
$.ajax({
|
||||
url: '/api/part/',
|
||||
data: {
|
||||
search: request.term,
|
||||
limit: user_settings.SEARCH_PREVIEW_RESULTS,
|
||||
offset: 0
|
||||
},
|
||||
success: function(data) {
|
||||
|
||||
var transformed = $.map(data.results, function(el) {
|
||||
return {
|
||||
label: el.full_name,
|
||||
id: el.pk,
|
||||
thumbnail: el.thumbnail,
|
||||
data: el,
|
||||
};
|
||||
});
|
||||
response(transformed);
|
||||
},
|
||||
error: function() {
|
||||
response([]);
|
||||
}
|
||||
});
|
||||
},
|
||||
create: function() {
|
||||
$(this).data('ui-autocomplete')._renderItem = function(ul, item) {
|
||||
|
||||
var html = `<a href='/part/${item.id}/'><span>`;
|
||||
|
||||
html += `<img class='hover-img-thumb' src='`;
|
||||
html += item.thumbnail || `/static/img/blank_image.png`;
|
||||
html += `'> `;
|
||||
html += item.label;
|
||||
|
||||
html += '</span>';
|
||||
|
||||
if (user_settings.SEARCH_SHOW_STOCK_LEVELS) {
|
||||
html += partStockLabel(item.data);
|
||||
}
|
||||
|
||||
html += '</a>';
|
||||
|
||||
return $('<li>').append(html).appendTo(ul);
|
||||
};
|
||||
},
|
||||
select: function( event, ui ) {
|
||||
window.location = '/part/' + ui.item.id + '/';
|
||||
},
|
||||
minLength: 2,
|
||||
classes: {
|
||||
'ui-autocomplete': 'dropdown-menu search-menu',
|
||||
},
|
||||
});
|
||||
|
||||
// Generate brand-icons
|
||||
$('.brand-icon').each(function(i, obj) {
|
||||
loadBrandIcon($(this), $(this).attr('brand_name'));
|
||||
});
|
||||
|
||||
// Callback for "admin view" button
|
||||
$('#admin-button').click(function() {
|
||||
var url = $(this).attr('url');
|
||||
|
||||
location.href = url;
|
||||
});
|
||||
}
|
||||
|
||||
function isFileTransfer(transfer) {
|
||||
/* Determine if a transfer (e.g. drag-and-drop) is a file transfer
|
||||
*/
|
||||
|
||||
return transfer.files.length > 0;
|
||||
}
|
||||
|
||||
|
||||
function enableDragAndDrop(element, url, options) {
|
||||
/* Enable drag-and-drop file uploading for a given element.
|
||||
|
||||
Params:
|
||||
element - HTML element lookup string e.g. "#drop-div"
|
||||
url - URL to POST the file to
|
||||
options - object with following possible values:
|
||||
label - Label of the file to upload (default='file')
|
||||
data - Other form data to upload
|
||||
success - Callback function in case of success
|
||||
error - Callback function in case of error
|
||||
method - HTTP method
|
||||
*/
|
||||
|
||||
var data = options.data || {};
|
||||
|
||||
$(element).on('drop', function(event) {
|
||||
|
||||
var transfer = event.originalEvent.dataTransfer;
|
||||
|
||||
var label = options.label || 'file';
|
||||
|
||||
var formData = new FormData();
|
||||
|
||||
// Add the extra data
|
||||
for (var key in data) {
|
||||
formData.append(key, data[key]);
|
||||
}
|
||||
|
||||
if (isFileTransfer(transfer)) {
|
||||
formData.append(label, transfer.files[0]);
|
||||
|
||||
inventreeFormDataUpload(
|
||||
url,
|
||||
formData,
|
||||
{
|
||||
success: function(data, status, xhr) {
|
||||
console.log('Uploaded file via drag-and-drop');
|
||||
if (options.success) {
|
||||
options.success(data, status, xhr);
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.log('File upload failed');
|
||||
if (options.error) {
|
||||
options.error(xhr, status, error);
|
||||
}
|
||||
},
|
||||
method: options.method || 'POST',
|
||||
}
|
||||
);
|
||||
} else {
|
||||
console.log('Ignoring drag-and-drop event (not a file)');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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}`;
|
||||
localStorage.setItem(key, value);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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);
|
||||
|
||||
if (value == null) {
|
||||
return defaultValue;
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
function loadBrandIcon(element, name) {
|
||||
// check if icon exists
|
||||
var icon = window.FontAwesome.icon({prefix: 'fab', iconName: name});
|
||||
|
||||
if (icon) {
|
||||
// add icon to button
|
||||
element.addClass('fab fa-' + name);
|
||||
}
|
||||
}
|
||||
|
||||
// Convenience function to determine if an element exists
|
||||
$.fn.exists = function() {
|
||||
return this.length !== 0;
|
||||
};
|
@ -1,6 +1,7 @@
|
||||
{% load inventree_extras %}
|
||||
|
||||
/* exported
|
||||
editSetting,
|
||||
user_settings,
|
||||
global_settings,
|
||||
*/
|
||||
@ -18,3 +19,83 @@ const global_settings = {
|
||||
{{ key }}: {% primitive_to_javascript value %},
|
||||
{% endfor %}
|
||||
};
|
||||
|
||||
/*
|
||||
* Edit a setting value
|
||||
*/
|
||||
function editSetting(pk, options={}) {
|
||||
|
||||
// Is this a global setting or a user setting?
|
||||
var global = options.global || false;
|
||||
|
||||
var url = '';
|
||||
|
||||
if (global) {
|
||||
url = `/api/settings/global/${pk}/`;
|
||||
} else {
|
||||
url = `/api/settings/user/${pk}/`;
|
||||
}
|
||||
|
||||
// First, read the settings object from the server
|
||||
inventreeGet(url, {}, {
|
||||
success: function(response) {
|
||||
|
||||
if (response.choices && response.choices.length > 0) {
|
||||
response.type = 'choice';
|
||||
}
|
||||
|
||||
// Construct the field
|
||||
var fields = {
|
||||
value: {
|
||||
label: response.name,
|
||||
help_text: response.description,
|
||||
type: response.type,
|
||||
choices: response.choices,
|
||||
}
|
||||
};
|
||||
|
||||
constructChangeForm(fields, {
|
||||
url: url,
|
||||
method: 'PATCH',
|
||||
title: options.title,
|
||||
processResults: function(data, fields, opts) {
|
||||
|
||||
switch (data.type) {
|
||||
case 'boolean':
|
||||
// Convert to boolean value
|
||||
data.value = data.value.toString().toLowerCase() == 'true';
|
||||
break;
|
||||
case 'integer':
|
||||
// Convert to integer value
|
||||
data.value = parseInt(data.value.toString());
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return data;
|
||||
},
|
||||
processBeforeUpload: function(data) {
|
||||
// Convert value to string
|
||||
data.value = data.value.toString();
|
||||
|
||||
return data;
|
||||
},
|
||||
onSuccess: function(response) {
|
||||
|
||||
var setting = response.key;
|
||||
|
||||
if (response.type == 'boolean') {
|
||||
var enabled = response.value.toString().toLowerCase() == 'true';
|
||||
$(`#setting-value-${setting}`).prop('checked', enabled);
|
||||
} else {
|
||||
$(`#setting-value-${setting}`).html(response.value);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(xhr) {
|
||||
showApiError(xhr, url);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -2,8 +2,6 @@
|
||||
{% load inventree_extras %}
|
||||
|
||||
/* globals
|
||||
renderErrorMessage,
|
||||
showAlertDialog,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
@ -63,11 +61,17 @@ function inventreeGet(url, filters={}, options={}) {
|
||||
},
|
||||
error: function(xhr, ajaxOptions, thrownError) {
|
||||
console.error('Error on GET at ' + url);
|
||||
console.error(thrownError);
|
||||
|
||||
if (thrownError) {
|
||||
console.error('Error: ' + thrownError);
|
||||
}
|
||||
|
||||
if (options.error) {
|
||||
options.error({
|
||||
error: thrownError
|
||||
});
|
||||
} else {
|
||||
showApiError(xhr, url);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -104,6 +108,8 @@ function inventreeFormDataUpload(url, data, options={}) {
|
||||
|
||||
if (options.error) {
|
||||
options.error(xhr, status, error);
|
||||
} else {
|
||||
showApiError(xhr, url);
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -139,6 +145,8 @@ function inventreePut(url, data={}, options={}) {
|
||||
} else {
|
||||
console.error(`Error on ${method} to '${url}' - STATUS ${xhr.status}`);
|
||||
console.error(thrownError);
|
||||
|
||||
showApiError(xhr, url);
|
||||
}
|
||||
},
|
||||
complete: function(xhr, status) {
|
||||
@ -162,13 +170,15 @@ function inventreeDelete(url, options={}) {
|
||||
return inventreePut(url, {}, options);
|
||||
}
|
||||
|
||||
|
||||
function showApiError(xhr) {
|
||||
/*
|
||||
* Display a notification with error information
|
||||
*/
|
||||
function showApiError(xhr, url) {
|
||||
|
||||
var title = null;
|
||||
var message = null;
|
||||
|
||||
switch (xhr.status) {
|
||||
switch (xhr.status || 0) {
|
||||
// No response
|
||||
case 0:
|
||||
title = '{% trans "No Response" %}';
|
||||
@ -208,7 +218,11 @@ function showApiError(xhr) {
|
||||
}
|
||||
|
||||
message += '<hr>';
|
||||
message += renderErrorMessage(xhr);
|
||||
message += `URL: ${url}`;
|
||||
|
||||
showAlertDialog(title, message);
|
||||
showMessage(title, {
|
||||
style: 'danger',
|
||||
icon: 'fas fa-server icon-red',
|
||||
details: message,
|
||||
});
|
||||
}
|
||||
|
@ -10,7 +10,6 @@
|
||||
modalSetSubmitText,
|
||||
modalShowSubmitButton,
|
||||
modalSubmit,
|
||||
showAlertOrCache,
|
||||
showQuestionDialog,
|
||||
*/
|
||||
|
||||
@ -36,7 +35,7 @@ function makeBarcodeInput(placeholderText='', hintText='') {
|
||||
<label class='control-label' for='barcode'>{% trans "Barcode" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'>
|
||||
<span class='input-group-text'>
|
||||
<span class='fas fa-qrcode'></span>
|
||||
</span>
|
||||
<input id='barcode' class='textinput textInput form-control' type='text' name='barcode' placeholder='${placeholderText}'>
|
||||
@ -59,7 +58,7 @@ function makeNotesField(options={}) {
|
||||
<label class='control-label' for='notes'>{% trans "Notes" %}</label>
|
||||
<div class='controls'>
|
||||
<div class='input-group'>
|
||||
<span class='input-group-addon'>
|
||||
<span class='input-group-text'>
|
||||
<span class='fas fa-sticky-note'></span>
|
||||
</span>
|
||||
<input id='notes' class='textinput textInput form-control' type='text' name='notes' placeholder='${placeholder}'>
|
||||
@ -258,7 +257,7 @@ function barcodeDialog(title, options={}) {
|
||||
|
||||
$(modal).modal({
|
||||
backdrop: 'static',
|
||||
keyboard: false,
|
||||
keyboard: user_settings.FORMS_CLOSE_USING_ESCAPE,
|
||||
});
|
||||
|
||||
if (options.preShow) {
|
||||
@ -480,10 +479,13 @@ function barcodeCheckIn(location_id) {
|
||||
$(modal).modal('hide');
|
||||
if (status == 'success' && 'success' in response) {
|
||||
|
||||
showAlertOrCache('alert-success', response.success, true);
|
||||
addCachedAlert(response.success);
|
||||
location.reload();
|
||||
} else {
|
||||
showAlertOrCache('alert-success', '{% trans "Error transferring stock" %}', false);
|
||||
showMessage('{% trans "Error transferring stock" %}', {
|
||||
style: 'danger',
|
||||
icon: 'fas fa-times-circle',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -604,10 +606,12 @@ function scanItemsIntoLocation(item_id_list, options={}) {
|
||||
$(modal).modal('hide');
|
||||
|
||||
if (status == 'success' && 'success' in response) {
|
||||
showAlertOrCache('alert-success', response.success, true);
|
||||
addCachedAlert(response.success);
|
||||
location.reload();
|
||||
} else {
|
||||
showAlertOrCache('alert-danger', '{% trans "Error transferring stock" %}', false);
|
||||
showMessage('{% trans "Error transferring stock" %}', {
|
||||
style: 'danger',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -43,11 +43,18 @@ function buildFormFields() {
|
||||
}
|
||||
},
|
||||
sales_order: {
|
||||
icon: 'fa-truck',
|
||||
},
|
||||
batch: {},
|
||||
target_date: {},
|
||||
take_from: {},
|
||||
destination: {},
|
||||
target_date: {
|
||||
icon: 'fa-calendar-alt',
|
||||
},
|
||||
take_from: {
|
||||
icon: 'fa-sitemap',
|
||||
},
|
||||
destination: {
|
||||
icon: 'fa-sitemap',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
@ -339,7 +346,7 @@ function completeBuildOutputs(build_id, outputs, options={}) {
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
showApiError(xhr);
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -1527,7 +1534,7 @@ function allocateStockToBuild(build_id, part_id, bom_items, options={}) {
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
showApiError(xhr);
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -281,7 +281,7 @@ function setupFilterList(tableKey, table, target) {
|
||||
// One blank slate, please
|
||||
element.empty();
|
||||
|
||||
element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-tag'><span class='fas fa-redo-alt'></span></button>`);
|
||||
element.append(`<button id='reload-${tableKey}' title='{% trans "Reload data" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-redo-alt'></span></button>`);
|
||||
|
||||
// Callback for reloading the table
|
||||
element.find(`#reload-${tableKey}`).click(function() {
|
||||
@ -293,11 +293,10 @@ function setupFilterList(tableKey, table, target) {
|
||||
return;
|
||||
}
|
||||
|
||||
// If there are filters currently "in use", add them in!
|
||||
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-tag'><span class='fas fa-filter'></span></button>`);
|
||||
element.append(`<button id='${add}' title='{% trans "Add new filter" %}' class='btn btn-outline-secondary filter-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-tag'><span class='fas fa-backspace icon-red'></span></button>`);
|
||||
element.append(`<button id='${clear}' title='{% trans "Clear all filters" %}' class='btn btn-outline-secondary filter-button'><span class='fas fa-backspace icon-red'></span></button>`);
|
||||
}
|
||||
|
||||
for (var key in filters) {
|
||||
@ -320,7 +319,7 @@ function setupFilterList(tableKey, table, target) {
|
||||
html += generateAvailableFilterList(tableKey);
|
||||
html += generateFilterInput(tableKey);
|
||||
|
||||
html += `<button title='{% trans "Create filter" %}' class='btn btn-outline-secondary filter-tag' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
html += `<button title='{% trans "Create filter" %}' class='btn btn-outline-secondary filter-button' id='${make}'><span class='fas fa-plus'></span></button>`;
|
||||
|
||||
element.append(html);
|
||||
|
||||
|
@ -19,13 +19,17 @@
|
||||
renderStockLocation,
|
||||
renderSupplierPart,
|
||||
renderUser,
|
||||
showAlertDialog,
|
||||
showAlertOrCache,
|
||||
showApiError,
|
||||
*/
|
||||
|
||||
/* exported
|
||||
setFormGroupVisibility
|
||||
clearFormInput,
|
||||
disableFormInput,
|
||||
enableFormInput,
|
||||
hideFormInput,
|
||||
setFormGroupVisibility,
|
||||
showFormInput,
|
||||
*/
|
||||
|
||||
/**
|
||||
@ -113,6 +117,10 @@ function canDelete(OPTIONS) {
|
||||
*/
|
||||
function getApiEndpointOptions(url, callback) {
|
||||
|
||||
if (!url) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Return the ajax request object
|
||||
$.ajax({
|
||||
url: url,
|
||||
@ -123,9 +131,10 @@ function getApiEndpointOptions(url, callback) {
|
||||
json: 'application/json',
|
||||
},
|
||||
success: callback,
|
||||
error: function() {
|
||||
error: function(xhr) {
|
||||
// TODO: Handle error
|
||||
console.log(`ERROR in getApiEndpointOptions at '${url}'`);
|
||||
showApiError(xhr, url);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -181,6 +190,7 @@ function constructChangeForm(fields, options) {
|
||||
// Request existing data from the API endpoint
|
||||
$.ajax({
|
||||
url: options.url,
|
||||
data: options.params || {},
|
||||
type: 'GET',
|
||||
contentType: 'application/json',
|
||||
dataType: 'json',
|
||||
@ -188,6 +198,17 @@ function constructChangeForm(fields, options) {
|
||||
json: 'application/json',
|
||||
},
|
||||
success: function(data) {
|
||||
|
||||
// An optional function can be provided to process the returned results,
|
||||
// before they are rendered to the form
|
||||
if (options.processResults) {
|
||||
var processed = options.processResults(data, fields, options);
|
||||
|
||||
// If the processResults function returns data, it will be stored
|
||||
if (processed) {
|
||||
data = processed;
|
||||
}
|
||||
}
|
||||
|
||||
// Push existing 'value' to each field
|
||||
for (const field in data) {
|
||||
@ -199,12 +220,14 @@ function constructChangeForm(fields, options) {
|
||||
|
||||
// Store the entire data object
|
||||
options.instance = data;
|
||||
|
||||
|
||||
constructFormBody(fields, options);
|
||||
},
|
||||
error: function() {
|
||||
error: function(xhr) {
|
||||
// TODO: Handle error here
|
||||
console.log(`ERROR in constructChangeForm at '${options.url}'`);
|
||||
|
||||
showApiError(xhr, options.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -241,9 +264,11 @@ function constructDeleteForm(fields, options) {
|
||||
|
||||
constructFormBody(fields, options);
|
||||
},
|
||||
error: function() {
|
||||
error: function(xhr) {
|
||||
// TODO: Handle error here
|
||||
console.log(`ERROR in constructDeleteForm at '${options.url}`);
|
||||
|
||||
showApiError(xhr, options.url);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -321,10 +346,12 @@ function constructForm(url, options) {
|
||||
constructCreateForm(OPTIONS.actions.POST, options);
|
||||
} else {
|
||||
// User does not have permission to POST to the endpoint
|
||||
showAlertDialog(
|
||||
'{% trans "Action Prohibited" %}',
|
||||
'{% trans "Create operation not allowed" %}'
|
||||
);
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "Create operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
console.log(`'POST action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -334,10 +361,12 @@ function constructForm(url, options) {
|
||||
constructChangeForm(OPTIONS.actions.PUT, options);
|
||||
} else {
|
||||
// User does not have permission to PUT/PATCH to the endpoint
|
||||
showAlertDialog(
|
||||
'{% trans "Action Prohibited" %}',
|
||||
'{% trans "Update operation not allowed" %}'
|
||||
);
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "Update operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
console.log(`${options.method} action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -346,10 +375,12 @@ function constructForm(url, options) {
|
||||
constructDeleteForm(OPTIONS.actions.DELETE, options);
|
||||
} else {
|
||||
// User does not have permission to DELETE to the endpoint
|
||||
showAlertDialog(
|
||||
'{% trans "Action Prohibited" %}',
|
||||
'{% trans "Delete operation not allowed" %}'
|
||||
);
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "Delete operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
console.log(`DELETE action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -358,10 +389,12 @@ function constructForm(url, options) {
|
||||
// TODO?
|
||||
} else {
|
||||
// User does not have permission to GET to the endpoint
|
||||
showAlertDialog(
|
||||
'{% trans "Action Prohibited" %}',
|
||||
'{% trans "View operation not allowed" %}'
|
||||
);
|
||||
showMessage('{% trans "Action Prohibited" %}', {
|
||||
style: 'danger',
|
||||
details: '{% trans "View operation not allowed" %}',
|
||||
icon: 'fas fa-user-times',
|
||||
});
|
||||
|
||||
console.log(`GET action unavailable at ${url}`);
|
||||
}
|
||||
break;
|
||||
@ -691,6 +724,11 @@ function submitFormData(fields, options) {
|
||||
data = form_data;
|
||||
}
|
||||
|
||||
// Optionally pre-process the data before uploading to the server
|
||||
if (options.processBeforeUpload) {
|
||||
data = options.processBeforeUpload(data);
|
||||
}
|
||||
|
||||
// Submit data
|
||||
upload_func(
|
||||
options.url,
|
||||
@ -708,7 +746,9 @@ function submitFormData(fields, options) {
|
||||
break;
|
||||
default:
|
||||
$(options.modal).modal('hide');
|
||||
showApiError(xhr);
|
||||
|
||||
console.log(`upload error at ${options.url}`);
|
||||
showApiError(xhr, options.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -885,19 +925,19 @@ function handleFormSuccess(response, options) {
|
||||
|
||||
// Display any messages
|
||||
if (response && response.success) {
|
||||
showAlertOrCache('alert-success', response.success, cache);
|
||||
showAlertOrCache(response.success, cache, {style: 'success'});
|
||||
}
|
||||
|
||||
if (response && response.info) {
|
||||
showAlertOrCache('alert-info', response.info, cache);
|
||||
showAlertOrCache(response.info, cache, {style: 'info'});
|
||||
}
|
||||
|
||||
if (response && response.warning) {
|
||||
showAlertOrCache('alert-warning', response.warning, cache);
|
||||
showAlertOrCache(response.warning, cache, {style: 'warning'});
|
||||
}
|
||||
|
||||
if (response && response.danger) {
|
||||
showAlertOrCache('alert-danger', response.danger, cache);
|
||||
showAlertOrCache(response.danger, cache, {style: 'danger'});
|
||||
}
|
||||
|
||||
if (options.onSuccess) {
|
||||
@ -1236,6 +1276,35 @@ function initializeGroups(fields, options) {
|
||||
}
|
||||
}
|
||||
|
||||
// Clear a form input
|
||||
function clearFormInput(name, options) {
|
||||
updateFieldValue(name, null, {}, options);
|
||||
}
|
||||
|
||||
// Disable a form input
|
||||
function disableFormInput(name, options) {
|
||||
$(options.modal).find(`#id_${name}`).prop('disabled', true);
|
||||
}
|
||||
|
||||
|
||||
// Enable a form input
|
||||
function enableFormInput(name, options) {
|
||||
$(options.modal).find(`#id_${name}`).prop('disabled', false);
|
||||
}
|
||||
|
||||
|
||||
// Hide a form input
|
||||
function hideFormInput(name, options) {
|
||||
$(options.modal).find(`#div_id_${name}`).hide();
|
||||
}
|
||||
|
||||
|
||||
// Show a form input
|
||||
function showFormInput(name, options) {
|
||||
$(options.modal).find(`#div_id_${name}`).show();
|
||||
}
|
||||
|
||||
|
||||
// Hide a form group
|
||||
function hideFormGroup(group, options) {
|
||||
$(options.modal).find(`#form-panel-${group}`).hide();
|
||||
|
@ -399,19 +399,19 @@ function afterForm(response, options) {
|
||||
|
||||
// Display any messages
|
||||
if (response.success) {
|
||||
showAlertOrCache('alert-success', response.success, cache);
|
||||
showAlertOrCache(response.success, cache, {style: 'success'});
|
||||
}
|
||||
|
||||
if (response.info) {
|
||||
showAlertOrCache('alert-info', response.info, cache);
|
||||
showAlertOrCache(response.info, cache, {style: 'info'});
|
||||
}
|
||||
|
||||
if (response.warning) {
|
||||
showAlertOrCache('alert-warning', response.warning, cache);
|
||||
showAlertOrCache(response.warning, cache, {style: 'warning'});
|
||||
}
|
||||
|
||||
if (response.danger) {
|
||||
showAlertOrCache('alert-danger', response.danger, cache);
|
||||
showAlertOrCache(response.danger, cache, {style: 'danger'});
|
||||
}
|
||||
|
||||
// Was a callback provided?
|
||||
|
@ -589,7 +589,7 @@ function receivePurchaseOrderItems(order_id, line_items, options={}) {
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
showApiError(xhr);
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -373,24 +373,23 @@ function duplicatePart(pk, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/* Toggle the 'starred' status of a part.
|
||||
* Performs AJAX queries and updates the display on the button.
|
||||
*
|
||||
* options:
|
||||
* - button: ID of the button (default = '#part-star-icon')
|
||||
* - URL: API url of the object
|
||||
* - user: pk of the user
|
||||
*/
|
||||
function toggleStar(options) {
|
||||
/* Toggle the 'starred' status of a part.
|
||||
* Performs AJAX queries and updates the display on the button.
|
||||
*
|
||||
* options:
|
||||
* - button: ID of the button (default = '#part-star-icon')
|
||||
* - part: pk of the part object
|
||||
* - user: pk of the user
|
||||
*/
|
||||
|
||||
var url = `/api/part/${options.part}/`;
|
||||
|
||||
inventreeGet(url, {}, {
|
||||
inventreeGet(options.url, {}, {
|
||||
success: function(response) {
|
||||
|
||||
var starred = response.starred;
|
||||
|
||||
inventreePut(
|
||||
url,
|
||||
options.url,
|
||||
{
|
||||
starred: !starred,
|
||||
},
|
||||
@ -398,9 +397,19 @@ function toggleStar(options) {
|
||||
method: 'PATCH',
|
||||
success: function(response) {
|
||||
if (response.starred) {
|
||||
$(options.button).addClass('icon-yellow');
|
||||
$(options.button).removeClass('fa fa-bell-slash').addClass('fas fa-bell icon-green');
|
||||
$(options.button).attr('title', '{% trans "You are subscribed to notifications for this item" %}');
|
||||
|
||||
showMessage('{% trans "You have subscribed to notifications for this item" %}', {
|
||||
style: 'success',
|
||||
});
|
||||
} else {
|
||||
$(options.button).removeClass('icon-yellow');
|
||||
$(options.button).removeClass('fas fa-bell icon-green').addClass('fa fa-bell-slash');
|
||||
$(options.button).attr('title', '{% trans "Subscribe to notifications for this item" %}');
|
||||
|
||||
showMessage('{% trans "You have unsubscribed to notifications for this item" %}', {
|
||||
style: 'warning',
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -410,12 +419,12 @@ function toggleStar(options) {
|
||||
}
|
||||
|
||||
|
||||
function partStockLabel(part) {
|
||||
function partStockLabel(part, options={}) {
|
||||
|
||||
if (part.in_stock) {
|
||||
return `<span class='badge rounded-pill bg-success'>{% trans "Stock" %}: ${part.in_stock}</span>`;
|
||||
return `<span class='badge rounded-pill bg-success ${options.classes}'>{% trans "Stock" %}: ${part.in_stock}</span>`;
|
||||
} else {
|
||||
return `<span class='badge rounded-pill bg-danger'>{% trans "No Stock" %}</span>`;
|
||||
return `<span class='badge rounded-pill bg-danger ${options.classes}'>{% trans "No Stock" %}</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -443,7 +452,7 @@ function makePartIcons(part) {
|
||||
}
|
||||
|
||||
if (part.starred) {
|
||||
html += makeIconBadge('fa-star', '{% trans "Starred part" %}');
|
||||
html += makeIconBadge('fa-bell icon-green', '{% trans "Subscribed part" %}');
|
||||
}
|
||||
|
||||
if (part.salable) {
|
||||
@ -451,7 +460,7 @@ function makePartIcons(part) {
|
||||
}
|
||||
|
||||
if (!part.active) {
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span>`;
|
||||
html += `<span class='badge badge-right rounded-pill bg-warning'>{% trans "Inactive" %}</span> `;
|
||||
}
|
||||
|
||||
return html;
|
||||
@ -983,7 +992,7 @@ function loadPartTable(table, url, options={}) {
|
||||
}
|
||||
});
|
||||
|
||||
var grid_view = inventreeLoad('part-grid-view') == 1;
|
||||
var grid_view = options.gridView && inventreeLoad('part-grid-view') == 1;
|
||||
|
||||
$(table).inventreeTable({
|
||||
url: url,
|
||||
@ -1011,7 +1020,7 @@ function loadPartTable(table, url, options={}) {
|
||||
$('#view-part-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
}
|
||||
},
|
||||
buttons: [
|
||||
buttons: options.gridView ? [
|
||||
{
|
||||
icon: 'fas fa-bars',
|
||||
attributes: {
|
||||
@ -1044,7 +1053,7 @@ function loadPartTable(table, url, options={}) {
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
] : [],
|
||||
customView: function(data) {
|
||||
|
||||
var html = '';
|
||||
@ -1133,8 +1142,10 @@ function loadPartTable(table, url, options={}) {
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display a table of part categories
|
||||
*/
|
||||
function loadPartCategoryTable(table, options) {
|
||||
/* Display a table of part categories */
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
@ -1148,6 +1159,13 @@ function loadPartCategoryTable(table, options) {
|
||||
filters = loadTableFilters(filterKey);
|
||||
}
|
||||
|
||||
|
||||
var tree_view = options.allowTreeView && inventreeLoad('category-tree-view') == 1;
|
||||
|
||||
if (tree_view) {
|
||||
params.cascade = true;
|
||||
}
|
||||
|
||||
var original = {};
|
||||
|
||||
for (var key in params) {
|
||||
@ -1157,15 +1175,13 @@ function loadPartCategoryTable(table, options) {
|
||||
|
||||
setupFilterList(filterKey, table, filterListElement);
|
||||
|
||||
var tree_view = inventreeLoad('category-tree-view') == 1;
|
||||
|
||||
table.inventreeTable({
|
||||
treeEnable: tree_view,
|
||||
rootParentId: options.params.parent,
|
||||
rootParentId: tree_view ? options.params.parent : null,
|
||||
uniqueId: 'pk',
|
||||
idField: 'pk',
|
||||
treeShowField: 'name',
|
||||
parentIdField: 'parent',
|
||||
parentIdField: tree_view ? 'parent' : null,
|
||||
method: 'get',
|
||||
url: options.url || '{% url "api-part-category-list" %}',
|
||||
queryParams: filters,
|
||||
@ -1176,7 +1192,7 @@ function loadPartCategoryTable(table, options) {
|
||||
name: 'category',
|
||||
original: original,
|
||||
showColumns: true,
|
||||
buttons: [
|
||||
buttons: options.allowTreeView ? [
|
||||
{
|
||||
icon: 'fas fa-bars',
|
||||
attributes: {
|
||||
@ -1215,28 +1231,31 @@ function loadPartCategoryTable(table, options) {
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
] : [],
|
||||
onPostBody: function() {
|
||||
|
||||
tree_view = inventreeLoad('category-tree-view') == 1;
|
||||
if (options.allowTreeView) {
|
||||
|
||||
if (tree_view) {
|
||||
tree_view = inventreeLoad('category-tree-view') == 1;
|
||||
|
||||
$('#view-category-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-category-tree').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
onChange: function() {
|
||||
table.bootstrapTable('resetView');
|
||||
},
|
||||
onExpand: function() {
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#view-category-tree').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-category-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
if (tree_view) {
|
||||
|
||||
$('#view-category-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-category-tree').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
|
||||
table.treegrid({
|
||||
treeColumn: 0,
|
||||
onChange: function() {
|
||||
table.bootstrapTable('resetView');
|
||||
},
|
||||
onExpand: function() {
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#view-category-tree').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-category-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
}
|
||||
}
|
||||
},
|
||||
columns: [
|
||||
@ -1253,10 +1272,17 @@ function loadPartCategoryTable(table, options) {
|
||||
switchable: true,
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
return renderLink(
|
||||
|
||||
var html = renderLink(
|
||||
value,
|
||||
`/part/category/${row.pk}/`
|
||||
);
|
||||
|
||||
if (row.starred) {
|
||||
html += makeIconBadge('fa-bell icon-green', '{% trans "Subscribed category" %}');
|
||||
}
|
||||
|
||||
return html;
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -4,9 +4,6 @@
|
||||
|
||||
/* globals
|
||||
attachSelect,
|
||||
enableField,
|
||||
clearField,
|
||||
clearFieldOptions,
|
||||
closeModal,
|
||||
constructField,
|
||||
constructFormBody,
|
||||
@ -33,10 +30,8 @@
|
||||
printStockItemLabels,
|
||||
printTestReports,
|
||||
renderLink,
|
||||
reloadFieldOptions,
|
||||
scanItemsIntoLocation,
|
||||
showAlertDialog,
|
||||
setFieldValue,
|
||||
setupFilterList,
|
||||
showApiError,
|
||||
stockStatusDisplay,
|
||||
@ -44,6 +39,10 @@
|
||||
|
||||
/* exported
|
||||
createNewStockItem,
|
||||
createStockLocation,
|
||||
duplicateStockItem,
|
||||
editStockItem,
|
||||
editStockLocation,
|
||||
exportStock,
|
||||
loadInstalledInTable,
|
||||
loadStockLocationTable,
|
||||
@ -51,20 +50,318 @@
|
||||
loadStockTestResultsTable,
|
||||
loadStockTrackingTable,
|
||||
loadTableFilters,
|
||||
locationFields,
|
||||
removeStockRow,
|
||||
serializeStockItem,
|
||||
stockItemFields,
|
||||
stockLocationFields,
|
||||
stockStatusCodes,
|
||||
*/
|
||||
|
||||
|
||||
function locationFields() {
|
||||
return {
|
||||
/*
|
||||
* Launches a modal form to serialize a particular StockItem
|
||||
*/
|
||||
|
||||
function serializeStockItem(pk, options={}) {
|
||||
|
||||
var url = `/api/stock/${pk}/serialize/`;
|
||||
|
||||
options.method = 'POST';
|
||||
options.title = '{% trans "Serialize Stock Item" %}';
|
||||
|
||||
options.fields = {
|
||||
quantity: {},
|
||||
serial_numbers: {
|
||||
icon: 'fa-hashtag',
|
||||
},
|
||||
destination: {
|
||||
icon: 'fa-sitemap',
|
||||
},
|
||||
notes: {},
|
||||
};
|
||||
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
|
||||
function stockLocationFields(options={}) {
|
||||
var fields = {
|
||||
parent: {
|
||||
help_text: '{% trans "Parent stock location" %}',
|
||||
},
|
||||
name: {},
|
||||
description: {},
|
||||
};
|
||||
|
||||
if (options.parent) {
|
||||
fields.parent.value = options.parent;
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch an API form to edit a stock location
|
||||
*/
|
||||
function editStockLocation(pk, options={}) {
|
||||
|
||||
var url = `/api/stock/location/${pk}/`;
|
||||
|
||||
options.fields = stockLocationFields(options);
|
||||
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch an API form to create a new stock location
|
||||
*/
|
||||
function createStockLocation(options={}) {
|
||||
|
||||
var url = '{% url "api-location-list" %}';
|
||||
|
||||
options.method = 'POST';
|
||||
options.fields = stockLocationFields(options);
|
||||
options.title = '{% trans "New Stock Location" %}';
|
||||
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
|
||||
function stockItemFields(options={}) {
|
||||
var fields = {
|
||||
part: {
|
||||
// Hide the part field unless we are "creating" a new stock item
|
||||
hidden: !options.create,
|
||||
onSelect: function(data, field, opts) {
|
||||
// Callback when a new "part" is selected
|
||||
|
||||
// If we are "creating" a new stock item,
|
||||
// change the available fields based on the part properties
|
||||
if (options.create) {
|
||||
|
||||
// If a "trackable" part is selected, enable serial number field
|
||||
if (data.trackable) {
|
||||
enableFormInput('serial_numbers', opts);
|
||||
// showFormInput('serial_numbers', opts);
|
||||
} else {
|
||||
clearFormInput('serial_numbers', opts);
|
||||
disableFormInput('serial_numbers', opts);
|
||||
}
|
||||
|
||||
// Enable / disable fields based on purchaseable status
|
||||
if (data.purchaseable) {
|
||||
enableFormInput('supplier_part', opts);
|
||||
enableFormInput('purchase_price', opts);
|
||||
enableFormInput('purchase_price_currency', opts);
|
||||
} else {
|
||||
clearFormInput('supplier_part', opts);
|
||||
clearFormInput('purchase_price', opts);
|
||||
|
||||
disableFormInput('supplier_part', opts);
|
||||
disableFormInput('purchase_price', opts);
|
||||
disableFormInput('purchase_price_currency', opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
supplier_part: {
|
||||
icon: 'fa-building',
|
||||
filters: {
|
||||
part_detail: true,
|
||||
supplier_detail: true,
|
||||
},
|
||||
adjustFilters: function(query, opts) {
|
||||
var part = getFormFieldValue('part', {}, opts);
|
||||
|
||||
if (part) {
|
||||
query.part = part;
|
||||
}
|
||||
|
||||
return query;
|
||||
}
|
||||
},
|
||||
location: {
|
||||
icon: 'fa-sitemap',
|
||||
},
|
||||
quantity: {
|
||||
help_text: '{% trans "Enter initial quantity for this stock item" %}',
|
||||
},
|
||||
serial_numbers: {
|
||||
icon: 'fa-hashtag',
|
||||
type: 'string',
|
||||
label: '{% trans "Serial Numbers" %}',
|
||||
help_text: '{% trans "Enter serial numbers for new stock (or leave blank)" %}',
|
||||
required: false,
|
||||
},
|
||||
serial: {
|
||||
icon: 'fa-hashtag',
|
||||
},
|
||||
status: {},
|
||||
expiry_date: {},
|
||||
batch: {},
|
||||
purchase_price: {
|
||||
icon: 'fa-dollar-sign',
|
||||
},
|
||||
purchase_price_currency: {},
|
||||
packaging: {
|
||||
icon: 'fa-box',
|
||||
},
|
||||
link: {
|
||||
icon: 'fa-link',
|
||||
},
|
||||
owner: {},
|
||||
delete_on_deplete: {},
|
||||
};
|
||||
|
||||
if (options.create) {
|
||||
// Use special "serial numbers" field when creating a new stock item
|
||||
delete fields['serial'];
|
||||
} else {
|
||||
// These fields cannot be edited once the stock item has been created
|
||||
delete fields['serial_numbers'];
|
||||
delete fields['quantity'];
|
||||
delete fields['location'];
|
||||
}
|
||||
|
||||
// Remove stock expiry fields if feature is not enabled
|
||||
if (!global_settings.STOCK_ENABLE_EXPIRY) {
|
||||
delete fields['expiry_date'];
|
||||
}
|
||||
|
||||
// Remove ownership field if feature is not enanbled
|
||||
if (!global_settings.STOCK_OWNERSHIP_CONTROL) {
|
||||
delete fields['owner'];
|
||||
}
|
||||
|
||||
return fields;
|
||||
}
|
||||
|
||||
|
||||
function stockItemGroups(options={}) {
|
||||
return {
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch a modal form to duplicate a given StockItem
|
||||
*/
|
||||
function duplicateStockItem(pk, options) {
|
||||
|
||||
// First, we need the StockItem informatino
|
||||
inventreeGet(`/api/stock/${pk}/`, {}, {
|
||||
success: function(data) {
|
||||
|
||||
// Do not duplicate the serial number
|
||||
delete data['serial'];
|
||||
|
||||
options.data = data;
|
||||
|
||||
options.create = true;
|
||||
options.fields = stockItemFields(options);
|
||||
options.groups = stockItemGroups(options);
|
||||
|
||||
options.method = 'POST';
|
||||
options.title = '{% trans "Duplicate Stock Item" %}';
|
||||
|
||||
constructForm('{% url "api-stock-list" %}', options);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch a modal form to edit a given StockItem
|
||||
*/
|
||||
function editStockItem(pk, options={}) {
|
||||
|
||||
var url = `/api/stock/${pk}/`;
|
||||
|
||||
options.create = false;
|
||||
|
||||
options.fields = stockItemFields(options);
|
||||
options.groups = stockItemGroups(options);
|
||||
|
||||
options.title = '{% trans "Edit Stock Item" %}';
|
||||
|
||||
// Query parameters for retrieving stock item data
|
||||
options.params = {
|
||||
part_detail: true,
|
||||
supplier_part_detail: true,
|
||||
};
|
||||
|
||||
// Augment the rendered form when we receive information about the StockItem
|
||||
options.processResults = function(data, fields, options) {
|
||||
if (data.part_detail.trackable) {
|
||||
delete options.fields.delete_on_deplete;
|
||||
} else {
|
||||
// Remove serial number field if part is not trackable
|
||||
delete options.fields.serial;
|
||||
}
|
||||
|
||||
// Remove pricing fields if part is not purchaseable
|
||||
if (!data.part_detail.purchaseable) {
|
||||
delete options.fields.supplier_part;
|
||||
delete options.fields.purchase_price;
|
||||
delete options.fields.purchase_price_currency;
|
||||
}
|
||||
};
|
||||
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Launch an API form to contsruct a new stock item
|
||||
*/
|
||||
function createNewStockItem(options={}) {
|
||||
|
||||
var url = '{% url "api-stock-list" %}';
|
||||
|
||||
options.title = '{% trans "New Stock Item" %}';
|
||||
options.method = 'POST';
|
||||
|
||||
options.create = true;
|
||||
|
||||
options.fields = stockItemFields(options);
|
||||
options.groups = stockItemGroups(options);
|
||||
|
||||
if (!options.onSuccess) {
|
||||
options.onSuccess = function(response) {
|
||||
// If a single stock item has been created, follow it!
|
||||
if (response.pk) {
|
||||
var url = `/stock/item/${response.pk}/`;
|
||||
|
||||
addCachedAlert('{% trans "Created new stock item" %}', {
|
||||
icon: 'fas fa-boxes',
|
||||
});
|
||||
|
||||
window.location.href = url;
|
||||
} else {
|
||||
|
||||
// Multiple stock items have been created (i.e. serialized stock)
|
||||
var details = `
|
||||
<br>{% trans "Quantity" %}: ${response.quantity}
|
||||
<br>{% trans "Serial Numbers" %}: ${response.serial_numbers}
|
||||
`;
|
||||
|
||||
showMessage('{% trans "Created multiple stock items" %}', {
|
||||
icon: 'fas fa-boxes',
|
||||
details: details,
|
||||
});
|
||||
|
||||
var table = options.table || '#stock-table';
|
||||
|
||||
// Reload the table
|
||||
$(table).bootstrapTable('refresh');
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
constructForm(url, options);
|
||||
}
|
||||
|
||||
|
||||
@ -427,7 +724,7 @@ function adjustStock(action, items, options={}) {
|
||||
break;
|
||||
default:
|
||||
$(opts.modal).modal('hide');
|
||||
showApiError(xhr);
|
||||
showApiError(xhr, opts.url);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -831,7 +1128,9 @@ function loadStockTable(table, options) {
|
||||
|
||||
col = {
|
||||
field: 'quantity',
|
||||
sortName: 'stock',
|
||||
title: '{% trans "Stock" %}',
|
||||
sortable: true,
|
||||
formatter: function(value, row) {
|
||||
|
||||
var val = parseFloat(value);
|
||||
@ -1416,13 +1715,22 @@ function loadStockTable(table, options) {
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display a table of stock locations
|
||||
*/
|
||||
function loadStockLocationTable(table, options) {
|
||||
/* Display a table of stock locations */
|
||||
|
||||
var params = options.params || {};
|
||||
|
||||
var filterListElement = options.filterList || '#filter-list-location';
|
||||
|
||||
var tree_view = options.allowTreeView && inventreeLoad('location-tree-view') == 1;
|
||||
|
||||
if (tree_view) {
|
||||
params.cascade = true;
|
||||
}
|
||||
|
||||
var filters = {};
|
||||
|
||||
var filterKey = options.filterKey || options.name || 'location';
|
||||
@ -1443,15 +1751,13 @@ function loadStockLocationTable(table, options) {
|
||||
filters[key] = params[key];
|
||||
}
|
||||
|
||||
var tree_view = inventreeLoad('location-tree-view') == 1;
|
||||
|
||||
table.inventreeTable({
|
||||
treeEnable: tree_view,
|
||||
rootParentId: options.params.parent,
|
||||
rootParentId: tree_view ? options.params.parent : null,
|
||||
uniqueId: 'pk',
|
||||
idField: 'pk',
|
||||
treeShowField: 'name',
|
||||
parentIdField: 'parent',
|
||||
parentIdField: tree_view ? 'parent' : null,
|
||||
disablePagination: tree_view,
|
||||
sidePagination: tree_view ? 'client' : 'server',
|
||||
serverSort: !tree_view,
|
||||
@ -1465,28 +1771,31 @@ function loadStockLocationTable(table, options) {
|
||||
showColumns: true,
|
||||
onPostBody: function() {
|
||||
|
||||
tree_view = inventreeLoad('location-tree-view') == 1;
|
||||
if (options.allowTreeView) {
|
||||
|
||||
if (tree_view) {
|
||||
tree_view = inventreeLoad('location-tree-view') == 1;
|
||||
|
||||
$('#view-location-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-location-tree').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
|
||||
table.treegrid({
|
||||
treeColumn: 1,
|
||||
onChange: function() {
|
||||
table.bootstrapTable('resetView');
|
||||
},
|
||||
onExpand: function() {
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#view-location-tree').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-location-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
if (tree_view) {
|
||||
|
||||
$('#view-location-list').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-location-tree').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
|
||||
table.treegrid({
|
||||
treeColumn: 1,
|
||||
onChange: function() {
|
||||
table.bootstrapTable('resetView');
|
||||
},
|
||||
onExpand: function() {
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
$('#view-location-tree').removeClass('btn-secondary').addClass('btn-outline-secondary');
|
||||
$('#view-location-list').removeClass('btn-outline-secondary').addClass('btn-secondary');
|
||||
}
|
||||
}
|
||||
},
|
||||
buttons: [
|
||||
buttons: options.allowTreeView ? [
|
||||
{
|
||||
icon: 'fas fa-bars',
|
||||
attributes: {
|
||||
@ -1525,7 +1834,7 @@ function loadStockLocationTable(table, options) {
|
||||
);
|
||||
}
|
||||
}
|
||||
],
|
||||
] : [],
|
||||
columns: [
|
||||
{
|
||||
checkbox: true,
|
||||
@ -1800,79 +2109,6 @@ function loadStockTrackingTable(table, options) {
|
||||
}
|
||||
|
||||
|
||||
function createNewStockItem(options) {
|
||||
/* Launch a modal form to create a new stock item.
|
||||
*
|
||||
* This is really just a helper function which calls launchModalForm,
|
||||
* but it does get called a lot, so here we are ...
|
||||
*/
|
||||
|
||||
// Add in some funky options
|
||||
|
||||
options.callback = [
|
||||
{
|
||||
field: 'part',
|
||||
action: function(value) {
|
||||
|
||||
if (!value) {
|
||||
// No part chosen
|
||||
|
||||
clearFieldOptions('supplier_part');
|
||||
enableField('serial_numbers', false);
|
||||
enableField('purchase_price_0', false);
|
||||
enableField('purchase_price_1', false);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Reload options for supplier part
|
||||
reloadFieldOptions(
|
||||
'supplier_part',
|
||||
{
|
||||
url: '{% url "api-supplier-part-list" %}',
|
||||
params: {
|
||||
part: value,
|
||||
pretty: true,
|
||||
},
|
||||
text: function(item) {
|
||||
return item.pretty_name;
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// Request part information from the server
|
||||
inventreeGet(
|
||||
`/api/part/${value}/`, {},
|
||||
{
|
||||
success: function(response) {
|
||||
|
||||
// Disable serial number field if the part is not trackable
|
||||
enableField('serial_numbers', response.trackable);
|
||||
clearField('serial_numbers');
|
||||
|
||||
enableField('purchase_price_0', response.purchaseable);
|
||||
enableField('purchase_price_1', response.purchaseable);
|
||||
|
||||
// Populate the expiry date
|
||||
if (response.default_expiry <= 0) {
|
||||
// No expiry date
|
||||
clearField('expiry_date');
|
||||
} else {
|
||||
var expiry = moment().add(response.default_expiry, 'days');
|
||||
|
||||
setFieldValue('expiry_date', expiry.format('YYYY-MM-DD'));
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
launchModalForm('{% url "stock-item-create" %}', options);
|
||||
}
|
||||
|
||||
|
||||
function loadInstalledInTable(table, options) {
|
||||
/*
|
||||
* Display a table showing the stock items which are installed in this stock item.
|
||||
|
@ -103,6 +103,10 @@ function getAvailableTableFilters(tableKey) {
|
||||
title: '{% trans "Include subcategories" %}',
|
||||
description: '{% trans "Include subcategories" %}',
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Subscribed" %}',
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@ -368,7 +372,7 @@ function getAvailableTableFilters(tableKey) {
|
||||
},
|
||||
starred: {
|
||||
type: 'bool',
|
||||
title: '{% trans "Starred" %}',
|
||||
title: '{% trans "Subscribed" %}',
|
||||
},
|
||||
salable: {
|
||||
type: 'bool',
|
||||
|
@ -10,7 +10,7 @@
|
||||
<div class="navbar-header clearfix content-heading">
|
||||
<a class="navbar-brand" id='logo' href="{% url 'index' %}" style="padding-top: 7px; padding-bottom: 5px;"><img src="{% static 'img/inventree.png' %}" width="32" height="32" style="display:block; margin: auto;"/></a>
|
||||
</div>
|
||||
<div class="navbar-collapse collapse">
|
||||
<div class="navbar-collapse collapse" id="navbar-objects">
|
||||
<ul class="navbar-nav">
|
||||
{% if roles.part.view %}
|
||||
<li class='nav-item'>
|
||||
@ -59,19 +59,24 @@
|
||||
</ul>
|
||||
</div>
|
||||
{% include "search_form.html" %}
|
||||
<ul class='navbar-nav'>
|
||||
<ul class='navbar-nav flex-row'>
|
||||
{% if barcodes %}
|
||||
<li id='navbar-barcode-li'>
|
||||
<li class='nav-item' id='navbar-barcode-li'>
|
||||
<button id='barcode-scan' class='btn btn-secondary' title='{% trans "Scan Barcode" %}'>
|
||||
<span class='fas fa-qrcode'></span>
|
||||
</button>
|
||||
</li>
|
||||
{% endif %}
|
||||
<li class='nav-item' id='navbar-barcode-li'>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbar-objects" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
</li>
|
||||
<li class='nav-item dropdown'>
|
||||
<a class='nav-link dropdown-toggle' href='#' id='userMenuDropdown' role='button' data-bs-toggle='dropdown'>
|
||||
<span class='fas fa-user'></span> <strong>{{ user.get_username }}</strong>
|
||||
</a>
|
||||
<ul class='dropdown-menu dropdown-menu-end'>
|
||||
<ul class='dropdown-menu dropdown-menu-end inventree-navbar-menu'>
|
||||
{% if user.is_authenticated %}
|
||||
{% if user.is_staff %}
|
||||
<li><a class='dropdown-item' href="/admin/"><span class="fas fa-user"></span> {% trans "Admin" %}</a></li>
|
||||
@ -105,6 +110,9 @@
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
{% if sticky %}
|
||||
<div class='navbar-spacer'></div>
|
||||
{% endif %}
|
||||
|
@ -1,18 +0,0 @@
|
||||
<div class='notification-area'>
|
||||
<div class="alert alert-success alert-dismissable" id="alert-success">
|
||||
<a href="#" class="close" data-bs-dismiss="alert" aria-label="close">×</a>
|
||||
<div class='alert-msg'>Success alert</div>
|
||||
</div>
|
||||
<div class='alert alert-info alert-dismissable' id='alert-info'>
|
||||
<a href="#" class="close" data-bs-dismiss="alert" aria-label="close">×</a>
|
||||
<div class='alert-msg'>Info alert</div>
|
||||
</div>
|
||||
<div class='alert alert-warning alert-dismissable' id='alert-warning'>
|
||||
<a href="#" class="close" data-bs-dismiss="alert" aria-label="close">×</a>
|
||||
<div class='alert-msg'>Warning alert</div>
|
||||
</div>
|
||||
<div class='alert alert-danger alert-dismissable' id='alert-danger'>
|
||||
<a href="#" class="close" data-bs-dismiss="alert" aria-label="close">×</a>
|
||||
<div class='alert-msg'>Danger alert</div>
|
||||
</div>
|
||||
</div>
|
@ -7,7 +7,7 @@
|
||||
|
||||
<div class='panel'>
|
||||
<div class='panel-heading'>
|
||||
<div class='d-flex flex-row'>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>
|
||||
{% block heading %}
|
||||
<i>-- page header goes here --</i>
|
||||
@ -57,4 +57,4 @@
|
||||
{% block page_content %}
|
||||
{% endblock %}
|
||||
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
@ -1,7 +1,14 @@
|
||||
<div class='panel panel-hidden' id='panel-{% block label %}name{% endblock %}'>
|
||||
{% block panel_heading %}
|
||||
<div class='panel-heading'>
|
||||
<h4>{% block heading %}HEADING{% endblock %}</h4>
|
||||
<div class='d-flex flex-wrap'>
|
||||
<h4>{% block heading %}HEADING{% endblock %}</h4>
|
||||
{% include "spacer.html" %}
|
||||
<div class='btn-group' role='group'>
|
||||
{% block actions %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
{% block panel_content %}
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
<div class='container' style='width: 80%;'>
|
||||
{% if qr_data %}
|
||||
<div class='qr-container'>
|
||||
<div class='d-flex justify-content-center'>
|
||||
<img src="{% qrcode qr_data %}">
|
||||
</div>
|
||||
{% else %}
|
||||
|
@ -1,5 +1,5 @@
|
||||
{% load i18n %}
|
||||
<span title='{% trans text %}' class="list-group-item sidebar-list-group-item border-end-0 d-inline-block text-truncate" data-bs-parent="#sidebar">
|
||||
<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">
|
||||
<h6>
|
||||
<i class="bi bi-bootstrap"></i>
|
||||
{% if icon %}<span class='sidebar-item-icon fas {{ icon }}'></span>{% endif %}
|
||||
|
@ -10,17 +10,10 @@
|
||||
|
||||
<div id='{{ prefix }}button-toolbar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<div class='btn-group'>
|
||||
<div class='btn-group' role='group'>
|
||||
<button class='btn btn-outline-secondary' id='stock-export' title='{% trans "Export Stock Information" %}'>
|
||||
<span class='fas fa-download'></span>
|
||||
</button>
|
||||
|
||||
{% if owner_control.value == "True" and user in owners or user.is_superuser or owner_control.value == "False" %}
|
||||
{% if not read_only and not prevent_new_stock and roles.stock.add %}
|
||||
<button class="btn btn-success" id='item-create' title='{% trans "New Stock Item" %}'>
|
||||
<span class='fas fa-plus-circle'></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if barcodes %}
|
||||
<!-- Barcode actions menu -->
|
||||
<div class='btn-group' role='group'>
|
||||
@ -46,7 +39,7 @@
|
||||
</div>
|
||||
{% if not read_only %}
|
||||
{% if roles.stock.change or roles.stock.delete %}
|
||||
<div class="btn-group">
|
||||
<div class="btn-group" role="group">
|
||||
<button id='stock-options' class="btn btn-primary dropdown-toggle" type="button" data-bs-toggle="dropdown" title='{% trans "Stock Options" %}'>
|
||||
<span class='fas fa-boxes'></span> <span class="caret"></span>
|
||||
</button>
|
||||
@ -66,10 +59,7 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<div class='filter-list' id='filter-list-stock'>
|
||||
<!-- An empty div in which the filter list will be constructed -->
|
||||
</div>
|
||||
{% include "filter_list.html" with id="stock" %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
Reference in New Issue
Block a user