feat: United Ucard claim searches by name+DOB and fills tooth number/surface when provided
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1537,6 +1537,8 @@ export function ClaimForm({
|
|||||||
insuranceProvider: "United Ucard",
|
insuranceProvider: "United Ucard",
|
||||||
appointmentId: appointmentIdToUse,
|
appointmentId: appointmentIdToUse,
|
||||||
insuranceSiteKey: "UNITED_UCARD",
|
insuranceSiteKey: "UNITED_UCARD",
|
||||||
|
firstName: patient?.firstName ?? "",
|
||||||
|
lastName: patient?.lastName ?? "",
|
||||||
claimId: createdClaim.id,
|
claimId: createdClaim.id,
|
||||||
claimFiles: claimFilesMeta,
|
claimFiles: claimFilesMeta,
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ service lines / attach files / submit / save confirmation PDF) are left as
|
|||||||
TODO stubs — fill these in once we know the exact DOM for the claim
|
TODO stubs — fill these in once we know the exact DOM for the claim
|
||||||
submission flow on the UHC Dental provider dashboard.
|
submission flow on the UHC Dental provider dashboard.
|
||||||
"""
|
"""
|
||||||
from selenium.common.exceptions import WebDriverException, TimeoutException
|
from selenium.common.exceptions import WebDriverException, TimeoutException, NoSuchElementException
|
||||||
from selenium.webdriver.common.by import By
|
from selenium.webdriver.common.by import By
|
||||||
from selenium.webdriver.support.ui import WebDriverWait
|
from selenium.webdriver.support.ui import WebDriverWait
|
||||||
from selenium.webdriver.support import expected_conditions as EC
|
from selenium.webdriver.support import expected_conditions as EC
|
||||||
@@ -382,12 +382,12 @@ class AutomationUnitedUcardClaimSubmit:
|
|||||||
|
|
||||||
def step3_fill_patient_information(self):
|
def step3_fill_patient_information(self):
|
||||||
"""
|
"""
|
||||||
Fill the Patient Information step: Date of Service, Subscriber ID,
|
Fill the Patient Information step: Date of Service, choose the
|
||||||
Date of Birth, then click Search to look up the member.
|
First Name + Last Name + DOB search, then First/Last Name, Date of Birth, then click Search to look up the member.
|
||||||
|
|
||||||
Confirmed against the live site (2026-09-13):
|
Confirmed against the live site (2026-09-13):
|
||||||
<input type="date" class="date-input date-of-service" placeholder="mm/dd/yyyy">
|
<input type="date" class="date-input date-of-service" placeholder="mm/dd/yyyy">
|
||||||
<input type="text" class="subscriber-id">
|
<input type="text" class="member-fname"> / <input type="text" class="member-lname">
|
||||||
<input type="date" class="date-input date-of-birth" placeholder="mm/dd/yyyy">
|
<input type="date" class="date-input date-of-birth" placeholder="mm/dd/yyyy">
|
||||||
<button class="search-button member-search">Search</button>
|
<button class="search-button member-search">Search</button>
|
||||||
"""
|
"""
|
||||||
@@ -402,12 +402,30 @@ class AutomationUnitedUcardClaimSubmit:
|
|||||||
return "ERROR: Date of Service field not found on Patient Information step"
|
return "ERROR: Date of Service field not found on Patient Information step"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
subscriber_id_field = self.driver.find_element(By.CSS_SELECTOR, "input.subscriber-id")
|
name_radio = WebDriverWait(self.driver, 10).until(
|
||||||
subscriber_id_field.clear()
|
EC.presence_of_element_located((By.CSS_SELECTOR, "input.searchname[name='member-search'][value='name']"))
|
||||||
subscriber_id_field.send_keys(self.memberId)
|
)
|
||||||
print(f"[UnitedUcard Claim] Entered Subscriber ID: {self.memberId}")
|
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", name_radio)
|
||||||
|
try:
|
||||||
|
name_radio.click()
|
||||||
|
except Exception:
|
||||||
|
self.driver.execute_script("arguments[0].click();", name_radio)
|
||||||
|
print("[UnitedUcard Claim] Selected 'First Name + Last Name + DOB' search")
|
||||||
|
except TimeoutException:
|
||||||
|
return "ERROR: 'First Name + Last Name + DOB' radio not found on Patient Information step"
|
||||||
|
|
||||||
|
try:
|
||||||
|
fname_field = WebDriverWait(self.driver, 10).until(
|
||||||
|
EC.element_to_be_clickable((By.CSS_SELECTOR, "input.member-fname"))
|
||||||
|
)
|
||||||
|
fname_field.clear()
|
||||||
|
fname_field.send_keys(self.firstName)
|
||||||
|
lname_field = self.driver.find_element(By.CSS_SELECTOR, "input.member-lname")
|
||||||
|
lname_field.clear()
|
||||||
|
lname_field.send_keys(self.lastName)
|
||||||
|
print(f"[UnitedUcard Claim] Entered name: {self.firstName} {self.lastName}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"ERROR: Subscriber ID field not found on Patient Information step: {e}"
|
return f"ERROR: First/Last Name fields not found on Patient Information step: {e}"
|
||||||
|
|
||||||
try:
|
try:
|
||||||
dob_field = self.driver.find_element(By.CSS_SELECTOR, "input.date-of-birth")
|
dob_field = self.driver.find_element(By.CSS_SELECTOR, "input.date-of-birth")
|
||||||
@@ -646,6 +664,16 @@ class AutomationUnitedUcardClaimSubmit:
|
|||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
return "ERROR: 'Add a Procedure' button not found"
|
return "ERROR: 'Add a Procedure' button not found"
|
||||||
|
|
||||||
|
def _wait_visible(self, element_id, timeout=10):
|
||||||
|
"""Return the element once visible, or None if it never appears
|
||||||
|
(the portal only shows tooth fields for certain procedure codes)."""
|
||||||
|
try:
|
||||||
|
return WebDriverWait(self.driver, timeout).until(
|
||||||
|
lambda d: next((e for e in d.find_elements(By.ID, element_id) if e.is_displayed()), None)
|
||||||
|
)
|
||||||
|
except TimeoutException:
|
||||||
|
return None
|
||||||
|
|
||||||
def _fill_and_save_procedure(self, line: dict, idx: int):
|
def _fill_and_save_procedure(self, line: dict, idx: int):
|
||||||
"""
|
"""
|
||||||
Fill one procedure row (Procedure Code, Date of Service, Procedure
|
Fill one procedure row (Procedure Code, Date of Service, Procedure
|
||||||
@@ -690,6 +718,28 @@ class AutomationUnitedUcardClaimSubmit:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
return f"ERROR: Procedure Fee field not found for line {idx + 1}: {e}"
|
return f"ERROR: Procedure Fee field not found for line {idx + 1}: {e}"
|
||||||
|
|
||||||
|
# Tooth Number / Surface are driven by the app's claim data: only wait
|
||||||
|
# for and fill them when the service line actually has a value.
|
||||||
|
tooth = str(line.get("toothNumber") or line.get("tooth") or "").strip()
|
||||||
|
if tooth:
|
||||||
|
tooth_field = self._wait_visible("toothNum")
|
||||||
|
if tooth_field:
|
||||||
|
tooth_field.clear()
|
||||||
|
tooth_field.send_keys(tooth)
|
||||||
|
print(f"[UnitedUcard Claim] Entered Tooth Number: {tooth}")
|
||||||
|
else:
|
||||||
|
print(f"[UnitedUcard Claim] Tooth Number field did not appear for {procedure_code}; skipped")
|
||||||
|
|
||||||
|
surface = str(line.get("toothSurface") or line.get("surface") or "").strip().upper()
|
||||||
|
if surface:
|
||||||
|
surface_field = self._wait_visible("toothSurf")
|
||||||
|
if surface_field:
|
||||||
|
surface_field.clear()
|
||||||
|
surface_field.send_keys(surface)
|
||||||
|
print(f"[UnitedUcard Claim] Entered Tooth Surface: {surface}")
|
||||||
|
else:
|
||||||
|
print(f"[UnitedUcard Claim] Tooth Surface field did not appear for {procedure_code}; skipped")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
save_btn = WebDriverWait(self.driver, 10).until(
|
save_btn = WebDriverWait(self.driver, 10).until(
|
||||||
EC.element_to_be_clickable((By.XPATH, "//*[@onclick='saveProcCode()']"))
|
EC.element_to_be_clickable((By.XPATH, "//*[@onclick='saveProcCode()']"))
|
||||||
|
|||||||
Reference in New Issue
Block a user