From d532af6111b1345cfb03fda841b725d2a7415dd2 Mon Sep 17 00:00:00 2001 From: davmar Date: Tue, 1 Sep 2026 17:52:52 +0200 Subject: [PATCH] fix low count of items scanned --- lib/services/receipt_parser.dart | 41 +++++++++++++++++++++++--------- test/receipt_parser_test.dart | 29 ++++++++++++++++++++++ 2 files changed, 59 insertions(+), 11 deletions(-) diff --git a/lib/services/receipt_parser.dart b/lib/services/receipt_parser.dart index b0ab6cb..b08655f 100644 --- a/lib/services/receipt_parser.dart +++ b/lib/services/receipt_parser.dart @@ -136,12 +136,21 @@ class ReceiptParser { } else { run = 0; if (_parseQty(stripped, config) == null && - !_isIgnorable(stripped, config)) { + !_isIgnorable(stripped, config) && + !config.matchesAny(stripped, [ + ...config.grandTotalKeywords, + ...config.subtotalKeywords, + ...config.taxInclusiveTotalKeywords, + ...config.totalDiscountKeywords, + ]) && + _trailingPrice(stripped, config) == null) { names++; } } } - return maxRun >= 4 && prices >= 4 && names >= 4; + if (maxRun >= 4 && prices >= 4 && names >= 4) return true; + // ML Kit sometimes splits a short receipt into names, then prices (2–3 items). + return maxRun >= 2 && prices >= 2 && names >= prices && maxRun >= prices; } static ReceiptParseResult _parseSequential( @@ -235,11 +244,17 @@ class ReceiptParser { } case _Kind.priceOnly: final amount = classified.amount!; - if (amount < 0 && pending != null) { - pending!.discount += amount; - pending!.lineTotal = roundMoney( - (pending!.lineTotal ?? pending!.unitPrice ?? 0) + amount, - ); + if (pending != null) { + if (amount < 0) { + pending!.discount += amount; + pending!.lineTotal = roundMoney( + (pending!.lineTotal ?? pending!.unitPrice ?? 0) + amount, + ); + } else if (pending!.lineTotal == null && + pending!.unitPrice == null) { + pending!.lineTotal = amount; + flush(); + } } } } @@ -307,19 +322,23 @@ class ReceiptParser { } final count = names.length < prices.length ? names.length : prices.length; + final pairedNames = names.length > count + ? names.sublist(names.length - count) + : names; + final nameOffset = names.length - pairedNames.length; final items = [ for (var i = 0; i < count; i++) LineItem( id: _id(), - name: names[i].name, + name: pairedNames[i].name, price: prices[i], - quantity: names[i].quantity, - unitPrice: names[i].unitPrice, + quantity: pairedNames[i].quantity, + unitPrice: pairedNames[i].unitPrice, ), ]; for (var i = 0; i < discountSlots.length && i < discounts.length; i++) { - final index = discountSlots[i]; + final index = discountSlots[i] - nameOffset; if (index < 0 || index >= items.length) continue; final item = items[index]; final discount = discounts[i]; diff --git a/test/receipt_parser_test.dart b/test/receipt_parser_test.dart index 1a652ed..b036ef4 100644 --- a/test/receipt_parser_test.dart +++ b/test/receipt_parser_test.dart @@ -15,6 +15,35 @@ CHANGE 1.50 expect(items.map((item) => item.price), [1.50, 2.00]); }); + test('parses name on one line and price on the next', () { + final items = ReceiptParser.parse(''' +MILK +1,50 +BREAD +2,00 +Totaal 3,50 +'''); + + expect(items, hasLength(2)); + expect(items.map((item) => item.name), ['MILK', 'BREAD']); + expect(items.map((item) => item.price), [1.50, 2.00]); + }); + + test('pairs two name lines with two price lines from short column OCR', () { + final items = ReceiptParser.parse(''' +Jumbo +MILK +BREAD +1,50 +2,00 +Totaal 3,50 +'''); + + expect(items, hasLength(2)); + expect(items.map((item) => item.name), ['MILK', 'BREAD']); + expect(items.map((item) => item.price), [1.50, 2.00]); + }); + test('uses the next line when quantity and total are split', () { final items = ReceiptParser.parse(''' EGGS