Commit 57d7b4b7 authored by ThinhNC's avatar ThinhNC

fix(guest): fix receipt extraction crash and add defensive normalization in ReceiptScannerView

parent 941d4805
......@@ -1528,11 +1528,28 @@ export function handleGuestRequest(config: InternalAxiosRequestConfig): AxiosRes
responseData = {
success: true,
data: {
merchant: "Siêu thị Co.opmart",
merchantName: "Siêu thị Co.opmart",
totalAmount: "250000",
currency: "VND",
taxAmount: "20000",
transactionDate: todayStr,
category: {
id: "cat-expense-1",
name: "Ăn uống",
icon: "utensils",
color: "#EF4444",
},
lineItems: [
{ name: "Sữa chua hoa quả", quantity: 2, unitPrice: "35000", totalAmount: "70000" },
{ name: "Sữa chua hoa quả Co.op", quantity: 2, unitPrice: "35000", totalAmount: "70000" },
{ name: "Bánh mì sandwich bơ", quantity: 1, unitPrice: "45000", totalAmount: "45000" },
{ name: "Nước ép cam ép tươi 1L", quantity: 2, unitPrice: "55000", totalAmount: "110000" },
{ name: "Khăn giấy ướt cao cấp", quantity: 1, unitPrice: "25000", totalAmount: "25000" },
],
rawText: `SIEU THI CO.OPMART\nNgay: ${todayStr}\n1. Sua chua hoa qua: 70.000\n2. Banh mi sandwich: 45.000\n3. Nuoc ep cam: 110.000\n4. Khan giay: 25.000\nTong tien: 250.000 VND\nVAT 8%: 20.000 VND`,
confidence: 0.96,
warnings: [
"Hóa đơn mẫu mô phỏng trong Chế độ Dùng thử (100% xử lý tại máy, không tốn token AI thật).",
],
},
};
......
......@@ -65,7 +65,20 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
{
onSuccess: (res) => {
if (res.success && res.data) {
setExtractedData(res.data);
const raw = res.data as any;
const normalizedData: ExtractReceiptData = {
merchant: raw.merchant || raw.merchantName || null,
transactionDate: raw.transactionDate || null,
totalAmount: raw.totalAmount || null,
currency: raw.currency || "VND",
taxAmount: raw.taxAmount || null,
category: raw.category || null,
lineItems: Array.isArray(raw.lineItems) ? raw.lineItems : [],
rawText: raw.rawText || "",
confidence: typeof raw.confidence === "number" ? raw.confidence : 0.95,
warnings: Array.isArray(raw.warnings) ? raw.warnings : [],
};
setExtractedData(normalizedData);
}
},
}
......@@ -98,19 +111,22 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
const handleConfirmTransaction = () => {
if (!extractedData) return;
const lineItemsCount = Array.isArray(extractedData.lineItems) ? extractedData.lineItems.length : 0;
const merchantName = extractedData.merchant || (extractedData as any).merchantName || "";
const prefillData = {
amount: extractedData.totalAmount || "",
type: "EXPENSE" as const,
categoryId: extractedData.category?.id || "",
date: extractedData.transactionDate || "",
description: extractedData.merchant
? `${extractedData.merchant}${
extractedData.lineItems.length > 0
? ` (${t("ai.ocr.itemCount", { count: extractedData.lineItems.length })})`
description: merchantName
? `${merchantName}${
lineItemsCount > 0
? ` (${t("ai.ocr.itemCount", { count: lineItemsCount })})`
: ""
}`
: "",
location: extractedData.merchant || "",
location: merchantName,
};
onTransactionPrefill(prefillData, selectedFile);
......@@ -125,10 +141,11 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
if (fileInputRef.current) fileInputRef.current.value = "";
};
const confidence = typeof extractedData?.confidence === "number" ? extractedData.confidence : 0.95;
const confidenceClassName = extractedData
? extractedData.confidence >= 0.8
? confidence >= 0.8
? "border-clay-income/30 bg-clay-income/20 text-clay-income"
: extractedData.confidence >= 0.5
: confidence >= 0.5
? "border-clay-warning/30 bg-clay-warning/20 text-clay-warning"
: "border-clay-expense/30 bg-clay-expense/20 text-clay-expense"
: "";
......@@ -263,7 +280,7 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
{t("ai.ocr.resultTitle")}
</h4>
<span className={`shrink-0 rounded-full border px-3 py-1 font-baloo text-xs font-bold shadow-clay-pressed ${confidenceClassName}`}>
{t("ai.ocr.confidence")}: {formatNumber(extractedData.confidence, {
{t("ai.ocr.confidence")}: {formatNumber(confidence, {
style: "percent",
maximumFractionDigits: 0,
})}
......@@ -276,7 +293,7 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
{t("ai.ocr.merchant")}
</span>
<span className="break-words font-nunito text-sm font-bold text-clay-text">
{extractedData.merchant || "—"}
{extractedData.merchant || (extractedData as any).merchantName || "—"}
</span>
</div>
......@@ -318,7 +335,7 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
</div>
{/* Line Items Table */}
{extractedData.lineItems.length > 0 && (
{Array.isArray(extractedData.lineItems) && extractedData.lineItems.length > 0 && (
<div className="mt-2 border-t border-clay-highlight/40 pt-3">
<h5 className="mb-2 font-nunito text-xs font-bold text-clay-text">
{t("ai.ocr.lineItems")} ({extractedData.lineItems.length})
......@@ -368,7 +385,7 @@ export const ReceiptScannerView: React.FC<ReceiptScannerViewProps> = ({ onTransa
)}
{/* Warnings */}
{extractedData.warnings.length > 0 && (
{Array.isArray(extractedData.warnings) && extractedData.warnings.length > 0 && (
<div className="mt-4 rounded-clay-sm bg-clay-warning/10 p-3 border border-clay-warning/30 text-xs font-medium text-clay-text space-y-1">
<span className="block font-bold text-clay-warning">
{t("ai.ocr.warnings")}:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment