From d600d94497e367222fe5cc8de5141cc65859e20c Mon Sep 17 00:00:00 2001 From: Gitead Date: Thu, 22 Oct 2026 23:38:36 -0400 Subject: [PATCH] fix: United Ucard claim advances past Upload Documents Fill the required Provider Narrative (claim remarks with shorthand expanded, or a plain-language summary of the procedures), find the Review and Sign button by its navy pill instead of the sidebar step, and verify each step is actually visible since every step lives in the DOM at once. Co-Authored-By: Claude Opus 5.5 --- .../selenium_UnitedUcard_claimSubmitWorker.py | 235 ++++++++++++++++-- 1 file changed, 214 insertions(+), 21 deletions(-) diff --git a/apps/SeleniumService/selenium_UnitedUcard_claimSubmitWorker.py b/apps/SeleniumService/selenium_UnitedUcard_claimSubmitWorker.py index aeeb5941..8153ba28 100644 --- a/apps/SeleniumService/selenium_UnitedUcard_claimSubmitWorker.py +++ b/apps/SeleniumService/selenium_UnitedUcard_claimSubmitWorker.py @@ -46,6 +46,7 @@ class AutomationUnitedUcardClaimSubmit: self.lastName = self.data.get("lastName", "") self.serviceLines = self.data.get("serviceLines", []) self.claimFiles = self.data.get("claimFiles", []) + self.remarks = self.data.get("remarks", "") self.providerName = self.data.get("providerName", "") self.uniteducard_username = self.data.get("uniteducardUsername", "") self.uniteducard_password = self.data.get("uniteducardPassword", "") @@ -792,28 +793,212 @@ class AutomationUnitedUcardClaimSubmit: print(f"[UnitedUcard Claim step5] Attached {attached}/{len(self.claimFiles)} file(s)") - try: - review_label = WebDriverWait(self.driver, 15).until( - EC.presence_of_element_located((By.XPATH, - "//div[normalize-space(text())='Review and Sign']")) - ) - self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", review_label) - time.sleep(0.3) - target = review_label - try: - target = review_label.find_element(By.XPATH, - "ancestor::*[self::button or self::a or @role='button' or @data-state][1]") - except Exception: - pass - try: - target.click() - except Exception: - self.driver.execute_script("arguments[0].click();", target) - print("[UnitedUcard Claim] Clicked 'Review and Sign'") - time.sleep(2) + result = self._fill_provider_narrative() + if isinstance(result, str) and result.startswith("ERROR"): + return result + + if self._click_review_and_sign_button(): return True + return "ERROR: Clicked 'Review and Sign' but the Review and Sign step never appeared (stuck on Upload Documents)" + + @staticmethod + def _describe_procedure(code): + """Plain-language name for a CDT code (falls back to the code itself).""" + m = re.match(r"^D(\d{4})", code.upper()) + n = int(m.group(1)) if m else -1 + if 2140 <= n <= 2161: + return "amalgam filling" + if 2330 <= n <= 2394: + return "composite filling" + if 2510 <= n <= 2664: + return "inlay/onlay" + if 2710 <= n <= 2799: + return "crown" + if 3310 <= n <= 3348: + return "root canal treatment" + if n in (7140, 7210): + return "extraction" + if n == 1110: + return "adult prophylaxis" + if n == 1120: + return "child prophylaxis" + return code + + @staticmethod + def _expand_narrative_shorthand(text): + """Expand office shorthand, e.g. "comp #8 MDB" -> "composite filling on #8 MDB".""" + text = re.sub(r"\bcomp(?:osite)?\b(?:\s+on\b)?", "composite filling on", text, flags=re.I) + text = re.sub(r"\bamal(?:gam)?\b(?:\s+on\b)?", "amalgam filling on", text, flags=re.I) + return text + + def _build_provider_narrative(self): + """ + Claim remarks (with shorthand expanded), or a description built from + the service lines, e.g. "Composite filling on #8 MDB." + """ + text = self._expand_narrative_shorthand((self.remarks or "").strip()) + if not text: + parts = [] + for line in self.serviceLines: + code = str(line.get("procedureCode", "")).strip() + if not code: + continue + tooth = str(line.get("toothNumber") or line.get("tooth") or "").strip() + surface = str(line.get("toothSurface") or line.get("surface") or "").strip().upper() + desc = self._describe_procedure(code) + if tooth: + desc += f" on #{tooth}" + if surface: + desc += f" {surface}" + parts.append(desc) + text = "; ".join(parts) + if text: + text += "." + text = " ".join(text.split()) + if text: + text = text[0].upper() + text[1:] + return text[:250] + + def _fill_provider_narrative(self): + """ + The site requires a Provider Narrative before "Review and Sign" will + advance (the box turns red otherwise). + + Confirmed against the live site (2026-10-22): + + """ + narrative = self._build_provider_narrative() + try: + box = WebDriverWait(self.driver, 10).until( + EC.visibility_of_element_located((By.ID, "narrative")) + ) except TimeoutException: - return "ERROR: 'Review and Sign' button not found on Upload Documents step" + return "ERROR: Provider Narrative box (#narrative) not found on Upload Documents step" + self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", box) + box.click() + box.clear() + box.send_keys(narrative) + print(f"[UnitedUcard Claim] Entered Provider Narrative: {narrative}") + return True + + def _click_review_and_sign_button(self): + """ + Click "Review and Sign" on the Upload Documents step and confirm the + Review and Sign step actually rendered. Every step lives in the DOM + at once (hidden when inactive), so presence checks prove nothing — + wait for a *visible* button, and for #userSignatureText to become + visible afterwards. + + Confirmed against the live site (2026-10-22): +