Default receive location (#12911)

* Add global setting

* docs

* Dynamically set API field default

* UI elements

* regression testing

* Add CHANGELOG entry

* Fix unit test
This commit is contained in:
Oliver authored and GitHub committed 2026-09-26 11:39:44 +10:00
1 parent 862c934e0d
commit 979ddb3f06
12 files changed
+105 -9

No files matched your search

+8 -1
View File
@@ -422,7 +422,14 @@ class InvenTreeMetadata(SimpleMetadata):
# If a default value is specified for the serializer field, add it!
if 'default' not in field_info and field.default != empty:
field_info['default'] = field.get_default()
default = field.get_default()
# Related fields resolve their default to a model instance,
# which is not JSON serializable - reduce it to its primitive representation
if isinstance(field, serializers.RelatedField) and default is not None:
default = field.to_representation(default)
field_info['default'] = default
# Force non-nullable fields to read as "required"
# (even if there is a default value!)
@@ -1031,6 +1031,15 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
'default': True,
'validator': bool,
},
'PURCHASEORDER_DEFAULT_RECEIVE_LOCATION': {
'name': _('Default Receive Location'),
'description': _(
'Default destination location for received goods on new purchase orders'
),
'default': '',
'model': 'stock.stocklocation',
'model_filters': {'structural': False},
},
# login / SSO
'LOGIN_ENABLE_PWD_FORGOT': {
'name': _('Enable password forgot'),
+2
View File
@@ -503,6 +503,8 @@ class SettingsTest(InvenTreeTestCase):
'hidden',
'choices',
'units',
'model',
'model_filters',
'requires_restart',
'after_save',
'before_save',
@@ -420,6 +420,22 @@ class PurchaseOrderSerializer(
return [*fields, 'duplicate']
def __init__(self, *args, **kwargs):
"""Set a dynamic default for the 'destination' field, on creation only."""
super().__init__(*args, **kwargs)
if self.instance is None:
location_pk = get_global_setting(
'PURCHASEORDER_DEFAULT_RECEIVE_LOCATION', backup_value=None
)
if location_pk:
self.fields[
'destination'
].default = stock.models.StockLocation.objects.filter(
pk=location_pk
).first()
duplicate = DuplicateOptionsSerializer(
order.models.PurchaseOrder.objects.all(),
copy_lines=True,
+47
View File
@@ -527,6 +527,53 @@ class PurchaseOrderTest(OrderTest):
# Revert the setting to previous value
InvenTreeSetting.set_setting(setting, False)
def test_po_create_default_destination(self):
"""Test that the PURCHASEORDER_DEFAULT_RECEIVE_LOCATION setting is applied on creation."""
self.assignRole('purchase_order.add')
url = reverse('api-po-list')
location = StockLocation.objects.first()
assert location
# By default, no destination is set on the setting - so the field is left blank
set_global_setting('PURCHASEORDER_DEFAULT_RECEIVE_LOCATION', '')
data = {
'reference': 'PO-99990001',
'supplier': 1,
'description': 'A test purchase order',
}
response = self.post(url, data, expected_code=201)
self.assertIsNone(response.data['destination'])
# Now, set the global default - newly created orders should inherit it
set_global_setting('PURCHASEORDER_DEFAULT_RECEIVE_LOCATION', location.pk)
# The OPTIONS metadata for the 'destination' field should reflect the default location
response = self.options(url, expected_code=200)
self.assertEqual(
response.data['actions']['POST']['destination']['default'], location.pk
)
data['reference'] = 'PO-99990002'
response = self.post(url, data, expected_code=201)
self.assertEqual(response.data['destination'], location.pk)
# An explicitly provided destination should always take priority
other_location = StockLocation.objects.exclude(pk=location.pk).first()
assert other_location
data['reference'] = 'PO-99990003'
data['destination'] = other_location.pk
response = self.post(url, data, expected_code=201)
self.assertEqual(response.data['destination'], other_location.pk)
# Revert the setting to its previous value
set_global_setting('PURCHASEORDER_DEFAULT_RECEIVE_LOCATION', '')
def test_po_creation_date(self):
"""Test that we can create set the creation_date field of PurchaseOrder via the API."""
self.assignRole('purchase_order.add')