fix: fix remote browser socket connection and related updates

This commit is contained in:
Gitead
2026-04-30 11:52:58 -04:00
parent 441cfcc8e3
commit d8f852741a
959 changed files with 13338 additions and 2208 deletions

View File

@@ -84,7 +84,7 @@ router.put("/set-npi-provider/:appointmentId", async (req: Request, res: Respons
*/
router.post("/save-for-appointment", async (req: Request, res: Response) => {
try {
const { appointmentId, patientId, npiProviderId, procedures } = req.body;
const { appointmentId, patientId, npiProviderId, procedures, attachments } = req.body;
if (!appointmentId || isNaN(Number(appointmentId))) {
return res.status(400).json({ message: "Invalid appointmentId" });
@@ -100,6 +100,14 @@ router.post("/save-for-appointment", async (req: Request, res: Response) => {
(p) => String(p.procedureCode ?? "").trim() !== ""
);
const validAttachments = Array.isArray(attachments)
? (attachments as any[]).filter((a) => a?.filename).map((a) => ({
filename: String(a.filename),
mimeType: a.mimeType ?? null,
filePath: a.filePath ?? null,
}))
: undefined;
const count = await storage.saveForAppointment({
appointmentId: Number(appointmentId),
patientId: Number(patientId),
@@ -110,6 +118,7 @@ router.post("/save-for-appointment", async (req: Request, res: Response) => {
toothNumber: p.toothNumber || null,
toothSurface: p.toothSurface || null,
})),
attachments: validAttachments,
});
return res.json({ success: true, count });

View File

@@ -425,18 +425,25 @@ router.post(
// Fetch active claim for this appointment (includes service lines from draft saves)
const activeClaim = await storage.getActiveClaimByAppointmentId(Number(apt.id));
// "Already claimed" = has a real claim number OR status is REVIEW/APPROVED
// A PENDING claim with no claimNumber is just a draft save — not yet submitted
const alreadyClaimed =
// Skip if claim was voided via the "Void" button in Select Procedures.
if (activeClaim?.status === "VOID") {
resultItem.skipped = true;
resultItem.error = "Voided";
results.push(resultItem);
continue;
}
// Skip appointments whose claim was already submitted (has claimNumber or REVIEW/APPROVED).
// The "Update & Resubmit" button resets the claim to PENDING so it is picked up again.
const alreadySubmitted =
activeClaim &&
((activeClaim.claimNumber != null &&
String(activeClaim.claimNumber).trim() !== "") ||
((activeClaim.claimNumber != null && String(activeClaim.claimNumber).trim() !== "") ||
activeClaim.status === "REVIEW" ||
activeClaim.status === "APPROVED");
if (alreadyClaimed) {
if (alreadySubmitted) {
resultItem.skipped = true;
resultItem.error = "Already claimed";
resultItem.error = "Already submitted";
results.push(resultItem);
continue;
}
@@ -544,7 +551,6 @@ router.post(
let claimId: number;
if (activeClaim?.id) {
claimId = activeClaim.id;
// Update claim's npiProviderId if the user chose a different provider via Select Procedures
if (procNpiProviderId && activeClaim.npiProviderId !== procNpiProviderId) {
await storage.updateClaim(claimId, { npiProviderId: procNpiProviderId });
}
@@ -634,12 +640,31 @@ router.post(
};
}
// Collect attachments: appointment-level files + claim-level files
const apptFiles = await storage.getAppointmentFiles(Number(apt.id));
const claimFiles = (activeClaim as any)?.claimFiles ?? [];
const allFileMeta = [
...apptFiles,
...claimFiles,
] as Array<{ filename: string; mimeType?: string | null; filePath?: string | null }>;
const filesForQueue = allFileMeta.flatMap((f) => {
if (!f.filePath) return [];
const absPath = path.join(process.cwd(), f.filePath);
if (!fs.existsSync(absPath)) {
console.warn(`[batch-column] attachment not found on disk: ${absPath}`);
return [];
}
const bufferBase64 = fs.readFileSync(absPath).toString("base64");
return [{ originalname: f.filename, bufferBase64, mimetype: f.mimeType ?? "application/octet-stream" }];
});
// Enqueue selenium claim-submit job
const job = await seleniumQueue.add("claim-submit", {
jobType: "claim-submit",
userId: req.user.id,
enrichedPayload,
files: [],
files: filesForQueue,
claimId,
});
@@ -991,4 +1016,72 @@ router.delete("/:id", async (req: Request, res: Response): Promise<any> => {
}
});
// POST /api/claims/void-for-appointment
// Marks the claim for an appointment as VOID so batch-column skips it permanently.
// If no claim exists yet, creates a minimal placeholder VOID claim.
router.post("/void-for-appointment", async (req: Request, res: Response): Promise<any> => {
try {
if (!req.user?.id) return res.status(401).json({ error: "Unauthorized" });
const { appointmentId } = req.body;
if (!appointmentId || isNaN(Number(appointmentId))) {
return res.status(400).json({ error: "Invalid appointmentId" });
}
const existing = await storage.getActiveClaimByAppointmentId(Number(appointmentId));
if (existing) {
await storage.updateClaim(Number(existing.id), { status: "VOID" } as any);
return res.json({ voided: true, claimId: existing.id });
}
// No claim yet — look up appointment + patient to create a minimal VOID placeholder
const apt = await storage.getAppointment(Number(appointmentId));
if (!apt) return res.status(404).json({ error: "Appointment not found" });
const patient = apt.patientId ? await storage.getPatient(apt.patientId) : null;
if (!patient) return res.status(404).json({ error: "Patient not found" });
const newClaim = await storage.createClaim({
patientId: Number(patient.id),
appointmentId: Number(appointmentId),
userId: req.user.id,
staffId: Number(apt.staffId),
patientName: `${patient.firstName ?? ""} ${patient.lastName ?? ""}`.trim(),
memberId: String(patient.insuranceId ?? ""),
dateOfBirth: patient.dateOfBirth ? new Date(patient.dateOfBirth) : new Date(),
serviceDate: apt.date instanceof Date ? apt.date : new Date(apt.date as any),
insuranceProvider: "MassHealth",
remarks: "",
missingTeethStatus: "No_missing",
missingTeeth: {},
status: "VOID",
} as any);
return res.json({ voided: true, claimId: newClaim.id });
} catch (err: any) {
console.error("void-for-appointment error", err);
return res.status(500).json({ error: err.message ?? "Server error" });
}
});
// POST /api/claims/reset-for-resubmit
// Resets the active claim for an appointment back to PENDING with no claimNumber,
// so the batch-column will pick it up again on the next run.
router.post("/reset-for-resubmit", async (req: Request, res: Response): Promise<any> => {
try {
const { appointmentId } = req.body;
if (!appointmentId || isNaN(Number(appointmentId))) {
return res.status(400).json({ error: "Invalid appointmentId" });
}
const claim = await storage.getActiveClaimByAppointmentId(Number(appointmentId));
if (!claim) {
return res.json({ reset: false, message: "No existing claim found — will be created fresh on next submit" });
}
await storage.updateClaim(Number(claim.id), { status: "PENDING", claimNumber: null } as any);
return res.json({ reset: true, claimId: claim.id });
} catch (err: any) {
console.error("reset-for-resubmit error", err);
return res.status(500).json({ error: err.message ?? "Server error" });
}
});
export default router;