From c1d7f2a3000c6c8291b6565bb41c6e62680e5e99 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 23 Dec 2025 12:16:51 +1100 Subject: [PATCH] [bug] Trim stock allocation (#11060) * Trim stock allocation - Handle condition where allocated quantity exceeds available stock - Prevent silent failure of build completion * Fix display in buildallocatedstock table * Consolidate table columns * Fetch substitutes for BOM table --- src/backend/InvenTree/build/models.py | 6 ++--- src/frontend/src/tables/bom/BomTable.tsx | 5 ++-- .../tables/build/BuildAllocatedStockTable.tsx | 26 +++++++++---------- src/frontend/tests/pages/pui_part.spec.ts | 2 +- 4 files changed, 19 insertions(+), 20 deletions(-) diff --git a/src/backend/InvenTree/build/models.py b/src/backend/InvenTree/build/models.py index 42e8213099..2aed4fda01 100644 --- a/src/backend/InvenTree/build/models.py +++ b/src/backend/InvenTree/build/models.py @@ -1887,7 +1887,7 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel): return self.build_line.bom_item if self.build_line else None @transaction.atomic - def complete_allocation(self, quantity=None, notes='', user=None) -> None: + def complete_allocation(self, quantity=None, notes: str = '', user=None) -> None: """Complete the allocation of this BuildItem into the output stock item. Arguments: @@ -1910,9 +1910,7 @@ class BuildItem(InvenTree.models.InvenTreeMetadataModel): # Ensure we are not allocating more than available if quantity > item.quantity: - raise ValidationError({ - 'quantity': _('Allocated quantity exceeds available stock quantity') - }) + quantity = item.quantity # Split the allocated stock if there are more available than allocated if item.quantity > quantity: diff --git a/src/frontend/src/tables/bom/BomTable.tsx b/src/frontend/src/tables/bom/BomTable.tsx index cc50be9764..9cb18a2720 100644 --- a/src/frontend/src/tables/bom/BomTable.tsx +++ b/src/frontend/src/tables/bom/BomTable.tsx @@ -670,9 +670,10 @@ export function BomTable({ params: { ...params, part: partId, - category_detail: true, + substitutes: true, part_detail: true, - sub_part_detail: true + sub_part_detail: true, + category_detail: true }, tableActions: tableActions, tableFilters: tableFilters, diff --git a/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx b/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx index 0a67448029..bacf33764e 100644 --- a/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx +++ b/src/frontend/src/tables/build/BuildAllocatedStockTable.tsx @@ -6,7 +6,7 @@ import { ApiEndpoints } from '@lib/enums/ApiEndpoints'; import { ModelType } from '@lib/enums/ModelType'; import { UserRoles } from '@lib/enums/Roles'; import { apiUrl } from '@lib/functions/Api'; -import { ActionButton } from '@lib/index'; +import { ActionButton, formatDecimal } from '@lib/index'; import type { TableFilter } from '@lib/types/Filters'; import type { StockOperationProps } from '@lib/types/Forms'; import type { TableColumn } from '@lib/types/Tables'; @@ -113,13 +113,6 @@ export default function BuildAllocatedStockTable({ sortable: true, switchable: true }, - { - accessor: 'serial', - title: t`Serial Number`, - sortable: false, - switchable: true, - render: (record: any) => record?.stock_item_detail?.serial - }, { accessor: 'batch', title: t`Batch Code`, @@ -128,15 +121,22 @@ export default function BuildAllocatedStockTable({ render: (record: any) => record?.stock_item_detail?.batch }, DecimalColumn({ - accessor: 'available', + accessor: 'stock_item_detail.quantity', title: t`Available` }), - DecimalColumn({ + { accessor: 'quantity', title: t`Allocated`, - sortable: true, - switchable: false - }), + render: (record: any) => { + const serial = record?.stock_item_detail?.serial; + + if (serial && record?.quantity == 1) { + return `${t`Serial`}: ${serial}`; + } + + return formatDecimal(record.quantity); + } + }, LocationColumn({ accessor: 'location_detail', switchable: true, diff --git a/src/frontend/tests/pages/pui_part.spec.ts b/src/frontend/tests/pages/pui_part.spec.ts index 863ed4d669..4fd6dce101 100644 --- a/src/frontend/tests/pages/pui_part.spec.ts +++ b/src/frontend/tests/pages/pui_part.spec.ts @@ -135,7 +135,7 @@ test('Parts - BOM', async ({ browser }) => { await page.getByRole('button', { name: 'Close' }).click(); }); -test('Part - Editing', async ({ browser }) => { +test('Parts - Editing', async ({ browser }) => { const page = await doCachedLogin(browser, { url: 'part/104/details' }); await page.getByText('A square table - with blue paint').first().waitFor();