fix: reliability fixes for UHC SCO and CMSP eligibility checks
- unitedSCOEligibilityProcessor: overwrite patient's stored insuranceId when
the eligibility page reports a different (updated) Member ID than the one
searched with.
- CMSP worker: add AngularJS late-binding retry to results-table extraction
(ported from the MH worker's a0417653 fix), since every patient now routes
through this worker for the accumulator check.
- cmspEligibilityHistoryRemainingProcessor: stop forcing patient status to
UNKNOWN when extraction fails; a failed run landing after a successful one
was clobbering a correct ACTIVE/INACTIVE status.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -101,11 +101,16 @@ export async function runCmspEligibilityHistoryRemainingProcessor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (patient && patient.id !== undefined) {
|
if (patient && patient.id !== undefined) {
|
||||||
let newStatus = "UNKNOWN";
|
// eligibility is null when extraction found no matching row (e.g. the
|
||||||
|
// AngularJS grid didn't finish rendering in time). Leave status untouched
|
||||||
|
// in that case rather than forcing UNKNOWN, so a failed run can't clobber
|
||||||
|
// a correct ACTIVE/INACTIVE status set by another (e.g. concurrent) run.
|
||||||
|
let newStatus: string | null = null;
|
||||||
if (seleniumResult.eligibility === "Y") newStatus = "ACTIVE";
|
if (seleniumResult.eligibility === "Y") newStatus = "ACTIVE";
|
||||||
else if (seleniumResult.eligibility === "N") newStatus = "INACTIVE";
|
else if (seleniumResult.eligibility === "N") newStatus = "INACTIVE";
|
||||||
|
|
||||||
const updates: any = { status: newStatus };
|
const updates: any = {};
|
||||||
|
if (newStatus) updates.status = newStatus;
|
||||||
if (seleniumInsurance) updates.insuranceProvider = seleniumInsurance;
|
if (seleniumInsurance) updates.insuranceProvider = seleniumInsurance;
|
||||||
|
|
||||||
const remainingBenefit = seleniumResult.remaining_amount?.trim() || null;
|
const remainingBenefit = seleniumResult.remaining_amount?.trim() || null;
|
||||||
@@ -118,8 +123,12 @@ export async function runCmspEligibilityHistoryRemainingProcessor(
|
|||||||
updates.thirdPartyInsurance = seleniumResult.third_party_insurance?.trim() || null;
|
updates.thirdPartyInsurance = seleniumResult.third_party_insurance?.trim() || null;
|
||||||
}
|
}
|
||||||
|
|
||||||
await storage.updatePatient(patient.id, updates);
|
if (Object.keys(updates).length > 0) {
|
||||||
outputResult.patientUpdateStatus = `Patient status updated to ${newStatus}`;
|
await storage.updatePatient(patient.id, updates);
|
||||||
|
}
|
||||||
|
outputResult.patientUpdateStatus = newStatus
|
||||||
|
? `Patient status updated to ${newStatus}`
|
||||||
|
: "Eligibility unknown — status not changed";
|
||||||
outputResult.remainingBenefit = remainingBenefit;
|
outputResult.remainingBenefit = remainingBenefit;
|
||||||
outputResult.thirdPartyInsurance = updates.thirdPartyInsurance ?? null;
|
outputResult.thirdPartyInsurance = updates.thirdPartyInsurance ?? null;
|
||||||
|
|
||||||
|
|||||||
@@ -108,16 +108,31 @@ async function processUnitedSCOResult(
|
|||||||
eligStatus === "active" || eligStatus === "y" ? "ACTIVE" :
|
eligStatus === "active" || eligStatus === "y" ? "ACTIVE" :
|
||||||
eligStatus === "inactive" ? "INACTIVE" : null;
|
eligStatus === "inactive" ? "INACTIVE" : null;
|
||||||
|
|
||||||
if (newStatus) {
|
const statusUpdates: any = { insuranceProvider: "United Healthcare SCO" };
|
||||||
await storage.updatePatient(patient.id, {
|
if (newStatus) statusUpdates.status = newStatus;
|
||||||
status: newStatus,
|
|
||||||
insuranceProvider: "United Healthcare SCO",
|
// The eligibility page can report a different (updated) Member ID than the
|
||||||
|
// one we searched with — e.g. UHC MA reissues IDs. Overwrite the patient's
|
||||||
|
// stored insuranceId so future lookups use the current one.
|
||||||
|
const scrapedMemberId =
|
||||||
|
typeof seleniumResult?.memberId === "string"
|
||||||
|
? seleniumResult.memberId.replace(/\s+/g, "")
|
||||||
|
: null;
|
||||||
|
if (scrapedMemberId && scrapedMemberId !== insuranceId) {
|
||||||
|
statusUpdates.insuranceId = scrapedMemberId;
|
||||||
|
log("unitedsco-processor", "member ID changed, overwriting on patient record", {
|
||||||
|
patientId: patient.id,
|
||||||
|
oldInsuranceId: insuranceId,
|
||||||
|
newInsuranceId: scrapedMemberId,
|
||||||
});
|
});
|
||||||
output.patientUpdateStatus = `Patient status updated to ${newStatus}`;
|
}
|
||||||
} else {
|
|
||||||
// Unknown eligibility — still update insuranceProvider but leave status unchanged
|
await storage.updatePatient(patient.id, statusUpdates);
|
||||||
await storage.updatePatient(patient.id, { insuranceProvider: "United Healthcare SCO" });
|
output.patientUpdateStatus = newStatus
|
||||||
output.patientUpdateStatus = `Eligibility unknown — status not changed`;
|
? `Patient status updated to ${newStatus}`
|
||||||
|
: `Eligibility unknown — status not changed`;
|
||||||
|
if (statusUpdates.insuranceId) {
|
||||||
|
output.patientUpdateStatus += ` (Member ID updated from ${insuranceId} to ${statusUpdates.insuranceId})`;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5) Resolve PDF buffer from file path (same as DDMA)
|
// 5) Resolve PDF buffer from file path (same as DDMA)
|
||||||
|
|||||||
@@ -181,30 +181,38 @@ class AutomationCMSPEligibilityHistoryRemainingCheck:
|
|||||||
(By.XPATH, "//h4[text()='Eligible' or text()='Ineligible']/following::table[1]/tbody/tr")
|
(By.XPATH, "//h4[text()='Eligible' or text()='Ineligible']/following::table[1]/tbody/tr")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
for status_label, elig_flag in [("Eligible", "Y"), ("Ineligible", "N")]:
|
# AngularJS populates cell text after elements appear in the DOM.
|
||||||
rows = self.driver.find_elements(
|
# Retry a few times until a row with non-empty cell[2] is found.
|
||||||
By.XPATH,
|
for attempt in range(4):
|
||||||
f"//h4[text()='{status_label}']/following::table[1]/tbody/tr"
|
for status_label, elig_flag in [("Eligible", "Y"), ("Ineligible", "N")]:
|
||||||
)
|
rows = self.driver.find_elements(
|
||||||
for row in rows:
|
By.XPATH,
|
||||||
cells = row.find_elements(By.TAG_NAME, "td")
|
f"//h4[text()='{status_label}']/following::table[1]/tbody/tr"
|
||||||
if len(cells) < 3:
|
)
|
||||||
continue
|
for row in rows:
|
||||||
norm_cell = self._normalize_id(self._cell_text(cells[2]))
|
cells = row.find_elements(By.TAG_NAME, "td")
|
||||||
norm_self = self._normalize_id(self.memberId)
|
if len(cells) < 3:
|
||||||
if norm_self and norm_cell and (norm_self in norm_cell or norm_cell in norm_self):
|
continue
|
||||||
full_name = self._cell_text(cells[4]) if len(cells) > 4 else ""
|
norm_cell = self._normalize_id(self._cell_text(cells[2]))
|
||||||
plan_name = (
|
norm_self = self._normalize_id(self.memberId)
|
||||||
self._cell_text(cells[6]) if len(cells) > 6
|
if norm_self and norm_cell and (norm_self in norm_cell or norm_cell in norm_self):
|
||||||
else (self._cell_text(cells[-1]) if len(cells) > 4 else "")
|
full_name = self._cell_text(cells[4]) if len(cells) > 4 else ""
|
||||||
)
|
plan_name = (
|
||||||
name_parts = full_name.split()
|
self._cell_text(cells[6]) if len(cells) > 6
|
||||||
return {
|
else (self._cell_text(cells[-1]) if len(cells) > 4 else "")
|
||||||
"eligibility": elig_flag,
|
)
|
||||||
"firstName": name_parts[0] if name_parts else "",
|
name_parts = full_name.split()
|
||||||
"lastName": " ".join(name_parts[1:]) if len(name_parts) > 1 else "",
|
return {
|
||||||
"insurance": plan_name,
|
"eligibility": elig_flag,
|
||||||
}
|
"firstName": name_parts[0] if name_parts else "",
|
||||||
|
"lastName": " ".join(name_parts[1:]) if len(name_parts) > 1 else "",
|
||||||
|
"insurance": plan_name,
|
||||||
|
}
|
||||||
|
if attempt < 3:
|
||||||
|
print(f"[extraction] cells not yet populated (attempt {attempt + 1}), retrying...")
|
||||||
|
time.sleep(1.5)
|
||||||
|
|
||||||
|
print(f"[extraction] No matching row for memberId='{self.memberId}'")
|
||||||
return {"eligibility": None}
|
return {"eligibility": None}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print("Extraction error:", e)
|
print("Extraction error:", e)
|
||||||
|
|||||||
Reference in New Issue
Block a user