Merged PR 1904: feat(utils-ean-validation, remission-list): add EAN validation library and im...

feat(utils-ean-validation, remission-list): add EAN validation library and implement exact search

Create new EAN validation utility library with validator function and isEan helper.
Implement exact search functionality for remission lists that bypasses filters
when scanning EAN codes or performing exact searches.

Changes:
- Add new utils/ean-validation library with EAN regex validation
- Export eanValidator for Angular reactive forms integration
- Export isEan utility function for EAN validation checks
- Configure library with Vitest for testing
- Update remission list resource to support exact search mode
- Clear filters and orderBy when performing EAN-based searches
- Add data attributes to product info component for E2E testing

Ref: #5128
This commit is contained in:
Nino Righi
2025-08-01 13:22:41 +00:00
committed by Patrick Brix
parent d7d535c10d
commit 14a5a67a1e
19 changed files with 34964 additions and 34729 deletions

View File

@@ -2,15 +2,15 @@ import { inject, resource } from '@angular/core';
import { ListResponseArgs, ResponseArgsError } from '@isa/common/data-access';
import {
QueryTokenInput,
RemissionItem,
RemissionListType,
RemissionSearchService,
RemissionStockService,
RemissionSupplierService,
ReturnItem,
ReturnSuggestion,
} from '@isa/remission/data-access';
import { SearchTrigger } from '@isa/shared/filter';
import { parseISO, compareDesc } from 'date-fns';
import { isEan } from '@isa/utils/ean-validation';
/**
* Creates an Angular resource for fetching remission lists.
@@ -36,7 +36,7 @@ import { parseISO, compareDesc } from 'date-fns';
* },
* searchTrigger: 'input'
* }));
*
*
* @remarks
* The searchTrigger parameter influences query behavior:
* - 'scan': Clears existing filters to show scan-specific results
@@ -70,19 +70,24 @@ export const createRemissionListResource = (
throw new Error('No Supplier available');
}
let res:
| ListResponseArgs<ReturnItem>
| ListResponseArgs<ReturnSuggestion>
| undefined;
let res: ListResponseArgs<RemissionItem> | undefined = undefined;
const queryToken = { ...params.queryToken };
if (params.searchTrigger === 'scan') {
// #5128 #5234 Bei Exact Search soll er über Alle Listen nur mit dem Input ohne aktive Filter / orderBy suchen
const isExactSearch =
params.searchTrigger === 'scan' || isEan(queryToken?.input?.['qs']);
if (isExactSearch) {
queryToken.filter = {};
queryToken.orderBy = [];
}
if (params.remissionListType === RemissionListType.Pflicht) {
res = await remissionSearchService.fetchList(
if (
isExactSearch ||
params.remissionListType === RemissionListType.Pflicht
) {
const fetchListResponse = await remissionSearchService.fetchList(
{
assignedStockId: assignedStock.id,
supplierId: firstSupplier.id,
@@ -90,17 +95,34 @@ export const createRemissionListResource = (
},
abortSignal,
);
res = fetchListResponse;
}
if (params.remissionListType === RemissionListType.Abteilung) {
res = await remissionSearchService.fetchDepartmentList(
{
assignedStockId: assignedStock.id,
supplierId: firstSupplier.id,
...params.queryToken,
},
abortSignal,
);
if (
isExactSearch ||
params.remissionListType === RemissionListType.Abteilung
) {
const fetchDepartmentListResponse =
await remissionSearchService.fetchDepartmentList(
{
assignedStockId: assignedStock.id,
supplierId: firstSupplier.id,
...queryToken,
},
abortSignal,
);
if (res) {
// Merge results if both lists are fetched
res.result = [
...(res.result || []),
...(fetchDepartmentListResponse.result || []),
];
res.hits += fetchDepartmentListResponse.hits;
res.skip += fetchDepartmentListResponse.skip;
res.take += fetchDepartmentListResponse.take;
} else {
res = fetchDepartmentListResponse;
}
}
if (res?.error) {