improve parsing

This commit is contained in:
davmar
2026-09-02 12:35:35 +02:00
parent d532af6111
commit e98fb0e7f4
9 changed files with 294 additions and 680 deletions
+23 -3
View File
@@ -23,21 +23,41 @@ For each receipt:
| `store` | Best-effort store from the header + `known_stores` |
| `receiptTotal` | First grand-total amount after the items |
| `totalDiscount` | Receipt-level `Totaal korting` (and similar) |
| `validationPassed` | Item sum matches `receiptTotal` within €0.01 |
| `validationPassed` | Item sum matches `receiptTotal` within €0.01, item count matches when printed, and no line needs manual review |
| `items[].sourceBounds` | Normalized rect on the receipt photo for tap-to-highlight in Review |
| `items[].needsManualReview` | Parser could not resolve price/qty confidently |
| `expectedItemCount` | From `Aantal Artikel`-style lines when found |
| `ocrImageWidth` / `ocrImageHeight` | Image size used to normalize bounds |
If validation fails, Review shows: *Parsed totals do not match the receipt.*
If validation fails, Review shows a banner asking you to check items before saving.
---
## Pipeline
### Primary: spatial (ML Kit bounding boxes)
Used automatically after scanning a receipt photo.
1. **Extract elements** from ML Kit `RecognizedText` (blocks → lines → elements), keeping each element's pixel bounding box.
2. **Cluster rows** by Y-center (threshold ≈ 1.2× median element height — see `spatial_parsing` in the JSON config).
3. **Classify rows** with column logic: right ~25% of content width is the price column; pure `N X price` rows are quantity modifiers (checked first).
4. **Build items** with a one-row lookahead buffer (Jumbo split layout + Kruidvat inline layout).
5. **Validate** totals, printed item count, and manual-review flags.
Implementation: `ReceiptOcr`, `ReceiptRowBuilder`, `ReceiptSpatialParser` in `lib/services/`.
### Fallback: flat text
`ReceiptParser.parseReceipt(String)` for tests and when only flattened OCR text exists.
1. **Preprocess** each OCR line: trim, collapse spaces, drop empty lines.
2. **Choose a layout**
- **Sequential** (default): name, price, quantity and discount appear in
reading order. Jumbo (printed), Lidl, Kruidvat, World Toko.
- **Column OCR fallback:** ML Kit sometimes dumps every name, then every
amount. Detected when there is a run of 4+ price-only lines *and* enough
name-like lines. Used for the Jumbo sample dump in tests.
name-like lines.
3. **Classify** each line (first match wins) using the keyword JSON.
4. **Build items** (pending name, then quantity/discount/price).
5. **Validate** against the receipt total when one was found.