Stock location fix (#12742)

* Fill out stock location based on part default

* Add playwright tests
This commit is contained in:
Oliver
2026-08-30 11:44:07 +10:00
committed by GitHub
parent a205717171
commit 871147d936
5 changed files with 80 additions and 6 deletions
+17
View File
@@ -91,6 +91,7 @@ import { TagsField } from './CommonFields';
*/
export function useStockFields({
partId,
locationId,
stockItem,
create = false,
supplierPartId,
@@ -98,6 +99,7 @@ export function useStockFields({
modalId
}: {
partId?: number;
locationId?: number;
stockItem?: any;
modalId: string;
create: boolean;
@@ -113,6 +115,9 @@ export function useStockFields({
supplierPartId ?? null
);
// Keep track of the "location" for the new stock item
const [location, setLocation] = useState<number | null>(locationId ?? null);
const [expiryDate, setExpiryDate] = useState<string | null>(null);
const [quantity, setQuantity] = useState<number | null>(null);
const [purchasePrice, setPurchasePrice] = useState<number | null>(null);
@@ -198,6 +203,15 @@ export function useStockFields({
dayjs().add(expiry_days, 'days').format('YYYY-MM-DD')
);
}
// Fill out the default location for the part, if not already set
setLocation(
(current) =>
current ??
record?.default_location ??
record?.category_default_location ??
null
);
}
},
supplier_part: {
@@ -227,7 +241,9 @@ export function useStockFields({
location: {
// Cannot adjust location for existing stock items
hidden: !create,
value: location,
onValueChange: (value) => {
setLocation(value);
batchGenerator.update({ location: value });
},
filters: {
@@ -323,6 +339,7 @@ export function useStockFields({
partId,
globalSettings,
supplierPart,
location,
create,
supplierPartId,
purchasePrice,
@@ -374,6 +374,7 @@ export default function StockDetail() {
const duplicateStockItemFields = useStockFields({
create: true,
locationId: stockitem.location,
modalId: 'duplicate-stock-item'
});
@@ -412,6 +412,7 @@ export function StockItemTable({
const newStockItemFields = useStockFields({
create: true,
partId: params.part,
locationId: params.location,
supplierPartId: params.supplier_part,
pricing: params.pricing,
modalId: 'add-stock-item'
@@ -423,8 +424,7 @@ export function StockItemTable({
modalId: 'add-stock-item',
fields: newStockItemFields,
initialData: {
part: params.part,
location: params.location
part: params.part
},
follow: params.openNewStockItem ?? true,
table: table,
+60 -2
View File
@@ -658,6 +658,64 @@ test('Stock - Disassembly', async ({ browser }) => {
await page.getByRole('cell', { name: 'Thumbnail XT90-F' }).waitFor();
await page.getByRole('cell', { name: 'Thumbnail XT90-M' }).waitFor();
await page.waitForTimeout(2000);
});
// Test that the "location" field is correctly pre-filled when creating new stock items
test('Stock - Default Location', async ({ browser }) => {
const page = await doCachedLogin(browser, {
url: 'stock/location/12/stock-items'
});
// The stock item location is rendered via a "tree field" (backed by a real input)
const locationField = () =>
page.getByRole('textbox', { name: 'tree-field-location' });
// Scenario 1: Creating a new stock item from within a location should default to that location
await page
.getByRole('button', { name: 'action-button-add-stock-item' })
.click();
await expect(locationField()).toHaveValue('Location 0');
await page.getByRole('button', { name: 'Cancel' }).click();
// Scenario 2: Duplicating a stock item should retain its original location
await navigate(page, 'stock/item/2/details');
await page
.getByRole('button', { name: 'action-menu-stock-item-actions' })
.click();
await page
.getByRole('menuitem', { name: 'action-menu-stock-item-actions-duplicate' })
.click();
await expect(locationField()).toHaveValue('Electronics Lab/Reel Storage');
await page.getByRole('button', { name: 'Cancel' }).click();
// Scenario 3: Creating a new stock item for a part with a specified default location
// First, assign a default location to the part
await navigate(page, 'part/86/details');
await page.getByText('1553WDBK').first().waitFor();
await page.keyboard.press('Control+E');
await page.getByPlaceholder('Select location').fill('Mechanical Lab');
await page
.getByRole('listbox')
.getByText('Mechanical Lab', { exact: true })
.click();
await page.waitForTimeout(250);
// The "Submit" button is only enabled if the value actually changed - if a
// previous test run already left this part with the same default location,
// there is nothing to save, so just close the form instead
const submitButton = page.getByRole('button', { name: 'Submit' });
if (await submitButton.isEnabled()) {
await submitButton.click();
await page.waitForLoadState('networkidle');
} else {
await page.getByRole('button', { name: 'Cancel' }).click();
}
// Now, create a new stock item for this part, and check the location is pre-filled
await navigate(page, 'part/86/stock');
await page
.getByRole('button', { name: 'action-button-add-stock-item' })
.click();
await expect(locationField()).toHaveValue('Factory/Mechanical Lab');
await page.getByRole('button', { name: 'Cancel' }).click();
});
-2
View File
@@ -428,8 +428,6 @@ test('Settings - Admin - Barcode History', async ({ browser }) => {
for (const barcode of barcodes) {
await checkBarcode(barcode);
}
await page.waitForTimeout(2500);
});
test('Settings - Admin - Parameter', async ({ browser }) => {