fix: DDMA claim checkbox, eligibility patient name, and ID+DOB patient lookup
- Claim step2: robust member checkbox selection with page-wide fallback - Eligibility: take patient name from 'Benefits for' heading, not subscriber name - Processor: update the patient just created/matched by ID+DOB, not first ID match Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -167,12 +167,17 @@ export async function createOrUpdatePatientByInsuranceId(options: {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await storage.createPatient(patientData);
|
const created = await storage.createPatient(patientData);
|
||||||
console.log(`[createOrUpdatePatient] patient created successfully for insuranceId="${normalizedId}"`);
|
console.log(`[createOrUpdatePatient] patient created successfully for insuranceId="${normalizedId}"`);
|
||||||
|
// Return the record just created — family members share an insuranceId, so an
|
||||||
|
// id-only lookup could return a different member.
|
||||||
|
if (created?.id) return created;
|
||||||
} catch (dbErr: any) {
|
} catch (dbErr: any) {
|
||||||
console.error(`[createOrUpdatePatient] DB create failed:`, dbErr?.message ?? dbErr);
|
console.error(`[createOrUpdatePatient] DB create failed:`, dbErr?.message ?? dbErr);
|
||||||
throw dbErr;
|
throw dbErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
return storage.getPatientByInsuranceId(normalizedId);
|
return dobDate
|
||||||
|
? storage.getPatientByInsuranceIdAndDob(normalizedId, dobDate)
|
||||||
|
: storage.getPatientByInsuranceId(normalizedId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ async function processDdmaResult(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 2) Create / update patient
|
// 2) Create / update patient
|
||||||
await createOrUpdatePatientByInsuranceId({
|
const upsertedPatient = await createOrUpdatePatientByInsuranceId({
|
||||||
insuranceId,
|
insuranceId,
|
||||||
firstName,
|
firstName,
|
||||||
lastName,
|
lastName,
|
||||||
@@ -129,7 +129,14 @@ async function processDdmaResult(
|
|||||||
|
|
||||||
// 3) Fetch patient (needed for ID)
|
// 3) Fetch patient (needed for ID)
|
||||||
const normalizedInsuranceId = insuranceId.replace(/\s+/g, "");
|
const normalizedInsuranceId = insuranceId.replace(/\s+/g, "");
|
||||||
const patient = await storage.getPatientByInsuranceId(normalizedInsuranceId);
|
// Use the patient just created/updated (family members share an insuranceId, so an
|
||||||
|
// id-only lookup could return a different member).
|
||||||
|
const dobForLookup = formDob ? new Date(formDob) : null;
|
||||||
|
const patient =
|
||||||
|
upsertedPatient ??
|
||||||
|
(dobForLookup && !isNaN(dobForLookup.getTime())
|
||||||
|
? await storage.getPatientByInsuranceIdAndDob(normalizedInsuranceId, dobForLookup)
|
||||||
|
: await storage.getPatientByInsuranceId(normalizedInsuranceId));
|
||||||
log("ddma-processor", `patient lookup after create: ${patient ? `id=${patient.id}` : "NOT FOUND"} for insuranceId="${normalizedInsuranceId}"`);
|
log("ddma-processor", `patient lookup after create: ${patient ? `id=${patient.id}` : "NOT FOUND"} for insuranceId="${normalizedInsuranceId}"`);
|
||||||
if (!patient?.id) {
|
if (!patient?.id) {
|
||||||
output.patientUpdateStatus = "Patient not found; no update performed";
|
output.patientUpdateStatus = "Patient not found; no update performed";
|
||||||
|
|||||||
@@ -397,8 +397,33 @@ class AutomationDDMAClaimSubmit:
|
|||||||
return "ERROR: step2 failed: no family member rows found"
|
return "ERROR: step2 failed: no family member rows found"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
checkbox = target_row.find_element(By.XPATH, ".//i[contains(@class,'fa-check-square')]")
|
# The unchecked box may render as an <i> icon (fa-square / fa-square-o /
|
||||||
checkbox.click()
|
# fa-check-square), a native/ARIA checkbox, or just a clickable first cell.
|
||||||
|
checkbox = None
|
||||||
|
for xp in (
|
||||||
|
".//i[contains(@class,'fa-check-square') or contains(@class,'fa-square')]",
|
||||||
|
".//input[@type='checkbox']",
|
||||||
|
".//*[@role='checkbox']",
|
||||||
|
"./td[1]//i",
|
||||||
|
"./td[1]//*[self::span or self::label or self::div]",
|
||||||
|
"./td[1]",
|
||||||
|
):
|
||||||
|
found = target_row.find_elements(By.XPATH, xp)
|
||||||
|
if found:
|
||||||
|
checkbox = found[0]
|
||||||
|
break
|
||||||
|
if checkbox is None:
|
||||||
|
# Icon may render late or outside the <tr> (e.g. a fixed-column table)
|
||||||
|
checkbox = WebDriverWait(self.driver, 10).until(
|
||||||
|
EC.presence_of_element_located((
|
||||||
|
By.XPATH,
|
||||||
|
"//i[contains(@class,'far') and (contains(@class,'fa-check-square') or contains(@class,'fa-square'))]",
|
||||||
|
))
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
checkbox.click()
|
||||||
|
except Exception:
|
||||||
|
self.driver.execute_script("arguments[0].click();", checkbox)
|
||||||
print("[DDMA Claim step2] Checked the matching family member row")
|
print("[DDMA Claim step2] Checked the matching family member row")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[DDMA Claim step2] Warning: Could not click member checkbox: {e}")
|
print(f"[DDMA Claim step2] Warning: Could not click member checkbox: {e}")
|
||||||
|
|||||||
@@ -542,9 +542,21 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
return False
|
return False
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
# Preferred: the "Benefits for JOSHUA ZHANG" heading names the actual
|
||||||
|
# patient. "Subscriber name:" is the policy holder (e.g. a parent), so
|
||||||
|
# it is only used below if this heading is missing.
|
||||||
|
for line in page_lines:
|
||||||
|
heading = re.match(r'^benefits\s+for\s+(.+)$', line, re.IGNORECASE)
|
||||||
|
if heading and looks_like_name(heading.group(1)):
|
||||||
|
patientName = heading.group(1).strip()
|
||||||
|
print(f"[DDMA step2] Extracted name from 'Benefits for' heading: '{patientName}'")
|
||||||
|
break
|
||||||
|
|
||||||
# Scan lines for the "Name" label. When "Name" appears alone we scan
|
# Scan lines for the "Name" label. When "Name" appears alone we scan
|
||||||
# forward past accessibility text to the first plausible name value.
|
# forward past accessibility text to the first plausible name value.
|
||||||
for i, line in enumerate(page_lines):
|
for i, line in enumerate(page_lines):
|
||||||
|
if patientName:
|
||||||
|
break
|
||||||
# Case 1: "Name : Value" or "Name: Value" on the same line
|
# Case 1: "Name : Value" or "Name: Value" on the same line
|
||||||
# (DDMA's detail page labels this "Subscriber name:", so that
|
# (DDMA's detail page labels this "Subscriber name:", so that
|
||||||
# prefix must be accepted rather than excluded)
|
# prefix must be accepted rather than excluded)
|
||||||
|
|||||||
Reference in New Issue
Block a user