mirror of
https://github.com/inventree/InvenTree.git
synced 2026-08-31 17:28:07 +00:00
Stock location fix (#12742)
* Fill out stock location based on part default * Add playwright tests
This commit is contained in:
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
@@ -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 }) => {
|
||||
|
||||
Reference in New Issue
Block a user