feat(ocr) - route fixed, saving done

This commit is contained in:
2025-09-05 00:17:18 +05:30
parent 399c47dcfd
commit 62834f6eb9
11 changed files with 279 additions and 54 deletions

View File

@@ -156,41 +156,56 @@ export default function PaymentOCRBlock() {
const handleSave = async () => {
try {
const payload = rows.map((row) => {
const billed = Number(row["Billed Amount"] ?? 0);
const allowed = Number(row["Allowed Amount"] ?? 0);
const paid = Number(row["Paid Amount"] ?? 0);
const skipped: string[] = [];
return {
patientId: parseInt(row["Patient ID"] as string, 10),
totalBilled: billed,
totalPaid: paid,
totalAdjusted: billed - allowed, // ❗ write-off
totalDue: allowed - paid, // ❗ patient responsibility
notes: `OCR import - CDT ${row["CDT Code"]}, Tooth ${row["Tooth"]}, Date ${row["Date SVC"]}`,
serviceLine: {
const payload = rows
.map((row, idx) => {
const patientName = row["Patient Name"];
const patientId = row["Patient ID"];
const procedureCode = row["CDT Code"];
if (!patientName || !patientId || !procedureCode) {
skipped.push(`Row ${idx + 1} (missing name/id/procedureCode)`);
return null;
}
return {
patientName,
insuranceId: patientId,
icn: row["ICN"] ?? null,
procedureCode: row["CDT Code"],
procedureDate: convertOCRDate(row["Date SVC"]), // youll parse "070825" → proper Date
toothNumber: row["Tooth"],
totalBilled: billed,
totalPaid: paid,
totalAdjusted: billed - allowed,
totalDue: allowed - paid,
},
transaction: {
paidAmount: paid,
adjustedAmount: billed - allowed, // same as totalAdjusted
method: "OTHER", // fallback, since OCR doesnt give this
receivedDate: new Date(),
},
};
});
toothNumber: row["Tooth"] ?? null,
toothSurface: row["Surface"] ?? null,
procedureDate: row["Date SVC"] ?? null,
totalBilled: Number(row["Billed Amount"] ?? 0),
totalAllowed: Number(row["Allowed Amount"] ?? 0),
totalPaid: Number(row["Paid Amount"] ?? 0),
sourceFile: row["Source File"] ?? null,
};
})
.filter((r) => r !== null);
const res = await apiRequest(
"POST",
"/api/payments/ocr",
JSON.stringify({ payments: payload })
);
if (skipped.length > 0) {
toast({
title:
"Some rows skipped, because of either no patient Name or MemberId given.",
description: skipped.join(", "),
variant: "destructive",
});
}
if (payload.length === 0) {
toast({
title: "Error",
description: "No valid rows to save",
variant: "destructive",
});
return;
}
const res = await apiRequest("POST", "/api/payments/full-ocr-import", {
rows: payload,
});
if (!res.ok) throw new Error("Failed to save OCR payments");

View File

@@ -370,6 +370,11 @@ export default function PaymentsRecentTable({
paymentsData?.totalCount || 0
);
const getName = (p: PaymentWithExtras) =>
p.patient
? `${p.patient.firstName} ${p.patient.lastName}`.trim()
: (p.patientName ?? "Unknown");
const getInitials = (fullName: string) => {
const parts = fullName.trim().split(/\s+/);
const filteredParts = parts.filter((part) => part.length > 0);
@@ -480,7 +485,7 @@ export default function PaymentsRecentTable({
<TableHead>Claim ID</TableHead>
<TableHead>Patient Name</TableHead>
<TableHead>Amount</TableHead>
<TableHead>Claim Submitted on</TableHead>
<TableHead>Service Date</TableHead>
<TableHead>Status</TableHead>
<TableHead className="text-right">Actions</TableHead>
</TableRow>
@@ -519,6 +524,14 @@ export default function PaymentsRecentTable({
const totalPaid = Number(payment.totalPaid || 0);
const totalDue = Number(payment.totalDue || 0);
const displayName = getName(payment);
const submittedOn =
payment.serviceLines?.[0]?.procedureDate ??
payment.claim?.createdAt ??
payment.createdAt ??
payment.serviceLineTransactions?.[0]?.receivedDate ??
null;
return (
<TableRow key={payment.id}>
{allowCheckbox && (
@@ -547,13 +560,13 @@ export default function PaymentsRecentTable({
className={`h-10 w-10 ${getAvatarColor(Number(payment.id))}`}
>
<AvatarFallback className="text-white">
{getInitials(payment.patientName)}
{getInitials(displayName)}
</AvatarFallback>
</Avatar>
<div className="ml-4">
<div className="text-sm font-medium text-gray-900">
{payment.patientName}
{displayName}
</div>
<div className="text-sm text-gray-500">
PID-{payment.patientId?.toString().padStart(4, "0")}
@@ -585,7 +598,7 @@ export default function PaymentsRecentTable({
</div>
</TableCell>
<TableCell>
{formatDateToHumanReadable(payment.paymentDate)}
{formatDateToHumanReadable(submittedOn)}
</TableCell>
<TableCell>