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({
|
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,
|
||||||
|
|||||||
@@ -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();
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -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 }) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user