Allow bulk-edit for ReturnOrderLineItem (#12635)

* Allow bulk-edit for ReturnOrderLineItem

- Allow bulk change of line item outcome

* Update API version documentation with pull request links
This commit is contained in:
Oliver
2026-08-13 20:10:10 +10:00
committed by GitHub
parent 695b9c1dc2
commit 9844a4805e
4 changed files with 71 additions and 3 deletions
@@ -1,11 +1,14 @@
"""InvenTree API version information.""" """InvenTree API version information."""
# InvenTree API version # InvenTree API version
INVENTREE_API_VERSION = 530 INVENTREE_API_VERSION = 531
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" """Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
INVENTREE_API_TEXT = """ INVENTREE_API_TEXT = """
v531 -> 2026-08-13 : https://github.com/inventree/InvenTree/pull/12635
- Adds "bulk update" support for the ReturnOrderLineItem API endpoint
v530 -> 2026-07-28 : https://github.com/inventree/InvenTree/pull/12487 v530 -> 2026-07-28 : https://github.com/inventree/InvenTree/pull/12487
- Adds searching against ReportSnippet API endpoint - Adds searching against ReportSnippet API endpoint
+1
View File
@@ -1793,6 +1793,7 @@ class ReturnOrderLineItemOutputOptions(OutputConfiguration):
class ReturnOrderLineItemList( class ReturnOrderLineItemList(
ReturnOrderLineItemMixin, ReturnOrderLineItemMixin,
BulkUpdateMixin,
DataExportViewMixin, DataExportViewMixin,
OutputOptionsMixin, OutputOptionsMixin,
ListCreateDestroyAPIView, ListCreateDestroyAPIView,
+36
View File
@@ -3716,6 +3716,42 @@ class ReturnOrderLineItemTests(InvenTreeAPITestCase):
self.assertEqual(models.ReturnOrderLineItem.objects.count(), n - 1) self.assertEqual(models.ReturnOrderLineItem.objects.count(), n - 1)
def test_bulk_update(self):
"""Test that we can bulk update the 'outcome' field for multiple ReturnOrderLineItems via the API."""
ro = models.ReturnOrder.objects.get(pk=6)
# Create some extra line items against the same order, so we have multiple to update
models.ReturnOrderLineItem.objects.bulk_create([
models.ReturnOrderLineItem(order=ro, item_id=1006, quantity=1),
models.ReturnOrderLineItem(order=ro, item_id=1007, quantity=1),
])
items = list(
models.ReturnOrderLineItem.objects.filter(order=ro).values_list(
'pk', flat=True
)
)
self.assertEqual(len(items), 3)
for line in models.ReturnOrderLineItem.objects.filter(pk__in=items):
self.assertEqual(line.outcome, ReturnOrderLineStatus.PENDING)
url = reverse('api-return-order-line-list')
data = {'items': items, 'outcome': ReturnOrderLineStatus.REPAIR.value}
# Update should fail without the correct role
self.patch(url, data, expected_code=403)
self.assignRole('return_order.change')
response = self.patch(url, data, expected_code=200).data
self.assertEqual(len(response['items']), 3)
for line in models.ReturnOrderLineItem.objects.filter(pk__in=items):
self.assertEqual(line.outcome, ReturnOrderLineStatus.REPAIR.value)
def test_extra_line_bulk_delete(self): def test_extra_line_bulk_delete(self):
"""Test that we can bulk delete multiple ReturnOrderExtraLine items via the API.""" """Test that we can bulk delete multiple ReturnOrderExtraLine items via the API."""
ro = models.ReturnOrder.objects.first() ro = models.ReturnOrder.objects.first()
@@ -1,5 +1,5 @@
import { t } from '@lingui/core/macro'; import { t } from '@lingui/core/macro';
import { IconSquareArrowRight } from '@tabler/icons-react'; import { IconFlag, IconSquareArrowRight } from '@tabler/icons-react';
import { useCallback, useMemo, useState } from 'react'; import { useCallback, useMemo, useState } from 'react';
import { ActionButton } from '@lib/components/ActionButton'; import { ActionButton } from '@lib/components/ActionButton';
@@ -16,6 +16,7 @@ import { apiUrl } from '@lib/functions/Api';
import useTable from '@lib/hooks/UseTable'; import useTable from '@lib/hooks/UseTable';
import type { TableFilter } from '@lib/types/Filters'; import type { TableFilter } from '@lib/types/Filters';
import type { TableColumn } from '@lib/types/Tables'; import type { TableColumn } from '@lib/types/Tables';
import { Alert } from '@mantine/core';
import { import {
DateColumn, DateColumn,
DescriptionColumn, DescriptionColumn,
@@ -37,6 +38,7 @@ import {
useReturnOrderLineItemFields useReturnOrderLineItemFields
} from '../../forms/ReturnOrderForms'; } from '../../forms/ReturnOrderForms';
import { import {
useBulkEditApiFormModal,
useCreateApiFormModal, useCreateApiFormModal,
useDeleteApiFormModal, useDeleteApiFormModal,
useEditApiFormModal useEditApiFormModal
@@ -110,6 +112,21 @@ export default function ReturnOrderLineItemTable({
table: table table: table
}); });
const setOutcome = useBulkEditApiFormModal({
url: ApiEndpoints.return_order_line_list,
items: table.selectedIds,
title: t`Set Outcome`,
preFormContent: (
<Alert color='blue'>
{t`Adjust the outcome for the selected line items.`}
</Alert>
),
fields: {
outcome: {}
},
onFormSuccess: table.refreshTable
});
const tableColumns: TableColumn[] = useMemo(() => { const tableColumns: TableColumn[] = useMemo(() => {
return [ return [
LineItemColumn({}), LineItemColumn({}),
@@ -211,6 +228,16 @@ export default function ReturnOrderLineItemTable({
receiveLineItems.open(); receiveLineItems.open();
}} }}
disabled={table.selectedRecords.length == 0} disabled={table.selectedRecords.length == 0}
/>,
<ActionButton
key='set-outcome'
tooltip={t`Set outcome for selected items`}
icon={<IconFlag />}
hidden={!editable || !user.hasChangeRole(UserRoles.return_order)}
onClick={() => {
setOutcome.open();
}}
disabled={table.selectedRecords.length == 0}
/> />
]; ];
}, [user, editable, inProgress, orderId, table.selectedRecords]); }, [user, editable, inProgress, orderId, table.selectedRecords]);
@@ -266,6 +293,7 @@ export default function ReturnOrderLineItemTable({
{editLine.modal} {editLine.modal}
{deleteLine.modal} {deleteLine.modal}
{receiveLineItems.modal} {receiveLineItems.modal}
{setOutcome.modal}
<InvenTreeTable <InvenTreeTable
url={apiUrl(ApiEndpoints.return_order_line_list)} url={apiUrl(ApiEndpoints.return_order_line_list)}
tableState={table} tableState={table}
@@ -279,7 +307,7 @@ export default function ReturnOrderLineItemTable({
}, },
defaultSortColumn: 'line', defaultSortColumn: 'line',
enableSelection: enableSelection:
inProgress && user.hasChangeRole(UserRoles.return_order), editable && user.hasChangeRole(UserRoles.return_order),
enableBulkDelete: enableBulkDelete:
editable && user.hasDeleteRole(UserRoles.return_order), editable && user.hasDeleteRole(UserRoles.return_order),
afterBulkDelete: orderDetailRefresh, afterBulkDelete: orderDetailRefresh,