Files
Wellspringmanagement01/apps/Backend/src/queue/workers/seleniumWorker.ts
T
GiteadandClaude Sonnet 4.6 3e899376c3 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>
2026-04-27 00:25:24 -04:00

107 lines
3.1 KiB
TypeScript

import { Worker, Job } from "bullmq";
import { redisConnection } from "../connection";
import { SeleniumJobData } from "../queues";
import { io } from "../../socket";
import { runEligibilityProcessor } from "../processors/eligibilityProcessor";
import { runClaimStatusProcessor } from "../processors/claimStatusProcessor";
import { runClaimSubmitProcessor } from "../processors/claimSubmitProcessor";
/**
* Emit a job-status event to the socket that enqueued the job (if any).
* Falls back to broadcasting when socketId is absent.
*/
function emitJobUpdate(
socketId: string | undefined,
jobId: string,
jobType: string,
status: "active" | "completed" | "failed",
payload: Record<string, any>
) {
const event = "job:update";
const data = { jobId, jobType, status, ...payload };
if (socketId && io) {
io.to(socketId).emit(event, data);
} else if (io) {
io.emit(event, data);
}
}
async function processSeleniumJob(job: Job<SeleniumJobData>) {
const { jobType, userId, socketId, enrichedPayload } = job.data;
const jobId = job.id ?? job.name;
emitJobUpdate(socketId, jobId, jobType, "active", {
message: "Selenium browser starting…",
});
try {
let result: any;
if (jobType === "eligibility-check") {
result = await runEligibilityProcessor({
enrichedPayload,
userId,
insuranceId: job.data.insuranceId!,
formFirstName: job.data.formFirstName,
formLastName: job.data.formLastName,
formDob: job.data.formDob,
});
} else if (jobType === "claim-status-check") {
result = await runClaimStatusProcessor({
enrichedPayload,
insuranceId: job.data.insuranceId!,
});
} else if (jobType === "claim-submit") {
result = await runClaimSubmitProcessor({
enrichedPayload,
files: job.data.files ?? [],
claimId: job.data.claimId,
variant: "claimsubmit",
socketId,
});
} else if (jobType === "claim-pre-auth") {
result = await runClaimSubmitProcessor({
enrichedPayload,
files: job.data.files ?? [],
claimId: job.data.claimId,
variant: "claim-pre-auth",
socketId,
});
} else {
throw new Error(`Unknown selenium jobType: ${jobType}`);
}
emitJobUpdate(socketId, jobId, jobType, "completed", { result });
return result;
} catch (err: any) {
const errorMsg = err?.message ?? String(err);
emitJobUpdate(socketId, jobId, jobType, "failed", { error: errorMsg });
throw err; // let BullMQ mark job as failed / retry
}
}
export function startSeleniumWorker() {
const worker = new Worker<SeleniumJobData>(
"selenium-jobs",
processSeleniumJob,
{
connection: redisConnection,
concurrency: 1, // mirror the Python semaphore(1) — 1 browser at a time
}
);
worker.on("completed", (job) => {
console.log(`[seleniumWorker] job ${job.id} (${job.data.jobType}) completed`);
});
worker.on("failed", (job, err) => {
console.error(
`[seleniumWorker] job ${job?.id} (${job?.data.jobType}) failed:`,
err.message
);
});
console.log("[seleniumWorker] started");
return worker;
}