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({ export function useStockFields({
partId, partId,
locationId,
stockItem, stockItem,
create = false, create = false,
supplierPartId, supplierPartId,
@@ -98,6 +99,7 @@ export function useStockFields({
modalId modalId
}: { }: {
partId?: number; partId?: number;
locationId?: number;
stockItem?: any; stockItem?: any;
modalId: string; modalId: string;
create: boolean; create: boolean;
@@ -113,6 +115,9 @@ export function useStockFields({
supplierPartId ?? null 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 [expiryDate, setExpiryDate] = useState<string | null>(null);
const [quantity, setQuantity] = useState<number | null>(null); const [quantity, setQuantity] = useState<number | null>(null);
const [purchasePrice, setPurchasePrice] = 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') 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: { supplier_part: {
@@ -227,7 +241,9 @@ export function useStockFields({
location: { location: {
// Cannot adjust location for existing stock items // Cannot adjust location for existing stock items
hidden: !create, hidden: !create,
value: location,
onValueChange: (value) => { onValueChange: (value) => {
setLocation(value);
batchGenerator.update({ location: value }); batchGenerator.update({ location: value });
}, },
filters: { filters: {
@@ -323,6 +339,7 @@ export function useStockFields({
partId, partId,
globalSettings, globalSettings,
supplierPart, supplierPart,
location,
create, create,
supplierPartId, supplierPartId,
purchasePrice, purchasePrice,
@@ -374,6 +374,7 @@ export default function StockDetail() {
const duplicateStockItemFields = useStockFields({ const duplicateStockItemFields = useStockFields({
create: true, create: true,
locationId: stockitem.location,
modalId: 'duplicate-stock-item' modalId: 'duplicate-stock-item'
}); });
@@ -412,6 +412,7 @@ export function StockItemTable({
const newStockItemFields = useStockFields({ const newStockItemFields = useStockFields({
create: true, create: true,
partId: params.part, partId: params.part,
locationId: params.location,
supplierPartId: params.supplier_part, supplierPartId: params.supplier_part,
pricing: params.pricing, pricing: params.pricing,
modalId: 'add-stock-item' modalId: 'add-stock-item'
@@ -423,8 +424,7 @@ export function StockItemTable({
modalId: 'add-stock-item', modalId: 'add-stock-item',
fields: newStockItemFields, fields: newStockItemFields,
initialData: { initialData: {
part: params.part, part: params.part
location: params.location
}, },
follow: params.openNewStockItem ?? true, follow: params.openNewStockItem ?? true,
table: table, 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-F' }).waitFor();
await page.getByRole('cell', { name: 'Thumbnail XT90-M' }).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) { for (const barcode of barcodes) {
await checkBarcode(barcode); await checkBarcode(barcode);
} }
await page.waitForTimeout(2500);
}); });
test('Settings - Admin - Parameter', async ({ browser }) => { test('Settings - Admin - Parameter', async ({ browser }) => {