fix: BCBS MA — identify patient by member ID + DOB, prevent overwriting subscriber

- Added getPatientByInsuranceIdAndDob to storage
- Processor uses insuranceId + DOB as unique key instead of insuranceId alone
- Dependent with same subscriber ID but different DOB gets a new patient record
  (bypasses createOrUpdatePatientByInsuranceId which would overwrite subscriber)
- Name extraction anchored to "Relationship:" to scope Patient Info column only

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
ff
2026-06-01 23:51:11 -04:00
parent 87d7ce9ed9
commit 3ac185b0ec
3 changed files with 87 additions and 31 deletions

View File

@@ -63,10 +63,45 @@ async function processBcbsMaResult(
? seleniumResult.lastName.trim()
: (formLastName ?? "");
await createOrUpdatePatientByInsuranceId({ insuranceId, firstName, lastName, dob: formDob, userId });
const normalizedInsuranceId = insuranceId.replace(/\s+/g, "");
const patient = await storage.getPatientByInsuranceId(normalizedInsuranceId);
// 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);
if (patient) {
// Existing patient found by insuranceId + DOB — update name
log("bcbs-ma-processor", `Patient matched by insuranceId+DOB id=${patient.id}`);
await storage.updatePatient(patient.id, { firstName, lastName });
} else {
// No match — create a brand new patient record.
// Do NOT use createOrUpdatePatientByInsuranceId here because it looks up by
// insuranceId alone and would overwrite the subscriber (e.g. Hugo) instead of
// creating a new record for the dependent (e.g. Maria).
log("bcbs-ma-processor", "No patient matched by insuranceId+DOB — creating new patient record");
try {
await storage.createPatient({
firstName,
lastName,
insuranceId: normalizedInsuranceId,
dateOfBirth: dobDate ?? undefined,
gender: "",
phone: "",
userId,
} as any);
} catch (createErr: any) {
log("bcbs-ma-processor", `createPatient failed: ${createErr?.message}`, createErr);
throw createErr;
}
patient = dobDate
? await storage.getPatientByInsuranceIdAndDob(normalizedInsuranceId, dobDate)
: await storage.getPatientByInsuranceId(normalizedInsuranceId);
}
if (!patient?.id) {
output.patientUpdateStatus = "Patient not found; no update performed";
return output;

View File

@@ -10,6 +10,7 @@ export interface IStorage {
// Patient methods
getPatient(id: number): Promise<Patient | undefined>;
getPatientByInsuranceId(insuranceId: string): Promise<Patient | null>;
getPatientByInsuranceIdAndDob(insuranceId: string, dob: Date): Promise<Patient | null>;
getAllPatients(): Promise<Patient[]>;
getRecentPatients(limit: number, offset: number): Promise<Patient[]>;
getPatientsByIds(ids: number[]): Promise<Patient[]>;
@@ -60,6 +61,19 @@ export const patientsStorage: IStorage = {
});
},
async getPatientByInsuranceIdAndDob(insuranceId: string, dob: Date): Promise<Patient | null> {
const startOfDay = new Date(dob);
startOfDay.setHours(0, 0, 0, 0);
const endOfDay = new Date(dob);
endOfDay.setHours(23, 59, 59, 999);
return db.patient.findFirst({
where: {
insuranceId,
dateOfBirth: { gte: startOfDay, lte: endOfDay },
},
});
},
async getRecentPatients(limit: number, offset: number): Promise<Patient[]> {
return db.patient.findMany({
skip: offset,