import { Prisma, PrismaClient } from '@prisma/client'; import { mapExpenseLineKind, parseOaJson } from './oa-json'; const prisma = new PrismaClient(); const FLOW = '/opt/import/prod/oa_flow_finance.json'; type Row = Record; function str(v: unknown) { if (v == null) return ''; return String(v).trim(); } function num(v: unknown) { if (v == null || v === '') return 0; const n = Number(String(v).replace(/,/g, '')); return Number.isFinite(n) ? n : 0; } function when(v: unknown) { const s = str(v); if (!s || s.startsWith('0000')) return null; const d = new Date(s.includes('T') ? s : s.replace(' ', 'T')); return Number.isNaN(d.getTime()) ? null : d; } function clip(v: unknown, n = 200) { const s = str(v); return s.length > n ? s.slice(0, n) : s; } async function main() { const flow = parseOaJson>(FLOW); const itemsByReimb = new Map(); for (const item of flow.reimbursement_item || []) { const key = str(item.reimbursement_id); if (!key) continue; const list = itemsByReimb.get(key) || []; list.push(item); itemsByReimb.set(key, list); } let claims = 0; let lines = 0; let skipped = 0; for (const r of flow.reimbursement || []) { const claimNo = str(r.reimburse_no); if (!claimNo) continue; const row = await prisma.expenseClaim.findFirst({ where: { tenantId: '1', claimNo } }); if (!row) { skipped += 1; continue; } const incoming = itemsByReimb.get(str(r.id)) || []; await prisma.$transaction(async (tx) => { await tx.expenseLine.deleteMany({ where: { claimId: row.id } }); for (const [i, line] of incoming.entries()) { await tx.expenseLine.create({ data: { claimId: row.id, kind: mapExpenseLineKind(str(line.item_type), str(line.remark)), amount: new Prisma.Decimal(num(line.amount)), invoiceNo: clip(line.invoice_no, 80) || undefined, occurAt: when(line.expense_date) || undefined, remark: clip(line.remark || line.item_type, 200) || undefined, sortNo: num(line.sort_order) || i, }, }); lines += 1; } }); claims += 1; } const sample = await prisma.expenseClaim.findFirst({ where: { claimNo: 'BX-20260828-002' }, include: { lines: { orderBy: { sortNo: 'asc' } } }, }); const sum = sample?.lines.reduce((s, l) => s + Number(l.amount), 0); console.log( JSON.stringify( { claims, lines, skipped, sample: sample ? { claimNo: sample.claimNo, header: Number(sample.amount), lineCount: sample.lines.length, lineSum: sum, kinds: sample.lines.map((l) => `${l.kind}:${l.amount}:${l.remark}`), } : null, }, null, 2, ), ); } main() .catch((e) => { console.error(e); process.exit(1); }) .finally(() => prisma.$disconnect());