Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
e2ae491
fix(eye): Dispense glasses Rx: manifest/dispensed refraction (#10882)
stephenwaite Feb 24, 2026
2670054
fix(eye): add state license number to prescription popup (#10894)
stephenwaite Mar 7, 2026
ebc2e36
fix(images): bring in Marie's customized eye imgs
stephenwaite Mar 19, 2026
e25d963
fix(eye): pt photo in eye report
stephenwaite Mar 21, 2026
268f89a
onReturnRetrieveKey() for eye report
stephenwaite Mar 21, 2026
ba6de08
fix retreive_action call positional args
stephenwaite Mar 21, 2026
920d3b8
fix retreive_action call positional args
stephenwaite Mar 21, 2026
093c257
onReturnRetrieveKey() for eye report
stephenwaite Mar 21, 2026
badc7d7
reconcile custom statement
stephenwaite Mar 25, 2026
d999998
fix for no payers in claim
stephenwaite Mar 25, 2026
ab3e9c6
reconcile for special statement
stephenwaite Mar 25, 2026
2763910
copy address to guardian
stephenwaite Mar 25, 2026
ebc39ee
copy statements to documents
stephenwaite Mar 25, 2026
0e4565a
fix background color
stephenwaite Mar 26, 2026
1aba75b
fix name, phone, email
stephenwaite Mar 26, 2026
b23b85d
ray's change for recalls
stephenwaite Mar 31, 2026
fc6f880
undo statements category name change
stephenwaite Apr 9, 2026
8425810
fix statement save to docs
stephenwaite Apr 14, 2026
3c3587a
fix saving in collection on auto create
stephenwaite Apr 14, 2026
cc08d2d
typo
stephenwaite Apr 19, 2026
1c9e2e2
add pt name to receipt
stephenwaite Apr 19, 2026
ea4bd3f
check for null usernames in eye mag comms
stephenwaite Apr 19, 2026
9b0ad05
fix up for emailing statement
stephenwaite Apr 20, 2026
44edb0e
addressee
stephenwaite Apr 20, 2026
8a2d40e
alignment and review sugg
stephenwaite Apr 20, 2026
6e90bd6
touch ups
stephenwaite Apr 20, 2026
40ca7ff
optimize with cache and cat id outside loop
stephenwaite Apr 20, 2026
c9cc626
sleep btw emails for rate limit
stephenwaite Apr 20, 2026
dc6208b
confirm sending statement emails
stephenwaite Apr 20, 2026
3a886f8
don't save to docs when statement generated, confirm emails
stephenwaite Apr 20, 2026
ad2d303
don't save to docs when statement generated, confirm emails
stephenwaite Apr 20, 2026
de24692
save as text for pdf d/l due to size and slowness
stephenwaite Apr 20, 2026
83cc92e
increase timeout to 5 mins
stephenwaite Apr 20, 2026
9eb5b70
statement msgs
stephenwaite Apr 21, 2026
26977dd
kancare 2ndary adjusts
stephenwaite Apr 23, 2026
8a1f62d
invoice writeoff adj bugs
stephenwaite Apr 23, 2026
3989ac7
debug prior ins name
stephenwaite Apr 23, 2026
d3edfb4
debug prior ins name
stephenwaite Apr 23, 2026
94006ab
fix invoice summary, remove debug
stephenwaite Apr 23, 2026
6addce3
fix posting strict type svc_paid bug
stephenwaite Apr 27, 2026
9e75427
resolved bug causing the FTCF check-box to display on report even not…
margarethaywood Apr 28, 2026
4c9c4f3
fix(eye_mag): correct checkbox round-trip and update VF default text
stephenwaite Apr 29, 2026
94154f0
changes addl findings to sensory/neuro exam
stephenwaite Apr 29, 2026
f7bd988
use unicode chars and logic fix for visual fields
stephenwaite Apr 30, 2026
3f9ce46
co 97 w/o
stephenwaite May 5, 2026
4618aab
co 97 w/o for 92015
stephenwaite May 5, 2026
75fa2a1
remove require portal email statements
stephenwaite May 6, 2026
d480ed7
adj reason titles
stephenwaite May 7, 2026
a37e00d
adj reason titles
stephenwaite May 7, 2026
2810419
pt statement table
stephenwaite May 7, 2026
2aa644c
hard code provider npi
stephenwaite May 19, 2026
6648c72
retry login
stephenwaite Jun 1, 2026
094105d
skip path check when isDocuments is set in fax module
stephenwaite Jun 3, 2026
45a2ca3
prime the sftp chdir pump
stephenwaite Jun 3, 2026
966ce60
debug elig
stephenwaite Jun 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .phpstan/baseline/variable.undefined.php
Original file line number Diff line number Diff line change
Expand Up @@ -5260,7 +5260,7 @@
];
$ignoreErrors[] = [
'message' => '#^Variable \\$RXTYPE might not be defined\\.$#',
'count' => 3,
'count' => 2,
'path' => __DIR__ . '/../../interface/forms/eye_mag/SpectacleRx.php',
];
$ignoreErrors[] = [
Expand Down
73 changes: 48 additions & 25 deletions interface/billing/sl_eob_invoice.php
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@
const pelement = f['form_line[' + code + '][pay]'];
const aelement = f['form_line[' + code + '][adj]'];
const relement = f['form_line[' + code + '][reason]'];
const tmp = belement.value - pelement.value;
aelement.value = Number(tmp).toFixed(2);

const bal = parseFloat(String(belement.value).replace(/,/g, '')) || 0;
const pay = parseFloat(String(pelement.value).replace(/,/g, '')) || 0;
const tmp = bal - pay;

aelement.value = tmp.toFixed(2);
if (aelement.value && !relement.value) {
relement.selectedIndex = 1;
}
Expand Down Expand Up @@ -224,12 +228,10 @@
<?php require($GLOBALS['srcdir'] . '/js/xl/jquery-datetimepicker-2-5-4.js.php'); ?>
<?php // can add any additional javascript settings to datetimepicker here; need to prepend first setting with a comma ?>
});
$("#ins_done_group").on("change", "input[name='form_done']", function() {
$("#ins_done").val('changed');
});
});

