mirror of
https://github.com/inventree/InvenTree.git
synced 2026-07-27 17:11:33 +00:00
Add configurable default for PO line item merging (#12472)
* Add configurable default for PO line item merging Introduce PURCHASEORDER_MERGE_LINE_ITEMS global setting to control the default state of the merge_items option when creating purchase order lines. Fixes #10947 * Apply ruff and biome formatting for CI
This commit is contained in:
@@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||
|
||||
### Added
|
||||
|
||||
- Adds configurable default for merging purchase order line items via the `PURCHASEORDER_MERGE_LINE_ITEMS` global setting
|
||||
- [#12393](https://github.com/inventree/InvenTree/pull/12393) adds "discount" attribute to order line items, allowing users to specify a discount for each line item on an order. The discount can be specified as either a percentage or a fixed amount, and is applied to the line item total when calculating the order total.
|
||||
- [#12391](https://github.com/inventree/InvenTree/pull/12391) adds facility for bulk deleting line items against orders
|
||||
- [#12388](https://github.com/inventree/InvenTree/pull/12388) adds uniqueness requirements options for the Parameter and ParameterTemplate models. This allows users to specify whether a parameter value should be unique for a given model type, or globally unique across all models.
|
||||
|
||||
@@ -225,3 +225,4 @@ The following [global settings](../settings/global.md) are available for purchas
|
||||
{{ globalsetting("PURCHASEORDER_CONVERT_CURRENCY") }}
|
||||
{{ globalsetting("PURCHASEORDER_EDIT_COMPLETED_ORDERS") }}
|
||||
{{ globalsetting("PURCHASEORDER_AUTO_COMPLETE") }}
|
||||
{{ globalsetting("PURCHASEORDER_MERGE_LINE_ITEMS") }}
|
||||
|
||||
@@ -1003,6 +1003,14 @@ SYSTEM_SETTINGS: dict[str, InvenTreeSettingsKeyType] = {
|
||||
'default': True,
|
||||
'validator': bool,
|
||||
},
|
||||
'PURCHASEORDER_MERGE_LINE_ITEMS': {
|
||||
'name': _('Merge Purchase Order Line Items'),
|
||||
'description': _(
|
||||
'Merge new purchase order line items with existing lines that share the same part, destination, and target date'
|
||||
),
|
||||
'default': True,
|
||||
'validator': bool,
|
||||
},
|
||||
# login / SSO
|
||||
'LOGIN_ENABLE_PWD_FORGOT': {
|
||||
'name': _('Enable password forgot'),
|
||||
|
||||
@@ -684,7 +684,14 @@ class PurchaseOrderLineItemList(
|
||||
|
||||
# possibly merge duplicate items
|
||||
line_item = None
|
||||
if data.get('merge_items', True):
|
||||
merge_items = data.get(
|
||||
'merge_items',
|
||||
common.settings.get_global_setting(
|
||||
'PURCHASEORDER_MERGE_LINE_ITEMS', backup_value=True
|
||||
),
|
||||
)
|
||||
|
||||
if merge_items:
|
||||
with transaction.atomic():
|
||||
# Lock the matching row, so concurrent line creations cannot
|
||||
# both read the same starting quantity (lost update)
|
||||
|
||||
@@ -745,6 +745,14 @@ class PurchaseOrderLineItemSerializer(
|
||||
write_only=True,
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Set dynamic defaults for create-only fields."""
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.fields['merge_items'].default = get_global_setting(
|
||||
'PURCHASEORDER_MERGE_LINE_ITEMS', backup_value=True
|
||||
)
|
||||
|
||||
sku = serializers.CharField(
|
||||
source='part.SKU', read_only=True, allow_null=True, label=_('SKU')
|
||||
)
|
||||
|
||||
@@ -1060,6 +1060,42 @@ class PurchaseOrderLineItemTest(OrderTest):
|
||||
).json()
|
||||
self.assertEqual(float(li5['purchase_price']), 1)
|
||||
|
||||
def test_po_line_merge_default_setting(self):
|
||||
"""Test that merge_items defaults to the global setting value."""
|
||||
self.assignRole('purchase_order.add')
|
||||
|
||||
su = Company.objects.get(pk=1)
|
||||
sp = SupplierPart.objects.get(pk=1)
|
||||
po = models.PurchaseOrder.objects.create(
|
||||
supplier=su, reference='PO-MERGE-DEFAULT'
|
||||
)
|
||||
|
||||
set_global_setting('PURCHASEORDER_MERGE_LINE_ITEMS', False)
|
||||
|
||||
li1 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{'order': po.pk, 'part': sp.pk, 'quantity': 1},
|
||||
expected_code=201,
|
||||
).json()
|
||||
|
||||
li2 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{'order': po.pk, 'part': sp.pk, 'quantity': 2},
|
||||
expected_code=201,
|
||||
).json()
|
||||
|
||||
self.assertNotEqual(li1['pk'], li2['pk'])
|
||||
|
||||
set_global_setting('PURCHASEORDER_MERGE_LINE_ITEMS', True)
|
||||
|
||||
li3 = self.post(
|
||||
reverse('api-po-line-list'),
|
||||
{'order': po.pk, 'part': sp.pk, 'quantity': 3},
|
||||
expected_code=201,
|
||||
).json()
|
||||
|
||||
self.assertEqual(li1['pk'], li3['pk'])
|
||||
|
||||
def test_output_options(self):
|
||||
"""Test PurchaseOrderLineItem output option endpoint."""
|
||||
self.run_output_test(
|
||||
|
||||
@@ -32,6 +32,7 @@ import { usePurchaseOrderFields } from '../../forms/PurchaseOrderForms';
|
||||
import { useCreateApiFormModal } from '../../hooks/UseForm';
|
||||
import { useInstance } from '../../hooks/UseInstance';
|
||||
import useWizard from '../../hooks/UseWizard';
|
||||
import { useGlobalSettingsState } from '../../states/SettingsStates';
|
||||
import RemoveRowButton from '../buttons/RemoveRowButton';
|
||||
import { StandaloneField } from '../forms/StandaloneField';
|
||||
import Expand from '../items/Expand';
|
||||
@@ -195,6 +196,7 @@ function SelectPartsStep({
|
||||
const [selectedRecord, setSelectedRecord] = useState<PartOrderRecord | null>(
|
||||
null
|
||||
);
|
||||
const globalSettings = useGlobalSettingsState();
|
||||
|
||||
const purchaseOrderFields = usePurchaseOrderFields({
|
||||
supplierId: selectedRecord?.supplier_part?.supplier
|
||||
@@ -240,9 +242,11 @@ function SelectPartsStep({
|
||||
},
|
||||
purchase_price: {},
|
||||
purchase_price_currency: {},
|
||||
merge_items: {}
|
||||
merge_items: {
|
||||
default: globalSettings.isSet('PURCHASEORDER_MERGE_LINE_ITEMS', true)
|
||||
}
|
||||
};
|
||||
}, [selectedRecord]);
|
||||
}, [selectedRecord, globalSettings]);
|
||||
|
||||
const addToOrder = useCreateApiFormModal({
|
||||
url: apiUrl(ApiEndpoints.purchase_order_line_list),
|
||||
|
||||
@@ -220,7 +220,9 @@ export function usePurchaseOrderLineItemFields({
|
||||
}
|
||||
|
||||
if (create) {
|
||||
fields['merge_items'] = {};
|
||||
fields['merge_items'] = {
|
||||
default: globalSettings.isSet('PURCHASEORDER_MERGE_LINE_ITEMS', true)
|
||||
};
|
||||
}
|
||||
|
||||
return fields;
|
||||
|
||||
Reference in New Issue
Block a user