feat: remove deprecated refreshCategories endpoint and refactor matching logic for improved clarity and performance
Test Suite / test (24.15.0) (push) Has been cancelled
Test Suite / test (24.15.0) (push) Has been cancelled
This commit is contained in:
@@ -106,8 +106,12 @@ describe('ReceiptImportService test matrix', () => {
|
||||
});
|
||||
|
||||
describe('alias fallback och prioritet', () => {
|
||||
function makeContext(aliases: any[], products: any[], unitMappings: any[] = [], userId?: number) {
|
||||
return { userId, aliases, products, unitMappings, categories, aiEnabled: false };
|
||||
}
|
||||
|
||||
it('prioriterar user-alias före global alias för samma receiptName', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([
|
||||
const aliases = [
|
||||
{
|
||||
receiptName: 'mjolk 1l',
|
||||
productId: 501,
|
||||
@@ -130,32 +134,17 @@ describe('ReceiptImportService test matrix', () => {
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
const context = makeContext(aliases, [], [], 77);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'MJOLK 1L' }, context);
|
||||
|
||||
const result = await (service as any).matchProducts(
|
||||
[{ rawName: 'MJOLK 1L' }],
|
||||
77,
|
||||
);
|
||||
|
||||
expect(prismaMock.receiptAlias.findMany).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
where: {
|
||||
OR: [
|
||||
{ ownerId: 77, isGlobal: false },
|
||||
{ isGlobal: true },
|
||||
],
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
expect(result[0].matchedProductId).toBe(501);
|
||||
expect(result[0].matchedProductName).toBe('Mjolk user');
|
||||
expect(result.matchedProductId).toBe(501);
|
||||
expect(result.matchedProductName).toBe('Mjolk user');
|
||||
});
|
||||
|
||||
it('använder global alias när user-alias saknas', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([
|
||||
const aliases = [
|
||||
{
|
||||
receiptName: 'snickers',
|
||||
productId: 222,
|
||||
@@ -167,24 +156,17 @@ describe('ReceiptImportService test matrix', () => {
|
||||
categoryRef: { id: 53, name: 'Choklad' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
const context = makeContext(aliases, [], [], 88);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'SNICKERS' }, context);
|
||||
|
||||
const result = await (service as any).matchProducts(
|
||||
[{ rawName: 'SNICKERS' }],
|
||||
88,
|
||||
);
|
||||
|
||||
expect(result[0].matchedProductId).toBe(222);
|
||||
expect(result[0].matchedProductName).toBe('Snickers');
|
||||
expect(result.matchedProductId).toBe(222);
|
||||
expect(result.matchedProductName).toBe('Snickers');
|
||||
});
|
||||
|
||||
it('flöde: manuell korrigering lär alias och nästa import matchar direkt', async () => {
|
||||
const aliases: any[] = [];
|
||||
prismaMock.receiptAlias.findMany.mockImplementation(async () => aliases);
|
||||
|
||||
prismaMock.product.findMany.mockResolvedValue([
|
||||
const products = [
|
||||
{
|
||||
id: 700,
|
||||
name: 'Arla Mjolk 1l',
|
||||
@@ -192,41 +174,39 @@ describe('ReceiptImportService test matrix', () => {
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
const first = await (service as any).matchProducts(
|
||||
[{ rawName: 'ARLA MJOLK 1L' }],
|
||||
42,
|
||||
);
|
||||
const contextNoAlias = makeContext([], products, [], 42);
|
||||
const first = await (service as any).matchAndEnrichReceiptItem({ rawName: 'ARLA MJOLK 1L' }, contextNoAlias);
|
||||
|
||||
expect(first[0].matchedProductId).toBeUndefined();
|
||||
expect(first[0].suggestedProductId).toBe(700);
|
||||
expect(first.matchedProductId).toBeUndefined();
|
||||
expect(first.suggestedProductId).toBe(700);
|
||||
|
||||
// Simulerar att användaren manuellt korrigerar och alias lärs in.
|
||||
aliases.push({
|
||||
receiptName: 'arla mjolk 1l',
|
||||
productId: 700,
|
||||
product: {
|
||||
id: 700,
|
||||
name: 'Arla Mjolk 1l',
|
||||
canonicalName: 'Mjolk',
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
const aliases = [
|
||||
{
|
||||
receiptName: 'arla mjolk 1l',
|
||||
productId: 700,
|
||||
product: {
|
||||
id: 700,
|
||||
name: 'Arla Mjolk 1l',
|
||||
canonicalName: 'Mjolk',
|
||||
categoryId: 30,
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
},
|
||||
});
|
||||
];
|
||||
|
||||
const second = await (service as any).matchProducts(
|
||||
[{ rawName: 'ARLA MJOLK 1L' }],
|
||||
42,
|
||||
);
|
||||
const contextWithAlias = makeContext(aliases, products, [], 42);
|
||||
const second = await (service as any).matchAndEnrichReceiptItem({ rawName: 'ARLA MJOLK 1L' }, contextWithAlias);
|
||||
|
||||
expect(second[0].matchedProductId).toBe(700);
|
||||
expect(second[0].matchedProductName).toBe('Mjolk');
|
||||
expect(second[0].suggestedProductId).toBeUndefined();
|
||||
expect(second.matchedProductId).toBe(700);
|
||||
expect(second.matchedProductName).toBe('Mjolk');
|
||||
expect(second.suggestedProductId).toBeUndefined();
|
||||
});
|
||||
|
||||
it('använder inlärd enhetsmappning vid aliasträff', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([
|
||||
const aliases = [
|
||||
{
|
||||
receiptName: 'mjolk 1l',
|
||||
productId: 501,
|
||||
@@ -238,60 +218,51 @@ describe('ReceiptImportService test matrix', () => {
|
||||
categoryRef: { id: 30, name: 'Mejeri' },
|
||||
},
|
||||
},
|
||||
]);
|
||||
];
|
||||
|
||||
prismaMock.unitMapping.findMany.mockResolvedValue([
|
||||
{
|
||||
productId: 501,
|
||||
originalUnit: 'l',
|
||||
preferredUnit: 'st',
|
||||
},
|
||||
]);
|
||||
const unitMappings = [{ productId: 501, originalUnit: 'l', preferredUnit: 'st' }];
|
||||
const context = makeContext(aliases, [], unitMappings, 77);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'MJOLK 1L', unit: 'L' }, context);
|
||||
|
||||
const result = await (service as any).matchProducts(
|
||||
[{ rawName: 'MJOLK 1L', unit: 'L' }],
|
||||
77,
|
||||
);
|
||||
|
||||
expect(result[0].matchedProductId).toBe(501);
|
||||
expect(result[0].unit).toBe('st');
|
||||
expect(result.matchedProductId).toBe(501);
|
||||
expect(result.unit).toBe('st');
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchedVia', () => {
|
||||
function makeContext(aliases: any[], products: any[], unitMappings: any[] = [], userId?: number) {
|
||||
return { userId, aliases, products, unitMappings, categories, aiEnabled: false };
|
||||
}
|
||||
|
||||
it('sätter matchedVia: alias vid aliasträff', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([
|
||||
const aliases = [
|
||||
{
|
||||
receiptName: 'snickers',
|
||||
productId: 222,
|
||||
product: { id: 222, name: 'Snickers', canonicalName: 'Snickers', categoryId: null, categoryRef: null },
|
||||
},
|
||||
]);
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
];
|
||||
const context = makeContext(aliases, [], [], 10);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'SNICKERS' }, context);
|
||||
|
||||
const result = await (service as any).matchProducts([{ rawName: 'SNICKERS' }], 10);
|
||||
|
||||
expect(result[0].matchedVia).toBe('alias');
|
||||
expect(result.matchedVia).toBe('alias');
|
||||
});
|
||||
|
||||
it('sätter matchedVia: wordmatch vid ordbaserad matchning', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([]);
|
||||
prismaMock.product.findMany.mockResolvedValue([
|
||||
const products = [
|
||||
{ id: 300, name: 'Mjolk', canonicalName: 'Mjolk', categoryId: null, categoryRef: null },
|
||||
]);
|
||||
];
|
||||
const context = makeContext([], products, [], 10);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'MJOLK 1L' }, context);
|
||||
|
||||
const result = await (service as any).matchProducts([{ rawName: 'MJOLK 1L' }], 10);
|
||||
|
||||
expect(result[0].matchedVia).toBe('wordmatch');
|
||||
expect(result.matchedVia).toBe('wordmatch');
|
||||
});
|
||||
|
||||
it('sätter matchedVia: none när ingen matchning finns', async () => {
|
||||
prismaMock.receiptAlias.findMany.mockResolvedValue([]);
|
||||
prismaMock.product.findMany.mockResolvedValue([]);
|
||||
const context = makeContext([], [], [], 10);
|
||||
const result = await (service as any).matchAndEnrichReceiptItem({ rawName: 'XYZXYZ' }, context);
|
||||
|
||||
const result = await (service as any).matchProducts([{ rawName: 'XYZXYZ' }], 10);
|
||||
|
||||
expect(result[0].matchedVia).toBe('none');
|
||||
expect(result.matchedVia).toBe('none');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user