chore(import): improve error handling and add flyer integration
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 3m41s
Test Suite / flutter-quality (push) Successful in 2m3s

- Replace BadRequestException with UnauthorizedException for authentication failures in flyer-import and flyer-selection controllers
- Add bulk selection endpoint in FlyerSelectionController for creating multiple selections in one request
- Update FlyerSelectionModule to include new FlyerSelectionMatcherService and FlyerSelectionSyncController
- Extend FlyerSelectionService with createMany method for bulk operations
- Add new DTOs for bulk selection and receipt matching functionality
- Update ReceiptImportService to accept FlyerSelectionService dependency and track successful rows
- Extend SaveReceiptResponse with flyerAutoSync field for receipt-to-flyer matching results
- Add new API paths for flyer import and selection endpoints
- Update Flutter UI to include Flyer import tab and adjust tab controller length
- Add new domain models and repository methods for flyer import functionality
- Update test files to include new FlyerSelectionService dependency
- Modify .kilo plan documentation to reflect current system architecture
This commit is contained in:
Nils-Johan Gynther
2026-05-18 22:51:27 +02:00
parent 24a96c3da1
commit d5f903db98
26 changed files with 1359 additions and 247 deletions
@@ -0,0 +1,43 @@
class FlyerImportItem {
final int? flyerItemId;
final String rawName;
final String normalizedName;
final String? category;
final double? price;
final String? priceUnit;
final String? offerText;
final int? matchedProductId;
final String? matchedProductName;
final String? matchedVia;
final double? matchConfidence;
FlyerImportItem({
required this.flyerItemId,
required this.rawName,
required this.normalizedName,
this.category,
this.price,
this.priceUnit,
this.offerText,
this.matchedProductId,
this.matchedProductName,
this.matchedVia,
this.matchConfidence,
});
factory FlyerImportItem.fromJson(Map<String, dynamic> json) {
return FlyerImportItem(
flyerItemId: (json['flyerItemId'] as num?)?.toInt(),
rawName: json['rawName'] as String? ?? '',
normalizedName: json['normalizedName'] as String? ?? '',
category: json['category'] as String?,
price: (json['price'] as num?)?.toDouble(),
priceUnit: json['priceUnit'] as String?,
offerText: json['offerText'] as String?,
matchedProductId: (json['matchedProductId'] as num?)?.toInt(),
matchedProductName: json['matchedProductName'] as String?,
matchedVia: json['matchedVia'] as String?,
matchConfidence: (json['matchConfidence'] as num?)?.toDouble(),
);
}
}
@@ -0,0 +1,29 @@
import 'flyer_import_item.dart';
class FlyerImportResult {
final int? sessionId;
final List<FlyerImportItem> items;
final List<String> warnings;
FlyerImportResult({
required this.sessionId,
required this.items,
required this.warnings,
});
factory FlyerImportResult.fromJson(Map<String, dynamic> json) {
final rawItems = (json['items'] as List?) ?? const [];
final warnings = (json['warnings'] as List?)
?.map((warning) => warning.toString())
.toList() ??
const <String>[];
return FlyerImportResult(
sessionId: (json['sessionId'] as num?)?.toInt(),
items: rawItems
.map((item) => FlyerImportItem.fromJson(item as Map<String, dynamic>))
.toList(),
warnings: warnings,
);
}
}