feat: BCBS MA persistent session, Expand All fix, address/gender extraction

- Persistent Chrome profile (bcbs_ma_browser_manager) reused across patients;
  login probes /myportal/ to skip login + OTP while the session is active
- Expand All: JS-click the expandCollapseViews span, verify sections opened,
  and unclip fixed-height result tables so the PDF includes every row
- Extract patient address and gender from the results page and save to patient
- Uppercase member IDs (XXA...), matching older lowercase records
- Add /clear-bcbs-ma-session endpoint

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-29 18:27:46 -04:00
co-authored by Claude Opus 5.5
parent 011869f641
commit 13be6e97d5
6 changed files with 474 additions and 89 deletions
@@ -63,20 +63,46 @@ async function processBcbsMaResult(
? seleniumResult.lastName.trim()
: (formLastName ?? "");
const normalizedInsuranceId = insuranceId.replace(/\s+/g, "");
// Address from the Patient Information section — only fields BCBS MA actually returned
const demographicFields: Record<string, string> = {};
for (const key of ["address", "city", "state", "zipCode"] as const) {
const val = seleniumResult?.[key];
if (typeof val === "string" && val.trim()) demographicFields[key] = val.trim();
}
// Gender from the Patient Information section ("M"/"F") → patient form values
const rawGender = String(seleniumResult?.gender ?? "").trim().toUpperCase();
const gender = rawGender.startsWith("M") ? "male" : rawGender.startsWith("F") ? "female" : "";
if (gender) demographicFields.gender = gender;
// BCBS MA member IDs are uppercase (XXA962094414)
const normalizedInsuranceId = insuranceId.replace(/\s+/g, "").toUpperCase();
// Identify patient by member ID + DOB — not member ID alone.
// Dependents (e.g. Maria) share the subscriber's member ID but have a different DOB.
const dobDate = formDob ? new Date(formDob) : null;
let patient = dobDate
? await storage.getPatientByInsuranceIdAndDob(normalizedInsuranceId, dobDate)
: await storage.getPatientByInsuranceId(normalizedInsuranceId);
const findPatient = (id: string) =>
dobDate
? storage.getPatientByInsuranceIdAndDob(id, dobDate)
: storage.getPatientByInsuranceId(id);
// Lookup is case-sensitive — also try lowercase so patients saved before
// the uppercase fix (xxa962094414) are matched instead of duplicated
let patient =
(await findPatient(normalizedInsuranceId)) ??
(await findPatient(normalizedInsuranceId.toLowerCase()));
if (patient) {
// Existing patient found by insuranceId + DOB — update name
// Existing patient found by insuranceId + DOB — update name, address and
// uppercase the stored member ID
log("bcbs-ma-processor", `Patient matched by insuranceId+DOB id=${patient.id}`);
await storage.updatePatient(patient.id, { firstName, lastName });
await storage.updatePatient(patient.id, {
firstName,
lastName,
insuranceId: normalizedInsuranceId,
...demographicFields,
});
} else {
// No match — create a brand new patient record.
// Do NOT use createOrUpdatePatientByInsuranceId here because it looks up by
@@ -91,6 +117,7 @@ async function processBcbsMaResult(
dateOfBirth: dobDate ?? undefined,
gender: "",
phone: "",
...demographicFields,
userId,
} as any);
} catch (createErr: any) {
@@ -53,8 +53,12 @@ router.post("/bcbs-ma-eligibility", async (req: Request, res: Response): Promise
});
}
// BCBS MA member IDs use an uppercase prefix (e.g. XXA962094414)
const memberId = String(rawData.memberId ?? "").trim().toUpperCase();
const enrichedData = {
...rawData,
memberId,
bcbsMaUsername: credentials.username,
bcbsMaPassword: credentials.password,
providerNpi: npiNumber,
@@ -67,13 +71,13 @@ router.post("/bcbs-ma-eligibility", async (req: Request, res: Response): Promise
userId: req.user.id,
socketId,
enrichedPayload: enrichedData,
insuranceId: String(rawData.memberId ?? "").trim(),
insuranceId: memberId,
formFirstName: rawData.firstName,
formLastName: rawData.lastName,
formDob: rawData.dateOfBirth,
});
log("bcbs-ma-route", "job enqueued", { jobId, insuranceId: rawData.memberId });
log("bcbs-ma-route", "job enqueued", { jobId, insuranceId: memberId });
return res.json({ status: "queued", jobId });
} catch (err: any) {
console.error("[bcbs-ma-route] enqueue failed:", err);