Huge Bootstrap 4 Classes Fix (#2807)
[openemr.git] / interface / patient_file / merge_patients.php
blobcc21fcfab0ab7c9b165c872e5a13868b3f70c7dd
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 * @package OpenEMR
7 * @link http://www.open-emr.org
8 * @author Rod Roark <rod@sunsetsystems.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @copyright Copyright (c) 2013 Rod Roark <rod@sunsetsystems.com>
11 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
12 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
16 set_time_limit(0);
18 require_once("../globals.php");
19 require_once("$srcdir/acl.inc");
21 use OpenEMR\Common\Csrf\CsrfUtils;
22 use OpenEMR\Core\Header;
24 // Set this to true for production use. If false you will get a "dry run" with no updates.
25 $PRODUCTION = true;
27 if (!acl_check('admin', 'super')) {
28 die(xlt('Not authorized'));
31 <!DOCTYPE html>
32 <html>
34 <head>
35 <title><?php echo xlt('Merge Patients'); ?></title>
36 <?php Header::setupHeader(['jquery-ui']); ?>
38 <script language="JavaScript">
40 var mypcc = <?php echo js_escape($GLOBALS['phone_country_code']); ?>;
42 var el_pt_name;
43 var el_pt_id;
45 // This is for callback by the find-patient popup.
46 function setpatient(pid, lname, fname, dob) {
47 el_pt_name.value = lname + ', ' + fname + ' (' + pid + ')';
48 el_pt_id.value = pid;
51 // This invokes the find-patient popup.
52 function sel_patient(ename, epid) {
53 el_pt_name = ename;
54 el_pt_id = epid;
55 dlgopen('../main/calendar/find_patient_popup.php', '_blank', 600, 400);
58 </script>
60 </head>
62 <body class="body_top">
63 <div class="container">
64 <h2><?php echo xlt('Merge Patients') ?></h2>
66 <?php
68 function deleteRows($tblname, $colname, $source_pid)
70 global $PRODUCTION;
71 $crow = sqlQuery("SELECT COUNT(*) AS count FROM " . escape_table_name($tblname) . " WHERE " . escape_sql_column_name($colname, array($tblname)) . " = ?", array($source_pid));
72 $count = $crow['count'];
73 if ($count) {
74 $sql = "DELETE FROM " . escape_table_name($tblname) . " WHERE " . escape_sql_column_name($colname, array($tblname)) . " = ?";
75 echo "<br />$sql ($count)";
76 if ($PRODUCTION) {
77 sqlStatement($sql, array($source_pid));
82 function updateRows($tblname, $colname, $source_pid, $target_pid)
84 global $PRODUCTION;
85 $crow = sqlQuery("SELECT COUNT(*) AS count FROM " . escape_table_name($tblname) . " WHERE " . escape_sql_column_name($colname, array($tblname)) . " = ?", array($source_pid));
86 $count = $crow['count'];
87 if ($count) {
88 $sql = "UPDATE " . escape_table_name($tblname) . " SET " . escape_sql_column_name($colname, array($tblname)) . " = ? WHERE " . escape_sql_column_name($colname, array($tblname)) . " = ?";
89 echo "<br />$sql ($count)";
90 if ($PRODUCTION) {
91 sqlStatement($sql, array($target_pid, $source_pid));
96 if (!empty($_POST['form_submit'])) {
97 if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
98 CsrfUtils::csrfNotVerified();
101 $target_pid = intval($_POST['form_target_pid']);
102 $source_pid = intval($_POST['form_source_pid']);
103 echo "<div class='well'>";
104 if ($target_pid == $source_pid) {
105 die(xlt('Target and source pid may not be the same!'));
108 $tprow = sqlQuery("SELECT * FROM patient_data WHERE pid = ?", array($target_pid));
109 $sprow = sqlQuery("SELECT * FROM patient_data WHERE pid = ?", array($source_pid));
111 // Do some checking to make sure source and target exist and are the same person.
112 if (empty($tprow['pid'])) {
113 die(xlt('Target patient not found'));
116 if (empty($sprow['pid'])) {
117 die(xlt('Source patient not found'));
120 if ($tprow['ss'] != $sprow['ss']) {
121 die(xlt('Target and source SSN do not match'));
124 if (empty($tprow['DOB']) || $tprow['DOB'] == '0000-00-00') {
125 die(xlt('Target patient has no DOB'));
128 if (empty($sprow['DOB']) || $sprow['DOB'] == '0000-00-00') {
129 die(xlt('Source patient has no DOB'));
132 if ($tprow['DOB'] != $sprow['DOB']) {
133 die(xlt('Target and source DOB do not match'));
136 $tdocdir = "$OE_SITE_DIR/documents/" . check_file_dir_name($target_pid);
137 $sdocdir = "$OE_SITE_DIR/documents/" . check_file_dir_name($source_pid);
138 $sencdir = "$sdocdir/encounters";
139 $tencdir = "$tdocdir/encounters";
141 // Change normal documents first as that could fail if CouchDB connection fails.
142 $dres = sqlStatement("SELECT * FROM `documents` WHERE `foreign_id` = ?", array($source_pid));
143 while ($drow = sqlFetchArray($dres)) {
144 $d = new Document($drow['id']);
145 echo "<br />" . xlt('Changing patient ID for document') . ' ' . text($d->get_url_file());
146 if ($PRODUCTION) {
147 if (!$d->change_patient($target_pid)) {
148 die("<br />" . xlt('Change failed! CouchDB connect error?'));
153 // Move scanned encounter documents and delete their container.
154 if (is_dir($sencdir)) {
155 if ($PRODUCTION && !file_exists($tdocdir)) {
156 mkdir($tdocdir);
159 if ($PRODUCTION && !file_exists($tencdir)) {
160 mkdir($tencdir);
163 $dh = opendir($sencdir);
164 if (!$dh) {
165 die(xlt('Cannot read directory') . " '" . text($sencdir) . "'");
168 while (false !== ($sfname = readdir($dh))) {
169 if ($sfname == '.' || $sfname == '..') {
170 continue;
173 if ($sfname == 'index.html') {
174 echo "<br />" . xlt('Deleting') . " " . text($sencdir) . "/" . text($sfname);
175 if ($PRODUCTION) {
176 if (!unlink("$sencdir/$sfname")) {
177 die("<br />" . xlt('Delete failed!'));
181 continue;
184 echo "<br />" . xlt('Moving') . " " . text($sencdir) . "/" . text($sfname) . " " . xlt('to{{Destination}}') . " " . text($tencdir) . "/" . text($sfname);
185 if ($PRODUCTION) {
186 if (!rename("$sencdir/$sfname", "$tencdir/$sfname")) {
187 die("<br />" . xlt('Move failed!'));
192 closedir($dh);
193 echo "<br />" . xlt('Deleting') . " $sencdir";
194 if ($PRODUCTION) {
195 if (!rmdir($sencdir)) {
196 echo "<br />" . xlt('Directory delete failed; continuing.');
201 $tres = sqlStatement("SHOW TABLES");
202 while ($trow = sqlFetchArray($tres)) {
203 $tblname = array_shift($trow);
204 if ($tblname == 'patient_data' || $tblname == 'history_data' || $tblname == 'insurance_data') {
205 deleteRows($tblname, 'pid', $source_pid);
206 } else if ($tblname == 'chart_tracker') {
207 updateRows($tblname, 'ct_pid', $source_pid, $target_pid);
208 } else if ($tblname == 'documents') {
209 // Documents already handled.
210 } else if ($tblname == 'openemr_postcalendar_events') {
211 updateRows($tblname, 'pc_pid', $source_pid, $target_pid);
212 } else if ($tblname == 'log') {
213 // Don't mess with log data.
214 } else {
215 $crow = sqlQuery("SHOW COLUMNS FROM `" . escape_table_name($tblname) . "` WHERE " .
216 "`Field` LIKE 'pid' OR `Field` LIKE 'patient_id'");
217 if (!empty($crow['Field'])) {
218 $colname = $crow['Field'];
219 updateRows($tblname, $colname, $source_pid, $target_pid);
224 echo "<br />" . xlt('Merge complete.') . "</div>";
226 exit(0);
232 </p>
234 <form method='post' action='merge_patients.php'>
235 <input type="hidden" name="csrf_token_form" value="<?php echo attr(CsrfUtils::collectCsrfToken()); ?>" />
236 <div class="table-responsive">
237 <table style='width:100%'>
238 <tr>
239 <td>
240 <?php echo xlt('Target Patient') ?>
241 </td>
242 <td>
243 <input type='text' size='30' name='form_target_patient'
244 value=' (<?php echo xla('Click to select'); ?>)'
245 onclick='sel_patient(this, this.form.form_target_pid)'
246 title='<?php echo xla('Click to select patient'); ?>' readonly />
247 <input type='hidden' name='form_target_pid' value='0' />
248 </td>
249 <td>
250 <?php echo xlt('This is the main chart that is to receive the merged data.'); ?>
251 </td>
252 </tr>
253 <tr>
254 <td>
255 <?php echo xlt('Source Patient') ?>
256 </td>
257 <td>
258 <input type='text' size='30' name='form_source_patient'
259 value=' (<?php echo xla('Click to select'); ?>)'
260 onclick='sel_patient(this, this.form.form_source_pid)'
261 title='<?php echo xla('Click to select patient'); ?>' readonly />
262 <input type='hidden' name='form_source_pid' value='0' />
263 </td>
264 <td>
265 <?php echo xlt('This is the chart that is to be merged into the main chart and then deleted.'); ?>
266 </td>
267 </tr>
268 </table>
269 <p><input type='submit' name='form_submit' value='<?php echo xla('Merge'); ?>' /></p>
270 </div>
271 </form>
272 <div class="well well-lg">
273 <p><strong><?php echo xlt('This utility is experimental. Back up your database and documents before using it!'); ?></strong></p>
275 <?php if (!$PRODUCTION) { ?>
276 <p><?php echo xlt('This will be a "dry run" with no physical data updates.'); ?></p>
277 <?php } ?>
279 <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>
281 <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>
283 <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>
285 <p><?php echo xlt('The merge will not run unless SSN and DOB for the two charts are identical. DOBs cannot be empty.'); ?></p>
286 </div>
287 </div>
288 </body>
289 </html>