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): +