2
0
mirror of https://github.com/inventree/InvenTree.git synced 2025-12-17 09:48:30 +00:00

[UI] Suggested pricing (#10867)

* Refactor NumberField into separate component

* Add helper func to ensure a number is a number

* Use placeholder value for suggested sale price

* Fix for auto-fill

* Tweak price calculation

* Add UI testing for sales order price breaks

* Fix aria label name

* Annotate price breaks to supplier part

* Fetch price break data

* Support price breaks for purchase order pricing

* Fix required to prevent circular imports

* Add playwright tests for purchase order price breaks

* Bump API version

* Re-add output options for SupplierPriceBreakList

* Revert change

* Simplify unit test
This commit is contained in:
Oliver
2025-11-20 22:51:27 +11:00
committed by GitHub
parent 835c7784f9
commit a76ec0a7b8
16 changed files with 465 additions and 201 deletions

View File

@@ -253,7 +253,7 @@ test('Build Order - Build Outputs', async ({ browser }) => {
// Accept the suggested batch code
await page
.getByRole('img', { name: 'text-field-batch_code-accept-placeholder' })
.getByRole('img', { name: 'field-batch_code-accept-placeholder' })
.click();
await page.getByLabel('related-field-location').click();

View File

@@ -514,10 +514,13 @@ test('Parts - Parameters', async ({ browser }) => {
// Submit with "false" value
await page.getByRole('button', { name: 'Submit' }).click();
const cell = await page.getByRole('cell', {
name: 'Is this part polarized?'
});
// Check for the expected values in the table
let row = await getRowFromCell(
await page.getByRole('cell', { name: 'Polarized', exact: true })
);
const row = await getRowFromCell(cell);
await row.getByRole('cell', { name: 'No', exact: true }).waitFor();
await row.getByRole('cell', { name: 'allaccess' }).waitFor();
await row.getByLabel(/row-action-menu-/i).click();
@@ -532,13 +535,6 @@ test('Parts - Parameters', async ({ browser }) => {
.click();
await page.getByRole('button', { name: 'Submit' }).click();
row = await getRowFromCell(
await page.getByRole('cell', { name: 'Polarized', exact: true })
);
await row.getByRole('cell', { name: 'Yes', exact: true }).waitFor();
await page.getByText('1 - 1 / 1').waitFor();
// Finally, delete the parameter
await row.getByLabel(/row-action-menu-/i).click();
await page.getByRole('menuitem', { name: 'Delete' }).click();

View File

@@ -216,6 +216,44 @@ test('Purchase Orders - Filters', async ({ browser }) => {
await page.getByRole('option', { name: 'Target Date After' }).waitFor();
});
test('Purchase Orders - Price Breaks', async ({ browser }) => {
const page = await doCachedLogin(browser, {
url: 'purchasing/purchase-order/14/line-items'
});
await page
.getByRole('button', { name: 'action-button-add-line-item' })
.click();
await page.getByLabel('related-field-part').fill('002.01');
await page.getByRole('option', { name: 'PCBWOY PCB-002.01' }).click();
// Expected price-break values
const priceBreaks = {
1: 500,
8: 500,
10: 565,
99: 565,
999: 205
};
for (const [qty, expectedPrice] of Object.entries(priceBreaks)) {
await page.getByLabel('number-field-quantity').fill(qty);
await expect(
page.getByRole('textbox', { name: 'number-field-purchase_price' })
).toHaveAttribute('placeholder', expectedPrice.toString(), {
timeout: 500
});
}
// Auto-fill the suggested sale price
await page.getByLabel('field-purchase_price-accept-placeholder').click();
await expect(
page.getByRole('textbox', { name: 'number-field-purchase_price' })
).toHaveValue('205', { timeout: 500 });
});
test('Purchase Orders - Order Parts', async ({ browser }) => {
const page = await doCachedLogin(browser);

View File

@@ -279,3 +279,45 @@ test('Sales Orders - Duplicate', async ({ browser }) => {
await page.getByText('Complete').first().waitFor();
await page.getByText('2 / 2').waitFor();
});
/**
* Test auto-calculation of price breaks on sales order line items
*/
test('Sales Orders - Price Breaks', async ({ browser }) => {
const page = await doCachedLogin(browser, {
url: 'sales/sales-order/14/line-items'
});
await page
.getByRole('button', { name: 'action-button-add-line-item' })
.click();
await page.getByLabel('related-field-part').fill('software');
await page.getByRole('option', { name: 'Software License' }).click();
const priceBreaks = {
1: 123,
2: 123,
10: 104,
49: 104,
56: 96,
104: 78
};
for (const [qty, expectedPrice] of Object.entries(priceBreaks)) {
await page.getByLabel('number-field-quantity').fill(qty);
await expect(
page.getByRole('textbox', { name: 'number-field-sale_price' })
).toHaveAttribute('placeholder', expectedPrice.toString(), {
timeout: 500
});
}
// Auto-fill the suggested sale price
await page.getByLabel('field-sale_price-accept-placeholder').click();
// The sale price field should now contain the suggested value
await expect(
page.getByRole('textbox', { name: 'number-field-sale_price' })
).toHaveValue('78', { timeout: 500 });
});