feat(inventory): add multi-country origin tracking
Test Suite / backend-pr-quick (push) Has been skipped
Test Suite / quick-import-pr-quick (push) Has been skipped
Test Suite / backend-full (push) Failing after 3m32s
Test Suite / flutter-quality (push) Failing after 1m0s

- Added `originCountries` field to `InventoryItem` model for multi-country origin support
- Updated `CreateInventoryDto` and `UpdateInventoryDto` with `originCountries` array field
- Modified `InventoryService` to handle `originCountries` in create and update operations
- Added `origin` field to `FlyerImportItem` response type for consistency
- Added `categoryId` field to `ParsedReceiptItem` DTO for improved receipt parsing
- Created database migration `20260524_add_origin_countries` for schema changes
This commit is contained in:
Nils-Johan Gynther
2026-05-24 21:31:53 +02:00
parent ca1eed5061
commit 27d622bfe6
9 changed files with 167 additions and 66 deletions
@@ -874,45 +874,46 @@ export class FlyerImportService {
const offerLimitText = this.extractOfferLimitText(item.offerText);
const offerSignals = this.extractOfferSignals(item.offerText);
return {
flyerItemId: item.id,
rawName: item.rawName,
normalizedName: item.normalizedName,
brand: item.brand,
category: categoryPath,
categoryId: item.categoryId,
price: item.price != null ? item.price.toNumber() : offerSignals.price,
priceUnit: this.normalizeUnit(item.priceUnit) ?? offerSignals.priceUnit,
comparisonPrice: item.comparisonPrice != null ? item.comparisonPrice.toNumber() : offerSignals.comparisonPrice,
comparisonUnit: this.normalizeUnit(item.comparisonUnit) ?? offerSignals.comparisonUnit,
weight: item.weight,
bundleWeight: item.bundleWeight,
isBundle: item.isBundle,
bundleItems: this.sanitizeBundleItems(toStringArray(item.bundleItems)),
displayNameDetailed:
item.displayNameDetailed ??
buildDisplayNameDetailed({
rawName: item.rawName,
isBundle: item.isBundle,
bundleItems: this.sanitizeBundleItems(toStringArray(item.bundleItems)),
}),
signals: toSignals(item.signals),
offerText: item.offerText,
isOffer:
item.price != null
|| item.comparisonPrice != null
|| !!item.offerText?.trim()
|| offerSignals.hasCampaignPattern,
offerLimitText,
parseConfidence: item.parseConfidence,
parseReasons: toStringArray(item.parseReasons),
parseReasonsDetailed: this.describeParseReasons(toStringArray(item.parseReasons)),
matchedProductId: item.matchedProductId,
matchedProductName: item.matchedProductName,
matchedVia: normalizedMatchVia,
matchConfidence: item.matchConfidence ?? 0,
matchReasons: toStringArray(item.matchReasons),
matchReasonsDetailed: this.describeMatchReasons(toStringArray(item.matchReasons)),
return {
flyerItemId: item.id,
rawName: item.rawName,
normalizedName: item.normalizedName,
brand: item.brand,
category: categoryPath,
categoryId: item.categoryId,
price: item.price != null ? item.price.toNumber() : offerSignals.price,
priceUnit: this.normalizeUnit(item.priceUnit) ?? offerSignals.priceUnit,
comparisonPrice: item.comparisonPrice != null ? item.comparisonPrice.toNumber() : offerSignals.comparisonPrice,
comparisonUnit: this.normalizeUnit(item.comparisonUnit) ?? offerSignals.comparisonUnit,
weight: item.weight,
bundleWeight: item.bundleWeight,
isBundle: item.isBundle,
bundleItems: this.sanitizeBundleItems(toStringArray(item.bundleItems)),
displayNameDetailed:
item.displayNameDetailed ?
buildDisplayNameDetailed({
rawName: item.rawName,
isBundle: item.isBundle,
bundleItems: this.sanitizeBundleItems(toStringArray(item.bundleItems)),
}),
signals: toSignals(item.signals),
offerText: item.offerText,
isOffer:
item.price != null
|| item.comparisonPrice != null
|| !!item.offerText?.trim()
|| offerSignals.hasCampaignPattern,
offerLimitText,
parseConfidence: item.parseConfidence,
parseReasons: toStringArray(item.parseReasons),
parseReasonsDetailed: this.describeParseReasons(toStringArray(item.parseReasons)),
matchedProductId: item.matchedProductId,
matchedProductName: item.matchedProductName,
matchedVia: normalizedMatchVia,
matchConfidence: item.matchConfidence ?? 0,
matchReasons: toStringArray(item.matchReasons),
matchReasonsDetailed: this.describeMatchReasons(toStringArray(item.matchReasons)),
origin: toSignals(item.signals)?.originCountries?.[0] ?? null,
};
}