$("#ins_done").on("change", function() {
$("#ins_done").val('changed');
});

</script>
<style>
@media only screen and (max-width: 768px) {
Expand Down Expand Up @@ -268,7 +270,7 @@
}

// A/R case, $trans_id matches form_encounter.id.
$ferow = sqlQuery("SELECT e.*, p.fname, p.mname, p.lname FROM form_encounter AS e, patient_data AS p WHERE e.id = ? AND p.pid = e.pid", [$trans_id]);

Check failure on line 273 in interface/billing/sl_eob_invoice.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Ignored error pattern #^Use QueryUtils\:\:querySingleRow\(\) or QueryUtils\:\:fetchRecords\(\) instead of sqlQuery\(\)\.$# in path /home/runner/work/openemr/openemr/interface/billing/sl_eob_invoice.php is expected to occur 7 times, but occurred 9 times.

Check failure on line 273 in interface/billing/sl_eob_invoice.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Ignored error pattern #^Use QueryUtils\:\:querySingleRow\(\) or QueryUtils\:\:fetchRecords\(\) instead of sqlQuery\(\)\.$# in path /home/runner/work/openemr/openemr/interface/billing/sl_eob_invoice.php is expected to occur 7 times, but occurred 9 times.
if (empty($ferow)) {
die("There is no encounter with form_encounter.id = '" . text($trans_id) . "'.");
}
Expand Down Expand Up @@ -359,39 +361,48 @@
$paytotal += $thispay;
}

// Be sure to record adjustment reasons, even for zero adjustments if
// they happen to be comments.
// Be sure to record adjustment reasons, even for zero adjustments if
// they happen to be comments.
// LOCAL: look up human-readable title for the memo without disturbing
// the existing $reason-based branching logic.
$reason_title_row = sqlQuery(
"SELECT title FROM list_options WHERE list_id = 'adjreason' AND activity = 1 AND option_id = ?",
[$reason]
);
$reason_readable = !empty($reason_title_row['title']) ? $reason_title_row['title'] : $reason;

