mirror of
https://github.com/inventree/InvenTree.git
synced 2026-04-04 10:31:03 +00:00
[API] Enhanced filtering for generic parameters (#11383)
* Enhanced filtering for parameter templates - Allow filtering by model ID as well as model type * Enhanced filtering for ParameterTemplate - Required for the parameteric data tables - Enable filtering by base model ID - Enable filtering by related model ID * Bump API version * Remove outdated comments * Fix typo * Remove debug statement * Added unit tests * Playwright tests * Fix unit test * Bump requirements * Revert
This commit is contained in:
@@ -101,12 +101,18 @@ function ParameterCell({
|
||||
*/
|
||||
export default function ParametricDataTable({
|
||||
modelType,
|
||||
modelId,
|
||||
relatedModel,
|
||||
relatedModelId,
|
||||
endpoint,
|
||||
queryParams,
|
||||
customFilters,
|
||||
customColumns
|
||||
}: {
|
||||
modelType: ModelType;
|
||||
modelId?: number;
|
||||
relatedModel?: string;
|
||||
relatedModelId?: number;
|
||||
endpoint: ApiEndpoints | string;
|
||||
queryParams?: Record<string, any>;
|
||||
customFilters?: TableFilter[];
|
||||
@@ -125,8 +131,12 @@ export default function ParametricDataTable({
|
||||
.get(apiUrl(ApiEndpoints.parameter_template_list), {
|
||||
params: {
|
||||
active: true,
|
||||
ordering: 'name',
|
||||
for_model: modelType,
|
||||
exists_for_model: modelType
|
||||
exists_for_model: modelType,
|
||||
exists_for_model_id: modelId ?? undefined,
|
||||
exists_for_related_model: relatedModel ?? undefined,
|
||||
exists_for_related_model_id: relatedModelId ?? undefined
|
||||
}
|
||||
})
|
||||
.then((response) => response.data);
|
||||
|
||||
@@ -56,6 +56,8 @@ export default function ParametricPartTable({
|
||||
return (
|
||||
<ParametricDataTable
|
||||
modelType={ModelType.part}
|
||||
relatedModel={'category'}
|
||||
relatedModelId={categoryId}
|
||||
endpoint={ApiEndpoints.part_list}
|
||||
customColumns={customColumns}
|
||||
customFilters={customFilters}
|
||||
|
||||
@@ -180,3 +180,33 @@ export const toggleColumnSorting = async (page: Page, columnName: string) => {
|
||||
await page.waitForTimeout(50);
|
||||
await page.waitForLoadState('networkidle');
|
||||
};
|
||||
|
||||
// Display the 'table' view
|
||||
export const showTableView = async (page: Page) => {
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
};
|
||||
|
||||
// Display the 'parameteric' view
|
||||
export const showParametricView = async (page: Page) => {
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
};
|
||||
|
||||
// Display the 'calendar' view
|
||||
export const showCalendarView = async (page: Page) => {
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-calendar' })
|
||||
.click();
|
||||
await page.waitForLoadState('networkidle');
|
||||
};
|
||||
|
||||
// Check for an expected number of columns in the visible table
|
||||
export const expectTableColumnCount = async (page: Page, count: number) => {
|
||||
const columns = page.locator('table thead tr th');
|
||||
await expect(columns).toHaveCount(count);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,10 @@ import {
|
||||
getRowFromCell,
|
||||
loadTab,
|
||||
navigate,
|
||||
setTableChoiceFilter
|
||||
setTableChoiceFilter,
|
||||
showCalendarView,
|
||||
showParametricView,
|
||||
showTableView
|
||||
} from '../helpers.ts';
|
||||
import { doCachedLogin } from '../login.ts';
|
||||
|
||||
@@ -17,18 +20,10 @@ test('Build - Index', async ({ browser }) => {
|
||||
await loadTab(page, 'Build Orders');
|
||||
|
||||
// Ensure all data views are available
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-calendar' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showCalendarView(page);
|
||||
await page.getByRole('button', { name: 'action-button-next-month' }).click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showTableView(page);
|
||||
});
|
||||
|
||||
test('Build Order - Basic Tests', async ({ browser }) => {
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import { test } from '../baseFixtures.js';
|
||||
import { clickOnParamFilter, loadTab, navigate } from '../helpers.js';
|
||||
import {
|
||||
clickOnParamFilter,
|
||||
loadTab,
|
||||
navigate,
|
||||
showParametricView
|
||||
} from '../helpers.js';
|
||||
import { doCachedLogin } from '../login.js';
|
||||
|
||||
test('Company', async ({ browser }) => {
|
||||
@@ -49,9 +54,7 @@ test('Company - Parameters', async ({ browser }) => {
|
||||
});
|
||||
|
||||
// Show parametric view
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
|
||||
// Filter by "payment terms" parameter value
|
||||
await clickOnParamFilter(page, 'Payment Terms');
|
||||
|
||||
@@ -4,10 +4,13 @@ import {
|
||||
clickOnParamFilter,
|
||||
clickOnRowMenu,
|
||||
deletePart,
|
||||
expectTableColumnCount,
|
||||
getRowFromCell,
|
||||
loadTab,
|
||||
navigate,
|
||||
setTableChoiceFilter
|
||||
setTableChoiceFilter,
|
||||
showParametricView,
|
||||
showTableView
|
||||
} from '../helpers';
|
||||
import { doCachedLogin } from '../login';
|
||||
import { setPluginState, setSettingState } from '../settings';
|
||||
@@ -499,6 +502,58 @@ test('Parts - Attachments', async ({ browser }) => {
|
||||
await page.getByRole('button', { name: 'Cancel' }).click();
|
||||
});
|
||||
|
||||
test('Parts - Parameters by Category', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser, { url: 'part/category/4/parts' });
|
||||
|
||||
await showParametricView(page);
|
||||
|
||||
// Check for expected parameter columns
|
||||
for (const col of [
|
||||
'Total Stock',
|
||||
'Capacitance [F]',
|
||||
'Power [W]',
|
||||
'Resistance [ohms]'
|
||||
]) {
|
||||
await page.getByRole('button', { name: col }).waitFor();
|
||||
}
|
||||
|
||||
await expectTableColumnCount(page, 9);
|
||||
|
||||
// Now let's go to the "resistors" category
|
||||
await navigate(page, 'part/category/5/parts');
|
||||
await showParametricView(page);
|
||||
|
||||
// Fewer parameter templates displayed here
|
||||
await expectTableColumnCount(page, 7);
|
||||
|
||||
// Check for expected parameter columns
|
||||
for (const col of [
|
||||
'Total Stock',
|
||||
'Tolerance [percent]',
|
||||
'Power [W]',
|
||||
'Resistance [ohms]'
|
||||
]) {
|
||||
await page.getByRole('button', { name: col }).waitFor();
|
||||
}
|
||||
|
||||
// Finally, let's go to the "capacitors" category, which has a different set of parameter templates
|
||||
await navigate(page, 'part/category/6/parts');
|
||||
await showParametricView(page);
|
||||
await expectTableColumnCount(page, 7);
|
||||
|
||||
for (const col of [
|
||||
'Total Stock',
|
||||
'Tolerance [percent]',
|
||||
'Polarized',
|
||||
'Capacitance [F]'
|
||||
]) {
|
||||
await page.getByRole('button', { name: col }).waitFor();
|
||||
}
|
||||
|
||||
// Reset to the table view
|
||||
await showTableView(page);
|
||||
});
|
||||
|
||||
test('Parts - Parameters', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser, { url: 'part/69/parameters' });
|
||||
|
||||
@@ -562,10 +617,8 @@ test('Parts - Parameter Filtering', async ({ browser }) => {
|
||||
const page = await doCachedLogin(browser, { url: 'part/' });
|
||||
|
||||
await loadTab(page, 'Parts', true);
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
|
||||
await showParametricView(page);
|
||||
await clearTableFilters(page);
|
||||
|
||||
// All parts should be available (no filters applied)
|
||||
|
||||
@@ -9,7 +9,10 @@ import {
|
||||
loadTab,
|
||||
navigate,
|
||||
openFilterDrawer,
|
||||
setTableChoiceFilter
|
||||
setTableChoiceFilter,
|
||||
showCalendarView,
|
||||
showParametricView,
|
||||
showTableView
|
||||
} from '../helpers.ts';
|
||||
import { doCachedLogin } from '../login.ts';
|
||||
|
||||
@@ -18,26 +21,14 @@ test('Purchasing - Index', async ({ browser }) => {
|
||||
|
||||
// Purchase Orders tab
|
||||
await loadTab(page, 'Purchase Orders');
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-calendar' })
|
||||
.click();
|
||||
await page.getByRole('button', { name: 'calendar-select-month' }).waitFor();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showCalendarView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Suppliers tab
|
||||
await loadTab(page, 'Suppliers');
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Check for expected values
|
||||
await clearTableFilters(page);
|
||||
@@ -45,12 +36,8 @@ test('Purchasing - Index', async ({ browser }) => {
|
||||
|
||||
// Supplier parts tab
|
||||
await loadTab(page, 'Supplier Parts');
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Check for expected values
|
||||
await clearTableFilters(page);
|
||||
@@ -60,12 +47,8 @@ test('Purchasing - Index', async ({ browser }) => {
|
||||
|
||||
// Manufacturers tab
|
||||
await loadTab(page, 'Manufacturers');
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Check for expected values
|
||||
await clearTableFilters(page);
|
||||
@@ -76,12 +59,8 @@ test('Purchasing - Index', async ({ browser }) => {
|
||||
|
||||
// Manufacturer parts tab
|
||||
await loadTab(page, 'Manufacturer Parts');
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Check for expected values
|
||||
await clearTableFilters(page);
|
||||
|
||||
@@ -5,7 +5,10 @@ import {
|
||||
clickOnRowMenu,
|
||||
globalSearch,
|
||||
loadTab,
|
||||
setTableChoiceFilter
|
||||
setTableChoiceFilter,
|
||||
showCalendarView,
|
||||
showParametricView,
|
||||
showTableView
|
||||
} from '../helpers.ts';
|
||||
import { doCachedLogin } from '../login.ts';
|
||||
|
||||
@@ -18,15 +21,9 @@ test('Sales Orders - Tabs', async ({ browser }) => {
|
||||
await loadTab(page, 'Sales Orders');
|
||||
await page.waitForURL('**/web/sales/index/salesorders');
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-calendar' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showCalendarView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Pending Shipments panel
|
||||
await loadTab(page, 'Pending Shipments');
|
||||
@@ -37,25 +34,15 @@ test('Sales Orders - Tabs', async ({ browser }) => {
|
||||
await loadTab(page, 'Return Orders');
|
||||
await page.getByRole('cell', { name: 'NOISE-COMPLAINT' }).waitFor();
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-calendar' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showCalendarView(page);
|
||||
await showTableView(page);
|
||||
|
||||
// Customers
|
||||
await loadTab(page, 'Customers');
|
||||
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-parametric' })
|
||||
.click();
|
||||
await page
|
||||
.getByRole('button', { name: 'segmented-icon-control-table' })
|
||||
.click();
|
||||
await showParametricView(page);
|
||||
await showTableView(page);
|
||||
|
||||
await page.getByText('Customer A').click();
|
||||
await loadTab(page, 'Notes');
|
||||
|
||||
Reference in New Issue
Block a user