fix: update DDMA claim submit worker for new deltadentalma.com claims-submission flow

Site now uses a dedicated claims-submission page with a Subscriber ID +
Birthdate search form instead of the member-ID search box, and login
detection now checks for the check-patient-benefits-search link rather
than the old member-search input placeholder.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-08 00:22:35 -04:00
parent 99cdfa702a
commit 3582da8510
2 changed files with 85 additions and 146 deletions

View File

@@ -9,6 +9,7 @@ import os
import re
import json
import base64
import calendar
from datetime import date
from ddma_browser_manager import get_browser_manager
@@ -108,7 +109,7 @@ class AutomationDDMAClaimSubmit:
if "member" not in current_url.lower():
try:
WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
)
print("[DDMA Claim login] Already logged in")
return "ALREADY_LOGGED_IN"
@@ -117,7 +118,7 @@ class AutomationDDMAClaimSubmit:
time.sleep(2)
try:
WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
)
print("[DDMA Claim login] Already logged in — member search found")
return "ALREADY_LOGGED_IN"
@@ -134,7 +135,7 @@ class AutomationDDMAClaimSubmit:
current_url = self.driver.current_url
if "sign-in" not in current_url.lower():
member_search = WebDriverWait(self.driver, 3).until(
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
)
if member_search:
print("[DDMA Claim login] Session valid — skipping login")
@@ -180,7 +181,7 @@ class AutomationDDMAClaimSubmit:
time.sleep(2)
try:
WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]"))
)
print("[DDMA Claim login] Already authenticated after modal dismiss")
return "ALREADY_LOGGED_IN"
@@ -239,94 +240,24 @@ class AutomationDDMAClaimSubmit:
print(f"[DDMA Claim login] Exception: {e}")
return f"ERROR:LOGIN FAILED: {e}"
def _select_provider_dropdown(self):
"""
Click the provider dropdown on the member search page and select the option
matching self.providerName (case-insensitive). Falls back to the first option.
The button data-testid is 'member-search_provider_select-btn'.
"""
try:
short_wait = WebDriverWait(self.driver, 5)
try:
provider_btn = short_wait.until(
EC.element_to_be_clickable(
(By.XPATH, '//button[@data-testid="member-search_provider_select-btn"]')
)
)
except TimeoutException:
print("[DDMA Claim step1] No provider dropdown found — skipping")
return
provider_btn.click()
time.sleep(0.5)
try:
WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH, "//*[@role='option']"))
)
except TimeoutException:
print("[DDMA Claim step1] Provider listbox did not open")
return
options = self.driver.find_elements(By.XPATH, "//*[@role='option']")
print(f"[DDMA Claim step1] Provider options: {[o.text.strip() for o in options]}")
target = (self.providerName or "").strip().lower()
selected = False
if target:
for opt in options:
if target in opt.text.lower():
opt.click()
print(f"[DDMA Claim step1] Selected provider: '{opt.text.strip()}'")
selected = True
break
if not selected and options:
options[0].click()
print(f"[DDMA Claim step1] No match for '{self.providerName}', selected first: '{options[0].text.strip()}'")
time.sleep(0.3)
except Exception as e:
print(f"[DDMA Claim step1] Provider selection error (non-fatal): {e}")
# ------------------------------------------------------------------ #
# Step 1 — Navigate directly to /members then search patient #
# (same as eligibility — bypasses onboarding date/location screen) #
# Step 1 — Navigate to claims-submission page, search by Subscriber #
# ID + Birthdate (same search form as the eligibility flow) #
# ------------------------------------------------------------------ #
def step1_search_patient(self):
"""Search for the patient — identical to DDMA eligibility step1.
Does NOT navigate: login already left the browser on the search page."""
"""Open the claims-submission page and search for the patient."""
wait = WebDriverWait(self.driver, 30)
def replace_with_sendkeys(el, value):
el.click()
el.send_keys(Keys.CONTROL, "a")
el.send_keys(Keys.BACKSPACE)
el.send_keys(value)
try:
print(f"[DDMA Claim step1] Current URL: {self.driver.current_url}")
print(f"[DDMA Claim step1] Waiting for member search input...")
claims_url = "https://www.deltadentalma.com/private/provider/claims-submission"
print(f"[DDMA Claim step1] Navigating to claims-submission page: {claims_url}")
self.driver.get(claims_url)
time.sleep(2)
# Select provider from dropdown based on NPI settings
self._select_provider_dropdown()
if "claims-submission" not in self.driver.current_url.lower():
print(f"[DDMA Claim step1] Warning: Ended up at {self.driver.current_url} instead of claims-submission page")
# Fill Member ID
if self.memberId:
try:
member_id_input = wait.until(EC.presence_of_element_located(
(By.XPATH, '//input[@placeholder="Search by member ID"]')
))
member_id_input.clear()
member_id_input.send_keys(self.memberId)
print(f"[DDMA Claim step1] Entered Member ID: {self.memberId}")
time.sleep(0.2)
except Exception as e:
print(f"[DDMA Claim step1] Warning: Could not fill Member ID: {e}")
# Fill DOB
# Fill Birthdate (masked mm/dd/yyyy input — send digits only, mask inserts slashes)
if self.dateOfBirth:
try:
dob_parts = self.dateOfBirth.split("-")
@@ -334,46 +265,47 @@ class AutomationDDMAClaimSubmit:
month = dob_parts[1].zfill(2)
day = dob_parts[2].zfill(2)
dob_container = wait.until(EC.presence_of_element_located(
(By.XPATH, "//div[@data-testid='member-search_date-of-birth']")
birthdate_input = wait.until(EC.element_to_be_clickable(
(By.XPATH, "//input[@data-test-id='InStateSearchBirthdate']")
))
month_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='month' and @contenteditable='true']")
day_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='day' and @contenteditable='true']")
year_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='year' and @contenteditable='true']")
replace_with_sendkeys(month_elem, month)
time.sleep(0.05)
replace_with_sendkeys(day_elem, day)
time.sleep(0.05)
replace_with_sendkeys(year_elem, year)
print(f"[DDMA Claim step1] Filled DOB: {month}/{day}/{year}")
birthdate_input.click()
birthdate_input.send_keys(Keys.CONTROL, "a")
birthdate_input.send_keys(Keys.BACKSPACE)
birthdate_input.send_keys(f"{month}{day}{year}")
print(f"[DDMA Claim step1] Filled Birthdate: {month}/{day}/{year}")
except Exception as e:
print(f"[DDMA Claim step1] Warning: Could not fill DOB: {e}")
print(f"[DDMA Claim step1] Warning: Could not fill Birthdate: {e}")
# Fill Subscriber ID (== member ID)
if self.memberId:
try:
subscriber_input = wait.until(EC.element_to_be_clickable(
(By.XPATH, "//input[@data-test-id='InStateSearchSubscriberId']")
))
subscriber_input.clear()
subscriber_input.send_keys(self.memberId)
print(f"[DDMA Claim step1] Filled Subscriber ID: {self.memberId}")
except Exception as e:
print(f"[DDMA Claim step1] Warning: Could not fill Subscriber ID: {e}")
time.sleep(0.3)
# Click Search
search_btn = wait.until(EC.element_to_be_clickable(
(By.XPATH, '//button[@data-testid="member-search_search-button"]')
(By.XPATH, "//button[@data-test-id='InStateSearchButton']")
))
search_btn.click()
print("[DDMA Claim step1] Clicked Search button")
# Wait for the member search result page to load
WebDriverWait(self.driver, 15).until(
EC.any_of(
EC.presence_of_element_located((By.XPATH, "//tbody//tr")),
EC.presence_of_element_located((By.XPATH, '//div[@data-testid="member-search-result-no-results"]')),
)
)
time.sleep(4)
# Wait for the Members results table to render
try:
no_results = self.driver.find_element(By.XPATH, '//div[@data-testid="member-search-result-no-results"]')
if no_results:
return "ERROR: No patient found with given search criteria"
except Exception:
pass
WebDriverWait(self.driver, 15).until(
EC.presence_of_element_located((By.XPATH, "//table//tbody//tr"))
)
time.sleep(2)
except TimeoutException:
print("[DDMA Claim step1] Warning: No results table appeared within timeout")
return "ERROR: No patient found with given search criteria"
print("[DDMA Claim step1] Search completed")
return "SUCCESS"
@@ -383,54 +315,61 @@ class AutomationDDMAClaimSubmit:
return f"ERROR: step1 failed: {e}"
# ------------------------------------------------------------------ #
# Step 2 — Click patient name → Member Information page #
# Step 2 — Select the matching family member row → Member detail page #
# ------------------------------------------------------------------ #
def step2_open_member_page(self):
"""Navigate to member detail page — same approach as DDMA eligibility step2."""
"""Pick the family member row matching this patient and open their detail page."""
try:
# Wait for the results table to appear, then let it stabilize
try:
WebDriverWait(self.driver, 20).until(
EC.presence_of_element_located((By.XPATH, "//tbody//tr"))
EC.presence_of_element_located((By.XPATH, "//table//tbody//tr"))
)
time.sleep(3) # wait for the list to fully stabilize
time.sleep(2)
except TimeoutException:
print("[DDMA Claim step2] Warning: Results table not found within timeout")
# Wait until the patient name link in the first row is clickable
detail_url = None
PATIENT_LINK_XPATHS = [
"(//table//tbody//tr)[1]//td[1]//a",
"(//tbody//tr)[1]//a[contains(@href,'member-details')]",
"(//tbody//tr)[1]//a[contains(@href,'member')]",
"//a[contains(@href,'member-details')]",
]
for selector in PATIENT_LINK_XPATHS:
rows = self.driver.find_elements(By.XPATH, "//table//tbody//tr")
print(f"[DDMA Claim step2] Found {len(rows)} family member row(s)")
target_row = None
if self.dateOfBirth and rows:
try:
link_el = WebDriverWait(self.driver, 20).until(
EC.element_to_be_clickable((By.XPATH, selector))
)
href = link_el.get_attribute("href")
if href and "member-details" in href:
detail_url = href
print(f"[DDMA Claim step2] Patient link is clickable: {href}")
dob_parts = self.dateOfBirth.split("-")
dob_year, dob_month, dob_day = dob_parts[0], int(dob_parts[1]), int(dob_parts[2])
expected_dob = f"{calendar.month_abbr[dob_month]} {dob_day}, {dob_year}".lower()
for row in rows:
cells = row.find_elements(By.XPATH, "./td")
if len(cells) >= 3 and cells[2].text.strip().lower() == expected_dob:
target_row = row
break
except Exception as e:
print(f"[DDMA Claim step2] DOB matching error (non-fatal): {e}")
if target_row is None and self.lastName and rows:
for row in rows:
if self.lastName.strip().lower() in row.text.lower():
target_row = row
break
except Exception:
continue
if not detail_url:
return "ERROR: step2 failed: could not find member-details link"
if target_row is None and rows:
target_row = rows[0]
print("[DDMA Claim step2] No DOB/name match — defaulting to first family member row")
self.driver.get(detail_url)
print(f"[DDMA Claim step2] Navigating to: {detail_url}")
if target_row is None:
return "ERROR: step2 failed: no family member rows found"
try:
WebDriverWait(self.driver, 15).until(
lambda d: "member-details" in d.current_url
)
print(f"[DDMA Claim step2] Member Information page loaded: {self.driver.current_url}")
except TimeoutException:
print(f"[DDMA Claim step2] Warning — URL: {self.driver.current_url}")
name_link = target_row.find_element(By.XPATH, ".//a")
name_link.click()
print("[DDMA Claim step2] Clicked into patient detail page")
except Exception as e:
print(f"[DDMA Claim step2] Warning: Could not click patient name link: {e}")
return f"ERROR: step2 failed: could not click patient link: {e}"
time.sleep(2) # let the detail page navigate/render
print(f"[DDMA Claim step2] Member detail page loaded: {self.driver.current_url}")
try:
# Wait until Create claim button is visible AND enabled (not just in the DOM)