From 51e654c592c7d4c5e43deb3315e6d94ea3d7d54b Mon Sep 17 00:00:00 2001 From: Nils-Johan Gynther Date: Fri, 1 May 2026 23:33:33 +0200 Subject: [PATCH] feat: implement product name normalization to Title Case with smart rules --- .../presentation/receipt_import_tab.dart | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/flutter/lib/features/import/presentation/receipt_import_tab.dart b/flutter/lib/features/import/presentation/receipt_import_tab.dart index 6da7a522..691264ca 100644 --- a/flutter/lib/features/import/presentation/receipt_import_tab.dart +++ b/flutter/lib/features/import/presentation/receipt_import_tab.dart @@ -86,6 +86,19 @@ String _formatSwedishNumber(double value) { return _formatCompactNumber(value).replaceAll('.', ','); } +/// Konverterar VERSALER-produktnamn till Title Case med smarta regler: +/// - Token med `/` (förkortningar) lämnas i versaler: KY/KAL/LE/TO +/// - Token som börjar med siffra (mängd/storlek) görs till gemener: 284g, 12x85g +/// - Övriga token: första bokstav versal, resten gemen: Aprikosmarmelad +String _normalizeProductName(String raw) { + return raw.trim().split(' ').map((token) { + if (token.isEmpty) return token; + if (token.contains('/')) return token; + if (RegExp(r'^\d').hasMatch(token)) return token.toLowerCase(); + return token[0].toUpperCase() + token.substring(1).toLowerCase(); + }).join(' '); +} + // ── Redigeringstillstånd per rad ───────────────────────────────────────────── typedef _ItemEdit = ItemEdit; @@ -153,7 +166,7 @@ class _EditDialogState extends State<_EditDialog> { text: widget.current.unit ?? widget.item.unit ?? '', ); _newProductNameCtrl = TextEditingController( - text: widget.current.productName ?? widget.item.rawName, + text: _normalizeProductName(widget.current.productName ?? widget.item.rawName), ); } @@ -314,7 +327,11 @@ class _EditDialogState extends State<_EditDialog> { final aiPath = item.categorySuggestionPath; final aiLabel = (aiPath != null && aiPath.isNotEmpty) ? aiPath - : ((aiCategory != null && aiCategory.isNotEmpty) ? aiCategory : null); + : ((aiCategory != null && aiCategory.isNotEmpty) + ? aiCategory + : (item.suggestedProductName?.isNotEmpty == true + ? item.suggestedProductName + : null)); final currentQuantity = double.tryParse(_quantityCtrl.text.replaceAll(',', '.')) ?? widget.item.quantity; final currentUnit = _unitCtrl.text.trim().isEmpty ? widget.item.unit : _unitCtrl.text.trim();