if (
(0.0 + $thisadj) ||
($reason && $reason_type == 5) ||
($reason && ($reason_type > 1 && $reason_type < 6))
) {
// "To copay" and "To ded'ble" need to become a comment in a zero
// adjustment, formatted just like sl_eob_process.php.
if ($reason_type == '2') {
$reason = $_POST['form_insurance'] . " coins: $thisadj";
$reason_for_save = $_POST['form_insurance'] . " coins: $thisadj";
$thisadj = 0;
} elseif ($reason_type == '3') {
$reason = $_POST['form_insurance'] . " dedbl: $thisadj";
$reason_for_save = $_POST['form_insurance'] . " dedbl: $thisadj";
$thisadj = 0;
} elseif ($reason_type == '4') {
$reason = $_POST['form_insurance'] . " ptresp: $thisadj $reason";
$reason_for_save = $_POST['form_insurance'] . " ptresp: $thisadj $reason_readable";
$thisadj = 0;
} elseif ($reason_type == '5') {
$reason = $_POST['form_insurance'] . " note: $thisadj $reason";
$reason_for_save = $_POST['form_insurance'] . " note: $thisadj $reason_readable";
$thisadj = 0;
} else {
// An adjustment reason including "Ins" is assumed to be assigned by
// insurance, and in that case we identify which one by appending
// Ins1, Ins2 or Ins3.
$reason_for_save = $reason_readable;
// "ins" check still operates on the original $reason code, preserving prior behavior
if (str_contains(strtolower((string) $reason), 'ins')) {
$reason .= ' ' . $_POST['form_insurance'];
$reason_for_save .= ' ' . $_POST['form_insurance'];
}
}
SLEOB::arPostAdjustment($patient_id, $encounter_id, $session_id, $thisadj, $code, $payer_type, $reason, $debug, '', $thiscodetype);
SLEOB::arPostAdjustment(
$patient_id, $encounter_id, $session_id,
$thisadj, $code, $payer_type,
$reason_for_save,
$debug, '', $thiscodetype
);
}
}

// Maintain which insurances are marked as finished.

$form_done = 0 + $_POST['form_done'];
Expand Down Expand Up @@ -500,7 +511,7 @@
for ($i = 1; $i <= 3; ++$i) {
$payerid = SLEOB::arGetPayerID($patient_id, $svcdate, $i);
if ($payerid) {
$tmp = sqlQuery("SELECT name FROM insurance_companies WHERE id = ?", [$payerid]);

Check failure on line 514 in interface/billing/sl_eob_invoice.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Use QueryUtils::querySingleRow() or QueryUtils::fetchRecords() instead of sqlQuery().

Check failure on line 514 in interface/billing/sl_eob_invoice.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Use QueryUtils::querySingleRow() or QueryUtils::fetchRecords() instead of sqlQuery().
echo "$i: " . $tmp['name'] . "<br />";
}
}
Expand Down Expand Up @@ -576,7 +587,8 @@
<input name='form_eobs' type='hidden' value='<?php echo attr($arrow['shipvia'] ?? '') ?>'/>
</div>
</div>
<div class="form-group col-lg" id='ins_done'>
<div class="form-group col-lg" id='ins_done_group'>
<input type="hidden" name="ins_done" id="ins_done" value="">
<label class="col-form-label" for=""><?php echo xlt('Done with'); ?>:</label>
<a class="btn btn-save bg-light text-primary"
onclick="document.forms[0].isLastClosed.value='3'; document.forms[0].submit()"><?php echo xlt("Save Level"); ?>
Expand All @@ -587,12 +599,23 @@
// we no longer expect any payments from that company for the claim.
$last_level_closed = 0 + $ferow['last_level_closed'];
foreach ([0 => 'None', 1 => 'Ins1', 2 => 'Ins2', 3 => 'Ins3'] as $key => $value) {
if ($key && !SLEOB::arGetPayerID($patient_id, $svcdate, $key)) {
continue;
$label = $value;
if ($key) {
$payer_id = SLEOB::arGetPayerID($patient_id, $svcdate, $key);
if (!$payer_id) {
continue;
}
$ins = sqlQuery(

Check failure on line 608 in interface/billing/sl_eob_invoice.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Use QueryUtils::querySingleRow() or QueryUtils::fetchRecords() instead of sqlQuery().

Check failure on line 608 in interface/billing/sl_eob_invoice.php

View workflow job for this annotation

GitHub Actions / phpstan (8.4)

Use QueryUtils::querySingleRow() or QueryUtils::fetchRecords() instead of sqlQuery().
"SELECT name FROM insurance_companies WHERE id = ?",
[$payer_id]
);
if (!empty($ins['name'])) {
$label = $ins['name'];
}
}
$checked = ($last_level_closed == $key) ? " checked" : "";
echo "<label class='radio-inline'>";
echo "<input type='radio' name='form_done' value='" . attr($key) . "'$checked />" . text($value);
echo "<input type='radio' name='form_done' value='" . attr($key) . "'$checked />" . text($label);
echo "</label>";
}
?>
Expand Down
24 changes: 21 additions & 3 deletions interface/billing/sl_eob_process.php
Original file line number Diff line number Diff line change
Expand Up @@ -347,6 +347,16 @@ function era_callback(array &$out): void
};

