feat(account): 영수증 사진 AI 구조화 — 비전 우선, OCR 폴백
Deploy / deploy (push) Failing after 14m23s

영수증 선택 시 Claude Vision(/receipt-ai)으로 금액·상호·날짜·분류·계좌 자동 채움.
AI 실패/미인식 시 기존 Google Vision OCR+규칙파서로 폴백. 결과 배너에 ·분류 표시.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
sb
2026-07-05 14:22:07 +09:00
parent 6a4d01b6e1
commit 7e13a42848
2 changed files with 45 additions and 3 deletions
+32 -1
View File
@@ -250,6 +250,35 @@ async function runOcr(image) {
formError.value = null
try {
const blob = await imageToBlob(image)
// 1) AI 비전 우선 — 금액·상호·날짜·분류·계좌를 한 번에 구조화
let ai = null
try {
ai = await accountApi.parseReceiptAi(blob, {
categoryHints: allCategoryNames.value,
walletHints: wallets.value.map((w) => w.name),
})
} catch {
ai = null // AI 실패 → 아래 OCR 폴백
}
if (ai && ai.recognized) {
form.type = ai.type === 'INCOME' ? 'INCOME' : 'EXPENSE'
if (ai.amount) form.amount = Number(ai.amount)
if (ai.merchant && !form.memo) form.memo = ai.merchant
if (ai.date) form.entryDate = ai.date
if (ai.category) form.category = ai.category
let aiCard = null
if (ai.wallet) {
const w = wallets.value.find((x) => x.name === ai.wallet)
if (w) {
form.walletKind = w.type
form.walletId = w.id
aiCard = w.name
}
}
ocrResult.value = { amount: ai.amount, date: ai.date, store: ai.merchant, card: aiCard, category: ai.category, ai: true }
return
}
// 2) 폴백 — 기존 Google Vision OCR + 규칙 파서
const { text } = await accountApi.ocrReceipt(blob)
const r = parseReceiptText(text)
// 추출값이 있을 때만 폼에 채움 (메모는 비어있을 때만)
@@ -1048,13 +1077,15 @@ onMounted(async () => {
</div>
<span v-if="ocrRunning" class="receipt-status">영수증 인식 </span>
<span v-else-if="ocrResult" class="receipt-status ok">
<template v-if="ocrResult.ai"> </template>
<template v-if="ocrResult.amount">{{ won(ocrResult.amount) }}</template>
<template v-if="ocrResult.date"> · {{ ocrResult.date }}</template>
<template v-if="ocrResult.store"> · {{ ocrResult.store }}</template>
<template v-if="ocrResult.category"> · {{ ocrResult.category }}</template>
<template v-if="ocrResult.card"> · 💳{{ ocrResult.card }}</template>
자동 입력됨
</span>
<span v-else class="receipt-hint">사진에서 금액·날짜·상호를 자동 입력</span>
<span v-else class="receipt-hint">사진에서 금액·날짜·상호·분류 자동 입력</span>
<div v-if="ocrRunning" class="receipt-progress"><div class="bar"></div></div>
</div>