Compare commits
2
Commits
90f587ac9d
...
8c0fcb4267
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c0fcb4267 | ||
|
|
ef919a2a2f |
@@ -343,6 +343,20 @@ class AutomationCCAClaimSubmit:
|
||||
if not verified:
|
||||
return "ERROR: 'Verify Eligibility' button not found"
|
||||
|
||||
# Multiple records popup: pick the first member (top Select)
|
||||
try:
|
||||
sel_btn = WebDriverWait(self.driver, 12).until(
|
||||
EC.element_to_be_clickable((By.ID, "buttonSelectMember0"))
|
||||
)
|
||||
print("[CCA Claim step2] Multiple records popup — clicking top Select")
|
||||
try:
|
||||
sel_btn.click()
|
||||
except Exception:
|
||||
self.driver.execute_script("arguments[0].click();", sel_btn)
|
||||
time.sleep(2)
|
||||
except TimeoutException:
|
||||
print("[CCA Claim step2] No multiple-records popup")
|
||||
|
||||
# Wait for eligibility result
|
||||
print("[CCA Claim step2] Waiting for eligibility result...")
|
||||
try:
|
||||
@@ -449,11 +463,43 @@ class AutomationCCAClaimSubmit:
|
||||
# Extract Encounter ID from the FIRST row (newest claim is at the top)
|
||||
claim_number = None
|
||||
|
||||
# Try table cells first (Encounter ID is the first column)
|
||||
for sel in [
|
||||
# Pick the latest claim for this member: rows matching DOB, highest Encounter ID
|
||||
try:
|
||||
def _variants(d):
|
||||
d = self._format_dob(d) if d else ""
|
||||
if not d:
|
||||
return []
|
||||
m, dd, y = d.split("/")
|
||||
return list({d, f"{int(m)}/{int(dd)}/{y}", f"{m}/{dd}/{y[-2:]}"})
|
||||
dos_v = _variants(self.serviceDate)
|
||||
dob_v = _variants(self.dateOfBirth)
|
||||
rows = self.driver.execute_script("""
|
||||
return Array.from(document.querySelectorAll('table tbody tr')).map(function(tr){
|
||||
return Array.from(tr.querySelectorAll('td')).map(function(td){return td.innerText.trim();});
|
||||
}).filter(function(c){return c.length;});
|
||||
""") or []
|
||||
# Rows for this member (by DOB); the largest Encounter ID is the latest claim
|
||||
id_rows = [c for c in rows if re.match(r'\d{10,}', c[0])]
|
||||
dob_rows = [c for c in id_rows if any(v in " | ".join(c) for v in dob_v)]
|
||||
if not dob_rows:
|
||||
# DOB not shown/matched — fall back to the service date
|
||||
dob_rows = [c for c in id_rows if any(v in " | ".join(c) for v in dos_v)]
|
||||
if dob_rows:
|
||||
best = max(dob_rows, key=lambda c: int(re.match(r'\d+', c[0]).group(0)))
|
||||
claim_number = re.match(r'\d+', best[0]).group(0)
|
||||
print(f"[CCA Claim step4] Encounter ID {claim_number} = latest of "
|
||||
f"{len(dob_rows)} row(s) for DOB {dob_v[:1]}: {best}")
|
||||
else:
|
||||
print(f"[CCA Claim step4] No row matched DOB={dob_v} or DOS={dos_v}; "
|
||||
f"falling back to top row. Rows seen: {rows[:6]}")
|
||||
except Exception as e:
|
||||
print(f"[CCA Claim step4] Row matching failed: {e!r}")
|
||||
|
||||
# Fallback: top row (Encounter ID is the first column)
|
||||
for sel in ([] if claim_number else [
|
||||
(By.XPATH, "//table//tbody//tr[1]/td[1]"),
|
||||
(By.XPATH, "//*[contains(@class,'wbx-table-results')]//tr[1]/td[1]"),
|
||||
]:
|
||||
]):
|
||||
try:
|
||||
cell = WebDriverWait(self.driver, 8).until(
|
||||
EC.presence_of_element_located(sel))
|
||||
@@ -806,27 +852,56 @@ class AutomationCCAClaimSubmit:
|
||||
print(f"[CCA Claim grid] Filled {input_id}={value!r}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"[CCA Claim grid] Fill {input_id} failed: {e}")
|
||||
print(f"[CCA Claim grid] Fill {input_id} failed: {e!r}")
|
||||
return False
|
||||
|
||||
def click_cell_and_fill_retry(col_expr, input_id, value, attempts=3):
|
||||
"""Retry when the editor input doesn't appear after the cell click
|
||||
(the grid sometimes hasn't finished committing the previous row)."""
|
||||
from selenium.webdriver.common.action_chains import ActionChains
|
||||
for attempt in range(1, attempts + 1):
|
||||
if click_cell_and_fill(col_expr, input_id, value):
|
||||
return True
|
||||
print(f"[CCA Claim grid] {input_id} attempt {attempt}/{attempts} failed")
|
||||
time.sleep(1.5)
|
||||
if attempt >= 2:
|
||||
# Escape any stray popup/editor and try a double-click on the cell
|
||||
try:
|
||||
ActionChains(self.driver).send_keys(Keys.ESCAPE).perform()
|
||||
cell = self.driver.find_element(
|
||||
By.XPATH,
|
||||
f"(//div[contains(@class,'e-gridcontent')]//tr)[{row_num}]/td[{col_expr}]")
|
||||
ActionChains(self.driver).double_click(cell).perform()
|
||||
time.sleep(0.5)
|
||||
field = self.driver.find_element(By.ID, input_id)
|
||||
field.send_keys(Keys.CONTROL + "a")
|
||||
field.send_keys(Keys.DELETE)
|
||||
field.send_keys(str(value))
|
||||
print(f"[CCA Claim grid] Filled {input_id}={value!r} via double-click")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"[CCA Claim grid] Double-click fallback failed: {e!r}")
|
||||
return False
|
||||
|
||||
# CODE
|
||||
code_ok = click_cell_and_fill(col_map["CODE"], "ServicesCODE", code)
|
||||
code_ok = click_cell_and_fill_retry(col_map["CODE"], "ServicesCODE", code)
|
||||
time.sleep(1.0) # let the grid finish its code lookup before the next cell
|
||||
|
||||
# TOOTH — skip if empty
|
||||
tooth_ok = True
|
||||
if tooth:
|
||||
tooth_ok = click_cell_and_fill(col_map["TOOTH"], "ServicesTOOTH", tooth)
|
||||
tooth_ok = click_cell_and_fill_retry(col_map["TOOTH"], "ServicesTOOTH", tooth)
|
||||
|
||||
# Surfaces — skip if empty
|
||||
if surface_chars:
|
||||
surf_ids = ["ServicesSURF1","ServicesSURF2","ServicesSURF3",
|
||||
"ServicesSURF4","ServicesSURF5"]
|
||||
for si, char in enumerate(surface_chars[:5]):
|
||||
click_cell_and_fill(col_map[f"SURF{si+1}"], surf_ids[si], char)
|
||||
click_cell_and_fill_retry(col_map[f"SURF{si+1}"], surf_ids[si], char)
|
||||
|
||||
# Billed Amount
|
||||
# QTY and Service Date auto-fill after CODE is entered and this cell is clicked
|
||||
billed_ok = click_cell_and_fill(col_map["BILLED_AMOUNT"], "ServicesBILLED_AMOUNT", billed_str)
|
||||
billed_ok = click_cell_and_fill_retry(col_map["BILLED_AMOUNT"], "ServicesBILLED_AMOUNT", billed_str)
|
||||
|
||||
if not (code_ok and tooth_ok and billed_ok):
|
||||
missing = [name for name, ok in
|
||||
@@ -954,23 +1029,36 @@ class AutomationCCAClaimSubmit:
|
||||
|
||||
# ---- Handle confirmation modal popup ----
|
||||
print("[CCA Claim step3] Waiting for confirmation modal...")
|
||||
time.sleep(1.5)
|
||||
modal_confirmed = False
|
||||
for sel in [
|
||||
modal_selectors = [
|
||||
# Most specific: Submit/Confirm/Yes button inside an active modal
|
||||
(By.XPATH, "//div[contains(@class,'modal') and contains(@class,'in')]//button[contains(text(),'Submit') or contains(text(),'Confirm') or contains(text(),'Yes')]"),
|
||||
(By.XPATH, "//div[@class='modal-footer']//button[contains(text(),'Submit') or contains(text(),'Confirm') or contains(text(),'Yes')]"),
|
||||
(By.XPATH, "//button[@ng-click and (contains(@ng-click,'confirm') or contains(@ng-click,'Confirm') or contains(@ng-click,'submit') or contains(@ng-click,'Submit'))]"),
|
||||
(By.XPATH, "//div[contains(@class,'modal')]//button[not(contains(@class,'cancel')) and not(contains(text(),'Cancel')) and not(contains(text(),'No'))]"),
|
||||
]:
|
||||
try:
|
||||
btn = WebDriverWait(self.driver, 5).until(EC.element_to_be_clickable(sel))
|
||||
btn.click()
|
||||
modal_confirmed = True
|
||||
print(f"[CCA Claim step3] Clicked modal confirm via {sel}")
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
]
|
||||
# Poll for the modal's own button first (patterns 1-2). The looser patterns (3-4)
|
||||
# also match the page's own "Submit Claim" button behind the popup, so they are
|
||||
# only tried after the modal-specific ones have had time to appear.
|
||||
start = time.monotonic()
|
||||
while time.monotonic() - start < 8 and not modal_confirmed:
|
||||
active = modal_selectors[:2] if time.monotonic() - start < 5 else modal_selectors
|
||||
for sel in active:
|
||||
try:
|
||||
btn = next((b for b in self.driver.find_elements(*sel)
|
||||
if b.is_displayed() and b.is_enabled()), None)
|
||||
if btn:
|
||||
btn.click()
|
||||
modal_confirmed = True
|
||||
print(f"[CCA Claim step3] Clicked modal confirm via {sel}")
|
||||
break
|
||||
except Exception:
|
||||
continue
|
||||
if not modal_confirmed:
|
||||
time.sleep(0.3)
|
||||
|
||||
if not modal_confirmed:
|
||||
time.sleep(0.3)
|
||||
|
||||
if not modal_confirmed:
|
||||
print("[CCA Claim step3] No confirmation modal found — may have auto-submitted")
|
||||
@@ -978,7 +1066,7 @@ class AutomationCCAClaimSubmit:
|
||||
# Wait for final confirmation page
|
||||
print("[CCA Claim step3] Waiting for submission confirmation...")
|
||||
try:
|
||||
WebDriverWait(self.driver, 30).until(
|
||||
WebDriverWait(self.driver, 10).until(
|
||||
lambda d: any(x in d.find_element(By.TAG_NAME, "body").text.lower() for x in [
|
||||
"claim submitted", "claim number", "confirmation", "success",
|
||||
"has been submitted", "claim id", "assigned the number",
|
||||
|
||||
@@ -291,15 +291,15 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//input[@name='dateInput' and @placeholder='mm/dd/yyyy' and @ng-model='date']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Use JavaScript to set the value directly to avoid date picker interference
|
||||
self.driver.execute_script(f"arguments[0].value = '{formatted_dob}'; arguments[0].dispatchEvent(new Event('input', {{ bubbles: true }})); arguments[0].dispatchEvent(new Event('change', {{ bubbles: true }}));", dob_input)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Press Tab to move to next field and ensure the date is set
|
||||
dob_input.send_keys(Keys.TAB)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Fill Member Number
|
||||
if self.memberId:
|
||||
@@ -309,15 +309,15 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//input[@placeholder='Member Number' and @ng-model='vm.number']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
member_input.click()
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
member_input.clear()
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
member_input.send_keys(self.memberId)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
member_input.send_keys(Keys.TAB)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 3: Fill First Name and Last Name
|
||||
# if self.firstName:
|
||||
@@ -383,12 +383,12 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//input[@ng-model='serviceLine.procedure' and @placeholder='Code']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Type the procedure code to trigger typeahead
|
||||
procedure_input.clear()
|
||||
procedure_input.send_keys(self.procedureCode)
|
||||
time.sleep(3) # Wait for typeahead to load
|
||||
time.sleep(0.5) # brief debounce; the dropdown wait below does the real waiting
|
||||
|
||||
# Step 3: Look for the typeahead dropdown and select the first option
|
||||
# The dropdown appears as ul with class 'dropdown-menu'
|
||||
@@ -405,13 +405,13 @@ class AutomationMassHealthClaimsLogin:
|
||||
if first_option:
|
||||
print("DEBUG: Found first option in procedure dropdown, clicking...")
|
||||
first_option.click()
|
||||
time.sleep(2)
|
||||
time.sleep(0.5)
|
||||
return "Success"
|
||||
except:
|
||||
# If no dropdown appears, just press Tab to move to next field
|
||||
print("DEBUG: No procedure dropdown found, pressing Tab")
|
||||
procedure_input.send_keys(Keys.TAB)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
return "Success"
|
||||
|
||||
return "Success"
|
||||
@@ -433,16 +433,16 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//input[@ng-model='serviceLine.toothCode' and @placeholder='Tooth']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Type the tooth number
|
||||
tooth_input.clear()
|
||||
tooth_input.send_keys(tooth_value.upper()) # Convert to uppercase as per onkeyup
|
||||
time.sleep(2)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Press Tab to move to next field
|
||||
tooth_input.send_keys(Keys.TAB)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
return "Success"
|
||||
except Exception as e:
|
||||
@@ -463,16 +463,16 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//input[@ng-model='serviceLine.surface' and @placeholder='Surface']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Type the tooth surface (convert to uppercase)
|
||||
surface_input.clear()
|
||||
surface_input.send_keys(surface_value.upper())
|
||||
time.sleep(2)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Press Tab to move to next field
|
||||
surface_input.send_keys(Keys.TAB)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
return "Success"
|
||||
except Exception as e:
|
||||
@@ -491,12 +491,12 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//select[@ng-model='serviceLine.quadrant']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Use Select class to choose Upper Right
|
||||
select = Select(quadrant_dropdown)
|
||||
select.select_by_visible_text("Upper Right")
|
||||
time.sleep(2)
|
||||
time.sleep(0.5)
|
||||
|
||||
return "Success"
|
||||
except Exception as e:
|
||||
@@ -515,12 +515,12 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//select[@ng-model='serviceLine.arch']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Use Select class to choose Entire Oral Cavity
|
||||
select = Select(arch_dropdown)
|
||||
select.select_by_visible_text("Entire Oral Cavity")
|
||||
time.sleep(2)
|
||||
time.sleep(0.5)
|
||||
|
||||
return "Success"
|
||||
except Exception as e:
|
||||
@@ -541,16 +541,16 @@ class AutomationMassHealthClaimsLogin:
|
||||
(By.XPATH, "//input[@ng-model='serviceLine.authorizationNumber' and @placeholder='No.']")
|
||||
)
|
||||
)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
# Step 2: Type the authorization number
|
||||
auth_input.clear()
|
||||
auth_input.send_keys(auth_number)
|
||||
time.sleep(2)
|
||||
time.sleep(0.5)
|
||||
|
||||
# Press Tab to move to next field
|
||||
auth_input.send_keys(Keys.TAB)
|
||||
time.sleep(1)
|
||||
time.sleep(0.3)
|
||||
|
||||
return "Success"
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user