$primary = ($inslabel === 'Ins1');
// LOCAL: KanCare/March Vision as secondary are Medicaid payers of last resort.
// Per 42 CFR 447.15, their adjustments are real write-offs, not
// duplicative notes. The default secondary-adjustment handling treats
// them as "garbage" comments, which causes phantom patient balances.
// Flag these here so the adjustment loop handles them correctly.
$MEDICAID_SECONDARY_PAYER_IDS = ['96385', '52461'];
$is_medicaid_secondary = (
!$primary
&& in_array((string)($out['payer_id'] ?? ''), $MEDICAID_SECONDARY_PAYER_IDS, true)
);
echo getMessageLine(
$bgcolor,
'infdetail',
Expand Down Expand Up @@ -592,7 +602,9 @@ class: $class,
foreach ($svc['adj'] as $adj) {
$description = ($adj['reason_code'] ?? '') . ': ' .
BillingUtilities::CLAIM_ADJUSTMENT_REASON_CODES[$adj['reason_code'] ?? ''];
if ($adj['group_code'] === 'PR' || !$primary) {
// LOCAL: Added !$is_medicaid_secondary guard so Medicaid-secondary
// adjustments fall through to the real write-off branch below.
if (($adj['group_code'] === 'PR' || !$primary) && !$is_medicaid_secondary) {
// Group code PR is Patient Responsibility. Enter these as zero
// adjustments to retain the note without crediting the claim.
if ($primary) {
Expand Down Expand Up @@ -630,26 +642,32 @@ class: $class,
echo getMessageLine($bgcolor, $class, $description . ' ' .
sprintf("%.2f", $adj['amount']));
} elseif (
$svc['paid'] === 0
(float) $svc['paid'] === 0.0
&& !(
$adj['group_code'] === "CO"
&& (
$adj['reason_code'] === '45'
|| $adj['reason_code'] === '59'
|| ($adj['reason_code'] === '97' && $svc['code'] === '92015')
)
)
&& !$is_medicaid_secondary // LOCAL: $0-pay is expected on Medicaid-secondary
) {
$class = 'errdetail';
$error = true;
} elseif (!$error && !$debug) {
// LOCAL: Tag Medicaid-absorbed writeoffs distinctly for A/R audit clarity.
$adj_reason = $is_medicaid_secondary
? ("Medicaid w/o " . $adj['group_code'] . '-' . $adj['reason_code'])
: ("Adjust code " . $adj['reason_code']);
SLEOB::arPostAdjustment(
patient_id: $pid,
encounter_id: $encounter,
session_id: $InsertionId[$out['check_number']],
amount: $adj['amount'],
code: $codekey,
payer_type: substr($inslabel, 3),
reason: "Adjust code " . $adj['reason_code'],
reason: $adj_reason,
debug: $debug,
time: '',
codetype: $codetype,
Expand Down
Loading
Loading