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:
@@ -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>
|
||||
|
||||
|
||||
@@ -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; } = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user