r""" conditions/bh_h_code_modifier.py — document #12: on a claim whose payer's name starts with "BH", an H code line (H0001-H2037) carries no modifier; telehealth visits are excluded (their GT stays). python tests\test_conditions_bh_h_code_modifier.py Uses hcfa_claim.html with an injected H code row, whose M1-M4 inputs carry the same data-fieldnames as the fixture's own rows. """ import sys import pathlib import inspect ROOT = pathlib.Path(__file__).resolve().parent.parent sys.path.insert(0, str(ROOT / "src")) FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "hcfa_claim.html" UB_FIXTURE = FIXTURE.with_name("ub_claim.html") from ecw_bot.conditions import bh_h_code_modifier as bh # noqa: E402 from ecw_bot.conditions.base import run_all # noqa: E402 from ecw_bot.pages import claim_form # noqa: E402 from ecw_bot.selectors import claim_form as sel # noqa: E402 CASES = [] BH = [{"Name": "BH COLORADO ACCESS"}] MEDICAID = [{"Name": "APM2 Medicaid"}] OFFICE_NOTE = {"opened": "tab", "text": "Office visit. Session Setting: Clinic."} TELE_NOTE = {"opened": "tab", "text": "Session Setting (PoS): TeleHealth"} def case(fn): CASES.append(fn) return fn def _inject_h_row(page, code="H0023", mods=("GT", "", "", ""), row_id="CPTROW_3"): page.evaluate( """(a) => { const tbody = document.querySelector( '#claimCPTCodesTable8888888888888 tbody'); const last = tbody.querySelector('tr:last-child'); const tr = document.createElement('tr'); tr.id = a.rowId; let html = '' + '' + ''; a.mods.forEach((m, i) => { html += ''; }); html += ''; tr.innerHTML = html; tbody.insertBefore(tr, last); tr.querySelectorAll('[data-fieldname^="ClaimCPTMOD"]').forEach(el => el.addEventListener('blur', () => { el.dataset.blurred = '1'; })); }""", {"code": code, "mods": list(mods), "rowId": row_id}) def _mods(page, index): row = page.locator(sel.SEL_CPT_ROWS).nth(index) return [row.locator(s).first.input_value() for s in sel.SEL_CPT_MOD] def _data(page, ins=BH, note=OFFICE_NOTE): data = claim_form.extract(page) data["insurance"] = ins # The fixture's Z13.31 would fire depression_dx_pointer in run_all, and # the injected row has no ICD inputs for it to read. data["icd"] = [] if note is not None: data["progress_notes"] = note return data # ── detect ─────────────────────────────────────────────────────────────────── def _cpt(code="H0023", mod1="GT", mod2=""): return [{"code": "99213", "mod1": "25"}, {"code": code, "mod1": mod1, "mod2": mod2}] @case def detect_true_for_a_bh_payer_h_code_with_a_modifier(): for payer in ("BH COLORADO ACCESS", "BH ROCKY MTN HEALTH 2022", "BH ROCKY MOUNTAIN HEALTH PLANS", " bh rock mtn ub "): data = {"insurance": [{"Name": payer}], "cpt": _cpt(), "progress_notes": OFFICE_NOTE} assert bh.detect("C1", data) is True, payer @case def detect_true_when_only_a_later_modifier_is_set(): data = {"insurance": BH, "cpt": _cpt(mod1="", mod2="HO"), "progress_notes": OFFICE_NOTE} assert bh.detect("C1", data) is True def _billed_h(billed=True, fee="150.00", mod1="GT"): return [{"code": "99213", "billed": True, "billed_fee": "210.00"}, {"code": "H0023", "mod1": mod1, "billed": billed, "billed_fee": fee}] @case def detect_true_for_a_billed_h_code_on_a_non_bh_payer(): """ Operator, 2026-10-12: an H code is billed to BH payers only; on any other payer it is unchecked and zeroed — telehealth or not, modifier or not. """ for payer in ("Carelon Behavioral Health, Inc", "APM2 Medicaid", "HCFA Medicare", "Rocky Mountain Health Prime"): for note in (OFFICE_NOTE, TELE_NOTE): for mod1 in ("GT", ""): data = {"insurance": [{"Name": payer}], "cpt": _billed_h(mod1=mod1), "progress_notes": note} assert bh.detect("C1", data) is True, (payer, note, mod1) @case def detect_true_for_a_non_bh_ub_h_line_with_a_fee_and_no_checkbox(): data = {"insurance": MEDICAID, "cpt": _billed_h(billed=None)} assert bh.detect("C1", data) is True @case def detect_false_for_a_non_bh_h_code_already_unchecked_and_zeroed(): for billed, fee in ((False, "150.00"), (None, "0.00"), (True, "0.00")): data = {"insurance": MEDICAID, "cpt": _billed_h(billed, fee)} assert bh.detect("C1", data) is False, (billed, fee) @case def detect_false_when_the_payer_was_not_read(): for ins in ([{"Name": ""}], []): data = {"insurance": ins, "cpt": _billed_h()} assert bh.detect("C1", data) is False, ins @case def detect_false_when_the_h_code_has_no_modifier(): data = {"insurance": BH, "cpt": _cpt(mod1=""), "progress_notes": OFFICE_NOTE} assert bh.detect("C1", data) is False @case def detect_false_for_codes_that_only_start_with_h(): """Only H0001-H2037 style codes: 'H' plus four digits.""" for code in ("HCG", "H00231", "H002"): data = {"insurance": BH, "cpt": _cpt(code=code), "progress_notes": OFFICE_NOTE} assert bh.detect("C1", data) is False, code @case def detect_false_on_a_telehealth_visit(): data = {"insurance": BH, "cpt": _cpt(), "progress_notes": TELE_NOTE} assert bh.detect("C1", data) is False @case def detect_true_when_the_note_was_not_read(): """Unknown telehealth: it fires, and apply() sends it to Review.""" for note in (None, {"opened": "disabled"}, {"opened": "absent"}, {"opened": "tab", "error": "boom"}): data = {"insurance": BH, "cpt": _cpt()} if note is not None: data["progress_notes"] = note assert bh.detect("C1", data) is True, note # ── apply ──────────────────────────────────────────────────────────────────── @case def apply_clears_every_modifier_on_the_h_code_row_only(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("GT", "HO", "", "")) other_before = [_mods(page, i) for i in (0, 1, 2)] result = bh.apply(page, "C1", _data(page)) assert result.changed is True, result assert result.review == "", result.review assert result.changes == [{"row": 3, "code": "H0023", "before": ["GT", "HO", "", ""], "after": ["", "", "", ""], "ok": True}], \ result.changes assert _mods(page, 3) == ["", "", "", ""] assert [_mods(page, i) for i in (0, 1, 2)] == other_before, \ "non-H-code rows must be left exactly as they were" row = page.locator(sel.SEL_CPT_ROWS).nth(3) assert row.locator(sel.SEL_CPT_MOD1).first.get_attribute("data-blurred") == "1", \ "M1 must be blurred so eCW's updateCPTMod commits the change" @case def an_empty_live_grid_declines_instead_of_passing(page): """Review finding 2 (2026-10-12): the grid did not re-render.""" page.goto(FIXTURE.as_uri()) _inject_h_row(page) data = _data(page) page.evaluate("""() => document.querySelectorAll('tr[id^="CPTROW_"]') .forEach(tr => tr.remove())""") result = bh.apply(page, "C1", data) assert result.changed is False assert result.review == bh.CPT_GRID_NOT_READ, result.review @case def a_fee_eCW_reprices_after_the_modifier_change_is_recorded(page): """Review finding 4: eCW re-prices the line on the modifier's blur.""" page.goto(FIXTURE.as_uri()) _inject_h_row(page) page.evaluate("""() => { const tr = document.getElementById('CPTROW_3'); const fee = tr.querySelector('[data-fieldname="ClaimCPTBilledFee"]'); tr.querySelector('[data-fieldname="ClaimCPTMOD1"]').addEventListener( 'blur', () => setTimeout(() => { fee.value = '120.00'; }, 300)); }""") result = bh.apply(page, "C1", _data(page)) assert result.changes[0]["fee"] == {"before": "150.00", "after": "120.00"}, result.changes text = bh.describe(result) assert "150.00" in text and "120.00" in text, text @case def a_partial_note_counts_as_not_read_on_a_bh_payer(page): """ Review, 2026-10-12: a note whose sections never settled may be missing the telehealth line, so GT must not be removed on its word. """ partial = dict(OFFICE_NOTE, partial=True, partial_sections=0) assert bh.detect("C1", {"insurance": BH, "cpt": _cpt(), "progress_notes": partial}) is True page.goto(FIXTURE.as_uri()) _inject_h_row(page) result = bh.apply(page, "C1", _data(page, note=partial)) assert result.changed is False assert result.review == bh.NOTE_NOT_READ, result.review assert _mods(page, 3)[0] == "GT" @case def a_modifier_eCW_puts_back_after_the_blur_is_not_removed(page): """ The read-back is judged after eCW has had time to react, not straight after the blur: here the modifier comes back 300 ms later. """ page.goto(FIXTURE.as_uri()) _inject_h_row(page) page.evaluate("""() => { const m1 = document.getElementById('CPTROW_3') .querySelector('[data-fieldname="ClaimCPTMOD1"]'); m1.addEventListener('blur', () => setTimeout(() => { m1.value = 'GT'; }, 300)); }""") result = bh.apply(page, "C1", _data(page)) assert result.changes[0]["ok"] is False, result.changes assert result.review == bh.NOT_REMOVED, result.review assert "STILL SET" in bh.describe(result) assert "NOT removed" in bh.summary_comment(result), \ bh.summary_comment(result) @case def apply_on_the_ub_form_clears_and_blurs_every_set_modifier(page): """The UB screen's own ids (uBClaimModN), M2 seeded as well as M1.""" page.goto(UB_FIXTURE.as_uri()) page.evaluate("""() => { const tr = document.querySelectorAll('tr[id^="CPTROW_"]')[0]; const m2 = tr.querySelector('[id^="uBClaimMod2"]'); m2.value = 'HO'; for (const el of tr.querySelectorAll('[id^="uBClaimMod"]')) el.addEventListener('blur', () => { el.dataset.blurred = '1'; }); }""") data = _data(page) assert data["cpt"][0]["code"] == "H0023" result = bh.apply(page, "C1", data) assert result.changes and result.changes[0]["before"][:2] == ["GT", "HO"], \ result.changes assert result.changes[0]["ok"] is True assert _mods(page, 0) == ["", "", "", ""] tr = page.locator(sel.SEL_CPT_ROWS).nth(0) for n in (1, 2): assert tr.locator(f'[id^="uBClaimMod{n}"]').first \ .get_attribute("data-blurred") == "1", f"M{n} not blurred" @case def apply_sends_an_unread_note_to_review_and_changes_nothing(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("GT", "", "", "")) result = bh.apply(page, "C1", _data(page, note={"opened": "disabled"})) assert result.changed is False assert result.review == bh.NOTE_NOT_READ, result.review assert _mods(page, 3) == ["GT", "", "", ""] @case def apply_is_a_no_op_when_the_h_code_has_no_modifier(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("", "", "", "")) result = bh.apply(page, "C1", _data(page)) assert result.changed is False and result.review == "", result @case def describe_names_the_code_and_the_removed_modifiers(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("GT", "HO", "", "")) text = bh.describe(bh.apply(page, "C1", _data(page))) assert "H0023" in text and "GT" in text and "HO" in text, text @case def run_all_on_a_bh_office_visit_fires_this_condition(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("GT", "", "", "")) report = {} results = run_all(page, "C1", _data(page), report=report) assert "bh_h_code_modifier" in report["fired"], report assert "bh_h_code_modifier" in [r.condition_name for r in results], results assert _mods(page, 3) == ["", "", "", ""] def _h_row_state(page, index=3): row = page.locator(sel.SEL_CPT_ROWS).nth(index) return (row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked(), row.locator(sel.SEL_CPT_BILLED_FEE).first.input_value()) @case def apply_on_a_non_bh_payer_unchecks_and_zeroes_the_h_code_only(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("GT", "", "", "")) others = [_h_row_state(page, i) for i in (0, 1, 2)] result = bh.apply(page, "C1", _data(page, ins=MEDICAID, note=TELE_NOTE)) assert result.changed is True and result.review == "", result assert result.changes == [{"row": 3, "code": "H0023", "unchecked": True, "fee": {"before": "150.00", "after": "0.00"}}], result.changes assert _h_row_state(page) == (False, "0.00") assert _mods(page, 3) == ["GT", "", "", ""], "modifier is not this path's" assert [_h_row_state(page, i) for i in (0, 1, 2)] == others @case def apply_on_a_non_bh_payer_with_an_unread_note_still_unchecks(page): """Telehealth does not matter off a BH payer, so the note does not.""" page.goto(FIXTURE.as_uri()) _inject_h_row(page) result = bh.apply(page, "C1", _data(page, ins=MEDICAID, note={"opened": "disabled"})) assert result.changed is True, result assert _h_row_state(page) == (False, "0.00") @case def apply_on_a_non_bh_payer_with_an_empty_live_grid_declines(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page) data = _data(page, ins=MEDICAID) page.evaluate("""() => document.querySelectorAll('tr[id^="CPTROW_"]') .forEach(tr => tr.remove())""") result = bh.apply(page, "C1", data) assert result.changed is False assert result.review == bh.CPT_GRID_NOT_READ, result.review @case def summary_comment_and_describe_follow_the_path_taken(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page) off = bh.apply(page, "C1", _data(page, ins=MEDICAID)) assert bh.summary_comment(off) == ("H code not billed to a non-BH payer " "(unchecked and fee zeroed)"), \ bh.summary_comment(off) # UB, no Assign-To-Patient checkbox: only the fee moved (option A). fee_only = bh.ConditionResult( condition_name=bh.NAME, changed=True, changes=[{"row": 0, "code": "H0023", "fee": {"before": "74.00", "after": "0.00"}}]) assert bh.summary_comment(fee_only) == ("H code not billed to a non-BH " "payer (fee zeroed)"), \ bh.summary_comment(fee_only) text = bh.describe(off) assert "H0023" in text and "150.00" in text and "non-BH" in text, text page.goto(FIXTURE.as_uri()) _inject_h_row(page) on = bh.apply(page, "C1", _data(page)) assert bh.summary_comment(on) == bh.MODIFIER_COMMENT, \ bh.summary_comment(on) @case def run_all_on_a_non_bh_payer_leaves_the_h_code_unchecked_at_zero(page): """priced_line_check (last) must not re-check it: its fee is 0.00.""" page.goto(FIXTURE.as_uri()) _inject_h_row(page) report = {} run_all(page, "C1", _data(page, ins=MEDICAID), report=report) assert "bh_h_code_modifier" in report["fired"], report assert _h_row_state(page) == (False, "0.00") @case def run_all_on_a_bh_telehealth_visit_keeps_gt(page): page.goto(FIXTURE.as_uri()) _inject_h_row(page, mods=("GT", "", "", "")) report = {} run_all(page, "C1", _data(page, note=TELE_NOTE), report=report) assert "bh_h_code_modifier" not in report["fired"], report assert _mods(page, 3)[0] == "GT" def main() -> int: if not FIXTURE.exists(): print(f" FAIL fixture missing: {FIXTURE}") return 1 try: from playwright.sync_api import sync_playwright except ImportError: print(" SKIP playwright is not installed") return 0 failures = 0 with sync_playwright() as pw: browser = pw.chromium.launch(headless=True) page = browser.new_page() for fn in CASES: name = fn.__name__.replace("_", " ") try: if len(inspect.signature(fn).parameters) == 0: fn() else: fn(page) print(f" ok {name}") except AssertionError as exc: print(f" FAIL {name}: {exc}") failures += 1 except Exception as exc: print(f" ERROR {name}: {type(exc).__name__}: {exc}") failures += 1 browser.close() print(f"\n{len(CASES) - failures}/{len(CASES)} passed") return 1 if failures else 0 if __name__ == "__main__": sys.exit(main())