Merge pull request #1070 from SchrodingersGat/global-settings

Global settings
This commit is contained in:
Oliver 2020-10-25 22:22:57 +11:00 committed by GitHub
commit 720579dcd7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 1459 additions and 726 deletions

View File

@ -120,6 +120,19 @@ def str2bool(text, test=True):
return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', 'off', ] return str(text).lower() in ['0', 'n', 'no', 'none', 'f', 'false', 'off', ]
def is_bool(text):
"""
Determine if a string value 'looks' like a boolean.
"""
if str2bool(text, True):
return True
elif str2bool(text, False):
return True
else:
return False
def isNull(text): def isNull(text):
""" """
Test if a string 'looks' like a null value. Test if a string 'looks' like a null value.

View File

@ -39,6 +39,8 @@ from .views import IndexView, SearchView, DatabaseStatsView
from .views import SettingsView, EditUserView, SetPasswordView, ColorThemeSelectView from .views import SettingsView, EditUserView, SetPasswordView, ColorThemeSelectView
from .views import DynamicJsView from .views import DynamicJsView
from common.views import SettingEdit
from .api import InfoView from .api import InfoView
from .api import ActionPluginView from .api import ActionPluginView
@ -71,6 +73,7 @@ settings_urls = [
url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'), url(r'^user/?', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings-user'),
url(r'^theme/?', ColorThemeSelectView.as_view(), name='settings-theme'), url(r'^theme/?', ColorThemeSelectView.as_view(), name='settings-theme'),
url(r'^global/?', SettingsView.as_view(template_name='InvenTree/settings/global.html'), name='settings-global'),
url(r'^currency/?', SettingsView.as_view(template_name='InvenTree/settings/currency.html'), name='settings-currency'), url(r'^currency/?', SettingsView.as_view(template_name='InvenTree/settings/currency.html'), name='settings-currency'),
url(r'^part/?', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'), url(r'^part/?', SettingsView.as_view(template_name='InvenTree/settings/part.html'), name='settings-part'),
url(r'^stock/?', SettingsView.as_view(template_name='InvenTree/settings/stock.html'), name='settings-stock'), url(r'^stock/?', SettingsView.as_view(template_name='InvenTree/settings/stock.html'), name='settings-stock'),
@ -78,6 +81,8 @@ settings_urls = [
url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'), url(r'^purchase-order/?', SettingsView.as_view(template_name='InvenTree/settings/po.html'), name='settings-po'),
url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'), url(r'^sales-order/?', SettingsView.as_view(template_name='InvenTree/settings/so.html'), name='settings-so'),
url(r'^(?P<pk>\d+)/edit/?', SettingEdit.as_view(), name='setting-edit'),
# Catch any other urls # Catch any other urls
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'), url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
] ]

View File

@ -32,7 +32,7 @@ class CommonConfig(AppConfig):
return return
# Default instance name # Default instance name
instance_name = 'InvenTree Server' instance_name = InvenTreeSetting.get_default_value('INVENTREE_INSTANCE')
# Use the old name if it exists # Use the old name if it exists
if InvenTreeSetting.objects.filter(key='InstanceName').exists(): if InvenTreeSetting.objects.filter(key='InstanceName').exists():
@ -59,12 +59,12 @@ class CommonConfig(AppConfig):
from .models import InvenTreeSetting from .models import InvenTreeSetting
for key in InvenTreeSetting.DEFAULT_VALUES.keys(): for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
try: try:
settings = InvenTreeSetting.objects.filter(key__iexact=key) settings = InvenTreeSetting.objects.filter(key__iexact=key)
if settings.count() == 0: if settings.count() == 0:
value = InvenTreeSetting.DEFAULT_VALUES[key] value = InvenTreeSetting.get_default_value(key)
print(f"Creating default setting for {key} -> '{value}'") print(f"Creating default setting for {key} -> '{value}'")

View File

@ -33,6 +33,5 @@ class SettingEditForm(HelperForm):
model = InvenTreeSetting model = InvenTreeSetting
fields = [ fields = [
'key',
'value' 'value'
] ]

View File

