fix: set default x12 partner for item in billing manager (#7513)
[openemr.git] / interface / patient_file / deleter.php
blob08a1c2084b157924bb4298fac27d97d03c999360
1 <?php
3 /**
4 * delete tool, for logging and removing patient data.
6 * Called from many different pages.
8 * @package OpenEMR
9 * @link http://www.open-emr.org
10 * @author Rod Roark <rod@sunsetsystems.com>
11 * @author Roberto Vasquez <robertogagliotta@gmail.com>
12 * @author Brady Miller <brady.g.miller@gmail.com>
13 * @copyright Copyright (c) 2005-2020 Rod Roark <rod@sunsetsystems.com>
14 * @copyright Copyright (c) 2015 Roberto Vasquez <robertogagliotta@gmail.com>
15 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
16 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
19 require_once('../globals.php');
21 use OpenEMR\Billing\BillingUtilities;
22 use OpenEMR\Common\Acl\AclMain;
23 use OpenEMR\Common\Csrf\CsrfUtils;
24 use OpenEMR\Common\Logging\EventAuditLogger;
25 use OpenEMR\Core\Header;
27 if (!empty($_GET)) {
28 if (!CsrfUtils::verifyCsrfToken($_GET["csrf_token_form"])) {
29 CsrfUtils::csrfNotVerified();
33 $patient = $_REQUEST['patient'] ?? '';
34 $encounterid = $_REQUEST['encounterid'] ?? '';
35 $formid = $_REQUEST['formid'] ?? '';
36 $issue = $_REQUEST['issue'] ?? '';
37 $document = $_REQUEST['document'] ?? '';
38 $payment = $_REQUEST['payment'] ?? '';
39 $billing = $_REQUEST['billing'] ?? '';
40 $transaction = $_REQUEST['transaction'] ?? '';
42 $info_msg = "";
44 // Delete rows, with logging, for the specified table using the
45 // specified WHERE clause.
47 function row_delete($table, $where)
49 $tres = sqlStatement("SELECT * FROM " . escape_table_name($table) . " WHERE $where");
50 $count = 0;
51 while ($trow = sqlFetchArray($tres)) {
52 $logstring = "";
53 foreach ($trow as $key => $value) {
54 if (! $value || $value == '0000-00-00 00:00:00') {
55 continue;
58 if ($logstring) {
59 $logstring .= " ";
62 $logstring .= $key . "= '" . $value . "' ";
65 EventAuditLogger::instance()->newEvent("delete", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "$table: $logstring");
66 ++$count;
69 if ($count) {
70 $query = "DELETE FROM " . escape_table_name($table) . " WHERE $where";
71 if (!$GLOBALS['sql_string_no_show_screen']) {
72 echo text($query) . "<br />\n";
75 sqlStatement($query);
79 // Deactivate rows, with logging, for the specified table using the
80 // specified SET and WHERE clauses.
82 function row_modify($table, $set, $where)
84 if (sqlQuery("SELECT * FROM " . escape_table_name($table) . " WHERE $where")) {
85 EventAuditLogger::instance()->newEvent("deactivate", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "$table: $where");
86 $query = "UPDATE " . escape_table_name($table) . " SET $set WHERE $where";
87 if (!$GLOBALS['sql_string_no_show_screen']) {
88 echo text($query) . "<br />\n";
91 sqlStatement($query);
95 // We use this to put dashes, colons, etc. back into a timestamp.
97 function decorateString($fmt, $str)
99 $res = '';
100 while ($fmt) {
101 $fc = substr($fmt, 0, 1);
102 $fmt = substr($fmt, 1);
103 if ($fc == '.') {
104 $res .= substr($str, 0, 1);
105 $str = substr($str, 1);
106 } else {
107 $res .= $fc;
111 return $res;
114 // Delete and undo product sales for a given patient or visit.
115 // This is special because it has to replace the inventory.
117 function delete_drug_sales($patient_id, $encounter_id = 0)
119 $where = $encounter_id ? "ds.encounter = '" . add_escape_custom($encounter_id) . "'" :
120 "ds.pid = '" . add_escape_custom($patient_id) . "' AND ds.encounter != 0";
121 sqlStatement("UPDATE drug_sales AS ds, drug_inventory AS di " .
122 "SET di.on_hand = di.on_hand + ds.quantity " .
123 "WHERE $where AND di.inventory_id = ds.inventory_id");
124 if ($encounter_id) {
125 row_delete("drug_sales", "encounter = '" . add_escape_custom($encounter_id) . "'");
126 } else {
127 row_delete("drug_sales", "pid = '" . add_escape_custom($patient_id) . "'");
131 // Delete a form's data that is specific to that form.
133 function form_delete($formdir, $formid, $patient_id, $encounter_id)
135 $formdir = ($formdir == 'newpatient') ? 'encounter' : $formdir;
136 $formdir = ($formdir == 'newGroupEncounter') ? 'groups_encounter' : $formdir;
137 if (substr($formdir, 0, 3) == 'LBF') {
138 row_delete("lbf_data", "form_id = '" . add_escape_custom($formid) . "'");
139 // Delete the visit's "source=visit" attributes that are not used by any other form.
140 $where = "pid = '" . add_escape_custom($patient_id) . "' AND encounter = '" .
141 add_escape_custom($encounter_id) . "' AND field_id NOT IN (" .
142 "SELECT lo.field_id FROM forms AS f, layout_options AS lo WHERE " .
143 "f.pid = '" . add_escape_custom($patient_id) . "' AND f.encounter = '" .
144 add_escape_custom($encounter_id) . "' AND f.formdir LIKE 'LBF%' AND " .
145 "f.deleted = 0 AND f.form_id != '" . add_escape_custom($formid) . "' AND " .
146 "lo.form_id = f.formdir AND lo.source = 'E' AND lo.uor > 0)";
147 // echo "<!-- $where -->\n"; // debugging
148 row_delete("shared_attributes", $where);
149 } elseif ($formdir == 'procedure_order') {
150 $tres = sqlStatement("SELECT procedure_report_id FROM procedure_report " .
151 "WHERE procedure_order_id = ?", array($formid));
152 while ($trow = sqlFetchArray($tres)) {
153 $reportid = (int)$trow['procedure_report_id'];
154 row_delete("procedure_result", "procedure_report_id = '" . add_escape_custom($reportid) . "'");
157 row_delete("procedure_report", "procedure_order_id = '" . add_escape_custom($formid) . "'");
158 row_delete("procedure_order_code", "procedure_order_id = '" . add_escape_custom($formid) . "'");
159 row_delete("procedure_order", "procedure_order_id = '" . add_escape_custom($formid) . "'");
160 } elseif ($formdir == 'physical_exam') {
161 row_delete("form_$formdir", "forms_id = '" . add_escape_custom($formid) . "'");
162 } elseif ($formdir == 'eye_mag') {
163 $tables = array('form_eye_base','form_eye_hpi','form_eye_ros','form_eye_vitals',
164 'form_eye_acuity','form_eye_refraction','form_eye_biometrics',
165 'form_eye_external', 'form_eye_antseg','form_eye_postseg',
166 'form_eye_neuro','form_eye_locking','form_eye_mag_orders');
167 foreach ($tables as $table_name) {
168 row_delete($table_name, "id = '" . add_escape_custom($formid) . "'");
170 row_delete("form_eye_mag_impplan", "form_id = '" . add_escape_custom($formid) . "'");
171 row_delete("form_eye_mag_wearing", "FORM_ID = '" . add_escape_custom($formid) . "'");
172 } else {
173 row_delete("form_$formdir", "id = '" . add_escape_custom($formid) . "'");
177 // Delete a specified document including its associated relations.
178 // Note the specific file is not deleted (instead flagged as deleted), since required to keep file for
179 // ONC 2015 certification purposes.
181 function delete_document($document)
183 sqlStatement("UPDATE `documents` SET `deleted` = 1 WHERE id = ?", [$document]);
184 row_delete("categories_to_documents", "document_id = '" . add_escape_custom($document) . "'");
185 row_delete("gprelations", "type1 = 1 AND id1 = '" . add_escape_custom($document) . "'");
188 <html>
189 <head>
190 <?php Header::setupHeader('opener'); ?>
191 <title><?php echo xlt('Delete Patient, Encounter, Form, Issue, Document, Payment, Billing or Transaction'); ?></title>
193 <script>
194 function submit_form() {
195 top.restoreSession();
196 document.deletefrm.submit();
199 // Javascript function for closing the popup
200 function popup_close() {
201 dlgclose();
203 </script>
204 </head>
206 <body>
207 <div class="container mt-3">
208 <?php
209 // If the delete is confirmed...
211 if (!empty($_POST['form_submit'])) {
212 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
213 CsrfUtils::csrfNotVerified();
216 if ($patient) {
217 if (!AclMain::aclCheckCore('admin', 'super') || !$GLOBALS['allow_pat_delete']) {
218 die(xlt("Not authorized!"));
221 row_modify("billing", "activity = 0", "pid = '" . add_escape_custom($patient) . "'");
222 row_modify("pnotes", "deleted = 1", "pid = '" . add_escape_custom($patient) . "'");
223 row_delete("prescriptions", "patient_id = '" . add_escape_custom($patient) . "'");
224 row_delete("claims", "patient_id = '" . add_escape_custom($patient) . "'");
225 delete_drug_sales($patient);
226 row_delete("payments", "pid = '" . add_escape_custom($patient) . "'");
227 row_modify("ar_activity", "deleted = NOW()", "pid = '" . add_escape_custom($patient) . "' AND deleted IS NULL");
228 row_delete("openemr_postcalendar_events", "pc_pid = '" . add_escape_custom($patient) . "'");
229 row_delete("immunizations", "patient_id = '" . add_escape_custom($patient) . "'");
230 row_delete("issue_encounter", "pid = '" . add_escape_custom($patient) . "'");
231 row_delete("lists", "pid = '" . add_escape_custom($patient) . "'");
232 row_delete("transactions", "pid = '" . add_escape_custom($patient) . "'");
233 row_delete("employer_data", "pid = '" . add_escape_custom($patient) . "'");
234 row_delete("history_data", "pid = '" . add_escape_custom($patient) . "'");
235 row_delete("insurance_data", "pid = '" . add_escape_custom($patient) . "'");
236 row_delete("patient_history", "pid = '" . add_escape_custom($patient) . "'");
238 $res = sqlStatement("SELECT * FROM forms WHERE pid = ?", array($patient));
239 while ($row = sqlFetchArray($res)) {
240 row_modify(
241 "forms",
242 "deleted = 1",
243 "pid = '" . add_escape_custom($row['pid']) .
244 "' AND form_id = '" . add_escape_custom($row['form_id']) . "'"
248 // Delete all documents for the patient.
249 $res = sqlStatement("SELECT id FROM documents WHERE foreign_id = ? AND deleted = 0", array($patient));
250 while ($row = sqlFetchArray($res)) {
251 delete_document($row['id']);
254 row_delete("patient_data", "pid = '" . add_escape_custom($patient) . "'");
255 } elseif ($encounterid) {
256 if (!AclMain::aclCheckCore('admin', 'super')) {
257 die("Not authorized!");
260 row_modify("billing", "activity = 0", "encounter = '" . add_escape_custom($encounterid) . "'");
261 delete_drug_sales(0, $encounterid);
262 row_modify("ar_activity", "deleted = NOW()", "encounter = '" . add_escape_custom($encounterid) . "' AND deleted IS NULL");
263 row_delete("claims", "encounter_id = '" . add_escape_custom($encounterid) . "'");
264 row_delete("issue_encounter", "encounter = '" . add_escape_custom($encounterid) . "'");
265 $res = sqlStatement("SELECT * FROM forms WHERE encounter = ?", array($encounterid));
266 while ($row = sqlFetchArray($res)) {
267 form_delete($row['formdir'], $row['form_id'], $row['pid'], $row['encounter']);
270 row_delete("forms", "encounter = '" . add_escape_custom($encounterid) . "'");
271 } elseif ($formid) {
272 if (!AclMain::aclCheckCore('admin', 'super')) {
273 die("Not authorized!");
276 $row = sqlQuery("SELECT * FROM forms WHERE id = ?", array($formid));
277 $formdir = $row['formdir'];
278 if (! $formdir) {
279 die("There is no form with id '" . text($formid) . "'");
281 form_delete($formdir, $row['form_id'], $row['pid'], $row['encounter']);
282 row_delete("forms", "id = '" . add_escape_custom($formid) . "'");
283 } elseif ($issue) {
284 if (!AclMain::aclCheckCore('admin', 'super')) {
285 die("Not authorized!");
288 $ids = explode(",", $issue);
289 foreach ($ids as $id) {
290 row_delete("issue_encounter", "list_id = '" . add_escape_custom($id) . "'");
291 row_delete("lists_medication", "list_id = '" . add_escape_custom($id) . "'");
292 row_delete("lists", "id = '" . add_escape_custom($id) . "'");
294 } elseif ($document) {
295 if (!AclMain::aclCheckCore('patients', 'docs_rm')) {
296 die("Not authorized!");
299 delete_document($document);
300 } elseif ($payment) {
301 if (!AclMain::aclCheckCore('admin', 'super')) {
302 // allow biller to delete misapplied payments
303 if (!AclMain::aclCheckCore('acct', 'bill')) {
304 die("Not authorized!");
308 list($patient_id, $timestamp, $ref_id) = explode(".", $payment);
309 // if (empty($ref_id)) $ref_id = -1;
310 $timestamp = decorateString('....-..-.. ..:..:..', $timestamp);
311 $payres = sqlStatement("SELECT * FROM payments WHERE " .
312 "pid = ? AND dtime = ?", array($patient_id, $timestamp));
313 while ($payrow = sqlFetchArray($payres)) {
314 if ($payrow['encounter']) {
315 $ref_id = -1;
316 // The session ID passed in is useless. Look for the most recent
317 // patient payment session with pay total matching pay amount and with
318 // no adjustments. The resulting session ID may be 0 (no session) which
319 // is why we start with -1.
320 $tpmt = $payrow['amount1'] + $payrow['amount2'];
321 $seres = sqlStatement("SELECT " .
322 "SUM(pay_amount) AS pay_amount, session_id " .
323 "FROM ar_activity WHERE " .
324 "pid = ? AND " .
325 "encounter = ? AND " .
326 "deleted IS NULL AND " .
327 "payer_type = 0 AND " .
328 "adj_amount = 0.00 " .
329 "GROUP BY session_id ORDER BY session_id DESC", array($patient_id, $payrow['encounter']));
330 while ($serow = sqlFetchArray($seres)) {
331 if (sprintf("%01.2f", $serow['adj_amount']) != 0.00) {
332 continue;
335 if (sprintf("%01.2f", $serow['pay_amount'] - $tpmt) == 0.00) {
336 $ref_id = $serow['session_id'];
337 break;
341 if ($ref_id == -1) {
342 die(xlt('Unable to match this payment in ar_activity') . ": " . text($tpmt));
345 // Delete the payment.
346 row_modify(
347 "ar_activity",
348 "deleted = NOW()",
349 "pid = '" . add_escape_custom($patient_id) . "' AND " .
350 "encounter = '" . add_escape_custom($payrow['encounter']) . "' AND " .
351 "deleted IS NULL AND " .
352 "payer_type = 0 AND " .
353 "pay_amount != 0.00 AND " .
354 "adj_amount = 0.00 AND " .
355 "session_id = '" . add_escape_custom($ref_id) . "'"
357 if ($ref_id) {
358 row_delete(
359 "ar_session",
360 "patient_id = '" . add_escape_custom($patient_id) . "' AND " .
361 "session_id = '" . add_escape_custom($ref_id) . "'"
364 } else {
365 // Encounter is 0! Seems this happens for pre-payments.
366 $tpmt = sprintf("%01.2f", $payrow['amount1'] + $payrow['amount2']);
367 // Patched out 09/06/17- If this is prepayment can't see need for ar_activity when prepayments not stored there? In this case passed in session id is valid.
368 // Was causing delete of wrong prepayment session in the case of delete from checkout undo and/or front receipt delete if payment happens to be same
369 // amount of a previous prepayment. Much tested but look here if problems in postings.
371 /* row_delete("ar_session",
372 "patient_id = ' " . add_escape_custom($patient_id) . " ' AND " .
373 "payer_id = 0 AND " .
374 "reference = '" . add_escape_custom($payrow['source']) . "' AND " .
375 "pay_total = '" . add_escape_custom($tpmt) . "' AND " .
376 "(SELECT COUNT(*) FROM ar_activity where ar_activity.session_id = ar_session.session_id) = 0 " .
377 "ORDER BY session_id DESC LIMIT 1"); */
379 row_delete("ar_session", "session_id = '" . add_escape_custom($ref_id) . "'");
382 row_delete("payments", "id = '" . add_escape_custom($payrow['id']) . "'");
384 } elseif ($billing) {
385 if (!AclMain::aclCheckCore('acct', 'disc')) {
386 die("Not authorized!");
389 list($patient_id, $encounter_id) = explode(".", $billing);
391 row_modify(
392 "ar_activity",
393 "deleted = NOW()",
394 "pid = '" . add_escape_custom($patient_id) . "' AND encounter = '" .
395 add_escape_custom($encounter_id) . "' AND deleted IS NULL"
398 // Looks like this deletes all ar_session rows that have no matching ar_activity rows.
399 sqlStatement(
400 "DELETE ar_session FROM ar_session LEFT JOIN " .
401 "ar_activity ON ar_session.session_id = ar_activity.session_id AND ar_activity.deleted IS NULL " .
402 "WHERE ar_activity.session_id IS NULL"
405 row_modify(
406 "billing",
407 "activity = 0",
408 "pid = '" . add_escape_custom($patient_id) . "' AND " .
409 "encounter = '" . add_escape_custom($encounter_id) . "' AND " .
410 "code_type = 'COPAY' AND " .
411 "activity = 1"
413 sqlStatement("UPDATE form_encounter SET last_level_billed = 0, " .
414 "last_level_closed = 0, stmt_count = 0, last_stmt_date = NULL " .
415 "WHERE pid = ? AND encounter = ?", array($patient_id, $encounter_id));
416 sqlStatement("UPDATE drug_sales SET billed = 0 WHERE " .
417 "pid = ? AND encounter = ?", array($patient_id, $encounter_id));
418 BillingUtilities::updateClaim(true, $patient_id, $encounter_id, -1, -1, 1, 0, ''); // clears for rebilling
419 } elseif ($transaction) {
420 if (!AclMain::aclCheckCore('admin', 'super')) {
421 die("Not authorized!");
424 row_delete("transactions", "id = '" . add_escape_custom($transaction) . "'");
425 } else {
426 die("Nothing was recognized to delete!");
429 if (! $info_msg) {
430 $info_msg = xl('Delete successful.');
433 // Close this window and tell our opener that it's done.
434 // Not sure yet if the callback can be used universally.
435 echo "<script>\n";
436 if (!$encounterid) {
437 if ($info_msg) {
438 echo "let message = " . js_escape($info_msg) . ";
439 (async (message, time) => {
440 await asyncAlertMsg(message, time, 'success', 'lg');
441 })(message, 2000)
442 .then(res => {";
443 // auto close on msg timeout with just enough time to show success or errors.
444 if ($GLOBALS['sql_string_no_show_screen']) {
445 echo "dlgclose();";
447 echo "});"; // close function.
448 // any close will call below.
449 echo " opener.dlgSetCallBack('imdeleted', false);\n";
450 } else {
451 echo " dlgclose('imdeleted', false);\n";
453 } else {
454 if ($GLOBALS['sql_string_no_show_screen']) {
455 echo " dlgclose('imdeleted', " . js_escape($encounterid) . ");\n";
456 } else { // this allows dialog to stay open then close with button or X.
457 echo " opener.dlgSetCallBack('imdeleted', " . js_escape($encounterid) . ");\n";
460 echo "</script></body></html>\n";
461 exit();
465 <form method='post' name="deletefrm" action='deleter.php?patient=<?php echo attr_url($patient) ?>&encounterid=<?php echo attr_url($encounterid) ?>&formid=<?php echo attr_url($formid) ?>&issue=<?php echo attr_url($issue) ?>&document=<?php echo attr_url($document) ?>&payment=<?php echo attr_url($payment) ?>&billing=<?php echo attr_url($billing) ?>&transaction=<?php echo attr_url($transaction); ?>&csrf_token_form=<?php echo attr_url(CsrfUtils::collectCsrfToken()); ?>'>
466 <input type="hidden" name="csrf_token_form"
467 value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
469 <?php
470 $type = '';
471 $id = '';
472 if ($patient) {
473 $id = $patient;
474 $type = 'patient';
475 } elseif ($encounterid) {
476 $id = $encounterid;
477 $type = 'encounter';
478 } elseif ($formid) {
479 $id = $formid;
480 $type = 'form';
481 } elseif ($issue) {
482 $id = $issue;
483 $type = ('issue');
484 } elseif ($document) {
485 $id = $document;
486 $type = 'document';
487 } elseif ($payment) {
488 $id = $payment;
489 $type = 'payment';
490 } elseif ($billing) {
491 $id = $billing;
492 $type = 'invoice';
493 } elseif ($transaction) {
494 $id = $transaction;
495 $type = 'transaction';
498 $ids = explode(",", $id);
499 if (count($ids) > 1) {
500 $type .= 's';
503 $msg = xl("You have selected to delete") . ' ' . count($ids) . ' ' . xl($type) . ". " . xl("Are you sure you want to continue?");
504 echo text($msg);
506 </p>
507 <div class="btn-group">
508 <button onclick="submit_form()" class="btn btn-sm btn-primary mr-2"><?php echo xlt('Yes'); ?></button>
509 <button type="button" class="btn btn-sm btn-secondary" onclick="popup_close();"><?php echo xlt('No');?></button>
510 </div>
511 <input type='hidden' name='form_submit' value='delete'/>
512 </form>
513 </div>
514 </body>
515 </html>