Improvements for global settings

- Name and description are defined in models.py
- Lookup functions for name / description / units / default
- Shortcut template for rending settings
- More unit testing
This commit is contained in:
Oliver Walters 2020-10-25 07:49:38 +11:00
parent 5e63ccc9f6
commit 10758a9626
11 changed files with 229 additions and 39 deletions

View File

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

View File

@ -27,34 +27,144 @@ class InvenTreeSetting(models.Model):
even if that key does not exist.
"""
# Dict of default values for various internal settings
DEFAULT_VALUES = {
# Global inventree settings
'INVENTREE_INSTANCE': 'InvenTree Server',
"""
Dict of all global settings values:
# Part settings
'PART_IPN_REGEX': '',
'PART_COPY_BOM': True,
'PART_COPY_PARAMETERS': True,
'PART_COPY_TESTS': True,
The key of each item is the name of the value as it appears in the database.
# 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
'BUILDORDER_REFERENCE_PREFIX': 'BO',
'BUILDORDER_REFERENCE_REGEX': '',
The keys must be upper-case
"""
# Purchase Order Settings
'PURCHASEORDER_REFERENCE_PREFIX': 'PO',
GLOBAL_SETTINGS = {
# Sales Order Settings
'SALESORDER_REFERENCE_PREFIX': 'SO',
'INVENTREE_INSTANCE': {
'name': _('InvenTree Instance Name'),
'default': 'InvenTree server',
'description': _('String descriptor for the server instance'),
},
'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,
},
'PART_COPY_PARAMETERS': {
'name': _('Copy Part Parameter Data'),
'description': _('Copy parameter data by default when duplicating a part'),
'default': True,
},
'PART_COPY_TESTS': {
'name': _('Copy Part Test Data'),
'description': _('Copy test data by default when duplicating a part'),
'default': True,
},
'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:
verbose_name = "InvenTree Setting"
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_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(cls, key, backup_value=None):
"""
@ -64,7 +174,7 @@ class InvenTreeSetting(models.Model):
# If no backup value is specified, atttempt to retrieve a "default" value
if backup_value is None:
backup_value = InvenTreeSetting.DEFAULT_VALUES.get(key, None)
backup_value = cls.get_default_value(key)
try:
settings = InvenTreeSetting.objects.filter(key__iexact=key)

View File

@ -35,14 +35,37 @@ class SettingsTest(TestCase):
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):
"""
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)

View File

@ -87,7 +87,34 @@ def inventree_docs_url(*args, **kwargs):
@register.simple_tag()
def inventree_setting(key, *args, **kwargs):
def settings_name(key, *args, **kwargs):
"""
Returns the name of a GLOBAL_SETTINGS object
"""
return InvenTreeSetting.get_setting_name(key)
@register.simple_tag()
def settings_description(key, *args, **kwargs):
"""
Returns the description of a GLOBAL_SETTINGS object
"""
return InvenTreeSetting.get_setting_description(key)
@register.simple_tag()
def settings_units(key, *args, **kwargs):
"""
Return the units of a GLOBAL_SETTINGS object
"""
return InvenTreeSetting.get_setting_units(key)
@register.simple_tag()
def settings_value(key, *args, **kwargs):
"""
Returns the value of a GLOBAL_SETTINGS object
"""
return InvenTreeSetting.get_setting(key, backup_value=kwargs.get('backup', None))

View File

@ -15,17 +15,8 @@
<table class='table table-striped table-condensed'>
<thead></thead>
<tbody>
<tr>
<th>{% trans "Reference Prefix" %}</th>
<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>
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REFERENCE_PREFIX" %}
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REFERENCE_REGEX" %}
</tbody>
</table>

View File

@ -11,6 +11,16 @@
{% 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>
<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 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;
}

View File

@ -10,4 +10,10 @@
{% endblock %}
{% 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 %}

View File

@ -0,0 +1,15 @@
{% load inventree_extras %}
{% load i18n %}
<tr>
<td><b>{% settings_name key %}</b></td>
<td><b>{% settings_value key %}</b>{% settings_units key %}</td>
<td>{% settings_description key %}</td>
<td>
<div class='btn-group float-right'>
<button class='btn btn-default btn-glyph btn-edit-setting' setting='{{ key }}' title='{% trans "Edit setting" %}'>
<span class='fas fa-edit icon-green'></span>
</button>
</div>
</td>
</tr>

View File

@ -10,4 +10,12 @@
{% endblock %}
{% 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 %}

View File

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

View File

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