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,628 @@
|
||||
r"""
|
||||
conditions/depression_dx_pointer.py — document condition #6.
|
||||
|
||||
python tests\test_conditions_depression_dx_pointer.py
|
||||
|
||||
Uses hcfa_claim.html rather than ub_claim.html: the real live claim this
|
||||
condition was built for (9103000) is the professional/HCFA screen, and that
|
||||
fixture already carries the full scenario (a wrongly-tagged 99213, a
|
||||
wrongly-tagged G8510, and an untouched J7298) — see its own comments.
|
||||
test_telehealth_fix.py separately proves the underlying primitive
|
||||
(`retag_cpt_icd_pointer`) against the UB form's own id scheme.
|
||||
|
||||
G8431 (positive depression) has no row of its own in that fixture — only
|
||||
G8510 — so `_inject_g8431_row()` adds one the same way the
|
||||
medicare_telehealth tests inject a G2025 row, rather than growing the
|
||||
shared fixture and risking every other test's row-count assumptions
|
||||
(`CPT/HCPCS (3)`, `len(cpt) == 3`, ...). G8431 shares the SAME Z13.31
|
||||
pointer as G8510 — there is no separate ICD code for "positive" depression,
|
||||
only for the underlying CPT/HCPCS code itself.
|
||||
"""
|
||||
|
||||
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"
|
||||
|
||||
from ecw_bot.conditions import depression_dx_pointer # noqa: E402
|
||||
from ecw_bot.conditions.base import ConditionResult, 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 = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
_ICD_WITH_DEPRESSION = [
|
||||
{"#": "1", "Code": "Z30.430", "Name": "Encounter for insertion..."},
|
||||
{"#": "2", "Code": "Z13.31", "Name": "Negative depression screening"},
|
||||
]
|
||||
|
||||
|
||||
def _inject_g8431_row(page, icd1="1", checked=False):
|
||||
"""
|
||||
hcfa_claim.html has no G8431 row of its own (only G8510) — injected the
|
||||
same way the medicare_telehealth tests inject a G2025 row, rather than
|
||||
growing the shared fixture and risking every other test's row-count
|
||||
assumptions (`CPT/HCPCS (3)`, `len(cpt) == 3`, ...).
|
||||
"""
|
||||
page.evaluate(
|
||||
"""(args) => {
|
||||
const tbody = document.querySelector(
|
||||
'#claimCPTCodesTable8888888888888 tbody');
|
||||
const addRowTr = tbody.querySelector('tr:last-child');
|
||||
const tr = document.createElement('tr');
|
||||
tr.id = 'CPTROW_3';
|
||||
tr.innerHTML =
|
||||
'<td><input data-fieldname="claimCPTCode" value="G8431" readonly></td>'
|
||||
+ '<td><input type="checkbox" ng-click="updateBillToIns(3)"'
|
||||
+ (args.checked ? ' checked' : '') + '></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD1" value="' + args.icd1 + '"></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD2" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD3" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD4" value=""></td>';
|
||||
tbody.insertBefore(tr, addRowTr);
|
||||
}""", {"icd1": icd1, "checked": checked})
|
||||
|
||||
|
||||
def _inject_duplicate_target_row(page, code, row_id="CPTROW_3",
|
||||
icd1="1", checked=True, fee="75.00"):
|
||||
"""
|
||||
Injects a SECOND row for a code already on the fixture (G8510, at
|
||||
CPTROW_2) — or for G8431 alongside `_inject_g8431_row` — to prove the
|
||||
duplicate-row suppression step: whichever row is later in DOM order is
|
||||
the one that should get unchecked and fee-zeroed, the first left alone.
|
||||
Carries a Billed Fee field, unlike `_inject_g8431_row`, since that is
|
||||
exactly what this step targets.
|
||||
"""
|
||||
page.evaluate(
|
||||
"""(args) => {
|
||||
const tbody = document.querySelector(
|
||||
'#claimCPTCodesTable8888888888888 tbody');
|
||||
const addRowTr = tbody.querySelector('tr:last-child');
|
||||
const tr = document.createElement('tr');
|
||||
tr.id = args.rowId;
|
||||
tr.innerHTML =
|
||||
'<td><input data-fieldname="claimCPTCode" value="' + args.code + '" readonly></td>'
|
||||
+ '<td><input type="checkbox" ng-click="updateBillToIns(3)"'
|
||||
+ (args.checked ? ' checked' : '') + '></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD1" value="' + args.icd1 + '"></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="' + args.fee + '"></td>';
|
||||
tbody.insertBefore(tr, addRowTr);
|
||||
}""", {"code": code, "rowId": row_id, "icd1": icd1,
|
||||
"checked": checked, "fee": fee})
|
||||
|
||||
|
||||
def _make_claim_already_correct(page):
|
||||
"""
|
||||
Puts the fixture into the state live claim 9131000 was already in
|
||||
before the bot touched it: the first G8510 (CPTROW_2) checked and
|
||||
carrying nothing but the Z13.31 pointer ("2"), and 99213 no longer
|
||||
carrying it — so the retag and check steps have nothing left to do on
|
||||
the fixture's own rows, and anything reported comes from whatever a
|
||||
test injects on top.
|
||||
"""
|
||||
page.evaluate(
|
||||
"""() => {
|
||||
const set = (rowId, field, value) => {
|
||||
document.querySelector(
|
||||
'#' + rowId + ' [data-fieldname="' + field + '"]').value = value;
|
||||
};
|
||||
set('CPTROW_0', 'ClaimCPTICD2', '3');
|
||||
set('CPTROW_0', 'ClaimCPTICD3', '');
|
||||
set('CPTROW_2', 'ClaimCPTICD1', '2');
|
||||
document.querySelector(
|
||||
'#CPTROW_2 [ng-click*="updateBillToIns("]').checked = true;
|
||||
}""")
|
||||
|
||||
|
||||
def _count_checkbox_clicks(page, row_id):
|
||||
"""
|
||||
Counts clicks on one row's "Assign To Patient" checkbox, read back as
|
||||
`window.clicksOn[row_id]`. On the live screen that click is what fires
|
||||
eCW's own `updateBillToIns()`, so a row that must stay excluded should
|
||||
never receive one at all — not merely end up unchecked again.
|
||||
"""
|
||||
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)
|
||||
|
||||
|
||||
@case
|
||||
def detect_true_when_z13_31_is_on_the_claim():
|
||||
data = {"icd": _ICD_WITH_DEPRESSION}
|
||||
assert depression_dx_pointer.detect("C1", data) is True
|
||||
|
||||
|
||||
@case
|
||||
def detect_false_when_z13_31_is_absent():
|
||||
data = {"icd": [{"#": "1", "Code": "Z30.430", "Name": "..."}]}
|
||||
assert depression_dx_pointer.detect("C1", data) is False
|
||||
|
||||
|
||||
@case
|
||||
def detect_false_with_no_icd_key_at_all():
|
||||
assert depression_dx_pointer.detect("C1", {}) is False
|
||||
|
||||
|
||||
@case
|
||||
def apply_retags_g8510_and_strips_99213_on_the_real_claim_shape(page):
|
||||
"""
|
||||
The full real scenario (claim 9103000): the ICD-pointer fix on 99213
|
||||
and G8510, PLUS the separately-reported bug — G8510's checkbox sat
|
||||
unchecked and needed checking once its pointer was corrected. Three
|
||||
changes total: two ICD-pointer edits and one checkbox check, both
|
||||
against G8510's own row.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
data = {"icd": _ICD_WITH_DEPRESSION}
|
||||
result = depression_dx_pointer.apply(page, "9103000", data)
|
||||
assert isinstance(result, ConditionResult)
|
||||
assert result.condition_name == "depression_dx_pointer"
|
||||
assert result.changed is True
|
||||
assert len(result.changes) == 3, result.changes
|
||||
|
||||
icd_changes = [c for c in result.changes if "before" in c]
|
||||
checkbox_changes = [c for c in result.changes if "before" not in c]
|
||||
|
||||
by_code = {c["code"]: c for c in icd_changes}
|
||||
assert set(by_code) == {"99213", "G8510"}, icd_changes
|
||||
assert by_code["G8510"]["after"]["icd"] == ["2", "", "", ""]
|
||||
assert by_code["99213"]["after"]["icd"] == ["1", "3", "", ""]
|
||||
|
||||
assert checkbox_changes == [{"row": 2, "code": "G8510"}], checkbox_changes
|
||||
|
||||
|
||||
@case
|
||||
def apply_retags_g8431_with_the_same_z13_31_pointer_as_g8510(page):
|
||||
"""
|
||||
G8510 (negative depression) and G8431 (positive depression) are
|
||||
different CPT codes but share the SAME one ICD pointer, Z13.31 — there
|
||||
is no separate "positive depression" ICD code (an earlier attempt to
|
||||
use Z13.32 for this was the client's own uncertain guess and has been
|
||||
reverted; only Z13.31 was ever actually given). Both rows end up
|
||||
carrying pointer "2", and 99213 keeps carrying nothing but its own
|
||||
diagnosis once the pointer is retagged onto both target rows.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_g8431_row(page, icd1="1", checked=False)
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
assert result.changed is True
|
||||
|
||||
icd_changes = [c for c in result.changes if "before" in c]
|
||||
checkbox_changes = [c for c in result.changes if "before" not in c]
|
||||
|
||||
by_code = {c["code"]: c for c in icd_changes}
|
||||
assert set(by_code) == {"99213", "G8510", "G8431"}, icd_changes
|
||||
assert by_code["G8510"]["after"]["icd"] == ["2", "", "", ""], by_code
|
||||
assert by_code["G8431"]["after"]["icd"] == ["2", "", "", ""], by_code
|
||||
assert by_code["99213"]["after"]["icd"] == ["1", "3", "", ""], by_code
|
||||
|
||||
checkbox_codes = {c["code"] for c in checkbox_changes}
|
||||
assert checkbox_codes == {"G8510", "G8431"}, checkbox_changes
|
||||
|
||||
g8431_row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert [g8431_row.locator(s).first.input_value() for s in sel.SEL_CPT_ICD] \
|
||||
== ["2", "", "", ""]
|
||||
assert g8431_row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is True
|
||||
|
||||
|
||||
@case
|
||||
def apply_suppresses_a_duplicate_g8510_row_keeping_the_first(page):
|
||||
"""
|
||||
The client's follow-up: G8510 duplicated on the same claim, both
|
||||
checked. The first (CPTROW_2, from the fixture) is retagged and left
|
||||
billed exactly as the base scenario proves; the duplicate (injected,
|
||||
later in DOM order) gets its ICD pointer retagged too but is then
|
||||
unchecked and its Billed Fee zeroed — "only one is enough".
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_duplicate_target_row(page, "G8510", icd1="1", checked=True, fee="75.00")
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
assert result.changed is True
|
||||
assert len(result.changes) == 5, result.changes
|
||||
|
||||
icd_changes = [c for c in result.changes if "before" in c]
|
||||
checked_changes = [c for c in result.changes
|
||||
if "before" not in c and "unchecked" not in c and "fee" not in c]
|
||||
duplicate_changes = [c for c in result.changes
|
||||
if "before" not in c and ("unchecked" in c or "fee" in c)]
|
||||
|
||||
assert {c["row"] for c in icd_changes} == {0, 2, 3}, icd_changes
|
||||
assert checked_changes == [{"row": 2, "code": "G8510"}], checked_changes
|
||||
assert duplicate_changes == [{
|
||||
"row": 3, "code": "G8510",
|
||||
"unchecked": True, "fee": {"before": "75.00", "after": "0.00"},
|
||||
}], duplicate_changes
|
||||
|
||||
first_row = page.locator(sel.SEL_CPT_ROWS).nth(2)
|
||||
assert first_row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is True
|
||||
assert first_row.locator(sel.SEL_CPT_BILLED_FEE).first.input_value() == "65.00", \
|
||||
"the first G8510 row must keep its own fee — only the duplicate is zeroed"
|
||||
|
||||
duplicate_row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert duplicate_row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is False
|
||||
assert duplicate_row.locator(sel.SEL_CPT_BILLED_FEE).first.input_value() == "0.00"
|
||||
|
||||
|
||||
@case
|
||||
def apply_leaves_a_single_g8510_row_alone_no_duplicate_suppression(page):
|
||||
"""
|
||||
No second G8510/G8431 row on the claim — the duplicate step must be a
|
||||
no-op, proven directly against the base fixture's change count (the
|
||||
same 3 changes the non-duplicate test already asserts).
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
assert len(result.changes) == 3, result.changes
|
||||
assert not any("unchecked" in c or "fee" in c for c in result.changes), result.changes
|
||||
|
||||
|
||||
@case
|
||||
def apply_leaves_an_already_excluded_duplicate_g8510_alone(page):
|
||||
"""
|
||||
Live claim 9131000 (Logs\\run_20260929_161613.log): the first G8510 was
|
||||
already checked and tagged with Z13.31 alone, and a second G8510 further
|
||||
down sat unchecked at 0.00 — already excluded. The check step used to
|
||||
check that duplicate and the duplicate step unchecked it again straight
|
||||
after: no net change to the claim, but it was reported as changed, and
|
||||
the review box listed "checkbox checked (was excluded from the claim)"
|
||||
and "checkbox unchecked (duplicate row)" for the same row. There is
|
||||
nothing to do on this claim at all.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_make_claim_already_correct(page)
|
||||
_inject_duplicate_target_row(page, "G8510", icd1="2", checked=False, fee="0.00")
|
||||
_count_checkbox_clicks(page, "CPTROW_3")
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9131000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
duplicate_changes = [c for c in result.changes if c["row"] == 3]
|
||||
assert duplicate_changes == [], duplicate_changes
|
||||
assert result.changed is False, result.changes
|
||||
assert result.changes == [], result.changes
|
||||
|
||||
duplicate_row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert duplicate_row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is False
|
||||
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0, \
|
||||
"the excluded duplicate's checkbox was clicked — updateBillToIns() would fire"
|
||||
|
||||
|
||||
@case
|
||||
def apply_checks_only_the_first_g8510_not_an_excluded_duplicate(page):
|
||||
"""
|
||||
The same already-excluded duplicate as above, but with the first G8510
|
||||
still unchecked (the fixture's own 9103000 shape): that first row is the
|
||||
one to re-include, and only it. The duplicate — unchecked at 0.00 and
|
||||
already carrying Z13.31 — is left exactly as it was.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_duplicate_target_row(page, "G8510", icd1="2", checked=False, fee="0.00")
|
||||
_count_checkbox_clicks(page, "CPTROW_3")
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
checked_changes = [c for c in result.changes
|
||||
if "before" not in c and "unchecked" not in c and "fee" not in c]
|
||||
assert checked_changes == [{"row": 2, "code": "G8510"}], checked_changes
|
||||
assert [c for c in result.changes if c["row"] == 3] == [], result.changes
|
||||
|
||||
rows = page.locator(sel.SEL_CPT_ROWS)
|
||||
assert rows.nth(2).locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is True
|
||||
assert rows.nth(3).locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is False
|
||||
assert rows.nth(3).locator(sel.SEL_CPT_BILLED_FEE).first.input_value() == "0.00"
|
||||
assert page.evaluate("window.clicksOn['CPTROW_3']") == 0, \
|
||||
"the excluded duplicate's checkbox was clicked — updateBillToIns() would fire"
|
||||
|
||||
|
||||
# =============================================================================
|
||||
# Which row is kept per code — the ONE shared helper both the re-include and
|
||||
# the duplicate step use (operator ruling, 2026-09-30)
|
||||
# =============================================================================
|
||||
|
||||
@case
|
||||
def kept_row_is_the_first_row_for_a_code_when_it_is_priced():
|
||||
rows = [(2, "G8510", "65.00"), (3, "G8510", "75.00")]
|
||||
assert claim_form.kept_cpt_row_per_code(rows) == {"G8510": 2}
|
||||
|
||||
|
||||
@case
|
||||
def kept_row_skips_zero_fee_copies_for_the_first_priced_one():
|
||||
rows = [(2, "G8510", "0.00"), (3, "G8510", ""), (4, "G8510", "65.00"),
|
||||
(5, "G8510", "75.00")]
|
||||
assert claim_form.kept_cpt_row_per_code(rows) == {"G8510": 4}
|
||||
|
||||
|
||||
@case
|
||||
def kept_row_is_the_first_by_position_when_every_copy_is_zero_fee():
|
||||
rows = [(2, "G8510", "0.00"), (3, "G8510", ""), (4, "G8510", "$0.00")]
|
||||
assert claim_form.kept_cpt_row_per_code(rows) == {"G8510": 2}
|
||||
|
||||
|
||||
@case
|
||||
def kept_row_counts_a_row_with_no_fee_input_as_eligible():
|
||||
# None: no Billed Fee input at all — unknown, not zero.
|
||||
rows = [(2, "G8510", "0.00"), (3, "G8510", None), (4, "G8510", "65.00")]
|
||||
assert claim_form.kept_cpt_row_per_code(rows) == {"G8510": 3}
|
||||
|
||||
|
||||
@case
|
||||
def kept_row_counts_a_non_numeric_fee_as_eligible():
|
||||
# is_zero_fee("N/A") is False — an unreadable fee is not zero.
|
||||
rows = [(2, "G8510", "0.00"), (3, "G8510", "N/A"), (4, "G8510", "65.00")]
|
||||
assert claim_form.kept_cpt_row_per_code(rows) == {"G8510": 3}
|
||||
|
||||
|
||||
@case
|
||||
def kept_row_is_chosen_per_code():
|
||||
rows = [(1, "G8431", "0.00"), (2, "G8510", "65.00"), (3, "G8431", "40.00"),
|
||||
(4, "G8510", "0.00")]
|
||||
assert claim_form.kept_cpt_row_per_code(rows) == {"G8431": 3, "G8510": 2}
|
||||
|
||||
|
||||
@case
|
||||
def kept_row_of_no_rows_is_empty():
|
||||
assert claim_form.kept_cpt_row_per_code([]) == {}
|
||||
|
||||
|
||||
def _set_fixture_g8510(page, *, checked=None, fee=None):
|
||||
"""Sets the fixture's own G8510 (CPTROW_2) checkbox and/or Billed Fee."""
|
||||
page.evaluate(
|
||||
"""(a) => {
|
||||
const tr = document.getElementById('CPTROW_2');
|
||||
if (a.checked !== null)
|
||||
tr.querySelector('[ng-click*="updateBillToIns("]').checked = a.checked;
|
||||
if (a.fee !== null)
|
||||
tr.querySelector('[data-fieldname="ClaimCPTBilledFee"]').value = a.fee;
|
||||
}""", {"checked": checked, "fee": fee})
|
||||
|
||||
|
||||
def _state(page, index):
|
||||
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_keeps_the_priced_g8510_when_the_first_copy_is_zero_fee(page):
|
||||
"""
|
||||
Operator ruling (2026-09-30): the G8510 kept is the first whose fee is
|
||||
not zero. The fixture's G8510 (unchecked) is set to 0.00 and a later
|
||||
G8510 sits unchecked at 65.00 — that one is re-included, and the 0.00
|
||||
copy is left exactly as it is: already unchecked, already 0.00, never
|
||||
clicked. Before, the re-include step skipped the 0.00 first copy and
|
||||
no G8510 was billed at all.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_set_fixture_g8510(page, fee="0.00")
|
||||
_inject_duplicate_target_row(page, "G8510", icd1="1", checked=False, fee="65.00")
|
||||
_count_checkbox_clicks(page, "CPTROW_2")
|
||||
_count_checkbox_clicks(page, "CPTROW_3")
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
not_retags = [c for c in result.changes if "before" not in c]
|
||||
assert not_retags == [{"row": 3, "code": "G8510"}], result.changes
|
||||
assert (_state(page, 2), _state(page, 3)) == ((False, "0.00"), (True, "65.00"))
|
||||
assert page.evaluate("window.clicksOn") == {"CPTROW_2": 0, "CPTROW_3": 1}
|
||||
|
||||
|
||||
@case
|
||||
def apply_unchecks_a_checked_zero_fee_first_copy_as_the_duplicate(page):
|
||||
"""
|
||||
The same ruling from the other side: the first G8510 checked at 0.00,
|
||||
a later one checked at 75.00. The 75.00 copy is kept and keeps its fee;
|
||||
the 0.00 first copy is now the duplicate, unchecked by the duplicate
|
||||
step (its fee is already 0.00, so it is not written). Called straight
|
||||
here — in a real run zero_fee_uncheck has unchecked it first
|
||||
(test_conditions_zero_fee_uncheck.py).
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_set_fixture_g8510(page, checked=True, fee="0.00")
|
||||
_inject_duplicate_target_row(page, "G8510", icd1="1", checked=True, fee="75.00")
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
not_retags = [c for c in result.changes if "before" not in c]
|
||||
assert not_retags == [{"row": 2, "code": "G8510", "unchecked": True}], result.changes
|
||||
assert (_state(page, 2), _state(page, 3)) == ((False, "0.00"), (True, "75.00"))
|
||||
|
||||
|
||||
@case
|
||||
def apply_never_checks_a_zero_fee_g8510_even_when_every_copy_is_zero_fee(page):
|
||||
# Every copy at zero: the first by position is the one kept, and it is
|
||||
# still never checked — a line billed at zero is never billed.
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_set_fixture_g8510(page, fee="0.00")
|
||||
_inject_duplicate_target_row(page, "G8510", icd1="1", checked=False, fee="0.00")
|
||||
_count_checkbox_clicks(page, "CPTROW_2")
|
||||
_count_checkbox_clicks(page, "CPTROW_3")
|
||||
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
assert [c for c in result.changes if "before" not in c] == [], result.changes
|
||||
assert (_state(page, 2), _state(page, 3)) == ((False, "0.00"), (False, "0.00"))
|
||||
assert page.evaluate("window.clicksOn") == {"CPTROW_2": 0, "CPTROW_3": 0}
|
||||
|
||||
|
||||
@case
|
||||
def re_include_and_duplicate_steps_always_agree_on_the_kept_row(page):
|
||||
"""
|
||||
`ensure_cpt_rows_checked()` and `zero_and_uncheck_duplicate_cpt_rows()`
|
||||
decide "which G8510 is kept" through the one shared helper, so across
|
||||
every shape the row the first may check is never a row the second
|
||||
unchecks — claim 9131000's toggle can not come back through them
|
||||
disagreeing. `(first checked, first fee, later checked, later fee)` ->
|
||||
the row kept.
|
||||
"""
|
||||
shapes = [
|
||||
((True, "65.00", False, "0.00"), 2), # live claim 9131000
|
||||
((False, "65.00", False, "0.00"), 2),
|
||||
((False, "0.00", False, "65.00"), 3),
|
||||
((True, "0.00", True, "65.00"), 3),
|
||||
((False, "", True, "75.00"), 3),
|
||||
((True, "65.00", True, "75.00"), 2),
|
||||
((False, "0.00", False, "0.00"), 2), # every copy zero: first by position
|
||||
]
|
||||
for (first_checked, first_fee, later_checked, later_fee), kept in shapes:
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_set_fixture_g8510(page, checked=first_checked, fee=first_fee)
|
||||
_inject_duplicate_target_row(page, "G8510", checked=later_checked,
|
||||
fee=later_fee)
|
||||
before = {2: _state(page, 2), 3: _state(page, 3)}
|
||||
shape = (first_checked, first_fee, later_checked, later_fee)
|
||||
|
||||
checked = claim_form.ensure_cpt_rows_checked(page, {"G8510", "G8431"})
|
||||
duplicate = claim_form.zero_and_uncheck_duplicate_cpt_rows(
|
||||
page, {"G8510", "G8431"})
|
||||
|
||||
assert {c["row"] for c in checked} <= {kept}, (shape, checked)
|
||||
assert kept not in {c["row"] for c in duplicate}, (shape, duplicate)
|
||||
other = 5 - kept
|
||||
other_checked, other_fee = _state(page, other)
|
||||
assert other_checked is False and claim_form.is_zero_fee(other_fee), \
|
||||
(shape, "the other copy must end excluded", _state(page, other))
|
||||
assert _state(page, kept)[1] == before[kept][1], \
|
||||
(shape, "the kept row's fee must never be written")
|
||||
|
||||
|
||||
@case
|
||||
def apply_logs_why_when_the_claim_is_already_correct(page):
|
||||
"""
|
||||
Live claim 9132000: Z13.31 was already only on G8431, so apply()
|
||||
returned ConditionResult.none — but the log's last word on it was
|
||||
"1 condition(s) matched — about to apply: depression_dx_pointer", which
|
||||
reads as a silent failure. Other conditions name their own "nothing
|
||||
to ..." reason; this one does too, and only on that path — not when
|
||||
the claim did change, and not when there is no Z13.31 to act on.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_make_claim_already_correct(page)
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
result = depression_dx_pointer.apply(page, "9132000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
assert result.changed is False, result.changes
|
||||
logged = out.getvalue()
|
||||
assert "[COND depression_dx_pointer 9132000]" in logged, logged
|
||||
assert "nothing to change" in logged, logged
|
||||
|
||||
page.goto(FIXTURE.as_uri())
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
assert result.changed is True, result.changes
|
||||
assert "nothing to change" not in out.getvalue(), out.getvalue()
|
||||
|
||||
page.goto(FIXTURE.as_uri())
|
||||
out = io.StringIO()
|
||||
with contextlib.redirect_stdout(out):
|
||||
depression_dx_pointer.apply(
|
||||
page, "9103000", {"icd": [{"#": "1", "Code": "Z30.430", "Name": "..."}]})
|
||||
assert "nothing to change" not in out.getvalue(), out.getvalue()
|
||||
|
||||
|
||||
@case
|
||||
def apply_is_a_no_op_when_the_claim_has_no_depression_dx(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
data = {"icd": [{"#": "1", "Code": "Z30.430", "Name": "..."}]}
|
||||
result = depression_dx_pointer.apply(page, "9103000", data)
|
||||
assert result.changed is False
|
||||
assert result.changes == []
|
||||
|
||||
|
||||
@case
|
||||
def describe_names_each_retagged_row(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
text = depression_dx_pointer.describe(result)
|
||||
assert "G8510" in text and "99213" in text, text
|
||||
|
||||
|
||||
@case
|
||||
def describe_names_g8431_too_when_it_is_on_the_claim(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_g8431_row(page, icd1="1", checked=False)
|
||||
result = depression_dx_pointer.apply(page, "9103000", {"icd": _ICD_WITH_DEPRESSION})
|
||||
text = depression_dx_pointer.describe(result)
|
||||
assert "G8431" in text, text
|
||||
|
||||
|
||||
@case
|
||||
def run_all_fires_depression_dx_pointer_alongside_telehealth_independently(page):
|
||||
"""
|
||||
Against the REAL registry, not fakes - proves this condition is wired
|
||||
into conditions.CONDITIONS and does not collide with the telehealth
|
||||
conditions, which key off progress_notes/insurance, not icd.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
data = {"icd": _ICD_WITH_DEPRESSION}
|
||||
results = run_all(page, "9103000", data)
|
||||
assert len(results) == 1, results
|
||||
assert results[0].condition_name == "depression_dx_pointer", results
|
||||
|
||||
|
||||
def _run_cases() -> 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
|
||||
|
||||
|
||||
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())
|
||||
Reference in new issue
Block a user