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
Vendored
+106
@@ -0,0 +1,106 @@
|
||||
<!doctype html>
|
||||
<!-- A claim form's side menu + eCW's Account Inquiry ("Patient Inquiry
|
||||
Detail") modal, after the bindings in Dom.txt (2026-10-20). Made-up
|
||||
claims only. A test sets window.HISTORY_PAGES (one array of claims per
|
||||
page: {id, date, codes}) before clicking the launcher. The CPT rows are
|
||||
loaded only once "Show CPT Payments" is ticked, as eCW reloads them. -->
|
||||
<html><head><meta charset="utf-8"><title>Account Inquiry fixture</title>
|
||||
<style>.ng-hide{display:none}.modal{display:none}.modal.in{display:block}</style>
|
||||
</head><body>
|
||||
<div class="claim-form" id="claimFormFixture">
|
||||
<ul class="side-menu">
|
||||
<li><a>UB Claim</a></li>
|
||||
<li><a>Documents</a></li>
|
||||
<li><a class="cursor" title="Account Inquiry" ng-click="vm.loadSmartPanel(item);" style="display:none"><label class="ft-label">Account Inquiry</label></a></li>
|
||||
<li><a class="cursor" title="Documents" ng-click="vm.loadSmartPanel(item);"><label class="ft-label">Documents</label></a></li>
|
||||
<li><a id="acctInq" class="cursor" title="Account Inquiry" ng-click="vm.loadSmartPanel(item);"><label class="ft-label">Account Inquiry</label></a></li>
|
||||
<li><a>Actions</a></li>
|
||||
</ul>
|
||||
<button id="formCancel">Cancel</button>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="piModal">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close"
|
||||
ng-click="validateandclosePatientInquiryDetailModal(false)">×</button>
|
||||
<h4 class="modal-title">Patient Inquiry Detail</h4>
|
||||
</div>
|
||||
<div id="accountsLoadingImg" style="display:none">Loading…</div>
|
||||
<div class="pull-left doc-att mt5">
|
||||
<label class="checkbox-inline" ng-repeat="filter in collClaimFiltersFieldNames track by $index"><input type="checkbox" ng-model="filter.mod" ng-click="reloadClaimData($index,$event)" data-f="cpt"> Show CPT Payments</label>
|
||||
<label class="checkbox-inline" ng-repeat="filter in collClaimFiltersFieldNames track by $index"><input type="checkbox" ng-model="filter.mod" ng-click="reloadClaimData($index,$event)"> Show CPT Payment Totals</label>
|
||||
<label class="checkbox-inline" ng-repeat="filter in collClaimFiltersFieldNames track by $index"><input type="checkbox" ng-model="filter.mod" ng-click="reloadClaimData($index,$event)"> Show Voided Claims</label>
|
||||
</div>
|
||||
<div>
|
||||
<button class="btn" ng-click="pibuttonprev()">PREV</button>
|
||||
<input type="text" ng-model="curpage" id="pigoToPageNo" value="1">
|
||||
of <span ng-bind="totalPage">1</span>
|
||||
<button class="btn" ng-click="pibuttonnext()">NEXT</button>
|
||||
</div>
|
||||
<table class="table claimsDtlMainTbl"><tbody id="claims"></tbody></table>
|
||||
<button class="btn" ng-click="validateandclosePatientInquiryDetailModal(true)">OK</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.HISTORY_PAGES = window.HISTORY_PAGES || [[]];
|
||||
let page = 0, showCpt = false;
|
||||
const modal = document.getElementById('piModal');
|
||||
const loader = document.getElementById('accountsLoadingImg');
|
||||
const cptBox = document.querySelector('input[data-f="cpt"]');
|
||||
|
||||
function render() {
|
||||
const pages = window.HISTORY_PAGES;
|
||||
document.getElementById('pigoToPageNo').value = String(page + 1);
|
||||
document.querySelector('[ng-bind="totalPage"]').textContent =
|
||||
String(pages.length);
|
||||
const body = document.getElementById('claims');
|
||||
body.innerHTML = '';
|
||||
for (const c of pages[page] || []) {
|
||||
const cpt = showCpt ? c.codes.map(code =>
|
||||
`<tr class="cptPaymentRow ng-scope" ng-repeat="charge in payment.Charges track by $index">` +
|
||||
`<td></td><td ng-bind="getCPTPaymentDescription(charge,true)">${code}:SOME SERVICE…</td>` +
|
||||
`<td ng-bind="charge.BilledFee">10.00</td></tr>`).join('') : '';
|
||||
const tr = document.createElement('tr');
|
||||
tr.setAttribute('ng-repeat', 'claim in claims track by $index');
|
||||
tr.innerHTML =
|
||||
`<td><table><tbody>` +
|
||||
`<tr class="claimMainRow"><td ng-bind="claim.ClaimId">${c.id}</td>` +
|
||||
`<td ng-bind="claim.ServiceDate | date:'MM/dd/yyyy'">${c.date}</td>` +
|
||||
`<td ng-bind="getClaimDescription(claim,true)">Charges (Pr:XX Fac: YY)</td></tr>` +
|
||||
`<tr class="claimChildRow0" ng-repeat="payment in claim.Payments track by $index">` +
|
||||
`<td><table><tbody><tr><td>Medicare</td></tr>${cpt}</tbody></table></td></tr>` +
|
||||
`</tbody></table></td>`;
|
||||
body.append(tr);
|
||||
}
|
||||
}
|
||||
|
||||
function loading(then) {
|
||||
loader.style.display = 'block';
|
||||
setTimeout(() => { then(); loader.style.display = 'none'; }, 300);
|
||||
}
|
||||
|
||||
document.getElementById('acctInq').addEventListener('click', () => {
|
||||
if (window.NO_INQUIRY) return;
|
||||
page = 0;
|
||||
setTimeout(() => { modal.classList.add('in'); loading(render); }, 200);
|
||||
});
|
||||
cptBox.addEventListener('click', () => {
|
||||
showCpt = cptBox.checked;
|
||||
loading(render);
|
||||
});
|
||||
document.querySelector('[ng-click="pibuttonnext()"]').addEventListener(
|
||||
'click', () => {
|
||||
if (page < window.HISTORY_PAGES.length - 1) { page++; loading(render); }
|
||||
});
|
||||
document.querySelector(
|
||||
'button.close[ng-click="validateandclosePatientInquiryDetailModal(false)"]')
|
||||
.addEventListener('click', () => {
|
||||
if (window.STUCK_OPEN) return;
|
||||
window.CLOSED_WITH = 'X';
|
||||
modal.classList.remove('in');
|
||||
});
|
||||
document.querySelector(
|
||||
'[ng-click="validateandclosePatientInquiryDetailModal(true)"]')
|
||||
.addEventListener('click', () => { window.CLOSED_WITH = 'OK'; });
|
||||
</script>
|
||||
</body></html>
|
||||
Vendored
+208
@@ -0,0 +1,208 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
A cut-down of eCW's Dental Claim screen (mapped from live claim 9125000 —
|
||||
DentaQuest; the claim number, patient and account here are made up). A THIRD claim-form shape, distinct from both the UB form and
|
||||
the professional/HCFA "Claim" screen: its own modal entirely
|
||||
(id="dentalClaim<timestamp>"), closeDntl() instead of
|
||||
closeUBClaimScreen()/closeClaimScreen(), saveData() instead of
|
||||
saveAllData(true)/saveAllData(), and a completely different tab set
|
||||
("ICD & CDT" / "Services Rendered" rather than "ICD & CPT" / "CPT/HCPCS").
|
||||
|
||||
Since 2026-10-12 Dental claims run the same conditions as UB/HCFA, so
|
||||
this fixture carries the screen's real shapes (from Dom.txt's capture):
|
||||
the Services Rendered grid, the ICD list, the insurance grid, the Billing
|
||||
Notes and the Prog. Notes button — see the comments on each. The claim
|
||||
number and the web_cptcode inputs are also what
|
||||
claim_form.wait_claim_loaded() watches (review item D7). Codes are
|
||||
generic CDT/CPT codes; nothing comes from a real claim. Every id carries the per-open timestamp 7777777777777,
|
||||
deliberately different from both hcfa_claim.html's 8888888888888 and
|
||||
ub_claim.html's 9999999999999.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div class="modal fade bluetheme billing-width dentalClaimModalBackdrop right-panel-mode in" id="dentalClaim7777777777777" style="display:block">
|
||||
<div class="modal-dialog visible" style="width: 1286px;">
|
||||
<div class="modal-content" id="dentalClaimModalContent7777777777777">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Dental Claim
|
||||
<span class="ml5 fs11 patient-identifier-span">TESTPATIENT, Demo Jan 01, 1970 (56 yo F) Acc No. 000000</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
<table class="table"><tbody><tr>
|
||||
<td><label>Claim No</label></td>
|
||||
<td><span ng-bind="ClaimData.claimNo">5000000</span></td>
|
||||
</tr></tbody></table>
|
||||
<!--
|
||||
"Services Rendered" (2026-10-12, Dental claims run the conditions):
|
||||
the live grid's shape from Dom.txt — rows are
|
||||
tr[ng-repeat="CPT in CPTCodes track by $index"], no CPTROW_ id; the
|
||||
fields go by data-fieldname (web_cptcode, web_icd1-4, web_units,
|
||||
web_billedFee); the checkbox is the same updateBillToIns() one as
|
||||
UB/HCFA. No per-row POS and no modifier columns. The trailing "new
|
||||
code" row has no data-fieldname. Codes are generic CDT/CPT codes, fees
|
||||
made up.
|
||||
-->
|
||||
<table id="claimCPTCodesTable7777777777777" class="table">
|
||||
<thead><tr>
|
||||
<th></th><th> </th><th>Tth No</th><th>Srfc</th>
|
||||
<th>Description of Service Performed</th><th>DOS</th><th>Proc No</th>
|
||||
<th>ICD1</th><th>ICD2</th><th>ICD3</th><th>ICD4</th><th>Units</th>
|
||||
<th>Fee</th><th></th>
|
||||
</tr></thead>
|
||||
<tbody ng-model="CPTCodes">
|
||||
<tr ng-repeat="CPT in CPTCodes track by $index">
|
||||
<td>1</td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk0" ng-click="updateBillToIns($index)" checked></td>
|
||||
<td id="TthNo0"></td><td></td><td>PERIODIC ORAL EVALUATION</td>
|
||||
<td><input id="DentalClaimCPTSDOS0" data-fieldname="DentalClaimCPTSDOS" type="text" value="09/01/2026"></td>
|
||||
<td><input id="dentalClaimCPTCode0" data-fieldname="web_cptcode" type="text" value="D0120"></td>
|
||||
<td><input id="dentalClaimIcd10" data-fieldname="web_icd1" type="text" value="1"></td>
|
||||
<td><input id="dentalClaimIcd20" data-fieldname="web_icd2" type="text" value=""></td>
|
||||
<td><input id="dentalClaimIcd30" data-fieldname="web_icd3" type="text" value=""></td>
|
||||
<td><input id="dentalClaimIcd40" data-fieldname="web_icd4" type="text" value=""></td>
|
||||
<td><input id="DentalClaimCPTUnits0" data-fieldname="web_units" type="text" value="1"></td>
|
||||
<td><input id="DentalClaimIpt1ngR0" data-fieldname="web_billedFee" type="text" value="45.00"></td>
|
||||
<td><button id="DentalClaimBtn1ngR0" ng-click="removeCPT($index)"></button></td>
|
||||
</tr>
|
||||
<tr ng-repeat="CPT in CPTCodes track by $index">
|
||||
<td>2</td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk1" ng-click="updateBillToIns($index)" checked></td>
|
||||
<td id="TthNo1"></td><td></td><td>DENTAL CONSULT</td>
|
||||
<td><input id="DentalClaimCPTSDOS1" data-fieldname="DentalClaimCPTSDOS" type="text" value="09/01/2026"></td>
|
||||
<td><input id="dentalClaimCPTCode1" data-fieldname="web_cptcode" type="text" value="DCONS"></td>
|
||||
<td><input id="dentalClaimIcd11" data-fieldname="web_icd1" type="text" value="1"></td>
|
||||
<td><input id="dentalClaimIcd21" data-fieldname="web_icd2" type="text" value=""></td>
|
||||
<td><input id="dentalClaimIcd31" data-fieldname="web_icd3" type="text" value=""></td>
|
||||
<td><input id="dentalClaimIcd41" data-fieldname="web_icd4" type="text" value=""></td>
|
||||
<td><input id="DentalClaimCPTUnits1" data-fieldname="web_units" type="text" value="1"></td>
|
||||
<td><input id="DentalClaimIpt1ngR1" data-fieldname="web_billedFee" type="text" value="30.00"></td>
|
||||
<td><button id="DentalClaimBtn1ngR1" ng-click="removeCPT($index)"></button></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td></td><td></td><td></td><td></td><td></td><td></td>
|
||||
<td ng-click="enterCPT($event)"><input type="text" id="editableTDTextNewCPTcode" ng-model="newCPT"></td>
|
||||
<td></td><td></td><td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<!-- "ICD & CDT": the diagnosis list (#icdTable #addTable on the live screen) -->
|
||||
<div class="allbrd white-bg icdTbl" id="icdTable">
|
||||
<table class="table" id="addTable">
|
||||
<thead><tr><th></th><th>Code</th><th>Name</th><th></th></tr></thead>
|
||||
<tbody ng-model="ICDData">
|
||||
<tr ng-repeat="ICDCode in ICDData">
|
||||
<td>1</td>
|
||||
<td ng-click="enterICD($event)"><input type="text" value="Z01.20"></td>
|
||||
<td>Encounter for dental exam</td>
|
||||
<td><button id="DentalClaimBtn2ngR0" ng-click="removeICD($index)"></button></td>
|
||||
</tr>
|
||||
<tr id="2">
|
||||
<td></td>
|
||||
<td ng-click="enterICD($event)"><input type="text" id="txtnewIcd7777777777777"></td>
|
||||
<td></td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Insurance (#DentalClaimTbl2 on the live screen) -->
|
||||
<table id="DentalClaimTbl2" class="table">
|
||||
<thead><tr>
|
||||
<th></th><th>Bill To</th><th>Ins Id</th><th>Name</th><th>State</th>
|
||||
<th>Subscriber No</th><th>Rel</th><th>Insured</th><th>Group No</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="insurance in Insurances">
|
||||
<td><span ng-bind="insurance.sequanceNo">P</span></td>
|
||||
<td><input id="DentalClaimIpt20ngR0" type="checkbox" ng-model="insurance.PrimIns" checked></td>
|
||||
<td><span ng-bind="insurance.InsId">1001</span></td>
|
||||
<td><span ng-bind="insurance.InsName">DentaQuest</span></td>
|
||||
<td><span ng-bind="insurance.InsState">CO</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberNo">X000000</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberRel">Self</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberName">TESTPATIENT, Demo</span></td>
|
||||
<td><span ng-bind="insurance.GroupNo"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<textarea id="dentalClaimNotes">Routine visit.</textarea>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer grey-bg claimOptionMenu">
|
||||
<button type="button" class="btn btn-lgrey btn-sm btn-xs pull-left" ng-click="viewProgNote()"
|
||||
id="dentalClaimProgressNoteBtn7777777777777">Prog. Notes</button>
|
||||
<button id="cancelDentalClaimButton" type="button" class="btn btn-lgrey btn-sm btn-xs pull-right ml10"
|
||||
ng-click="closeDntl();">Cancel</button>
|
||||
<button id="DentalClaimBtn17" type="button" class="btn btn-blue btn-sm btn-xs pull-right ml10"
|
||||
ng-click="saveData()">OK</button>
|
||||
</div>
|
||||
|
||||
</div></div></div>
|
||||
|
||||
<script>
|
||||
// Cancel and OK both hide the same backdrop the real closeDntl() /
|
||||
// saveData() do — same reasoning hcfa_claim.html's script block uses.
|
||||
document.getElementById('cancelDentalClaimButton').addEventListener('click', function () {
|
||||
document.getElementById('dentalClaim7777777777777').style.display = 'none';
|
||||
});
|
||||
document.getElementById('DentalClaimBtn17').addEventListener('click', function () {
|
||||
document.getElementById('dentalClaim7777777777777').style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// ?footer=wrapped (review of unit C, 2026-10-05): wraps Cancel in a div
|
||||
// whose id also starts with "dentalClaim", inside the modal. The form's
|
||||
// window is found by walking up from the visible Cancel; an id-prefix-only
|
||||
// window selector stopped at this wrapper, which holds neither the claim
|
||||
// number nor the service lines. Runs before the load script so ?load=
|
||||
// combines with it. The click handler stays on the moved button.
|
||||
(function () {
|
||||
if (!/[?&]footer=wrapped(&|$)/.test(location.search)) return;
|
||||
var cancel = document.getElementById('cancelDentalClaimButton');
|
||||
var wrap = document.createElement('div');
|
||||
wrap.id = 'dentalClaimFooter7777777777777';
|
||||
cancel.parentNode.insertBefore(wrap, cancel);
|
||||
wrap.appendChild(cancel);
|
||||
})();
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Angular's load, simulated (review item D7) — opt-in through the query
|
||||
// string, so every test that loads this fixture plainly still sees a form
|
||||
// that is already filled.
|
||||
// ?load=delayed claim number and codes blank, filled 800 ms later
|
||||
// ?load=never blank, and never filled (the timeout path)
|
||||
// ?load=wrong filled 800 ms later with a DIFFERENT claim number
|
||||
// ?load=noclaimno claim-number element removed; codes filled 800 ms later
|
||||
// Codes are blanked as the .value PROPERTY only, which is where Angular
|
||||
// keeps them: the value="" attribute still holds the template's code, so a
|
||||
// reader that took the attribute would call the form loaded at once.
|
||||
(function () {
|
||||
var m = /[?&]load=(\w+)/.exec(location.search);
|
||||
if (!m) return;
|
||||
var mode = m[1];
|
||||
var no = document.querySelector('[id^="dentalClaim"] [ng-bind="ClaimData.claimNo"]');
|
||||
var codes = Array.prototype.slice.call(
|
||||
document.querySelectorAll('[id^="dentalClaim"] [data-fieldname="web_cptcode"]'));
|
||||
var want = codes.map(function (e) { return e.value; });
|
||||
var claimNo = no ? no.textContent : '';
|
||||
if (no) no.textContent = '';
|
||||
if (mode === 'noclaimno' && no) { no.remove(); no = null; }
|
||||
codes.forEach(function (e) { e.value = ''; });
|
||||
if (mode === 'never') return;
|
||||
setTimeout(function () {
|
||||
if (no) no.textContent = (mode === 'wrong' ? '5000001' : claimNo);
|
||||
codes.forEach(function (e, i) { e.value = want[i]; });
|
||||
}, 800);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+499
@@ -0,0 +1,499 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
A cut-down of the professional/HCFA "Claim" screen eCW served on 2026-09-17
|
||||
for claim 9103000 (Commercial payer — United Health Care).
|
||||
|
||||
Reproduced on purpose, rather than saved from the live page: the real form
|
||||
is patient data and must never be committed. This screen is NOT the UB
|
||||
Claim form (no #UBClaimBtn17, no #BillType) — is_ub_claim_form() must
|
||||
return False against this fixture, the same as it does against the real
|
||||
claim.
|
||||
|
||||
The real DOM has none of the UB form's ids at all — no #UBClaimDiv,
|
||||
#BillType, #icdCodesTable, #UBClaimTbl3/4/10/17, uBClaimPOS*/uBClaimMod1*.
|
||||
Every id here carries the per-open timestamp 8888888888888 instead
|
||||
(deliberately different from ub_claim.html's 9999999999999, so a selector
|
||||
that had one baked in fails against the other).
|
||||
|
||||
What matters most, reproduced faithfully from the real DOM:
|
||||
* there is only ONE tab covering ICD and CPT/HCPCS together
|
||||
("ICD & CPT") — no separate CPT/HCPCS tab exists to click at all
|
||||
* the CPT rows still carry the stable `CPTROW_0`/`CPTROW_1` ids, same as
|
||||
the UB form
|
||||
* but the per-field ids are entirely different (`billingClaimIpt4ngR0`,
|
||||
not `uBClaimPOS0`) and are positional/sequential — NOT safe to match
|
||||
by prefix the way the UB form's are. `data-fieldname` is what is
|
||||
actually stable here (`ClaimCPTPOS`, `ClaimCPTMOD1`, ... and, the one
|
||||
exception, `claimCPTCode` lower-case for the code cell) — that is
|
||||
the real live-verified attribute this fixture exists to test against.
|
||||
* the whole form sits inside angular-ui-bootstrap's own modal window —
|
||||
`class="modal fade ng-isolate-scope claimModalBackdrop in"`,
|
||||
`window-class="claimModalBackdrop"`, no id — which closes on Escape
|
||||
WITHOUT running closeClaimScreen() (verified live, claims 9109000 and
|
||||
9119000, 2026-09-29). The UB form's wrapper is `ubClaimModalBackdrop`
|
||||
instead and the Dental screen has none.
|
||||
* the payer is in TWO tables, neither timestamped: `#billingClaimTbl5`
|
||||
under "ICD & CPT" (just P + Name, and its Name cell carries an edit
|
||||
input as well as the name), and `#billingClaimTbl8` under
|
||||
"Insurances & Payment" — the same columns as the UB form's
|
||||
#UBClaimTbl3, plus Ins Id. Subscriber values here are made up.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div class="modal fade ng-isolate-scope claimModalBackdrop in" role="dialog" tabindex="-1"
|
||||
modal-window window-class="claimModalBackdrop" ng-click="close($event)"
|
||||
style="display:block; z-index:1050">
|
||||
<div class="modal-dialog ui-draggable"><div class="modal-content">
|
||||
<div id="div_Billingclaim8888888888888">
|
||||
|
||||
<div class="modal fade bluetheme billing-width right-panel-mode in" id="claimModal8888888888888" style="display:block">
|
||||
<div class="modal-dialog visible" style="width: 1286px;">
|
||||
<div class="modal-content" id="claimModalContent8888888888888">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Claim
|
||||
<span class="ml5 fs11 patient-identifier-span">VALENZUELA, Fabiola Oct 19, 1984 (41 yo F) Acc No. 550201</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<table class="table"><tbody><tr>
|
||||
<td><label>Claim No</label></td>
|
||||
<td><span ng-bind="ClaimData.InvId" title="9103000">9103000</span></td>
|
||||
</tr></tbody></table>
|
||||
|
||||
<ul id="billingClaimUl1" class="nav">
|
||||
<li class="active"><a class="cursor" data-toggle="tab" data-target="#icd8888888888888">ICD & CPT</a></li>
|
||||
<li><a class="cursor" data-toggle="tab" data-target="#inp8888888888888">Insurances & Payment</a></li>
|
||||
<li><a class="cursor" data-toggle="tab" data-target="#addinf8888888888888">Additional Information</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content claim-tab">
|
||||
<div class="tab-pane active" id="icd8888888888888">
|
||||
|
||||
<h4>ICD Codes (2)</h4>
|
||||
<table id="icdCodesTable8888888888888" class="table table-bordered blue-fade-table">
|
||||
<thead><tr><th>#</th><th></th><th>Code</th><th>Name</th><th></th></tr></thead>
|
||||
<tbody ng-model="ICDCodes">
|
||||
<tr ng-repeat="icd in ICDCodes">
|
||||
<td class="text-center">1</td>
|
||||
<td class="text-center"><input type="checkbox"></td>
|
||||
<td title="Z30.430"><input id="billingClaimIpt2ngR0" data-fieldname="ClaimICDCode" type="text" value="Z30.430"></td>
|
||||
<td title="Encounter for insertion of intrauterine contraceptive device">Encounter for insertion of intrauterine contraceptive device</td>
|
||||
<td class="text-center"><button></button></td>
|
||||
</tr>
|
||||
<tr ng-repeat="icd in ICDCodes">
|
||||
<td class="text-center">2</td>
|
||||
<td class="text-center"><input type="checkbox"></td>
|
||||
<td title="Z13.31"><input id="billingClaimIpt2ngR1" data-fieldname="ClaimICDCode" type="text" value="Z13.31"></td>
|
||||
<td title="Negative depression screening">Negative depression screening</td>
|
||||
<td class="text-center"><button></button></td>
|
||||
</tr>
|
||||
<tr id="3">
|
||||
<td class="text-center">*</td>
|
||||
<td></td>
|
||||
<td><input type="text" id="txtnewIcd8888888888888" ng-model="newICD"></td>
|
||||
<td></td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4>Insurances</h4>
|
||||
<table id="billingClaimTbl5" class="table table-bordered blue-fade-table nomargin">
|
||||
<thead><tr><th></th><th>Name</th></tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="insurance in Insurances track by $index">
|
||||
<td><span ng-bind="insurance.sequanceNo">P</span></td>
|
||||
<!-- eCW's own tag typo, <sapn>, reproduced as served -->
|
||||
<td title="United Health Care"><input type="text" value="" style="display:none"><sapn ng-bind="insurance.InsName">United Health Care</sapn></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<h4 class="blue-header">CPT/HCPCS (3)</h4>
|
||||
<button id="claimAddCPTBtn8888888888888" type="button" class="btn btn-lgrey btn-xs" ng-click="addProcedureCode('');">Add</button>
|
||||
<div class="allbrd cptTbl" id="cptTable8888888888888">
|
||||
<table class="table table-bordered blue-fade-table" id="claimCPTCodesTable8888888888888">
|
||||
<thead><tr>
|
||||
<th class="text-center">#</th><th> </th><th>Code</th><th>POS</th><th>TOS</th>
|
||||
<th>SDOS</th><th>EDOS</th><th>M1</th><th>M2</th><th>M3</th><th>M4</th>
|
||||
<th>ICD1</th><th>ICD2</th><th>ICD3</th><th>ICD4</th><th>Units</th>
|
||||
<th>Billed Fee</th><th>Pro Id</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_0">
|
||||
<td class="text-center">1</td>
|
||||
<td><input type="checkbox" ng-click="updateBillToIns($index)" ng-checked="cpt.BillToIns=='1'" checked></td>
|
||||
<td title="Office Visit, Est Pt., Level 3"><input id="claimCPTCode0" data-fieldname="claimCPTCode" type="text" value="99213" readonly></td>
|
||||
<td><input id="billingClaimIpt4ngR0" data-fieldname="ClaimCPTPOS" type="text" value="11"></td>
|
||||
<td><input id="billingClaimIpt5ngR0" data-fieldname="ClaimCPTTOS" type="text" value="1"></td>
|
||||
<td><input id="billingClaimIpt6ngR0" data-fieldname="ClaimCPTSDOS" type="text" value="07/07/2026"></td>
|
||||
<td><input id="billingClaimIpt7ngR0" data-fieldname="ClaimCPTEDOS" type="text" value="07/07/2026"></td>
|
||||
<td><input id="billingClaimIpt8ngR0" data-fieldname="ClaimCPTMOD1" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt9ngR0" data-fieldname="ClaimCPTMOD2" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt10ngR0" data-fieldname="ClaimCPTMOD3" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt11ngR0" data-fieldname="ClaimCPTMOD4" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt12ngR0" data-fieldname="ClaimCPTICD1" type="text" value="1"></td>
|
||||
<td><input id="billingClaimIpt13ngR0" data-fieldname="ClaimCPTICD2" type="text" value="2"></td>
|
||||
<td><input id="billingClaimIpt14ngR0" data-fieldname="ClaimCPTICD3" type="text" value="3"></td>
|
||||
<td><input id="billingClaimIpt15ngR0" data-fieldname="ClaimCPTICD4" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt16ngR0" data-fieldname="ClaimCPTUnits" type="text" value="1"></td>
|
||||
<td title="210.00"><input id="billingClaimIpt17ngR0" data-fieldname="ClaimCPTBilledFee" type="text" value="210.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_1">
|
||||
<td class="text-center">2</td>
|
||||
<td><input type="checkbox" ng-click="updateBillToIns($index)" ng-checked="cpt.BillToIns=='1'" checked></td>
|
||||
<td title="MIRENA/LNG-RELEASING IUC SYS 52MG 5 YR DUR"><input id="claimCPTCode1" data-fieldname="claimCPTCode" type="text" value="J7298" readonly></td>
|
||||
<td><input id="billingClaimIpt4ngR1" data-fieldname="ClaimCPTPOS" type="text" value="11"></td>
|
||||
<td><input id="billingClaimIpt5ngR1" data-fieldname="ClaimCPTTOS" type="text" value="1"></td>
|
||||
<td><input id="billingClaimIpt6ngR1" data-fieldname="ClaimCPTSDOS" type="text" value="07/07/2026"></td>
|
||||
<td><input id="billingClaimIpt7ngR1" data-fieldname="ClaimCPTEDOS" type="text" value="07/07/2026"></td>
|
||||
<td><input id="billingClaimIpt8ngR1" data-fieldname="ClaimCPTMOD1" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt9ngR1" data-fieldname="ClaimCPTMOD2" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt10ngR1" data-fieldname="ClaimCPTMOD3" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt11ngR1" data-fieldname="ClaimCPTMOD4" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt12ngR1" data-fieldname="ClaimCPTICD1" type="text" value="1"></td>
|
||||
<td><input id="billingClaimIpt13ngR1" data-fieldname="ClaimCPTICD2" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt14ngR1" data-fieldname="ClaimCPTICD3" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt15ngR1" data-fieldname="ClaimCPTICD4" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt16ngR1" data-fieldname="ClaimCPTUnits" type="text" value="1"></td>
|
||||
<td title="1200.00"><input id="billingClaimIpt17ngR1" data-fieldname="ClaimCPTBilledFee" type="text" value="1200.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_2">
|
||||
<td class="text-center">3</td>
|
||||
<!-- Unchecked on purpose: the real bug report - G8510 had
|
||||
been excluded from billing and needs re-including once
|
||||
its ICD pointer is corrected. -->
|
||||
<td><input type="checkbox" ng-click="updateBillToIns($index)" ng-checked="cpt.BillToIns=='1'"></td>
|
||||
<td title="NEG SCR D PT NOT ELIG F/U/PLN DOC"><input id="claimCPTCode2" data-fieldname="claimCPTCode" type="text" value="G8510" readonly></td>
|
||||
<td><input id="billingClaimIpt4ngR2" data-fieldname="ClaimCPTPOS" type="text" value="11"></td>
|
||||
<td><input id="billingClaimIpt5ngR2" data-fieldname="ClaimCPTTOS" type="text" value="1"></td>
|
||||
<td><input id="billingClaimIpt6ngR2" data-fieldname="ClaimCPTSDOS" type="text" value="07/07/2026"></td>
|
||||
<td><input id="billingClaimIpt7ngR2" data-fieldname="ClaimCPTEDOS" type="text" value="07/07/2026"></td>
|
||||
<td><input id="billingClaimIpt8ngR2" data-fieldname="ClaimCPTMOD1" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt9ngR2" data-fieldname="ClaimCPTMOD2" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt10ngR2" data-fieldname="ClaimCPTMOD3" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt11ngR2" data-fieldname="ClaimCPTMOD4" type="text" value=""></td>
|
||||
<!-- Wrong on purpose: points at ICD1 (Z30.430), not the
|
||||
depression pointer (2) - this is the "target code
|
||||
needs retagging" case set_cpt_pos_modifier's sibling
|
||||
must fix. -->
|
||||
<td><input id="billingClaimIpt12ngR2" data-fieldname="ClaimCPTICD1" type="text" value="1"></td>
|
||||
<td><input id="billingClaimIpt13ngR2" data-fieldname="ClaimCPTICD2" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt14ngR2" data-fieldname="ClaimCPTICD3" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt15ngR2" data-fieldname="ClaimCPTICD4" type="text" value=""></td>
|
||||
<td><input id="billingClaimIpt16ngR2" data-fieldname="ClaimCPTUnits" type="text" value="1"></td>
|
||||
<td title="65.00"><input id="billingClaimIpt17ngR2" data-fieldname="ClaimCPTBilledFee" type="text" value="65.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<!-- the blank entry row eCW leaves at the bottom: no CPTROW_ id -->
|
||||
<tr>
|
||||
<td class="text-center">*</td>
|
||||
<td class="text-center"><input type="checkbox" checked></td>
|
||||
<td><input type="text" ng-model="newCPT"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="inp8888888888888">
|
||||
<h4>Insurances</h4>
|
||||
<table id="billingClaimTbl8" class="table table-bordered blue-fade-table nomargin">
|
||||
<thead><tr>
|
||||
<th></th><th>Bill To</th><th>Ins Id</th><th>Name</th><th>State</th>
|
||||
<th>Subscriber No</th><th>Rel</th><th>Insured</th><th>Group No</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="insurance in Insurances track by $index">
|
||||
<td><span ng-bind="insurance.sequanceNo">P</span></td>
|
||||
<td><input type="checkbox" ng-model="insurance.PrimIns" checked></td>
|
||||
<td><span ng-bind="insurance.InsId">1660</span></td>
|
||||
<td><span ng-bind="insurance.InsName">United Health Care</span></td>
|
||||
<td><span ng-bind="insurance.InsState">CO</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberNo">X00000000</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberRel">1</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberName">Doe, Alex</span></td>
|
||||
<td><input type="text" value="" style="display:none"><span ng-bind="insurance.GroupNo"></span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /modal-body -->
|
||||
|
||||
<!--
|
||||
eCW's Fee Schedule / CPT picker (opened by the "Add" button above),
|
||||
reproduced from the real DOM captured on a live "Claim" screen (claim
|
||||
9109000). Unlike everything else on the claim screen, ITS OWN controls
|
||||
carry stable ids with no per-open timestamp at all — this is a shared,
|
||||
reusable component, not something scoped to one claim's open session.
|
||||
-->
|
||||
<div class="modal fade bluetheme" id="claimFeeScheduleModal" style="display:none">
|
||||
<div class="modal-dialog"><div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button id="claimFeeScheduleBtn1" type="button" class="close">×</button>
|
||||
<h4 class="modal-title">Fee Schedule</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-bordered claimFSCPTTbl" id="example1">
|
||||
<thead><tr><th>Id</th><th>CPT</th><th>Name</th><th>Fee</th></tr></thead>
|
||||
<tbody id="claimFSCPTTblBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<input id="claimFeeScheduleIpt6" type="text" placeholder="Search CPT">
|
||||
<button id="claimFeeScheduleBtn8" type="button">Cancel</button>
|
||||
<button id="claimFeeSchOkBtn" type="button">OK</button>
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<div class="grey-bg modal-footer">
|
||||
<button id="claimProgressNoteBtn8888888888888" type="button" class="btn btn-lgrey btn-sm btn-xs pull-left"
|
||||
ng-click="viewProgNote()">Prog. Notes</button>
|
||||
<button id="billingClaimBtn36" type="button" class="btn btn-lgrey btn-sm btn-xs pull-right"
|
||||
ng-click="closeClaimScreen()">Cancel</button>
|
||||
<button id="claimScreenOkBtn8888888888888" type="button" class="btn btn-blue btn-sm btn-xs pull-right"
|
||||
ng-click="currentButtonAction='';saveAllData()">OK</button>
|
||||
</div>
|
||||
|
||||
</div></div></div>
|
||||
|
||||
</div></div></div></div><!-- /div_Billingclaim, modal-content, modal-dialog, uib window -->
|
||||
|
||||
<script>
|
||||
// Cancel and OK both hide the same backdrop the real closeClaimScreen() /
|
||||
// saveAllData() do — added so tests can prove the modal actually closes on
|
||||
// both paths, exactly the same reasoning ub_claim.html's script block uses.
|
||||
//
|
||||
// Both take the whole uib window out of the DOM, as eCW does: after a
|
||||
// real close, not one #billingClaimTbl8 was left in the document.
|
||||
// `closeClaimScreenRan` is what lets a test tell a real Cancel apart from
|
||||
// the form vanishing under an Escape.
|
||||
window.closeClaimScreenRan = false;
|
||||
function removeClaimWindow() {
|
||||
var w = document.querySelector('.claimModalBackdrop');
|
||||
if (w) w.remove();
|
||||
}
|
||||
document.getElementById('billingClaimBtn36').addEventListener('click', function () {
|
||||
window.closeClaimScreenRan = true;
|
||||
removeClaimWindow();
|
||||
});
|
||||
document.getElementById('claimScreenOkBtn8888888888888').addEventListener('click', function () {
|
||||
removeClaimWindow();
|
||||
});
|
||||
|
||||
// uib's modal stack dismisses its topmost window on Escape — for this
|
||||
// form that means gone, closeClaimScreen() never run (verified live,
|
||||
// claim 9109000). The Fee Schedule picker is on top while it is showing,
|
||||
// so Escape goes to it instead.
|
||||
document.addEventListener('keydown', function (e) {
|
||||
if (e.key !== 'Escape') return;
|
||||
var picker = document.getElementById('claimFeeScheduleModal');
|
||||
if (picker && picker.style.display === 'block') return;
|
||||
removeClaimWindow();
|
||||
});
|
||||
|
||||
// The real DOM (claim 9103000) carries ng-blur="updateCPTMod(...)" on every
|
||||
// M1 input, same as the UB form — reproduced as a data attribute so a test
|
||||
// can prove set_cpt_pos_modifier() blurs the field on THIS screen too.
|
||||
document.querySelectorAll('[data-fieldname="ClaimCPTMOD1"]').forEach(function (el) {
|
||||
el.addEventListener('blur', function () { el.dataset.blurred = '1'; });
|
||||
});
|
||||
|
||||
// The Fee Schedule picker: a small seed catalog, filtered by the search
|
||||
// box the same "Starts With" way the real picker defaults to. This
|
||||
// fakes eCW's own lookup/filter logic, not the fact that add_cpt_row()
|
||||
// clicks Add, types a search, selects the one match, and clicks OK —
|
||||
// that sequence is what the test proves, against real, stable ids.
|
||||
var feeScheduleCatalog = [
|
||||
{id: '509263', code: 'G2025', name: 'Telehealth originating site facility fee', fee: '25.00'},
|
||||
{id: '488892', code: 'G2026', name: 'Unrelated decoy code', fee: '0.00'},
|
||||
];
|
||||
var feeScheduleSelectedCode = null;
|
||||
|
||||
function renderFeeScheduleResults(filterText) {
|
||||
var tbody = document.getElementById('claimFSCPTTblBody');
|
||||
tbody.innerHTML = '';
|
||||
var q = (filterText || '').toUpperCase();
|
||||
feeScheduleCatalog
|
||||
.filter(function (row) { return row.code.toUpperCase().indexOf(q) === 0; })
|
||||
.forEach(function (row) {
|
||||
var tr = document.createElement('tr');
|
||||
tr.innerHTML =
|
||||
'<td>' + row.id + '</td>'
|
||||
+ '<td><span>' + row.code + '</span></td>'
|
||||
+ '<td>' + row.name + '</td>'
|
||||
+ '<td>' + row.fee + '</td>';
|
||||
tr.addEventListener('click', function () { feeScheduleSelectedCode = row.code; });
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('claimAddCPTBtn8888888888888').addEventListener('click', function () {
|
||||
feeScheduleSelectedCode = null;
|
||||
document.getElementById('claimFeeScheduleIpt6').value = '';
|
||||
renderFeeScheduleResults('');
|
||||
document.getElementById('claimFeeScheduleModal').style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('claimFeeScheduleIpt6').addEventListener('input', function () {
|
||||
renderFeeScheduleResults(this.value);
|
||||
});
|
||||
|
||||
document.getElementById('claimFeeScheduleBtn8').addEventListener('click', function () {
|
||||
document.getElementById('claimFeeScheduleModal').style.display = 'none';
|
||||
});
|
||||
|
||||
document.getElementById('claimFeeSchOkBtn').addEventListener('click', function () {
|
||||
document.getElementById('claimFeeScheduleModal').style.display = 'none';
|
||||
if (!feeScheduleSelectedCode) return;
|
||||
var tbody = document.querySelector('#claimCPTCodesTable8888888888888 tbody');
|
||||
var addRowTr = tbody.querySelector('tr:last-child');
|
||||
var n = document.querySelectorAll('#claimCPTCodesTable8888888888888 [id^="CPTROW_"]').length;
|
||||
var newRow = document.createElement('tr');
|
||||
newRow.id = 'CPTROW_' + n;
|
||||
newRow.innerHTML =
|
||||
'<td class="text-center">' + (n + 1) + '</td>'
|
||||
+ '<td><input type="checkbox" ng-click="updateBillToIns(' + n + ')" checked></td>'
|
||||
+ '<td><input data-fieldname="claimCPTCode" type="text" value="' + feeScheduleSelectedCode + '" readonly></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTPOS" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTTOS" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTSDOS" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTEDOS" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTMOD1" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTMOD2" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTMOD3" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTMOD4" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD1" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD2" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD3" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTICD4" type="text" value=""></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTUnits" type="text" value="1"></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTBilledFee" type="text" value="25.00"></td>'
|
||||
+ '<td><input type="text" value="" readonly></td>';
|
||||
tbody.insertBefore(newRow, addRowTr);
|
||||
newRow.querySelector('[data-fieldname="ClaimCPTMOD1"]')
|
||||
.addEventListener('blur', function () { this.dataset.blurred = '1'; });
|
||||
});
|
||||
|
||||
document.getElementById('claimProgressNoteBtn8888888888888').addEventListener('click', function () {
|
||||
var m = document.createElement('div');
|
||||
m.className = 'modal in';
|
||||
m.id = 'progressNoteViewerDiv8888888888888';
|
||||
m.style.display = 'block';
|
||||
m.innerHTML =
|
||||
'<div class="modal-dialog"><div class="modal-content">'
|
||||
+ '<div class="modal-header"><h4 class="modal-title">Progress Note</h4></div>'
|
||||
+ '<div class="modal-body">'
|
||||
+ '<iframe id="progressNoteFrame" src="progress_note.html" '
|
||||
+ 'style="width:900px;height:400px;border:0"></iframe>'
|
||||
+ '</div>'
|
||||
+ '<div class="modal-footer">'
|
||||
+ '<button type="button" class="btn btn-blue btn-sm btn-xs pull-right" '
|
||||
+ 'ng-click="closeProgressNoteViwer()">Close</button>'
|
||||
+ '</div>'
|
||||
+ '</div></div>';
|
||||
document.body.appendChild(m);
|
||||
m.querySelector('[ng-click="closeProgressNoteViwer()"]')
|
||||
.addEventListener('click', function () { m.style.display = 'none'; });
|
||||
});
|
||||
</script>
|
||||
|
||||
<script>
|
||||
// Angular's load, simulated (review item D7) — opt-in through the query
|
||||
// string, so every test that loads this fixture plainly still sees a form
|
||||
// that is already filled.
|
||||
// ?load=delayed claim number and codes blank, filled 800 ms later
|
||||
// ?load=never blank, and never filled (the timeout path)
|
||||
// ?load=wrong filled 800 ms later with a DIFFERENT claim number
|
||||
// ?load=noclaimno claim-number element removed; codes filled 800 ms later
|
||||
// ?load=staggered claim number and row 1 at 800 ms, row 2 at 1100 ms,
|
||||
// row 3 at 1400 ms — Angular filling one digest at a
|
||||
// time, so the first non-zero count is not the last
|
||||
// Plus, combinable with any ?load= mode:
|
||||
// &stale=other a HIDDEN copy of an earlier claim's form (claim
|
||||
// 5000002, one filled CPT row) sits in the DOM ahead of
|
||||
// this one, and this form's claim number stays hidden
|
||||
// until it is filled — eCW leaves closed forms behind
|
||||
// &stale=same the same hidden leftover, but showing THIS claim's
|
||||
// number, so only its filled row could fool a reader
|
||||
// Codes are blanked as the .value PROPERTY only, which is where Angular
|
||||
// keeps them: the value="" attribute still holds the template's code, so a
|
||||
// reader that took the attribute would call the form loaded at once.
|
||||
//
|
||||
// And the Prog. Notes button's ng-disabled="!FormData.EnableProgressNote",
|
||||
// which Angular flips only once the claim has loaded:
|
||||
// ?pn=late disabled, enabled 1 s later
|
||||
// ?pn=off disabled for good
|
||||
(function () {
|
||||
var m = /[?&]pn=(\w+)/.exec(location.search);
|
||||
if (!m) return;
|
||||
var b = document.querySelector('[ng-click="viewProgNote()"]');
|
||||
b.disabled = true;
|
||||
if (m[1] === 'late') setTimeout(function () { b.disabled = false; }, 1000);
|
||||
})();
|
||||
(function () {
|
||||
var m = /[?&]load=(\w+)/.exec(location.search);
|
||||
if (!m) return;
|
||||
var mode = m[1];
|
||||
var no = document.querySelector('[ng-bind="ClaimData.InvId"]');
|
||||
var codes = Array.prototype.slice.call(
|
||||
document.querySelectorAll('tr[id^="CPTROW_"] [data-fieldname="claimCPTCode"]'));
|
||||
var want = codes.map(function (e) { return e.value; });
|
||||
var claimNo = no ? no.textContent : '';
|
||||
var s = /[?&]stale=(\w+)/.exec(location.search);
|
||||
if (s) {
|
||||
// Built before this form's values are blanked, and first in <body>.
|
||||
var old = document.createElement('div');
|
||||
old.className = 'modal fade ng-isolate-scope claimModalBackdrop';
|
||||
old.style.display = 'none';
|
||||
old.innerHTML =
|
||||
'<span ng-bind="ClaimData.InvId">'
|
||||
+ (s[1] === 'same' ? claimNo : '5000002') + '</span>'
|
||||
+ '<table><tbody><tr id="CPTROW_0"><td>'
|
||||
+ '<input data-fieldname="claimCPTCode" type="text" value="99999">'
|
||||
+ '</td></tr></tbody></table>'
|
||||
+ '<button type="button" ng-click="closeClaimScreen()">Cancel</button>';
|
||||
document.body.insertBefore(old, document.body.firstChild);
|
||||
if (no) no.style.display = 'none';
|
||||
}
|
||||
if (no) no.textContent = '';
|
||||
if (mode === 'noclaimno' && no) { no.remove(); no = null; }
|
||||
codes.forEach(function (e) { e.value = ''; });
|
||||
if (mode === 'never') return;
|
||||
if (mode === 'staggered') {
|
||||
codes.forEach(function (e, i) {
|
||||
setTimeout(function () {
|
||||
if (i === 0 && no) { no.textContent = claimNo; no.style.display = ''; }
|
||||
e.value = want[i];
|
||||
}, 800 + 300 * i);
|
||||
});
|
||||
return;
|
||||
}
|
||||
setTimeout(function () {
|
||||
if (no) {
|
||||
no.textContent = (mode === 'wrong' ? '5000001' : claimNo);
|
||||
no.style.display = '';
|
||||
}
|
||||
codes.forEach(function (e, i) { e.value = want[i]; });
|
||||
}, 800);
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+331
@@ -0,0 +1,331 @@
|
||||
<html>
|
||||
<!--
|
||||
A cut-down of the Progress Note eCW served on 2026-09-07 for claim 9104000.
|
||||
|
||||
Reproduced rather than saved: the real note is dense patient narrative and
|
||||
must never be committed. Trimmed to the structures the extractor reaches for,
|
||||
with the real attribute names kept exactly:
|
||||
|
||||
* prisma-section="..." on every section row - eCW's own labels, and the
|
||||
whole reason this note is read as structure instead of as prose
|
||||
* "Footer" repeats four times, so a naive dict build would drop the
|
||||
signature block. The extractor joins repeats; a test covers it.
|
||||
* #chartLockedId is display:none - innerText returns "" for it, so it has
|
||||
to be read with textContent
|
||||
* the Assessments number sits in its own <td>, away from the text
|
||||
* the Procedure Code line carries the modifier inline: "... Modifiers: GT"
|
||||
-->
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<style type="text/css">
|
||||
.PtHeading { font-size: 18pt; font-weight: bold; }
|
||||
.PtData { font-size: 9pt; font-weight: bold; }
|
||||
.leftPaneHeading, .rightPaneHeading { font-size: 10pt; }
|
||||
.leftPaneData, .rightPaneData { font-size: 9pt; }
|
||||
.PNSig { font-size: 10pt; font-weight: bold; }
|
||||
.TableFooter { font-size: 9pt; font-weight: bold; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="mousetrap">
|
||||
<hr>
|
||||
<table cellpadding="0" cellspacing="0" border="0" width="99%">
|
||||
<thead id="mainThead">
|
||||
<tr class="patient_header_tr">
|
||||
<td><span>SAMPLE, Pat Q <b>DOB:</b> 05/11/1979 (47 yo F)
|
||||
<b>Acc No.</b> 900123 <b>DOS:</b> 06/29/2026</span></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>
|
||||
|
||||
<table cellspacing="0" cellpadding="0" width="100%" prisma-section="Header">
|
||||
<tbody>
|
||||
<tr class="PtHeading"><td align="right"><b>Sample, Pat Q</b></td></tr>
|
||||
<tr class="PtData"><td align="right">47 Y old Female, DOB: 05/11/1979</td></tr>
|
||||
<tr class="PtData"><td align="right">Account Number: 900123</td></tr>
|
||||
<tr class="PtData"><td align="right">100 SAMPLE ST, ANYTOWN, CO-80000-0000</td></tr>
|
||||
<tr class="PtData"><td align="right">Home: 555-010-0100</td></tr>
|
||||
<tr class="PtData"><td align="right">Guarantor: Sample, Pat Q
|
||||
Insurance: BH COLORADO ACCESS Payer ID: UOACC</td></tr>
|
||||
<tr class="PtData"><td align="right">PCP: Jordan K Rivera</td></tr>
|
||||
<tr class="PtData"><td align="right">Appointment Facility: Brighton Womens Clinic</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr>
|
||||
<!-- the left pane -->
|
||||
<td width="35%" valign="top" class="lefttbl" id="leftPanelModernStyle">
|
||||
<table cellspacing="8" cellpadding="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<!-- present but empty on this note, exactly as served -->
|
||||
<tr prisma-section="Active Problem List"><td valign="top"></td></tr>
|
||||
|
||||
<tr prisma-section="Past Medical History">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="leftPaneHeading"><td>Past Medical History</td></tr>
|
||||
<tr class="leftPaneData"><td>
|
||||
<ul style="padding-left: 25px;margin-bottom: 0px;">
|
||||
<li>Current Tobacco Use- about 1 pack per day, started at age 14.</li>
|
||||
<li>Generalized Anxiety Disorder.</li>
|
||||
<li>Low Back Pain with Right-Sided Sciatica.</li>
|
||||
</ul>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Surgical History">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="leftPaneHeading"><td><b>Surgical History</b></td></tr>
|
||||
<tr class="leftPaneData"><td>
|
||||
<ul style="padding-left: 25px;margin-bottom: 0px;">
|
||||
<li>Cyst Removal Right Breast - fibrous tissue 1997</li>
|
||||
</ul>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Family History">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="leftPaneHeading"><td><b>Family History</b></td></tr>
|
||||
<tr class="leftPaneData"><td>
|
||||
<ul style="padding-left: 25px;margin-bottom: 0px;">
|
||||
<li>Mother: alive 66 yrs, diagnosed with Cancer</li>
|
||||
<li>Father: alive 76 yrs, diagnosed with Hypertension</li>
|
||||
<li>Siblings: alive, diagnosed with Hypertension</li>
|
||||
</ul>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Social History">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="leftPaneHeading"><td><b>Social History</b></td></tr>
|
||||
<tr class="leftPaneData"><td>
|
||||
<table class="leftPaneData" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr><td>Tobacco use</td></tr>
|
||||
<tr><td>Tobacco Status: <i>Current tobacco user pack a day</i></td></tr>
|
||||
<tr><td>Depression Screening PHQ-2</td></tr>
|
||||
<tr><td>Screening Test Result <i>Positive - 3 or more</i></td></tr>
|
||||
<tr><td>Follow Up Plan (required) <i>Schedule f/u with Behavioral Health</i></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Hospitalization/Major Diagnostic Procedure">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="leftPaneHeading"><td><b>Hospitalization/Major Diagnostic Procedure</b></td></tr>
|
||||
<tr class="leftPaneData"><td>
|
||||
<ul style="padding-left: 25px;margin-bottom: 0px;">
|
||||
<li>Childbirth x 1 vaginal, uncomplicated</li>
|
||||
</ul>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
|
||||
<!-- the right pane -->
|
||||
<td width="65%" valign="top">
|
||||
<table id="rightPanelDataTable" cellspacing="8" cellpadding="0" border="0" width="100%">
|
||||
<tbody>
|
||||
|
||||
<tr prisma-section="Reason for Appointment">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="rightPaneHeading"><td><b>Reason for Appointment</b></td></tr>
|
||||
<tr class="rightPaneData"><td>
|
||||
<table class="rightPaneData" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody><tr>
|
||||
<td width="28px"> 1. </td>
|
||||
<td>BH Outreach</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="History of Present Illness">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="rightPaneHeading"><td><b>History of Present Illness</b></td></tr>
|
||||
<tr class="rightPaneData"><td>
|
||||
<table class="rightPaneData" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody><tr><td> <u>TELEHEALTH</u>: </td></tr></tbody>
|
||||
</table>
|
||||
<table width="100%" class="rightPaneData" cellspacing="0" cellpadding="0">
|
||||
<tbody><tr>
|
||||
<td width="28px"></td>
|
||||
<td><b>BH Visit Details</b><br>
|
||||
BHP conducted a scheduled outreach telephone call as part of
|
||||
ongoing behavioral health outreach services.<br>
|
||||
<b>Assessment</b><br>
|
||||
<b>Columbia Suicide Severity Rating Scale (C-SSRS):</b> Low Risk.<br>
|
||||
<b>Outcome Rating Scale (ORS):</b> Score of 4; Clinical.<br>
|
||||
<b>Plan</b><br>
|
||||
No behavioral health follow-up appointment was scheduled at this time.
|
||||
</td>
|
||||
</tr></tbody>
|
||||
</table>
|
||||
<table class="rightPaneData" cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr><td>Session Start: <i>1:00pm.</i></td></tr>
|
||||
<tr><td>Session End: <i>2:00pm.</i></td></tr>
|
||||
<tr><td>Session Duration (Minutes): <i>60Minutes.</i></td></tr>
|
||||
<tr><td>Session Setting (PoS) <i>Telehealth</i></td></tr>
|
||||
<tr><td>Mode of Treatment: <i>Telephone</i></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Assessments">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="rightPaneHeading"><td><b>Assessments</b></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="rightPaneData">
|
||||
<td width="28px" style="text-align: right; padding-right: 3px;">1.</td>
|
||||
<td>Illness, unspecified - R69 (Primary) </td>
|
||||
</tr>
|
||||
<tr class="rightPaneData">
|
||||
<td width="28px" style="text-align: right; padding-right: 3px;">2.</td>
|
||||
<td>Generalized anxiety disorder - F41.1 </td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Labs">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="rightPaneHeading"><td><b>Labs </b></td></tr>
|
||||
<tr class="rightPaneData">
|
||||
<td><u>Lab: BH - Outcome Rating Scale (ORS)
|
||||
(Collection Date & Time - 06/29/2026)</u></td>
|
||||
</tr>
|
||||
<tr><td>
|
||||
<table border="0" class="rightPaneData" width="100%">
|
||||
<tbody>
|
||||
<tr><td width="50%"> Individually</td>
|
||||
<td width="10%">1</td><td width="10%"></td><td width="30%"></td></tr>
|
||||
<tr><td width="50%"> Total</td>
|
||||
<td width="10%">4</td><td width="10%"></td><td width="30%"></td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Procedure Codes">
|
||||
<td valign="top">
|
||||
<table cellspacing="0" cellpadding="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="rightPaneHeading"><td><b>Procedure Codes</b></td></tr>
|
||||
<tr class="rightPaneData"><td>
|
||||
<ul style="padding-left: 28px;margin-bottom: 0px;">
|
||||
<li>H0023 Behavioral Health Outreach Service (TH). Modifiers: GT</li>
|
||||
<li>90791 Psychiatric diagnostic evaluation.</li>
|
||||
</ul>
|
||||
</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr prisma-section="Care Plan Details">
|
||||
<td valign="top">
|
||||
<div class="leftPaneHeading">Care Plan Details</div>
|
||||
<table style="width:99%; border-collapse: collapse;" cellspacing="0" cellpadding="1">
|
||||
<tbody><tr></tr></tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<!-- "Footer" repeats: the signature, the status, and the facility
|
||||
block are three separate prisma-section="Footer" rows. -->
|
||||
<tr prisma-section="Footer">
|
||||
<td valign="top"><img id="signature1" width="175px" height="100px" src=""></td>
|
||||
</tr>
|
||||
<tr class="PNSig" prisma-section="Footer">
|
||||
<td valign="top">Electronically signed by Provider: Morgan Lee Taylor , LPC
|
||||
on 07/13/2026 at 07:52 PM MDT</td>
|
||||
</tr>
|
||||
<tr class="PNSig" prisma-section="Footer">
|
||||
<td valign="top">Sign off status: Completed</td>
|
||||
</tr>
|
||||
<tr class="Facility" prisma-section="Footer">
|
||||
<td align="center">
|
||||
<hr><br>Brighton Womens Clinic<br>30 S 20TH AVE<br>
|
||||
BRIGHTON, CO 80601-3705<br>Tel: 303-655-6180<br>
|
||||
Fax: 720-685-8714<br>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</td></tr>
|
||||
</tbody>
|
||||
<tfoot class="mainTfoot" style="display:table-footer-group">
|
||||
<tr prisma-section="Footer">
|
||||
<td>
|
||||
<table cellspacing="0" cellpadding="0" border="0" width="100%">
|
||||
<tbody>
|
||||
<tr class="TableFooter"><td align="center">
|
||||
Progress Note: Morgan Lee Taylor 06/29/2026</td></tr>
|
||||
<tr class="eclinicalworks"><td align="center">
|
||||
Note generated by eClinicalWorks EMR/PM Software</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
<span name="chartLockedId" id="chartLockedId" style="display:none; color:white">true</span>
|
||||
</body>
|
||||
</html>
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
A claim screen's Prog. Notes button whose note arrives LATE — the shape of
|
||||
the 3 misses in ~115 note reads behind review item D8 (2026-10-01): the
|
||||
viewer had opened, but the note document inside it had not loaded yet, so
|
||||
a fixed 3 s sleep and one probe reported "nothing" and the claim went out
|
||||
Clean with telehealth never checked.
|
||||
|
||||
Same split as progress_note_host.html: the note is a whole document in an
|
||||
IFRAME inside the viewer modal, and Close (eCW's own "Viwer" typo) lives on
|
||||
the parent page. The note itself is progress_note.html, made-up data.
|
||||
|
||||
The mode is chosen by query string:
|
||||
?mode=slow&delay=3500 viewer opens at once on an empty frame; the note
|
||||
document is loaded into it `delay` ms later
|
||||
(3.5 s by default — longer than the old 3 s sleep)
|
||||
?mode=never viewer opens on a frame that never gets a note
|
||||
?mode=reclick the first click does nothing at all; the second
|
||||
opens the viewer with the note
|
||||
?mode=growing the note keeps gaining sections every 200 ms and
|
||||
never settles
|
||||
?mode=empty the note's header (#mainThead) arrives and nothing
|
||||
under it ever does: a note marker, 0 sections
|
||||
?mode=stalled&delay=1500
|
||||
the note document is streamed: its header and two
|
||||
sections are parsed, then the "server" stalls for
|
||||
`delay` ms (readyState stays "loading") before the
|
||||
Procedure Codes and the end of the document arrive
|
||||
?mode=reused&delay=1500
|
||||
a viewer from an EARLIER claim is already in the
|
||||
DOM, hidden (Close only hides it), its iframe still
|
||||
holding that claim's note (procedure A1111). The
|
||||
click shows that same viewer again and its iframe
|
||||
gets this claim's note (H0023) `delay` ms later
|
||||
?mode=tab&delay=2000 the click opens a new tab at once on about:blank;
|
||||
the note is loaded into it `delay` ms later (past
|
||||
the halfway point of a 3 s wait) and there is no
|
||||
in-page viewer at all
|
||||
-->
|
||||
<html>
|
||||
<head><title>Prog. Notes, delayed</title></head>
|
||||
<body>
|
||||
|
||||
<button type="button" ng-click="viewProgNote()">Prog. Notes</button>
|
||||
|
||||
<script>
|
||||
var params = new URLSearchParams(location.search);
|
||||
var mode = params.get('mode') || 'slow';
|
||||
var delay = parseInt(params.get('delay') || '3500', 10);
|
||||
window.progNoteClicks = 0;
|
||||
|
||||
function openViewer() {
|
||||
var m = document.createElement('div');
|
||||
m.className = 'modal in';
|
||||
m.id = 'progressNoteViewerDiv6666666666666';
|
||||
m.style.display = 'block';
|
||||
m.innerHTML =
|
||||
'<div class="modal-dialog"><div class="modal-content">'
|
||||
+ '<div class="modal-body">'
|
||||
+ '<iframe id="progressNoteFrame" src="about:blank" '
|
||||
+ 'style="width:900px;height:400px;border:0"></iframe>'
|
||||
+ '</div>'
|
||||
+ '<div class="modal-footer">'
|
||||
+ '<button type="button" ng-click="closeProgressNoteViwer()">Close</button>'
|
||||
+ '</div>'
|
||||
+ '</div></div>';
|
||||
document.body.appendChild(m);
|
||||
m.querySelector('[ng-click="closeProgressNoteViwer()"]')
|
||||
.addEventListener('click', function () { m.remove(); });
|
||||
return m.querySelector('iframe');
|
||||
}
|
||||
|
||||
// A note that never stops growing: a header (the note marker) and one more
|
||||
// prisma-section every 200 ms.
|
||||
var GROWING =
|
||||
'<html><body><table><thead id="mainThead"><tr><td>Header</td></tr></thead>'
|
||||
+ '<tbody id="b"></tbody></table><script>'
|
||||
+ 'var n = 0; setInterval(function () {'
|
||||
+ ' var tr = document.createElement("tr");'
|
||||
+ ' tr.setAttribute("prisma-section", "Section " + (++n));'
|
||||
+ ' tr.innerHTML = "<td>line " + n + "</td>";'
|
||||
+ ' document.getElementById("b").appendChild(tr);'
|
||||
+ '}, 200);</' + 'script></body></html>';
|
||||
|
||||
// The header alone: the note marker is there, not one section ever is.
|
||||
var EMPTY =
|
||||
'<html><body><table><thead id="mainThead"><tr><td>Header</td></tr>'
|
||||
+ '</thead><tbody></tbody></table></body></html>';
|
||||
|
||||
// A small whole note with one procedure code — made-up data.
|
||||
function noteDoc(code) {
|
||||
return '<html><body><table><thead id="mainThead"><tr><td>Header</td>'
|
||||
+ '</tr></thead><tbody>'
|
||||
+ '<tr prisma-section="Assessments"><td>1. Illness - R69</td></tr>'
|
||||
+ '<tr prisma-section="Procedure Codes"><td><ul><li>' + code
|
||||
+ ' Made-up procedure. Modifiers: GT</li></ul></td></tr>'
|
||||
+ '</tbody></table></body></html>';
|
||||
}
|
||||
|
||||
// 'reused': the earlier claim's viewer, hidden by its Close, still in the
|
||||
// DOM with that claim's note in its iframe.
|
||||
var leftover = null;
|
||||
if (mode === 'reused') {
|
||||
leftover = openViewer();
|
||||
leftover.srcdoc = noteDoc('A1111');
|
||||
var lm = leftover.closest('.modal');
|
||||
lm.style.display = 'none';
|
||||
var lc = lm.querySelector('[ng-click="closeProgressNoteViwer()"]');
|
||||
var hide = lc.cloneNode(true); // drop openViewer's remove() handler
|
||||
lc.replaceWith(hide);
|
||||
hide.addEventListener('click', function () { lm.style.display = 'none'; });
|
||||
}
|
||||
|
||||
document.querySelector('[ng-click="viewProgNote()"]')
|
||||
.addEventListener('click', function () {
|
||||
window.progNoteClicks++;
|
||||
if (mode === 'reclick' && window.progNoteClicks === 1) return;
|
||||
if (mode === 'reused') {
|
||||
leftover.closest('.modal').style.display = 'block';
|
||||
setTimeout(function () { leftover.srcdoc = noteDoc('H0023'); }, delay);
|
||||
return;
|
||||
}
|
||||
if (mode === 'tab') {
|
||||
var w = window.open('about:blank', '_blank');
|
||||
setTimeout(function () {
|
||||
w.location.href = new URL('progress_note.html', location.href).href;
|
||||
}, delay);
|
||||
return;
|
||||
}
|
||||
var frame = openViewer();
|
||||
if (mode === 'stalled') {
|
||||
// document.open() without close(): readyState stays "loading"
|
||||
// until the rest of the document is written — a streamed response.
|
||||
var d = frame.contentDocument;
|
||||
d.open();
|
||||
d.write('<html><body><table><thead id="mainThead"><tr><td>Header'
|
||||
+ '</td></tr></thead><tbody>'
|
||||
+ '<tr prisma-section="History of Present Illness"><td>hpi</td></tr>'
|
||||
+ '<tr prisma-section="Assessments"><td>1. Illness - R69</td></tr>');
|
||||
setTimeout(function () {
|
||||
d.write('<tr prisma-section="Procedure Codes"><td><ul><li>H0023 '
|
||||
+ 'Behavioral Health Outreach Service (TH). Modifiers: GT</li>'
|
||||
+ '</ul></td></tr></tbody></table></body></html>');
|
||||
d.close();
|
||||
}, delay);
|
||||
return;
|
||||
}
|
||||
if (mode === 'slow') {
|
||||
setTimeout(function () { frame.src = 'progress_note.html'; }, delay);
|
||||
} else if (mode === 'reclick') {
|
||||
frame.src = 'progress_note.html';
|
||||
} else if (mode === 'growing') {
|
||||
frame.srcdoc = GROWING;
|
||||
} else if (mode === 'empty') {
|
||||
frame.srcdoc = EMPTY;
|
||||
}
|
||||
// 'never': the frame stays about:blank.
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
The Progress Note viewer as it actually arrives: the note is a whole printable
|
||||
document with its own <html>, so eCW hosts it in an IFRAME inside a modal, and
|
||||
the Close button lives on the PARENT page - not in the note.
|
||||
|
||||
That split is the trap this fixture exists for. Reading the modal's innerText
|
||||
finds nothing, because iframe content is not part of it; and looking for Close
|
||||
inside the note document finds nothing either. progress_note.find() has to
|
||||
walk the frames, and progress_note.close() has to click on the parent.
|
||||
|
||||
The Close button's ng-click is spelled "closeProgressNoteViwer" - eCW's own
|
||||
typo, reproduced exactly, because correcting it would match nothing.
|
||||
-->
|
||||
<html>
|
||||
<head><title>Progress Note Viewer</title></head>
|
||||
<body>
|
||||
|
||||
<div class="modal in" id="progressNoteViewerDiv1788790287089" style="display:block">
|
||||
<div class="modal-dialog" style="width:1200px">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">Progress Note</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body" style="height:600px">
|
||||
<iframe id="progressNoteFrame" src="progress_note.html"
|
||||
style="width:100%;height:560px;border:0"></iframe>
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-blue btn-sm btn-xs pull-right"
|
||||
ng-click="closeProgressNoteViwer()">Close</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
document.querySelector('[ng-click="closeProgressNoteViwer()"]')
|
||||
.addEventListener('click', function () {
|
||||
document.getElementById('progressNoteViewerDiv1788790287089')
|
||||
.style.display = 'none';
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+93
@@ -0,0 +1,93 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head><meta charset="utf-8"><title>sliding fee fixture</title>
|
||||
<style>.hide { display: none; }</style></head>
|
||||
<body>
|
||||
<!--
|
||||
Minimal copy of the eCW path, attribute-for-attribute where the bot
|
||||
matches on it (Dom.txt, UB claim 9124000): the claim header "Info"
|
||||
button, Patient Information (#ptInfo) with its Sliding Fee Schedule
|
||||
button and Save/Cancel, the unsaved-data prompt, and the Sliding Fee
|
||||
Schedule modal whose inputs Angular fills a moment after it shows.
|
||||
Fictional values only.
|
||||
|
||||
Knobs, set from the test before clicking:
|
||||
window.fixture.disabled Sliding Fee Schedule button disabled
|
||||
window.fixture.values what the modal fills in (null = stays blank)
|
||||
window.fixture.prompt Cancel raises the "unsaved data" prompt
|
||||
Evidence the test reads back:
|
||||
window.saved any Save was clicked (must stay false)
|
||||
-->
|
||||
<div id="UBClaimDiv">
|
||||
<button type="button" id="claimPatientInfo1790766147287"
|
||||
ng-click="loadPatientHubnInfo('Info');">Info</button>
|
||||
</div>
|
||||
|
||||
<div class="modal hide" id="ptInfo">
|
||||
<div class="modal-content" id="ptInfoModal">
|
||||
<button id="savePrompt-tplBtn1" type="button" class="close"
|
||||
ng-click="closeModalWindow()">x</button>
|
||||
<div id="savePrompt" class="hide">
|
||||
You have unsaved data. Click Save to retain your changes.
|
||||
<button id="savePrompt-tplBtn2" ng-click="saveData(1)">Save</button>
|
||||
<button id="savePrompt-tplBtn4" ng-click="saveData(2)">Don't Save</button>
|
||||
<button id="savePrompt-tplBtn3" ng-click="saveData(3)">Cancel</button>
|
||||
</div>
|
||||
<button id="btnSlidingFeeschedule" ng-disabled="slidingfeedisable"
|
||||
data-ng-click="slidingFeeScheduleClick();">Sliding Fee Schedule</button>
|
||||
<button id="patient-demographicsBtn56" type="button"
|
||||
ng-click="cmdOk_Click()">Save</button>
|
||||
<button id="patient-demographicsBtn57" type="button"
|
||||
ng-click="cancelbtn()">Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="modal hide" id="slidingfeescheduleDailog">
|
||||
<input id="slidingfeescheduleIpt34" data-ng-model="patientIncomeDetails.PovertyLevel">
|
||||
<input id="slidingfeescheduleIpt35" data-ng-model="status.name">
|
||||
<input id="slidingfeescheduleIpt37" data-ng-model="patientIncomeDetails.AssignedType">
|
||||
<input id="slidingfeescheduleIpt40" data-ng-model="patientIncomeDetails.AssignedDate">
|
||||
<input id="slidingfeescheduleIpt41" data-ng-model="patientIncomeDetails.ExpiryDate">
|
||||
<input id="slidingfeescheduleIpt42" data--ng-model="patientIncomeDetails.FeeSchedule">
|
||||
<button id="slidingfeescheduleBtn6" data-ng-click="assignSlidingFeeSchedule();">Assign</button>
|
||||
<button id="slidingfeescheduleBtn8" type="button"
|
||||
ng-click="closeSlidingFeeScheduleModal()">Close</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
window.fixture = {disabled: false, values: null, prompt: false};
|
||||
window.saved = false;
|
||||
const $ = s => document.querySelector(s);
|
||||
const show = (s, on) => $(s).classList.toggle('hide', !on);
|
||||
const FIELDS = {
|
||||
poverty_level: '#slidingfeescheduleIpt34', status: '#slidingfeescheduleIpt35',
|
||||
scale_type: '#slidingfeescheduleIpt37', from_date: '#slidingfeescheduleIpt40',
|
||||
to_date: '#slidingfeescheduleIpt41', fee_schedule: '#slidingfeescheduleIpt42',
|
||||
};
|
||||
|
||||
$('#claimPatientInfo1790766147287').onclick = () => {
|
||||
$('#btnSlidingFeeschedule').disabled = !!window.fixture.disabled;
|
||||
setTimeout(() => show('#ptInfo', true), 300);
|
||||
};
|
||||
$('#btnSlidingFeeschedule').onclick = () => {
|
||||
for (const s of Object.values(FIELDS)) $(s).value = '';
|
||||
show('#slidingfeescheduleDailog', true);
|
||||
const v = window.fixture.values;
|
||||
if (v) setTimeout(() => {
|
||||
for (const [k, s] of Object.entries(FIELDS)) $(s).value = v[k] || '';
|
||||
}, 700);
|
||||
};
|
||||
$('#slidingfeescheduleBtn8').onclick = () => show('#slidingfeescheduleDailog', false);
|
||||
$('#patient-demographicsBtn57').onclick = () => {
|
||||
if (window.fixture.prompt) show('#savePrompt', true);
|
||||
else show('#ptInfo', false);
|
||||
};
|
||||
$('#savePrompt-tplBtn4').onclick = () => {
|
||||
show('#savePrompt', false); show('#ptInfo', false);
|
||||
};
|
||||
for (const s of ['#savePrompt-tplBtn2', '#patient-demographicsBtn56',
|
||||
'#slidingfeescheduleBtn6'])
|
||||
$(s).onclick = () => { window.saved = true; };
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+458
@@ -0,0 +1,458 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
A faithful cut-down of the UB Claim modal eCW served on 2026-09-07.
|
||||
|
||||
Reproduced on purpose, rather than saved from the live page: the real form is
|
||||
patient data and must never be committed. Everything the extractor reaches for
|
||||
is here with the real attribute names, the real nesting, and — the point of the
|
||||
whole fixture — the real **timestamped ids**.
|
||||
|
||||
The timestamp here is 9999999999999, deliberately different from the
|
||||
1788790287089 of the captured DOM. Any selector that had the number baked in
|
||||
would fail against this file, which is exactly what the test is for.
|
||||
|
||||
Also reproduced faithfully:
|
||||
* the ICD code lives in an <input value="R69">, not in text
|
||||
* the CPT grid is entirely inputs, ids ending in the row index
|
||||
* eCW pads every grid with blank ng-repeat rows to hold its height
|
||||
* the address text is ellipsised, with the full value in title=
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div class="modal fade bluetheme ubClaimModalBackdrop in" id="UBClaimDiv9999999999999" style="display:block">
|
||||
<div class="modal-dialog visible" id="UBClaimDiv">
|
||||
<div id="UBClaimModalContent9999999999999" class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<button id="UBClaimBtn3" type="button" class="close">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">UB Form Details
|
||||
<span class="ml5 fs11 patient-identifier-span">SAMPLE, Pat Q May 11, 1979 (48 yo F) Acc No. 900123</span>
|
||||
</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<!-- patient strip -->
|
||||
<div class="single-column middle">
|
||||
<span class="blue_txt" ng-bind="showEllipsisForField(PatientInfo.namewithsuffix,25)" title="Sample, Pat, Q">Sample, Pat, Q</span>
|
||||
<span class="blue_txt" ng-bind="' ' + PatientInfo.Age + ', ' + PatientInfo.patSex"> 48Y, F</span>
|
||||
<span class="fnt11" ng-bind="PatientInfo.dob">05/11/1979</span>
|
||||
<span class="fnt11" ng-bind="showEllipsisForField(PatientInfo.address +', '+ PatientInfo.address2 + ', ' + PatientInfo.state+ ' ' + PatientInfo.zip,45)" title="100 SAMPLE ST, , CO 80000-0000">100 SAMPLE ST, , CO 806...</span>
|
||||
<span class="fnt11" ng-bind="PatientInfo.phone">555-010-0100</span>
|
||||
Acct No : <span ng-bind="showEllipsisForField(PatientInfo.ControlNo,10)">900123</span>
|
||||
</div>
|
||||
|
||||
<!-- header block -->
|
||||
<table class="table section-table"><tbody><tr>
|
||||
<td>
|
||||
<table class="table"><tbody>
|
||||
<tr>
|
||||
<td><label>Claim No:</label></td>
|
||||
<td class="w60p ubClaimNumberText"><span title="9104000">9104000</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Claim Date</label></td>
|
||||
<td><input type="text" class="form-control MMDDYYYYDatePicker hasDatepicker"
|
||||
ng-model="ClaimData.ClaimDate" id="claimDate9999999999999"
|
||||
value="06/29/2026" placeholder="MM/DD/YYYY"></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</td>
|
||||
<td>
|
||||
<table class="table"><tbody>
|
||||
<tr><td><label>Bill Type</label></td>
|
||||
<td><input type="text" ng-model="Ub92Data.BillType" id="BillType" value="131"></td></tr>
|
||||
</tbody></table>
|
||||
</td>
|
||||
<td>
|
||||
<label>Facility:</label>
|
||||
<input id="UBClaimIpt23" type="text" ng-model="selectApptFacilityData.facility.Name"
|
||||
title="MWC:Brighton Womens Clinic" value="MWC:Brighton Womens Clinic" readonly>
|
||||
<label>Copay</label>
|
||||
<input type="text" id="ClaimDataCopay" ng-model="ClaimData.copay" value="0.00">
|
||||
<label>Pt. Uncovered Amt:</label>
|
||||
<input type="text" id="ClaimDataPtUnCovAmt" ng-model="ClaimData.PtUnCovAmt" value="0.00">
|
||||
</td>
|
||||
<td>
|
||||
<label>Attending</label>
|
||||
<input id="UBClaimIpt29" type="text" ng-model="ClaimData.PayToProviderName"
|
||||
value="Clinician, Casey M" readonly>
|
||||
<label>Claim Status</label>
|
||||
<select id="claimStatusSel9999999999999" ng-model="selectedClaimStatusCodes" ng-change="claimStatusChanged()">
|
||||
<option value="CMC">Ready to Submit (Electronic)</option>
|
||||
<option value="113ME">Medcor Testing</option>
|
||||
<option value="108ME" selected="selected">Medcor Ready</option>
|
||||
<option value="CMS">Submitted</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr></tbody></table>
|
||||
|
||||
<!-- upper tabs -->
|
||||
<ul id="UBClaimUl1" class="nav">
|
||||
<li class="active"><a id="UBClaimLink4" href="javascript:void(0)" data-toggle="tab" data-target="#claimedi9999999999999">ICD, CPT and Revenue Codes</a></li>
|
||||
<li><a id="UBClaimLink3" href="javascript:void(0)" data-toggle="tab" data-target="#inp9999999999999">Occurrence and Value Codes</a></li>
|
||||
<li><a id="UBClaimLink2" href="javascript:void(0)" data-toggle="tab" data-target="#addinf9999999999999">Insurance and Payments</a></li>
|
||||
<li><a id="UBClaimLink1" href="javascript:void(0)" data-toggle="tab" data-target="#icd9999999999999">Additional Information</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Insurance and Payments pane -->
|
||||
<div class="tab-pane" id="addinf9999999999999">
|
||||
<table id="UBClaimTbl3" class="table table-bordered blue-fade-table">
|
||||
<thead><tr>
|
||||
<th style="width:30px"><div class="th-inner"></div></th>
|
||||
<th><div class="th-inner">Bill To</div></th>
|
||||
<th><div class="th-inner">Name</div></th>
|
||||
<th><div class="th-inner">State</div></th>
|
||||
<th><div class="th-inner">Subscriber No.</div></th>
|
||||
<th><div class="th-inner">Rel</div></th>
|
||||
<th><div class="th-inner">Insured</div></th>
|
||||
<th><div class="th-inner">Group No.</div></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="insurance in InvInsList">
|
||||
<td><span ng-bind="insurance.sequanceNo">P</span></td>
|
||||
<td><input id="UBClaimIpt1ngR0" type="checkbox" name="billto" checked></td>
|
||||
<td><span ng-bind="insurance.InsName">BH COLORADO ACCESS</span></td>
|
||||
<td><span ng-bind="insurance.InsState">CO</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberNo">J000000</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberRel">1</span></td>
|
||||
<td><span ng-bind="insurance.SubscriberName">Sample, Pat, Q</span></td>
|
||||
<td><span ng-bind="insurance.GroupNo"></span></td>
|
||||
</tr>
|
||||
<tr ng-repeat="temp in InvInsListBlankRows">
|
||||
<td> </td><td></td><td></td><td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table id="UBClaimTbl4" class="table table-bordered blue-fade-table">
|
||||
<thead><tr>
|
||||
<th><div class="th-inner">Id</div></th>
|
||||
<th><div class="th-inner">Date</div></th>
|
||||
<th><div class="th-inner">From</div></th>
|
||||
<th><div class="th-inner">Allowed</div></th>
|
||||
<th><div class="th-inner">Deduct</div></th>
|
||||
<th><div class="th-inner">Coins</div></th>
|
||||
<th><div class="th-inner">Copay</div></th>
|
||||
<th><div class="th-inner">Paid</div></th>
|
||||
<th><div class="th-inner">Adjust</div></th>
|
||||
<th><div class="th-inner">Withheld</div></th>
|
||||
<th><div class="th-inner">E</div></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="PayAdjRef in PayAdjRefund">
|
||||
<td ng-bind="PayAdjRef.id">2000001</td>
|
||||
<td ng-bind="PayAdjRef.date">06/29/2026</td>
|
||||
<td ng-bind="PayAdjRef.from">Patient</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.allowed">0.00</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.deduct">0.00</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.coins">0.00</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.copay">0.00</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.paid">0.00</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.adjust">0.00</td>
|
||||
<td class="text-right" ng-bind="PayAdjRef.withheld">0.00</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ng-repeat="temp in PayAdjRefundBlankRows">
|
||||
<td></td><td></td><td></td><td></td><td></td><td></td>
|
||||
<td></td><td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- ICD, CPT and Revenue pane -->
|
||||
<div class="tab-pane active" id="claimedi9999999999999">
|
||||
|
||||
<div id="icdCodesTableDiv">
|
||||
<table class="table table-bordered blue-fade-table" id="icdCodesTable">
|
||||
<thead><tr>
|
||||
<th class="text-center">#</th>
|
||||
<th></th>
|
||||
<th>Code</th>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
<tbody ng-model="ICDCodes">
|
||||
<tr ng-repeat="icd in ICDCodes">
|
||||
<td class="text-center"><span ng-bind="$index+1">1</span></td>
|
||||
<td class="text-center"><span class="icd10-green">10</span></td>
|
||||
<td title="R69"><input id="UBClaimIpt2ngR0" type="text"
|
||||
class="input-xs editableTDText" value="R69"></td>
|
||||
<td title="Illness, unspecified">Illness, unspecified</td>
|
||||
<td class="text-center"><button id="UBClaimBtn1ngR0"></button></td>
|
||||
</tr>
|
||||
<tr id="2">
|
||||
<td class="text-center">*</td>
|
||||
<td></td>
|
||||
<td><input type="text" ng-model="newICD" id="txtnewIcd" class="input-xs editableTDText"></td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- Condition Codes FL 18-28, as the real form (Dom.txt, 2026-10-16):
|
||||
select-claim-code inputs uBClaimCondtnCode1..11, no blur / change
|
||||
handler. setConditionCodes([...]) fills them per test case. -->
|
||||
<table class="table table-bordered blue-fade-table" id="example4">
|
||||
<thead><tr><th colspan="11" class="text-center">Condition Codes</th></tr></thead>
|
||||
<thead><tr><th>18</th><th>19</th><th>20</th><th>21</th><th>22</th><th>23</th>
|
||||
<th>24</th><th>25</th><th>26</th><th>27</th><th>28</th></tr></thead>
|
||||
<tbody><tr id="conditionCodeRow"></tr></tbody>
|
||||
</table>
|
||||
<script>
|
||||
(function () {
|
||||
var row = document.getElementById('conditionCodeRow');
|
||||
for (var n = 1; n <= 11; n++) {
|
||||
row.insertAdjacentHTML('beforeend',
|
||||
'<td><select-claim-code field-id="uBClaimCondtnCode' + n + '" max-length="2"' +
|
||||
' table-name="UB92_conditioncodes" search-record="Ub92Data.ConditionCode' + n + '">' +
|
||||
'<div class="select-record"><div class="input-group">' +
|
||||
'<input type="text" class="form-control editableTDText text-center" maxlength="2"' +
|
||||
' id="uBClaimCondtnCode' + n + '" ng-model="searchRecord" ng-focus="loadCodes();"' +
|
||||
' ng-keydown="validateKey($event);" ng-keyup="showCodes($event);">' +
|
||||
'</div></div></select-claim-code></td>');
|
||||
}
|
||||
// A test sets this to have the box marked invalid on input, as
|
||||
// select-claim-code's ng-class does for a code it does not know.
|
||||
window.conditionCodeInvalid = false;
|
||||
row.addEventListener('input', function (e) {
|
||||
if (window.conditionCodeInvalid) e.target.classList.add('invalid');
|
||||
});
|
||||
window.setConditionCodes = function (codes) {
|
||||
for (var n = 1; n <= 11; n++) {
|
||||
document.getElementById('uBClaimCondtnCode' + n).value = codes[n - 1] || '';
|
||||
}
|
||||
};
|
||||
})();
|
||||
</script>
|
||||
|
||||
<!-- CPT / Revenue tabs -->
|
||||
<ul class="nav">
|
||||
<li class="active" id="liCptRev"><a class="cursor addBrdr" data-toggle="tab"
|
||||
data-target="#cptRev9999999999999" ng-click="cptTabUpdate('cptRev')">CPT/Revenue Codes (1)</a></li>
|
||||
<li id="liCPTHcpcs"><a class="cursor" data-toggle="tab"
|
||||
data-target="#cptHCPCS9999999999999" ng-click="cptTabUpdate('cptHcpcs')">CPT/HCPCS (1)</a></li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content claim-tab">
|
||||
<div class="tab-pane active" id="cptRev9999999999999">
|
||||
<table id="UBClaimTbl10" class="table table-bordered blue-fade-table">
|
||||
<thead><tr>
|
||||
<th class="text-center"></th>
|
||||
<th>42 RV CC</th>
|
||||
<th>43 Description</th>
|
||||
<th>44 HCPCS/Rates</th>
|
||||
<th>45 Srvc Date</th>
|
||||
<th>46 Srvc Unit</th>
|
||||
<th>47 Total Charges</th>
|
||||
<th>48 Non Convered</th>
|
||||
<th>49 </th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="cptdetails in UB92CPTDetails">
|
||||
<td class="text-center">1</td>
|
||||
<td>0900</td>
|
||||
<td>Psychological Services</td>
|
||||
<td>H0023 GT</td>
|
||||
<td>06/29/2026</td>
|
||||
<td>1</td>
|
||||
<td class="text-right">74.00</td>
|
||||
<td></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
<tr ng-repeat="temp in UB92CPTDetailsBlankRows">
|
||||
<td></td><td> </td><td></td><td></td><td></td>
|
||||
<td></td><td></td><td></td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="UBClaimTbl11" class="table">
|
||||
<thead><tr>
|
||||
<th></th><th></th><th></th><th></th>
|
||||
<th class="text-center">Total Charges </th>
|
||||
<th class="text-right" ng-bind="TotalCharges">74.00</th>
|
||||
<th></th><th></th>
|
||||
</tr></thead>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="cptHCPCS9999999999999">
|
||||
<div class="allbrd cptTbl" id="CPTCodesDiv">
|
||||
<table class="table table-bordered blue-fade-table" id="claimCPTCodesTable9999999999999">
|
||||
<thead><tr>
|
||||
<th class="text-center">#</th><th> </th><th>Code</th><th>POS</th><th>TOS</th>
|
||||
<th>SDOS</th><th>EDOS</th><th>M1</th><th>M2</th><th>M3</th><th>M4</th>
|
||||
<th>ICD1</th><th>ICD2</th><th>ICD3</th><th>ICD4</th><th>Units</th>
|
||||
<th>Billed Fee</th><th>Pro Id</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_0">
|
||||
<td class="text-center"><span ng-bind="$index + 1">1</span></td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk0" checked></td>
|
||||
<td title="Behavioral Health Outreach Service (TH)"><input id="uBClaimCPTCode0" type="text" value="H0023" readonly></td>
|
||||
<td><input type="text" id="uBClaimPOS0" value="11"></td>
|
||||
<td><input type="text" id="uBClaimTOS0" value="1"></td>
|
||||
<td><input type="text" id="uBClaimSDOS0" value="06/29/2026"></td>
|
||||
<td><input type="text" id="uBClaimEDOS0" value="06/29/2026"></td>
|
||||
<td><input type="text" id="uBClaimMod10" value="GT"></td>
|
||||
<td><input type="text" id="uBClaimMod20" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod30" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod40" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD10" value="1"></td>
|
||||
<td><input type="text" id="uBClaimICD20" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD30" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD40" value=""></td>
|
||||
<td><input type="text" id="uBClaimUnits0" value="1"></td>
|
||||
<td title="74.00"><input type="text" id="uBClaimBillFee0" value="74.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_1">
|
||||
<td class="text-center"><span>2</span></td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk1" checked></td>
|
||||
<td title="Psychiatric diagnostic evaluation"><input id="uBClaimCPTCode1" type="text" value="90791" readonly></td>
|
||||
<td><input type="text" id="uBClaimPOS1" value="11"></td>
|
||||
<td><input type="text" id="uBClaimTOS1" value="1"></td>
|
||||
<td><input type="text" id="uBClaimSDOS1" value="06/30/2026"></td>
|
||||
<td><input type="text" id="uBClaimEDOS1" value="06/30/2026"></td>
|
||||
<td><input type="text" id="uBClaimMod11" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod21" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod31" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod41" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD11" value="1"></td>
|
||||
<td><input type="text" id="uBClaimICD21" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD31" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD41" value=""></td>
|
||||
<td><input type="text" id="uBClaimUnits1" value="2"></td>
|
||||
<td title="150.00"><input type="text" id="uBClaimBillFee1" value="150.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<!-- the blank entry row eCW leaves at the bottom: no CPTROW_ id -->
|
||||
<tr>
|
||||
<td class="text-center">*</td>
|
||||
<td class="text-center"><input type="checkbox" checked></td>
|
||||
<td><input type="text" id="editableTDTextNewCPTcode" ng-model="newCPT"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- bottom tabs: notes, error logs -->
|
||||
<div class="tab-content error-claim-tab">
|
||||
<div class="tab-pane active" id="tab19999999999999">
|
||||
<label>Billing Notes</label>
|
||||
<textarea class="form-control" ng-model="ClaimData.notes" id="ubClaimNotes">Awaiting coding review.</textarea>
|
||||
<table id="UBClaimTbl15" class="table table-condensed">
|
||||
<thead><tr><th colspan="2"><label>Patient Portion</label></th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Charges</td><td class="text-right boldTd" ng-bind="InvBalances[0].PtResp">0.00</td></tr>
|
||||
<tr><td>Payments ...</td><td class="text-right boldTd" ng-bind="InvBalances[0].PtPayment">0.00</td></tr>
|
||||
<tr><td>Balance</td><td class="text-right boldTd" ng-bind="InvBalances[0].PtBalance">0.00</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="UBClaimTbl16" class="table table-condensed">
|
||||
<thead><tr><th colspan="2"><label>Total</label></th></tr></thead>
|
||||
<tbody>
|
||||
<tr><td>Charges</td><td class="text-right boldTd" ng-bind="InvBalances[0].InvoiceAmount">224.00</td></tr>
|
||||
<tr><td>Payments ...</td><td class="text-right boldTd" ng-bind="InvBalances[0].Payment">0.00</td></tr>
|
||||
<tr><td>Balance</td><td class="text-right boldTd" ng-bind="InvBalances[0].Balance">224.00</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="tab29999999999999">
|
||||
<table id="UBClaimTbl17" class="table table-bordered blue-fade-table">
|
||||
<thead><tr>
|
||||
<th class="text-center">Sl No</th>
|
||||
<th class="text-center">Claim/Batch #</th>
|
||||
<th class="text-center">Error</th>
|
||||
<th>Actions</th>
|
||||
<th> </th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<!-- ngRepeat: chkError in ChkCurUB92Batch -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /modal-body -->
|
||||
|
||||
<div class="grey-bg modal-footer">
|
||||
<button id="UBClaimProgressNoteBtn" type="button" class="btn btn-lgrey btn-sm btn-xs pull-left"
|
||||
ng-click="viewProgNote()">Prog. Notes</button>
|
||||
<button id="UBClaimBtn17" type="button" class="btn btn-lgrey btn-sm btn-xs pull-right">Cancel</button>
|
||||
<button id="UBClaimBtn18" type="button" class="btn btn-blue btn-sm btn-xs pull-right">OK</button>
|
||||
</div>
|
||||
|
||||
</div></div></div>
|
||||
|
||||
<script>
|
||||
// Prog. Notes opens the note in an IFRAME inside a modal, with Close on this
|
||||
// parent page - which is how eCW actually serves it. The note is a whole
|
||||
// printable document with its own <html>, so it cannot be inline.
|
||||
//
|
||||
// Reproduced faithfully because the split is the trap: reading this modal's
|
||||
// innerText finds none of the note, and looking for Close inside the note
|
||||
// finds nothing either. progress_note.find() walks the frames; close()
|
||||
// clicks here. The ng-click typo "closeProgressNoteViwer" is eCW's own.
|
||||
// Cancel and OK both hide the same backdrop eCW's real closeUBClaimScreen()
|
||||
// / saveAllData(true) do — added so tests can prove the modal actually
|
||||
// closes on both paths, not just that the button was clicked. Neither
|
||||
// button did anything at all before this: the original fixture only ever
|
||||
// needed to test reading, never closing.
|
||||
document.getElementById('UBClaimBtn17').addEventListener('click', function () {
|
||||
document.getElementById('UBClaimDiv9999999999999').style.display = 'none';
|
||||
});
|
||||
document.getElementById('UBClaimBtn18').addEventListener('click', function () {
|
||||
document.getElementById('UBClaimDiv9999999999999').style.display = 'none';
|
||||
});
|
||||
|
||||
// The real DOM (claim 9120000) carries ng-blur="updateCPTMod(...)" on every
|
||||
// M1 input — Angular does not commit the modifier to CPTCodes[$index].mod1
|
||||
// until that fires. Reproduced here as a data attribute, not real Angular,
|
||||
// so a test can prove set_cpt_pos_modifier() actually blurs the field
|
||||
// instead of just filling it and trusting the fill to be enough.
|
||||
document.querySelectorAll('[id^="uBClaimMod1"]').forEach(function (el) {
|
||||
el.addEventListener('blur', function () { el.dataset.blurred = '1'; });
|
||||
});
|
||||
|
||||
document.getElementById('UBClaimProgressNoteBtn').addEventListener('click', function () {
|
||||
var m = document.createElement('div');
|
||||
m.className = 'modal in';
|
||||
m.id = 'progressNoteViewerDiv9999999999999';
|
||||
m.style.display = 'block';
|
||||
m.innerHTML =
|
||||
'<div class="modal-dialog"><div class="modal-content">'
|
||||
+ '<div class="modal-header"><h4 class="modal-title">Progress Note</h4></div>'
|
||||
+ '<div class="modal-body">'
|
||||
+ '<iframe id="progressNoteFrame" src="progress_note.html" '
|
||||
+ 'style="width:900px;height:400px;border:0"></iframe>'
|
||||
+ '</div>'
|
||||
+ '<div class="modal-footer">'
|
||||
+ '<button type="button" class="btn btn-blue btn-sm btn-xs pull-right" '
|
||||
+ 'ng-click="closeProgressNoteViwer()">Close</button>'
|
||||
+ '</div>'
|
||||
+ '</div></div>';
|
||||
document.body.appendChild(m);
|
||||
m.querySelector('[ng-click="closeProgressNoteViwer()"]')
|
||||
.addEventListener('click', function () { m.style.display = 'none'; });
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Vendored
+506
@@ -0,0 +1,506 @@
|
||||
<!doctype html>
|
||||
<!--
|
||||
A cut-down UB Claim form carrying a VACCINE claim, for conditions
|
||||
/vaccine_admin_codes.py (Charge Condition Document row 18).
|
||||
|
||||
Reproduced by hand, never saved from the live page: a real claim is
|
||||
patient data and must never be committed. The per-open timestamp here is
|
||||
7777777777777 — deliberately different from ub_claim.html's
|
||||
9999999999999 and hcfa_claim.html's 8888888888888, so a selector that
|
||||
had one baked in fails against the others.
|
||||
|
||||
What this fixture exists to reproduce:
|
||||
* the UB form's CPT/HCPCS grid — `tr id="CPTROW_n"`, `uBClaim*<n>`
|
||||
field ids, and the code cell's `title` holding eCW's HOVER text
|
||||
("TETANUS DIPHTHERIA TOXOIDS AND ACELLULAR PERTUSSIS VACC", "PCV20"),
|
||||
which is what extract() saves as a row's `description` and what the
|
||||
condition recognises a vaccine by;
|
||||
* each row's "Assign To Patient" checkbox (`ng-click="updateBillToIns(n)"`)
|
||||
— only a CHECKED row is actually billed, so only checked vaccine and
|
||||
admin rows count;
|
||||
* the patient strip's DOB (`ng-bind="PatientInfo.dob"`) and age/sex
|
||||
text (" 50Y, M" / " 12M 12D, F");
|
||||
* the "Add" button + Fee Schedule picker, copied from hcfa_claim.html.
|
||||
NOTE: that picker flow is live-verified on the "Claim"/HCFA and Dental
|
||||
screens (claim 9109000), NOT yet on the UB form — it is here because
|
||||
the condition calls the same `claim_form.add_cpt_row()` either way.
|
||||
|
||||
The default grid is an adult (DOB 01/02/1976, 50 on the 07/07/2026 date
|
||||
of service), Medicaid, with three checked vaccines and NO admin code —
|
||||
exactly the shape the condition fires on: 90732, 90746 and 90734, all on
|
||||
the client's vaccine code list (2026-10-13; vaccines are recognised by
|
||||
that list only, so the earlier 90677 / 90750 / 90715 here were replaced).
|
||||
|
||||
Tests reshape it with the helpers in the script block at the bottom
|
||||
(seedCptRows, setPatient, setClaimDate, removeFromFeeSchedule, and the
|
||||
window.feeScheduleOk* / window.units* behaviour flags documented there)
|
||||
rather than keeping one fixture per scenario.
|
||||
-->
|
||||
<html>
|
||||
<body>
|
||||
|
||||
<div class="modal fade bluetheme ubClaimModalBackdrop in" id="UBClaimDiv7777777777777" style="display:block">
|
||||
<div class="modal-dialog visible" id="UBClaimDiv">
|
||||
<div id="UBClaimModalContent7777777777777" class="modal-content">
|
||||
|
||||
<div class="modal-header">
|
||||
<button id="UBClaimBtn3" type="button" class="close">×</button>
|
||||
<h4 class="modal-title" id="myModalLabel">UB Form Details</h4>
|
||||
</div>
|
||||
|
||||
<div class="modal-body">
|
||||
|
||||
<!-- patient strip -->
|
||||
<div class="single-column middle">
|
||||
<span class="blue_txt" ng-bind="showEllipsisForField(PatientInfo.namewithsuffix,25)" title="Doe, Jordan">Doe, Jordan</span>
|
||||
<span class="blue_txt" id="patAgeSex" ng-bind="' ' + PatientInfo.Age + ', ' + PatientInfo.patSex"> 50Y, M</span>
|
||||
<span class="fnt11" id="patDob" ng-bind="PatientInfo.dob">01/02/1976</span>
|
||||
Acct No : <span ng-bind="showEllipsisForField(PatientInfo.ControlNo,10)">500001</span>
|
||||
</div>
|
||||
|
||||
<table class="table section-table"><tbody><tr>
|
||||
<td>
|
||||
<table class="table"><tbody>
|
||||
<tr>
|
||||
<td><label>Claim No:</label></td>
|
||||
<td class="w60p ubClaimNumberText"><span title="9113000">9113000</span></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label>Claim Date</label></td>
|
||||
<td><input type="text" id="claimDate7777777777777" ng-model="ClaimData.ClaimDate" value="07/07/2026"></td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
</td>
|
||||
<td><label>Bill Type</label><input type="text" ng-model="Ub92Data.BillType" id="BillType" value="131"></td>
|
||||
</tr></tbody></table>
|
||||
|
||||
<!-- Insurance and Payments pane (shown, so extract() reads the payer) -->
|
||||
<table id="UBClaimTbl3" class="table table-bordered blue-fade-table">
|
||||
<thead><tr>
|
||||
<th style="width:30px"><div class="th-inner"></div></th>
|
||||
<th><div class="th-inner">Bill To</div></th>
|
||||
<th><div class="th-inner">Name</div></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="insurance in InvInsList">
|
||||
<td><span ng-bind="insurance.sequanceNo">P</span></td>
|
||||
<td><input type="checkbox" name="billto" checked></td>
|
||||
<td><span ng-bind="insurance.InsName">Medicaid</span></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<table class="table table-bordered blue-fade-table" id="icdCodesTable">
|
||||
<thead><tr><th class="text-center">#</th><th></th><th>Code</th><th>Name</th><th></th></tr></thead>
|
||||
<tbody ng-model="ICDCodes">
|
||||
<tr ng-repeat="icd in ICDCodes">
|
||||
<td class="text-center"><span>1</span></td><td></td>
|
||||
<td title="Z00.00"><input type="text" value="Z00.00"></td>
|
||||
<td title="Encounter for general adult medical examination">Encounter for general adult medical examination</td><td></td>
|
||||
</tr>
|
||||
<tr ng-repeat="icd in ICDCodes">
|
||||
<td class="text-center"><span>2</span></td><td></td>
|
||||
<td title="Z23"><input type="text" value="Z23"></td>
|
||||
<td title="Encounter for immunization">Encounter for immunization</td><td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
<ul class="nav">
|
||||
<li id="liCptRev"><a class="cursor" data-toggle="tab" data-target="#cptRev7777777777777"
|
||||
ng-click="cptTabUpdate('cptRev')">CPT/Revenue Codes</a></li>
|
||||
<li class="active" id="liCPTHcpcs"><a class="cursor" data-toggle="tab" data-target="#cptHCPCS7777777777777"
|
||||
ng-click="cptTabUpdate('cptHcpcs')">CPT/HCPCS</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Not live-verified on the UB form — see the header comment. -->
|
||||
<button id="ubClaimAddCPTBtn7777777777777" type="button" class="btn btn-lgrey btn-xs"
|
||||
ng-click="addProcedureCode('');">Add</button>
|
||||
|
||||
<div class="tab-pane active" id="cptHCPCS7777777777777">
|
||||
<div class="allbrd cptTbl" id="CPTCodesDiv">
|
||||
<table class="table table-bordered blue-fade-table" id="claimCPTCodesTable7777777777777">
|
||||
<thead><tr>
|
||||
<th class="text-center">#</th><th> </th><th>Code</th><th>POS</th><th>TOS</th>
|
||||
<th>SDOS</th><th>EDOS</th><th>M1</th><th>M2</th><th>M3</th><th>M4</th>
|
||||
<th>ICD1</th><th>ICD2</th><th>ICD3</th><th>ICD4</th><th>Units</th>
|
||||
<th>Billed Fee</th><th>Pro Id</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_0">
|
||||
<td class="text-center"><span>1</span></td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk77777777777770" ng-click="updateBillToIns(0)" ng-checked="cpt.BillToIns=='1'" checked></td>
|
||||
<td title="PREVENTIVE VISIT EST AGE 40-64"><input id="uBClaimCPTCode0" type="text" value="99396" readonly></td>
|
||||
<td><input type="text" id="uBClaimPOS0" value="11"></td>
|
||||
<td><input type="text" id="uBClaimTOS0" value="1"></td>
|
||||
<td><input type="text" id="uBClaimSDOS0" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimEDOS0" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimMod10" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod20" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod30" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod40" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD10" value="1"></td>
|
||||
<td><input type="text" id="uBClaimICD20" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD30" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD40" value=""></td>
|
||||
<td><input type="text" id="uBClaimUnits0" value="1" ng-blur="padZero($index);editCPTRow('UNITS')"></td>
|
||||
<td title="250.00"><input type="text" id="uBClaimBillFee0" value="250.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_1">
|
||||
<td class="text-center"><span>2</span></td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk77777777777771" ng-click="updateBillToIns(1)" ng-checked="cpt.BillToIns=='1'" checked></td>
|
||||
<td title="PNEUMOCOCCAL VACCINE"><input id="uBClaimCPTCode1" type="text" value="90732" readonly></td>
|
||||
<td><input type="text" id="uBClaimPOS1" value="11"></td>
|
||||
<td><input type="text" id="uBClaimTOS1" value="1"></td>
|
||||
<td><input type="text" id="uBClaimSDOS1" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimEDOS1" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimMod11" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod21" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod31" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod41" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD11" value="2"></td>
|
||||
<td><input type="text" id="uBClaimICD21" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD31" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD41" value=""></td>
|
||||
<td><input type="text" id="uBClaimUnits1" value="1" ng-blur="padZero($index);editCPTRow('UNITS')"></td>
|
||||
<td title="290.00"><input type="text" id="uBClaimBillFee1" value="290.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_2">
|
||||
<td class="text-center"><span>3</span></td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk77777777777772" ng-click="updateBillToIns(2)" ng-checked="cpt.BillToIns=='1'" checked></td>
|
||||
<td title="HEPATITIS B VACCINE ADULT DOSAGE FOR INTRAMUSCULAR USE"><input id="uBClaimCPTCode2" type="text" value="90746" readonly></td>
|
||||
<td><input type="text" id="uBClaimPOS2" value="11"></td>
|
||||
<td><input type="text" id="uBClaimTOS2" value="1"></td>
|
||||
<td><input type="text" id="uBClaimSDOS2" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimEDOS2" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimMod12" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod22" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod32" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod42" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD12" value="2"></td>
|
||||
<td><input type="text" id="uBClaimICD22" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD32" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD42" value=""></td>
|
||||
<td><input type="text" id="uBClaimUnits2" value="1" ng-blur="padZero($index);editCPTRow('UNITS')"></td>
|
||||
<td title="240.00"><input type="text" id="uBClaimBillFee2" value="240.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<tr ng-repeat="cpt in CPTCodes" id="CPTROW_3">
|
||||
<td class="text-center"><span>4</span></td>
|
||||
<td><input type="checkbox" id="claimCPTBillToInsChk77777777777773" ng-click="updateBillToIns(3)" ng-checked="cpt.BillToIns=='1'" checked></td>
|
||||
<td title="MENINGOCOCCAL VACCINE, IM"><input id="uBClaimCPTCode3" type="text" value="90734" readonly></td>
|
||||
<td><input type="text" id="uBClaimPOS3" value="11"></td>
|
||||
<td><input type="text" id="uBClaimTOS3" value="1"></td>
|
||||
<td><input type="text" id="uBClaimSDOS3" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimEDOS3" value="07/07/2026"></td>
|
||||
<td><input type="text" id="uBClaimMod13" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod23" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod33" value=""></td>
|
||||
<td><input type="text" id="uBClaimMod43" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD13" value="2"></td>
|
||||
<td><input type="text" id="uBClaimICD23" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD33" value=""></td>
|
||||
<td><input type="text" id="uBClaimICD43" value=""></td>
|
||||
<td><input type="text" id="uBClaimUnits3" value="1" ng-blur="padZero($index);editCPTRow('UNITS')"></td>
|
||||
<td title="60.00"><input type="text" id="uBClaimBillFee3" value="60.00"></td>
|
||||
<td><input type="text" value="" readonly></td>
|
||||
</tr>
|
||||
<!-- the blank entry row eCW leaves at the bottom: no CPTROW_ id -->
|
||||
<tr id="cptEntryRow">
|
||||
<td class="text-center">*</td>
|
||||
<td class="text-center"><input type="checkbox" checked></td>
|
||||
<td><input type="text" id="editableTDTextNewCPTcode" ng-model="newCPT"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td><input type="text"></td><td><input type="text"></td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- /modal-body -->
|
||||
|
||||
<!-- eCW's shared Fee Schedule picker — same stable ids as hcfa_claim.html. -->
|
||||
<div class="modal fade bluetheme" id="claimFeeScheduleModal" style="display:none">
|
||||
<div class="modal-dialog"><div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button id="claimFeeScheduleBtn1" type="button" class="close">×</button>
|
||||
<h4 class="modal-title">Fee Schedule</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<table class="table table-bordered claimFSCPTTbl" id="example1">
|
||||
<thead><tr><th>Id</th><th>CPT</th><th>Name</th><th>Fee</th></tr></thead>
|
||||
<tbody id="claimFSCPTTblBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<input id="claimFeeScheduleIpt6" type="text" placeholder="Search CPT">
|
||||
<button id="claimFeeScheduleBtn8" type="button">Cancel</button>
|
||||
<button id="claimFeeSchOkBtn" type="button">OK</button>
|
||||
</div>
|
||||
</div></div>
|
||||
</div>
|
||||
|
||||
<div class="grey-bg modal-footer">
|
||||
<button id="UBClaimProgressNoteBtn" type="button" ng-click="viewProgNote()">Prog. Notes</button>
|
||||
<button id="UBClaimBtn17" type="button">Cancel</button>
|
||||
<button id="UBClaimBtn18" type="button">OK</button>
|
||||
</div>
|
||||
|
||||
</div></div></div>
|
||||
|
||||
<script>
|
||||
document.getElementById('UBClaimBtn17').addEventListener('click', function () {
|
||||
document.getElementById('UBClaimDiv7777777777777').style.display = 'none';
|
||||
});
|
||||
document.getElementById('UBClaimBtn18').addEventListener('click', function () {
|
||||
document.getElementById('UBClaimDiv7777777777777').style.display = 'none';
|
||||
});
|
||||
|
||||
var CPT_TBODY = document.querySelector('#claimCPTCodesTable7777777777777 tbody');
|
||||
var ENTRY_ROW = document.getElementById('cptEntryRow');
|
||||
|
||||
function esc(s) {
|
||||
return String(s == null ? '' : s).replace(/&/g, '&').replace(/"/g, '"')
|
||||
.replace(/</g, '<');
|
||||
}
|
||||
|
||||
// One UB-style CPTROW_<n>, the same markup as the static rows above.
|
||||
// `r.noCheckbox` drops the "Assign To Patient" checkbox entirely — the
|
||||
// shape extract() must report as billed: null, not false. `r.noFee`
|
||||
// likewise drops the Billed Fee input — extract() must report
|
||||
// billed_fee: null (never read), not "" (read and blank, i.e. zero).
|
||||
function buildCptRow(n, r) {
|
||||
var tr = document.createElement('tr');
|
||||
tr.id = 'CPTROW_' + n;
|
||||
tr.setAttribute('ng-repeat', 'cpt in CPTCodes');
|
||||
var checked = r.checked === false ? '' : ' checked';
|
||||
var box = r.noCheckbox ? '' :
|
||||
'<input type="checkbox" id="claimCPTBillToInsChk7777777777777' + n + '"'
|
||||
+ ' ng-click="updateBillToIns(' + n + ')" ng-checked="cpt.BillToIns==\'1\'"' + checked + '>';
|
||||
var f = function (prefix, v) {
|
||||
return '<td><input type="text" id="' + prefix + n + '" value="' + esc(v) + '"></td>';
|
||||
};
|
||||
tr.innerHTML =
|
||||
'<td class="text-center"><span>' + (n + 1) + '</span></td>'
|
||||
+ '<td>' + box + '</td>'
|
||||
+ '<td title="' + esc(r.title) + '"><input id="uBClaimCPTCode' + n + '" type="text" value="'
|
||||
+ esc(r.code) + '" readonly></td>'
|
||||
+ f('uBClaimPOS', r.pos == null ? '11' : r.pos)
|
||||
+ f('uBClaimTOS', '1')
|
||||
+ f('uBClaimSDOS', r.sdos == null ? '07/07/2026' : r.sdos)
|
||||
+ f('uBClaimEDOS', r.sdos == null ? '07/07/2026' : r.sdos)
|
||||
+ f('uBClaimMod1', r.mod1 || '') + f('uBClaimMod2', '')
|
||||
+ f('uBClaimMod3', '') + f('uBClaimMod4', '')
|
||||
+ f('uBClaimICD1', r.icd1 == null ? '1' : r.icd1) + f('uBClaimICD2', r.icd2 || '')
|
||||
+ f('uBClaimICD3', r.icd3 || '') + f('uBClaimICD4', r.icd4 || '')
|
||||
+ '<td><input type="text" id="uBClaimUnits' + n + '" value="' + esc(r.units || '1') + '"'
|
||||
+ ' ng-blur="padZero($index);editCPTRow(\'UNITS\')"'
|
||||
+ (r.unitFee ? ' data-unit-fee="' + esc(r.unitFee) + '"' : '') + '></td>'
|
||||
+ (r.noFee ? '<td></td>'
|
||||
: '<td title="' + esc(r.fee || '0.00') + '"><input type="text" id="uBClaimBillFee' + n
|
||||
+ '" value="' + esc(r.fee || '0.00') + '"></td>')
|
||||
+ '<td><input type="text" value="" readonly></td>';
|
||||
|
||||
// Stand-in for eCW's own ng-blur="padZero($index);editCPTRow('UNITS')":
|
||||
// re-price the line from its per-unit fee when Units loses focus, so a
|
||||
// test reading the fee back proves the blur actually happened.
|
||||
// window.unitsRepriceDelayMs makes the re-price land LATER (an async
|
||||
// $http-style handler); window.unitsNoReprice makes it never land.
|
||||
var units = tr.querySelector('[id^="uBClaimUnits"]');
|
||||
units.addEventListener('blur', function () {
|
||||
units.dataset.blurred = '1';
|
||||
var per = parseFloat(units.dataset.unitFee || '');
|
||||
var qty = parseFloat(units.value || '0');
|
||||
if (window.unitsNoReprice || isNaN(per) || isNaN(qty)) return;
|
||||
var reprice = function () {
|
||||
tr.querySelector('[id^="uBClaimBillFee"]').value = (per * qty).toFixed(2);
|
||||
};
|
||||
if (window.unitsRepriceDelayMs) setTimeout(reprice, window.unitsRepriceDelayMs);
|
||||
else reprice();
|
||||
});
|
||||
return tr;
|
||||
}
|
||||
|
||||
function cptRowCount() {
|
||||
return CPT_TBODY.querySelectorAll('tr[id^="CPTROW_"]').length;
|
||||
}
|
||||
|
||||
// Replace every CPTROW_ with `rows` (objects of buildCptRow's shape).
|
||||
window.seedCptRows = function (rows) {
|
||||
CPT_TBODY.querySelectorAll('tr[id^="CPTROW_"]').forEach(function (tr) { tr.remove(); });
|
||||
rows.forEach(function (r, i) { CPT_TBODY.insertBefore(buildCptRow(i, r), ENTRY_ROW); });
|
||||
};
|
||||
|
||||
window.setPatient = function (dob, ageSex) {
|
||||
document.getElementById('patDob').textContent = dob;
|
||||
document.getElementById('patAgeSex').textContent = ageSex;
|
||||
};
|
||||
|
||||
window.setClaimDate = function (d) {
|
||||
document.getElementById('claimDate7777777777777').value = d;
|
||||
};
|
||||
|
||||
// The Fee Schedule picker, copied from hcfa_claim.html: a seed catalog,
|
||||
// filtered "Starts With" the way the real picker defaults to. The decoy
|
||||
// 904711 starts with 90471, so a search for 90471 lists both — proving
|
||||
// add_cpt_row() picks the EXACT match, not the first row.
|
||||
var feeScheduleCatalog = [
|
||||
{id: '610001', code: '90460', name: 'IMMUNIZATION ADMIN THROUGH 18 YRS 1ST COMPONENT', fee: '25.00'},
|
||||
{id: '610002', code: '90461', name: 'IMMUNIZATION ADMIN THROUGH 18 YRS EACH ADD COMPONENT', fee: '15.00'},
|
||||
{id: '610003', code: '90471', name: 'IMMUNIZATION ADMIN SINGLE OR COMBINATION', fee: '30.00'},
|
||||
{id: '610004', code: '90472', name: 'IMMUNIZATION ADMIN, EACH ADD', fee: '20.00'},
|
||||
{id: '610005', code: '96372', name: 'THER/PROPH/DIAG INJ SC/IM', fee: '25.00'},
|
||||
// PPS Codes (#25). G0467H is left out on purpose: a PPS Code the
|
||||
// picker cannot find.
|
||||
{id: '610010', code: 'G0466', name: 'FQHC VISIT, NEW PATIENT', fee: '180.00'},
|
||||
{id: '610011', code: 'G0467', name: 'FQHC VISIT, ESTABLISHED PATIENT', fee: '150.00'},
|
||||
{id: '610012', code: 'G0467NH', name: 'FQHC VISIT, ESTABLISHED, NURSING HOME', fee: '150.00'},
|
||||
{id: '610013', code: 'G0468', name: 'FQHC VISIT, IPPE OR AWV', fee: '200.00'},
|
||||
{id: '610099', code: '904711', name: 'Unrelated decoy code', fee: '99.00'},
|
||||
];
|
||||
var feeScheduleSelected = null;
|
||||
|
||||
// Tests take a code out of the catalog to make its add fail the way a
|
||||
// missing fee-schedule entry would on the live picker.
|
||||
// Tests re-price a code in the catalog — e.g. to 0.00, a Fee Schedule
|
||||
// entry with no fee, so an added admin line comes back unpriced.
|
||||
window.setFeeScheduleFee = function (code, fee) {
|
||||
feeScheduleCatalog.forEach(function (r) { if (r.code === code) r.fee = fee; });
|
||||
};
|
||||
|
||||
window.removeFromFeeSchedule = function (code) {
|
||||
feeScheduleCatalog = feeScheduleCatalog.filter(function (r) { return r.code !== code; });
|
||||
};
|
||||
|
||||
// Tests set this to make OK close the picker WITHOUT adding a row, the
|
||||
// shape add_cpt_row() cannot tell from success on its own when the same
|
||||
// code already sits on the claim (e.g. an unchecked 90460).
|
||||
window.feeScheduleOkAddsNothing = false;
|
||||
|
||||
// More ways the live picker's OK has not been verified NOT to behave,
|
||||
// each one a shape the condition must report rather than hide:
|
||||
// feeScheduleOkKeepsOpen — OK does nothing, the picker stays up
|
||||
// (eCW's own validation holding it open);
|
||||
// feeScheduleOkRechecksExisting — OK re-checks and re-prices the claim's
|
||||
// existing UNCHECKED line for the code
|
||||
// instead of appending a new one;
|
||||
// feeScheduleInsertBeforeCode — the new line goes in ABOVE the first
|
||||
// existing line with this code, not at
|
||||
// the bottom (a sorted grid);
|
||||
// feeScheduleOkAlsoAdds — OK also appends a line for this code
|
||||
// (a second open keeping the first pick).
|
||||
// feeScheduleNewLineUnits — the new line comes back with this Units
|
||||
// (e.g. '0') instead of 1, priced to match,
|
||||
// so the bot's Units write + re-price path
|
||||
// runs (it writes Units only when eCW did
|
||||
// not already give the new line 1).
|
||||
window.feeScheduleOkKeepsOpen = false;
|
||||
window.feeScheduleOkRechecksExisting = false;
|
||||
window.feeScheduleNewLineUnits = null;
|
||||
window.feeScheduleInsertBeforeCode = null;
|
||||
window.feeScheduleOkAlsoAdds = null;
|
||||
|
||||
function renderFeeScheduleResults(filterText) {
|
||||
var tbody = document.getElementById('claimFSCPTTblBody');
|
||||
tbody.innerHTML = '';
|
||||
var q = (filterText || '').toUpperCase();
|
||||
feeScheduleCatalog
|
||||
.filter(function (row) { return row.code.toUpperCase().indexOf(q) === 0; })
|
||||
.forEach(function (row) {
|
||||
var tr = document.createElement('tr');
|
||||
tr.innerHTML =
|
||||
'<td>' + row.id + '</td>'
|
||||
+ '<td><span>' + row.code + '</span></td>'
|
||||
+ '<td>' + row.name + '</td>'
|
||||
+ '<td>' + row.fee + '</td>';
|
||||
tr.addEventListener('click', function () { feeScheduleSelected = row; });
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('ubClaimAddCPTBtn7777777777777').addEventListener('click', function () {
|
||||
feeScheduleSelected = null;
|
||||
document.getElementById('claimFeeScheduleIpt6').value = '';
|
||||
renderFeeScheduleResults('');
|
||||
document.getElementById('claimFeeScheduleModal').style.display = 'block';
|
||||
});
|
||||
|
||||
document.getElementById('claimFeeScheduleIpt6').addEventListener('input', function () {
|
||||
renderFeeScheduleResults(this.value);
|
||||
});
|
||||
|
||||
document.getElementById('claimFeeScheduleBtn8').addEventListener('click', function () {
|
||||
document.getElementById('claimFeeScheduleModal').style.display = 'none';
|
||||
});
|
||||
|
||||
// OK appends a row the way eCW's own addClaimFSCPTCode('save') does —
|
||||
// including filling POS, dates and the ICD pointers ITSELF (faked here by
|
||||
// copying the claim's first line). The bot never hand-fills those; a test
|
||||
// proves the added row keeps exactly what this "eCW" put there.
|
||||
function rowsWithCode(code) {
|
||||
return Array.prototype.filter.call(
|
||||
CPT_TBODY.querySelectorAll('tr[id^="CPTROW_"]'),
|
||||
function (tr) { return tr.querySelector('[id^="uBClaimCPTCode"]').value === code; });
|
||||
}
|
||||
|
||||
function appendFromCatalog(row) {
|
||||
var first = CPT_TBODY.querySelector('tr[id^="CPTROW_"]');
|
||||
var copy = function (prefix) {
|
||||
var e = first ? first.querySelector('[id^="' + prefix + '"]') : null;
|
||||
return e ? e.value : '';
|
||||
};
|
||||
var newUnits = window.feeScheduleNewLineUnits || '1';
|
||||
var anchor = ENTRY_ROW;
|
||||
if (window.feeScheduleInsertBeforeCode) {
|
||||
var hits = rowsWithCode(window.feeScheduleInsertBeforeCode);
|
||||
if (hits.length) anchor = hits[0];
|
||||
}
|
||||
CPT_TBODY.insertBefore(buildCptRow(cptRowCount(), {
|
||||
code: row.code, title: row.name, checked: true,
|
||||
pos: copy('uBClaimPOS'), sdos: copy('uBClaimSDOS'),
|
||||
icd1: copy('uBClaimICD1'), icd2: copy('uBClaimICD2'),
|
||||
icd3: copy('uBClaimICD3'), icd4: copy('uBClaimICD4'),
|
||||
units: newUnits, unitFee: row.fee,
|
||||
fee: newUnits === '1' ? row.fee
|
||||
: (parseFloat(row.fee) * parseFloat(newUnits || '0')).toFixed(2),
|
||||
}), anchor);
|
||||
}
|
||||
|
||||
document.getElementById('claimFeeSchOkBtn').addEventListener('click', function () {
|
||||
if (window.feeScheduleOkKeepsOpen) return;
|
||||
document.getElementById('claimFeeScheduleModal').style.display = 'none';
|
||||
if (!feeScheduleSelected || window.feeScheduleOkAddsNothing) return;
|
||||
var row = feeScheduleSelected;
|
||||
if (window.feeScheduleOkRechecksExisting) {
|
||||
var old = rowsWithCode(row.code).filter(function (tr) {
|
||||
var box = tr.querySelector('[ng-click*="updateBillToIns("]');
|
||||
return box && !box.checked;
|
||||
})[0];
|
||||
if (old) {
|
||||
old.querySelector('[ng-click*="updateBillToIns("]').checked = true;
|
||||
old.querySelector('[id^="uBClaimBillFee"]').value = row.fee;
|
||||
return;
|
||||
}
|
||||
}
|
||||
appendFromCatalog(row);
|
||||
if (window.feeScheduleOkAlsoAdds) {
|
||||
var extra = feeScheduleCatalog.filter(function (r) {
|
||||
return r.code === window.feeScheduleOkAlsoAdds;
|
||||
})[0];
|
||||
// only on a LATER pick of a different code — the stale first pick
|
||||
if (extra && extra.code !== row.code) appendFromCatalog(extra);
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in new issue
Block a user