Files
ecw-charge-validation/tests/test_conditions_zero_fee_uncheck.py
T

835 lines
36 KiB
Python

r"""
conditions/zero_fee_uncheck.py — the operator's common rule for every
claim (2026-09-30): a CPT line that is checked but billed at zero gets
unchecked. Only that direction here — an unchecked line is never touched by
this condition; the reverse half (unchecked with a billed fee -> checked)
is conditions/priced_line_check.py's, registered last, and tested in
test_conditions_priced_line_check.py.
python tests\test_conditions_zero_fee_uncheck.py
Pure-data cases first (the shared zero-fee predicate, then detect()), then
page cases against BOTH claim screens: hcfa_claim.html, whose own rows carry
the live-verified "Assign To Patient" checkbox (`ng-click="updateBillToIns(
$index)"`), and ub_claim.html, whose own rows carry a plain checkbox with no
ng-click at all — so on the UB form a UB-style row is injected with the
checkbox vaccine_claim.html's rows use (`id="claimCPTBillToInsChk..."
ng-click="updateBillToIns(n)"`), and the fixture's own rows stand in for a
row whose checkbox state is unknown.
Rows are injected with page.evaluate, the same way
test_conditions_depression_dx_pointer.py's `_inject_duplicate_target_row`
adds its row, rather than growing either shared fixture and its row-count
assumptions (`CPT/HCPCS (3)`, `len(cpt) == 3`, ...).
The live evidence this rule was built against: of 39 saved claim snapshots,
exactly one checked zero-fee line — 85018 (Hemoglobin) at 0.00 — which is
the code the injected rows below use.
The last two cases are where this rule meets another condition's own rule
— depression's choice of which G8510 to keep, and vaccine_admin_codes'
checked-admin test on vaccine_claim.html. Both used to be PINNED as known
gaps; the operator ruled on both on 2026-09-30, and they now assert the
ruled outcome: the priced G8510 is the one kept, and a checked zero-fee
admin line no longer counts as "admin already billed".
"""
import io
import sys
import pathlib
import inspect
import contextlib
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 = pathlib.Path(__file__).resolve().parent / "fixtures" / "ub_claim.html"
VACCINE_FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "vaccine_claim.html"
from ecw_bot import conditions # noqa: E402
from ecw_bot.conditions import zero_fee_uncheck as zfu # noqa: E402
from ecw_bot.conditions import (bh_h_code_modifier, # noqa: E402
dental_dummy_codes,
dental_payer_check,
depression_dx_pointer, dummy_codes,
injection_admin_code,
medicare_pps_code,
j2930_j2001_update, lab_codes, medicaid_uncheck_cpts,
t1017_non_billable,
medicare_billing_screening,
medicare_telehealth,
medicare_vaccine_a6, medicare_wellness_history,
medicaid_pregnancy_th,
priced_line_check, telehealth,
vaccine_admin_codes, vaccine_z23_pointer)
from ecw_bot.conditions.base import ( # noqa: E402
CPT_TAB_NOT_CONFIRMED, ConditionResult, run_all)
from ecw_bot.pages import claim_form # noqa: E402
from ecw_bot.selectors import claim_form as sel # noqa: E402
CASES = []
def case(fn):
CASES.append(fn)
return fn
_HEMOGLOBIN = "85018"
def _cpt(code, billed=True, fee="0.00"):
"""One `extract()["cpt"]` row, cut down to what detect() reads."""
return {"code": code, "billed": billed, "billed_fee": fee}
def _inject_row(page, code, row_id, *, screen="hcfa", checked=True,
fee="0.00", has_checkbox=True, has_fee=True):
"""
Adds one real CPT row (a `CPTROW_` id) above the grid's trailing "*"
entry row. `screen` picks the row's own markup:
* "hcfa" — hcfa_claim.html's shape: `data-fieldname` fields.
* "ub" — the UB form's `uBClaim*<n>` id prefixes, with the
"Assign To Patient" checkbox vaccine_claim.html's rows carry
(ub_claim.html's own rows have none that SEL_CPT_ASSIGN_TO_PATIENT
matches).
`has_checkbox` / `has_fee` drop the checkbox or the Billed Fee input
entirely — the rows whose state is unknown, which must be left alone.
"""
page.evaluate(
"""(a) => {
const n = a.rowId.replace('CPTROW_', '');
const tbody = document.querySelector(a.screen === 'ub'
? '#claimCPTCodesTable9999999999999 tbody'
: '#claimCPTCodesTable8888888888888 tbody');
const entryRow = tbody.querySelector('tr:last-child');
const esc = s => String(s).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
const box = !a.hasCheckbox ? '' : (a.screen === 'ub'
? '<input type="checkbox" id="claimCPTBillToInsChk9999999999999' + n + '"'
+ ' ng-click="updateBillToIns(' + n + ')"'
: '<input type="checkbox" ng-click="updateBillToIns(' + n + ')"')
+ (a.checked ? ' checked' : '') + '>';
const codeInput = a.screen === 'ub'
? '<input id="uBClaimCPTCode' + n + '" type="text" value="' + esc(a.code) + '" readonly>'
: '<input data-fieldname="claimCPTCode" type="text" value="' + esc(a.code) + '" readonly>';
const feeInput = !a.hasFee ? '' : (a.screen === 'ub'
? '<input type="text" id="uBClaimBillFee' + n + '" value="' + esc(a.fee) + '">'
: '<input data-fieldname="ClaimCPTBilledFee" type="text" value="' + esc(a.fee) + '">');
const tr = document.createElement('tr');
tr.id = a.rowId;
tr.setAttribute('ng-repeat', 'cpt in CPTCodes');
tr.innerHTML = '<td>' + box + '</td><td>' + codeInput + '</td>'
+ '<td>' + feeInput + '</td>';
tbody.insertBefore(tr, entryRow);
}""", {"code": code, "rowId": row_id, "screen": screen,
"checked": checked, "fee": fee,
"hasCheckbox": has_checkbox, "hasFee": has_fee})
def _set_row(page, row_id, *, checked=None, fee=None):
"""Sets one existing row's checkbox and/or Billed Fee in place."""
page.evaluate(
"""(a) => {
const tr = document.getElementById(a.rowId);
if (a.checked !== null)
tr.querySelector('[ng-click*="updateBillToIns("]').checked = a.checked;
if (a.fee !== null)
tr.querySelector('[id^="uBClaimBillFee"], [data-fieldname="ClaimCPTBilledFee"]')
.value = a.fee;
}""", {"rowId": row_id, "checked": checked, "fee": fee})
def _count_checkbox_clicks(page, row_id):
"""
Counts clicks on one row's "Assign To Patient" checkbox, read back as
`window.clicksOn[row_id]` — the same helper
test_conditions_depression_dx_pointer.py uses. On the live screen that
click is what fires eCW's own `updateBillToIns()`, so a row this rule
must leave alone should never receive one at all.
"""
page.evaluate(
"""(rowId) => {
window.clicksOn = window.clicksOn || {};
window.clicksOn[rowId] = 0;
document.querySelector('#' + rowId + ' [ng-click*="updateBillToIns("]')
.addEventListener('click', () => { window.clicksOn[rowId] += 1; });
}""", row_id)
def _watch_fee_writes(page):
"""
Counts `input`/`change` events on every Billed Fee input on the grid as
`window.feeWrites` — Playwright's fill() fires both, so a zero here
proves the fee was never written, not merely left with the same value.
Call after every row has been injected.
"""
page.evaluate(
"""(s) => {
window.feeWrites = 0;
document.querySelectorAll('tr[id^="CPTROW_"]').forEach(tr => {
tr.querySelectorAll(s).forEach(el => {
el.addEventListener('input', () => { window.feeWrites += 1; });
el.addEventListener('change', () => { window.feeWrites += 1; });
});
});
}""", sel.SEL_CPT_BILLED_FEE)
def _row_state(page, index):
"""(checked or None, fee or None) for the index-th real CPT row."""
row = page.locator(sel.SEL_CPT_ROWS).nth(index)
box = row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first
fee = row.locator(sel.SEL_CPT_BILLED_FEE).first
return (box.is_checked() if box.count() else None,
fee.input_value() if fee.count() else None)
# =============================================================================
# The shared zero-fee predicate
# =============================================================================
@case
def is_zero_fee_reads_blank_and_every_zero_spelling_as_zero():
for value in ("", " ", None, "0", "0.00", "0.0", ".00", " 0.00 ",
"$0.00", "0,000.00"):
assert claim_form.is_zero_fee(value) is True, repr(value)
@case
def is_zero_fee_reads_a_priced_or_non_numeric_fee_as_not_zero():
for value in ("65.00", "0.01", "$1,200.00", "1,200.00", "N/A", "abc",
"--", "0.00 USD"):
assert claim_form.is_zero_fee(value) is False, repr(value)
# =============================================================================
# detect()
# =============================================================================
@case
def detect_true_for_a_checked_line_billed_at_0_00():
# The one live case: 85018 (Hemoglobin), checked, at 0.00.
data = {"cpt": [_cpt("99213", fee="210.00"), _cpt(_HEMOGLOBIN, fee="0.00")]}
assert zfu.detect("C1", data) is True
@case
def detect_true_for_a_checked_line_with_a_blank_fee():
assert zfu.detect("C1", {"cpt": [_cpt(_HEMOGLOBIN, fee="")]}) is True
@case
def detect_true_for_a_checked_line_billed_at_plain_0():
assert zfu.detect("C1", {"cpt": [_cpt(_HEMOGLOBIN, fee="0")]}) is True
@case
def detect_false_for_an_unchecked_line_at_0_00():
# Already excluded — this condition never touches an unchecked line; the
# reverse half (unchecked AND priced -> checked) is priced_line_check's,
# and 0.00 is not priced anyway.
data = {"cpt": [_cpt("99213", fee="210.00"),
_cpt("G8510", billed=False, fee="0.00")]}
assert zfu.detect("C1", data) is False
@case
def detect_false_when_the_checkbox_state_is_unknown():
# billed None: extract() found no "Assign To Patient" checkbox on the row.
assert zfu.detect("C1", {"cpt": [_cpt(_HEMOGLOBIN, billed=None, fee="0.00")]}) is False
assert zfu.detect("C1", {"cpt": [{"code": _HEMOGLOBIN, "billed_fee": "0.00"}]}) is False
@case
def detect_false_for_a_checked_line_whose_fee_was_never_read():
# billed_fee None: extract() found no Billed Fee input on the row.
# apply() (uncheck_zero_fee_cpt_rows) leaves such a row alone, so
# detect() must not fire on it either — unknown is not zero (review
# finding, 2026-09-30).
assert zfu.detect("C1", {"cpt": [_cpt(_HEMOGLOBIN, fee=None)]}) is False
@case
def detect_false_for_a_checked_priced_line():
data = {"cpt": [_cpt("99213", fee="210.00"), _cpt("J7298", fee="1200.00")]}
assert zfu.detect("C1", data) is False
@case
def detect_false_for_a_non_numeric_fee():
assert zfu.detect("C1", {"cpt": [_cpt(_HEMOGLOBIN, fee="N/A")]}) is False
@case
def detect_false_with_no_cpt_lines():
assert zfu.detect("C1", {"cpt": []}) is False
assert zfu.detect("C1", {}) is False
assert zfu.detect("C1", None) is False
# =============================================================================
# apply() — the professional/HCFA screen
# =============================================================================
@case
def apply_unchecks_a_checked_zero_fee_line_on_the_hcfa_screen(page):
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, fee="0.00")
_count_checkbox_clicks(page, "CPTROW_3")
_watch_fee_writes(page)
result = zfu.apply(page, "9103000", {})
assert isinstance(result, ConditionResult)
assert result.condition_name == "zero_fee_uncheck"
assert result.changed is True
assert result.changes == [{"row": 3, "code": _HEMOGLOBIN, "fee": "0.00"}], result.changes
assert _row_state(page, 3) == (False, "0.00")
assert page.evaluate("window.clicksOn['CPTROW_3']") == 1
# Priced lines keep their checkbox and fee; G8510 (unchecked, 65.00) too.
assert _row_state(page, 0) == (True, "210.00")
assert _row_state(page, 1) == (True, "1200.00")
assert _row_state(page, 2) == (False, "65.00")
assert page.evaluate("window.feeWrites") == 0, "the Billed Fee was written"
# A second pass has nothing left to uncheck.
assert zfu.apply(page, "9103000", {}).changed is False
@case
def apply_leaves_priced_and_unchecked_lines_alone(page):
page.goto(FIXTURE.as_uri())
_inject_row(page, "G8510", "CPTROW_3", checked=False, fee="0.00")
_count_checkbox_clicks(page, "CPTROW_3")
_count_checkbox_clicks(page, "CPTROW_0")
_watch_fee_writes(page)
result = zfu.apply(page, "9131000", {})
assert result.changed is False, result.changes
assert result.changes == [], result.changes
assert _row_state(page, 0) == (True, "210.00")
assert _row_state(page, 1) == (True, "1200.00")
assert _row_state(page, 2) == (False, "65.00")
assert _row_state(page, 3) == (False, "0.00")
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0, \
"an unchecked line must never be clicked — updateBillToIns() would fire"
assert page.evaluate("window.clicksOn['CPTROW_0']") == 0
assert page.evaluate("window.feeWrites") == 0
@case
def apply_unchecks_every_zero_spelling_and_never_writes_the_fee(page):
"""
Blank, "0", ".00" and "0.00" are all zero — each line is unchecked, and
its fee is reported and left exactly as read: never normalised to 0.00,
never filled, never blurred.
"""
page.goto(FIXTURE.as_uri())
_inject_row(page, "85018", "CPTROW_3", fee="")
_inject_row(page, "85025", "CPTROW_4", fee="0")
_inject_row(page, "36415", "CPTROW_5", fee=".00")
_inject_row(page, "81002", "CPTROW_6", fee="0.00")
_watch_fee_writes(page)
result = zfu.apply(page, "9103000", {})
assert result.changes == [
{"row": 3, "code": "85018", "fee": ""},
{"row": 4, "code": "85025", "fee": "0"},
{"row": 5, "code": "36415", "fee": ".00"},
{"row": 6, "code": "81002", "fee": "0.00"},
], result.changes
assert [_row_state(page, i) for i in (3, 4, 5, 6)] == [
(False, ""), (False, "0"), (False, ".00"), (False, "0.00")]
assert page.evaluate("window.feeWrites") == 0, "the Billed Fee was written"
@case
def apply_leaves_a_non_numeric_fee_alone(page):
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, fee="N/A")
_count_checkbox_clicks(page, "CPTROW_3")
result = zfu.apply(page, "9103000", {})
assert result.changed is False, result.changes
assert _row_state(page, 3) == (True, "N/A")
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0
@case
def apply_leaves_a_checked_row_with_no_fee_input_alone(page):
# No Billed Fee input at all: the fee is unknown, not zero.
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, has_fee=False)
_count_checkbox_clicks(page, "CPTROW_3")
result = zfu.apply(page, "9103000", {})
assert result.changed is False, result.changes
assert _row_state(page, 3) == (True, None)
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0
@case
def apply_leaves_a_zero_fee_row_with_no_checkbox_alone(page):
# No "Assign To Patient" checkbox: whether it is billed is unknown.
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", fee="0.00", has_checkbox=False)
_watch_fee_writes(page)
result = zfu.apply(page, "9103000", {})
assert result.changed is False, result.changes
assert _row_state(page, 3) == (None, "0.00")
assert page.evaluate("window.feeWrites") == 0
# =============================================================================
# apply() — the UB Claim form
# =============================================================================
@case
def apply_unchecks_a_checked_zero_fee_line_on_the_ub_form(page):
page.goto(UB_FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_2", screen="ub", checked=True, fee="0.00")
_inject_row(page, "90791", "CPTROW_3", screen="ub", checked=True, fee="150.00")
_inject_row(page, "G8510", "CPTROW_4", screen="ub", checked=False, fee="0.00")
_count_checkbox_clicks(page, "CPTROW_2")
_count_checkbox_clicks(page, "CPTROW_3")
_count_checkbox_clicks(page, "CPTROW_4")
_watch_fee_writes(page)
result = zfu.apply(page, "9104000", {})
assert result.changes == [{"row": 2, "code": _HEMOGLOBIN, "fee": "0.00"}], result.changes
assert _row_state(page, 2) == (False, "0.00")
assert _row_state(page, 3) == (True, "150.00")
assert _row_state(page, 4) == (False, "0.00")
assert page.evaluate("window.clicksOn['CPTROW_2']") == 1
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0
assert page.evaluate("window.clicksOn['CPTROW_4']") == 0
assert page.evaluate("window.feeWrites") == 0, "the Billed Fee was written"
@case
def apply_leaves_ub_rows_without_the_assign_to_patient_checkbox_alone(page):
"""
ub_claim.html's own rows carry a plain checkbox with no
`updateBillToIns(` ng-click — not the live-verified "Assign To Patient"
box — so their billed state is unknown, and a 0.00 fee on one of them
must not be acted on.
"""
page.goto(UB_FIXTURE.as_uri())
page.evaluate("() => { document.getElementById('uBClaimBillFee0').value = '0.00'; }")
_watch_fee_writes(page)
result = zfu.apply(page, "9104000", {})
assert result.changed is False, result.changes
assert page.locator("#claimCPTBillToInsChk0").is_checked() is True
assert page.locator("#uBClaimBillFee0").input_value() == "0.00"
assert page.evaluate("window.feeWrites") == 0
# =============================================================================
# apply() — no change, and a tab that cannot be confirmed
# =============================================================================
@case
def apply_logs_why_when_there_is_nothing_to_uncheck(page):
page.goto(FIXTURE.as_uri())
out = io.StringIO()
with contextlib.redirect_stdout(out):
result = zfu.apply(page, "9103000", {})
assert result == ConditionResult.none("zero_fee_uncheck"), result
logged = out.getvalue()
assert "[COND zero_fee_uncheck 9103000]" in logged, logged
assert "nothing to uncheck" in logged, logged
# Only on that path — not when a line was actually unchecked.
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, fee="0.00")
out = io.StringIO()
with contextlib.redirect_stdout(out):
result = zfu.apply(page, "9103000", {})
assert result.changed is True, result.changes
assert "nothing to uncheck" not in out.getvalue(), out.getvalue()
@case
def apply_leaves_the_claim_as_read_when_the_cpt_tab_cannot_be_confirmed(page):
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, fee="0.00")
def tab_that_raises(page_):
raise RuntimeError("No CPT/HCPCS tab matched (simulated)")
real = claim_form.open_cpt_hcpcs_tab
claim_form.open_cpt_hcpcs_tab = tab_that_raises
out = io.StringIO()
try:
with contextlib.redirect_stdout(out):
result = zfu.apply(page, "9103000", {})
finally:
claim_form.open_cpt_hcpcs_tab = real
assert result.changed is False, result.changes
assert _row_state(page, 3) == (True, "0.00"), "nothing may be touched"
assert "leaving the claim as read" in out.getvalue(), out.getvalue()
# Declined, not none(): never checked is not clean (review item D2).
assert result.review == CPT_TAB_NOT_CONFIRMED, result
# =============================================================================
# describe()
# =============================================================================
@case
def describe_names_each_unchecked_line_and_its_fee(page):
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, fee="0.00")
result = zfu.apply(page, "9103000", {})
text = zfu.describe(result)
lines = text.splitlines()
assert lines[0] == "Checked CPT line(s) with a zero billed fee unchecked:", text
assert lines[1:] == [" Row 4 (85018): checked with billed fee 0.00 -> unchecked"], text
@case
def describe_says_blank_rather_than_printing_nothing():
result = ConditionResult(condition_name="zero_fee_uncheck", changed=True,
changes=[{"row": 0, "code": _HEMOGLOBIN, "fee": ""}])
text = zfu.describe(result)
assert " Row 1 (85018): checked with billed fee (blank) -> unchecked" in text, text
# =============================================================================
# The registry
# =============================================================================
@case
def registered_first_in_conditions():
"""
First, so it runs on the untouched claim and every specific condition
runs after it on a consistent grid and has the last word — and the
specific conditions keep their own order behind it. The one entry
after them is this rule's reverse half, priced_line_check, registered
LAST (2026-09-30) so it only ever sees a line an exclusion condition
has already zeroed — see test_conditions_priced_line_check.py.
"""
assert conditions.CONDITIONS[0] is zfu, conditions.CONDITIONS
assert conditions.CONDITIONS[1:] == [
telehealth, medicare_telehealth, depression_dx_pointer,
j2930_j2001_update, dummy_codes, dental_dummy_codes,
dental_payer_check, medicaid_uncheck_cpts, lab_codes,
t1017_non_billable,
medicare_billing_screening, medicare_vaccine_a6,
medicare_wellness_history, medicaid_pregnancy_th,
bh_h_code_modifier, vaccine_z23_pointer, vaccine_admin_codes,
injection_admin_code,
medicare_pps_code,
priced_line_check,
], conditions.CONDITIONS
assert conditions.BY_NAME["zero_fee_uncheck"] is zfu
assert zfu.NAME == "zero_fee_uncheck"
# =============================================================================
# run_all() against the REAL registry
# =============================================================================
@case
def run_all_fires_zero_fee_uncheck_alone_on_a_plain_zero_fee_line(page):
"""
Against the REAL registry, not fakes — proves this condition is wired
into conditions.CONDITIONS, and that a plain zero-fee line sets off
nothing else. `icd` is left out of the data on purpose: the fixture
carries Z13.31, which would fire depression_dx_pointer on its own
account (the next case covers the two together). And the fixture's own
G8510 is checked first: as served it sits unchecked at 65.00, which
priced_line_check (the reverse half, 2026-09-30) would check on its
own account too.
"""
page.goto(FIXTURE.as_uri())
_set_row(page, "CPTROW_2", checked=True)
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, fee="0.00")
data = {"cpt": claim_form.extract(page)["cpt"]}
results = run_all(page, "9103000", data)
assert [r.condition_name for r in results] == ["zero_fee_uncheck"], results
assert results[0].changes == [{"row": 3, "code": _HEMOGLOBIN, "fee": "0.00"}]
@case
def run_all_unchecks_a_zero_fee_g8510_and_depression_does_not_re_check_it(page):
"""
The toggle this rule would otherwise start — the same check/uncheck
shape fixed for claim 9131000: the first G8510 is checked at 0.00 and
the claim carries Z13.31, so both zero_fee_uncheck and
depression_dx_pointer fire. zero_fee_uncheck runs first and unchecks
it; depression's own "re-include the first G8510" step must then leave
it unchecked, because a zero-fee line is never billed. Before, it
checked it straight back: two contradictory changes reported for one
row, and updateBillToIns() fired twice on a line that must stay out.
"""
page.goto(FIXTURE.as_uri())
_set_row(page, "CPTROW_2", checked=True, fee="0.00")
_count_checkbox_clicks(page, "CPTROW_2")
data = claim_form.extract(page)
assert zfu.detect("9103000", data) is True
assert depression_dx_pointer.detect("9103000", data) is True
results = run_all(page, "9103000", data)
names = [r.condition_name for r in results]
assert names == ["zero_fee_uncheck", "depression_dx_pointer"], names
assert results[0].changes == [{"row": 2, "code": "G8510", "fee": "0.00"}], results[0].changes
depression_checks = [c for c in results[1].changes
if "before" not in c and "unchecked" not in c and "fee" not in c]
assert depression_checks == [], ("depression re-checked the zero-fee G8510",
results[1].changes)
# Its pointer is still retagged — only the check is withheld.
retagged = {c["row"] for c in results[1].changes if "before" in c}
assert retagged == {0, 2}, results[1].changes
assert _row_state(page, 2) == (False, "0.00")
assert page.evaluate("window.clicksOn['CPTROW_2']") == 1, \
"the G8510 checkbox was clicked more than once — a check/uncheck toggle"
# =============================================================================
# run_all() — where the rule meets another condition's own rule. Both were
# PINNED as known gaps until the operator ruled on them (2026-09-30); each
# now asserts the ruled outcome. See zero_fee_uncheck.py's module docstring.
# =============================================================================
def _inject_full_g8510_row(page, row_id, *, checked, fee):
"""
A second G8510 on hcfa_claim.html, after the fixture's own (CPTROW_2),
carrying all four ICD pointer fields — depression_dx_pointer's retag
step reads every one, the same as
test_conditions_depression_dx_pointer.py's `_inject_duplicate_target_row`.
"""
page.evaluate(
"""(a) => {
const tbody = document.querySelector(
'#claimCPTCodesTable8888888888888 tbody');
const entryRow = tbody.querySelector('tr:last-child');
const n = a.rowId.replace('CPTROW_', '');
const tr = document.createElement('tr');
tr.id = a.rowId;
tr.innerHTML =
'<td><input data-fieldname="claimCPTCode" value="G8510" readonly></td>'
+ '<td><input type="checkbox" ng-click="updateBillToIns(' + n + ')"'
+ (a.checked ? ' checked' : '') + '></td>'
+ '<td><input data-fieldname="ClaimCPTICD1" value="1"></td>'
+ '<td><input data-fieldname="ClaimCPTICD2" value=""></td>'
+ '<td><input data-fieldname="ClaimCPTICD3" value=""></td>'
+ '<td><input data-fieldname="ClaimCPTICD4" value=""></td>'
+ '<td><input data-fieldname="ClaimCPTBilledFee" value="' + a.fee + '"></td>';
tbody.insertBefore(tr, entryRow);
}""", {"rowId": row_id, "checked": checked, "fee": fee})
@case
def run_all_keeps_the_priced_g8510_when_the_first_copy_is_zero_fee(page):
"""
Operator ruling (2026-09-30), flipped from a PINNED gap. The first
G8510 is checked at 0.00 and a later G8510 checked at 65.00 — a shape
none of the 39 snapshots shows. zero_fee_uncheck unchecks the 0.00
copy. depression then keeps the first G8510 whose fee is NOT zero —
the 65.00 one — so its re-include step has nothing to check (it is
already checked) and its duplicate step has nothing to do on the 0.00
copy (already unchecked, already 0.00). The priced G8510 stays billed
with its fee; before the ruling it was zeroed, and no G8510 was left
billed at all.
Without zero_fee_uncheck in the registry, depression reaches the same
end state on its own: the 0.00 copy is now the duplicate, and its
duplicate step unchecks it.
"""
def claim():
page.goto(FIXTURE.as_uri())
_set_row(page, "CPTROW_2", checked=True, fee="0.00")
_inject_full_g8510_row(page, "CPTROW_3", checked=True, fee="65.00")
_count_checkbox_clicks(page, "CPTROW_2")
_count_checkbox_clicks(page, "CPTROW_3")
return claim_form.extract(page)
results = run_all(page, "9103000", claim())
names = [r.condition_name for r in results]
assert names == ["zero_fee_uncheck", "depression_dx_pointer"], names
assert results[0].changes == [{"row": 2, "code": "G8510", "fee": "0.00"}], \
results[0].changes
not_retags = [c for c in results[1].changes if "before" not in c]
assert not_retags == [], ("depression checked, unchecked or zeroed a "
"G8510", results[1].changes)
assert {c["row"] for c in results[1].changes} == {0, 2, 3}, results[1].changes
assert (_row_state(page, 2), _row_state(page, 3)) == \
((False, "0.00"), (True, "65.00")), "the priced G8510 stays billed"
assert page.evaluate("window.clicksOn") == {"CPTROW_2": 1, "CPTROW_3": 0}
# Without the rule: depression's own duplicate step unchecks the 0.00
# copy, the priced one untouched.
results = run_all(page, "9103000", claim(),
conditions=[c for c in conditions.CONDITIONS
if c is not zfu])
assert [r.condition_name for r in results] == ["depression_dx_pointer"], results
assert [c for c in results[0].changes if "before" not in c] == \
[{"row": 2, "code": "G8510", "unchecked": True}], results[0].changes
assert (_row_state(page, 2), _row_state(page, 3)) == \
((False, "0.00"), (True, "65.00"))
assert page.evaluate("window.clicksOn") == {"CPTROW_2": 1, "CPTROW_3": 0}
_TDAP = "TETANUS DIPHTHERIA TOXOIDS AND ACELLULAR PERTUSSIS VACC"
_ADMIN_1ST = "IMMUNIZATION ADMIN SINGLE OR COMBINATION"
@case
def run_all_adds_the_admin_code_when_the_only_admin_line_is_checked_at_zero(page):
"""
Operator ruling (2026-09-30), flipped from a PINNED known gap. `run_all`
runs every detect() on the claim as read, where the 90471 is CHECKED at
0.00. It used to count as "admin already billed", so vaccine_admin_codes
never fired; zero_fee_uncheck then unchecked that 90471 and the claim
left with the vaccine billed and no admin code — document #18's own
case. Now a checked zero-fee admin line does not count (zero_fee_uncheck,
first, will uncheck it before vaccine_admin_codes' apply() runs): both
fire, the 0.00 line ends unchecked, and a new 90471 is added.
The same claim with the 90471 ALREADY unchecked (live claim 9132000's
shape) reaches the same grid, with vaccine_admin_codes alone.
"""
def claim(admin_checked):
page.goto(VACCINE_FIXTURE.as_uri())
page.evaluate("rows => window.seedCptRows(rows)", [
{"code": "99396", "title": "PREVENTIVE VISIT EST AGE 40-64",
"fee": "250.00"},
{"code": "90734", "title": _TDAP, "fee": "60.00", "icd1": "2"},
{"code": "90471", "title": _ADMIN_1ST, "fee": "0.00",
"checked": admin_checked},
])
return claim_form.extract(page)
def grid():
return [(r["code"], r["billed"], r["billed_fee"])
for r in claim_form.extract(page)["cpt"]]
fixed = [("99396", True, "250.00"), ("90734", True, "60.00"),
("90471", False, "0.00"), ("90471", True, "30.00")]
data = claim(admin_checked=True)
assert vaccine_admin_codes.detect("9113000", data) is True
results = run_all(page, "9113000", data)
assert [r.condition_name for r in results] == \
["zero_fee_uncheck", "vaccine_admin_codes"], results
assert results[0].changes == [{"row": 2, "code": "90471", "fee": "0.00"}], \
results[0].changes
added = [c for c in results[1].changes if c.get("added")]
assert [c["code"] for c in added] == ["90471"], results[1].changes
assert grid() == fixed, grid()
results = run_all(page, "9113000", claim(admin_checked=False))
assert [r.condition_name for r in results] == ["vaccine_admin_codes"], results
assert grid() == fixed, grid()
@case
def run_all_does_not_fire_on_a_checked_row_with_no_fee_input(page):
# The page-level shape: extract() reports the row's fee as None, not
# "" — so detect() and apply() agree and the log never claims a
# zero-fee line that apply() then cannot find.
page.goto(FIXTURE.as_uri())
_inject_row(page, _HEMOGLOBIN, "CPTROW_3", checked=True, has_fee=False)
data = claim_form.extract(page)
row = [r for r in data["cpt"] if r["code"] == _HEMOGLOBIN]
assert [(r["billed"], r["billed_fee"]) for r in row] == [(True, None)], row
assert zfu.detect("9103000", data) is False
# =============================================================================
# PINNED, NEEDS AN OPERATOR RULING — medicare_telehealth on a claim whose
# G2025 is checked at 0.00. Today's outcome, not the settled one (review
# finding, 2026-09-30).
# =============================================================================
_MEDICARE_DATA = {
"progress_notes": {"text": "Session Setting (PoS): TeleHealth"},
"insurance": [{"Name": "Humana Medicare"}],
}
@case
def pinned_needs_ruling_a_g2025_checked_at_zero_leaves_no_line_billed(page):
"""
PINNED, NEEDS A RULING. The claim a G2025 added at 0.00 leaves for the
next run (the Fee Schedule priced it at nothing). zero_fee_uncheck,
first, unchecks the 0.00 G2025 — the common rule. medicare_telehealth
then asks `cpt_code_present("G2025")`, which ignores the checkbox,
counts G2025 as confirmed, and unchecks and zeroes every OTHER line:
the claim ends with NO billed line, which that step's own gate says it
must never do. Requiring a checked, non-zero G2025 there changes
document #22's behaviour — beyond rulings A/B/C.
"""
page.goto(FIXTURE.as_uri())
_inject_row(page, "G2025", "CPTROW_3", checked=True, fee="0.00")
data = dict(_MEDICARE_DATA, cpt=claim_form.extract(page)["cpt"])
results = run_all(page, "9103000", data)
assert [r.condition_name for r in results] == ["zero_fee_uncheck", "medicare_telehealth"], results
assert results[0].changes == [{"row": 3, "code": "G2025", "fee": "0.00"}]
billed = [r["code"] for r in claim_form.extract(page)["cpt"] if r["billed"]]
assert billed == [], ("today: no line left billed", billed)
def _run_cases() -> int:
for fixture in (FIXTURE, UB_FIXTURE, VACCINE_FIXTURE):
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
def main() -> int:
from ecw_bot import settings as _cfg
# These cases exercise eCW's own click and Add flow, as a LIVE-WRITE run
# does. VERIFY clicks no box and adds no row (eCW saves both at once,
# 2026-10-16): that is tests/test_verify_no_live_writes.py.
orig = _cfg.SAVE_CORRECTIONS
_cfg.SAVE_CORRECTIONS = True
try:
return _run_cases()
finally:
_cfg.SAVE_CORRECTIONS = orig
if __name__ == "__main__":
sys.exit(main())