mirror of
https://github.com/inventree/InvenTree.git
synced 2025-04-28 11:36:44 +00:00
API date filter updates (#8544)
* Add 'stocktake_before' and 'stocktake_after' filters for StockItem API * Enable new filters for StockItemTable * Update CUI table filters * Add more date filter options for orders * Add date filters to BuildList * Update BuildOrderTable filters * Add more order date filters * Cleanup PurchaseOrderFilter code * Implement more PUI table filters * Add "Completion Date" column to PurchaseOrderTable * Update ReturnOrderTable * Add 'text' option for TableFilter * filter state management * Bump API version * Sorting for table filters * Add playwright tests for stock table filtering * Playwright updates - Add some helper functions for common operations * Refactoring for Playwright tests
This commit is contained in:
parent
5e762bc7f7
commit
809a978f7d
@ -1,13 +1,20 @@
|
||||
"""InvenTree API version information."""
|
||||
|
||||
# InvenTree API version
|
||||
INVENTREE_API_VERSION = 283
|
||||
INVENTREE_API_VERSION = 284
|
||||
|
||||
"""Increment this API version number whenever there is a significant change to the API that any clients need to know about."""
|
||||
|
||||
|
||||
INVENTREE_API_TEXT = """
|
||||
|
||||
v284 - 2024-11-25 : https://github.com/inventree/InvenTree/pull/8544
|
||||
- Adds new date filters to the StockItem API
|
||||
- Adds new date filters to the BuildOrder API
|
||||
- Adds new date filters to the SalesOrder API
|
||||
- Adds new date filters to the PurchaseOrder API
|
||||
- Adds new date filters to the ReturnOrder API
|
||||
|
||||
v283 - 2024-11-20 : https://github.com/inventree/InvenTree/pull/8524
|
||||
- Adds "note" field to the PartRelated API endpoint
|
||||
|
||||
|
@ -23,7 +23,7 @@ import build.serializers
|
||||
from build.models import Build, BuildLine, BuildItem
|
||||
import part.models
|
||||
from users.models import Owner
|
||||
from InvenTree.filters import SEARCH_ORDER_FILTER_ALIAS
|
||||
from InvenTree.filters import InvenTreeDateFilter, SEARCH_ORDER_FILTER_ALIAS
|
||||
|
||||
|
||||
class BuildFilter(rest_filters.FilterSet):
|
||||
@ -179,6 +179,36 @@ class BuildFilter(rest_filters.FilterSet):
|
||||
return queryset.exclude(project_code=None)
|
||||
return queryset.filter(project_code=None)
|
||||
|
||||
created_before = InvenTreeDateFilter(
|
||||
label=_('Created before'),
|
||||
field_name='creation_date', lookup_expr='lt'\
|
||||
)
|
||||
|
||||
created_after = InvenTreeDateFilter(
|
||||
label=_('Created after'),
|
||||
field_name='creation_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
target_date_before = InvenTreeDateFilter(
|
||||
label=_('Target date before'),
|
||||
field_name='target_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
target_date_after = InvenTreeDateFilter(
|
||||
label=_('Target date after'),
|
||||
field_name='target_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
completed_before = InvenTreeDateFilter(
|
||||
label=_('Completed before'),
|
||||
field_name='completion_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
completed_after = InvenTreeDateFilter(
|
||||
label=_('Completed after'),
|
||||
field_name='completion_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
|
||||
class BuildMixin:
|
||||
"""Mixin class for Build API endpoints."""
|
||||
|
@ -21,7 +21,11 @@ import company.models
|
||||
from generic.states.api import StatusView
|
||||
from importer.mixins import DataExportViewMixin
|
||||
from InvenTree.api import ListCreateDestroyAPIView, MetadataView
|
||||
from InvenTree.filters import SEARCH_ORDER_FILTER, SEARCH_ORDER_FILTER_ALIAS
|
||||
from InvenTree.filters import (
|
||||
SEARCH_ORDER_FILTER,
|
||||
SEARCH_ORDER_FILTER_ALIAS,
|
||||
InvenTreeDateFilter,
|
||||
)
|
||||
from InvenTree.helpers import str2bool
|
||||
from InvenTree.helpers_model import construct_absolute_url, get_base_url
|
||||
from InvenTree.mixins import CreateAPI, ListAPI, ListCreateAPI, RetrieveUpdateDestroyAPI
|
||||
@ -140,6 +144,22 @@ class OrderFilter(rest_filters.FilterSet):
|
||||
queryset=Owner.objects.all(), field_name='responsible', label=_('Responsible')
|
||||
)
|
||||
|
||||
created_before = InvenTreeDateFilter(
|
||||
label=_('Created Before'), field_name='creation_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
created_after = InvenTreeDateFilter(
|
||||
label=_('Created After'), field_name='creation_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
target_date_before = InvenTreeDateFilter(
|
||||
label=_('Target Date Before'), field_name='target_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
target_date_after = InvenTreeDateFilter(
|
||||
label=_('Target Date After'), field_name='target_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
|
||||
class LineItemFilter(rest_filters.FilterSet):
|
||||
"""Base class for custom API filters for order line item list(s)."""
|
||||
@ -171,6 +191,41 @@ class PurchaseOrderFilter(OrderFilter):
|
||||
model = models.PurchaseOrder
|
||||
fields = ['supplier']
|
||||
|
||||
part = rest_filters.ModelChoiceFilter(
|
||||
queryset=Part.objects.all(),
|
||||
field_name='part',
|
||||
label=_('Part'),
|
||||
method='filter_part',
|
||||
)
|
||||
|
||||
def filter_part(self, queryset, name, part: Part):
|
||||
"""Filter by provided Part instance."""
|
||||
orders = part.purchase_orders()
|
||||
|
||||
return queryset.filter(pk__in=[o.pk for o in orders])
|
||||
|
||||
supplier_part = rest_filters.ModelChoiceFilter(
|
||||
queryset=company.models.SupplierPart.objects.all(),
|
||||
label=_('Supplier Part'),
|
||||
method='filter_supplier_part',
|
||||
)
|
||||
|
||||
def filter_supplier_part(
|
||||
self, queryset, name, supplier_part: company.models.SupplierPart
|
||||
):
|
||||
"""Filter by provided SupplierPart instance."""
|
||||
orders = supplier_part.purchase_orders()
|
||||
|
||||
return queryset.filter(pk__in=[o.pk for o in orders])
|
||||
|
||||
completed_before = InvenTreeDateFilter(
|
||||
label=_('Completed Before'), field_name='complete_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
completed_after = InvenTreeDateFilter(
|
||||
label=_('Completed After'), field_name='complete_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
|
||||
class PurchaseOrderMixin:
|
||||
"""Mixin class for PurchaseOrder endpoints."""
|
||||
@ -221,32 +276,6 @@ class PurchaseOrderList(PurchaseOrderMixin, DataExportViewMixin, ListCreateAPI):
|
||||
|
||||
params = self.request.query_params
|
||||
|
||||
# Attempt to filter by part
|
||||
part = params.get('part', None)
|
||||
|
||||
if part is not None:
|
||||
try:
|
||||
part = Part.objects.get(pk=part)
|
||||
queryset = queryset.filter(
|
||||
id__in=[p.id for p in part.purchase_orders()]
|
||||
)
|
||||
except (Part.DoesNotExist, ValueError):
|
||||
pass
|
||||
|
||||
# Attempt to filter by supplier part
|
||||
supplier_part = params.get('supplier_part', None)
|
||||
|
||||
if supplier_part is not None:
|
||||
try:
|
||||
supplier_part = company.models.SupplierPart.objects.get(
|
||||
pk=supplier_part
|
||||
)
|
||||
queryset = queryset.filter(
|
||||
id__in=[p.id for p in supplier_part.purchase_orders()]
|
||||
)
|
||||
except (ValueError, company.models.SupplierPart.DoesNotExist):
|
||||
pass
|
||||
|
||||
# Filter by 'date range'
|
||||
min_date = params.get('min_date', None)
|
||||
max_date = params.get('max_date', None)
|
||||
@ -276,6 +305,7 @@ class PurchaseOrderList(PurchaseOrderMixin, DataExportViewMixin, ListCreateAPI):
|
||||
'reference',
|
||||
'supplier__name',
|
||||
'target_date',
|
||||
'complete_date',
|
||||
'line_items',
|
||||
'status',
|
||||
'responsible',
|
||||
@ -648,6 +678,14 @@ class SalesOrderFilter(OrderFilter):
|
||||
# Now we have a list of matching IDs, filter the queryset
|
||||
return queryset.filter(pk__in=sales_orders)
|
||||
|
||||
completed_before = InvenTreeDateFilter(
|
||||
label=_('Completed Before'), field_name='shipment_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
completed_after = InvenTreeDateFilter(
|
||||
label=_('Completed After'), field_name='shipment_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
|
||||
class SalesOrderMixin:
|
||||
"""Mixin class for SalesOrder endpoints."""
|
||||
@ -1257,6 +1295,14 @@ class ReturnOrderFilter(OrderFilter):
|
||||
# Now we have a list of matching IDs, filter the queryset
|
||||
return queryset.filter(pk__in=return_orders)
|
||||
|
||||
completed_before = InvenTreeDateFilter(
|
||||
label=_('Completed Before'), field_name='complete_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
completed_after = InvenTreeDateFilter(
|
||||
label=_('Completed After'), field_name='complete_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
|
||||
class ReturnOrderMixin:
|
||||
"""Mixin class for ReturnOrder endpoints."""
|
||||
@ -1325,6 +1371,7 @@ class ReturnOrderList(ReturnOrderMixin, DataExportViewMixin, ListCreateAPI):
|
||||
'line_items',
|
||||
'status',
|
||||
'target_date',
|
||||
'complete_date',
|
||||
'project_code',
|
||||
]
|
||||
|
||||
|
@ -1159,10 +1159,10 @@ class PartFilter(rest_filters.FilterSet):
|
||||
|
||||
# Created date filters
|
||||
created_before = InvenTreeDateFilter(
|
||||
label='Updated before', field_name='creation_date', lookup_expr='lte'
|
||||
label='Updated before', field_name='creation_date', lookup_expr='lt'
|
||||
)
|
||||
created_after = InvenTreeDateFilter(
|
||||
label='Updated after', field_name='creation_date', lookup_expr='gte'
|
||||
label='Updated after', field_name='creation_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
|
||||
|
@ -807,19 +807,28 @@ class StockFilter(rest_filters.FilterSet):
|
||||
|
||||
# Update date filters
|
||||
updated_before = InvenTreeDateFilter(
|
||||
label='Updated before', field_name='updated', lookup_expr='lte'
|
||||
label=_('Updated before'), field_name='updated', lookup_expr='lt'
|
||||
)
|
||||
|
||||
updated_after = InvenTreeDateFilter(
|
||||
label='Updated after', field_name='updated', lookup_expr='gte'
|
||||
label=_('Updated after'), field_name='updated', lookup_expr='gt'
|
||||
)
|
||||
|
||||
stocktake_before = InvenTreeDateFilter(
|
||||
label=_('Stocktake Before'), field_name='stocktake_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
stocktake_after = InvenTreeDateFilter(
|
||||
label=_('Stocktake After'), field_name='stocktake_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
# Stock "expiry" filters
|
||||
expiry_date_lte = InvenTreeDateFilter(
|
||||
label=_('Expiry date before'), field_name='expiry_date', lookup_expr='lte'
|
||||
expiry_before = InvenTreeDateFilter(
|
||||
label=_('Expiry date before'), field_name='expiry_date', lookup_expr='lt'
|
||||
)
|
||||
|
||||
expiry_date_gte = InvenTreeDateFilter(
|
||||
label=_('Expiry date after'), field_name='expiry_date', lookup_expr='gte'
|
||||
expiry_after = InvenTreeDateFilter(
|
||||
label=_('Expiry date after'), field_name='expiry_date', lookup_expr='gt'
|
||||
)
|
||||
|
||||
stale = rest_filters.BooleanFilter(label=_('Stale'), method='filter_stale')
|
||||
|
@ -421,11 +421,11 @@ function getStockTableFilters() {
|
||||
title: '{% trans "Has purchase price" %}',
|
||||
description: '{% trans "Show stock items which have a purchase price set" %}',
|
||||
},
|
||||
expiry_date_lte: {
|
||||
expiry_before: {
|
||||
type: 'date',
|
||||
title: '{% trans "Expiry Date before" %}',
|
||||
},
|
||||
expiry_date_gte: {
|
||||
expiry_after: {
|
||||
type: 'date',
|
||||
title: '{% trans "Expiry Date after" %}',
|
||||
},
|
||||
|
@ -3,8 +3,6 @@ import { Group, Skeleton, Stack, Text } from '@mantine/core';
|
||||
import { IconInfoCircle, IconPackages, IconSitemap } from '@tabler/icons-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
|
||||
import { ActionButton } from '../../components/buttons/ActionButton';
|
||||
import AdminButton from '../../components/buttons/AdminButton';
|
||||
import { PrintingActions } from '../../components/buttons/PrintingActions';
|
||||
import {
|
||||
@ -278,12 +276,6 @@ export default function Stock() {
|
||||
() => [
|
||||
<AdminButton model={ModelType.stocklocation} id={location.pk} />,
|
||||
<LocateItemButton locationId={location.pk} />,
|
||||
<ActionButton
|
||||
icon={<InvenTreeIcon icon='stocktake' />}
|
||||
onClick={notYetImplemented}
|
||||
variant='outline'
|
||||
size='lg'
|
||||
/>,
|
||||
location.pk ? (
|
||||
<BarcodeActionDropdown
|
||||
model={ModelType.stocklocation}
|
||||
|
@ -242,6 +242,14 @@ export function CreationDateColumn(props: TableColumnProps): TableColumn {
|
||||
});
|
||||
}
|
||||
|
||||
export function CompletionDateColumn(props: TableColumnProps): TableColumn {
|
||||
return DateColumn({
|
||||
accessor: 'completion_date',
|
||||
title: t`Completion Date`,
|
||||
...props
|
||||
});
|
||||
}
|
||||
|
||||
export function ShipmentDateColumn(props: TableColumnProps): TableColumn {
|
||||
return DateColumn({
|
||||
accessor: 'shipment_date',
|
||||
|
@ -17,8 +17,9 @@ export type TableFilterChoice = {
|
||||
* boolean: A simple true/false filter
|
||||
* choice: A filter which allows selection from a list of (supplied)
|
||||
* date: A filter which allows selection from a date input
|
||||
* text: A filter which allows raw text input
|
||||
*/
|
||||
export type TableFilterType = 'boolean' | 'choice' | 'date';
|
||||
export type TableFilterType = 'boolean' | 'choice' | 'date' | 'text';
|
||||
|
||||
/**
|
||||
* Interface for the table filter type. Provides a number of options for selecting filter value:
|
||||
@ -137,6 +138,60 @@ export function MaxDateFilter(): TableFilter {
|
||||
};
|
||||
}
|
||||
|
||||
export function CreatedBeforeFilter(): TableFilter {
|
||||
return {
|
||||
name: 'created_before',
|
||||
label: t`Created Before`,
|
||||
description: t`Show items created before this date`,
|
||||
type: 'date'
|
||||
};
|
||||
}
|
||||
|
||||
export function CreatedAfterFilter(): TableFilter {
|
||||
return {
|
||||
name: 'created_after',
|
||||
label: t`Created After`,
|
||||
description: t`Show items created after this date`,
|
||||
type: 'date'
|
||||
};
|
||||
}
|
||||
|
||||
export function TargetDateBeforeFilter(): TableFilter {
|
||||
return {
|
||||
name: 'target_date_before',
|
||||
label: t`Target Date Before`,
|
||||
description: t`Show items with a target date before this date`,
|
||||
type: 'date'
|
||||
};
|
||||
}
|
||||
|
||||
export function TargetDateAfterFilter(): TableFilter {
|
||||
return {
|
||||
name: 'target_date_after',
|
||||
label: t`Target Date After`,
|
||||
description: t`Show items with a target date after this date`,
|
||||
type: 'date'
|
||||
};
|
||||
}
|
||||
|
||||
export function CompletedBeforeFilter(): TableFilter {
|
||||
return {
|
||||
name: 'completed_before',
|
||||
label: t`Completed Before`,
|
||||
description: t`Show items completed before this date`,
|
||||
type: 'date'
|
||||
};
|
||||
}
|
||||
|
||||
export function CompletedAfterFilter(): TableFilter {
|
||||
return {
|
||||
name: 'completed_after',
|
||||
label: t`Completed After`,
|
||||
description: t`Show items completed after this date`,
|
||||
type: 'date'
|
||||
};
|
||||
}
|
||||
|
||||
export function HasProjectCodeFilter(): TableFilter {
|
||||
return {
|
||||
name: 'has_project_code',
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { t } from '@lingui/macro';
|
||||
import {
|
||||
ActionIcon,
|
||||
Badge,
|
||||
Button,
|
||||
CloseButton,
|
||||
@ -10,12 +11,14 @@ import {
|
||||
Select,
|
||||
Stack,
|
||||
Text,
|
||||
TextInput,
|
||||
Tooltip
|
||||
} from '@mantine/core';
|
||||
import { DateInput, type DateValue } from '@mantine/dates';
|
||||
import dayjs from 'dayjs';
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { IconCheck } from '@tabler/icons-react';
|
||||
import { StylishText } from '../components/items/StylishText';
|
||||
import type { TableState } from '../hooks/UseTable';
|
||||
import {
|
||||
@ -60,6 +63,77 @@ function FilterItem({
|
||||
);
|
||||
}
|
||||
|
||||
function FilterElement({
|
||||
filterType,
|
||||
valueOptions,
|
||||
onValueChange
|
||||
}: {
|
||||
filterType: TableFilterType;
|
||||
valueOptions: TableFilterChoice[];
|
||||
onValueChange: (value: string | null) => void;
|
||||
}) {
|
||||
const setDateValue = useCallback(
|
||||
(value: DateValue) => {
|
||||
if (value) {
|
||||
const date = value.toString();
|
||||
onValueChange(dayjs(date).format('YYYY-MM-DD'));
|
||||
} else {
|
||||
onValueChange('');
|
||||
}
|
||||
},
|
||||
[onValueChange]
|
||||
);
|
||||
|
||||
const [textValue, setTextValue] = useState<string>('');
|
||||
|
||||
switch (filterType) {
|
||||
case 'text':
|
||||
return (
|
||||
<TextInput
|
||||
label={t`Value`}
|
||||
value={textValue}
|
||||
placeholder={t`Enter filter value`}
|
||||
rightSection={
|
||||
<ActionIcon
|
||||
aria-label='apply-text-filter'
|
||||
variant='transparent'
|
||||
onClick={() => onValueChange(textValue)}
|
||||
>
|
||||
<IconCheck />
|
||||
</ActionIcon>
|
||||
}
|
||||
onChange={(e) => setTextValue(e.currentTarget.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter') {
|
||||
onValueChange(textValue);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
);
|
||||
case 'date':
|
||||
return (
|
||||
<DateInput
|
||||
label={t`Value`}
|
||||
placeholder={t`Select date value`}
|
||||
onChange={setDateValue}
|
||||
/>
|
||||
);
|
||||
case 'choice':
|
||||
case 'boolean':
|
||||
default:
|
||||
return (
|
||||
<Select
|
||||
data={valueOptions}
|
||||
searchable={filterType != 'boolean'}
|
||||
label={t`Value`}
|
||||
placeholder={t`Select filter value`}
|
||||
onChange={(value: string | null) => onValueChange(value)}
|
||||
maxDropdownHeight={800}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function FilterAddGroup({
|
||||
tableState,
|
||||
availableFilters
|
||||
@ -79,6 +153,7 @@ function FilterAddGroup({
|
||||
return (
|
||||
availableFilters
|
||||
?.filter((flt) => !activeFilterNames.includes(flt.name))
|
||||
?.sort((a, b) => a.label.localeCompare(b.label))
|
||||
?.map((flt) => ({
|
||||
value: flt.name,
|
||||
label: flt.label,
|
||||
@ -133,22 +208,13 @@ function FilterAddGroup({
|
||||
};
|
||||
|
||||
tableState.setActiveFilters([...filters, newFilter]);
|
||||
|
||||
// Clear selected filter
|
||||
setSelectedFilter(null);
|
||||
},
|
||||
[selectedFilter]
|
||||
);
|
||||
|
||||
const setDateValue = useCallback(
|
||||
(value: DateValue) => {
|
||||
if (value) {
|
||||
const date = value.toString();
|
||||
setSelectedValue(dayjs(date).format('YYYY-MM-DD'));
|
||||
} else {
|
||||
setSelectedValue('');
|
||||
}
|
||||
},
|
||||
[setSelectedValue]
|
||||
);
|
||||
|
||||
return (
|
||||
<Stack gap='xs'>
|
||||
<Divider />
|
||||
@ -160,23 +226,13 @@ function FilterAddGroup({
|
||||
onChange={(value: string | null) => setSelectedFilter(value)}
|
||||
maxDropdownHeight={800}
|
||||
/>
|
||||
{selectedFilter &&
|
||||
(filterType === 'date' ? (
|
||||
<DateInput
|
||||
label={t`Value`}
|
||||
placeholder={t`Select date value`}
|
||||
onChange={setDateValue}
|
||||
{selectedFilter && (
|
||||
<FilterElement
|
||||
filterType={filterType}
|
||||
valueOptions={valueOptions}
|
||||
onValueChange={setSelectedValue}
|
||||
/>
|
||||
) : (
|
||||
<Select
|
||||
data={valueOptions}
|
||||
label={t`Value`}
|
||||
searchable={true}
|
||||
placeholder={t`Select filter value`}
|
||||
onChange={(value: string | null) => setSelectedValue(value)}
|
||||
maxDropdownHeight={800}
|
||||
/>
|
||||
))}
|
||||
)}
|
||||
</Stack>
|
||||
);
|
||||
}
|
||||
|
@ -25,12 +25,18 @@ import {
|
||||
} from '../ColumnRenderers';
|
||||
import {
|
||||
AssignedToMeFilter,
|
||||
CompletedAfterFilter,
|
||||
CompletedBeforeFilter,
|
||||
CreatedAfterFilter,
|
||||
CreatedBeforeFilter,
|
||||
HasProjectCodeFilter,
|
||||
MaxDateFilter,
|
||||
MinDateFilter,
|
||||
OverdueFilter,
|
||||
StatusFilterOptions,
|
||||
type TableFilter
|
||||
type TableFilter,
|
||||
TargetDateAfterFilter,
|
||||
TargetDateBeforeFilter
|
||||
} from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
|
||||
@ -130,6 +136,12 @@ export function BuildOrderTable({
|
||||
AssignedToMeFilter(),
|
||||
MinDateFilter(),
|
||||
MaxDateFilter(),
|
||||
CreatedBeforeFilter(),
|
||||
CreatedAfterFilter(),
|
||||
TargetDateBeforeFilter(),
|
||||
TargetDateAfterFilter(),
|
||||
CompletedBeforeFilter(),
|
||||
CompletedAfterFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
|
@ -14,6 +14,7 @@ import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import {
|
||||
CompletionDateColumn,
|
||||
CreationDateColumn,
|
||||
DescriptionColumn,
|
||||
LineItemsProgressColumn,
|
||||
@ -25,13 +26,19 @@ import {
|
||||
} from '../ColumnRenderers';
|
||||
import {
|
||||
AssignedToMeFilter,
|
||||
CompletedAfterFilter,
|
||||
CompletedBeforeFilter,
|
||||
CreatedAfterFilter,
|
||||
CreatedBeforeFilter,
|
||||
HasProjectCodeFilter,
|
||||
MaxDateFilter,
|
||||
MinDateFilter,
|
||||
OutstandingFilter,
|
||||
OverdueFilter,
|
||||
StatusFilterOptions,
|
||||
type TableFilter
|
||||
type TableFilter,
|
||||
TargetDateAfterFilter,
|
||||
TargetDateBeforeFilter
|
||||
} from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
|
||||
@ -64,6 +71,12 @@ export function PurchaseOrderTable({
|
||||
AssignedToMeFilter(),
|
||||
MinDateFilter(),
|
||||
MaxDateFilter(),
|
||||
CreatedBeforeFilter(),
|
||||
CreatedAfterFilter(),
|
||||
TargetDateBeforeFilter(),
|
||||
TargetDateAfterFilter(),
|
||||
CompletedBeforeFilter(),
|
||||
CompletedAfterFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
@ -108,6 +121,9 @@ export function PurchaseOrderTable({
|
||||
ProjectCodeColumn({}),
|
||||
CreationDateColumn({}),
|
||||
TargetDateColumn({}),
|
||||
CompletionDateColumn({
|
||||
accessor: 'complete_date'
|
||||
}),
|
||||
{
|
||||
accessor: 'total_price',
|
||||
title: t`Total Price`,
|
||||
|
@ -14,8 +14,8 @@ import { useTable } from '../../hooks/UseTable';
|
||||
import { apiUrl } from '../../states/ApiState';
|
||||
import { useUserState } from '../../states/UserState';
|
||||
import {
|
||||
CompletionDateColumn,
|
||||
CreationDateColumn,
|
||||
DateColumn,
|
||||
DescriptionColumn,
|
||||
LineItemsProgressColumn,
|
||||
ProjectCodeColumn,
|
||||
@ -26,13 +26,19 @@ import {
|
||||
} from '../ColumnRenderers';
|
||||
import {
|
||||
AssignedToMeFilter,
|
||||
CompletedAfterFilter,
|
||||
CompletedBeforeFilter,
|
||||
CreatedAfterFilter,
|
||||
CreatedBeforeFilter,
|
||||
HasProjectCodeFilter,
|
||||
MaxDateFilter,
|
||||
MinDateFilter,
|
||||
OutstandingFilter,
|
||||
OverdueFilter,
|
||||
StatusFilterOptions,
|
||||
type TableFilter
|
||||
type TableFilter,
|
||||
TargetDateAfterFilter,
|
||||
TargetDateBeforeFilter
|
||||
} from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
|
||||
@ -62,6 +68,12 @@ export function ReturnOrderTable({
|
||||
AssignedToMeFilter(),
|
||||
MinDateFilter(),
|
||||
MaxDateFilter(),
|
||||
CreatedBeforeFilter(),
|
||||
CreatedAfterFilter(),
|
||||
TargetDateBeforeFilter(),
|
||||
TargetDateAfterFilter(),
|
||||
CompletedBeforeFilter(),
|
||||
CompletedAfterFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
@ -117,9 +129,8 @@ export function ReturnOrderTable({
|
||||
ProjectCodeColumn({}),
|
||||
CreationDateColumn({}),
|
||||
TargetDateColumn({}),
|
||||
DateColumn({
|
||||
accessor: 'complete_date',
|
||||
title: t`Completion Date`
|
||||
CompletionDateColumn({
|
||||
accessor: 'complete_date'
|
||||
}),
|
||||
ResponsibleColumn({}),
|
||||
{
|
||||
|
@ -27,13 +27,19 @@ import {
|
||||
} from '../ColumnRenderers';
|
||||
import {
|
||||
AssignedToMeFilter,
|
||||
CompletedAfterFilter,
|
||||
CompletedBeforeFilter,
|
||||
CreatedAfterFilter,
|
||||
CreatedBeforeFilter,
|
||||
HasProjectCodeFilter,
|
||||
MaxDateFilter,
|
||||
MinDateFilter,
|
||||
OutstandingFilter,
|
||||
OverdueFilter,
|
||||
StatusFilterOptions,
|
||||
type TableFilter
|
||||
type TableFilter,
|
||||
TargetDateAfterFilter,
|
||||
TargetDateBeforeFilter
|
||||
} from '../Filter';
|
||||
import { InvenTreeTable } from '../InvenTreeTable';
|
||||
|
||||
@ -63,6 +69,12 @@ export function SalesOrderTable({
|
||||
AssignedToMeFilter(),
|
||||
MinDateFilter(),
|
||||
MaxDateFilter(),
|
||||
CreatedBeforeFilter(),
|
||||
CreatedAfterFilter(),
|
||||
TargetDateBeforeFilter(),
|
||||
TargetDateAfterFilter(),
|
||||
CompletedBeforeFilter(),
|
||||
CompletedAfterFilter(),
|
||||
{
|
||||
name: 'project_code',
|
||||
label: t`Project Code`,
|
||||
|
@ -286,7 +286,11 @@ function stockItemTableColumns(): TableColumn[] {
|
||||
/**
|
||||
* Construct a list of available filters for the stock item table
|
||||
*/
|
||||
function stockItemTableFilters(): TableFilter[] {
|
||||
function stockItemTableFilters({
|
||||
enableExpiry
|
||||
}: {
|
||||
enableExpiry: boolean;
|
||||
}): TableFilter[] {
|
||||
return [
|
||||
{
|
||||
name: 'active',
|
||||
@ -354,15 +358,35 @@ function stockItemTableFilters(): TableFilter[] {
|
||||
label: t`Is Serialized`,
|
||||
description: t`Show items which have a serial number`
|
||||
},
|
||||
// TODO: serial
|
||||
// TODO: serial_gte
|
||||
// TODO: serial_lte
|
||||
{
|
||||
name: 'batch',
|
||||
label: t`Batch Code`,
|
||||
description: t`Filter items by batch code`,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'serial',
|
||||
label: t`Serial Number`,
|
||||
description: t`Filter items by serial number`,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'serial_lte',
|
||||
label: t`Serial Number LTE`,
|
||||
description: t`Show items with serial numbers less than or equal to a given value`,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'serial_gte',
|
||||
label: t`Serial Number GTE`,
|
||||
description: t`Show items with serial numbers greater than or equal to a given value`,
|
||||
type: 'text'
|
||||
},
|
||||
{
|
||||
name: 'has_batch',
|
||||
label: t`Has Batch Code`,
|
||||
description: t`Show items which have a batch code`
|
||||
},
|
||||
// TODO: batch
|
||||
{
|
||||
name: 'tracked',
|
||||
label: t`Tracked`,
|
||||
@ -373,10 +397,56 @@ function stockItemTableFilters(): TableFilter[] {
|
||||
label: t`Has Purchase Price`,
|
||||
description: t`Show items which have a purchase price`
|
||||
},
|
||||
// TODO: Expired
|
||||
// TODO: stale
|
||||
// TODO: expiry_date_lte
|
||||
// TODO: expiry_date_gte
|
||||
{
|
||||
name: 'expired',
|
||||
label: t`Expired`,
|
||||
description: t`Show items which have expired`,
|
||||
active: enableExpiry
|
||||
},
|
||||
{
|
||||
name: 'stale',
|
||||
label: t`Stale`,
|
||||
description: t`Show items which are stale`,
|
||||
active: enableExpiry
|
||||
},
|
||||
{
|
||||
name: 'expiry_before',
|
||||
label: t`Expired Before`,
|
||||
description: t`Show items which expired before this date`,
|
||||
type: 'date',
|
||||
active: enableExpiry
|
||||
},
|
||||
{
|
||||
name: 'expiry_after',
|
||||
label: t`Expired After`,
|
||||
description: t`Show items which expired after this date`,
|
||||
type: 'date',
|
||||
active: enableExpiry
|
||||
},
|
||||
{
|
||||
name: 'updated_before',
|
||||
label: t`Updated Before`,
|
||||
description: t`Show items updated before this date`,
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'updated_after',
|
||||
label: t`Updated After`,
|
||||
description: t`Show items updated after this date`,
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'stocktake_before',
|
||||
label: t`Stocktake Before`,
|
||||
description: t`Show items counted before this date`,
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'stocktake_after',
|
||||
label: t`Stocktake After`,
|
||||
description: t`Show items counted after this date`,
|
||||
type: 'date'
|
||||
},
|
||||
{
|
||||
name: 'external',
|
||||
label: t`External Location`,
|
||||
@ -397,12 +467,25 @@ export function StockItemTable({
|
||||
allowAdd?: boolean;
|
||||
tableName: string;
|
||||
}>) {
|
||||
const tableColumns = useMemo(() => stockItemTableColumns(), []);
|
||||
const tableFilters = useMemo(() => stockItemTableFilters(), []);
|
||||
|
||||
const table = useTable(tableName);
|
||||
const user = useUserState();
|
||||
|
||||
const settings = useGlobalSettingsState();
|
||||
|
||||
const stockExpiryEnabled = useMemo(
|
||||
() => settings.isSet('STOCK_ENABLE_EXPIRY'),
|
||||
[settings]
|
||||
);
|
||||
|
||||
const tableColumns = useMemo(() => stockItemTableColumns(), []);
|
||||
const tableFilters = useMemo(
|
||||
() =>
|
||||
stockItemTableFilters({
|
||||
enableExpiry: stockExpiryEnabled
|
||||
}),
|
||||
[stockExpiryEnabled]
|
||||
);
|
||||
|
||||
const tableActionParams: StockOperationProps = useMemo(() => {
|
||||
return {
|
||||
items: table.selectedRecords,
|
||||
|
46
src/frontend/tests/helpers.ts
Normal file
46
src/frontend/tests/helpers.ts
Normal file
@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Open the filter drawer for the currently visible table
|
||||
* @param page - The page object
|
||||
*/
|
||||
export const openFilterDrawer = async (page) => {
|
||||
await page.getByLabel('table-select-filters').click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Close the filter drawer for the currently visible table
|
||||
* @param page - The page object
|
||||
*/
|
||||
export const closeFilterDrawer = async (page) => {
|
||||
await page.getByLabel('filter-drawer-close').click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Click the specified button (if it is visible)
|
||||
* @param page - The page object
|
||||
* @param name - The name of the button to click
|
||||
*/
|
||||
export const clickButtonIfVisible = async (page, name, timeout = 500) => {
|
||||
await page.waitForTimeout(timeout);
|
||||
|
||||
if (await page.getByRole('button', { name }).isVisible()) {
|
||||
await page.getByRole('button', { name }).click();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Clear all filters from the currently visible table
|
||||
* @param page - The page object
|
||||
*/
|
||||
export const clearTableFilters = async (page) => {
|
||||
await openFilterDrawer(page);
|
||||
await clickButtonIfVisible(page, 'Clear Filters');
|
||||
await page.getByLabel('filter-drawer-close').click();
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the parent 'row' element for a given 'cell' element
|
||||
* @param cell - The cell element
|
||||
*/
|
||||
export const getRowFromCell = async (cell) => {
|
||||
return cell.locator('xpath=ancestor::tr').first();
|
||||
};
|
@ -1,8 +1,13 @@
|
||||
import { test } from '../baseFixtures.ts';
|
||||
import { baseUrl } from '../defaults.ts';
|
||||
import {
|
||||
clickButtonIfVisible,
|
||||
getRowFromCell,
|
||||
openFilterDrawer
|
||||
} from '../helpers.ts';
|
||||
import { doQuickLogin } from '../login.ts';
|
||||
|
||||
test('Pages - Build Order', async ({ page }) => {
|
||||
test('Build Order - Basic Tests', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.goto(`${baseUrl}/part/`);
|
||||
@ -82,7 +87,7 @@ test('Pages - Build Order', async ({ page }) => {
|
||||
.waitFor();
|
||||
});
|
||||
|
||||
test('Pages - Build Order - Build Outputs', async ({ page }) => {
|
||||
test('Build Order - Build Outputs', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.goto(`${baseUrl}/part/`);
|
||||
@ -140,7 +145,7 @@ test('Pages - Build Order - Build Outputs', async ({ page }) => {
|
||||
|
||||
// Cancel one of the newly created outputs
|
||||
const cell = await page.getByRole('cell', { name: `# ${sn}` });
|
||||
const row = await cell.locator('xpath=ancestor::tr').first();
|
||||
const row = await getRowFromCell(cell);
|
||||
await row.getByLabel(/row-action-menu-/i).click();
|
||||
await page.getByRole('menuitem', { name: 'Cancel' }).click();
|
||||
await page.getByRole('button', { name: 'Submit' }).click();
|
||||
@ -148,7 +153,7 @@ test('Pages - Build Order - Build Outputs', async ({ page }) => {
|
||||
|
||||
// Complete the other output
|
||||
const cell2 = await page.getByRole('cell', { name: `# ${sn + 1}` });
|
||||
const row2 = await cell2.locator('xpath=ancestor::tr').first();
|
||||
const row2 = await getRowFromCell(cell2);
|
||||
await row2.getByLabel(/row-action-menu-/i).click();
|
||||
await page.getByRole('menuitem', { name: 'Complete' }).click();
|
||||
await page.getByLabel('related-field-location').click();
|
||||
@ -158,7 +163,7 @@ test('Pages - Build Order - Build Outputs', async ({ page }) => {
|
||||
await page.getByText('Build outputs have been completed').waitFor();
|
||||
});
|
||||
|
||||
test('Pages - Build Order - Allocation', async ({ page }) => {
|
||||
test('Build Order - Allocation', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.goto(`${baseUrl}/manufacturing/build-order/1/line-items`);
|
||||
@ -170,7 +175,7 @@ test('Pages - Build Order - Allocation', async ({ page }) => {
|
||||
|
||||
// The capacitor stock should be fully allocated
|
||||
const cell = await page.getByRole('cell', { name: /C_1uF_0805/ });
|
||||
const row = await cell.locator('xpath=ancestor::tr').first();
|
||||
const row = await getRowFromCell(cell);
|
||||
|
||||
await row.getByText(/150 \/ 150/).waitFor();
|
||||
|
||||
@ -237,7 +242,7 @@ test('Pages - Build Order - Allocation', async ({ page }) => {
|
||||
const item = data[idx];
|
||||
|
||||
const cell = await page.getByRole('cell', { name: item.name });
|
||||
const row = await cell.locator('xpath=ancestor::tr').first();
|
||||
const row = await getRowFromCell(cell);
|
||||
const progress = `${item.allocated} / ${item.required}`;
|
||||
|
||||
await row.getByRole('cell', { name: item.ipn }).first().waitFor();
|
||||
@ -257,3 +262,14 @@ test('Pages - Build Order - Allocation', async ({ page }) => {
|
||||
.getByRole('menuitem', { name: 'Deallocate Stock', exact: true })
|
||||
.waitFor();
|
||||
});
|
||||
|
||||
test('Build Order - Filters', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.goto(`${baseUrl}/manufacturing/index/buildorders`);
|
||||
|
||||
await openFilterDrawer(page);
|
||||
await clickButtonIfVisible(page, 'Clear Filters');
|
||||
|
||||
await page.waitForTimeout(2500);
|
||||
});
|
||||
|
@ -2,7 +2,7 @@ import { test } from '../baseFixtures.js';
|
||||
import { doQuickLogin } from '../login.js';
|
||||
import { setPluginState } from '../settings.js';
|
||||
|
||||
test('Pages - Dashboard - Basic', async ({ page }) => {
|
||||
test('Dashboard - Basic', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.getByText('Use the menu to add widgets').waitFor();
|
||||
@ -35,7 +35,7 @@ test('Pages - Dashboard - Basic', async ({ page }) => {
|
||||
await page.getByLabel('dashboard-accept-layout').click();
|
||||
});
|
||||
|
||||
test('Pages - Dashboard - Plugins', async ({ page, request }) => {
|
||||
test('Dashboard - Plugins', async ({ page, request }) => {
|
||||
// Ensure that the "SampleUI" plugin is enabled
|
||||
await setPluginState({
|
||||
request,
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { test } from '../baseFixtures';
|
||||
import { baseUrl } from '../defaults';
|
||||
import { getRowFromCell } from '../helpers';
|
||||
import { doQuickLogin } from '../login';
|
||||
|
||||
/**
|
||||
@ -129,9 +130,7 @@ test('Parts - Allocations', async ({ page }) => {
|
||||
|
||||
// Check "progress" bar of BO0001
|
||||
const build_order_cell = await page.getByRole('cell', { name: 'BO0001' });
|
||||
const build_order_row = await build_order_cell
|
||||
.locator('xpath=ancestor::tr')
|
||||
.first();
|
||||
const build_order_row = await getRowFromCell(build_order_cell);
|
||||
await build_order_row.getByText('11 / 75').waitFor();
|
||||
|
||||
// Expand allocations against BO0001
|
||||
@ -147,9 +146,7 @@ test('Parts - Allocations', async ({ page }) => {
|
||||
|
||||
// Check "progress" bar of SO0025
|
||||
const sales_order_cell = await page.getByRole('cell', { name: 'SO0025' });
|
||||
const sales_order_row = await sales_order_cell
|
||||
.locator('xpath=ancestor::tr')
|
||||
.first();
|
||||
const sales_order_row = await getRowFromCell(sales_order_cell);
|
||||
await sales_order_row.getByText('3 / 10').waitFor();
|
||||
|
||||
// Expand allocations against SO0025
|
||||
|
@ -1,4 +1,5 @@
|
||||
import { test } from '../baseFixtures.ts';
|
||||
import { clickButtonIfVisible, openFilterDrawer } from '../helpers.ts';
|
||||
import { doQuickLogin } from '../login.ts';
|
||||
|
||||
test('Purchase Orders - General', async ({ page }) => {
|
||||
@ -51,6 +52,30 @@ test('Purchase Orders - General', async ({ page }) => {
|
||||
await page.getByRole('tab', { name: 'Details' }).waitFor();
|
||||
});
|
||||
|
||||
test('Purchase Orders - Filters', async ({ page }) => {
|
||||
await doQuickLogin(page, 'reader', 'readonly');
|
||||
|
||||
await page.getByRole('tab', { name: 'Purchasing' }).click();
|
||||
await page.getByRole('tab', { name: 'Purchase Orders' }).click();
|
||||
|
||||
// Open filters drawer
|
||||
await openFilterDrawer(page);
|
||||
await clickButtonIfVisible(page, 'Clear Filters');
|
||||
|
||||
await page.getByRole('button', { name: 'Add Filter' }).click();
|
||||
|
||||
// Check for expected filter options
|
||||
await page.getByPlaceholder('Select filter').fill('before');
|
||||
await page.getByRole('option', { name: 'Created Before' }).waitFor();
|
||||
await page.getByRole('option', { name: 'Completed Before' }).waitFor();
|
||||
await page.getByRole('option', { name: 'Target Date Before' }).waitFor();
|
||||
|
||||
await page.getByPlaceholder('Select filter').fill('after');
|
||||
await page.getByRole('option', { name: 'Created After' }).waitFor();
|
||||
await page.getByRole('option', { name: 'Completed After' }).waitFor();
|
||||
await page.getByRole('option', { name: 'Target Date After' }).waitFor();
|
||||
});
|
||||
|
||||
/**
|
||||
* Tests for receiving items against a purchase order
|
||||
*/
|
||||
|
@ -1,8 +1,9 @@
|
||||
import { test } from '../baseFixtures.js';
|
||||
import { baseUrl } from '../defaults.js';
|
||||
import { clickButtonIfVisible, openFilterDrawer } from '../helpers.js';
|
||||
import { doQuickLogin } from '../login.js';
|
||||
|
||||
test('Stock', async ({ page }) => {
|
||||
test('Stock - Basic Tests', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
await page.goto(`${baseUrl}/stock/location/index/`);
|
||||
@ -49,6 +50,45 @@ test('Stock - Location Tree', async ({ page }) => {
|
||||
await page.getByRole('cell', { name: 'Factory' }).first().waitFor();
|
||||
});
|
||||
|
||||
test('Stock - Filters', async ({ page }) => {
|
||||
await doQuickLogin(page, 'steven', 'wizardstaff');
|
||||
|
||||
await page.goto(`${baseUrl}/stock/location/index/`);
|
||||
await page.getByRole('tab', { name: 'Stock Items' }).click();
|
||||
|
||||
await openFilterDrawer(page);
|
||||
await clickButtonIfVisible(page, 'Clear Filters');
|
||||
|
||||
// Filter by updated date
|
||||
await page.getByRole('button', { name: 'Add Filter' }).click();
|
||||
await page.getByPlaceholder('Select filter').fill('updated');
|
||||
await page.getByText('Updated After').click();
|
||||
await page.getByPlaceholder('Select date value').fill('2010-01-01');
|
||||
await page.getByText('Show items updated after this date').waitFor();
|
||||
|
||||
// Filter by batch code
|
||||
await page.getByRole('button', { name: 'Add Filter' }).click();
|
||||
await page.getByPlaceholder('Select filter').fill('batch');
|
||||
await page
|
||||
.getByRole('option', { name: 'Batch Code', exact: true })
|
||||
.locator('span')
|
||||
.click();
|
||||
await page.getByPlaceholder('Enter filter value').fill('TABLE-B02');
|
||||
await page.getByLabel('apply-text-filter').click();
|
||||
|
||||
// Close dialog
|
||||
await page.keyboard.press('Escape');
|
||||
|
||||
// Ensure correct result is displayed
|
||||
await page
|
||||
.getByRole('cell', { name: 'A round table - with blue paint' })
|
||||
.waitFor();
|
||||
|
||||
// Clear filters (ready for next set of tests)
|
||||
await openFilterDrawer(page);
|
||||
await clickButtonIfVisible(page, 'Clear Filters');
|
||||
});
|
||||
|
||||
test('Stock - Serial Numbers', async ({ page }) => {
|
||||
await doQuickLogin(page);
|
||||
|
||||
|
@ -1,23 +1,23 @@
|
||||
import { test } from './baseFixtures.js';
|
||||
import { baseUrl } from './defaults.js';
|
||||
import {
|
||||
clearTableFilters,
|
||||
closeFilterDrawer,
|
||||
openFilterDrawer
|
||||
} from './helpers.js';
|
||||
import { doQuickLogin } from './login.js';
|
||||
|
||||
// Helper function to set the value of a specific table filter
|
||||
const setFilter = async (page, name: string, value: string) => {
|
||||
await page.getByLabel('table-select-filters').click();
|
||||
await openFilterDrawer(page);
|
||||
|
||||
await page.getByRole('button', { name: 'Add Filter' }).click();
|
||||
await page.getByPlaceholder('Select filter').click();
|
||||
await page.getByRole('option', { name: name, exact: true }).click();
|
||||
await page.getByPlaceholder('Select filter value').click();
|
||||
await page.getByRole('option', { name: value, exact: true }).click();
|
||||
await page.getByLabel('filter-drawer-close').click();
|
||||
};
|
||||
|
||||
// Helper function to clear table filters
|
||||
const clearFilters = async (page) => {
|
||||
await page.getByLabel('table-select-filters').click();
|
||||
await page.getByRole('button', { name: 'Clear Filters' }).click();
|
||||
await page.getByLabel('filter-drawer-close').click();
|
||||
await closeFilterDrawer(page);
|
||||
};
|
||||
|
||||
test('Tables - Filters', async ({ page }) => {
|
||||
@ -30,14 +30,14 @@ test('Tables - Filters', async ({ page }) => {
|
||||
await setFilter(page, 'Responsible', 'allaccess');
|
||||
await setFilter(page, 'Project Code', 'PRJ-NIM');
|
||||
|
||||
await clearFilters(page);
|
||||
await clearTableFilters(page);
|
||||
|
||||
// Head to the "part list" page
|
||||
await page.goto(`${baseUrl}/part/category/index/parts/`);
|
||||
|
||||
await setFilter(page, 'Assembly', 'Yes');
|
||||
|
||||
await clearFilters(page);
|
||||
await clearTableFilters(page);
|
||||
|
||||
// Head to the "purchase order list" page
|
||||
await page.goto(`${baseUrl}/purchasing/index/purchaseorders/`);
|
||||
@ -47,7 +47,7 @@ test('Tables - Filters', async ({ page }) => {
|
||||
await setFilter(page, 'Assigned to me', 'No');
|
||||
await setFilter(page, 'Project Code', 'PRO-ZEN');
|
||||
|
||||
await clearFilters(page);
|
||||
await clearTableFilters(page);
|
||||
});
|
||||
|
||||
test('Tables - Columns', async ({ page }) => {
|
||||
|
Loading…
x
Reference in New Issue
Block a user