Fix: ViewReceipt parse button now uses parse queue instead of direct parsing

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-15 19:26:40 -05:00
parent e6512f9b7f
commit 324ab2c627
3 changed files with 33 additions and 55 deletions

View File

@@ -156,28 +156,20 @@
<strong>Parse Receipt</strong>
</div>
<div class="card-body">
@if (Model.AvailableParsers.Any())
{
<form method="post" asp-page-handler="Parse" asp-route-id="@Model.Receipt.Id">
<input type="hidden" name="parser" value="@Model.AvailableParsers.First().FullName" />
<p class="text-muted small mb-2">
Using: <strong>@Model.SelectedModel</strong>
<a href="/Settings" class="ms-2 small">Change</a>
</p>
<div class="mb-2">
<label for="ParsingNotes" class="form-label small text-muted mb-1">Notes for AI</label>
<textarea asp-for="ParsingNotes" class="form-control form-control-sm" rows="3"
placeholder="Optional hints for parsing (e.g., 'This is a restaurant receipt', 'Ignore the voided items')"></textarea>
</div>
<button type="submit" class="btn btn-primary btn-sm w-100">
Parse Receipt
</button>
</form>
}
else
{
<p class="text-muted small mb-0">No parsers available</p>
}
<form method="post" asp-page-handler="Parse" asp-route-id="@Model.Receipt.Id">
<p class="text-muted small mb-2">
Using: <strong>@Model.SelectedModel</strong>
<a href="/Settings" class="ms-2 small">Change</a>
</p>
<div class="mb-2">
<label for="ParsingNotes" class="form-label small text-muted mb-1">Notes for AI</label>
<textarea asp-for="ParsingNotes" class="form-control form-control-sm" rows="3"
placeholder="Optional hints for parsing (e.g., 'This is a restaurant receipt', 'Ignore the voided items')"></textarea>
</div>
<button type="submit" class="btn btn-primary btn-sm w-100">
Parse Receipt
</button>
</form>
</div>
</div>

View File

@@ -11,25 +11,24 @@ namespace MoneyMap.Pages
{
private readonly MoneyMapContext _db;
private readonly IReceiptManager _receiptManager;
private readonly IEnumerable<IReceiptParser> _parsers;
private readonly IReceiptParseQueue _parseQueue;
private readonly IConfiguration _config;
public ViewReceiptModel(
MoneyMapContext db,
IReceiptManager receiptManager,
IEnumerable<IReceiptParser> parsers,
IReceiptParseQueue parseQueue,
IConfiguration config)
{
_db = db;
_receiptManager = receiptManager;
_parsers = parsers;
_parseQueue = parseQueue;
_config = config;
}
public Receipt? Receipt { get; set; }
public List<ReceiptLineItem> LineItems { get; set; } = new();
public List<ReceiptParseLog> ParseLogs { get; set; } = new();
public List<ParserOption> AvailableParsers { get; set; } = new();
public string ReceiptUrl { get; set; } = "";
public string SelectedModel => _config["AI:ReceiptParsingModel"] ?? "gpt-4o-mini";
@@ -62,13 +61,6 @@ namespace MoneyMap.Pages
// Get receipt URL for display - use handler parameter
ReceiptUrl = $"/ViewReceipt/{id}?handler=file";
// Get available parsers
AvailableParsers = _parsers.Select(p => new ParserOption
{
Name = p.GetType().Name.Replace("ReceiptParser", ""),
FullName = p.GetType().Name
}).ToList();
return Page();
}
@@ -94,35 +86,26 @@ namespace MoneyMap.Pages
return File(fileBytes, receipt.ContentType);
}
public async Task<IActionResult> OnPostParseAsync(long id, string parser)
public async Task<IActionResult> OnPostParseAsync(long id)
{
var selectedParser = _parsers.FirstOrDefault(p => p.GetType().Name == parser);
var receipt = await _db.Receipts.FindAsync(id);
if (selectedParser == null)
if (receipt == null)
{
ErrorMessage = "Parser not found.";
ErrorMessage = "Receipt not found.";
return RedirectToPage(new { id });
}
// Use the configured model from settings, pass user notes
var result = await selectedParser.ParseReceiptAsync(id, SelectedModel, ParsingNotes);
// Save parsing notes to the receipt entity so the worker can use them
receipt.ParsingNotes = ParsingNotes;
receipt.ParseStatus = ReceiptParseStatus.Queued;
await _db.SaveChangesAsync();
if (result.IsSuccess)
{
SuccessMessage = result.Message;
}
else
{
ErrorMessage = result.Message;
}
// Enqueue the receipt for parsing
await _parseQueue.EnqueueAsync(id);
SuccessMessage = "Receipt queued for parsing.";
return RedirectToPage(new { id });
}
public class ParserOption
{
public string Name { get; set; } = "";
public string FullName { get; set; } = "";
}
}
}

View File

@@ -60,6 +60,9 @@ namespace MoneyMap.Services
if (!File.Exists(filePath))
return ReceiptParseResult.Failure("Receipt file not found on disk.");
// Fall back to receipt.ParsingNotes if notes parameter is null
var effectiveNotes = notes ?? receipt.ParsingNotes;
var selectedModel = model ?? _configuration["AI:ReceiptParsingModel"] ?? "gpt-4o-mini";
var (client, provider) = _clientResolver.Resolve(selectedModel);
@@ -79,7 +82,7 @@ namespace MoneyMap.Services
try
{
var (base64Data, mediaType) = await PrepareImageDataAsync(receipt, filePath);
var promptText = await BuildPromptAsync(receipt, notes, client);
var promptText = await BuildPromptAsync(receipt, effectiveNotes, client);
var visionResult = await CallVisionClientAsync(client, base64Data, mediaType, promptText, selectedModel);
if (!visionResult.IsSuccess)
@@ -89,7 +92,7 @@ namespace MoneyMap.Services
}
var parseData = ParseResponse(visionResult.Content);
await ApplyParseResultAsync(receipt, receiptId, parseData, notes);
await ApplyParseResultAsync(receipt, receiptId, parseData, effectiveNotes);
parseLog.Success = true;
parseLog.Confidence = parseData.Confidence;