- Added new API path for unit mappings in `api_paths.dart`. - Implemented `upsertUnitMapping` method in `ImportRepository` to handle unit mapping creation. - Updated `ReceiptImportTab` to learn and save unit mappings during receipt import. - Created DTO for unit mapping with validation in `create-unit-mapping.dto.ts`. - Added SQL migration for `UnitMapping` table creation with necessary constraints.
This commit is contained in:
+45
-1
@@ -143,6 +143,35 @@ let ReceiptImportService = ReceiptImportService_1 = class ReceiptImportService {
|
||||
const matched = await this.matchProducts(rawItems, userId);
|
||||
return this.enrichWithAiCategories(matched, userId);
|
||||
}
|
||||
async upsertUnitMapping(userId, productId, originalUnit, preferredUnit) {
|
||||
const prismaAny = this.prisma;
|
||||
const normalizedOriginalUnit = originalUnit.trim().toLowerCase();
|
||||
const normalizedPreferredUnit = preferredUnit.trim().toLowerCase();
|
||||
if (!normalizedOriginalUnit || !normalizedPreferredUnit) {
|
||||
throw new common_1.BadRequestException('Enheter måste vara ifyllda.');
|
||||
}
|
||||
if (normalizedOriginalUnit === normalizedPreferredUnit) {
|
||||
return { skipped: true };
|
||||
}
|
||||
return prismaAny.unitMapping.upsert({
|
||||
where: {
|
||||
productId_originalUnit_userId: {
|
||||
productId,
|
||||
originalUnit: normalizedOriginalUnit,
|
||||
userId,
|
||||
},
|
||||
},
|
||||
update: {
|
||||
preferredUnit: normalizedPreferredUnit,
|
||||
},
|
||||
create: {
|
||||
productId,
|
||||
userId,
|
||||
originalUnit: normalizedOriginalUnit,
|
||||
preferredUnit: normalizedPreferredUnit,
|
||||
},
|
||||
});
|
||||
}
|
||||
async parseReceiptViaImporter(file) {
|
||||
const form = new FormData();
|
||||
form.append('file', new Blob([new Uint8Array(file.buffer)], { type: file.mimetype }), file.originalname);
|
||||
@@ -175,6 +204,7 @@ let ReceiptImportService = ReceiptImportService_1 = class ReceiptImportService {
|
||||
return items.filter((item) => !isIgnoredReceiptName(item.rawName));
|
||||
}
|
||||
async matchProducts(items, userId) {
|
||||
const prismaAny = this.prisma;
|
||||
const productFilter = userId ? { isActive: true, ownerId: userId } : { isActive: true };
|
||||
const aliasFilter = userId
|
||||
? {
|
||||
@@ -184,7 +214,13 @@ let ReceiptImportService = ReceiptImportService_1 = class ReceiptImportService {
|
||||
],
|
||||
}
|
||||
: { isGlobal: true };
|
||||
const [aliases, products] = await Promise.all([
|
||||
const unitMappingsPromise = userId && prismaAny.unitMapping?.findMany
|
||||
? prismaAny.unitMapping.findMany({
|
||||
where: { userId },
|
||||
select: { productId: true, originalUnit: true, preferredUnit: true },
|
||||
})
|
||||
: Promise.resolve([]);
|
||||
const [aliases, products, unitMappings] = await Promise.all([
|
||||
this.prisma.receiptAlias.findMany({
|
||||
where: aliasFilter,
|
||||
orderBy: [
|
||||
@@ -197,6 +233,7 @@ let ReceiptImportService = ReceiptImportService_1 = class ReceiptImportService {
|
||||
where: productFilter,
|
||||
select: { id: true, name: true, canonicalName: true, categoryId: true, categoryRef: { select: { id: true, name: true } } },
|
||||
}),
|
||||
unitMappingsPromise,
|
||||
]);
|
||||
return items.map((item) => {
|
||||
const raw = (item.rawName ?? '').toLowerCase().trim();
|
||||
@@ -204,11 +241,14 @@ let ReceiptImportService = ReceiptImportService_1 = class ReceiptImportService {
|
||||
return item;
|
||||
const alias = aliases.find((a) => a.receiptName === raw);
|
||||
if (alias) {
|
||||
const mappedUnit = unitMappings.find((um) => um.productId === alias.product.id &&
|
||||
um.originalUnit === (item.unit ?? '').trim().toLowerCase())?.preferredUnit;
|
||||
const cat = alias.product.categoryRef;
|
||||
return {
|
||||
...item,
|
||||
matchedProductId: alias.product.id,
|
||||
matchedProductName: alias.product.canonicalName ?? alias.product.name,
|
||||
unit: mappedUnit ?? item.unit,
|
||||
...(cat ? { categorySuggestion: { categoryId: cat.id, categoryName: cat.name, path: cat.name, confidence: 'high', usedFallback: false } } : {}),
|
||||
};
|
||||
}
|
||||
@@ -216,11 +256,15 @@ let ReceiptImportService = ReceiptImportService_1 = class ReceiptImportService {
|
||||
if (!suggestion) {
|
||||
return { ...item };
|
||||
}
|
||||
const unitMapping = unitMappings.find((um) => um.productId === suggestion.id &&
|
||||
um.originalUnit === (item.unit ?? '').trim().toLowerCase());
|
||||
const preferredUnit = unitMapping ? unitMapping.preferredUnit : item.unit;
|
||||
const cat = suggestion.categoryRef;
|
||||
return {
|
||||
...item,
|
||||
suggestedProductId: suggestion.id,
|
||||
suggestedProductName: suggestion.canonicalName ?? suggestion.name,
|
||||
unit: preferredUnit,
|
||||
...(cat ? { categorySuggestion: { categoryId: cat.id, categoryName: cat.name, path: cat.name, confidence: 'medium', usedFallback: false } } : {}),
|
||||
};
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user