@ -11,10 +11,12 @@ import decimal
from django.db import models from django.db import models
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.core.validators import MinValueValidator, MaxValueValidator from django.core.validators import MinValueValidator, MaxValueValidator
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
import InvenTree.helpers
import InvenTree.fields import InvenTree.fields
@ -27,34 +29,205 @@ class InvenTreeSetting(models.Model):
even if that key does not exist. even if that key does not exist.
""" """
# Dict of default values for various internal settings """
DEFAULT_VALUES = { Dict of all global settings values:
# Global inventree settings
'INVENTREE_INSTANCE': 'InvenTree Server',
# Part settings The key of each item is the name of the value as it appears in the database.
'PART_IPN_REGEX': '',
'PART_COPY_BOM': True,
'PART_COPY_PARAMETERS': True,
'PART_COPY_TESTS': True,
# Stock settings Each global setting has the following parameters:
- name: Translatable string name of the setting (required)
- description: Translatable string description of the setting (required)
- default: Default value (optional)
- units: Units of the particular setting (optional)
- validator: Validation function for the setting (optional)
# Build Order settings The keys must be upper-case
'BUILDORDER_REFERENCE_PREFIX': 'BO', """
'BUILDORDER_REFERENCE_REGEX': '',
# Purchase Order Settings GLOBAL_SETTINGS = {
'PURCHASEORDER_REFERENCE_PREFIX': 'PO',
# Sales Order Settings 'INVENTREE_INSTANCE': {
'SALESORDER_REFERENCE_PREFIX': 'SO', 'name': _('InvenTree Instance Name'),
'default': 'InvenTree server',
'description': _('String descriptor for the server instance'),
},
'INVENTREE_COMPANY_NAME': {
'name': _('Company name'),
'description': _('Internal company name'),
'default': 'My company name',
},
'PART_IPN_REGEX': {
'name': _('IPN Regex'),
'description': _('Regular expression pattern for matching Part IPN')
},
'PART_COPY_BOM': {
'name': _('Copy Part BOM Data'),
'description': _('Copy BOM data by default when duplicating a part'),
'default': True,
'validator': bool,
},
'PART_COPY_PARAMETERS': {
'name': _('Copy Part Parameter Data'),
'description': _('Copy parameter data by default when duplicating a part'),
'default': True,
'validator': bool,
},
'PART_COPY_TESTS': {
'name': _('Copy Part Test Data'),
'description': _('Copy test data by default when duplicating a part'),
'default': True,
'validator': bool
},
'BUILDORDER_REFERENCE_PREFIX': {
'name': _('Build Order Reference Prefix'),
'description': _('Prefix value for build order reference'),
'default': 'BO',
},
'BUILDORDER_REFERENCE_REGEX': {
'name': _('Build Order Reference Regex'),
'description': _('Regular expression pattern for matching build order reference')
},
'SALESORDER_REFERENCE_PREFIX': {
'name': _('Sales Order Reference Prefix'),
'description': _('Prefix value for sales order reference'),
},
'PURCHASEORDER_REFERENCE_PREFIX': {
'name': _('Purchase Order Reference Prefix'),
'description': _('Prefix value for purchase order reference'),
},
} }
class Meta: class Meta:
verbose_name = "InvenTree Setting" verbose_name = "InvenTree Setting"
verbose_name_plural = "InvenTree Settings" verbose_name_plural = "InvenTree Settings"
@classmethod
def get_setting_name(cls, key):
"""
Return the name of a particular setting.
If it does not exist, return an empty string.
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('name', '')
else:
return ''
@classmethod
def get_setting_description(cls, key):
"""
Return the description for a particular setting.
If it does not exist, return an empty string.
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('description', '')
else:
return ''
@classmethod
def get_setting_units(cls, key):
"""
Return the units for a particular setting.
If it does not exist, return an empty string.
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('units', '')
else:
return ''
@classmethod
def get_setting_validator(cls, key):
"""
Return the validator for a particular setting.
If it does not exist, return None
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('validator', None)
else:
return None
@classmethod
def get_default_value(cls, key):
"""
Return the default value for a particular setting.
If it does not exist, return an empty string
"""
key = str(key).strip().upper()
if key in cls.GLOBAL_SETTINGS:
setting = cls.GLOBAL_SETTINGS[key]
return setting.get('default', '')
else:
return ''
@classmethod
def get_setting_object(cls, key):
"""
Return an InvenTreeSetting object matching the given key.
- Key is case-insensitive
- Returns None if no match is made
"""
key = str(key).strip().upper()
try:
setting = InvenTreeSetting.objects.filter(key__iexact=key).first()
except (InvenTreeSetting.DoesNotExist):
# Create the setting if it does not exist
setting = InvenTreeSetting.create(
key=key,
value=InvenTreeSetting.get_default_value(key)
)
return setting
@classmethod
def get_setting_pk(cls, key):
"""
Return the primary-key value for a given setting.
If the setting does not exist, return None
"""
setting = InvenTreeSetting.get_setting_object(cls)
if setting:
return setting.pk
else:
return None
@classmethod @classmethod
def get_setting(cls, key, backup_value=None): def get_setting(cls, key, backup_value=None):
""" """
@ -64,16 +237,13 @@ class InvenTreeSetting(models.Model):
# If no backup value is specified, atttempt to retrieve a "default" value # If no backup value is specified, atttempt to retrieve a "default" value
if backup_value is None: if backup_value is None:
backup_value = InvenTreeSetting.DEFAULT_VALUES.get(key, None) backup_value = cls.get_default_value(key)
try: setting = InvenTreeSetting.get_setting_object(key)
settings = InvenTreeSetting.objects.filter(key__iexact=key)
if len(settings) > 0: if setting:
return settings[0].value return setting.value
else: else:
return backup_value
except InvenTreeSetting.DoesNotExist:
return backup_value return backup_value
@classmethod @classmethod
@ -108,6 +278,59 @@ class InvenTreeSetting(models.Model):
value = models.CharField(max_length=200, blank=True, unique=False, help_text=_('Settings value')) value = models.CharField(max_length=200, blank=True, unique=False, help_text=_('Settings value'))
@property
def name(self):
return InvenTreeSetting.get_setting_name(self.key)
@property
def description(self):
return InvenTreeSetting.get_setting_description(self.key)
@property
def units(self):
return InvenTreeSetting.get_setting_units(self.key)
def clean(self):
"""
If a validator (or multiple validators) are defined for a particular setting key,
run them against the 'value' field.
"""
super().clean()
validator = InvenTreeSetting.get_setting_validator(self.key)
if validator is not None:
self.run_validator(validator)
def run_validator(self, validator):
"""
Run a validator against the 'value' field for this InvenTreeSetting object.
"""
if validator is None:
return
# If a list of validators is supplied, iterate through each one
if type(validator) in [list, tuple]:
for v in validator:
self.run_validator(v)
return
# Check if a 'type' has been specified for this value
if type(validator) == type:
if validator == bool:
# Value must "look like" a boolean value
if InvenTree.helpers.is_bool(self.value):
# Coerce into either "True" or "False"
self.value = str(InvenTree.helpers.str2bool(self.value))
else:
raise ValidationError({
'value': _('Value must be a boolean value')
})
def validate_unique(self, exclude=None): def validate_unique(self, exclude=None):
""" Ensure that the key:value pair is unique. """ Ensure that the key:value pair is unique.
In addition to the base validators, this ensures that the 'key' In addition to the base validators, this ensures that the 'key'
@ -123,6 +346,24 @@ class InvenTreeSetting(models.Model):
except InvenTreeSetting.DoesNotExist: except InvenTreeSetting.DoesNotExist:
pass pass
def is_bool(self):
"""
Check if this setting is required to be a boolean value
"""
validator = InvenTreeSetting.get_setting_validator(self.key)
return validator == bool
def as_bool(self):
"""
Return the value of this setting converted to a boolean value.
Warning: Only use on values where is_bool evaluates to true!
"""
return InvenTree.helpers.str2bool(self.value)
class Currency(models.Model): class Currency(models.Model):
""" """

View File

@ -0,0 +1,14 @@
{% extends "modal_form.html" %}
{% load i18n %}
{% block pre_form_content %}
{{ block.super }}
<!--
<p>
<b>{{ name }}</b><br>
{{ description }}<br>
<i>{% trans "Current value" %}: {{ value }}</i>
</p>
-->
{% endblock %}

View File

@ -35,14 +35,37 @@ class SettingsTest(TestCase):
self.client.login(username='username', password='password') self.client.login(username='username', password='password')
def test_required_values(self):
"""
- Ensure that every global setting has a name.
- Ensure that every global setting has a description.
"""
for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
setting = InvenTreeSetting.GLOBAL_SETTINGS[key]
name = setting.get('name', None)
if name is None:
raise ValueError(f'Missing GLOBAL_SETTING name for {key}')
description = setting.get('description', None)
if description is None:
raise ValueError(f'Missing GLOBAL_SETTING description for {key}')
if not key == key.upper():
raise ValueError(f"GLOBAL_SETTINGS key '{key}' is not uppercase")
def test_defaults(self): def test_defaults(self):
""" """
Populate the settings with default values Populate the settings with default values
""" """
for key in InvenTreeSetting.DEFAULT_VALUES.keys(): for key in InvenTreeSetting.GLOBAL_SETTINGS.keys():
value = InvenTreeSetting.DEFAULT_VALUES[key] value = InvenTreeSetting.get_default_value(key)
InvenTreeSetting.set_setting(key, value, self.user) InvenTreeSetting.set_setting(key, value, self.user)

View File

@ -6,8 +6,10 @@ Django views for interacting with common models
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.forms import CheckboxInput
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
from InvenTree.helpers import str2bool
from . import models from . import models
from . import forms from . import forms
@ -46,3 +48,47 @@ class SettingEdit(AjaxUpdateView):
model = models.InvenTreeSetting model = models.InvenTreeSetting
ajax_form_title = _('Change Setting') ajax_form_title = _('Change Setting')
form_class = forms.SettingEditForm form_class = forms.SettingEditForm
ajax_template_name = "common/edit_setting.html"
def get_context_data(self, **kwargs):
"""
Add extra context information about the particular setting object.
"""
ctx = super().get_context_data(**kwargs)
setting = self.get_object()
ctx['key'] = setting.key
ctx['value'] = setting.value
ctx['name'] = models.InvenTreeSetting.get_setting_name(setting.key)
ctx['description'] = models.InvenTreeSetting.get_setting_description(setting.key)
return ctx
def get_form(self):
"""
Override default get_form behaviour
"""
form = super().get_form()
setting = self.get_object()
if setting.is_bool():
form.fields['value'].widget = CheckboxInput()
self.object.value = str2bool(setting.value)
form.fields['value'].value = str2bool(setting.value)
name = models.InvenTreeSetting.get_setting_name(setting.key)
if name:
form.fields['value'].label = name
description = models.InvenTreeSetting.get_setting_description(setting.key)
if description:
form.fields['value'].help_text = description
return form

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -5,7 +5,8 @@ import os
from django import template from django import template
from InvenTree import version, settings from InvenTree import version, settings
from InvenTree.helpers import decimal2string
import InvenTree.helpers
from common.models import InvenTreeSetting, ColorTheme from common.models import InvenTreeSetting, ColorTheme
@ -16,7 +17,14 @@ register = template.Library()
def decimal(x, *args, **kwargs): def decimal(x, *args, **kwargs):
""" Simplified rendering of a decimal number """ """ Simplified rendering of a decimal number """
return decimal2string(x) return InvenTree.helpers.decimal2string(x)
@register.simple_tag()
def str2bool(x, *args, **kwargs):
""" Convert a string to a boolean value """
return InvenTree.helpers.str2bool(x)
@register.simple_tag() @register.simple_tag()
@ -28,7 +36,7 @@ def inrange(n, *args, **kwargs):
@register.simple_tag() @register.simple_tag()
def multiply(x, y, *args, **kwargs): def multiply(x, y, *args, **kwargs):
""" Multiply two numbers together """ """ Multiply two numbers together """
return decimal2string(x * y) return InvenTree.helpers.decimal2string(x * y)
@register.simple_tag() @register.simple_tag()
@ -41,7 +49,7 @@ def add(x, y, *args, **kwargs):
def part_allocation_count(build, part, *args, **kwargs): def part_allocation_count(build, part, *args, **kwargs):
""" Return the total number of <part> allocated to <build> """ """ Return the total number of <part> allocated to <build> """
return decimal2string(build.getAllocatedQuantity(part)) return InvenTree.helpers.decimal2string(build.getAllocatedQuantity(part))
@register.simple_tag() @register.simple_tag()
@ -87,8 +95,15 @@ def inventree_docs_url(*args, **kwargs):
@register.simple_tag() @register.simple_tag()
def inventree_setting(key, *args, **kwargs): def setting_object(key, *args, **kwargs):
return InvenTreeSetting.get_setting(key, backup_value=kwargs.get('backup', None)) """
Return a setting object speciifed by the given key
(Or return None if the setting does not exist)
"""
setting = InvenTreeSetting.get_setting_object(key)
return setting
@register.simple_tag() @register.simple_tag()

View File

@ -15,17 +15,8 @@
<table class='table table-striped table-condensed'> <table class='table table-striped table-condensed'>
<thead></thead> <thead></thead>
<tbody> <tbody>
<tr> {% include "InvenTree/settings/setting.html" with key="BUILDORDER_REFERENCE_PREFIX" %}
<th>{% trans "Reference Prefix" %}</th> {% include "InvenTree/settings/setting.html" with key="BUILDORDER_REFERENCE_REGEX" %}
<th>{% inventree_setting 'BUILDORDER_REFERENCE_PREFIX' backup='BO' %}</th>
<td>{% trans "Prefix for Build Order reference" %}</td>
</tr>
<tr>
<th>{% trans "Reference Regex" %}</th>
<th>{% inventree_setting 'BUILDORDER_REFERENCE_REGEX' %}</th>
<td>{% trans "Regex validator for Build Order reference" %}</td>
<td></td>
</tr>
</tbody> </tbody>
</table> </table>

View File

@ -0,0 +1,23 @@
{% extends "InvenTree/settings/settings.html" %}
{% load i18n %}
{% load inventree_extras %}
{% block tabs %}
{% include "InvenTree/settings/tabs.html" with tab='global' %}
{% endblock %}
{% block subtitle %}
{% trans "Global InvenTree Settings" %}
{% endblock %}
{% block settings %}
<table class='table table-striped table-condensed'>
<thead></thead>
<tbody>
{% include "InvenTree/settings/setting.html" with key="INVENTREE_INSTANCE" %}
{% include "InvenTree/settings/setting.html" with key="INVENTREE_COMPANY_NAME" %}
</tbody>
</table>
{% endblock %}

View File

@ -11,6 +11,16 @@
{% block settings %} {% block settings %}
<table class='table table-striped table-condensed'>
<thead></thead>
<tbody>
{% include "InvenTree/settings/setting.html" with key="PART_IPN_REGEX" %}
{% include "InvenTree/settings/setting.html" with key="PART_COPY_BOM" %}
{% include "InvenTree/settings/setting.html" with key="PART_COPY_PARAMETERS" %}
{% include "InvenTree/settings/setting.html" with key="PART_COPY_TESTS" %}
</tbody>
</table>
<h4>{% trans "Part Parameter Templates" %}</h4> <h4>{% trans "Part Parameter Templates" %}</h4>
<div id='param-buttons'> <div id='param-buttons'>
@ -53,7 +63,7 @@
var bEdit = "<button title='{% trans "Edit Template" %}' class='template-edit btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-edit'></span></button>"; var bEdit = "<button title='{% trans "Edit Template" %}' class='template-edit btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-edit'></span></button>";
var bDel = "<button title='{% trans "Delete Template" %}' class='template-delete btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-trash-alt icon-red'></span></button>"; var bDel = "<button title='{% trans "Delete Template" %}' class='template-delete btn btn-default btn-glyph' type='button' pk='" + row.pk + "'><span class='fas fa-trash-alt icon-red'></span></button>";
var html = "<div class='btn-group' role='group'>" + bEdit + bDel + "</div>"; var html = "<div class='btn-group float-right' role='group'>" + bEdit + bDel + "</div>";
return html; return html;
} }

View File

@ -10,4 +10,10 @@
{% endblock %} {% endblock %}
{% block settings %} {% block settings %}
<table class='table table-striped table-condensed'>
<thead></thead>
<tbody>
{% include "InvenTree/settings/setting.html" with key="PURCHASEORDER_REFERENCE_PREFIX" %}
</tbody>
</table>
{% endblock %} {% endblock %}

View File

@ -0,0 +1,29 @@
{% load inventree_extras %}
{% load i18n %}
{% setting_object key as setting %}
<tr>
<td><b>{{ setting.name }}</b></td>
<td>
{% if setting.is_bool %}
<div>
<input fieldname='{{ setting.key }}' class='slidey' type='checkbox' data-offstyle='warning' data-onstyle='success' data-size='small' data-toggle='toggle' disabled autocomplete='off' {% if setting.as_bool %}checked=''{% endif %}>
</div>
{% else %}
{% if setting.value %}
<b>{{ setting.value }}</b>{{ setting.units }}</td>
{% else %}
<i>{% trans "No value set" %}</i>
{% endif %}
{% endif %}
<td>
{{ setting.description }}
</td>
<td>
<div class='btn-group float-right'>
<button class='btn btn-default btn-glyph btn-edit-setting' pk='{{ setting.pk }}' setting='{{ key }}' title='{% trans "Edit setting" %}'>
<span class='fas fa-edit icon-green'></span>
</button>
</div>
</td>
</tr>

View File

@ -37,3 +37,20 @@ InvenTree | {% trans "Settings" %}
{% block js_load %} {% block js_load %}
{{ block.super }} {{ block.super }}
{% endblock %} {% endblock %}
{% block js_ready %}
{{ block.super }}
$('table').find('.btn-edit-setting').click(function() {
var setting = $(this).attr('setting');
var pk = $(this).attr('pk');
launchModalForm(
`/settings/${pk}/edit/`,
{
reload: true,
}
);
});
{% endblock %}

View File

@ -10,4 +10,12 @@
{% endblock %} {% endblock %}
{% block settings %} {% block settings %}
<table class='table table-striped table-condensed'>
<thead></thead>
<tbody>
{% include "InvenTree/settings/setting.html" with key="SALESORDER_REFERENCE_PREFIX" %}
</tbody>
</table>
{% endblock %} {% endblock %}

View File

@ -9,8 +9,12 @@
<a href="{% url 'settings-theme' %}"><span class='fas fa-fill'></span> {% trans "Theme" %}</a> <a href="{% url 'settings-theme' %}"><span class='fas fa-fill'></span> {% trans "Theme" %}</a>
</li> </li>
</ul> </ul>
{% if user.is_staff %}
<h4><span class='fas fa-cogs'></span> {% trans "InvenTree Settings" %}</h4> <h4><span class='fas fa-cogs'></span> {% trans "InvenTree Settings" %}</h4>
<ul class='nav nav-pills nav-stacked'> <ul class='nav nav-pills nav-stacked'>
<li {% if tab == 'global' %} class='active' {% endif %}>
<a href='{% url "settings-global" %}'><span class='fas fa-globe'></span> {% trans "Global" %}</a>
</li>
<li{% ifequal tab 'currency' %} class='active'{% endifequal %}> <li{% ifequal tab 'currency' %} class='active'{% endifequal %}>
<a href="{% url 'settings-currency' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currency" %}</a> <a href="{% url 'settings-currency' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currency" %}</a>
</li> </li>
@ -29,4 +33,5 @@
<li {% if tab == 'so' %} class='active'{% endif %}> <li {% if tab == 'so' %} class='active'{% endif %}>
<a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a> <a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a>
</li> </li>
</ul> </ul>
{% endif %}

View File

@ -42,7 +42,7 @@ function loadBuildTable(table, options) {
switchable: false, switchable: false,
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var prefix = "{% inventree_setting 'BUILDORDER_REFERENCE_PREFIX' %}"; var prefix = "{% settings_value 'BUILDORDER_REFERENCE_PREFIX' %}";
if (prefix) { if (prefix) {
value = `${prefix}${value}`; value = `${prefix}${value}`;

View File

@ -139,7 +139,7 @@ function loadPurchaseOrderTable(table, options) {
title: '{% trans "Purchase Order" %}', title: '{% trans "Purchase Order" %}',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var prefix = "{% inventree_setting 'PURCHASEORDER_REFERENCE_PREFIX' %}"; var prefix = "{% settings_value 'PURCHASEORDER_REFERENCE_PREFIX' %}";
if (prefix) { if (prefix) {
value = `${prefix}${value}`; value = `${prefix}${value}`;
@ -221,7 +221,7 @@ function loadSalesOrderTable(table, options) {
title: '{% trans "Sales Order" %}', title: '{% trans "Sales Order" %}',
formatter: function(value, row, index, field) { formatter: function(value, row, index, field) {
var prefix = "{% inventree_setting 'SALESORDER_REFERENCE_PREFIX' %}"; var prefix = "{% settings_value 'SALESORDER_REFERENCE_PREFIX' %}";
if (prefix) { if (prefix) {
value = `${prefix}${value}`; value = `${prefix}${value}`;