feat: United SCO claim worker rewrite + eligibility/patient-table fixes
- Rewrote UnitedDH claim worker to navigate via eligibility page → Selected Patient → Submit Claim button flow - Updated helpers_uniteddh_claim.py step names to match new 9-step workflow - Changed payer selection in both eligibility and claim workers to type + Enter - Updated patient table column header from 'DOB / Gender' to 'DOB' Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -972,7 +972,7 @@ export function PatientTable({
|
|||||||
<TableRow>
|
<TableRow>
|
||||||
{allowCheckbox && <TableHead>Select</TableHead>}
|
{allowCheckbox && <TableHead>Select</TableHead>}
|
||||||
<TableHead>Patient</TableHead>
|
<TableHead>Patient</TableHead>
|
||||||
<TableHead>DOB / Gender</TableHead>
|
<TableHead>DOB</TableHead>
|
||||||
{/*<TableHead>Contact</TableHead>*/}
|
{/*<TableHead>Contact</TableHead>*/}
|
||||||
<TableHead>Insurance</TableHead>
|
<TableHead>Insurance</TableHead>
|
||||||
<TableHead>Status</TableHead>
|
<TableHead>Status</TableHead>
|
||||||
|
|||||||
@@ -250,13 +250,14 @@ async def start_uniteddh_claim_run(sid: str, data: dict, url: str):
|
|||||||
|
|
||||||
# --- Claim steps ---
|
# --- Claim steps ---
|
||||||
for step_name, step_fn in [
|
for step_name, step_fn in [
|
||||||
("step1_search_patient", bot.step1_search_patient),
|
("step1_search_patient", bot.step1_search_patient),
|
||||||
("step2_open_member_page", bot.step2_open_member_page),
|
("step2_click_submit_claim", bot.step2_click_submit_claim),
|
||||||
("step3_click_create_claim", bot.step3_click_create_claim),
|
("step3_continue_prefilled", bot.step3_continue_prefilled),
|
||||||
("step4_fill_claim_form", bot.step4_fill_claim_form),
|
("step4_select_insurance_ok", bot.step4_select_insurance_ok),
|
||||||
("step5_attach_files", bot.step5_attach_files),
|
("step5_practitioner_continue", bot.step5_practitioner_continue),
|
||||||
("step6_click_next", bot.step6_click_next),
|
("step6_fill_claim_form", bot.step6_fill_claim_form),
|
||||||
("step7_submit_claim", bot.step7_submit_claim),
|
("step7_attach_files", bot.step7_attach_files),
|
||||||
|
("step8_submit_claim", bot.step8_submit_claim),
|
||||||
]:
|
]:
|
||||||
result = step_fn()
|
result = step_fn()
|
||||||
print(f"[UnitedDH Claim] {step_name} result: {result}")
|
print(f"[UnitedDH Claim] {step_name} result: {result}")
|
||||||
@@ -267,14 +268,14 @@ async def start_uniteddh_claim_run(sid: str, data: dict, url: str):
|
|||||||
asyncio.create_task(_remove_session_later(sid, 30))
|
asyncio.create_task(_remove_session_later(sid, 30))
|
||||||
return {"status": "error", "message": result}
|
return {"status": "error", "message": result}
|
||||||
|
|
||||||
# --- Step 8: PDF + claim number ---
|
# --- Step 9: PDF + claim number ---
|
||||||
step8_result = bot.step8_save_confirmation_pdf()
|
step9_result = bot.step9_save_confirmation_pdf()
|
||||||
print(f"[UnitedDH Claim] step8 result: {step8_result}")
|
print(f"[UnitedDH Claim] step9 result: {step9_result}")
|
||||||
if isinstance(step8_result, str) and step8_result.startswith("ERROR"):
|
if isinstance(step9_result, str) and step9_result.startswith("ERROR"):
|
||||||
print(f"[UnitedDH Claim] step8 warning (non-fatal): {step8_result}")
|
print(f"[UnitedDH Claim] step9 warning (non-fatal): {step9_result}")
|
||||||
step8_result = {}
|
step9_result = {}
|
||||||
|
|
||||||
pdf_path = step8_result.get("pdf_path") if isinstance(step8_result, dict) else None
|
pdf_path = step9_result.get("pdf_path") if isinstance(step9_result, dict) else None
|
||||||
pdf_url = None
|
pdf_url = None
|
||||||
if pdf_path:
|
if pdf_path:
|
||||||
import os as _os
|
import os as _os
|
||||||
@@ -284,7 +285,7 @@ async def start_uniteddh_claim_run(sid: str, data: dict, url: str):
|
|||||||
pdf_url = f"http://{url_host}:{port}/downloads/{filename}"
|
pdf_url = f"http://{url_host}:{port}/downloads/{filename}"
|
||||||
print(f"[UnitedDH Claim] pdf_url: {pdf_url}")
|
print(f"[UnitedDH Claim] pdf_url: {pdf_url}")
|
||||||
|
|
||||||
claim_number = step8_result.get("claimNumber") if isinstance(step8_result, dict) else None
|
claim_number = step9_result.get("claimNumber") if isinstance(step9_result, dict) else None
|
||||||
|
|
||||||
result = {
|
result = {
|
||||||
"status": "success",
|
"status": "success",
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -524,7 +524,7 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
|
|
||||||
# Step 1.2: Select Payer - UnitedHealthcare Massachusetts
|
# Step 1.2: Select Payer - UnitedHealthcare Massachusetts
|
||||||
print("[UnitedSCO step1] Selecting Payer...")
|
print("[UnitedSCO step1] Selecting Payer...")
|
||||||
|
|
||||||
# First dismiss any blocking dialogs (e.g. Chrome password save)
|
# First dismiss any blocking dialogs (e.g. Chrome password save)
|
||||||
try:
|
try:
|
||||||
self.driver.execute_script("""
|
self.driver.execute_script("""
|
||||||
@@ -534,12 +534,10 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
""")
|
""")
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
payer_selected = False
|
payer_selected = False
|
||||||
|
|
||||||
# Strategy 1: Click the ng-select, type to search, and select the option
|
|
||||||
try:
|
try:
|
||||||
# Find the Payer ng-select by multiple selectors
|
|
||||||
payer_selectors = [
|
payer_selectors = [
|
||||||
"//label[contains(text(),'Payer')]/following-sibling::ng-select",
|
"//label[contains(text(),'Payer')]/following-sibling::ng-select",
|
||||||
"//label[contains(text(),'Payer')]/..//ng-select",
|
"//label[contains(text(),'Payer')]/..//ng-select",
|
||||||
@@ -554,101 +552,27 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if payer_ng_select:
|
if payer_ng_select:
|
||||||
# Scroll to it and click to open
|
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", payer_ng_select)
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", payer_ng_select)
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
payer_ng_select.click()
|
payer_ng_select.click()
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
search_input = payer_ng_select.find_element(By.XPATH,
|
||||||
# Type into the search input inside ng-select to filter options
|
".//input[contains(@type,'text') or contains(@role,'combobox')]")
|
||||||
try:
|
search_input.clear()
|
||||||
search_input = payer_ng_select.find_element(By.XPATH, ".//input[contains(@type,'text') or contains(@role,'combobox')]")
|
search_input.send_keys("UnitedHealthcare Massachusetts")
|
||||||
search_input.clear()
|
print("[UnitedSCO step1] Typed payer search text")
|
||||||
search_input.send_keys("UnitedHealthcare Massachusetts")
|
time.sleep(2)
|
||||||
print("[UnitedSCO step1] Typed payer search text")
|
search_input.send_keys(Keys.ENTER)
|
||||||
time.sleep(2)
|
print("[UnitedSCO step1] Pressed Enter to select Payer")
|
||||||
except Exception:
|
|
||||||
# If no search input, try sending keys directly to ng-select
|
|
||||||
try:
|
|
||||||
ActionChains(self.driver).send_keys("UnitedHealthcare Mass").perform()
|
|
||||||
print("[UnitedSCO step1] Typed payer search via ActionChains")
|
|
||||||
time.sleep(2)
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Find and click the matching option
|
|
||||||
payer_options = self.driver.find_elements(By.XPATH,
|
|
||||||
"//ng-dropdown-panel//div[contains(@class,'ng-option')]"
|
|
||||||
)
|
|
||||||
for opt in payer_options:
|
|
||||||
opt_text = opt.text.strip()
|
|
||||||
if "UnitedHealthcare Massachusetts" in opt_text:
|
|
||||||
opt.click()
|
|
||||||
print(f"[UnitedSCO step1] Selected Payer: {opt_text}")
|
|
||||||
payer_selected = True
|
|
||||||
break
|
|
||||||
|
|
||||||
if not payer_selected and payer_options:
|
|
||||||
# Select first visible option if it contains "United"
|
|
||||||
for opt in payer_options:
|
|
||||||
opt_text = opt.text.strip()
|
|
||||||
if "United" in opt_text and opt.is_displayed():
|
|
||||||
opt.click()
|
|
||||||
print(f"[UnitedSCO step1] Selected first matching Payer: {opt_text}")
|
|
||||||
payer_selected = True
|
|
||||||
break
|
|
||||||
|
|
||||||
# Close dropdown
|
|
||||||
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
|
payer_selected = True
|
||||||
else:
|
else:
|
||||||
print("[UnitedSCO step1] Could not find Payer ng-select element")
|
print("[UnitedSCO step1] Could not find Payer ng-select element")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"[UnitedSCO step1] Payer selection strategy 1 error: {e}")
|
print(f"[UnitedSCO step1] Payer selection error: {e}")
|
||||||
try:
|
|
||||||
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
# Strategy 2: JavaScript direct selection if strategy 1 failed
|
|
||||||
if not payer_selected:
|
|
||||||
try:
|
|
||||||
# Try clicking via JavaScript
|
|
||||||
clicked = self.driver.execute_script("""
|
|
||||||
// Find ng-select near the Payer label
|
|
||||||
var labels = document.querySelectorAll('label');
|
|
||||||
for (var i = 0; i < labels.length; i++) {
|
|
||||||
if (labels[i].textContent.includes('Payer')) {
|
|
||||||
var parent = labels[i].parentElement;
|
|
||||||
var ngSelect = parent.querySelector('ng-select') || labels[i].nextElementSibling;
|
|
||||||
if (ngSelect) {
|
|
||||||
ngSelect.click();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
""")
|
|
||||||
if clicked:
|
|
||||||
time.sleep(1)
|
|
||||||
ActionChains(self.driver).send_keys("UnitedHealthcare Mass").perform()
|
|
||||||
time.sleep(2)
|
|
||||||
payer_options = self.driver.find_elements(By.XPATH,
|
|
||||||
"//ng-dropdown-panel//div[contains(@class,'ng-option')]"
|
|
||||||
)
|
|
||||||
for opt in payer_options:
|
|
||||||
if "UnitedHealthcare" in opt.text and "Massachusetts" in opt.text:
|
|
||||||
opt.click()
|
|
||||||
print(f"[UnitedSCO step1] Selected Payer via JS: {opt.text.strip()}")
|
|
||||||
payer_selected = True
|
|
||||||
break
|
|
||||||
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
|
||||||
except Exception as e:
|
|
||||||
print(f"[UnitedSCO step1] Payer selection strategy 2 error: {e}")
|
|
||||||
|
|
||||||
if not payer_selected:
|
if not payer_selected:
|
||||||
print("[UnitedSCO step1] WARNING: Could not select Payer - form may fail")
|
print("[UnitedSCO step1] WARNING: Could not select Payer - form may fail")
|
||||||
|
|
||||||
@@ -699,6 +623,10 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
print("[UnitedSCO step1] Select Insurance modal closed")
|
print("[UnitedSCO step1] Select Insurance modal closed")
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
print("[UnitedSCO step1] Modal staleness timeout — continuing anyway")
|
print("[UnitedSCO step1] Modal staleness timeout — continuing anyway")
|
||||||
|
# Wait for the Provider & Location page to begin rendering after modal closes
|
||||||
|
WebDriverWait(self.driver, 5).until(
|
||||||
|
EC.presence_of_element_located((By.XPATH, "//label[@for='treatmentLocation'] | //label[@for='paymentGroupId']"))
|
||||||
|
)
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
print("[UnitedSCO step1] Select Insurance popup not found — proceeding")
|
print("[UnitedSCO step1] Select Insurance popup not found — proceeding")
|
||||||
|
|
||||||
@@ -721,17 +649,15 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
"//label[@for='treatmentLocation']/..//ng-select"
|
"//label[@for='treatmentLocation']/..//ng-select"
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'end'});", location_ng)
|
# Center in viewport so panel opens downward instead of upward
|
||||||
arrow = location_ng.find_element(By.XPATH, ".//span[contains(@class,'ng-arrow-wrapper')]")
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", location_ng)
|
||||||
ActionChains(self.driver).move_to_element(arrow).click().perform()
|
arrow = location_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
|
||||||
WebDriverWait(self.driver, 10).until(
|
arrow.click()
|
||||||
EC.presence_of_element_located((By.XPATH, "//ng-dropdown-panel"))
|
first_option = WebDriverWait(self.driver, 5).until(
|
||||||
)
|
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
|
||||||
first_option = self.driver.find_element(By.XPATH,
|
|
||||||
"//ng-dropdown-panel//div[contains(@class,'ng-option') and not(contains(@class,'disabled'))]"
|
|
||||||
)
|
)
|
||||||
option_text = first_option.text.strip()
|
option_text = first_option.text.strip()
|
||||||
ActionChains(self.driver).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform()
|
first_option.click()
|
||||||
print(f"[UnitedSCO step1] Selected Treatment Location: {option_text}")
|
print(f"[UnitedSCO step1] Selected Treatment Location: {option_text}")
|
||||||
location_selected = True
|
location_selected = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -750,17 +676,15 @@ class AutomationUnitedSCOEligibilityCheck:
|
|||||||
"//label[@for='paymentGroupId']/..//ng-select"
|
"//label[@for='paymentGroupId']/..//ng-select"
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
self.driver.execute_script("arguments[0].scrollIntoView({block:'end'});", billing_ng)
|
# Center in viewport so panel opens downward instead of upward
|
||||||
arrow = billing_ng.find_element(By.XPATH, ".//span[contains(@class,'ng-arrow-wrapper')]")
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng)
|
||||||
ActionChains(self.driver).move_to_element(arrow).click().perform()
|
arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
|
||||||
WebDriverWait(self.driver, 10).until(
|
arrow.click()
|
||||||
EC.presence_of_element_located((By.XPATH, "//ng-dropdown-panel"))
|
first_option = WebDriverWait(self.driver, 5).until(
|
||||||
)
|
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
|
||||||
first_option = self.driver.find_element(By.XPATH,
|
|
||||||
"//ng-dropdown-panel//div[contains(@class,'ng-option') and not(contains(@class,'disabled'))]"
|
|
||||||
)
|
)
|
||||||
option_text = first_option.text.strip()
|
option_text = first_option.text.strip()
|
||||||
ActionChains(self.driver).send_keys(Keys.ARROW_DOWN).send_keys(Keys.ENTER).perform()
|
first_option.click()
|
||||||
print(f"[UnitedSCO step1] Selected Billing Entity: {option_text}")
|
print(f"[UnitedSCO step1] Selected Billing Entity: {option_text}")
|
||||||
billing_selected = True
|
billing_selected = True
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|||||||
Reference in New Issue
Block a user