2
0
mirror of https://github.com/inventree/InvenTree.git synced 2026-05-17 23:08:28 +00:00

Support physical units for BOM lines (#11631)

* Add new "raw_amount" field to BomItem model

* Batch process data migration

* Update migration

* Calculate 'quantity' from 'raw_amount' field

* Improve decimal formatting in migration

* Allow raw_amount in serializer

* Adjust frontend form

* API validation and unit tests

* Additional playwright tests

* Update API version and CHANGELOG

* Updated docs

* Fix docstring

* Better handling of empty values

* Tweak unit tests

* Tweak unit test

* Fix unit test

* Adjust form field text

* Adjust migration file

* Tweak playwright tests

* Fix unit test

* Adjust serializers / import-export / playwright

* Fix migration

* Fix validation

* Loosen comparison

---------

Co-authored-by: Matthias Mair <code@mjmair.com>
This commit is contained in:
Oliver
2026-05-10 15:32:34 +10:00
committed by GitHub
parent 29027c1cf2
commit bb2a72a6fb
16 changed files with 335 additions and 51 deletions
+53 -3
View File
@@ -1,3 +1,4 @@
import { expect } from '@playwright/test';
import { test } from '../baseFixtures';
import {
clearTableFilters,
@@ -219,6 +220,53 @@ test('Parts - BOM', async ({ browser }) => {
await page.getByRole('button', { name: 'Add Substitute' }).waitFor();
await page.getByRole('button', { name: 'Close' }).click();
// Let's try a BOM which has a "raw amount" which considers the units of the underlying part
await navigate(page, 'part/109/bom');
await page.getByRole('button', { name: 'action-button-edit-bom' }).click();
const paintCell = await page.getByRole('cell', {
name: 'Thumbnail Green Paint'
});
await clickOnRowMenu(paintCell);
await page.getByRole('menuitem', { name: 'Edit', exact: true }).click();
await expect(
page.getByRole('textbox', { name: 'text-field-raw_amount' })
).toHaveValue(/quart/);
// Try to assign invalid units to this item, which should be rejected by validation
await page
.getByRole('textbox', { name: 'text-field-raw_amount' })
.fill('2 cm');
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByText('Errors exist for one or more').waitFor();
await page.getByText('Could not convert 2 cm to litres').waitFor();
await page.getByRole('button', { name: 'Cancel' }).click();
// Create a new BOM item with valid units
await page.getByRole('button', { name: 'action-menu-add-bom-items' }).click();
await page
.getByRole('menuitem', { name: 'action-menu-add-bom-items-add' })
.click();
await page
.getByRole('combobox', { name: 'related-field-sub_part' })
.fill('red wire');
await page
.getByRole('option', { name: 'Thumbnail Silicon Wire 12AWG' })
.click();
await page
.getByRole('textbox', { name: 'text-field-reference' })
.fill('my-ref');
await page
.getByRole('textbox', { name: 'text-field-raw_amount' })
.fill('3/4 inches');
await page.getByRole('switch', { name: 'boolean-field-optional' }).click();
await page.getByRole('button', { name: 'Submit' }).click();
// Check for the value converted back to [m]
await page.getByRole('cell', { name: '0.01905' }).first().waitFor();
await page.getByRole('cell', { name: 'my-ref' }).first().waitFor();
// Finish editing the BOM
await page
.getByRole('button', { name: 'action-button-finish-editing-' })
@@ -240,13 +288,15 @@ test('Parts - BOM Validation', async ({ browser }) => {
.waitFor();
// Edit line item, to ensure BOM is not valid
const cell = await page.getByRole('cell', { name: 'Thumbnail Red Paint' });
const cell = await page.getByRole('cell', { name: 'paint', exact: true });
// await cell.click({ button: 'right' });
// await page.getByRole('button', { name: 'Edit', exact: true }).click();
await clickOnRowMenu(cell);
await page.getByRole('menuitem', { name: 'Edit', exact: true }).click();
const input = await page.getByRole('textbox', {
name: 'number-field-quantity'
name: 'text-field-raw_amount'
});
const value = await input.inputValue();
@@ -901,7 +951,7 @@ test('Parts - Parameter Filtering', async ({ browser }) => {
test('Parts - Test Results', async ({ browser }) => {
const page = await doCachedLogin(browser, { url: '/part/74/test_results' });
await page.waitForTimeout(500);
await page.waitForTimeout(200);
await page.getByText(/1 - \d+ \/ 1\d\d/).waitFor();
await page.getByText('Blue Paint Applied').waitFor();