mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Merge remote-tracking branch 'inventree/master'
This commit is contained in:
commit
a047b7c6df
@ -120,6 +120,19 @@ def str2bool(text, test=True):
|
||||
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):
|
||||
"""
|
||||
Test if a string 'looks' like a null value.
|
||||
|
@ -39,6 +39,8 @@ from .views import IndexView, SearchView, DatabaseStatsView
|
||||
from .views import SettingsView, EditUserView, SetPasswordView, ColorThemeSelectView
|
||||
from .views import DynamicJsView
|
||||
|
||||
from common.views import SettingEdit
|
||||
|
||||
from .api import InfoView
|
||||
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'^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'^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'),
|
||||
@ -78,6 +81,8 @@ settings_urls = [
|
||||
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'^(?P<pk>\d+)/edit/?', SettingEdit.as_view(), name='setting-edit'),
|
||||
|
||||
# Catch any other urls
|
||||
url(r'^.*$', SettingsView.as_view(template_name='InvenTree/settings/user.html'), name='settings'),
|
||||
]
|
||||
|
@ -128,9 +128,13 @@ class InvenTreeRoleMixin(PermissionRequiredMixin):
|
||||
|
||||
def has_permission(self):
|
||||
"""
|
||||
Determine if the current user
|
||||
Determine if the current user has specified permissions
|
||||
"""
|
||||
|
||||
if self.permission_required:
|
||||
# Ignore role-based permissions
|
||||
return super().has_permission()
|
||||
|
||||
roles_required = []
|
||||
|
||||
if type(self.role_required) is str:
|
||||
|
@ -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}'")
|
||||
|
||||
|
@ -33,6 +33,5 @@ class SettingEditForm(HelperForm):
|
||||
model = InvenTreeSetting
|
||||
|
||||
fields = [
|
||||
'key',
|
||||
'value'
|
||||
]
|
||||
|
@ -11,10 +11,12 @@ import decimal
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from django.core.exceptions import ValidationError
|
||||
|
||||
import InvenTree.helpers
|
||||
import InvenTree.fields
|
||||
|
||||
|
||||
@ -27,34 +29,205 @@ 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:
|
||||
|
||||
# Build Order settings
|
||||
'BUILDORDER_REFERENCE_PREFIX': 'BO',
|
||||
'BUILDORDER_REFERENCE_REGEX': '',
|
||||
- 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)
|
||||
|
||||
# Purchase Order Settings
|
||||
'PURCHASEORDER_REFERENCE_PREFIX': 'PO',
|
||||
The keys must be upper-case
|
||||
"""
|
||||
|
||||
# Sales Order Settings
|
||||
'SALESORDER_REFERENCE_PREFIX': 'SO',
|
||||
GLOBAL_SETTINGS = {
|
||||
|
||||
'INVENTREE_INSTANCE': {
|
||||
'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:
|
||||
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_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
|
||||
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 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)
|
||||
setting = InvenTreeSetting.get_setting_object(key)
|
||||
|
||||
if len(settings) > 0:
|
||||
return settings[0].value
|
||||
else:
|
||||
return backup_value
|
||||
except InvenTreeSetting.DoesNotExist:
|
||||
if setting:
|
||||
return setting.value
|
||||
else:
|
||||
return backup_value
|
||||
|
||||
@classmethod
|
||||
@ -108,6 +278,59 @@ class InvenTreeSetting(models.Model):
|
||||
|
||||
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):
|
||||
""" Ensure that the key:value pair is unique.
|
||||
In addition to the base validators, this ensures that the 'key'
|
||||
@ -123,6 +346,24 @@ class InvenTreeSetting(models.Model):
|
||||
except InvenTreeSetting.DoesNotExist:
|
||||
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):
|
||||
"""
|
||||
|
14
InvenTree/common/templates/common/edit_setting.html
Normal file
14
InvenTree/common/templates/common/edit_setting.html
Normal 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 %}
|
@ -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)
|
||||
|
||||
|
@ -6,8 +6,10 @@ Django views for interacting with common models
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.forms import CheckboxInput
|
||||
|
||||
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
from InvenTree.helpers import str2bool
|
||||
|
||||
from . import models
|
||||
from . import forms
|
||||
@ -46,3 +48,47 @@ class SettingEdit(AjaxUpdateView):
|
||||
model = models.InvenTreeSetting
|
||||
ajax_form_title = _('Change Setting')
|
||||
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
|
||||
|
@ -0,0 +1,38 @@
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
def make_empty_email_field_null(apps, schema_editor):
|
||||
Company = apps.get_model('company', 'Company')
|
||||
for company in Company.objects.all():
|
||||
if company.email == '':
|
||||
company.email = None
|
||||
company.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('company', '0023_auto_20200808_0715'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
# Allow email field to be NULL
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
name='email',
|
||||
field=models.EmailField(blank=True, help_text='Contact email address', max_length=254, null=True, unique=False, verbose_name='Email'),
|
||||
),
|
||||
# Convert empty email string to NULL
|
||||
migrations.RunPython(make_empty_email_field_null),
|
||||
# Remove unique constraint on name field
|
||||
migrations.AlterField(
|
||||
model_name='company',
|
||||
name='name',
|
||||
field=models.CharField(help_text='Company name', max_length=100, verbose_name='Company name'),
|
||||
),
|
||||
# Add unique constraint on name/email pair
|
||||
migrations.AddConstraint(
|
||||
model_name='company',
|
||||
constraint=models.UniqueConstraint(fields=('name', 'email'), name='unique_name_email_pair'),
|
||||
),
|
||||
]
|
@ -12,7 +12,7 @@ import math
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.validators import MinValueValidator
|
||||
from django.db import models
|
||||
from django.db.models import Sum, Q
|
||||
from django.db.models import Sum, Q, UniqueConstraint
|
||||
|
||||
from django.apps import apps
|
||||
from django.urls import reverse
|
||||
@ -81,8 +81,11 @@ class Company(models.Model):
|
||||
|
||||
class Meta:
|
||||
ordering = ['name', ]
|
||||
constraints = [
|
||||
UniqueConstraint(fields=['name', 'email'], name='unique_name_email_pair')
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=100, blank=False, unique=True,
|
||||
name = models.CharField(max_length=100, blank=False,
|
||||
help_text=_('Company name'),
|
||||
verbose_name=_('Company name'))
|
||||
|
||||
@ -98,7 +101,8 @@ class Company(models.Model):
|
||||
verbose_name=_('Phone number'),
|
||||
blank=True, help_text=_('Contact phone number'))
|
||||
|
||||
email = models.EmailField(blank=True, verbose_name=_('Email'), help_text=_('Contact email address'))
|
||||
email = models.EmailField(blank=True, null=True,
|
||||
verbose_name=_('Email'), help_text=_('Contact email address'))
|
||||
|
||||
contact = models.CharField(max_length=100,
|
||||
verbose_name=_('Contact'),
|
||||
|
@ -23,23 +23,27 @@ InvenTree | {% trans "Company" %} - {{ company.name }}
|
||||
<hr>
|
||||
<h4>
|
||||
{{ company.name }}
|
||||
{% if user.is_staff and roles.company.change %}
|
||||
{% if user.is_staff and perms.company.change_company %}
|
||||
<a href="{% url 'admin:company_company_change' company.pk %}"><span title="{% trans 'Admin view' %}" class='fas fa-user-shield'></span></a>
|
||||
{% endif %}
|
||||
</h4>
|
||||
<p>{{ company.description }}</p>
|
||||
<div class='btn-group action-buttons'>
|
||||
{% if company.is_supplier %}
|
||||
{% if company.is_supplier and roles.purchase_order.add %}
|
||||
<button type='button' class='btn btn-default' id='company-order-2' title='Create purchase order'>
|
||||
<span class='fas fa-shopping-cart'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if perms.company.change_company %}
|
||||
<button type='button' class='btn btn-default' id='company-edit' title='Edit company information'>
|
||||
<span class='fas fa-edit icon-green'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
{% if perms.company.delete_company %}
|
||||
<button type='button' class='btn btn-default' id='company-delete' title='Delete company'>
|
||||
<span class='fas fa-trash-alt icon-red'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
|
@ -9,17 +9,25 @@
|
||||
|
||||
<hr>
|
||||
|
||||
{% if roles.purchase_order.change %}
|
||||
<div id='button-toolbar' class='btn-group'>
|
||||
{% if roles.purchase_order.add %}
|
||||
<button class="btn btn-success" id='part-create' title='{% trans "Create new supplier part" %}'>{% trans "New Supplier Part" %}</button>
|
||||
{% endif %}
|
||||
<div class="dropdown" style="float: right;">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" data-toggle="dropdown">{% trans "Options" %}
|
||||
<span class="caret"></span></button>
|
||||
<ul class="dropdown-menu">
|
||||
{% if roles.purchase_order.add %}
|
||||
<li><a href='#' id='multi-part-order' title='{% trans "Order parts" %}'>{% trans "Order Parts" %}</a></li>
|
||||
{% endif %}
|
||||
{% if roles.purchase_order.delete %}
|
||||
<li><a href='#' id='multi-part-delete' title='{% trans "Delete parts" %}'>{% trans "Delete Parts" %}</a></li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped table-condensed' id='part-table' data-toolbar='#button-toolbar'>
|
||||
</table>
|
||||
|
@ -12,12 +12,13 @@ InvenTree | {% trans "Supplier List" %}
|
||||
<h3>{{ title }}</h3>
|
||||
<hr>
|
||||
|
||||
{% if pagetype == 'manufacturers' and roles.purchase_order.add or pagetype == 'suppliers' and roles.purchase_order.add or pagetype == 'customers' and roles.sales_order.add %}
|
||||
<div id='button-toolbar'>
|
||||
<div class='btn-group'>
|
||||
<button type='button' class="btn btn-success" id='new-company'>{{ button_text }}</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped' id='company-table' data-toolbar='#button-toolbar'>
|
||||
</table>
|
||||
|
@ -9,6 +9,7 @@
|
||||
<h4>{% trans "Purchase Orders" %}</h4>
|
||||
<hr>
|
||||
|
||||
{% if roles.purchase_order.add %}
|
||||
<div id='button-bar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<button class='btn btn-primary' type='button' id='company-order2' title='{% trans "Create new purchase order" %}'>{% trans "New Purchase Order" %}</button>
|
||||
@ -17,6 +18,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped table-condensed po-table' id='purchase-order-table' data-toolbar='#button-bar'>
|
||||
</table>
|
||||
|
@ -9,6 +9,7 @@
|
||||
<h4>{% trans "Sales Orders" %}</h4>
|
||||
<hr>
|
||||
|
||||
{% if roles.sales_order.add %}
|
||||
<div id='button-bar'>
|
||||
<div class='button-toolbar container-fluid' style='float: right;'>
|
||||
<button class='btn btn-primary' type='button' id='new-sales-order' title='{% trans "Create new sales order" %}'>{% trans "New Sales Order" %}</button>
|
||||
@ -17,6 +18,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped table-condensed po-table' id='sales-order-table' data-toolbar='#button-bar'>
|
||||
</table>
|
||||
|
@ -18,19 +18,27 @@ src="{% static 'img/blank_image.png' %}"
|
||||
{% block page_data %}
|
||||
<h3>{% trans "Supplier Part" %}</h3>
|
||||
<p>{{ part.supplier.name }} - {{ part.SKU }}</p>
|
||||
|
||||
{% if roles.purchase_order.change %}
|
||||
<div class='btn-row'>
|
||||
<div class='btn-group action-buttons' role='group'>
|
||||
{% if roles.purchase_order.add %}
|
||||
<button type='button' class='btn btn-default btn-glyph' id='order-part' title='{% trans "Order part" %}'>
|
||||
<span class='fas fa-shopping-cart'></span>
|
||||
</button>
|
||||
{% endif %}
|
||||
<button type='button' class='btn btn-default btn-glyph' id='edit-part' title='{% trans "Edit supplier part" %}'>
|
||||
<span class='fas fa-edit icon-green'/>
|
||||
</button>
|
||||
{% if roles.purchase_order.delete %}
|
||||
<button type='button' class='btn btn-default btn-glyph' id='delete-part' title='{% trans "Delete supplier part" %}'>
|
||||
<span class='fas fa-trash-alt icon-red'/>
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% endblock %}
|
||||
|
||||
{% block page_details %}
|
||||
|
@ -10,11 +10,13 @@
|
||||
|
||||
<hr>
|
||||
|
||||
{% if roles.purchase_order.add %}
|
||||
<div id='button-bar'>
|
||||
<div class='btn-group'>
|
||||
<button class='btn btn-primary' type='button' id='order-part2' title='Order part'>Order Part</button>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped table-condensed po-table' id='purchase-order-table' data-toolbar='#button-bar'>
|
||||
</table>
|
||||
|
@ -11,9 +11,11 @@
|
||||
|
||||
<hr>
|
||||
|
||||
{% if roles.purchase_order.add %}
|
||||
<div id='price-break-toolbar' class='btn-group'>
|
||||
<button class='btn btn-primary' id='new-price-break' type='button'>{% trans "Add Price Break" %}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped table-condensed' id='price-break-table' data-toolbar='#price-break-toolbar'>
|
||||
</table>
|
||||
|
@ -6,6 +6,7 @@ from __future__ import unicode_literals
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Group
|
||||
|
||||
from .models import SupplierPart
|
||||
|
||||
@ -25,7 +26,24 @@ class CompanyViewTest(TestCase):
|
||||
|
||||
# Create a user
|
||||
User = get_user_model()
|
||||
User.objects.create_user('username', 'user@email.com', 'password')
|
||||
self.user = User.objects.create_user(
|
||||
username='username',
|
||||
email='user@email.com',
|
||||
password='password'
|
||||
)
|
||||
|
||||
# Put the user into a group with the correct permissions
|
||||
group = Group.objects.create(name='mygroup')
|
||||
self.user.groups.add(group)
|
||||
|
||||
# Give the group *all* the permissions!
|
||||
for rule in group.rule_sets.all():
|
||||
rule.can_view = True
|
||||
rule.can_change = True
|
||||
rule.can_add = True
|
||||
rule.can_delete = True
|
||||
|
||||
rule.save()
|
||||
|
||||
self.client.login(username='username', password='password')
|
||||
|
||||
|
@ -14,6 +14,7 @@ from django.forms import HiddenInput
|
||||
|
||||
from InvenTree.views import AjaxCreateView, AjaxUpdateView, AjaxDeleteView
|
||||
from InvenTree.helpers import str2bool
|
||||
from InvenTree.views import InvenTreeRoleMixin
|
||||
|
||||
from common.models import Currency
|
||||
|
||||
@ -29,7 +30,7 @@ from .forms import EditSupplierPartForm
|
||||
from .forms import EditPriceBreakForm
|
||||
|
||||
|
||||
class CompanyIndex(ListView):
|
||||
class CompanyIndex(InvenTreeRoleMixin, ListView):
|
||||
""" View for displaying list of companies
|
||||
"""
|
||||
|
||||
@ -37,6 +38,7 @@ class CompanyIndex(ListView):
|
||||
template_name = 'company/index.html'
|
||||
context_object_name = 'companies'
|
||||
paginate_by = 50
|
||||
permission_required = 'company.view_company'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
|
||||
@ -116,8 +118,8 @@ class CompanyNotes(UpdateView):
|
||||
context_object_name = 'company'
|
||||
template_name = 'company/notes.html'
|
||||
model = Company
|
||||
|
||||
fields = ['notes']
|
||||
permission_required = 'company.view_company'
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('company-notes', kwargs={'pk': self.get_object().id})
|
||||
@ -137,6 +139,7 @@ class CompanyDetail(DetailView):
|
||||
template_name = 'company/detail.html'
|
||||
queryset = Company.objects.all()
|
||||
model = Company
|
||||
permission_required = 'company.view_company'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@ -150,6 +153,7 @@ class CompanyImage(AjaxUpdateView):
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Update Company Image')
|
||||
form_class = CompanyImageForm
|
||||
permission_required = 'company.change_company'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -164,6 +168,7 @@ class CompanyEdit(AjaxUpdateView):
|
||||
context_object_name = 'company'
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Edit Company')
|
||||
permission_required = 'company.change_company'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -177,6 +182,7 @@ class CompanyCreate(AjaxCreateView):
|
||||
context_object_name = 'company'
|
||||
form_class = EditCompanyForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
permission_required = 'company.add_company'
|
||||
|
||||
def get_form_title(self):
|
||||
|
||||
@ -230,6 +236,7 @@ class CompanyDelete(AjaxDeleteView):
|
||||
ajax_template_name = 'company/delete.html'
|
||||
ajax_form_title = _('Delete Company')
|
||||
context_object_name = 'company'
|
||||
permission_required = 'company.delete_company'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -243,6 +250,7 @@ class SupplierPartDetail(DetailView):
|
||||
template_name = 'company/supplier_part_detail.html'
|
||||
context_object_name = 'part'
|
||||
queryset = SupplierPart.objects.all()
|
||||
permission_required = 'purchase_order.view'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
ctx = super().get_context_data(**kwargs)
|
||||
@ -258,6 +266,7 @@ class SupplierPartEdit(AjaxUpdateView):
|
||||
form_class = EditSupplierPartForm
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Edit Supplier Part')
|
||||
role_required = 'purchase_order.change'
|
||||
|
||||
|
||||
class SupplierPartCreate(AjaxCreateView):
|
||||
@ -268,6 +277,7 @@ class SupplierPartCreate(AjaxCreateView):
|
||||
ajax_template_name = 'modal_form.html'
|
||||
ajax_form_title = _('Create new Supplier Part')
|
||||
context_object_name = 'part'
|
||||
role_required = 'purchase_order.add'
|
||||
|
||||
def get_form(self):
|
||||
""" Create Form instance to create a new SupplierPart object.
|
||||
@ -327,6 +337,7 @@ class SupplierPartDelete(AjaxDeleteView):
|
||||
success_url = '/supplier/'
|
||||
ajax_template_name = 'company/partdelete.html'
|
||||
ajax_form_title = _('Delete Supplier Part')
|
||||
role_required = 'purchase_order.delete'
|
||||
|
||||
parts = []
|
||||
|
||||
@ -398,6 +409,7 @@ class PriceBreakCreate(AjaxCreateView):
|
||||
form_class = EditPriceBreakForm
|
||||
ajax_form_title = _('Add Price Break')
|
||||
ajax_template_name = 'modal_form.html'
|
||||
role_required = 'purchase_order.add'
|
||||
|
||||
def get_data(self):
|
||||
return {
|
||||
@ -440,6 +452,7 @@ class PriceBreakEdit(AjaxUpdateView):
|
||||
form_class = EditPriceBreakForm
|
||||
ajax_form_title = _('Edit Price Break')
|
||||
ajax_template_name = 'modal_form.html'
|
||||
role_required = 'purchase_order.change'
|
||||
|
||||
def get_form(self):
|
||||
|
||||
@ -455,3 +468,4 @@ class PriceBreakDelete(AjaxDeleteView):
|
||||
model = SupplierPriceBreak
|
||||
ajax_form_title = _("Delete Price Break")
|
||||
ajax_template_name = 'modal_delete_form.html'
|
||||
role_required = 'purchase_order.delete'
|
||||
|
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
@ -13,9 +13,11 @@
|
||||
|
||||
<h4>{% trans "Sales Order Items" %}</h4>
|
||||
|
||||
{% if roles.sales_order.change %}
|
||||
<div id='order-toolbar-buttons' class='btn-group' style='float: right;'>
|
||||
<button type='button' class='btn btn-default' id='new-so-line'>{% trans "Add Line Item" %}</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<table class='table table-striped table-condensed' id='so-lines-table' data-toolbar='#order-toolbar-buttons'>
|
||||
|
||||
|
@ -37,7 +37,9 @@
|
||||
<hr>
|
||||
<div class='panel panel-default'>
|
||||
<div class='panel-content'>
|
||||
{% if part.notes %}
|
||||
{{ part.notes | markdownify }}
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -5,7 +5,8 @@ import os
|
||||
|
||||
from django import template
|
||||
from InvenTree import version, settings
|
||||
from InvenTree.helpers import decimal2string
|
||||
|
||||
import InvenTree.helpers
|
||||
|
||||
from common.models import InvenTreeSetting, ColorTheme
|
||||
|
||||
@ -16,7 +17,14 @@ register = template.Library()
|
||||
def decimal(x, *args, **kwargs):
|
||||
""" 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()
|
||||
@ -28,7 +36,7 @@ def inrange(n, *args, **kwargs):
|
||||
@register.simple_tag()
|
||||
def multiply(x, y, *args, **kwargs):
|
||||
""" Multiply two numbers together """
|
||||
return decimal2string(x * y)
|
||||
return InvenTree.helpers.decimal2string(x * y)
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
@ -41,7 +49,7 @@ def add(x, y, *args, **kwargs):
|
||||
def part_allocation_count(build, part, *args, **kwargs):
|
||||
""" Return the total number of <part> allocated to <build> """
|
||||
|
||||
return decimal2string(build.getAllocatedQuantity(part))
|
||||
return InvenTree.helpers.decimal2string(build.getAllocatedQuantity(part))
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
@ -87,8 +95,15 @@ def inventree_docs_url(*args, **kwargs):
|
||||
|
||||
|
||||
@register.simple_tag()
|
||||
def inventree_setting(key, *args, **kwargs):
|
||||
return InvenTreeSetting.get_setting(key, backup_value=kwargs.get('backup', None))
|
||||
def setting_object(key, *args, **kwargs):
|
||||
"""
|
||||
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()
|
||||
|
@ -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>
|
||||
|
||||
|
23
InvenTree/templates/InvenTree/settings/global.html
Normal file
23
InvenTree/templates/InvenTree/settings/global.html
Normal 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 %}
|
@ -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;
|
||||
}
|
||||
|
@ -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 %}
|
29
InvenTree/templates/InvenTree/settings/setting.html
Normal file
29
InvenTree/templates/InvenTree/settings/setting.html
Normal 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>
|
@ -37,3 +37,20 @@ InvenTree | {% trans "Settings" %}
|
||||
{% block js_load %}
|
||||
{{ block.super }}
|
||||
{% 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 %}
|
||||
|
@ -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 %}
|
@ -9,8 +9,12 @@
|
||||
<a href="{% url 'settings-theme' %}"><span class='fas fa-fill'></span> {% trans "Theme" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% if user.is_staff %}
|
||||
<h4><span class='fas fa-cogs'></span> {% trans "InvenTree Settings" %}</h4>
|
||||
<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 %}>
|
||||
<a href="{% url 'settings-currency' %}"><span class='fas fa-dollar-sign'></span> {% trans "Currency" %}</a>
|
||||
</li>
|
||||
@ -30,3 +34,4 @@
|
||||
<a href="{% url 'settings-so' %}"><span class='fas fa-truck'></span> {% trans "Sales Orders" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endif %}
|
@ -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}`;
|
||||
|
@ -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}`;
|
||||
|
Loading…
Reference in New Issue
Block a user