fix: D0140 claim sync, schedule column prefill, multi-appt, DentaQuest OTP session

- Fix Express route ordering in appointments-procedures so /prefill-from-appointment
  is matched before /:id (D0140 and other codes now always reach the claim)
- claim-form: always fetch AppointmentProcedure records when an existing claim loads
  so post-save procedure edits (e.g. adding D0140) are reflected immediately
- appointments-page: replace sessionStorage with React state (newApptPrefill) for
  slot-click prefill so columns B-F correctly carry their staffId into the form
- add-appointment-modal / appointment-form: thread prefillData prop; add
  NewAppointmentPrefill interface; useEffect applies values via setValue
- appointments upsert: remove per-patient dedup so the same patient can have
  multiple appointments on the same day in the same column
- DentaQuest / TuftsSCO: navigate to about:blank and minimize instead of
  quit_driver after each run — session cookie stays in memory so OTP is only
  required once per app startup, not on every eligibility or claim check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-05-28 15:39:36 -04:00
parent 0b3cc241bf
commit b20dc8e976
8 changed files with 181 additions and 281 deletions

View File

@@ -331,26 +331,54 @@ export function ClaimForm({
} catch {}
}
// Restore service lines
const mappedLines = (claim.serviceLines ?? []).map((sl: any) => ({
procedureCode: sl.procedureCode ?? "",
procedureDate: sl.procedureDate
? String(sl.procedureDate).split("T")[0]
: claimDate,
quad: sl.quad ?? "",
arch: sl.arch ?? "",
toothNumber: sl.toothNumber ?? "",
toothSurface: sl.toothSurface ?? "",
totalBilled: new Decimal(Number(sl.totalBilled ?? 0)),
totalAdjusted: new Decimal(Number(sl.totalAdjusted ?? 0)),
totalPaid: new Decimal(Number(sl.totalPaid ?? 0)),
}));
// Prefer AppointmentProcedure records for service lines — they reflect any
// updates the user made in the Select Procedure dialog after the claim was saved.
let serviceLines: InputServiceLine[] = [];
try {
const procRes = await apiRequest(
"GET",
`/api/appointment-procedures/prefill-from-appointment/${appointmentId}`,
);
if (procRes.ok) {
const procData = await procRes.json();
if ((procData.procedures || []).length > 0) {
serviceLines = (procData.procedures as any[]).map((p) => ({
procedureCode: p.procedureCode ?? "",
procedureDate: claimDate || serviceDate,
quad: p.quad || "",
arch: p.arch || "",
toothNumber: p.toothNumber || "",
toothSurface: p.toothSurface || "",
totalBilled: new Decimal(Number(p.fee ?? 0)),
totalAdjusted: new Decimal(0),
totalPaid: new Decimal(0),
}));
}
}
} catch {}
// Fall back to the claim's own service lines if no AppointmentProcedure records exist
if (serviceLines.length === 0) {
serviceLines = (claim.serviceLines ?? []).map((sl: any) => ({
procedureCode: sl.procedureCode ?? "",
procedureDate: sl.procedureDate
? String(sl.procedureDate).split("T")[0]
: claimDate,
quad: sl.quad ?? "",
arch: sl.arch ?? "",
toothNumber: sl.toothNumber ?? "",
toothSurface: sl.toothSurface ?? "",
totalBilled: new Decimal(Number(sl.totalBilled ?? 0)),
totalAdjusted: new Decimal(Number(sl.totalAdjusted ?? 0)),
totalPaid: new Decimal(Number(sl.totalPaid ?? 0)),
}));
}
setForm((prev) => ({
...prev,
claimId: claim.id,
serviceDate: claimDate || prev.serviceDate,
serviceLines: mappedLines.length > 0 ? mappedLines : prev.serviceLines,
serviceLines: serviceLines.length > 0 ? serviceLines : prev.serviceLines,
remarks: claim.remarks ?? "",
missingTeethStatus: (claim.missingTeethStatus as MissingTeethStatus) ?? "No_missing",
missingTeeth: (claim.missingTeeth as Record<string, "X" | "O">) ?? {},
@@ -2168,10 +2196,10 @@ export function ClaimForm({
</h3>
{proceduresOnly ? (
<div className="flex justify-center gap-3">
<Button className="w-40" variant="default" onClick={handleProceduresSave}>
<Button className="w-40" variant="default" onClick={() => runWithPriceCheck(handleProceduresSave)}>
Save Procedures
</Button>
<Button className="w-40" variant="secondary" onClick={handleProceduresUpdate}>
<Button className="w-40" variant="secondary" onClick={() => runWithPriceCheck(handleProceduresUpdate)}>
Update &amp; Resubmit
</Button>
<Button className="w-28" variant="destructive" onClick={handleProceduresVoid}>