diff --git a/_rest_config.php b/_rest_config.php
index c1f652a315d..d931bb969e9 100644
--- a/_rest_config.php
+++ b/_rest_config.php
@@ -202,19 +202,30 @@ public static function verifyAccessToken()
$logger = new SystemLogger();
$response = self::createServerResponse();
$request = self::createServerRequest();
- $server = new ResourceServer(
- new AccessTokenRepository(),
- self::$publicKey
- );
try {
+ // if we there's a key problem need to catch the exception
+ $server = new ResourceServer(
+ new AccessTokenRepository(),
+ self::$publicKey
+ );
$raw = $server->validateAuthenticatedRequest($request);
} catch (OAuthServerException $exception) {
$logger->error("RestConfig->verifyAccessToken() OAuthServerException", ["message" => $exception->getMessage()]);
return $exception->generateHttpResponse($response);
} catch (\Exception $exception) {
- $logger->error("RestConfig->verifyAccessToken() Exception", ["message" => $exception->getMessage()]);
- return (new OAuthServerException($exception->getMessage(), 0, 'unknown_error', 500))
- ->generateHttpResponse($response);
+ if ($exception instanceof LogicException) {
+ $logger->error(
+ "RestConfig->verifyAccessToken() LogicException, likely oauth2 public key is missing, corrupted, or misconfigured",
+ ["message" => $exception->getMessage()]
+ );
+ return (new OAuthServerException("Invalid access token", 0, 'invalid_token', 401))
+ ->generateHttpResponse($response);
+ } else {
+ $logger->error("RestConfig->verifyAccessToken() Exception", ["message" => $exception->getMessage()]);
+ // do NOT reveal what happened at the server level if we have a server exception
+ return (new OAuthServerException("Server Error", 0, 'unknown_error', 500))
+ ->generateHttpResponse($response);
+ }
}
return $raw;
diff --git a/_rest_routes.inc.php b/_rest_routes.inc.php
index b2792744a90..d3fa1ef324e 100644
--- a/_rest_routes.inc.php
+++ b/_rest_routes.inc.php
@@ -7622,6 +7622,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -7814,6 +7823,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -7945,6 +7963,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -8113,6 +8140,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -8196,6 +8232,15 @@
* type="string"
* )
* ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
* @OA\Response(
* response="200",
* description="Standard Response",
@@ -8305,6 +8350,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -8484,6 +8538,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -8642,6 +8705,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -8802,6 +8874,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -9011,6 +9092,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -9338,6 +9428,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -9537,6 +9636,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -9697,6 +9805,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -9861,6 +9978,24 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -10014,6 +10149,15 @@
* type="string"
* )
* ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
* @OA\Response(
* response="200",
* description="Standard Response",
@@ -10133,6 +10277,24 @@
* path="/fhir/Medication",
* description="Returns a list of Medication resources.",
* tags={"fhir"},
+ * @OA\Parameter(
+ * name="_id",
+ * in="query",
+ * description="The uuid for the Medication resource.",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
* @OA\Response(
* response="200",
* description="Standard Response",
@@ -10270,6 +10432,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -10458,6 +10629,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -10684,6 +10864,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="name",
* in="query",
* description="The name of the Organization resource.",
@@ -11491,6 +11680,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="identifier",
* in="query",
* description="The identifier of the Patient resource.",
@@ -11860,6 +12058,24 @@
* description="Returns a list of Person resources.",
* tags={"fhir"},
* @OA\Parameter(
+ * name="_id",
+ * in="query",
+ * description="The uuid for the Person resource.",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="name",
* in="query",
* description="The name of the Person resource.",
@@ -12138,6 +12354,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="name",
* in="query",
* description="The name of the Practitioner resource.",
@@ -12569,6 +12794,24 @@
* description="Returns a list of PractitionerRole resources.",
* tags={"fhir"},
* @OA\Parameter(
+ * name="_id",
+ * in="query",
+ * description="The uuid for the PractitionerRole resource.",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="specialty",
* in="query",
* description="The specialty of the PractitionerRole resource.",
@@ -12727,6 +12970,15 @@
* )
* ),
* @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
+ * @OA\Parameter(
* name="patient",
* in="query",
* description="The uuid for the patient.",
@@ -13051,6 +13303,15 @@
* type="string"
* )
* ),
+ * @OA\Parameter(
+ * name="_lastUpdated",
+ * in="query",
+ * description="Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)",
+ * required=false,
+ * @OA\Schema(
+ * type="string"
+ * )
+ * ),
* @OA\Response(
* response="200",
* description="Standard Response",
diff --git a/interface/super/load_codes.php b/interface/super/load_codes.php
index 9bea7da6e07..89d122b13d1 100644
--- a/interface/super/load_codes.php
+++ b/interface/super/load_codes.php
@@ -226,7 +226,10 @@
that (zipped or not). You may do the same with the weekly updates, but for those uncheck the
"" checkbox above.
-
+
diff --git a/library/classes/Installer.class.php b/library/classes/Installer.class.php
index 319d14dad11..3bb2eb0ac7b 100644
--- a/library/classes/Installer.class.php
+++ b/library/classes/Installer.class.php
@@ -1346,14 +1346,23 @@ private function execute_sql($sql, $showError = true)
$this->user_database_connection();
}
- $results = mysqli_query($this->dbh, $sql);
- if ($results) {
- return $results;
- } else {
+ try {
+ $results = mysqli_query($this->dbh, $sql);
+ if ($results) {
+ return $results;
+ } else {
+ if ($showError) {
+ $error_mes = mysqli_error($this->dbh);
+ $this->error_message = "unable to execute SQL: '$sql' due to: " . $error_mes;
+ error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: " . htmlspecialchars($sql, ENT_QUOTES) . " due to: " . htmlspecialchars($error_mes, ENT_QUOTES));
+ }
+ return false;
+ }
+ // this exception only occurs if MYSQLI_REPORT_STRICT is enabled (see https://www.php.net/manual/en/mysqli.query.php)
+ } catch (\mysqli_sql_exception $exception) {
if ($showError) {
- $error_mes = mysqli_error($this->dbh);
- $this->error_message = "unable to execute SQL: '$sql' due to: " . $error_mes;
- error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: " . htmlspecialchars($sql, ENT_QUOTES) . " due to: " . htmlspecialchars($error_mes, ENT_QUOTES));
+ $this->error_message = "unable to execute SQL: '$sql' due to: " . $exception->getMessage();
+ error_log("ERROR IN OPENEMR INSTALL: Unable to execute SQL: " . htmlspecialchars($sql, ENT_QUOTES) . " due to: " . htmlspecialchars($exception->getMessage(), ENT_QUOTES));
}
return false;
}
diff --git a/sql/7_0_2-to-7_0_3_upgrade.sql b/sql/7_0_2-to-7_0_3_upgrade.sql
index 40f7ffb72cb..3cf5447325d 100644
--- a/sql/7_0_2-to-7_0_3_upgrade.sql
+++ b/sql/7_0_2-to-7_0_3_upgrade.sql
@@ -134,4 +134,72 @@ INSERT INTO `supported_external_dataloads` (`load_type`, `load_source`, `load_re
#IfNotRow4D supported_external_dataloads load_type ICD10 load_source CMS load_release_date 2024-10-01 load_filename Zip File 3 2025 ICD-10-PCS Codes File.zip
INSERT INTO `supported_external_dataloads` (`load_type`, `load_source`, `load_release_date`, `load_filename`, `load_checksum`) VALUES
('ICD10', 'CMS', '2024-10-01', 'Zip File 3 2025 ICD-10-PCS Codes File.zip', 'a47ceb9a09fcc475fec19cee6526a335');
-#EndIf
\ No newline at end of file
+#EndIf
+
+#IfMissingColumn users date_created
+ALTER TABLE `users` ADD `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn users last_updated
+ALTER TABLE `users` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn facility date_created
+ALTER TABLE `facility` ADD `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn facility last_updated
+ALTER TABLE `facility` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn insurance_companies date_created
+ALTER TABLE `insurance_companies` ADD `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn insurance_companies last_updated
+ALTER TABLE `insurance_companies` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn facility_user_ids date_created
+ALTER TABLE `facility_user_ids` ADD `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn facility_user_ids last_updated
+ALTER TABLE `facility_user_ids` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn openemr_postcalendar_categories pc_last_updated
+ALTER TABLE `openemr_postcalendar_categories` ADD `pc_last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn list_options last_updated
+ALTER TABLE `list_options` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn form_clinical_notes last_updated
+ALTER TABLE `form_clinical_notes` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn form_vitals last_updated
+ALTER TABLE `form_vitals` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn procedure_providers date_created
+ALTER TABLE `procedure_providers` ADD `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn procedure_providers last_updated
+ALTER TABLE `procedure_providers` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn drugs date_created
+ALTER TABLE `drugs` ADD `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn drugs last_updated
+ALTER TABLE `drugs` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
+
+#IfMissingColumn patient_data last_updated
+ALTER TABLE `patient_data` ADD `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;
+#EndIf
diff --git a/sql/database.sql b/sql/database.sql
index 1fd6da2f49f..6954d015db1 100644
--- a/sql/database.sql
+++ b/sql/database.sql
@@ -1485,6 +1485,8 @@ CREATE TABLE `drugs` (
`drug_code` varchar(25) NULL,
`consumable` tinyint(1) NOT NULL DEFAULT 0 COMMENT '1 = will not show on the fee sheet',
`dispensable` tinyint(1) NOT NULL DEFAULT 1 COMMENT '0 = pharmacy elsewhere, 1 = dispensed here',
+ `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`drug_id`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=InnoDB AUTO_INCREMENT=1;
@@ -1737,6 +1739,8 @@ CREATE TABLE `facility` (
`info` TEXT,
`weno_id` VARCHAR(10) DEFAULT NULL,
`inactive` tinyint(1) NOT NULL DEFAULT '0',
+ `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `uuid` (`uuid`),
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4;
@@ -1745,7 +1749,7 @@ CREATE TABLE `facility` (
-- Inserting data for table `facility`
--
-INSERT INTO `facility` VALUES (3, NULL, 'Your Clinic Name Here', '000-000-0000', '000-000-0000', '', '', '', '', '', '', NULL, NULL, 1, 1, 1, NULL, '', '', '', '', '', '','#99FFFF','0', '', '1', '', '', '', '', '', '', '', '', NULL, 0);
+INSERT INTO `facility` VALUES (3, NULL, 'Your Clinic Name Here', '000-000-0000', '000-000-0000', '', '', '', '', '', '', NULL, NULL, 1, 1, 1, NULL, '', '', '', '', '', '','#99FFFF','0', '', '1', '', '', '', '', '', '', '', '', NULL, 0, NOW(), NOW());
-- --------------------------------------------------------
@@ -1761,6 +1765,8 @@ CREATE TABLE `facility_user_ids` (
`uuid` binary(16) DEFAULT NULL,
`field_id` varchar(31) NOT NULL COMMENT 'references layout_options.field_id',
`field_value` TEXT,
+ `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `uid` (`uid`,`facility_id`,`field_id`),
KEY `uuid` (`uuid`)
@@ -1839,6 +1845,7 @@ CREATE TABLE `form_clinical_notes` (
`clinical_notes_type` varchar(100) DEFAULT NULL,
`clinical_notes_category` varchar(100) DEFAULT NULL,
`note_related_to` text,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=InnoDB;
@@ -2293,6 +2300,7 @@ CREATE TABLE `form_vitals` (
`ped_bmi` DECIMAL(6,2) default '0.00',
`ped_head_circ` DECIMAL(6,2) default '0.00',
`inhaled_oxygen_concentration` DECIMAL(6,2) DEFAULT '0.00',
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `pid` (`pid`),
UNIQUE KEY `uuid` (`uuid`)
@@ -3137,6 +3145,8 @@ CREATE TABLE `insurance_companies` (
`eligibility_id` VARCHAR(32) default NULL,
`x12_default_eligibility_id` INT(11) default NULL,
`cqm_sop` int DEFAULT NULL COMMENT 'HL7 Source of Payment for eCQMs',
+ `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=InnoDB;
@@ -3715,6 +3725,7 @@ CREATE TABLE `list_options` (
`subtype` varchar(31) NOT NULL DEFAULT '',
`edit_options` tinyint(1) NOT NULL DEFAULT '1',
`timestamp` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`list_id`,`option_id`)
) ENGINE=InnoDB;
@@ -7289,6 +7300,7 @@ CREATE TABLE `openemr_postcalendar_categories` (
`pc_active` tinyint(1) NOT NULL default 1,
`pc_seq` int(11) NOT NULL default '0',
`aco_spec` VARCHAR(63) NOT NULL default 'encounters|notes',
+ `pc_last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`pc_catid`),
UNIQUE KEY (`pc_constant_id`),
KEY `basic_cat` (`pc_catname`,`pc_catcolor`)
@@ -7298,21 +7310,37 @@ CREATE TABLE `openemr_postcalendar_categories` (
-- Inserting data for table `openemr_postcalendar_categories`
--
-INSERT INTO `openemr_postcalendar_categories` VALUES (1,'no_show', 'No Show', '#dee2e6', 'Reserved to define when an event did not occur as specified.', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 0, 0, 0, 0, 0, 0,1,1,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (2,'in_office', 'In Office', '#cce5ff', 'Reserved todefine when a provider may haveavailable appointments after.', 1, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 1, 3, 2, 0, 0, 1,1,2,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (3,'out_of_office', 'Out Of Office', '#fdb172', 'Reserved to define when a provider may not have available appointments after.', 1, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 1, 3, 2, 0, 0, 1,1,3,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (4,'vacation', 'Vacation', '#e9ecef', 'Reserved for use to define Scheduled Vacation Time', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 0, 0, 0, 1, 0, 1,1,4,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (5,'office_visit', 'Office Visit', '#ffecb4', 'Normal Office Visit', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,5,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (6,'holidays','Holidays','#8663ba','Clinic holiday',0,NULL,'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}',0,86400,1,3,2,0,0,2,1,6,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (7,'closed','Closed','#2374ab','Clinic closed',0,NULL,'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}',0,86400,1,3,2,0,0,2,1,7,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (8,'lunch', 'Lunch', '#ffd351', 'Lunch', 1, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 3600, 0, 3, 2, 0, 0, 1,1,8,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (9,'established_patient', 'Established Patient', '#93d3a2', '', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0, 0,1,9,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (10,'new_patient','New Patient', '#a2d9e2', '', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 1800, 0, 0, 0, 0, 0, 0,1,10,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (11,'reserved','Reserved','#b02a37','Reserved',1,NULL,'a:5:{s:17:\"event_repeat_freq\";s:1:\"1\";s:22:\"event_repeat_freq_type\";s:1:\"4\";s:19:\"event_repeat_on_num\";s:1:\"1\";s:19:\"event_repeat_on_day\";s:1:\"0\";s:20:\"event_repeat_on_freq\";s:1:\"0\";}',0,900,0,3,2,0,0, 1,1,11,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (12,'health_and_behavioral_assessment', 'Health and Behavioral Assessment', '#ced4da', 'Health and Behavioral Assessment', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,12,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (13,'preventive_care_services', 'Preventive Care Services', '#d3c6ec', 'Preventive Care Services', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,13,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (14,'ophthalmological_services', 'Ophthalmological Services', '#febe89', 'Ophthalmological Services', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,14,'encounters|notes');
-INSERT INTO `openemr_postcalendar_categories` VALUES (15,'group_therapy', 'Group Therapy' , '#adb5bd' , 'Group Therapy', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 3600, 0, 0, 0, 0, 0, 3, 1, 15,'encounters|notes');
+
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (1,'no_show', 'No Show', '#dee2e6', 'Reserved to define when an event did not occur as specified.', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 0, 0, 0, 0, 0, 0,1,1,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (2,'in_office', 'In Office', '#cce5ff', 'Reserved todefine when a provider may haveavailable appointments after.', 1, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 1, 3, 2, 0, 0, 1,1,2,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (3,'out_of_office', 'Out Of Office', '#fdb172', 'Reserved to define when a provider may not have available appointments after.', 1, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 1, 3, 2, 0, 0, 1,1,3,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (4,'vacation', 'Vacation', '#e9ecef', 'Reserved for use to define Scheduled Vacation Time', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 0, 0, 0, 0, 1, 0, 1,1,4,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (5,'office_visit', 'Office Visit', '#ffecb4', 'Normal Office Visit', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,5,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (6,'holidays','Holidays','#8663ba','Clinic holiday',0,NULL,'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}',0,86400,1,3,2,0,0,2,1,6,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (7,'closed','Closed','#2374ab','Clinic closed',0,NULL,'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}',0,86400,1,3,2,0,0,2,1,7,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (8,'lunch', 'Lunch', '#ffd351', 'Lunch', 1, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"1";s:22:"event_repeat_freq_type";s:1:"4";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 3600, 0, 3, 2, 0, 0, 1,1,8,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (9,'established_patient', 'Established Patient', '#93d3a2', '', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0, 0,1,9,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (10,'new_patient','New Patient', '#a2d9e2', '', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 1800, 0, 0, 0, 0, 0, 0,1,10,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (11,'reserved','Reserved','#b02a37','Reserved',1,NULL,'a:5:{s:17:\"event_repeat_freq\";s:1:\"1\";s:22:\"event_repeat_freq_type\";s:1:\"4\";s:19:\"event_repeat_on_num\";s:1:\"1\";s:19:\"event_repeat_on_day\";s:1:\"0\";s:20:\"event_repeat_on_freq\";s:1:\"0\";}',0,900,0,3,2,0,0, 1,1,11,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (12,'health_and_behavioral_assessment', 'Health and Behavioral Assessment', '#ced4da', 'Health and Behavioral Assessment', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,12,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (13,'preventive_care_services', 'Preventive Care Services', '#d3c6ec', 'Preventive Care Services', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,13,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (14,'ophthalmological_services', 'Ophthalmological Services', '#febe89', 'Ophthalmological Services', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 900, 0, 0, 0, 0, 0,0,1,14,'encounters|notes');
+INSERT INTO `openemr_postcalendar_categories`(`pc_catid`, `pc_constant_id`, `pc_catname`, `pc_catcolor`, `pc_catdesc`, `pc_recurrtype`, `pc_enddate`, `pc_recurrspec`, `pc_recurrfreq`, `pc_duration`, `pc_end_date_flag`, `pc_end_date_type`, `pc_end_date_freq`, `pc_end_all_day`, `pc_dailylimit`, `pc_cattype`, `pc_active`, `pc_seq`, `aco_spec`)
+ VALUES (15,'group_therapy', 'Group Therapy' , '#adb5bd' , 'Group Therapy', 0, NULL, 'a:5:{s:17:"event_repeat_freq";s:1:"0";s:22:"event_repeat_freq_type";s:1:"0";s:19:"event_repeat_on_num";s:1:"1";s:19:"event_repeat_on_day";s:1:"0";s:20:"event_repeat_on_freq";s:1:"0";}', 0, 3600, 0, 0, 0, 0, 0, 3, 1, 15,'encounters|notes');
-- --------------------------------------------------------
@@ -7521,6 +7549,7 @@ CREATE TABLE `patient_data` (
`updated_by` BIGINT(20) DEFAULT NULL COMMENT 'users.id the user that last modified this record',
`preferred_name` TINYTEXT,
`nationality_country` TINYTEXT,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY `pid` (`pid`),
UNIQUE KEY `uuid` (`uuid`),
KEY `id` (`id`)
@@ -8895,6 +8924,8 @@ CREATE TABLE `users` (
`supervisor_id` int(11) NOT NULL DEFAULT '0',
`billing_facility` TEXT,
`billing_facility_id` INT(11) NOT NULL DEFAULT '0',
+ `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uuid` (`uuid`),
KEY `abook_type` (`abook_type`)
@@ -9343,6 +9374,8 @@ CREATE TABLE `procedure_providers` (
`lab_director` bigint(20) NOT NULL DEFAULT '0',
`active` tinyint(1) NOT NULL DEFAULT '1',
`type` varchar(31) DEFAULT NULL,
+ `date_created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ `last_updated` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`ppid`),
UNIQUE KEY `uuid` (`uuid`)
) ENGINE=InnoDB;
diff --git a/src/FHIR/Export/ExportJob.php b/src/FHIR/Export/ExportJob.php
index 0c771ca61e1..87eeec842f7 100644
--- a/src/FHIR/Export/ExportJob.php
+++ b/src/FHIR/Export/ExportJob.php
@@ -22,6 +22,8 @@
use http\Exception\InvalidArgumentException;
use OpenEMR\Common\Uuid\UuidRegistry;
+use OpenEMR\Services\Search\SearchComparator;
+use OpenEMR\Services\Utils\DateFormatterUtils;
class ExportJob
{
@@ -210,6 +212,16 @@ public function getResourceIncludeTime(): \DateTime
return $this->resourceIncludeTime;
}
+ public function getResourceIncludeSearchParamValue()
+ {
+ return SearchComparator::GREATER_THAN_OR_EQUAL_TO . $this->getResourceIncludeISO8601Date();
+ }
+
+ public function getResourceIncludeISO8601Date(): string
+ {
+ return DateFormatterUtils::getFormattedISO8601DateFromDateTime($this->resourceIncludeTime);
+ }
+
/**
* @param \DateTime $resourceIncludeTime
*/
diff --git a/src/RestControllers/AuthorizationController.php b/src/RestControllers/AuthorizationController.php
index ce7c95e79cc..b0c4349358d 100644
--- a/src/RestControllers/AuthorizationController.php
+++ b/src/RestControllers/AuthorizationController.php
@@ -90,6 +90,9 @@ class AuthorizationController
public const GRANT_TYPE_CLIENT_CREDENTIALS = 'client_credentials';
public const OFFLINE_ACCESS_SCOPE = 'offline_access';
+ // https://hl7.org/fhir/uv/bulkdata/authorization/index.html#issuing-access-tokens Spec states 5 min max
+ public const GRANT_TYPE_ACCESS_CODE_TTL = "PT300S"; // 5 minutes
+
public $authBaseUrl;
public $authBaseFullUrl;
public $siteId;
@@ -642,8 +645,7 @@ public function getAuthorizationServer($includeAuthGrantRefreshToken = true): Au
$client_credentials->setHttpClient(new Client()); // set our guzzle client here
$authServer->enableGrantType(
$client_credentials,
- // https://hl7.org/fhir/uv/bulkdata/authorization/index.html#issuing-access-tokens Spec states 5 min max
- new \DateInterval('PT300S')
+ new \DateInterval(self::GRANT_TYPE_ACCESS_CODE_TTL)
);
}
diff --git a/src/RestControllers/FHIR/Operations/FhirOperationExportRestController.php b/src/RestControllers/FHIR/Operations/FhirOperationExportRestController.php
index f1377855296..78bfae45d41 100644
--- a/src/RestControllers/FHIR/Operations/FhirOperationExportRestController.php
+++ b/src/RestControllers/FHIR/Operations/FhirOperationExportRestController.php
@@ -23,6 +23,8 @@
use OpenEMR\Services\FHIR\IFhirExportableResourceService;
use OpenEMR\Services\FHIR\Utils\FhirServiceLocator;
use OpenEMR\Services\FHIR\UtilsService;
+use OpenEMR\Services\Search\DateSearchField;
+use OpenEMR\Services\Search\SearchFieldComparableValue;
use Psr\Http\Message\ResponseInterface;
use Psr\Log\LoggerInterface;
use Ramsey\Uuid\Uuid;
@@ -104,7 +106,11 @@ public function processExport($exportParams, $exportType, $acceptHeader, $prefer
}
$outputFormat = $exportParams['_outputFormat'] ?? ExportJob::OUTPUT_FORMAT_FHIR_NDJSON;
- $since = $exportParams['_since'] ?? new \DateTime(date("Y-m-d H:i:s", 0)); // since epoch time
+ if (!empty($exportParams['_since'])) {
+ $since = $this->parseFHIRInstant($exportParams['_since']);
+ } else {
+ $since = new \DateTime(date(\DateTimeInterface::ATOM, 0)); // since epoch time
+ }
$type = $exportParams['type'] ?? '';
$groupId = $exportParams['groupId'] ?? null;
$resources = !empty($type) ? explode(",", $type) : [];
@@ -624,4 +630,19 @@ private function getPatientUuidsForGroup($groupId)
}
return $patientUuids;
}
+
+ private function parseFHIRInstant(string $_since)
+ {
+ // concievably they could send us a date that is not an actual INSTANCE of a date, but we'll just convert it
+ // to a regular date anyways, if the format is invalid DateSearchField will error out.
+ $dateField = new DateSearchField("_since", [$_since], DateSearchField::DATE_TYPE_DATETIME);
+ $values = $dateField->getValues();
+ $comparable = reset($values);
+ $value = $comparable->getValue();
+ if ($value instanceof \DatePeriod) {
+ return $value->getStartDate();
+ } else {
+ throw new \InvalidArgumentException("Invalid date format for _since parameter");
+ }
+ }
}
diff --git a/src/Services/AppointmentService.php b/src/Services/AppointmentService.php
index c6d9e62b584..1e7776877ba 100644
--- a/src/Services/AppointmentService.php
+++ b/src/Services/AppointmentService.php
@@ -475,7 +475,7 @@ public function deleteAppointmentRecord($eid)
*/
public function getCalendarCategories()
{
- $sql = "SELECT pc_catid, pc_constant_id, pc_catname, pc_cattype,aco_spec FROM openemr_postcalendar_categories "
+ $sql = "SELECT pc_catid, pc_constant_id, pc_catname, pc_cattype,aco_spec, pc_last_updated FROM openemr_postcalendar_categories "
. " WHERE pc_active = 1 ORDER BY pc_seq";
return QueryUtils::fetchRecords($sql);
}
@@ -656,4 +656,19 @@ public function getOneCalendarCategory($cat_id)
$sql = "SELECT * FROM openemr_postcalendar_categories WHERE pc_catid = ?";
return QueryUtils::fetchRecords($sql, [$cat_id]);
}
+
+ public function searchCalendarCategories(array $oeSearchParameters)
+ {
+ $sql = "SELECT * FROM openemr_postcalendar_categories ";
+ $whereClause = FhirSearchWhereClauseBuilder::build($oeSearchParameters, true);
+ $sql .= $whereClause->getFragment();
+ $sqlBindArray = $whereClause->getBoundValues();
+ $records = QueryUtils::fetchRecords($sql, $sqlBindArray);
+ $processingResult = new ProcessingResult();
+ if (!empty($records)) {
+ $processingResult->setData($records);
+ }
+ // TODO: look at handling offset and limit here
+ return $processingResult;
+ }
}
diff --git a/src/Services/CareTeamService.php b/src/Services/CareTeamService.php
index e3d44d0729d..4c1e9d56cd2 100644
--- a/src/Services/CareTeamService.php
+++ b/src/Services/CareTeamService.php
@@ -50,6 +50,7 @@ public function search($search, $isAndCondition = true)
careteam_mapping.care_team_provider as providers,
careteam_mapping.care_team_facility as facilities,
careteam_mapping.care_team_status,
+ careteam_mapping.date,
care_team_status_title
FROM (
SELECT
@@ -58,6 +59,7 @@ public function search($search, $isAndCondition = true)
,patient_data.care_team_provider
,patient_data.care_team_facility
,patient_data.care_team_status
+ ,patient_data.date
FROM
uuid_mapping
-- we join on this to make sure we've got data integrity since we don't actually use foreign keys right now
@@ -72,6 +74,7 @@ public function search($search, $isAndCondition = true)
,patient_history.care_team_provider
,patient_history.care_team_facility
,'inactive' AS care_team_status
+ ,patient_history.date
FROM
patient_history
JOIN patient_data ON patient_history.pid = patient_data.pid
diff --git a/src/Services/ClinicalNotesService.php b/src/Services/ClinicalNotesService.php
index 1c6c4345a69..9c61653bafd 100644
--- a/src/Services/ClinicalNotesService.php
+++ b/src/Services/ClinicalNotesService.php
@@ -74,6 +74,8 @@ public function search($search, $isAndCondition = true)
,notes.clinical_notes_type
,notes.note_related_to
,notes.clinical_notes_category
+ ,notes.last_updated
+ ,forms.date_created
,lo_category.category_code
,lo_category.category_title
,patients.pid
@@ -100,6 +102,7 @@ public function search($search, $isAndCondition = true)
,note_related_to
,clinical_notes_category
,form_id
+ ,last_updated
,user
FROM
form_clinical_notes
@@ -109,6 +112,7 @@ public function search($search, $isAndCondition = true)
id AS form_id,
encounter
,pid AS form_pid
+ ,`date` AS date_created
FROM
forms
) forms ON forms.form_id = notes.form_id
diff --git a/src/Services/ConditionService.php b/src/Services/ConditionService.php
index a3fd1bf37f6..a1423c36823 100644
--- a/src/Services/ConditionService.php
+++ b/src/Services/ConditionService.php
@@ -48,6 +48,7 @@ public function search($search, $isAndCondition = true)
patient.puuid,
patient.patient_uuid,
condition_ids.condition_uuid,
+ condition_ids.last_updated_time,
verification.title as verification_title
,provider.provider_id
,provider.provider_npi
@@ -55,7 +56,7 @@ public function search($search, $isAndCondition = true)
,provider.provider_username
FROM lists
INNER JOIN (
- SELECT lists.uuid AS condition_uuid FROM lists
+ SELECT lists.uuid AS condition_uuid, lists.modifydate as last_updated_time FROM lists
) condition_ids ON lists.uuid = condition_ids.condition_uuid
LEFT JOIN list_options as verification ON verification.option_id = lists.verification and verification.list_id = 'condition-verification'
RIGHT JOIN (
@@ -68,7 +69,7 @@ public function search($search, $isAndCondition = true)
LEFT JOIN issue_encounter as issue ON issue.list_id =lists.id
LEFT JOIN form_encounter as encounter ON encounter.encounter =issue.encounter
LEFT JOIN (
- select
+ select
id AS provider_id
,uuid AS provider_uuid
,npi AS provider_npi
diff --git a/src/Services/DeviceService.php b/src/Services/DeviceService.php
index b2e5540868a..e8bf46077af 100644
--- a/src/Services/DeviceService.php
+++ b/src/Services/DeviceService.php
@@ -38,7 +38,7 @@ public function search($search, $isAndCondition = true)
(
SELECT
`udi`,
- `uuid`, `date`, `title`,`udi_data`, `begdate`, `diagnosis`, `user`, `pid`
+ `uuid`, `date`, `title`,`udi_data`, `begdate`, `diagnosis`, `user`, `pid`,modifydate
FROM lists WHERE `type` = 'medical_device'
) l
JOIN (
@@ -46,7 +46,7 @@ public function search($search, $isAndCondition = true)
from patient_data
) patients ON l.pid = patients.pid
LEFT JOIN (
- select
+ select
id AS provider_id
,npi AS provider_npi
,uuid AS provider_uuid
diff --git a/src/Services/DrugService.php b/src/Services/DrugService.php
index ee72ba86132..34ff7567fda 100644
--- a/src/Services/DrugService.php
+++ b/src/Services/DrugService.php
@@ -94,29 +94,49 @@ public function getOne($uuid)
public function search($search, $isAndCondition = true)
{
- $sql = "SELECT drugs.drug_id,
- uuid,
- name,
- ndc_number,
- form,
- size,
- unit,
- route,
- related_code,
- active,
- drug_code,
+ $sql = "SELECT
+ drug_table.drug_id,
+ drug_table.uuid,
+ drug_table.name,
+ drug_table.ndc_number,
+ drug_table.form,
+ drug_table.size,
+ drug_table.unit,
+ drug_table.route,
+ drug_table.related_code,
+ drug_table.active,
+ drug_table.drug_code,
IF(drug_prescriptions.rxnorm_drugcode!=''
,drug_prescriptions.rxnorm_drugcode
- ,IF(drug_code IS NULL, '', concat('RXCUI:',drug_code))
+ ,IF(drug_table.drug_code IS NULL, '', drug_table.drug_code)
) AS 'rxnorm_drugcode',
drug_inventory.manufacturer,
drug_inventory.lot_number,
- drug_inventory.expiration
- FROM drugs
+ drug_inventory.expiration,
+ drug_table.drug_last_updated,
+ drug_table.drug_date_created
+ FROM (
+ select
+ drug_id,
+ uuid,
+ name,
+ ndc_number,
+ form,
+ size,
+ unit,
+ route,
+ related_code,
+ active,
+ drug_code,
+ last_updated AS drug_last_updated,
+ date_created AS drug_date_created
+ FROM
+ drugs
+ ) drug_table
LEFT JOIN drug_inventory
- ON drugs.drug_id = drug_inventory.drug_id
+ ON drug_table.drug_id = drug_inventory.drug_id
LEFT JOIN (
- select
+ select
uuid AS prescription_uuid
,rxnorm_drugcode
,drug_id
@@ -124,7 +144,7 @@ public function search($search, $isAndCondition = true)
FROM
prescriptions
) drug_prescriptions
- ON drug_prescriptions.drug_id = drugs.drug_id
+ ON drug_prescriptions.drug_id = drug_table.drug_id
LEFT JOIN (
select uuid AS puuid
,pid
@@ -161,7 +181,14 @@ protected function createResultRecordFromDatabaseResult($row)
$record = parent::createResultRecordFromDatabaseResult($row);
if ($record['rxnorm_drugcode'] != "") {
- $codes = $this->addCoding($row['rxnorm_drugcode']);
+ // removed the RXCUI concatenation out of the db query and into the code here
+ // some parts of OpenEMR adds the RXCUI designation in the drug_code such as the inventory/dispensary module
+ // and this causes the FHIR medication resource to not get the actual RXCUI code.
+ if ($row['drug_code'] == $record['rxnorm_drugcode'] && strpos($row['drug_code'], ':') === false) {
+ $codes = $this->addCoding("RXCUI:" . $row['drug_code']);
+ } else {
+ $codes = $this->addCoding($row['rxnorm_drugcode']);
+ }
$updatedCodes = [];
foreach ($codes as $code => $codeValues) {
if (empty($codeValues['description'])) {
@@ -173,6 +200,7 @@ protected function createResultRecordFromDatabaseResult($row)
$record['drug_code'] = $updatedCodes;
}
+ // TODO: @adunsulag this looks odd... why modify the original row...? look at removing this.
if ($row['rxnorm_drugcode'] != "") {
$row['drug_code'] = $this->addCoding($row['drug_code']);
}
diff --git a/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportClinicalNotesService.php b/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportClinicalNotesService.php
index 2ac98de8718..55da1778b69 100644
--- a/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportClinicalNotesService.php
+++ b/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportClinicalNotesService.php
@@ -31,6 +31,7 @@
use OpenEMR\Services\FHIR\UtilsService;
use OpenEMR\Services\ListService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\SearchModifier;
use OpenEMR\Services\Search\ServiceField;
@@ -65,9 +66,15 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category_code']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
public function supportsCategory($category)
{
$loincCategory = "LOINC:" . $category;
@@ -85,10 +92,14 @@ public function supportsCode($code)
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$report = new FHIRDiagnosticReport();
- $meta = new FHIRMeta();
- $meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $report->setMeta($meta);
+ $fhirMeta = new FHIRMeta();
+ $fhirMeta->setVersionId('1');
+ if (!empty($dataRecord['last_updated'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $report->setMeta($fhirMeta);
$id = new FHIRId();
$id->setValue($dataRecord['uuid']);
diff --git a/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportLaboratoryService.php b/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportLaboratoryService.php
index 393e8bc696e..ddfcb5c581c 100644
--- a/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportLaboratoryService.php
+++ b/src/Services/FHIR/DiagnosticReport/FhirDiagnosticReportLaboratoryService.php
@@ -29,6 +29,7 @@
use OpenEMR\Services\FHIR\UtilsService;
use OpenEMR\Services\ProcedureService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\SearchModifier;
use OpenEMR\Services\Search\ServiceField;
@@ -71,9 +72,15 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['report_date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('report_uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['report_date']);
+ }
+
public function supportsCategory($category)
{
return $category === self::LAB_CATEGORY;
@@ -91,14 +98,15 @@ public function supportsCode($code)
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$report = new FHIRDiagnosticReport();
- $meta = new FHIRMeta();
- $meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $report->setMeta($meta);
-
-
$dataRecordReport = array_pop($dataRecord['reports']);
-
+ $fhirMeta = new FHIRMeta();
+ $fhirMeta->setVersionId('1');
+ if (!empty($dataRecordReport['date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecordReport['date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $report->setMeta($fhirMeta);
$id = new FHIRId();
$id->setValue($dataRecordReport['uuid']);
diff --git a/src/Services/FHIR/DocumentReference/FhirClinicalNotesService.php b/src/Services/FHIR/DocumentReference/FhirClinicalNotesService.php
index 2b71c4183dc..dcd70bfd5cb 100644
--- a/src/Services/FHIR/DocumentReference/FhirClinicalNotesService.php
+++ b/src/Services/FHIR/DocumentReference/FhirClinicalNotesService.php
@@ -36,6 +36,7 @@
use OpenEMR\Services\FHIR\Traits\PatientSearchTrait;
use OpenEMR\Services\FHIR\UtilsService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\SearchModifier;
use OpenEMR\Services\Search\ServiceField;
@@ -85,16 +86,26 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATE, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$docReference = new FHIRDocumentReference();
- $meta = new FHIRMeta();
- $meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $docReference->setMeta($meta);
+ $fhirMeta = new FHIRMeta();
+ $fhirMeta->setVersionId('1');
+ if (!empty($dataRecord['date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $docReference->setMeta($fhirMeta);
$id = new FHIRId();
$id->setValue($dataRecord['uuid']);
diff --git a/src/Services/FHIR/DocumentReference/FhirPatientDocumentReferenceService.php b/src/Services/FHIR/DocumentReference/FhirPatientDocumentReferenceService.php
index 485b0f8b3ca..9271d883d9d 100644
--- a/src/Services/FHIR/DocumentReference/FhirPatientDocumentReferenceService.php
+++ b/src/Services/FHIR/DocumentReference/FhirPatientDocumentReferenceService.php
@@ -31,6 +31,7 @@
use OpenEMR\Services\FHIR\Traits\PatientSearchTrait;
use OpenEMR\Services\FHIR\UtilsService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\SearchModifier;
use OpenEMR\Services\Search\ServiceField;
@@ -74,9 +75,15 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
protected function searchForOpenEMRRecords($openEMRSearchParameters): ProcessingResult
{
if (isset($openEMRSearchParameters['category'])) {
@@ -99,10 +106,14 @@ protected function searchForOpenEMRRecords($openEMRSearchParameters): Processing
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$docReference = new FHIRDocumentReference();
- $meta = new FHIRMeta();
- $meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $docReference->setMeta($meta);
+ $fhirMeta = new FHIRMeta();
+ $fhirMeta->setVersionId('1');
+ if (!empty($dataRecord['date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $docReference->setMeta($fhirMeta);
$id = new FHIRId();
$id->setValue($dataRecord['uuid']);
diff --git a/src/Services/FHIR/FhirAllergyIntoleranceService.php b/src/Services/FHIR/FhirAllergyIntoleranceService.php
index 3fdc04ad0ff..717710fc722 100644
--- a/src/Services/FHIR/FhirAllergyIntoleranceService.php
+++ b/src/Services/FHIR/FhirAllergyIntoleranceService.php
@@ -30,6 +30,7 @@
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\PractitionerService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\ReferenceSearchValue;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
@@ -76,9 +77,14 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('allergy_uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['modifydate']);
+ }
/**
* Parses an OpenEMR allergyIntolerance record, returning the equivalent FHIR AllergyIntolerance Resource
@@ -113,7 +119,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$allergyIntoleranceResource = new FHIRAllergyIntolerance();
$fhirMeta = new FHIRMeta();
$fhirMeta->setVersionId("1");
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['modifydate']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$allergyIntoleranceResource->setMeta($fhirMeta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirAppointmentService.php b/src/Services/FHIR/FhirAppointmentService.php
index 658c71544b1..2c07df49680 100644
--- a/src/Services/FHIR/FhirAppointmentService.php
+++ b/src/Services/FHIR/FhirAppointmentService.php
@@ -65,11 +65,16 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('pc_uuid', ServiceField::TYPE_UUID)]),
- '_lastUpdated' => new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['pc_time']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATE, ['pc_eventDate']),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['pc_time']);
+ }
+
/**
* Parses an OpenEMR data record, returning the equivalent FHIR Resource
*
diff --git a/src/Services/FHIR/FhirCarePlanService.php b/src/Services/FHIR/FhirCarePlanService.php
index ff6411e3f6f..1d70b3f2bab 100644
--- a/src/Services/FHIR/FhirCarePlanService.php
+++ b/src/Services/FHIR/FhirCarePlanService.php
@@ -23,6 +23,7 @@
use OpenEMR\Services\FHIR\Traits\FhirBulkExportDomainResourceTrait;
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -57,9 +58,16 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('status', SearchFieldType::TOKEN, ['careplan_category']),
// note even though we label this as a uuid, it is a SURROGATE UID because of the nature of CarePlan
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, ['uuid']),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ // TODO: @adunsulag introduce a last_modified date field to the care plan table as we don't track this anywhere
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['creation_date']);
+ }
+
/**
* Parses an OpenEMR record, returning the equivalent FHIR Resource
*
@@ -73,7 +81,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$fhirMeta = new FHIRMeta();
$fhirMeta->setVersionId('1');
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['creation_date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['creation_date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$carePlanResource->setMeta($fhirMeta);
$fhirId = new FHIRId();
diff --git a/src/Services/FHIR/FhirCareTeamService.php b/src/Services/FHIR/FhirCareTeamService.php
index 46fcd3d084f..663cae8b8e6 100644
--- a/src/Services/FHIR/FhirCareTeamService.php
+++ b/src/Services/FHIR/FhirCareTeamService.php
@@ -24,6 +24,7 @@
use OpenEMR\Services\FHIR\Traits\FhirBulkExportDomainResourceTrait;
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -67,9 +68,15 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'status' => new FhirSearchParameterDefinition('status', SearchFieldType::TOKEN, ['care_team_status']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
/**
* Parses an OpenEMR careTeam record, returning the equivalent FHIR CareTeam Resource
*
@@ -83,7 +90,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$fhirMeta = new FHIRMeta();
$fhirMeta->setVersionId('1');
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$careTeamResource->setMeta($fhirMeta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirConditionService.php b/src/Services/FHIR/FhirConditionService.php
index 874485b5ab9..09e902751d1 100644
--- a/src/Services/FHIR/FhirConditionService.php
+++ b/src/Services/FHIR/FhirConditionService.php
@@ -14,6 +14,7 @@
use OpenEMR\Services\FHIR\Traits\FhirBulkExportDomainResourceTrait;
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -58,9 +59,15 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('condition_uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated_time']);
+ }
+
/**
* Parses an OpenEMR condition record, returning the equivalent FHIR Condition Resource
*
@@ -74,7 +81,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['last_updated_time'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated_time']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$conditionResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirCoverageService.php b/src/Services/FHIR/FhirCoverageService.php
index 2d4483f8f9c..7ecd6030e87 100644
--- a/src/Services/FHIR/FhirCoverageService.php
+++ b/src/Services/FHIR/FhirCoverageService.php
@@ -6,6 +6,7 @@
use OpenEMR\FHIR\R4\FHIRElement\FHIRCoding;
use OpenEMR\FHIR\R4\FHIRElement\FHIRCode;
use OpenEMR\FHIR\R4\FHIRElement\FHIRId;
+use OpenEMR\FHIR\R4\FHIRElement\FHIRMeta;
use OpenEMR\FHIR\R4\FHIRElement\FHIRReference;
use OpenEMR\FHIR\R4\FHIRDomainResource\FHIRCoverage;
use OpenEMR\Services\FHIR\FhirServiceBase;
@@ -13,6 +14,7 @@
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\InsuranceService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -53,10 +55,16 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'payor' => new FhirSearchParameterDefinition('payor', SearchFieldType::TOKEN, ['provider']),
- '_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)])
+ '_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
/**
* Parses an OpenEMR Insurance record, returning the equivalent FHIR Coverage Resource
*
@@ -67,7 +75,13 @@ protected function loadSearchParameters()
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$coverageResource = new FHIRCoverage();
- $meta = array('versionId' => '1', 'lastUpdated' => UtilsService::getDateFormattedAsUTC());
+ $meta = new FHIRMeta();
+ $meta->setVersionId('1');
+ if (!empty($dataRecord['date'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$coverageResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirDeviceService.php b/src/Services/FHIR/FhirDeviceService.php
index b9eb9560830..d65b79fa2a7 100644
--- a/src/Services/FHIR/FhirDeviceService.php
+++ b/src/Services/FHIR/FhirDeviceService.php
@@ -14,12 +14,14 @@
use OpenEMR\FHIR\R4\FHIRDomainResource\FHIRDevice;
use OpenEMR\FHIR\R4\FHIRElement\FHIRDateTime;
use OpenEMR\FHIR\R4\FHIRElement\FHIRId;
+use OpenEMR\FHIR\R4\FHIRElement\FHIRMeta;
use OpenEMR\FHIR\R4\FHIRResource\FHIRDevice\FHIRDeviceUdiCarrier;
use OpenEMR\Services\DeviceService;
use OpenEMR\Services\FHIR\Traits\BulkExportSupportAllOperationsTrait;
use OpenEMR\Services\FHIR\Traits\FhirBulkExportDomainResourceTrait;
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -52,9 +54,15 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['modifydate']);
+ }
+
/**
* Parses an OpenEMR data record, returning the equivalent FHIR Resource
*
@@ -66,7 +74,14 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$device = new FHIRDevice();
- $device->setMeta(UtilsService::createFhirMeta('1', UtilsService::getDateFormattedAsUTC()));
+ $fhirMeta = new FHIRMeta();
+ $fhirMeta->setVersionId('1');
+ if (!empty($dataRecord['modifydate'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['modifydate']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $device->setMeta($fhirMeta);
$id = new FHIRId();
$id->setValue($dataRecord['uuid']);
diff --git a/src/Services/FHIR/FhirDiagnosticReportService.php b/src/Services/FHIR/FhirDiagnosticReportService.php
index 8aacf715a01..38097e641b8 100644
--- a/src/Services/FHIR/FhirDiagnosticReportService.php
+++ b/src/Services/FHIR/FhirDiagnosticReportService.php
@@ -20,6 +20,7 @@
use OpenEMR\Services\FHIR\Traits\MappedServiceCodeTrait;
use OpenEMR\Services\FHIR\Traits\PatientSearchTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldException;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
@@ -50,11 +51,19 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'code' => new FhirSearchParameterDefinition('code', SearchFieldType::TOKEN, ['code']),
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
+ // shouldn't be a problem if date and _lastUpdated are provided as it will just be ignored with duplicate WHERE clause conditions
+ // TODO: @adunsulag test this assumption to make sure it is correct
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
/**
* Retrieves all of the fhir observation resources mapped to the underlying openemr data elements.
* @param $fhirSearchParameters The FHIR resource search parameters
diff --git a/src/Services/FHIR/FhirDocumentReferenceService.php b/src/Services/FHIR/FhirDocumentReferenceService.php
index 7c163097d12..78b63e84e7b 100644
--- a/src/Services/FHIR/FhirDocumentReferenceService.php
+++ b/src/Services/FHIR/FhirDocumentReferenceService.php
@@ -20,6 +20,7 @@
use OpenEMR\Services\FHIR\Traits\MappedServiceCodeTrait;
use OpenEMR\Services\FHIR\Traits\PatientSearchTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldException;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
@@ -54,12 +55,20 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'type' => new FhirSearchParameterDefinition('type', SearchFieldType::TOKEN, ['type']),
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
+ // shouldn't be a problem if date and _lastUpdated are provided as it will just be ignored with duplicate WHERE clause conditions
+ // TODO: @adunsulag test this assumption to make sure it is correct
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
// it will search all the services, but since we are only grabbing a single id this should be relatively fast
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
/**
* Retrieves all of the fhir observation resources mapped to the underlying openemr data elements.
* @param $fhirSearchParameters The FHIR resource search parameters
diff --git a/src/Services/FHIR/FhirEncounterService.php b/src/Services/FHIR/FhirEncounterService.php
index 0bfada182ee..5cccdb6ca4a 100644
--- a/src/Services/FHIR/FhirEncounterService.php
+++ b/src/Services/FHIR/FhirEncounterService.php
@@ -39,6 +39,7 @@
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\FHIR\Traits\PatientSearchTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -95,10 +96,15 @@ protected function loadSearchParameters()
),
'patient' => $this->getPatientContextSearchField(),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
- '_lastUpdated' => new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_update'])
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_update']);
+ }
+
/**
* Parses an OpenEMR patient record, returning the equivalent FHIR Patient Resource
* https://build.fhir.org/ig/HL7/US-Core-R4/StructureDefinition-us-core-encounter-definitions.html
diff --git a/src/Services/FHIR/FhirGoalService.php b/src/Services/FHIR/FhirGoalService.php
index fc523b76a3d..2ee3c321b89 100644
--- a/src/Services/FHIR/FhirGoalService.php
+++ b/src/Services/FHIR/FhirGoalService.php
@@ -25,6 +25,7 @@
use OpenEMR\Services\FHIR\Traits\FhirBulkExportDomainResourceTrait;
use OpenEMR\Services\FHIR\Traits\FhirServiceBaseEmptyTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -59,15 +60,22 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
// note even though we label this as a uuid, it is a SURROGATE UID because of the nature of how goals are stored
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, ['uuid']),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ // TODO: @adunsulag introduce a last_modified date field to the care plan table as we don't track this anywhere
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['creation_date']);
+ }
+
/**
* Parses an OpenEMR careTeam record, returning the equivalent FHIR CareTeam Resource
*
* @param array $dataRecord The source OpenEMR data record
* @param boolean $encode Indicates if the returned resource is encoded into a string. Defaults to false.
- * @return FHIRCareTeam
+ * @return FHIRGoal
*/
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
@@ -75,7 +83,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$fhirMeta = new FHIRMeta();
$fhirMeta->setVersionId('1');
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['creation_date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['creation_date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$goal->setMeta($fhirMeta);
$fhirId = new FHIRId();
diff --git a/src/Services/FHIR/FhirGroupService.php b/src/Services/FHIR/FhirGroupService.php
index ab1b3c7d854..5f258fafb7f 100644
--- a/src/Services/FHIR/FhirGroupService.php
+++ b/src/Services/FHIR/FhirGroupService.php
@@ -19,6 +19,7 @@
use OpenEMR\Services\FHIR\Traits\MappedServiceTrait;
use OpenEMR\Services\FHIR\Traits\PatientSearchTrait;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldException;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
@@ -48,9 +49,15 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
/**
* Retrieves all of the fhir observation resources mapped to the underlying openemr data elements.
* @param $fhirSearchParameters The FHIR resource search parameters
diff --git a/src/Services/FHIR/FhirImmunizationService.php b/src/Services/FHIR/FhirImmunizationService.php
index b00d0a859e8..9f4db5a09ab 100644
--- a/src/Services/FHIR/FhirImmunizationService.php
+++ b/src/Services/FHIR/FhirImmunizationService.php
@@ -62,9 +62,15 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('uuid', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['update_date']);
+ }
+
/**
* Parses an OpenEMR immunization record, returning the equivalent FHIR Immunization Resource
*
@@ -78,7 +84,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['update_date'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['update_date']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$immunizationResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirLocationService.php b/src/Services/FHIR/FhirLocationService.php
index f3c23e97931..9778bc06c29 100644
--- a/src/Services/FHIR/FhirLocationService.php
+++ b/src/Services/FHIR/FhirLocationService.php
@@ -59,10 +59,16 @@ public function __construct()
protected function loadSearchParameters()
{
return [
- '_id' => new FhirSearchParameterDefinition('uuid', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)])
+ '_id' => new FhirSearchParameterDefinition('uuid', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
/**
* Parses an OpenEMR location record, returning the equivalent FHIR Location Resource
*
@@ -76,7 +82,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$locationResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirMedicationRequestService.php b/src/Services/FHIR/FhirMedicationRequestService.php
index 0c52b04e44f..674804743af 100644
--- a/src/Services/FHIR/FhirMedicationRequestService.php
+++ b/src/Services/FHIR/FhirMedicationRequestService.php
@@ -109,9 +109,15 @@ protected function loadSearchParameters()
'intent' => new FhirSearchParameterDefinition('intent', SearchFieldType::TOKEN, ['intent']),
'status' => new FhirSearchParameterDefinition('status', SearchFieldType::TOKEN, ['status']),
'_id' => new FhirSearchParameterDefinition('uuid', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date_modified']);
+ }
+
/**
* Parses an OpenEMR prescription record, returning the equivalent FHIR Patient Resource
*
@@ -125,7 +131,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['date_modified'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date_modified']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$medRequestResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirMedicationService.php b/src/Services/FHIR/FhirMedicationService.php
index 864c1ba6a98..9adbf598125 100644
--- a/src/Services/FHIR/FhirMedicationService.php
+++ b/src/Services/FHIR/FhirMedicationService.php
@@ -48,9 +48,15 @@ protected function loadSearchParameters()
{
return [
'_id' => new FhirSearchParameterDefinition('uuid', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['drug_last_updated']);
+ }
+
/**
* Parses an OpenEMR medication record, returning the equivalent FHIR Medication Resource
*
@@ -64,7 +70,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['drug_last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['drug_last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$medicationResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirObservationService.php b/src/Services/FHIR/FhirObservationService.php
index f3af4cef617..493d390cf7e 100644
--- a/src/Services/FHIR/FhirObservationService.php
+++ b/src/Services/FHIR/FhirObservationService.php
@@ -76,9 +76,15 @@ protected function loadSearchParameters(): array
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, ['uuid']),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date_modified']);
+ }
+
/**
* Retrieves all of the fhir observation resources mapped to the underlying openemr data elements.
* @param $fhirSearchParameters The FHIR resource search parameters
diff --git a/src/Services/FHIR/FhirOrganizationService.php b/src/Services/FHIR/FhirOrganizationService.php
index cba23a2ef86..e600a56d5a3 100644
--- a/src/Services/FHIR/FhirOrganizationService.php
+++ b/src/Services/FHIR/FhirOrganizationService.php
@@ -81,10 +81,16 @@ public function getSearchParams()
'address-city' => new FhirSearchParameterDefinition('address-city', SearchFieldType::STRING, ['city']),
'address-postalcode' => new FhirSearchParameterDefinition('address-postalcode', SearchFieldType::STRING, ['postal_code', "zip"]),
'address-state' => new FhirSearchParameterDefinition('address-state', SearchFieldType::STRING, ['state']),
- 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name'])
+ 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name']),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
public function getOne($fhirResourceId, $puuidBind = null): ProcessingResult
{
return $this->getAll(['_id' => $fhirResourceId], $puuidBind);
diff --git a/src/Services/FHIR/FhirPatientService.php b/src/Services/FHIR/FhirPatientService.php
index 573fad5869d..cdde9982cdd 100644
--- a/src/Services/FHIR/FhirPatientService.php
+++ b/src/Services/FHIR/FhirPatientService.php
@@ -90,6 +90,8 @@ class FhirPatientService extends FhirServiceBase implements IFhirExportableResou
const FIELD_NAME_GENDER = 'sex';
+ private ?array $searchParameters = null;
+
public function __construct()
{
parent::__construct();
@@ -143,11 +145,16 @@ protected function loadSearchParameters()
'given' => new FhirSearchParameterDefinition('given', SearchFieldType::STRING, ['fname', 'mname']),
'phone' => new FhirSearchParameterDefinition('phone', SearchFieldType::TOKEN, ['phone_home', 'phone_biz', 'phone_cell']),
'telecom' => new FhirSearchParameterDefinition('telecom', SearchFieldType::TOKEN, ['email','email_direct', 'phone_home', 'phone_biz', 'phone_cell']),
- '_lastUpdated' => new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
'generalPractitioner' => new FhirSearchParameterDefinition('generalPractitioner', SearchFieldType::REFERENCE, ['provider_uuid'])
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
/**
* Parses an OpenEMR patient record, returning the equivalent FHIR Patient Resource
*
@@ -161,8 +168,8 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- if (!empty($dataRecord['date'])) {
- $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date']));
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
} else {
$meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
}
@@ -172,7 +179,7 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$id = new FHIRId();
$id->setValue($dataRecord['uuid']);
$patientResource->setId($id);
- $patientResource->setDeceasedBoolean($dataRecord[ 'deceasedDate' ] != null);
+ $patientResource->setDeceasedBoolean($dataRecord[ 'deceased_date' ] != null);
$this->parseOpenEMRPatientSummaryText($patientResource, $dataRecord);
$this->parseOpenEMRPatientName($patientResource, $dataRecord);
diff --git a/src/Services/FHIR/FhirPersonService.php b/src/Services/FHIR/FhirPersonService.php
index b5dafac4a4b..025a6fb6d86 100644
--- a/src/Services/FHIR/FhirPersonService.php
+++ b/src/Services/FHIR/FhirPersonService.php
@@ -67,10 +67,16 @@ protected function loadSearchParameters()
'given' => new FhirSearchParameterDefinition('given', SearchFieldType::STRING, ["fname", "mname"]),
'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ["users.title", "fname", "mname", "lname"]),
- '_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)])
+ '_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
/**
* Parses an OpenEMR user record, returning the equivalent FHIR Person Resource
@@ -85,7 +91,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$person->setMeta($meta);
$person->setActive($dataRecord['active'] == "1" ? true : false);
diff --git a/src/Services/FHIR/FhirPractitionerRoleService.php b/src/Services/FHIR/FhirPractitionerRoleService.php
index 4e75a93db6c..23b72168bc3 100644
--- a/src/Services/FHIR/FhirPractitionerRoleService.php
+++ b/src/Services/FHIR/FhirPractitionerRoleService.php
@@ -46,10 +46,21 @@ protected function loadSearchParameters()
return [
'specialty' => new FhirSearchParameterDefinition('specialty', SearchFieldType::TOKEN, ['specialty_code']),
'practitioner' => new FhirSearchParameterDefinition('practitioner', SearchFieldType::STRING, ['user_name']),
- '_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('providers.facility_role_uuid', ServiceField::TYPE_UUID)])
+ '_id' => new FhirSearchParameterDefinition(
+ '_id',
+ SearchFieldType::TOKEN,
+ [new ServiceField('providers.facility_role_uuid', ServiceField::TYPE_UUID)]
+ ),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ // we just go off of role as specialty gets updated at the same time
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['role_last_updated']);
+ }
+
/**
* Parses an OpenEMR practitionerRole record, returning the equivalent FHIR PractitionerRole Resource
*
@@ -63,7 +74,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['role_last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['role_last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$practitionerRoleResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/FhirPractitionerService.php b/src/Services/FHIR/FhirPractitionerService.php
index 41fc9539cbb..7652ff43e6c 100644
--- a/src/Services/FHIR/FhirPractitionerService.php
+++ b/src/Services/FHIR/FhirPractitionerService.php
@@ -64,10 +64,18 @@ protected function loadSearchParameters()
'address-state' => new FhirSearchParameterDefinition('address-state', SearchFieldType::STRING, ['state']),
'family' => new FhirSearchParameterDefinition('family', SearchFieldType::STRING, ["lname"]),
'given' => new FhirSearchParameterDefinition('given', SearchFieldType::STRING, ["fname", "mname"]),
- 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ["title", "fname", "mname", "lname"])
+ 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ["title", "fname", "mname", "lname"]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ // TODO: @adunsulag I don't like specifying full table name here in the search field, but I don't see a way around it
+ // right now... if we ever need to implement better escaping this is an issue.
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['users.last_updated']);
+ }
+
/**
* Parses an OpenEMR practitioner record, returning the equivalent FHIR Practitioner Resource
@@ -82,7 +90,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$practitionerResource->setMeta($meta);
$practitionerResource->setActive($dataRecord['active'] == "1" ? true : false);
diff --git a/src/Services/FHIR/FhirProcedureService.php b/src/Services/FHIR/FhirProcedureService.php
index 0ed8ce946fa..700d061f34a 100644
--- a/src/Services/FHIR/FhirProcedureService.php
+++ b/src/Services/FHIR/FhirProcedureService.php
@@ -70,9 +70,15 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['report_date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
/**
* Retrieves all of the fhir observation resources mapped to the underlying openemr data elements.
diff --git a/src/Services/FHIR/FhirProvenanceService.php b/src/Services/FHIR/FhirProvenanceService.php
index d007cf4fdaa..317eab65212 100644
--- a/src/Services/FHIR/FhirProvenanceService.php
+++ b/src/Services/FHIR/FhirProvenanceService.php
@@ -230,7 +230,7 @@ public function getAll($fhirSearchParameters, $puuidBind = null): ProcessingResu
if (!empty($fhirSearchParameters['_id'])) {
$fhirSearchResult = $this->getProvenanceRecordsForId($fhirSearchParameters['_id'], $puuidBind);
} else {
- $fhirSearchResult = $this->getAllProvenanceRecordsFromServices($puuidBind);
+ $fhirSearchResult = $this->getAllProvenanceRecordsFromServices($fhirSearchParameters, $puuidBind);
}
} catch (SearchFieldException $exception) {
$systemLogger = new SystemLogger();
@@ -242,13 +242,15 @@ public function getAll($fhirSearchParameters, $puuidBind = null): ProcessingResu
return $fhirSearchResult;
}
- private function getAllProvenanceRecordsFromServices($puuidBind = null)
+ private function getAllProvenanceRecordsFromServices(array $fhirSearchParameters, $puuidBind = null)
{
$processingResult = new ProcessingResult();
if (empty($this->serviceLocator)) {
(new SystemLogger())->errorLogCaller("class was not properly configured with the service locator");
}
+ $searchParams = $this->filterSupportedSearchParams($fhirSearchParameters);
+
// we only return provenances for
$servicesByResource = $this->serviceLocator->findServices(IResourceUSCIGProfileService::class);
@@ -258,13 +260,13 @@ private function getAllProvenanceRecordsFromServices($puuidBind = null)
continue;
}
try {
- $this->addAllProvenanceRecordsForService($processingResult, $service, [], $puuidBind);
+ $this->addAllProvenanceRecordsForService($processingResult, $service, $searchParams, $puuidBind);
} catch (SearchFieldException $ex) {
$systemLogger = new SystemLogger();
- $systemLogger->error(get_class($this) . "->getAll() exception thrown", ['message' => $exception->getMessage(),
- 'field' => $exception->getField(), 'trace' => $exception->getTraceAsString()]);
+ $systemLogger->error(get_class($this) . "->getAll() exception thrown", ['message' => $ex->getMessage(),
+ 'field' => $ex->getField(), 'trace' => $ex->getTraceAsString()]);
// put our exception information here
- $processingResult->setValidationMessages([$exception->getField() => $exception->getMessage()]);
+ $processingResult->setValidationMessages([$ex->getField() => $ex->getMessage()]);
return $processingResult;
} catch (Exception $ex) {
$systemLogger = new SystemLogger();
@@ -333,57 +335,6 @@ private function getProvenanceRecordsForId($id, $puuidBind)
return $processingResult;
}
- /**
- * Searches for OpenEMR records using OpenEMR search parameters
- * @param openEMRSearchParameters OpenEMR search fields
- * @param $puuidBind - Optional variable to only allow visibility of the patient with this puuid.
- * @return OpenEMR records
- */
- protected function searchForOpenEMRRecords($openEMRSearchParameters): ProcessingResult
- {
- $patientToken = $openEMRSearchParameters['patient'] ?? new TokenSearchField('patient', []);
- $patientBinding = !empty($patientToken->getValues()) ? $patientToken->getValues()[0]->getCode() : null;
- /**
- * @var TokenSearchField
- */
- $id = $openEMRSearchParameters['_id'] ?? new TokenSearchField('_id', []);
- $processingResult = new ProcessingResult();
- foreach ($id->getValues() as $value) {
- // should be in format of ResourceType/uuid
- $code = $value->getCode() ?? "";
- try {
- $idParts = explode(":", $code);
- $resourceName = array_shift($idParts);
-
- $innerId = implode(":", $idParts);
- $className = RestControllerHelper::FHIR_SERVICES_NAMESPACE . $resourceName . "Service";
- if (class_exists($className)) {
- $newServiceClass = new $className();
- if ($newServiceClass instanceof IResourceReadableService) {
- $searchParams = [
- '_id' => $innerId
- ,'_revinclude' => 'Provenance:target'
- ];
- $results = $newServiceClass->getAll($searchParams, $patientBinding);
- if ($results->hasData()) {
- foreach ($results->getData() as $datum) {
- if ($datum instanceof FHIRProvenance) {
- $processingResult->addData($datum);
- }
- }
- } else {
- $processingResult->addProcessingResult($results);
- }
- }
- }
- } catch (\Exception $exception) {
- // TODO: @adunsulag log the exception
- $processingResult->addInternalError("Server error occurred in returning provenance for _id " . $code);
- }
- }
- return $processingResult;
- }
-
/**
* Returns the Canonical URIs for the FHIR resource for each of the US Core Implementation Guide Profiles that the
* resource implements. Most resources have only one profile, but several like DiagnosticReport and Observation
@@ -416,7 +367,9 @@ public function getSurrogateKeyForResource(FHIRDomainResource $resource)
"Resource missing required Meta->lastUpdated field",
['resource' => $resource->getId(), 'type' => $resource->get_fhirElementName()]
);
- } else {
+ // patients were the only ones who actually were tracking a valid last updated date instead of the most
+ // current timestamp for V1 so we need to check for that, everything else is V2 as last updated wasn't really tracked.
+ } else if ($resource->get_fhirElementName() === 'Patient') {
// we use DATE_ATOM to get an ISO8601 compatible date as DATE_ISO8601 does not actually conform to an ISO8601 date for php legacy purposes
$lastUpdated = \DateTime::createFromFormat(DATE_ATOM, $resource->getMeta()->getLastUpdated());
@@ -490,23 +443,43 @@ public function export(ExportStreamWriter $writer, ExportJob $job, $lastResource
$searchParams[$searchField->getName()] = implode(",", $patientUuids);
}
}
-
- $serviceResult = $service->getAll($searchParams);
- // now loop through and grab all of our provenance resources
- if ($serviceResult->hasData()) {
- foreach ($serviceResult->getData() as $record) {
- if (!($record instanceof FHIRDomainResource)) {
- throw new ExportException(self::class . " returned records that are not a valid fhir resource type for this class", 0, $lastResourceIdExported);
- }
- // we only want to write out provenance records
- if (!($record instanceof FHIRProvenance)) {
- continue;
+ $searchParams['_lastUpdated'] = $job->getResourceIncludeSearchParamValue();
+ try {
+ $serviceResult = $service->getAll($searchParams);
+ // now loop through and grab all of our provenance resources
+ if ($serviceResult->hasData()) {
+ foreach ($serviceResult->getData() as $record) {
+ if (!($record instanceof FHIRDomainResource)) {
+ throw new ExportException(self::class . " returned records that are not a valid fhir resource type for this class", 0, $lastResourceIdExported);
+ }
+ // we only want to write out provenance records
+ if (!($record instanceof FHIRProvenance)) {
+ continue;
+ }
+ $writer->append($record);
+ $lastResourceIdExported = $record->getId();
}
- $writer->append($record);
- $lastResourceIdExported = $record->getId();
}
+ } catch (SearchFieldException $exception) {
+ $message = $exception->getMessage() . " Search Field " . $exception->getField();
+ throw new ExportException($message, 0, $lastResourceIdExported);
}
}
}
}
+
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ // nothing to really do here as we handle it internally in the export operation
+ return null;
+ }
+
+ private function filterSupportedSearchParams(array $fhirSearchParameters)
+ {
+ $supportedParams = [];
+ if (isset($fhirSearchParameters['_lastUpdated'])) {
+ $supportedParams['_lastUpdated'] = $fhirSearchParameters['_lastUpdated'];
+ }
+ return $supportedParams;
+ }
}
diff --git a/src/Services/FHIR/FhirValueSetService.php b/src/Services/FHIR/FhirValueSetService.php
index 31f26b4df7f..d9659511b73 100644
--- a/src/Services/FHIR/FhirValueSetService.php
+++ b/src/Services/FHIR/FhirValueSetService.php
@@ -85,13 +85,13 @@ class FhirValueSetService extends FhirServiceBase implements IResourceUSCIGProfi
*/
-
const USCGI_PROFILE_URI = 'http://hl7.org/fhir/StructureDefinition/shareablevalueset';
const APPOINTMENT_TYPE = 'appointment-type';
public function __construct()
{
parent::__construct();
+ // TODO: @adunsulag we need to look at adding a mapping service here in order to get our value sets out.
$this->appointmentService = new AppointmentService();
$this->listOptionService = new ListService();
}
@@ -103,9 +103,20 @@ protected function loadSearchParameters()
{
return [
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('id', ServiceField::TYPE_STRING)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['sublist_updated_date', 'last_updated']);
+ }
+
+ private function getLastModifiedSearchFieldForAppointmentCategories()
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['pc_last_updated']);
+ }
+
/**
* Retrieves all of the fhir observation resources mapped to the underlying openemr data elements.
* @param $fhirSearchParameters The FHIR resource search parameters
@@ -115,74 +126,14 @@ public function getAll($fhirSearchParameters, $puuidBind = null): ProcessingResu
{
$fhirSearchResult = new ProcessingResult();
try {
- if (
- !isset($fhirSearchParameters[ '_id' ])
- // could be array (AND) or comma-delimited string value (OR)
- // check array first but should only be len 1 ("AND", becuase cannot be 2 simultaneous)
- || ( is_array($fhirSearchParameters[ '_id' ])
- && count($fhirSearchParameters[ '_id' ]) == 1
- && $fhirSearchParameters[ '_id' ][ 0 ] == self::APPOINTMENT_TYPE )
- // and string which could be comma-delimiter OR of exploded values
- || ( !is_array($fhirSearchParameters[ '_id' ])
- && in_array(self::APPOINTMENT_TYPE, explode(",", $fhirSearchParameters[ '_id' ])) )
- ) {
- $calendarCategories = $this->appointmentService->getCalendarCategories();
- $valueSet = new FHIRValueSet();
- $valueSet->setId(self::APPOINTMENT_TYPE);
- $compose = new FHIRValueSetCompose();
- $include = new FHIRValueSetInclude();
- foreach ($calendarCategories as $category) {
- if ($category["pc_cattype"] != 0) {
- continue; // only cat_type==0
- }
- $concept = new FHIRValueSetConcept();
- $code = new FHIRCode();
- $code->setValue($category[ "pc_constant_id"]);
- $concept->setCode($code);
- $concept->setDisplay($category[ "pc_catname" ]);
- $include->addConcept($concept);
- }
- $compose->addInclude($include);
- $valueSet->setCompose($compose);
- $fhirSearchResult->addData($valueSet);
+ // we don't really deal with provenance for ValueSet pieces so we will ignore this property
+ if (isset($fhirSearchParameters['_revinclude'])) {
+ unset($fhirSearchParameters['_revinclude']);
}
- // Now the same for list_options selected in $listNames
- $list_ids = $this->listOptionService->getListIds();
- foreach ($list_ids as $listName) {
- if (
- isset($fhirSearchParameters[ '_id' ])
- // could be array (AND) or comma-delimited string value (OR)
- // check array first but should only be len 1 ("AND", becuase cannot be 2 simultaneous)
- && ( ( is_array($fhirSearchParameters[ '_id' ])
- && count($fhirSearchParameters[ '_id' ]) == 1
- && $fhirSearchParameters[ '_id' ][ 0 ] != $listName )
- // and string which could be comma-delimiter OR of exploded values
- || ( !is_array($fhirSearchParameters[ '_id' ])
- && !in_array($listName, explode(",", $fhirSearchParameters[ '_id' ])) ) )
- ) {
- continue;
- }
- $options = $this->listOptionService->getOptionsByListName($listName); // does not return title
- if (count($options) == 0) {
- continue;
- }
- $valueSet = new FHIRValueSet();
- $valueSet->setId($listName);
- $compose = new FHIRValueSetCompose();
- $include = new FHIRValueSetInclude();
- foreach ($options as $option) {
- $concept = new FHIRValueSetConcept();
- $code = new FHIRCode();
- $code->setValue($option[ "option_id"]);
- $concept->setCode($code);
- $concept->setDisplay($option[ "title" ]);
- $include->addConcept($concept);
- }
- $compose->addInclude($include);
- $valueSet->setCompose($compose);
- $fhirSearchResult->addData($valueSet);
- }
+ $this->addAppointmentCategoriesValueSetForSearch($fhirSearchResult, $fhirSearchParameters);
+
+ $this->addListOptionsValueSetsForSearch($fhirSearchResult, $fhirSearchParameters, $puuidBind);
} catch (SearchFieldException $exception) {
(new SystemLogger())->errorLogCaller("search exception thrown", ['message' => $exception->getMessage(),
'field' => $exception->getField()]);
@@ -204,4 +155,82 @@ function getProfileURIs(): array
{
return [self::USCGI_PROFILE_URI];
}
+
+ private function addAppointmentCategoriesValueSetForSearch(ProcessingResult $fhirSearchResult, array $fhirSearchParameters, string $puuidBind = null)
+ {
+ $this->getSearchFieldFactory()->setSearchFieldDefinition('_lastUpdated', $this->getLastModifiedSearchFieldForAppointmentCategories());
+ $oeSearchParameters = $this->createOpenEMRSearchParameters($fhirSearchParameters, $puuidBind);
+ if (
+ !isset($oeSearchParameters['_id'])
+ // could be array (AND) or comma-delimited string value (OR)
+ // check array first but should only be len 1 ("AND", becuase cannot be 2 simultaneous)
+ || $oeSearchParameters['_id']->hasCodeValue(self::APPOINTMENT_TYPE)
+ ) {
+ if (!isset($oeSearchParameters['_id'])) {
+ // if we have any match on categories we want to return everything... hate the double db call
+ // but rather than mess with a complex query we will just do it this way
+ $processingResult = $this->appointmentService->searchCalendarCategories($oeSearchParameters);
+ // nothing to do here as we have no categories matching so we return
+ if (!$processingResult->hasData()) {
+ return $fhirSearchResult;
+ }
+ }
+ $calendarCategories = $this->appointmentService->getCalendarCategories();
+ $valueSet = new FHIRValueSet();
+ $valueSet->setId(self::APPOINTMENT_TYPE);
+ $compose = new FHIRValueSetCompose();
+ $include = new FHIRValueSetInclude();
+ foreach ($calendarCategories as $category) {
+ if ($category["pc_cattype"] != 0) {
+ continue; // only cat_type==0
+ }
+ $concept = new FHIRValueSetConcept();
+ $code = new FHIRCode();
+ $code->setValue($category["pc_constant_id"]);
+ $concept->setCode($code);
+ $concept->setDisplay($category["pc_catname"]);
+ $include->addConcept($concept);
+ }
+ $compose->addInclude($include);
+ $valueSet->setCompose($compose);
+ $fhirSearchResult->addData($valueSet);
+ }
+ return $fhirSearchResult;
+ }
+
+ private function addListOptionsValueSetsForSearch(ProcessingResult $fhirSearchResult, array $fhirSearchParameters, ?string $puuidBind = null)
+ {
+ $this->getSearchFieldFactory()->setSearchFieldDefinition('_lastUpdated', $this->getLastModifiedSearchField());
+ $oeSearchParameters = $this->createOpenEMRSearchParameters($fhirSearchParameters, $puuidBind);
+
+ // Now the same for list_options selected in $listNames
+ $listsResult = $this->listOptionService->searchLists($oeSearchParameters);
+ if (!$listsResult->hasData()) {
+ $fhirSearchResult->addProcessingResult($listsResult);
+ return $fhirSearchResult;
+ }
+ foreach ($listsResult->getData() as $listRecord) {
+ $listName = $listRecord["option_id"];
+ $options = $this->listOptionService->getOptionsByListName($listName); // does not return title
+ if (count($options) == 0) {
+ continue;
+ }
+ $valueSet = new FHIRValueSet();
+ $valueSet->setId($listName);
+ $compose = new FHIRValueSetCompose();
+ $include = new FHIRValueSetInclude();
+ foreach ($options as $option) {
+ $concept = new FHIRValueSetConcept();
+ $code = new FHIRCode();
+ $code->setValue($option["option_id"]);
+ $concept->setCode($code);
+ $concept->setDisplay($option["title"]);
+ $include->addConcept($concept);
+ }
+ $compose->addInclude($include);
+ $valueSet->setCompose($compose);
+ $fhirSearchResult->addData($valueSet);
+ }
+ return $fhirSearchResult;
+ }
}
diff --git a/src/Services/FHIR/Group/FhirPatientProviderGroupService.php b/src/Services/FHIR/Group/FhirPatientProviderGroupService.php
index 38d559edf5a..d66565e3b9b 100644
--- a/src/Services/FHIR/Group/FhirPatientProviderGroupService.php
+++ b/src/Services/FHIR/Group/FhirPatientProviderGroupService.php
@@ -20,6 +20,7 @@
use OpenEMR\Services\FHIR\UtilsService;
use OpenEMR\Services\GroupService;
use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
use OpenEMR\Services\Search\SearchFieldType;
use OpenEMR\Services\Search\ServiceField;
use OpenEMR\Validators\ProcessingResult;
@@ -45,9 +46,15 @@ protected function loadSearchParameters()
return [
'patient' => $this->getPatientContextSearchField(),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['patient_last_updated']);
+ }
+
protected function searchForOpenEMRRecords($openEMRSearchParameters): ProcessingResult
{
return $this->service->searchPatientProviderGroups($openEMRSearchParameters);
@@ -58,7 +65,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$fhirGroup = new FHIRGroup();
$fhirMeta = new FHIRMeta();
$fhirMeta->setVersionId("1");
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['last_modified_date'])) {
+ $fhirMeta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_modified_date']));
+ } else {
+ $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$fhirGroup->setMeta($fhirMeta);
$fhirGroup->setId($dataRecord['uuid']);
diff --git a/src/Services/FHIR/IFhirExportableResourceService.php b/src/Services/FHIR/IFhirExportableResourceService.php
index a008468fcfb..6c56871261c 100644
--- a/src/Services/FHIR/IFhirExportableResourceService.php
+++ b/src/Services/FHIR/IFhirExportableResourceService.php
@@ -18,6 +18,8 @@
use OpenEMR\FHIR\Export\ExportJob;
use OpenEMR\FHIR\Export\ExportStreamWriter;
use OpenEMR\FHIR\Export\ExportWillShutdownException;
+use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
interface IFhirExportableResourceService
{
@@ -59,4 +61,12 @@ function supportsGroupExport();
* @return bool true if this resource service should be called for a patient export operation, false otherwise
*/
function supportsPatientExport();
+
+ /**
+ * Returns the search field that represents the last modified date for the resource used in the export _since
+ * parameter for the export operation. If the resource does not support the _since parameter then this method
+ * will return null and the export should return ALL the resources for the resource service.
+ * @return ISearchField|null The search field that represents the last modified date for the resource
+ */
+ function getLastModifiedSearchField(): ?FhirSearchParameterDefinition;
}
diff --git a/src/Services/FHIR/Observation/FhirObservationLaboratoryService.php b/src/Services/FHIR/Observation/FhirObservationLaboratoryService.php
index b2f6aaad2df..61aa3bddc25 100644
--- a/src/Services/FHIR/Observation/FhirObservationLaboratoryService.php
+++ b/src/Services/FHIR/Observation/FhirObservationLaboratoryService.php
@@ -95,9 +95,15 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['report_date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('result_uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['report_date']);
+ }
+
/**
* Searches for OpenEMR records using OpenEMR search parameters
@@ -159,7 +165,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$observation = new FHIRObservation();
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['report_date'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['report_date']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$observation->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/Observation/FhirObservationSocialHistoryService.php b/src/Services/FHIR/Observation/FhirObservationSocialHistoryService.php
index fc64062dfc4..0ef55ca4d55 100644
--- a/src/Services/FHIR/Observation/FhirObservationSocialHistoryService.php
+++ b/src/Services/FHIR/Observation/FhirObservationSocialHistoryService.php
@@ -113,9 +113,15 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date']);
+ }
+
/**
* Inserts an OpenEMR record into the sytem.
@@ -269,7 +275,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$observation = new FHIRObservation();
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['date'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$observation->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/Observation/FhirObservationVitalsService.php b/src/Services/FHIR/Observation/FhirObservationVitalsService.php
index 0c59793ac4d..6109b130676 100644
--- a/src/Services/FHIR/Observation/FhirObservationVitalsService.php
+++ b/src/Services/FHIR/Observation/FhirObservationVitalsService.php
@@ -241,9 +241,15 @@ protected function loadSearchParameters()
'category' => new FhirSearchParameterDefinition('category', SearchFieldType::TOKEN, ['category']),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
/**
* Inserts an OpenEMR record into the sytem.
@@ -342,6 +348,7 @@ private function parseVitalsIntoObservationRecords(ProcessingResult $processingR
, "uuid" => UuidRegistry::uuidToString($uuidMappings[self::VITALS_PANEL_LOINC_CODE])
, "user_uuid" => $record['user_uuid']
, "date" => $record['date']
+ , "last_updated" => $record['last_updated']
];
foreach ($uuidMappings as $code => $uuid) {
if (!$this->isVitalSignPanelCodes($code)) { // we will skip over our vital signs code, and any pediatric stuff
@@ -365,6 +372,7 @@ private function parseVitalsIntoObservationRecords(ProcessingResult $processingR
, "user_uuid" => $record['user_uuid']
,"uuid" => UuidRegistry::uuidToString($uuidMappings[$code])
,"date" => $record['date']
+ , "last_updated" => $record['last_updated']
];
$columns = $this->getColumnsForCode($code);
@@ -421,7 +429,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$observation = new FHIRObservation();
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$observation->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/Organization/FhirOrganizationFacilityService.php b/src/Services/FHIR/Organization/FhirOrganizationFacilityService.php
index c8424c11a94..85fd9b93ddb 100644
--- a/src/Services/FHIR/Organization/FhirOrganizationFacilityService.php
+++ b/src/Services/FHIR/Organization/FhirOrganizationFacilityService.php
@@ -101,10 +101,16 @@ protected function loadSearchParameters()
'address-city' => new FhirSearchParameterDefinition('address-city', SearchFieldType::STRING, ['city']),
'address-postalcode' => new FhirSearchParameterDefinition('address-postalcode', SearchFieldType::STRING, ['postal_code', "zip"]),
'address-state' => new FhirSearchParameterDefinition('address-state', SearchFieldType::STRING, ['state']),
- 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name'])
+ 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name']),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
/**
* Searches for OpenEMR records using OpenEMR search parameters
* @param openEMRSearchParameters OpenEMR search fields
@@ -160,10 +166,14 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$organizationResource = new FHIROrganization();
- $fhirMeta = new FHIRMeta();
- $fhirMeta->setVersionId('1');
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $organizationResource->setMeta($fhirMeta);
+ $meta = new FHIRMeta();
+ $meta->setVersionId('1');
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $organizationResource->setMeta($meta);
// facilities have no active / inactive state
$organizationResource->setActive(true);
diff --git a/src/Services/FHIR/Organization/FhirOrganizationInsuranceService.php b/src/Services/FHIR/Organization/FhirOrganizationInsuranceService.php
index e5899daef0b..db6bdc6ff22 100644
--- a/src/Services/FHIR/Organization/FhirOrganizationInsuranceService.php
+++ b/src/Services/FHIR/Organization/FhirOrganizationInsuranceService.php
@@ -59,10 +59,16 @@ protected function loadSearchParameters()
'address-city' => new FhirSearchParameterDefinition('address-city', SearchFieldType::STRING, ['city']),
'address-postalcode' => new FhirSearchParameterDefinition('address-postalcode', SearchFieldType::STRING, ["zip"]),
'address-state' => new FhirSearchParameterDefinition('address-state', SearchFieldType::STRING, ['state']),
- 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name'])
+ 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name']),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
protected function searchForOpenEMRRecords($openEMRSearchParameters): ProcessingResult
{
if (!isset($openEMRSearchParameters['name'])) {
@@ -91,10 +97,14 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$organizationResource = new FHIROrganization();
- $fhirMeta = new FHIRMeta();
- $fhirMeta->setVersionId('1');
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $organizationResource->setMeta($fhirMeta);
+ $meta = new FHIRMeta();
+ $meta->setVersionId('1');
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $organizationResource->setMeta($meta);
$organizationResource->setActive($dataRecord['inactive'] == '0');
$narrativeText = trim($dataRecord['name'] ?? "");
diff --git a/src/Services/FHIR/Organization/FhirOrganizationProcedureProviderService.php b/src/Services/FHIR/Organization/FhirOrganizationProcedureProviderService.php
index 52aced40b2f..a7caed3aefa 100644
--- a/src/Services/FHIR/Organization/FhirOrganizationProcedureProviderService.php
+++ b/src/Services/FHIR/Organization/FhirOrganizationProcedureProviderService.php
@@ -55,10 +55,16 @@ protected function loadSearchParameters()
{
return [
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
- 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name'])
+ 'name' => new FhirSearchParameterDefinition('name', SearchFieldType::STRING, ['name']),
+ '_lastUpdated' => $this->getLastModifiedSearchField()
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['last_updated']);
+ }
+
protected function searchForOpenEMRRecords($openEMRSearchParameters): ProcessingResult
{
if (!isset($openEMRSearchParameters['name'])) {
@@ -82,10 +88,14 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$organizationResource = new FHIROrganization();
- $fhirMeta = new FHIRMeta();
- $fhirMeta->setVersionId('1');
- $fhirMeta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
- $organizationResource->setMeta($fhirMeta);
+ $meta = new FHIRMeta();
+ $meta->setVersionId('1');
+ if (!empty($dataRecord['last_updated'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['last_updated']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
+ $organizationResource->setMeta($meta);
$organizationResource->setActive($dataRecord['active'] == '1');
$narrativeText = trim($dataRecord['name'] ?? "");
diff --git a/src/Services/FHIR/Procedure/FhirProcedureOEProcedureService.php b/src/Services/FHIR/Procedure/FhirProcedureOEProcedureService.php
index 12507f24be4..233abb18d5e 100644
--- a/src/Services/FHIR/Procedure/FhirProcedureOEProcedureService.php
+++ b/src/Services/FHIR/Procedure/FhirProcedureOEProcedureService.php
@@ -72,9 +72,15 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['report_date']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('report_uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['report_date']);
+ }
+
/**
* Searches for OpenEMR records using OpenEMR search parameters
* @param openEMRSearchParameters OpenEMR search fields
@@ -110,13 +116,17 @@ protected function searchForOpenEMRRecords($openEMRSearchParameters): Processing
public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
{
$procedureResource = new FHIRProcedure();
+ $report = array_pop($dataRecord['reports']);
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($report['date'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($report['date']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$procedureResource->setMeta($meta);
- $report = array_pop($dataRecord['reports']);
$id = new FHIRId();
$id->setValue($report['uuid']);
diff --git a/src/Services/FHIR/Procedure/FhirProcedureSurgeryService.php b/src/Services/FHIR/Procedure/FhirProcedureSurgeryService.php
index 367e6df2e68..040e6a3f8e4 100644
--- a/src/Services/FHIR/Procedure/FhirProcedureSurgeryService.php
+++ b/src/Services/FHIR/Procedure/FhirProcedureSurgeryService.php
@@ -57,9 +57,15 @@ protected function loadSearchParameters()
'patient' => $this->getPatientContextSearchField(),
'date' => new FhirSearchParameterDefinition('date', SearchFieldType::DATETIME, ['begdate']),
'_id' => new FhirSearchParameterDefinition('_id', SearchFieldType::TOKEN, [new ServiceField('uuid', ServiceField::TYPE_UUID)]),
+ '_lastUpdated' => $this->getLastModifiedSearchField(),
];
}
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return new FhirSearchParameterDefinition('_lastUpdated', SearchFieldType::DATETIME, ['date_modified']);
+ }
+
/**
* Searches for OpenEMR records using OpenEMR search parameters
* @param openEMRSearchParameters OpenEMR search fields
@@ -85,7 +91,11 @@ public function parseOpenEMRRecord($dataRecord = array(), $encode = false)
$meta = new FHIRMeta();
$meta->setVersionId('1');
- $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ if (!empty($dataRecord['date_modified'])) {
+ $meta->setLastUpdated(UtilsService::getLocalDateAsUTC($dataRecord['date_modified']));
+ } else {
+ $meta->setLastUpdated(UtilsService::getDateFormattedAsUTC());
+ }
$procedureResource->setMeta($meta);
$id = new FHIRId();
diff --git a/src/Services/FHIR/Traits/FhirBulkExportDomainResourceTrait.php b/src/Services/FHIR/Traits/FhirBulkExportDomainResourceTrait.php
index 4629fd88247..68c408fc4b1 100644
--- a/src/Services/FHIR/Traits/FhirBulkExportDomainResourceTrait.php
+++ b/src/Services/FHIR/Traits/FhirBulkExportDomainResourceTrait.php
@@ -22,6 +22,10 @@
use OpenEMR\FHIR\R4\FHIRResource\FHIRDomainResource;
use OpenEMR\Services\FHIR\IPatientCompartmentResourceService;
use OpenEMR\Services\FHIR\IResourceReadableService;
+use OpenEMR\Services\Search\DateSearchField;
+use OpenEMR\Services\Search\FhirSearchParameterDefinition;
+use OpenEMR\Services\Search\ISearchField;
+use OpenEMR\Services\Search\SearchComparator;
use OpenEMR\Services\Search\TokenSearchField;
trait FhirBulkExportDomainResourceTrait
@@ -68,6 +72,10 @@ public function export(ExportStreamWriter $writer, ExportJob $job, $lastResource
$searchParams[$searchField->getName()] = implode(",", $patientUuids);
}
}
+ $searchField = $this->getLastModifiedSearchField();
+ if ($searchField !== null) {
+ $searchParams[$searchField->getName()] = $job->getResourceIncludeSearchParamValue();
+ }
// if we can grab our list of patient ids from the export job...
$processingResult = $this->getAll($searchParams);
@@ -80,4 +88,9 @@ public function export(ExportStreamWriter $writer, ExportJob $job, $lastResource
$lastResourceIdExported = $record->getId();
}
}
+
+ public function getLastModifiedSearchField(): ?FhirSearchParameterDefinition
+ {
+ return null;
+ }
}
diff --git a/src/Services/FHIR/UtilsService.php b/src/Services/FHIR/UtilsService.php
index 8906169b2e8..2c570d0d12c 100644
--- a/src/Services/FHIR/UtilsService.php
+++ b/src/Services/FHIR/UtilsService.php
@@ -361,6 +361,20 @@ public static function getDateFormattedAsUTC(): string
return (new \DateTime())->format(DATE_ATOM);
}
+ public static function getLocalTimestampAsUTCDate($date)
+ {
+ // make this assumption explicit that we are using the current timezone specified in PHP
+ // when we use strtotime or gmdate we get bad behavior when dealing with DST
+ // we really should be storing dates internally as UTC instead of local time... but until that happens we have
+ // to do this.
+ // note this is what we were using before
+ // $date = gmdate('c', strtotime($dataRecord['date']));
+ // w/ DST the date 2015-06-22 00:00:00 server time becomes 2015-06-22T04:00:00+00:00 w/o DST the server time becomes 2015-06-22T00:00:00-04:00
+ $date = new \DateTime("@" . $date, new \DateTimeZone(date('P')));
+ $utcDate = $date->format(DATE_ATOM);
+ return $utcDate;
+ }
+
public static function getLocalDateAsUTC($date)
{
// make this assumption explicit that we are using the current timezone specified in PHP
diff --git a/src/Services/GroupService.php b/src/Services/GroupService.php
index 5fc0ed3cbaa..cf6bd458816 100644
--- a/src/Services/GroupService.php
+++ b/src/Services/GroupService.php
@@ -46,7 +46,7 @@ public function searchPatientProviderGroups($search = array(), $isAndCondition =
{
// we inner join on status in case we ever decide to add a status property (and layers above this one can rely
// on the property without changing code).
- $sql = "SELECT
+ $sqlSelectFull = "SELECT
patient_provider_groups.uuid
,patient_provider_groups.provider_id
,patient_provider_groups.provider_fname
@@ -57,15 +57,20 @@ public function searchPatientProviderGroups($search = array(), $isAndCondition =
,patient_provider_groups.patient_fname
,patient_provider_groups.patient_mname
,patient_provider_groups.patient_lname
- FROM (
+ ,patient_provider_groups.creation_date
+ ,patient_provider_groups.patient_last_updated ";
+ $sqlIds = "SELECT DISTINCT patient_provider_groups.uuid ";
+ $sqlFrom = "FROM (
SELECT
uuid_mapping.target_uuid AS pruuid
,uuid_mapping.uuid
+ ,uuid_mapping.created AS `creation_date`
,users.id AS provider_id
,users.fname AS provider_fname
,users.lname AS provider_lname
,users.mname AS provider_mname
,patients.uuid AS puuid
+ ,patients.last_updated AS patient_last_updated
,patients.title AS patient_title
,patients.fname AS patient_fname
,patients.mname AS patient_mname
@@ -80,11 +85,18 @@ public function searchPatientProviderGroups($search = array(), $isAndCondition =
$whereClause = FhirSearchWhereClauseBuilder::build($search, $isAndCondition);
- $sql .= $whereClause->getFragment();
+ $sqlIds .= $sqlFrom . $whereClause->getFragment();
$sqlBindArray = $whereClause->getBoundValues();
- $statementResults = QueryUtils::sqlStatementThrowException($sql, $sqlBindArray);
-
- $processingResult = $this->hydratePatientProviderSearchResultsFromQueryResource($statementResults);
+ $uuids = QueryUtils::fetchTableColumn($sqlIds, 'uuid', $sqlBindArray);
+ if (!empty($uuids)) {
+ // TODO: if we have a LARGE number of provider groups we will reach our max parameter count here...
+ // need to do optimization here for large # of providers.
+ $sqlSelectFull .= $sqlFrom . " WHERE patient_provider_groups.uuid IN (" . str_repeat("?, ", count($uuids) - 1) . "? )";
+ $statementResults = QueryUtils::sqlStatementThrowException($sqlSelectFull, $uuids);
+ $processingResult = $this->hydratePatientProviderSearchResultsFromQueryResource($statementResults);
+ } else {
+ $processingResult = new ProcessingResult();
+ }
return $processingResult;
}
@@ -112,6 +124,7 @@ private function hydratePatientProviderSearchResultsFromQueryResource($queryReso
$record = [
'uuid' => $recordUuid
,'name' => $groupName
+ ,'last_modified_date' => $dbRecord['patient_last_updated'] ?? $dbRecord['creation_date']
,'patients' => []
];
$orderedList[] = $recordUuid;
diff --git a/src/Services/ImmunizationService.php b/src/Services/ImmunizationService.php
index fd0636d8526..3efc4c2a821 100644
--- a/src/Services/ImmunizationService.php
+++ b/src/Services/ImmunizationService.php
@@ -76,6 +76,7 @@ public function search($search, $isAndCondition = true)
education_date,
note,
create_date,
+ update_date,
amount_administered,
amount_administered_unit,
expiration_date,
@@ -92,7 +93,7 @@ public function search($search, $isAndCondition = true)
providers.provider_uuid,
providers.provider_npi,
providers.provider_username,
-
+
IF(
IF(
information_source = 'new_immunization_record' AND
@@ -133,7 +134,7 @@ public function search($search, $isAndCondition = true)
notes AS refusal_reason_cdc_nip_code,
codes AS refusal_reason_codes,
title AS refusal_reason_description
- FROM list_options
+ FROM list_options
WHERE list_id = 'immunization_refusal_reason'
) refusal_reasons ON immunizations.refusal_reason = refusal_reasons.refusal_reason_id";
diff --git a/src/Services/InsuranceCompanyService.php b/src/Services/InsuranceCompanyService.php
index 037f26aa7ca..f0d90acd33c 100644
--- a/src/Services/InsuranceCompanyService.php
+++ b/src/Services/InsuranceCompanyService.php
@@ -151,7 +151,9 @@ public function search($search, $isAndCondition = true)
$sql .= " a.state,";
$sql .= " a.zip,";
$sql .= " a.plus_four,";
- $sql .= " a.country";
+ $sql .= " a.country,";
+ $sql .= " i.date_created,";
+ $sql .= " i.last_updated";
$sql .= " FROM insurance_companies i ";
$sql .= " LEFT JOIN (SELECT line1,line2,city,state,zip,plus_four,country,foreign_id FROM addresses) a ON i.id = a.foreign_id";
// the foreign_id here is a globally unique sequence so there is no conflict.
diff --git a/src/Services/ListService.php b/src/Services/ListService.php
index ab3bc55f472..2bbf7aef5d7 100644
--- a/src/Services/ListService.php
+++ b/src/Services/ListService.php
@@ -15,6 +15,12 @@
namespace OpenEMR\Services;
use OpenEMR\Common\Database\QueryUtils;
+use OpenEMR\Services\Search\FhirSearchWhereClauseBuilder;
+use OpenEMR\Services\Search\SearchFieldException;
+use OpenEMR\Services\Search\SearchModifier;
+use OpenEMR\Services\Search\StringSearchField;
+use OpenEMR\Services\Search\TokenSearchField;
+use OpenEMR\Validators\ProcessingResult;
use Particle\Validator\Validator;
use OpenEMR\Common\Uuid\UuidRegistry;
@@ -65,6 +71,50 @@ public function getListOptionsForLists($lists)
return $records;
}
+ /**
+ * Allows searching on the top level lists in the lists_options table. Will return the top level lists that match
+ * the search criteria as well as the last updated date of the sublist.
+ * @param $search
+ * @param $isAndCondition
+ * @return ProcessingResult
+ */
+ public function searchLists($search, $isAndCondition = true)
+ {
+ // TODO: @adunsulag this is copy-pasta from BaseService... need to investigate if we can just have ListService extend BaseService
+ $processingResult = new ProcessingResult();
+ try {
+ $sql = "SELECT
+ lo.*,
+ sub_list.sublist_updated_date
+ FROM
+ list_options lo
+ JOIN(
+ SELECT lo2.list_id AS sublist_list_id,
+ MAX(last_updated) AS sublist_updated_date
+ FROM
+ list_options lo2
+ WHERE
+ lo2.list_id != 'lists'
+ GROUP BY
+ list_id
+ ) sub_list
+ ON
+ lo.option_id = sub_list.sublist_list_id ";
+ $whereFragment = FhirSearchWhereClauseBuilder::build($search, $isAndCondition);
+ $sql .= $whereFragment->getFragment() . " AND lo.list_id = 'lists' ORDER BY lo.seq, lo.list_id, lo.option_id ";
+ $records = QueryUtils::fetchRecords($sql, $whereFragment->getBoundValues());
+ if (!empty($records)) {
+ foreach ($records as $row) {
+ $processingResult->addData($row);
+ }
+ }
+ } catch (SearchFieldException $exception) {
+ $processingResult->setValidationMessages([$exception->getField() => $exception->getMessage()]);
+ }
+
+ return $processingResult;
+ }
+
public function getListIds()
{
$sql = "SELECT DISTINCT list_id FROM list_options ORDER BY list_id";
diff --git a/src/Services/LocationService.php b/src/Services/LocationService.php
index a1cb52d82f5..44b8a929dbe 100644
--- a/src/Services/LocationService.php
+++ b/src/Services/LocationService.php
@@ -73,8 +73,9 @@ public function getAll($search = array(), $isAndCondition = true)
null as fax,
null as website,
email,
+ `date` AS last_updated,
"' . self::TYPE_PATIENT . '" AS `type`
- from
+ from
patient_data
UNION SELECT
uuid as table_uuid,
@@ -88,8 +89,9 @@ public function getAll($search = array(), $isAndCondition = true)
fax,
website,
email,
+ last_updated,
"' . self::TYPE_FACILITY . '" AS `type`
- from
+ from
facility
UNION SELECT
uuid as table_uuid,
@@ -103,8 +105,9 @@ public function getAll($search = array(), $isAndCondition = true)
fax,
url as website,
email,
+ last_updated,
"' . self::TYPE_USER . '" AS `type`
- from
+ from
users
) as location
LEFT JOIN uuid_mapping ON uuid_mapping.target_uuid=location.table_uuid AND uuid_mapping.resource="Location"';
diff --git a/src/Services/PractitionerRoleService.php b/src/Services/PractitionerRoleService.php
index da965b2f277..49e56954515 100644
--- a/src/Services/PractitionerRoleService.php
+++ b/src/Services/PractitionerRoleService.php
@@ -54,13 +54,18 @@ public function search($search, $isAndCondition = true)
providers.user_name,
providers.provider_id,
providers.provider_uuid,
+ providers.provider_last_updated,
facilities.facility_uuid,
facilities.facility_name,
role_codes.role_code,
role_codes.role_title,
+ role_codes.role_last_updated,
+
specialty_codes.specialty_code,
specialty_codes.specialty_title,
+ specialty_codes.specialty_last_updated,
+
physician_types.physician_type_codes,
physician_types.physician_type,
physician_types.physician_type_title
@@ -68,13 +73,13 @@ public function search($search, $isAndCondition = true)
select
facility_user_ids.uuid AS facility_role_uuid,
facility_user_ids.id AS facility_role_id,
- -- field_value AS provider_id,
facility_user_ids.facility_id,
uid AS user_id,
-- we are treating the user_id as the provider id
-- TODO: @adunsulag figure out whether we should actually be using the user entered provider_id
uid AS provider_id,
users.uuid AS provider_uuid,
+ users.last_updated AS provider_last_updated,
users.physician_type,
CONCAT(COALESCE(users.fname,''),
IF(users.mname IS NULL OR users.mname = '','',' '),COALESCE(users.mname,''),
@@ -94,7 +99,9 @@ public function search($search, $isAndCondition = true)
field_id,
role.title AS role_title,
facility_id,
- uid AS user_id
+ uid AS user_id,
+ facility_user_ids.last_updated AS role_last_updated,
+ facility_user_ids.date_created AS role_date_created
FROM
facility_user_ids
JOIN
@@ -119,7 +126,9 @@ public function search($search, $isAndCondition = true)
specialty.title AS specialty_title,
field_id,
facility_id,
- uid AS user_id
+ uid AS user_id,
+ facilities_specialty.last_updated AS specialty_last_updated,
+ facilities_specialty.date_created AS specialty_date_created
FROM
facility_user_ids facilities_specialty
JOIN
diff --git a/src/Services/PrescriptionService.php b/src/Services/PrescriptionService.php
index 6a4c013e9c6..ae87305b09f 100644
--- a/src/Services/PrescriptionService.php
+++ b/src/Services/PrescriptionService.php
@@ -82,7 +82,7 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
// order comes from our MedicationRequest intent value set, since we are only reporting on completed prescriptions
// we will put the intent down as 'order' @see http://hl7.org/fhir/R4/valueset-medicationrequest-intent.html
- $sql = "SELECT
+ $sql = "SELECT
combined_prescriptions.uuid
,combined_prescriptions.source_table
,combined_prescriptions.drug
@@ -100,6 +100,8 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
,combined_prescriptions.note
,combined_prescriptions.status
,combined_prescriptions.drug_dosage_instructions
+ ,combined_prescriptions.date_added
+ ,combined_prescriptions.date_modified
,patient.puuid
,encounter.euuid
,practitioner.pruuid
@@ -133,6 +135,7 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
,IF(drugs.drug_code IS NULL, '', concat('RXCUI:',drugs.drug_code))
) AS 'rxnorm_drugcode'
,date_added
+ ,date_modified
,COALESCE(prescriptions.unit,drugs.unit) AS unit
,prescriptions.`interval`
,COALESCE(prescriptions.`route`,drugs.`route`) AS 'route'
@@ -142,12 +145,12 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
,provider_id
,drugs.uuid AS drug_uuid
,prescriptions.drug_dosage_instructions
- ,CASE
+ ,CASE
WHEN prescriptions.end_date IS NOT NULL AND prescriptions.active = '1' THEN 'completed'
WHEN prescriptions.active = '1' THEN 'active'
ELSE 'stopped'
END as 'status'
-
+
FROM
prescriptions
LEFT JOIN
@@ -166,6 +169,7 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
,lists_medication.usage_category_title AS category_title
,lists.diagnosis AS rxnorm_drugcode
,`date` AS date_added
+ ,`modifydate` AS date_modified
,NULL as unit
,NULL as 'interval'
,NULL as `route`
@@ -175,20 +179,20 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
,users.id AS provider_id
,NULL as drug_uuid
,lists_medication.drug_dosage_instructions
- ,CASE
+ ,CASE
WHEN lists.enddate IS NOT NULL AND lists.activity = 1 THEN 'completed'
WHEN lists.activity = 1 THEN 'active'
ELSE 'stopped'
END as 'status'
FROM
lists
- LEFT JOIN
+ LEFT JOIN
users ON users.username = lists.user
LEFT JOIN
lists_medication ON lists_medication.list_id = lists.id
LEFT JOIN
(
- select
+ select
pid AS issues_encounter_pid
, list_id AS issues_encounter_list_id
-- lists have a 0..* relationship with issue_encounters which is a problem as FHIR treats medications as a 0.1
@@ -214,7 +218,7 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
,title AS interval_title
,codes AS interval_codes
FROM list_options
- WHERE list_id='drug_route'
+ WHERE list_id='drug_route'
) intervals_list ON intervals_list.interval_id = combined_prescriptions.interval
LEFT JOIN
(
@@ -239,7 +243,7 @@ public function getAll($search = array(), $isAndCondition = true, $puuidBind = n
) encounter
ON encounter.encounter = combined_prescriptions.encounter
LEFT JOIN (
- SELECT
+ SELECT
id AS practitioner_id
,uuid AS pruuid
FROM users
diff --git a/src/Services/ProcedureProviderService.php b/src/Services/ProcedureProviderService.php
index d89f9f240f7..60f7115ebd7 100644
--- a/src/Services/ProcedureProviderService.php
+++ b/src/Services/ProcedureProviderService.php
@@ -58,6 +58,8 @@ public function search($search, $isAndCondition = true)
,prov.lab_director
,prov.active
,prov.type
+ ,prov.last_updated
+ ,prov.date_created
FROM procedure_providers prov
";
diff --git a/src/Services/Search/DateSearchField.php b/src/Services/Search/DateSearchField.php
index d13dcde2731..917c78179b4 100644
--- a/src/Services/Search/DateSearchField.php
+++ b/src/Services/Search/DateSearchField.php
@@ -41,7 +41,7 @@ class DateSearchField extends BasicSearchField
private const COMPARATOR_MATCH = "/^(\D{2})?(\d{4})(-\d{2})?(-\d{2})?(?:(T\d{2}:\d{2})(:\d{2})?)?(\.\d{1,4})?(Z|(\+|-)(\d{2}):(\d{2}))?$/";
// php's DATE_ATOM does not handle milliseconds so we have to add them in manually
- private const DATE_ATOM_MILLISECONDS = 'Y-m-d\TH:i:s.uP';
+ public const DATE_ATOM_MILLISECONDS = 'Y-m-d\TH:i:s.uP';
private const COMPARATOR_INDEX_FULL = 0;
diff --git a/src/Services/Search/FHIRSearchFieldFactory.php b/src/Services/Search/FHIRSearchFieldFactory.php
index 79c94788264..34421e0295f 100644
--- a/src/Services/Search/FHIRSearchFieldFactory.php
+++ b/src/Services/Search/FHIRSearchFieldFactory.php
@@ -58,6 +58,16 @@ public function getFhirUrlResolver(): FhirUrlResolver
return $this->fhirUrlResolver;
}
+ /**
+ * @param $fhirSearchField
+ * @param FhirSearchParameterDefinition $definition
+ * @return void
+ */
+ public function setSearchFieldDefinition(string $fhirSearchField, FhirSearchParameterDefinition $definition)
+ {
+ $this->resourceSearchParameters[$fhirSearchField] = $definition;
+ }
+
/**
* Checks whethere the factory has a search definition for the passed in search field name
* @param $fhirSearchField
@@ -76,8 +86,8 @@ public function getSearchFieldDefinition($fhirSearchField): FhirSearchParameterD
/**
* Factory method to build a search field using the factory's search field definitions.
- * @param $fhirSearchField The passed in parameter name for the search field the user agent sent. Can contain search modifiers
- * @param $fhirSearchValues The array of search values the user agent sent for the $fhirSearchField
+ * @param $fhirSearchField string The passed in parameter name for the search field the user agent sent. Can contain search modifiers
+ * @param $fhirSearchValues array The array of search values the user agent sent for the $fhirSearchField
* @throws \InvalidArgumentException If the factory does not have a search definition for $fhirSearchField
* @return CompositeSearchField|DateSearchField|StringSearchField|TokenSearchField
*/
diff --git a/src/Services/SurgeryService.php b/src/Services/SurgeryService.php
index 17d4c040ca8..6d54be74d14 100644
--- a/src/Services/SurgeryService.php
+++ b/src/Services/SurgeryService.php
@@ -58,7 +58,8 @@ public function search($search, $isAndCondition = true)
encounter.euuid,
recorders.recorder_npi,
recorders.recorder_uuid,
- recorders.recorder_username
+ recorders.recorder_username,
+ surgeries.date_modified
FROM (
SELECT
id
@@ -71,6 +72,7 @@ public function search($search, $isAndCondition = true)
,`pid`
,`comments`
,`user` as surgery_recorder
+ ,`modifydate` AS date_modified
FROM lists
WHERE
`type` = 'surgery'
diff --git a/src/Services/UserService.php b/src/Services/UserService.php
index ca63fde1cdd..5bdd1cb5e3a 100644
--- a/src/Services/UserService.php
+++ b/src/Services/UserService.php
@@ -254,13 +254,17 @@ public function search($search, $isAndCondition = true)
phonecell,
users.notes,
state_license_number,
- abook.title as abook_title";
+ abook.title as abook_title,
+ last_updated ";
if ($this->_includeUsername) {
$sql .= ", username";
}
+ // grab our address book type, make sure to use the index w/ list_id and option_id
$sql .= "
FROM users
- LEFT JOIN list_options as abook ON abook.option_id = users.abook_type";
+ LEFT JOIN (
+ SELECT list_id,option_id, title FROM list_options
+ ) abook ON abook.list_id = 'abook_type' AND abook.option_id = users.abook_type";
$whereClause = FhirSearchWhereClauseBuilder::build($search, $isAndCondition);
$sql .= $whereClause->getFragment();
diff --git a/src/Services/Utils/DateFormatterUtils.php b/src/Services/Utils/DateFormatterUtils.php
index 5152e5d9b9c..baaa47d3be4 100644
--- a/src/Services/Utils/DateFormatterUtils.php
+++ b/src/Services/Utils/DateFormatterUtils.php
@@ -140,4 +140,13 @@ public static function getTimeFormat($seconds = false)
}
return $formatted;
}
+
+ public static function getFormattedISO8601DateFromDateTime(\DateTime $dateTime): string
+ {
+ // ISO8601 doesn't support fractional dates so we need to change from microseconds to milliseconds
+ // TODO: @adunsulag this is a hack to get around the fact that PHP does microseconds and ISO8601 uses milliseconds
+ // , look at refactoring all of this so we don't have to do multiple date conversions up and down the stack.
+ $dateStr = substr($dateTime->format('Y-m-d\TH:i:s.u'), 0, -3) . $dateTime->format('P');
+ return $dateStr;
+ }
}
diff --git a/src/Services/VitalsService.php b/src/Services/VitalsService.php
index 94daf4ff138..bb338117c6d 100644
--- a/src/Services/VitalsService.php
+++ b/src/Services/VitalsService.php
@@ -115,6 +115,8 @@ public function search($search, $isAndCondition = true)
,vitals.ped_bmi
,vitals.ped_head_circ
,vitals.inhaled_oxygen_concentration
+ ,vitals.last_updated
+ ,forms.date_created
,details.details_id
,details.interpretation_list_id
,details.interpretation_option_id
@@ -132,6 +134,7 @@ public function search($search, $isAndCondition = true)
,bpd,bps,weight,height,temperature,temp_method,pulse,respiration,BMI,BMI_status,waist_circ
,head_circ,oxygen_saturation,oxygen_flow_rate,inhaled_oxygen_concentration
, ped_weight_height,ped_bmi,ped_head_circ
+ , last_updated
FROM
form_vitals
) vitals
@@ -143,6 +146,7 @@ public function search($search, $isAndCondition = true)
,`user`
,deleted
,formdir
+ ,`date` AS date_created
FROM
forms
) forms ON vitals.id = forms.form_id
@@ -151,9 +155,11 @@ public function search($search, $isAndCondition = true)
encounter AS eid
,uuid AS euuid
,`date` AS encounter_date
+ ,pid AS encounter_pid
FROM
form_encounter
- ) encounters ON encounters.eid = forms.encounter
+ -- use both columns in order to leverage the index
+ ) encounters ON encounters.encounter_pid = forms.form_pid AND encounters.eid = forms.encounter
LEFT JOIN
(
SELECT
diff --git a/swagger/openemr-api.yaml b/swagger/openemr-api.yaml
index 1f83fdf6430..b9d176c4b12 100644
--- a/swagger/openemr-api.yaml
+++ b/swagger/openemr-api.yaml
@@ -3854,6 +3854,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -3939,6 +3946,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4013,6 +4027,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4103,6 +4124,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4152,6 +4180,13 @@ paths:
required: true
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
responses:
'200':
description: 'Standard Response'
@@ -4190,6 +4225,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4272,6 +4314,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4359,6 +4408,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4444,6 +4500,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4551,6 +4614,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4727,6 +4797,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4818,6 +4895,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4899,6 +4983,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -4989,6 +5080,20 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -5072,6 +5177,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
responses:
'200':
description: 'Standard Response'
@@ -5137,6 +5249,21 @@ paths:
tags:
- fhir
description: 'Returns a list of Medication resources.'
+ parameters:
+ -
+ name: _id
+ in: query
+ description: 'The uuid for the Medication resource.'
+ required: false
+ schema:
+ type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
responses:
'200':
description: 'Standard Response'
@@ -5210,6 +5337,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -5309,6 +5443,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -5413,6 +5554,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: name
in: query
@@ -5677,6 +5825,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: identifier
in: query
@@ -5997,6 +6152,20 @@ paths:
- fhir
description: 'Returns a list of Person resources.'
parameters:
+ -
+ name: _id
+ in: query
+ description: 'The uuid for the Person resource.'
+ required: false
+ schema:
+ type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: name
in: query
@@ -6149,6 +6318,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: name
in: query
@@ -6401,6 +6577,20 @@ paths:
- fhir
description: 'Returns a list of PractitionerRole resources.'
parameters:
+ -
+ name: _id
+ in: query
+ description: 'The uuid for the PractitionerRole resource.'
+ required: false
+ schema:
+ type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: specialty
in: query
@@ -6488,6 +6678,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
-
name: patient
in: query
@@ -6646,6 +6843,13 @@ paths:
required: false
schema:
type: string
+ -
+ name: _lastUpdated
+ in: query
+ description: 'Allows filtering resources by the _lastUpdated field. A FHIR Instant value in the format YYYY-MM-DDThh:mm:ss.sss+zz:zz. See FHIR date/time modifiers for filtering options (ge,gt,le, etc)'
+ required: false
+ schema:
+ type: string
responses:
'200':
description: 'Standard Response'
@@ -7540,7 +7744,6 @@ components:
- subscriber_postal_code
- subscriber_city
- subscriber_state
- - subscriber_country
- subscriber_sex
- accept_assignment
properties: