Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one commit with no earlier history: the tracked files were de-identified first (synthetic claim ids and patient data only; see Context.md section 7). Credentials, run logs, screenshots, claim reports and client workbooks are gitignored and not included. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,459 @@
|
||||
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 = '<td><input type="checkbox" ng-click="updateBillToIns(3)" checked></td>'
|
||||
+ '<td><input data-fieldname="claimCPTCode" value="' + a.code + '" readonly></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTPOS" type="text" value="11"></td>';
|
||||
a.mods.forEach((m, i) => {
|
||||
html += '<td><input data-fieldname="ClaimCPTMOD' + (i + 1)
|
||||
+ '" type="text" value="' + m + '"></td>';
|
||||
});
|
||||
html += '<td><input data-fieldname="ClaimCPTBilledFee" value="150.00"></td>';
|
||||
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())
|
||||
Reference in new issue
Block a user