Receipt parser improvements: voided items, UPC, quantity defaults, and model selection

Major improvements to receipt parsing:

Voided Item Handling:
- Add Voided boolean field to ReceiptLineItem model and database
- Never skip any line items - include voided items with voided=true and lineTotal=0.00
- Strong parser hints: "CONTINUE reading", "do NOT stop parsing", "Read ENTIRE receipt"
- Ensures all items after void markers are captured

UPC/Barcode Extraction:
- Extract UPC codes (12-13 digits) and store in Sku field
- Enables price tracking over time even when descriptions change

Quantity Defaults:
- ALWAYS default to 1.0 for ALL retail products (groceries, goods, merchandise)
- Only use null for utility bills, service fees, or taxes
- Emphatic instructions: "MUST be 1.0", "do NOT leave it null"
- Prevents missing quantities on retail items

Model Selection:
- Add AI model dropdown in ViewReceipt UI (gpt-4o-mini vs gpt-4o)
- Update IReceiptParser interface to accept optional model parameter
- Pass selected model through to OpenAI API
- Store model name in parse logs for history tracking
- Allows using smarter model for complex receipts

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
AJ
2025-10-19 16:08:56 -04:00
parent d0f4b420f8
commit f09d19ec5c
8 changed files with 698 additions and 31 deletions

View File

@@ -1,7 +1,3 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
@@ -88,7 +84,7 @@ namespace MoneyMap.Pages
return File(fileBytes, receipt.ContentType);
}
public async Task<IActionResult> OnPostParseAsync(long id, string parser)
public async Task<IActionResult> OnPostParseAsync(long id, string parser, string? model = null)
{
var selectedParser = _parsers.FirstOrDefault(p => p.GetType().Name == parser);
@@ -98,7 +94,7 @@ namespace MoneyMap.Pages
return RedirectToPage(new { id });
}
var result = await selectedParser.ParseReceiptAsync(id);
var result = await selectedParser.ParseReceiptAsync(id, model);
if (result.IsSuccess)
{