Read about OpenEMR's Response to the COVID-19 Pandemic at

Post-op visits and Co-Pay

How do you handle the issue of post-op visits in the global period when no co-pay should be charged but a line item for co-pay populates in the payment panel.
If we dont collect the copay, since the patient is in the postoperative period, then the patient balance shows that they owe $30 for the visit copay.

Is there a way to have encounters that can be excluded from the copay?


I’m not certain if anyone is actually following this thread any more, but I had a similar problem and was able to solve it fairly easily. It’s a three-step process.

  1. From Administration > Other > Calendar > Categories, create a category named Post-op.

  2. Edit library/ as follows:
    Near the bottom, in the get_patient_balance function definition, edit the first part of the $sqlstatement to the following:

    $sqlstatement = "SELECT, fe.encounter, fe.last_level_billed, " .
    "fe.last_level_closed, fe.stmt_count FROM form_encounter AS fe " .
    "INNER JOIN openemr_postcalendar_categories AS opc ON opc.pc_catid = fe.pc_catid " .
    “WHERE opc.pc_catname != ‘Post-op’ AND pid = ?”;

  3. Edit src/Billing/BillingUtilities.php as follows:
    Near the bottom, in the getCopay function definition, make the following changes:
    Change this:
    $tmp = sqlQuery("SELECT provider, copay FROM insurance_data " .
    "WHERE pid = ? AND type = ‘primary’ " .
    “AND date <= ? ORDER BY date DESC LIMIT 1”, array($patient_id, $encdate));
    To this:
    $tmp = sqlQuery("SELECT id.provider, id.copay, fe.pc_catid,, opc.pc_catname FROM form_encounter fe " .
    "INNER JOIN insurance_data id ON = " .
    "INNER JOIN openemr_postcalendar_categories opc ON opc.pc_catid = fe.pc_catid " .
    "WHERE = ? AND id.type = ‘primary’ AND <= ? " .
    “ORDER BY DESC LIMIT 1;”, array($patient_id, $encdate));

    And a few lines below change this:

         if ($tmp['provider']) { 
     to this:
         if ($tmp['provider'] && $tmp['pc_catname'] != 'Post-op') {

Copays should no longer be applied to any visit of the Post-op category. I suppose it’s up to you whether or not you’ll charge for the visit within the global period. There’s nothing in OpenEMR that prevents you from doing so but it does go against generally accepted CPT rules.

Change step 2 (above) to the following, otherwise the billing section of the Dashboard won’t display correctly (your data will be OK but it won’t display correctly).

Replace the entire get_patient_balance function at the bottom of library/ with the following:

function get_patient_balance($pid, $with_insurance = false, $eid = false)
$balance = 0;
$bindarray = array($pid);
$sqlstatement = "SELECT, fe.encounter, fe.last_level_billed, " .
"fe.last_level_closed, fe.stmt_count, opc.pc_catname FROM form_encounter AS fe " .
“INNER JOIN openemr_postcalendar_categories AS opc ON opc.pc_catid = fe.pc_catid “.
" WHERE pid = ?”;
if ($eid) {
$sqlstatement .= " AND encounter = ?”;
array_push($bindarray, $eid);
$feres = sqlStatement($sqlstatement, $bindarray);
while ($ferow = sqlFetchArray($feres)) {
$encounter = $ferow[‘encounter’];
$dos = substr($ferow[‘date’], 0, 10);
$insarr = getEffectiveInsurances($pid, $dos);
$inscount = count($insarr);
if (!$with_insurance && $ferow[‘last_level_closed’] < $inscount && $ferow[‘stmt_count’] == 0) {
// It’s out to insurance so only the co-pay might be due.
$brow = sqlQuery(
"SELECT SUM(fee) AS amount FROM billing WHERE " .
"pid = ? AND encounter = ? AND " .
“code_type = ‘copay’ AND activity = 1”,
array($pid, $encounter)
$drow = sqlQuery(
"SELECT SUM(pay_amount) AS payments " .
"FROM ar_activity WHERE " .
“pid = ? AND encounter = ? AND payer_type = 0”,
array($pid, $encounter)
if ($ferow[‘pc_catname’] != ‘Post-op’ && $ferow[‘pc_catname’] != ‘Surgery’) {
$copay = !empty($insarr[0][‘copay’]) ? $insarr[0][‘copay’] * 1 : 0;
} else {
$copay = 0;
$amt = !empty($brow[‘amount’]) ? $brow[‘amount’] * 1 : 0;
$pay = !empty($drow[‘payments’]) ? $drow[‘payments’] * 1 : 0;
$ptbal = $copay + $amt - $pay;
if ($ptbal) { // @TODO check if we want to show patient payment credits.
$balance += $ptbal;
} else {
// Including insurance or not out to insurance, everything is due.
$brow = sqlQuery("SELECT SUM(fee) AS amount FROM billing WHERE " .
"pid = ? AND encounter = ? AND " .
“activity = 1”, array($pid, $encounter));
$drow = sqlQuery("SELECT SUM(pay_amount) AS payments, " .
"SUM(adj_amount) AS adjustments FROM ar_activity WHERE " .
“pid = ? AND encounter = ?”, array($pid, $encounter));
$srow = sqlQuery("SELECT SUM(fee) AS amount FROM drug_sales WHERE " .
“pid = ? AND encounter = ?”, array($pid, $encounter));
$balance += $brow[‘amount’] + $srow[‘amount’]
- $drow[‘payments’] - $drow[‘adjustments’];
return sprintf(’%01.2f’, $balance);

Hi @Mouse55 @albanyeye
If one of you would open a feature request issue, i’d consider adding to v6.0 and possibly 5.0.2(5).
Issues · openemr/openemr · GitHub

I’d be happy to do so but don’t know how. Is this as simple as just mentioning it in the Developers section of the forum?

Just click Issues · openemr/openemr · GitHub then click New Issue and follow the template.

Done (I think), although I didn’t set up and clone openemr from GitHub.

I need to take a closer look at the integration and expand on that for other copay exclusions. I’m sure there are others where I’d want to make this more tightly integrated and versatile.
For now just leave with me.

I’m sure there are other exclusions as well. For my part, hospital consultations come to mind. On my system these are all customizations that I’ve done by hand. If this is to be more integrated in the system, however, I would think that there ought to be a way to check off the types of visits for which copays don’t a apply somewhere in the global settings page. It does get complicated, however. My staff tells me that some insurance companies allow copays (and charges, for that matter) on certain types of visits whereas others do not. That being the case it’s always nice to have a way to manually override things.