mirror of
https://github.com/inventree/InvenTree
synced 2024-08-30 18:33:04 +00:00
Order responsible requirement (#6866)
* Add BUILDORDER_REQUIRE_RESPONSIBLE setting - If set, build orders must specify a responsible owner * Add responsible required setting to other order models: - PurchaseOrder - SalesOrder - ReturnOrder * Add unit test * Adjust unit tests * Settings updates: - Only check settings for global and user settings - Plugin settings are not defined at run-time * typo fix * More spelling fixes * Specify responsible owner pk
This commit is contained in:
parent
785b3b0e68
commit
cd0d35047d
@ -121,6 +121,12 @@ class Build(InvenTree.models.InvenTreeBarcodeMixin, InvenTree.models.InvenTreeNo
|
||||
|
||||
super().clean()
|
||||
|
||||
if common.models.InvenTreeSetting.get_setting('BUILDORDER_REQUIRE_RESPONSIBLE'):
|
||||
if not self.responsible:
|
||||
raise ValidationError({
|
||||
'responsible': _('Responsible user or group must be specified')
|
||||
})
|
||||
|
||||
# Prevent changing target part after creation
|
||||
if self.has_field_changed('part'):
|
||||
raise ValidationError({
|
||||
|
@ -148,7 +148,7 @@ class CurrencyExchangeView(APIView):
|
||||
|
||||
response = {
|
||||
'base_currency': common.models.InvenTreeSetting.get_setting(
|
||||
'INVENTREE_DEFAULT_CURRENCY', 'USD'
|
||||
'INVENTREE_DEFAULT_CURRENCY', backup_value='USD'
|
||||
),
|
||||
'exchange_rates': {},
|
||||
'updated': updated,
|
||||
|
@ -190,6 +190,8 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
SETTINGS: dict[str, SettingsKeyType] = {}
|
||||
|
||||
CHECK_SETTING_KEY = False
|
||||
|
||||
extra_unique_fields: list[str] = []
|
||||
|
||||
class Meta:
|
||||
@ -286,16 +288,16 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
def save_to_cache(self):
|
||||
"""Save this setting object to cache."""
|
||||
ckey = self.cache_key
|
||||
key = self.cache_key
|
||||
|
||||
# skip saving to cache if no pk is set
|
||||
if self.pk is None:
|
||||
return
|
||||
|
||||
logger.debug("Saving setting '%s' to cache", ckey)
|
||||
logger.debug("Saving setting '%s' to cache", key)
|
||||
|
||||
try:
|
||||
cache.set(ckey, self, timeout=3600)
|
||||
cache.set(key, self, timeout=3600)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@ -559,8 +561,8 @@ class BaseInvenTreeSetting(models.Model):
|
||||
# Unless otherwise specified, attempt to create the setting
|
||||
create = kwargs.pop('create', True)
|
||||
|
||||
# Perform cache lookup by default
|
||||
do_cache = kwargs.pop('cache', True)
|
||||
# Specify if cache lookup should be performed
|
||||
do_cache = kwargs.pop('cache', False)
|
||||
|
||||
# Prevent saving to the database during data import
|
||||
if InvenTree.ready.isImportingData():
|
||||
@ -572,12 +574,12 @@ class BaseInvenTreeSetting(models.Model):
|
||||
create = False
|
||||
do_cache = False
|
||||
|
||||
ckey = cls.create_cache_key(key, **kwargs)
|
||||
cache_key = cls.create_cache_key(key, **kwargs)
|
||||
|
||||
if do_cache:
|
||||
try:
|
||||
# First attempt to find the setting object in the cache
|
||||
cached_setting = cache.get(ckey)
|
||||
cached_setting = cache.get(cache_key)
|
||||
|
||||
if cached_setting is not None:
|
||||
return cached_setting
|
||||
@ -635,6 +637,17 @@ class BaseInvenTreeSetting(models.Model):
|
||||
|
||||
If it does not exist, return the backup value (default = None)
|
||||
"""
|
||||
if (
|
||||
cls.CHECK_SETTING_KEY
|
||||
and key not in cls.SETTINGS
|
||||
and not key.startswith('_')
|
||||
):
|
||||
logger.warning(
|
||||
"get_setting: Setting key '%s' is not defined for class %s",
|
||||
key,
|
||||
str(cls),
|
||||
)
|
||||
|
||||
# If no backup value is specified, attempt to retrieve a "default" value
|
||||
if backup_value is None:
|
||||
backup_value = cls.get_setting_default(key, **kwargs)
|
||||
@ -670,6 +683,17 @@ class BaseInvenTreeSetting(models.Model):
|
||||
change_user: User object (must be staff member to update a core setting)
|
||||
create: If True, create a new setting if the specified key does not exist.
|
||||
"""
|
||||
if (
|
||||
cls.CHECK_SETTING_KEY
|
||||
and key not in cls.SETTINGS
|
||||
and not key.startswith('_')
|
||||
):
|
||||
logger.warning(
|
||||
"set_setting: Setting key '%s' is not defined for class %s",
|
||||
key,
|
||||
str(cls),
|
||||
)
|
||||
|
||||
if change_user is not None and not change_user.is_staff:
|
||||
return
|
||||
|
||||
@ -1199,6 +1223,8 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
|
||||
SETTINGS: dict[str, InvenTreeSettingsKeyType]
|
||||
|
||||
CHECK_SETTING_KEY = True
|
||||
|
||||
class Meta:
|
||||
"""Meta options for InvenTreeSetting."""
|
||||
|
||||
@ -1694,7 +1720,7 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'STOCK_DELETE_DEPLETED_DEFAULT': {
|
||||
'name': _('Delete Depleted Stock'),
|
||||
'description': _(
|
||||
'Determines default behaviour when a stock item is depleted'
|
||||
'Determines default behavior when a stock item is depleted'
|
||||
),
|
||||
'default': True,
|
||||
'validator': bool,
|
||||
@ -1766,6 +1792,20 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': 'BO-{ref:04d}',
|
||||
'validator': build.validators.validate_build_order_reference_pattern,
|
||||
},
|
||||
'BUILDORDER_REQUIRE_RESPONSIBLE': {
|
||||
'name': _('Require Responsible Owner'),
|
||||
'description': _('A responsible owner must be assigned to each order'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS': {
|
||||
'name': _('Block Until Tests Pass'),
|
||||
'description': _(
|
||||
'Prevent build outputs from being completed until all required tests pass'
|
||||
),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'RETURNORDER_ENABLED': {
|
||||
'name': _('Enable Return Orders'),
|
||||
'description': _('Enable return order functionality in the user interface'),
|
||||
@ -1780,6 +1820,12 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': 'RMA-{ref:04d}',
|
||||
'validator': order.validators.validate_return_order_reference_pattern,
|
||||
},
|
||||
'RETURNORDER_REQUIRE_RESPONSIBLE': {
|
||||
'name': _('Require Responsible Owner'),
|
||||
'description': _('A responsible owner must be assigned to each order'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'RETURNORDER_EDIT_COMPLETED_ORDERS': {
|
||||
'name': _('Edit Completed Return Orders'),
|
||||
'description': _(
|
||||
@ -1796,6 +1842,12 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': 'SO-{ref:04d}',
|
||||
'validator': order.validators.validate_sales_order_reference_pattern,
|
||||
},
|
||||
'SALESORDER_REQUIRE_RESPONSIBLE': {
|
||||
'name': _('Require Responsible Owner'),
|
||||
'description': _('A responsible owner must be assigned to each order'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'SALESORDER_DEFAULT_SHIPMENT': {
|
||||
'name': _('Sales Order Default Shipment'),
|
||||
'description': _('Enable creation of default shipment with sales orders'),
|
||||
@ -1818,6 +1870,12 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': 'PO-{ref:04d}',
|
||||
'validator': order.validators.validate_purchase_order_reference_pattern,
|
||||
},
|
||||
'PURCHASEORDER_REQUIRE_RESPONSIBLE': {
|
||||
'name': _('Require Responsible Owner'),
|
||||
'description': _('A responsible owner must be assigned to each order'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'PURCHASEORDER_EDIT_COMPLETED_ORDERS': {
|
||||
'name': _('Edit Completed Purchase Orders'),
|
||||
'description': _(
|
||||
@ -2004,14 +2062,6 @@ class InvenTreeSetting(BaseInvenTreeSetting):
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS': {
|
||||
'name': _('Block Until Tests Pass'),
|
||||
'description': _(
|
||||
'Prevent build outputs from being completed until all required tests pass'
|
||||
),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
},
|
||||
'TEST_STATION_DATA': {
|
||||
'name': _('Enable Test Station Data'),
|
||||
'description': _('Enable test station data collection for test results'),
|
||||
@ -2054,7 +2104,9 @@ def label_printer_options():
|
||||
|
||||
|
||||
class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
"""An InvenTreeSetting object with a usercontext."""
|
||||
"""An InvenTreeSetting object with a user context."""
|
||||
|
||||
CHECK_SETTING_KEY = True
|
||||
|
||||
class Meta:
|
||||
"""Meta options for InvenTreeUserSetting."""
|
||||
@ -2093,7 +2145,7 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': bool,
|
||||
},
|
||||
'HOMEPAGE_BOM_REQUIRES_VALIDATION': {
|
||||
'name': _('Show unvalidated BOMs'),
|
||||
'name': _('Show invalid BOMs'),
|
||||
'description': _('Show BOMs that await validation on the homepage'),
|
||||
'default': False,
|
||||
'validator': bool,
|
||||
@ -2406,6 +2458,14 @@ class InvenTreeUserSetting(BaseInvenTreeSetting):
|
||||
'validator': [int],
|
||||
'default': '',
|
||||
},
|
||||
'DEFAULT_LINE_LABEL_TEMPLATE': {
|
||||
'name': _('Default build line label template'),
|
||||
'description': _(
|
||||
'The build line label template to be automatically selected'
|
||||
),
|
||||
'validator': [int],
|
||||
'default': '',
|
||||
},
|
||||
'NOTIFICATION_ERROR_REPORT': {
|
||||
'name': _('Receive error reports'),
|
||||
'description': _('Receive notifications for system errors'),
|
||||
@ -2616,7 +2676,7 @@ class VerificationMethod(Enum):
|
||||
|
||||
|
||||
class WebhookEndpoint(models.Model):
|
||||
"""Defines a Webhook entdpoint.
|
||||
"""Defines a Webhook endpoint.
|
||||
|
||||
Attributes:
|
||||
endpoint_id: Path to the webhook,
|
||||
@ -2951,7 +3011,7 @@ class NewsFeedEntry(models.Model):
|
||||
- published: Date of publishing of the news item
|
||||
- author: Author of news item
|
||||
- summary: Summary of the news items content
|
||||
- read: Was this iteam already by a superuser?
|
||||
- read: Was this item already by a superuser?
|
||||
"""
|
||||
|
||||
feed_id = models.CharField(verbose_name=_('Id'), unique=True, max_length=250)
|
||||
|
@ -207,6 +207,8 @@ class Order(
|
||||
responsible: User (or group) responsible for managing the order
|
||||
"""
|
||||
|
||||
REQUIRE_RESPONSIBLE_SETTING = None
|
||||
|
||||
class Meta:
|
||||
"""Metaclass options. Abstract ensures no database table is created."""
|
||||
|
||||
@ -227,6 +229,16 @@ class Order(
|
||||
"""Custom clean method for the generic order class."""
|
||||
super().clean()
|
||||
|
||||
# Check if a responsible owner is required for this order type
|
||||
if self.REQUIRE_RESPONSIBLE_SETTING:
|
||||
if common_models.InvenTreeSetting.get_setting(
|
||||
self.REQUIRE_RESPONSIBLE_SETTING, backup_value=False
|
||||
):
|
||||
if not self.responsible:
|
||||
raise ValidationError({
|
||||
'responsible': _('Responsible user or group must be specified')
|
||||
})
|
||||
|
||||
# Check that the referenced 'contact' matches the correct 'company'
|
||||
if self.company and self.contact:
|
||||
if self.contact.company != self.company:
|
||||
@ -347,6 +359,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
target_date: Expected delivery target date for PurchaseOrder completion (optional)
|
||||
"""
|
||||
|
||||
REFERENCE_PATTERN_SETTING = 'PURCHASEORDER_REFERENCE_PATTERN'
|
||||
REQUIRE_RESPONSIBLE_SETTING = 'PURCHASEORDER_REQUIRE_RESPONSIBLE'
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Get the 'web' URL for this order."""
|
||||
if settings.ENABLE_CLASSIC_FRONTEND:
|
||||
@ -372,9 +387,6 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
|
||||
return defaults
|
||||
|
||||
# Global setting for specifying reference pattern
|
||||
REFERENCE_PATTERN_SETTING = 'PURCHASEORDER_REFERENCE_PATTERN'
|
||||
|
||||
@staticmethod
|
||||
def filterByDate(queryset, min_date, max_date):
|
||||
"""Filter by 'minimum and maximum date range'.
|
||||
@ -805,6 +817,9 @@ class PurchaseOrder(TotalPriceMixin, Order):
|
||||
class SalesOrder(TotalPriceMixin, Order):
|
||||
"""A SalesOrder represents a list of goods shipped outwards to a customer."""
|
||||
|
||||
REFERENCE_PATTERN_SETTING = 'SALESORDER_REFERENCE_PATTERN'
|
||||
REQUIRE_RESPONSIBLE_SETTING = 'SALESORDER_REQUIRE_RESPONSIBLE'
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Get the 'web' URL for this order."""
|
||||
if settings.ENABLE_CLASSIC_FRONTEND:
|
||||
@ -828,9 +843,6 @@ class SalesOrder(TotalPriceMixin, Order):
|
||||
|
||||
return defaults
|
||||
|
||||
# Global setting for specifying reference pattern
|
||||
REFERENCE_PATTERN_SETTING = 'SALESORDER_REFERENCE_PATTERN'
|
||||
|
||||
@staticmethod
|
||||
def filterByDate(queryset, min_date, max_date):
|
||||
"""Filter by "minimum and maximum date range".
|
||||
@ -1943,6 +1955,9 @@ class ReturnOrder(TotalPriceMixin, Order):
|
||||
status: The status of the order (refer to status_codes.ReturnOrderStatus)
|
||||
"""
|
||||
|
||||
REFERENCE_PATTERN_SETTING = 'RETURNORDER_REFERENCE_PATTERN'
|
||||
REQUIRE_RESPONSIBLE_SETTING = 'RETURNORDER_REQUIRE_RESPONSIBLE'
|
||||
|
||||
def get_absolute_url(self):
|
||||
"""Get the 'web' URL for this order."""
|
||||
if settings.ENABLE_CLASSIC_FRONTEND:
|
||||
@ -1968,8 +1983,6 @@ class ReturnOrder(TotalPriceMixin, Order):
|
||||
|
||||
return defaults
|
||||
|
||||
REFERENCE_PATTERN_SETTING = 'RETURNORDER_REFERENCE_PATTERN'
|
||||
|
||||
def __str__(self):
|
||||
"""Render a string representation of this ReturnOrder."""
|
||||
return f"{self.reference} - {self.customer.name if self.customer else _('no customer')}"
|
||||
|
@ -13,6 +13,7 @@ from djmoney.money import Money
|
||||
from icalendar import Calendar
|
||||
from rest_framework import status
|
||||
|
||||
from common.models import InvenTreeSetting
|
||||
from common.settings import currency_codes
|
||||
from company.models import Company, SupplierPart, SupplierPriceBreak
|
||||
from InvenTree.status_codes import (
|
||||
@ -27,6 +28,7 @@ from InvenTree.unit_test import InvenTreeAPITestCase
|
||||
from order import models
|
||||
from part.models import Part
|
||||
from stock.models import StockItem
|
||||
from users.models import Owner
|
||||
|
||||
|
||||
class OrderTest(InvenTreeAPITestCase):
|
||||
@ -347,15 +349,35 @@ class PurchaseOrderTest(OrderTest):
|
||||
"""Test that we can create a new PurchaseOrder via the API."""
|
||||
self.assignRole('purchase_order.add')
|
||||
|
||||
self.post(
|
||||
reverse('api-po-list'),
|
||||
{
|
||||
'reference': 'PO-12345678',
|
||||
'supplier': 1,
|
||||
'description': 'A test purchase order',
|
||||
},
|
||||
expected_code=201,
|
||||
)
|
||||
setting = 'PURCHASEORDER_REQUIRE_RESPONSIBLE'
|
||||
url = reverse('api-po-list')
|
||||
|
||||
InvenTreeSetting.set_setting(setting, False)
|
||||
|
||||
data = {
|
||||
'reference': 'PO-12345678',
|
||||
'supplier': 1,
|
||||
'description': 'A test purchase order',
|
||||
}
|
||||
|
||||
self.post(url, data, expected_code=201)
|
||||
|
||||
# Check the 'responsible required' field
|
||||
InvenTreeSetting.set_setting(setting, True)
|
||||
|
||||
data['reference'] = 'PO-12345679'
|
||||
data['responsible'] = None
|
||||
|
||||
response = self.post(url, data, expected_code=400)
|
||||
|
||||
self.assertIn('Responsible user or group must be specified', str(response.data))
|
||||
|
||||
data['responsible'] = Owner.objects.first().pk
|
||||
|
||||
response = self.post(url, data, expected_code=201)
|
||||
|
||||
# Revert the setting to previous value
|
||||
InvenTreeSetting.set_setting(setting, False)
|
||||
|
||||
def test_po_creation_date(self):
|
||||
"""Test that we can create set the creation_date field of PurchaseOrder via the API."""
|
||||
|
@ -25,7 +25,9 @@ def compile_full_name_template(*args, **kwargs):
|
||||
global _part_full_name_template
|
||||
global _part_full_name_template_string
|
||||
|
||||
template_string = InvenTreeSetting.get_setting('PART_NAME_FORMAT', '')
|
||||
template_string = InvenTreeSetting.get_setting(
|
||||
'PART_NAME_FORMAT', backup_value='', cache=True
|
||||
)
|
||||
|
||||
# Skip if the template string has not changed
|
||||
if (
|
||||
|
@ -491,7 +491,7 @@ class PartCategoryAPITest(InvenTreeAPITestCase):
|
||||
|
||||
PartCategory.objects.rebuild()
|
||||
|
||||
with self.assertNumQueriesLessThan(10):
|
||||
with self.assertNumQueriesLessThan(12):
|
||||
response = self.get(reverse('api-part-category-tree'), expected_code=200)
|
||||
|
||||
self.assertEqual(len(response.data), PartCategory.objects.count())
|
||||
|
@ -446,7 +446,7 @@ class StockLocationTest(StockAPITestCase):
|
||||
|
||||
StockLocation.objects.rebuild()
|
||||
|
||||
with self.assertNumQueriesLessThan(10):
|
||||
with self.assertNumQueriesLessThan(12):
|
||||
response = self.get(reverse('api-location-tree'), expected_code=200)
|
||||
|
||||
self.assertEqual(len(response.data), StockLocation.objects.count())
|
||||
|
@ -13,6 +13,7 @@
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REFERENCE_PATTERN" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="BUILDORDER_REQUIRE_RESPONSIBLE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS" %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -11,6 +11,7 @@
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="PURCHASEORDER_REFERENCE_PATTERN" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="PURCHASEORDER_REQUIRE_RESPONSIBLE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="PURCHASEORDER_EDIT_COMPLETED_ORDERS" icon='fa-edit' %}
|
||||
{% include "InvenTree/settings/setting.html" with key="PURCHASEORDER_AUTO_COMPLETE" icon='fa-check-circle' %}
|
||||
</tbody>
|
||||
|
@ -13,6 +13,7 @@
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_ENABLED" icon="fa-check-circle" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_REFERENCE_PATTERN" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_REQUIRE_RESPONSIBLE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="RETURNORDER_EDIT_COMPLETED_ORDERS" icon="fa-edit" %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
@ -12,6 +12,7 @@
|
||||
<table class='table table-striped table-condensed'>
|
||||
<tbody>
|
||||
{% include "InvenTree/settings/setting.html" with key="SALESORDER_REFERENCE_PATTERN" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SALESORDER_REQUIRE_RESPONSIBLE" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SALESORDER_DEFAULT_SHIPMENT" icon="fa-truck-loading" %}
|
||||
{% include "InvenTree/settings/setting.html" with key="SALESORDER_EDIT_COMPLETED_ORDERS" icon='fa-edit' %}
|
||||
</tbody>
|
||||
|
@ -231,6 +231,7 @@ export default function SystemSettings() {
|
||||
<GlobalSettingList
|
||||
keys={[
|
||||
'BUILDORDER_REFERENCE_PATTERN',
|
||||
'BUILDORDER_REQUIRE_RESPONSIBLE',
|
||||
'PREVENT_BUILD_COMPLETION_HAVING_INCOMPLETED_TESTS'
|
||||
]}
|
||||
/>
|
||||
@ -244,6 +245,7 @@ export default function SystemSettings() {
|
||||
<GlobalSettingList
|
||||
keys={[
|
||||
'PURCHASEORDER_REFERENCE_PATTERN',
|
||||
'PURCHASEORDER_REQUIRE_RESPONSIBLE',
|
||||
'PURCHASEORDER_EDIT_COMPLETED_ORDERS',
|
||||
'PURCHASEORDER_AUTO_COMPLETE'
|
||||
]}
|
||||
@ -258,6 +260,7 @@ export default function SystemSettings() {
|
||||
<GlobalSettingList
|
||||
keys={[
|
||||
'SALESORDER_REFERENCE_PATTERN',
|
||||
'SALESORDER_REQUIRE_RESPONSIBLE',
|
||||
'SALESORDER_DEFAULT_SHIPMENT',
|
||||
'SALESORDER_EDIT_COMPLETED_ORDERS'
|
||||
]}
|
||||
@ -273,6 +276,7 @@ export default function SystemSettings() {
|
||||
keys={[
|
||||
'RETURNORDER_ENABLED',
|
||||
'RETURNORDER_REFERENCE_PATTERN',
|
||||
'RETURNORDER_REQUIRE_RESPONSIBLE',
|
||||
'RETURNORDER_EDIT_COMPLETED_ORDERS'
|
||||
]}
|
||||
/>
|
||||
|
Loading…
Reference in New Issue
Block a user