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