feat: Select Procedures flow, batch-column NPI provider fix, auto PDF save

- Add 'Select Procedures' right-click option on appointment page (separate from Claims/PreAuth)
- Select Procedures form saves CDT codes + NPI provider to AppointmentProcedure storage
- Remove Save button from insurance claim form; Claims/PreAuth opens for insurance submission only
- Claims/PreAuth auto-prefills from saved procedures including NPI provider
- Batch-column: procedures npiProviderId takes priority over stale claim npiProviderId
- Batch-column: auto-save PDF to patient Documents after successful submission (no socket needed)
- Add npiProviderId column to AppointmentProcedure table (prisma db push)
- Fix 'invalid db creation invocation': guard staffId, npiProviderId, procedureDate as Date object, totalBilled NaN guard
- Add full error logging to batch-column catch block

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Gitead
2026-04-27 00:25:24 -04:00
co-authored by Claude Sonnet 4.6
parent a279a3e7c1
commit 3e899376c3
838 changed files with 28488 additions and 773 deletions
@@ -54,6 +54,71 @@ router.get(
}
);
/**
* PUT /api/appointment-procedures/set-npi-provider/:appointmentId
* Set the npiProviderId on all procedures for an appointment (lightweight update).
*/
router.put("/set-npi-provider/:appointmentId", async (req: Request, res: Response) => {
try {
const appointmentId = Number(req.params.appointmentId);
if (isNaN(appointmentId)) return res.status(400).json({ message: "Invalid appointmentId" });
const npiProviderId = req.body.npiProviderId != null ? Number(req.body.npiProviderId) : null;
await prisma.appointmentProcedure.updateMany({
where: { appointmentId },
data: { npiProviderId: npiProviderId ?? null },
});
return res.json({ success: true });
} catch (err: any) {
console.error("set-npi-provider error", err);
return res.status(500).json({ message: err.message ?? "Server error" });
}
});
/**
* POST /api/appointment-procedures/save-for-appointment
* Replace all procedures for an appointment (clear + insert).
* Body: { appointmentId, patientId, npiProviderId?, procedures: [{procedureCode, fee?, toothNumber?, toothSurface?}] }
*/
router.post("/save-for-appointment", async (req: Request, res: Response) => {
try {
const { appointmentId, patientId, npiProviderId, procedures } = req.body;
if (!appointmentId || isNaN(Number(appointmentId))) {
return res.status(400).json({ message: "Invalid appointmentId" });
}
if (!patientId || isNaN(Number(patientId))) {
return res.status(400).json({ message: "Invalid patientId" });
}
if (!Array.isArray(procedures)) {
return res.status(400).json({ message: "procedures must be an array" });
}
const filtered = (procedures as any[]).filter(
(p) => String(p.procedureCode ?? "").trim() !== ""
);
const count = await storage.saveForAppointment({
appointmentId: Number(appointmentId),
patientId: Number(patientId),
npiProviderId: npiProviderId ? Number(npiProviderId) : null,
procedures: filtered.map((p) => ({
procedureCode: String(p.procedureCode).trim(),
fee: p.fee != null ? Number(p.fee) : null,
toothNumber: p.toothNumber || null,
toothSurface: p.toothSurface || null,
})),
});
return res.json({ success: true, count });
} catch (err: any) {
console.error("save-for-appointment error", err);
return res.status(500).json({ message: err.message ?? "Server error" });
}
});
/**
* POST /api/appointment-procedures
* Add single manual procedure