Skip to content

Commit 1bd7ee3

Browse files
authoredJun 18, 2022
Multiple Patient Address Initial Support (openemr#5396)
* Multiple Patient Address Initial Support Fixes openemr#5396 Fixes openemr#5502 Fixes openemr#5501 Added a new data type for LBF to support Address List data type. Addresses can be added with multiple addresses supported. Initial framework for saving addresses is here as well s retrieving them. Rolled out the initial contact and contact address classes that can be used to support Patients. Decided to leverage the ORM classes for Contact and ContactAddress to quickly persist and model the data. * Implement Multiple Address Save. Implemented the multiple address save for contact and contact address. Removed the duplicate code in ORDataObject to populate the object and instead call the populateArray object. This make sit possible to overload the object and define custom behavior there. Moved the Address, Contact, and ContactAddress class into our src folder so we can follow normal PSR4 namespacing. Fixed the primary key problem with the contact table in the database. * PSR style fixes * Add address type and use to address widget * Address widget date selectors,county,reuse lbf Made it so the address widget re-uses the lbf widget display code for each of the address element codes. Refactored the tables into divs to be mobile friendly. Added in the date start and end dates so we can support previous addresses. Added the address use and address type selectors so people can identify what kind of address it is. Exposed the option js functions into the global window under the oeUI container so we can have our add button on the state, country, use, and type selector widgets. * Fixes openemr#5367 previous address support Implemented the saving of the address list lbf template in the new patient and the existing patient demographics page. Added a new lbf field called additional_addresses to be displayed in the contacts group layout. Added the district field to support counties / districts in addresses. Added the use and type FHIR data fields. Also added a period start and period end to the addresses so we can track the time period an address was in use. New addresses default to a current date of today with an empty end date. Added new list options for address-uses and address-types to support the new FHIR options. The option_id is the FHIR key name. Migrated some of the date formatting into the DateFormatterUtils object so we can leverage them inside our objects without having to include the formatter.inc.php file. Added the address_list datatype to the restricted lbf fields so users can't remove the datatype since we have to have it for USCDI support. Refactored all of the styles of the address list widget to be responsive and mobile friendly using bootstrap classes and styles. Made the js functions of addresses consistent in naming conventions with our other js functions. Implemented the archiving / deactivating of the address list items. Had to switch from the BINARY(1) data type to a char value. The ORDataObject has a bug in that you can't save a value with a numeric 0 as its treated as empty. I didn't want to try and change the logic and mess up the other classes that extend ORDataObject, so in the interest of time I switched isPrimary and status to be char fields that we key off of. Renamed the contact table fields to be consistent with fire naming conventions per @mdsupport's recommendation. Changed up the js events to be added in javacsript and not using inline onclick events. This simplifies the debugging immensely. * Removed unused comments, added copyright notices * Remove NULL from text columns * Remove text display constraint,address list title * Removed unused code, fixed event bug, label fixes Had the close button not working when it was a new address, was only working on existing address so had to fix that. Also made the labels the same on the display as on the edit. Removed unused code in the formatting.inc.php * Fixes openemr#5502,openemr#5501 FHIR Addresses, Immunization Fixes openemr#5501 Immunization reason code column mispelling. Fixes openemr#5502 for multiple previous address support in FHIR. Changed contact_address notes datatype to tinytext for review. Added indexes to patient_history and contact_address to speed up patient service search. Added to api patient service the ability to include the patient addresses. Had to refactor the queries as we were excluding any previous names and previous addresses that didn't match the search criteria. Added to the /api/patient endpoint an address array of the patient data. This includes both the main patient address and their additional address information. Eventually we can drop the inline address information in the standard api. Made the patient address search work on both the main address and additional address information the patient has. Made the DateFormatterUtils::dateStringToDateTime have support for times.
1 parent 1b1d3d1 commit 1bd7ee3

31 files changed

+2307
-146
lines changed
 

‎interface/new/new_comprehensive_save.php

+20-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
require_once("../globals.php");
1616

1717
use OpenEMR\Common\Csrf\CsrfUtils;
18+
use OpenEMR\Services\ContactService;
1819

1920
if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
2021
CsrfUtils::csrfNotVerified();
@@ -45,6 +46,7 @@
4546
$fres = sqlStatement("SELECT * FROM layout_options " .
4647
"WHERE form_id = 'DEM' AND (uor > 0 OR field_id = 'pubpid') AND field_id != '' " .
4748
"ORDER BY group_id, seq");
49+
$addressFieldsToSave = array();
4850
while ($frow = sqlFetchArray($fres)) {
4951
$data_type = $frow['data_type'];
5052
$field_id = $frow['field_id'];
@@ -57,7 +59,10 @@
5759
}
5860

5961
//get value only if field exist in $_POST (prevent deleting of field with disabled attribute)
60-
if (isset($_POST["form_$field_id"]) || $field_id == "pubpid") {
62+
// TODO: why is this a different conditional than demographics_save.php...
63+
if ($data_type == 54) { // address list
64+
$addressFieldsToSave[$field_id] = get_layout_form_value($frow);
65+
} else if (isset($_POST["form_$field_id"]) || $field_id == "pubpid") {
6166
$value = get_layout_form_value($frow);
6267
$newdata[$tblname][$colname] = $value;
6368
}
@@ -70,7 +75,20 @@
7075
die("Internal error: setpid(" . text($pid) . ") failed!");
7176
}
7277
setpid($pid);
73-
updateEmployerData($pid, $newdata['employer_data'], true);
78+
if (!$GLOBALS['omit_employers']) {
79+
updateEmployerData($pid, $newdata['employer_data'], true);
80+
}
81+
82+
if (!empty($addressFieldsToSave)) {
83+
// TODO: we would handle other types of address fields here, for now we will just go through and populate the patient
84+
// address information
85+
// TODO: how are error messages supposed to display if the save fails?
86+
foreach ($addressFieldsToSave as $field => $addressFieldData) {
87+
// if we need to save other kinds of addresses we could do that here with our field column...
88+
$contactService = new ContactService();
89+
$contactService->saveContactsForPatient($pid, $addressFieldData);
90+
}
91+
}
7492

7593
$i1dob = DateToYYYYMMDD(filter_input(INPUT_POST, "i1subscriber_DOB"));
7694
$i1date = DateToYYYYMMDD(filter_input(INPUT_POST, "i1effective_date"));

‎interface/patient_file/summary/demographics_save.php

+19-1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
use OpenEMR\Common\Acl\AclMain;
1818
use OpenEMR\Common\Csrf\CsrfUtils;
19+
use OpenEMR\Services\ContactService;
1920

2021
if (!CsrfUtils::verifyCsrfToken($_POST["csrf_token_form"])) {
2122
CsrfUtils::csrfNotVerified();
@@ -50,6 +51,8 @@
5051
$fres = sqlStatement("SELECT * FROM layout_options " .
5152
"WHERE form_id = 'DEM' AND uor > 0 AND field_id != '' " .
5253
"ORDER BY group_id, seq");
54+
55+
$addressFieldsToSave = array();
5356
while ($frow = sqlFetchArray($fres)) {
5457
$data_type = $frow['data_type'];
5558
if ((int)$data_type === 52) {
@@ -67,16 +70,31 @@
6770
// Get value only if field exist in $_POST (prevent deleting of field with disabled attribute)
6871
// *unless* the data_type is a checkbox ("21"), because if the checkbox is unchecked, then it will not
6972
// have a value set on the form, it will be empty.
70-
if (isset($_POST["form_$field_id"]) || $data_type == 21) {
73+
if ($data_type == 54) { // address list
74+
$addressFieldsToSave[$field_id] = get_layout_form_value($frow);
75+
} else if (isset($_POST["form_$field_id"]) || $data_type == 21) {
7176
$newdata[$table][$colname] = get_layout_form_value($frow);
7277
}
7378
}
7479

80+
// TODO: All of this should be bundled up inside a transaction...
81+
7582
updatePatientData($pid, $newdata['patient_data']);
7683
if (!$GLOBALS['omit_employers']) {
7784
updateEmployerData($pid, $newdata['employer_data']);
7885
}
7986

87+
if (!empty($addressFieldsToSave)) {
88+
// TODO: we would handle other types of address fields here, for now we will just go through and populate the patient
89+
// address information
90+
// TODO: how are error messages supposed to display if the save fails?
91+
foreach ($addressFieldsToSave as $field => $addressFieldData) {
92+
// if we need to save other kinds of addresses we could do that here with our field column...
93+
$contactService = new ContactService();
94+
$contactService->saveContactsForPatient($pid, $addressFieldData);
95+
}
96+
}
97+
8098
$i1dob = DateToYYYYMMDD(filter_input(INPUT_POST, "i1subscriber_DOB"));
8199
$i1date = DateToYYYYMMDD(filter_input(INPUT_POST, "i1effective_date"));
82100

‎interface/super/edit_layout.php

+2-1
Original file line numberDiff line numberDiff line change
@@ -254,7 +254,8 @@ function isColumnReserved($tablename, $field_id)
254254
'care_team_facility',
255255
'name_history',
256256
'care_team_status',
257-
'patient_groups'
257+
'patient_groups',
258+
'additional_addresses'
258259
))
259260
) {
260261
return true;

‎library/classes/Company.class.php

+1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
**************************************************************************/
1111

1212
use OpenEMR\Common\ORDataObject\ORDataObject;
13+
use OpenEMR\Common\ORDataObject\Address;
1314

1415
/**
1516
* class Address

‎library/classes/InsuranceCompany.class.php

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
use OpenEMR\Common\ORDataObject\ORDataObject;
1616
use OpenEMR\Services\InsuranceCompanyService;
17+
use OpenEMR\Common\ORDataObject\Address;
1718

1819
/**
1920
* class Insurance Company

‎library/classes/Pharmacy.class.php

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121

2222
use OpenEMR\Common\ORDataObject\ORDataObject;
23+
use OpenEMR\Common\ORDataObject\Address;
2324

2425
class Pharmacy extends ORDataObject
2526
{

‎library/formatting.inc.php

+6-49
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,10 @@
1212
* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
1313
*/
1414

15+
use OpenEMR\Services\Utils\DateFormatterUtils;
16+
17+
// TODO: look at moving all of the date functions into the DateFormatterUtils class.
18+
1519
function oeFormatMoney($amount, $symbol = false)
1620
{
1721
$s = number_format(
@@ -30,36 +34,7 @@ function oeFormatMoney($amount, $symbol = false)
3034

3135
function oeFormatShortDate($date = 'today', $showYear = true)
3236
{
33-
if ($date === 'today') {
34-
$date = date('Y-m-d');
35-
}
36-
37-
if (strlen($date ?? '') >= 10) {
38-
// assume input is yyyy-mm-dd
39-
if ($GLOBALS['date_display_format'] == 1) { // mm/dd/yyyy, note year is added below
40-
$newDate = substr($date, 5, 2) . '/' . substr($date, 8, 2);
41-
} elseif ($GLOBALS['date_display_format'] == 2) { // dd/mm/yyyy, note year is added below
42-
$newDate = substr($date, 8, 2) . '/' . substr($date, 5, 2);
43-
}
44-
45-
// process the year (add for formats 1 and 2; remove for format 0)
46-
if ($GLOBALS['date_display_format'] == 1 || $GLOBALS['date_display_format'] == 2) {
47-
if ($showYear) {
48-
$newDate .= '/' . substr($date, 0, 4);
49-
}
50-
} elseif (!$showYear) { // $GLOBALS['date_display_format'] == 0
51-
// need to remove the year
52-
$newDate = substr($date, 5, 2) . '-' . substr($date, 8, 2);
53-
} else { // $GLOBALS['date_display_format'] == 0
54-
// keep the year (so will simply be the original $date)
55-
$newDate = substr($date, 0, 10);
56-
}
57-
58-
return $newDate;
59-
}
60-
61-
// this is case if the $date does not have 10 characters
62-
return $date;
37+
return DateFormatterUtils::oeFormatShortDate($date, $showYear);
6338
}
6439

6540
// 0 - Time format 24 hr
@@ -198,25 +173,7 @@ function DateFormatRead($mode = 'legacy')
198173

199174
function DateToYYYYMMDD($DateValue)
200175
{
201-
//With the help of function DateFormatRead() now the user can enter date is any of the 3 formats depending upon the global setting.
202-
//But in database the date can be stored only in the yyyy-mm-dd format.
203-
//This function accepts a date in any of the 3 formats, and as per the global setting, converts it to the yyyy-mm-dd format.
204-
if (trim($DateValue ?? '') == '') {
205-
return '';
206-
}
207-
208-
if ($GLOBALS['date_display_format'] == 0) {
209-
return $DateValue;
210-
} elseif ($GLOBALS['date_display_format'] == 1 || $GLOBALS['date_display_format'] == 2) {
211-
$DateValueArray = explode('/', $DateValue);
212-
if ($GLOBALS['date_display_format'] == 1) {
213-
return $DateValueArray[2] . '-' . $DateValueArray[0] . '-' . $DateValueArray[1];
214-
}
215-
216-
if ($GLOBALS['date_display_format'] == 2) {
217-
return $DateValueArray[2] . '-' . $DateValueArray[1] . '-' . $DateValueArray[0];
218-
}
219-
}
176+
return DateFormatterUtils::DateToYYYYMMDD($DateValue);
220177
}
221178

222179
function TimeToHHMMSS($TimeValue)

‎library/layout.inc.php

+24-21
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,28 @@
11
<?php
22

33
/**
4-
* Sql functions/classes for OpenEMR.
5-
*
6-
* Things related to layout based forms in general.
7-
*
8-
* Copyright (C) 2017-2021 Rod Roark <rod@sunsetsystems.com>
9-
*
10-
* LICENSE: This program is free software: you can redistribute it and/or modify
11-
* it under the terms of the GNU General Public License as published by
12-
* the Free Software Foundation, either version 3 of the License, or
13-
* (at your option) any later version.
14-
* This program is distributed in the hope that it will be useful,
15-
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16-
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17-
* GNU General Public License for more details.
18-
* You should have received a copy of the GNU General Public License
19-
* along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
20-
*
21-
* @package OpenEMR
22-
* @link http://www.open-emr.org
23-
*/
4+
* Sql functions/classes for OpenEMR.
5+
*
6+
* Things related to layout based forms in general.
7+
*
8+
* Copyright (C) 2017-2021 Rod Roark <rod@sunsetsystems.com>
9+
* Copyright (c) 2022 Stephen Nielson <snielson@discoverandchange.com>
10+
* Copyright (c) 2022 David Eschelbacher <psoas@tampabay.rr.com>
11+
*
12+
* LICENSE: This program is free software: you can redistribute it and/or modify
13+
* it under the terms of the GNU General Public License as published by
14+
* the Free Software Foundation, either version 3 of the License, or
15+
* (at your option) any later version.
16+
* This program is distributed in the hope that it will be useful,
17+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
18+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19+
* GNU General Public License for more details.
20+
* You should have received a copy of the GNU General Public License
21+
* along with this program. If not, see <http://opensource.org/licenses/gpl-license.php>.
22+
*
23+
* @package OpenEMR
24+
* @link http://www.open-emr.org
25+
*/
2426

2527
// array of the data_types of the fields
2628
// TODO: Move these all to a statically typed class with constants that can be referenced throughout the codebase!
@@ -61,7 +63,8 @@
6163
"46" => xl("List box w/comment"),
6264
"51" => xl("Patient"),
6365
"52" => xl("Previous Names"),
64-
"53" => xl("Patient Encounters List")
66+
"53" => xl("Patient Encounters List"),
67+
"54" => xl("Address List")
6568
);
6669

6770
// These are the data types that can reference a list.

‎library/options.inc.php

+16-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
// Copyright © 2010 by "Boyd Stephen Smith Jr." <bss@iguanasuicide.net>
66
// Copyright (c) 2017 - 2021 Jerry Padgett <sjpadgett@gmail.com>
77
// Copyright (c) 2021 Robert Down <robertdown@live.com>
8+
// Copyright (c) 2022 David Eschelbacher <psoas@tampabay.rr.com>
89
//
910
// This program is free software; you can redistribute it and/or
1011
// modify it under the terms of the GNU General Public License
@@ -130,7 +131,8 @@ function generate_select_list(
130131
$multiple = false, // new #10
131132
$backup_list = '', // new #11
132133
$ignore_default = false,
133-
$include_inactive = false
134+
$include_inactive = false,
135+
$tabIndex = false
134136
) {
135137
$s = '';
136138

@@ -142,6 +144,10 @@ function generate_select_list(
142144

143145
$s .= "<select name='$tag_name_esc'";
144146

147+
if ($tabIndex !== false) {
148+
$s .= " tabindex='" . attr($tabIndex) . "' '";
149+
}
150+
145151
if ($multiple) {
146152
$s .= " multiple='multiple'";
147153
}
@@ -1691,6 +1697,8 @@ class='form-control{$smallform}'
16911697
}
16921698
}
16931699
echo "</select>";
1700+
} elseif ($data_type == 54) {
1701+
include "templates/address_list_form.php";
16941702
}
16951703
}
16961704

@@ -2894,6 +2902,8 @@ function generate_display_field($frow, $currvalue)
28942902
$s = text($encounter['date'] ?? '');
28952903
}
28962904
}
2905+
} elseif ($data_type == 54) {
2906+
include "templates/address_list_display.php";
28972907
}
28982908

28992909
return $s;
@@ -4357,7 +4367,11 @@ function get_layout_form_value($frow, $prefix = 'form_')
43574367
":<br />&nbsp;<br />" . htmlspecialchars($value, ENT_NOQUOTES));
43584368
}
43594369

4360-
return trim($value);
4370+
if (is_string($value)) {
4371+
return trim($value);
4372+
} else {
4373+
return $value;
4374+
}
43614375
}
43624376

43634377
// Generate JavaScript validation logic for the required fields.

‎library/options_listadd.inc

+12
Original file line numberDiff line numberDiff line change
@@ -210,5 +210,17 @@ $(function () {
210210

211211
} // end SaveNewListItem
212212

213+
// let's expose some of these options so that others can use them if they are using the options.inc.php piece
214+
let optionWidgets = {
215+
CancelAddToList: CancelAddToList
216+
,AddToList: AddToList
217+
,SaveNewListItem: SaveNewListItem
218+
};
219+
220+
if (!window.oeUI) {
221+
window.oeUI = {};
222+
}
223+
window.oeUI.optionWidgets = optionWidgets;
224+
213225
}); // end jQuery .ready
214226
</script>

0 commit comments

Comments
 (0)