diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index e4602695e0..bf324d275c 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,11 +1,14 @@ """InvenTree API version information.""" # 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.""" 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 - Adds searching against ReportSnippet API endpoint diff --git a/src/backend/InvenTree/order/api.py b/src/backend/InvenTree/order/api.py index d6b66ae4c4..9ca3108b85 100644 --- a/src/backend/InvenTree/order/api.py +++ b/src/backend/InvenTree/order/api.py @@ -1793,6 +1793,7 @@ class ReturnOrderLineItemOutputOptions(OutputConfiguration): class ReturnOrderLineItemList( ReturnOrderLineItemMixin, + BulkUpdateMixin, DataExportViewMixin, OutputOptionsMixin, ListCreateDestroyAPIView, diff --git a/src/backend/InvenTree/order/test_api.py b/src/backend/InvenTree/order/test_api.py index 83a90d50a3..893976cdb2 100644 --- a/src/backend/InvenTree/order/test_api.py +++ b/src/backend/InvenTree/order/test_api.py @@ -3716,6 +3716,42 @@ class ReturnOrderLineItemTests(InvenTreeAPITestCase): 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): """Test that we can bulk delete multiple ReturnOrderExtraLine items via the API.""" ro = models.ReturnOrder.objects.first() diff --git a/src/frontend/src/tables/sales/ReturnOrderLineItemTable.tsx b/src/frontend/src/tables/sales/ReturnOrderLineItemTable.tsx index 2f533067f3..918fe31955 100644 --- a/src/frontend/src/tables/sales/ReturnOrderLineItemTable.tsx +++ b/src/frontend/src/tables/sales/ReturnOrderLineItemTable.tsx @@ -1,5 +1,5 @@ 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 { ActionButton } from '@lib/components/ActionButton'; @@ -16,6 +16,7 @@ import { apiUrl } from '@lib/functions/Api'; import useTable from '@lib/hooks/UseTable'; import type { TableFilter } from '@lib/types/Filters'; import type { TableColumn } from '@lib/types/Tables'; +import { Alert } from '@mantine/core'; import { DateColumn, DescriptionColumn, @@ -37,6 +38,7 @@ import { useReturnOrderLineItemFields } from '../../forms/ReturnOrderForms'; import { + useBulkEditApiFormModal, useCreateApiFormModal, useDeleteApiFormModal, useEditApiFormModal @@ -110,6 +112,21 @@ export default function ReturnOrderLineItemTable({ table: table }); + const setOutcome = useBulkEditApiFormModal({ + url: ApiEndpoints.return_order_line_list, + items: table.selectedIds, + title: t`Set Outcome`, + preFormContent: ( + + {t`Adjust the outcome for the selected line items.`} + + ), + fields: { + outcome: {} + }, + onFormSuccess: table.refreshTable + }); + const tableColumns: TableColumn[] = useMemo(() => { return [ LineItemColumn({}), @@ -211,6 +228,16 @@ export default function ReturnOrderLineItemTable({ receiveLineItems.open(); }} disabled={table.selectedRecords.length == 0} + />, + } + hidden={!editable || !user.hasChangeRole(UserRoles.return_order)} + onClick={() => { + setOutcome.open(); + }} + disabled={table.selectedRecords.length == 0} /> ]; }, [user, editable, inProgress, orderId, table.selectedRecords]); @@ -266,6 +293,7 @@ export default function ReturnOrderLineItemTable({ {editLine.modal} {deleteLine.modal} {receiveLineItems.modal} + {setOutcome.modal}