2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-09-14 06:31:27 +00:00

Build order consume (#8191)

* Adds "consumed" field to BuildLine model

* Expose new field to serializer

* Add "consumed" column to BuildLineTable

* Boolean column tweaks

* Increase consumed count when completing allocation

* Add comment

* Update migration

* Add serializer for consuming build items

* Improve build-line sub-table

* Refactor BuildItem.complete_allocation method

- Allow optional quantity to be specified
- Adjust the allocated quantity when consuming

* Perform consumption

* Add "BuildConsume" API endpoint

* Implement frontend form

* Fixes for serializer

* Enhance front-end form

* Fix rendering of BuildLineTable

* Further improve rendering

* Bump API version

* Update API description

* Add option to consume by specifying a list of BuildLine objects

* Add form to consume stock via BuildLine reference

* Fix api_version

* Fix backup colors

* Fix typo

* Fix migrations

* Fix build forms

* Forms fixes

* Fix formatting

* Fixes for BuildLineTable

* Account for consumed stock in requirements calculation

* Reduce API requirements for BuildLineTable

* Docs updates

* Updated playwright testing

* Update src/frontend/src/forms/BuildForms.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Update src/frontend/src/tables/build/BuildLineTable.tsx

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>

* Add unit test for filters

* Add functional tests

* Tweak query count

* Increase max query time for testing

* adjust unit test again

* Prevent consumption of "tracked" items

* Adjust playwright tests

* Fix table

* Fix rendering

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
Oliver
2025-08-19 17:03:19 +10:00
committed by GitHub
parent ce6ffdac18
commit 49cc5fb137
24 changed files with 1079 additions and 142 deletions

View File

@@ -350,6 +350,59 @@ test('Build Order - Allocation', async ({ browser }) => {
.waitFor();
});
// Test partial stock consumption against build order
test('Build Order - Consume Stock', async ({ browser }) => {
const page = await doCachedLogin(browser, {
url: 'manufacturing/build-order/24/line-items'
});
// Check for expected progress values
await page.getByText('2 / 2', { exact: true }).waitFor();
await page.getByText('8 / 10', { exact: true }).waitFor();
await page.getByText('5 / 35', { exact: true }).waitFor();
await page.getByText('5 / 40', { exact: true }).waitFor();
// Open the "Allocate Stock" dialog
await page.getByRole('checkbox', { name: 'Select all records' }).click();
await page
.getByRole('button', { name: 'action-button-allocate-stock' })
.click();
await page
.getByLabel('Allocate Stock')
.getByText('5 / 35', { exact: true })
.waitFor();
await page.getByRole('button', { name: 'Cancel' }).click();
// Open the "Consume Stock" dialog
await page
.getByRole('button', { name: 'action-button-consume-stock' })
.click();
await page.getByLabel('Consume Stock').getByText('2 / 2').waitFor();
await page.getByLabel('Consume Stock').getByText('8 / 10').waitFor();
await page.getByLabel('Consume Stock').getByText('5 / 35').waitFor();
await page.getByLabel('Consume Stock').getByText('5 / 40').waitFor();
await page
.getByRole('textbox', { name: 'text-field-notes' })
.fill('some notes here...');
await page.getByRole('button', { name: 'Cancel' }).click();
// Try with a different build order
await navigate(page, 'manufacturing/build-order/26/line-items');
await page.getByRole('checkbox', { name: 'Select all records' }).click();
await page
.getByRole('button', { name: 'action-button-consume-stock' })
.click();
await page.getByLabel('Consume Stock').getByText('306 / 1,900').waitFor();
await page
.getByLabel('Consume Stock')
.getByText('Fully consumed')
.first()
.waitFor();
await page.waitForTimeout(1000);
});
test('Build Order - Tracked Outputs', async ({ browser }) => {
const page = await doCachedLogin(browser, {
url: 'manufacturing/build-order/10/incomplete-outputs'

View File

@@ -249,7 +249,13 @@ test('Parts - Requirements', async ({ browser }) => {
await page.getByText('5 / 100').waitFor(); // Allocated to sales orders
await page.getByText('10 / 125').waitFor(); // In production
await page.waitForTimeout(2500);
// Also check requirements for part with open build orders which have been partially consumed
await navigate(page, 'part/105/details');
await page.getByText('Required: 2').waitFor();
await page.getByText('Available: 32').waitFor();
await page.getByText('In Stock: 34').waitFor();
await page.getByText('2 / 2').waitFor(); // Allocated to build orders
});
test('Parts - Allocations', async ({ browser }) => {
@@ -377,7 +383,6 @@ test('Parts - Pricing (Supplier)', async ({ browser }) => {
// Supplier Pricing
await page.getByRole('button', { name: 'Supplier Pricing' }).click();
await page.waitForTimeout(500);
await page.getByRole('button', { name: 'SKU Not sorted' }).waitFor();
// Supplier Pricing - linkjumping

View File

@@ -323,6 +323,7 @@ test('Stock - Return Items', async ({ browser }) => {
name: 'action-menu-stock-operations-return-stock'
})
.click();
await page.getByText('#128').waitFor();
await page.getByText('Merge into existing stock').waitFor();
await page.getByRole('textbox', { name: 'number-field-quantity' }).fill('0');

View File

@@ -52,8 +52,6 @@ test('Plugins - Settings', async ({ browser, request }) => {
.fill(originalValue == '999' ? '1000' : '999');
await page.getByRole('button', { name: 'Submit' }).click();
await page.waitForTimeout(500);
// Change it back
await page.getByLabel('edit-setting-NUMERICAL_SETTING').click();
await page.getByLabel('number-field-value').fill(originalValue);
@@ -164,8 +162,6 @@ test('Plugins - Panels', async ({ browser, request }) => {
value: true
});
await page.waitForTimeout(500);
// Ensure that the SampleUI plugin is enabled
await setPluginState({
request,
@@ -173,8 +169,6 @@ test('Plugins - Panels', async ({ browser, request }) => {
state: true
});
await page.waitForTimeout(500);
// Navigate to the "part" page
await navigate(page, 'part/69/');
@@ -186,20 +180,14 @@ test('Plugins - Panels', async ({ browser, request }) => {
// Check out each of the plugin panels
await loadTab(page, 'Broken Panel');
await page.waitForTimeout(500);
await page.getByText('Error occurred while loading plugin content').waitFor();
await loadTab(page, 'Dynamic Panel');
await page.waitForTimeout(500);
await page.getByText('Instance ID: 69');
await page
.getByText('This panel has been dynamically rendered by the plugin system')
.waitFor();
await loadTab(page, 'Part Panel');
await page.waitForTimeout(500);
await page.getByText('This content has been rendered by a custom plugin');
// Disable the plugin, and ensure it is no longer visible
@@ -260,8 +248,6 @@ test('Plugins - Locate Item', async ({ browser, request }) => {
state: true
});
await page.waitForTimeout(500);
// Navigate to the "stock item" page
await navigate(page, 'stock/item/287/');
await page.waitForLoadState('networkidle');
@@ -273,7 +259,6 @@ test('Plugins - Locate Item', async ({ browser, request }) => {
// Show the location
await page.getByLabel('breadcrumb-1-factory').click();
await page.waitForTimeout(500);
await page.getByLabel('action-button-locate-item').click();
await page.getByRole('button', { name: 'Submit' }).click();