preparing for 5.0.1 release in several weeks (#1509)
[openemr.git] / interface / patient_file / merge_patients.php
blob921d3e66c9b8474b27b1c638bbe7aa96e8fd64f6
1 <?php
2 /**
3 * This script merges two patient charts into a single patient chart.
4 * It is to correct the error of creating a duplicate patient.
6 * Copyright (C) 2013 Rod Roark <rod@sunsetsystems.com>
8 * LICENSE: This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
19 * @package OpenEMR
20 * @author Rod Roark <rod@sunsetsystems.com>
23 set_time_limit(0);
28 require_once("../globals.php");
29 require_once("$srcdir/acl.inc");
30 require_once("$srcdir/log.inc");
32 use OpenEMR\Core\Header;
34 // Set this to true for production use. If false you will get a "dry run" with no updates.
35 $PRODUCTION = true;
37 if (!acl_check('admin', 'super')) {
38 die(xlt('Not authorized'));
41 <!DOCTYPE html>
42 <html>
44 <head>
45 <title><?php echo xlt('Merge Patients'); ?></title>
46 <?php Header::setupHeader(['jquery-ui']); ?>
48 <script language="JavaScript">
50 var mypcc = '<?php echo $GLOBALS['phone_country_code']; ?>';
52 var el_pt_name;
53 var el_pt_id;
55 // This is for callback by the find-patient popup.
56 function setpatient(pid, lname, fname, dob) {
57 el_pt_name.value = lname + ', ' + fname + ' (' + pid + ')';
58 el_pt_id.value = pid;
61 // This invokes the find-patient popup.
62 function sel_patient(ename, epid) {
63 el_pt_name = ename;
64 el_pt_id = epid;
65 dlgopen('../main/calendar/find_patient_popup.php', '_blank', 600, 400);
68 </script>
70 </head>
72 <body class="body_top">
73 <div class="container">
74 <h2><?php echo xlt('Merge Patients') ?></h2>
76 <?php
78 function deleteRows($tblname, $colname, $source_pid)
80 global $PRODUCTION;
81 $crow = sqlQuery("SELECT COUNT(*) AS count FROM `$tblname` WHERE `$colname` = $source_pid");
82 $count = $crow['count'];
83 if ($count) {
84 $sql = "DELETE FROM `$tblname` WHERE `$colname` = $source_pid";
85 echo "<br />$sql ($count)";
86 if ($PRODUCTION) {
87 sqlStatement($sql);
92 function updateRows($tblname, $colname, $source_pid, $target_pid)
94 global $PRODUCTION;
95 $crow = sqlQuery("SELECT COUNT(*) AS count FROM `$tblname` WHERE `$colname` = $source_pid");
96 $count = $crow['count'];
97 if ($count) {
98 $sql = "UPDATE `$tblname` SET `$colname` = '$target_pid' WHERE `$colname` = $source_pid";
99 echo "<br />$sql ($count)";
100 if ($PRODUCTION) {
101 sqlStatement($sql);
106 if (!empty($_POST['form_submit'])) {
107 $target_pid = intval($_POST['form_target_pid']);
108 $source_pid = intval($_POST['form_source_pid']);
109 echo "<div class='well'>";
110 if ($target_pid == $source_pid) {
111 die(xlt('Target and source pid may not be the same!'));
114 $tprow = sqlQuery("SELECT * FROM patient_data WHERE pid = ?", array($target_pid));
115 $sprow = sqlQuery("SELECT * FROM patient_data WHERE pid = ?", array($source_pid));
117 // Do some checking to make sure source and target exist and are the same person.
118 if (empty($tprow['pid'])) {
119 die(xlt('Target patient not found'));
122 if (empty($sprow['pid'])) {
123 die(xlt('Source patient not found'));
126 if ($tprow['ss'] != $sprow['ss']) {
127 die(xlt('Target and source SSN do not match'));
130 if (empty($tprow['DOB']) || $tprow['DOB'] == '0000-00-00') {
131 die(xlt('Target patient has no DOB'));
134 if (empty($sprow['DOB']) || $sprow['DOB'] == '0000-00-00') {
135 die(xlt('Source patient has no DOB'));
138 if ($tprow['DOB'] != $sprow['DOB']) {
139 die(xlt('Target and source DOB do not match'));
142 $tdocdir = "$OE_SITE_DIR/documents/$target_pid";
143 $sdocdir = "$OE_SITE_DIR/documents/$source_pid";
144 $sencdir = "$sdocdir/encounters";
145 $tencdir = "$tdocdir/encounters";
147 // Change normal documents first as that could fail if CouchDB connection fails.
148 $dres = sqlStatement("SELECT * FROM `documents` WHERE `foreign_id` = '$source_pid'");
149 while ($drow = sqlFetchArray($dres)) {
150 $d = new Document($drow['id']);
151 echo "<br />" . xlt('Changing patient ID for document') . ' ' . text($d->get_url_file());
152 if ($PRODUCTION) {
153 if (!$d->change_patient($target_pid)) {
154 die("<br />" . xlt('Change failed! CouchDB connect error?'));
159 // Move scanned encounter documents and delete their container.
160 if (is_dir($sencdir)) {
161 if ($PRODUCTION && !file_exists($tdocdir)) {
162 mkdir($tdocdir);
165 if ($PRODUCTION && !file_exists($tencdir)) {
166 mkdir($tencdir);
169 $dh = opendir($sencdir);
170 if (!$dh) {
171 die(xlt('Cannot read directory') . " '$sencdir'");
174 while (false !== ($sfname = readdir($dh))) {
175 if ($sfname == '.' || $sfname == '..') {
176 continue;
179 if ($sfname == 'index.html') {
180 echo "<br />" . xlt('Deleting') . " $sencdir/$sfname";
181 if ($PRODUCTION) {
182 if (!unlink("$sencdir/$sfname")) {
183 die("<br />" . xlt('Delete failed!'));
187 continue;
190 echo "<br />" . xlt('Moving') . " $sencdir/$sfname " . xlt('to') . " $tencdir/$sfname";
191 if ($PRODUCTION) {
192 if (!rename("$sencdir/$sfname", "$tencdir/$sfname")) {
193 die("<br />" . xlt('Move failed!'));
198 closedir($dh);
199 echo "<br />" . xlt('Deleting') . " $sencdir";
200 if ($PRODUCTION) {
201 if (!rmdir($sencdir)) {
202 echo "<br />" . xlt('Directory delete failed; continuing.');
207 $tres = sqlStatement("SHOW TABLES");
208 while ($trow = sqlFetchArray($tres)) {
209 $tblname = array_shift($trow);
210 if ($tblname == 'patient_data' || $tblname == 'history_data' || $tblname == 'insurance_data') {
211 deleteRows($tblname, 'pid', $source_pid);
212 } else if ($tblname == 'chart_tracker') {
213 updateRows($tblname, 'ct_pid', $source_pid, $target_pid);
214 } else if ($tblname == 'documents') {
215 // Documents already handled.
216 } else if ($tblname == 'openemr_postcalendar_events') {
217 updateRows($tblname, 'pc_pid', $source_pid, $target_pid);
218 } else if ($tblname == 'log') {
219 // Don't mess with log data.
220 } else {
221 $crow = sqlQuery("SHOW COLUMNS FROM `$tblname` WHERE " .
222 "`Field` LIKE 'pid' OR `Field` LIKE 'patient_id'");
223 if (!empty($crow['Field'])) {
224 $colname = $crow['Field'];
225 updateRows($tblname, $colname, $source_pid, $target_pid);
230 echo "<br />" . xlt('Merge complete.') . "</div>";
232 exit(0);
238 </p>
240 <form method='post' action='merge_patients.php'>
241 <div class="table-responsive">
242 <table style='width:100%'>
243 <tr>
244 <td>
245 <?php echo xlt('Target Patient') ?>
246 </td>
247 <td>
248 <input type='text' size='30' name='form_target_patient'
249 value=' (<?php echo xla('Click to select'); ?>)'
250 onclick='sel_patient(this, this.form.form_target_pid)'
251 title='<?php echo xla('Click to select patient'); ?>' readonly />
252 <input type='hidden' name='form_target_pid' value='0' />
253 </td>
254 <td>
255 <?php echo xlt('This is the main chart that is to receive the merged data.'); ?>
256 </td>
257 </tr>
258 <tr>
259 <td>
260 <?php echo xlt('Source Patient') ?>
261 </td>
262 <td>
263 <input type='text' size='30' name='form_source_patient'
264 value=' (<?php echo xla('Click to select'); ?>)'
265 onclick='sel_patient(this, this.form.form_source_pid)'
266 title='<?php echo xla('Click to select patient'); ?>' readonly />
267 <input type='hidden' name='form_source_pid' value='0' />
268 </td>
269 <td>
270 <?php echo xlt('This is the chart that is to be merged into the main chart and then deleted.'); ?>
271 </td>
272 </tr>
273 </table>
274 <p><input type='submit' name='form_submit' value='<?php echo xla('Merge'); ?>' /></p>
275 </div>
276 </form>
277 <div class="well well-lg">
278 <p><strong><?php echo xlt('This utility is experimental. Back up your database and documents before using it!'); ?></strong></p>
280 <?php if (!$PRODUCTION) { ?>
281 <p><?php echo xlt('This will be a "dry run" with no physical data updates.'); ?></p>
282 <?php } ?>
284 <p><?php echo xlt('This will merge two patient charts into one. It is useful when a patient has been duplicated by mistake. If that happens often, fix your office procedures - do not run this routinely!'); ?></p>
286 <p><?php echo xlt('The first ("target") chart is the one that is considered the most complete and accurate. Demographics, history and insurance sections for this one will be retained.'); ?></p>
288 <p><?php echo xlt('The second ("source") chart will have its demographics, history and insurance sections discarded. Its other data will be merged into the target chart.'); ?></p>
290 <p><?php echo xlt('The merge will not run unless SSN and DOB for the two charts are identical. DOBs cannot be empty.'); ?></p>
291 </div>
292 </div>
293 </body>
294 </html>