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:
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user