From 48527473b6f1f19c564ebf95a677d23410e051be Mon Sep 17 00:00:00 2001
From: Stephen Nielson
Date: Wed, 9 Oct 2024 20:56:28 -0400
Subject: [PATCH] Bug openemr fix 7746 export since delta fix (#7754)
* lastModifiedDate export support.
_since parameter was not working properly. Need to expose on all of the
FHIR services the _lastModifiedDate search parameter and verify all the
endpoints are functioning and performant.
The export can specify a _since parameter and apparently the original
implementation only supported _since from the beginning of the system
implementation, and no actual date filtering. Anyone wanting to do a
delta would not have their export data function properly.
This is in reference to issue #7746
* Fixed _since implementation in bulk export.
The _since date filterer was broken and was only allowing dates from the
beginning of time for the _since filter. This was preventing any kind of
bulk export using the delta of a system.
Had to add everywhere the _lastModified parameter.
Resources implemented here are
- DiagnosticReport
- ClinicalNotes
- AllergyIntolerance
- CarePlan
- CareTeam
- Coverage
- Device
- DocumentReference
- Encounter
- Goal
- Group (this was tricky, it uses the patient last updated date to
determine group membership)
- Immunization
- Patient
- Location
- ValueSet
- CareTeam,
- Practitioner,
- PractitionerRole
- Condition
- DiagnosticReport
- MedicationRequest
- Observation
- Organization
- Procedure
- Provenance
Had to add date fields to the facility and user table in order to handle their location data since the tables are denormalized using uuid_mapping for the date.
Needed to configure list service and appointments to handle searching on
list options and appointment categories w/ last updated status so people
can grab delta on if the value set has changed. Really need better
support for published versions, etc. But it is what it is for now.
Added db last_updated fields (and where missing date_create) fields for
- Insurance companies
- user&facility roles IE PractitionerRole endpoint
- calendar categories
- list options
- added _lastUpdated to form_clinical_notes
NOTE if you have a LOT of vital records the upgrade may take some time.
Need to make sure we mention this in the patch.
Added last_updated column to vitals form to track modifications to the
form.
Improved vitals query performance by making it use an index on the
forms_encounter.
* Fix FHIR _lastUpdated social history
Made it so the smoking status w/ social history works w/ the
_lastUpdated field.
* _lastUpdated for Organization resource
Added to table procedure_providers the following columns
- date_created
- procedure_providers
Implemented _lastUpdated for Organization resources which includes
facility, insurance, and procedure providers.
* _lastUpdated and FHIR fix for Medication
Had an issue in the Medication resource where drugs added via the
pharmacy/dispensary module would not actual show their valid RXCUI
system if there was no code system available.
Made _lastUpdated work for filtering on medications.
Added last_updated and date_created columns to the medication service.
* Add helpful message for internal data loads
* FHIR patient _lastUpdated, deceasedDate fix
Made the _lastUpdated work by adding a last_updated field to
patient_data. Hopefully people won't have maxed out their patient_data
table record for MySQL as we absolutely need to track the most recent
patient_data values from an API perspective so we have to make this
change.
Also discovered that deceasedDate was throwing errors in the API due to
a bad column value. Fixed the issue so deceased boolean flag is now
reporting correctly.
* FHIR _lastUpdated search for Procedure resource
Made the fhir procedure service work with the _lastUpdated parameter for
both surgeries and openemr procedures.
Fixed issue with unix_timestamp conversion on the prescription service.
* Fix style issues.
* _lastUpdated Person resource implementation
Made it so the _lastUpdated search parameter works with the FHIR Person
resource.
* FHIR _lastUpdated database changes
added date_created to users and facility table.
Added all of the date_created and last_modified columns to the fresh
database installs.
* FHIR Provenance _lastUpdated support
Made it so the provenance _lastUpdated gets passed down to all of the
sub resources when working with the provenance piece.
* FHIR appointment,group bug fixes
Group was only showing the most recently changed patients when
_lastUpdated is used instead of showing the entire resource.
Appointment was not using the correct search field when doing an export.
Style fixes.
* Safer error messages if oauth2 key invalid
* Update api documentation
* Fix date_created, missing columns in new db
Date_created does not need on update clause.
Missed two database columns in the new database.
* Fix surrogate key provenance issue.
Provenance was incorrectly pushing out surrogate key values based on
lastUpdated value fields. Now that lastUpdated is actually reflecting
on the actual resources instead of just the latest timestamp, we were
incorrectly getting surrogate keys if they the resource was updated
earlier than 2022 (V1 key value).
* Fix insert statements on calendar categories
* Provide better error messages on installer class
Error messages of actual SQL causing the installer to fail was being
surpressed. Made it so the error messages show instead of just fatal
death w/o any more information.
---
_rest_config.php | 25 +-
_rest_routes.inc.php | 261 ++++++++++++++++++
interface/super/load_codes.php | 5 +-
library/classes/Installer.class.php | 23 +-
sql/7_0_2-to-7_0_3_upgrade.sql | 70 ++++-
sql/database.sql | 65 +++--
src/FHIR/Export/ExportJob.php | 12 +
.../AuthorizationController.php | 6 +-
.../FhirOperationExportRestController.php | 23 +-
src/Services/AppointmentService.php | 17 +-
src/Services/CareTeamService.php | 3 +
src/Services/ClinicalNotesService.php | 4 +
src/Services/ConditionService.php | 5 +-
src/Services/DeviceService.php | 4 +-
src/Services/DrugService.php | 64 +++--
...irDiagnosticReportClinicalNotesService.php | 19 +-
.../FhirDiagnosticReportLaboratoryService.php | 22 +-
.../FhirClinicalNotesService.php | 19 +-
.../FhirPatientDocumentReferenceService.php | 19 +-
.../FHIR/FhirAllergyIntoleranceService.php | 12 +-
src/Services/FHIR/FhirAppointmentService.php | 7 +-
src/Services/FHIR/FhirCarePlanService.php | 14 +-
src/Services/FHIR/FhirCareTeamService.php | 13 +-
src/Services/FHIR/FhirConditionService.php | 13 +-
src/Services/FHIR/FhirCoverageService.php | 18 +-
src/Services/FHIR/FhirDeviceService.php | 17 +-
.../FHIR/FhirDiagnosticReportService.php | 9 +
.../FHIR/FhirDocumentReferenceService.php | 9 +
src/Services/FHIR/FhirEncounterService.php | 8 +-
src/Services/FHIR/FhirGoalService.php | 16 +-
src/Services/FHIR/FhirGroupService.php | 7 +
src/Services/FHIR/FhirImmunizationService.php | 12 +-
src/Services/FHIR/FhirLocationService.php | 14 +-
.../FHIR/FhirMedicationRequestService.php | 12 +-
src/Services/FHIR/FhirMedicationService.php | 12 +-
src/Services/FHIR/FhirObservationService.php | 6 +
src/Services/FHIR/FhirOrganizationService.php | 8 +-
src/Services/FHIR/FhirPatientService.php | 15 +-
src/Services/FHIR/FhirPersonService.php | 14 +-
.../FHIR/FhirPractitionerRoleService.php | 19 +-
src/Services/FHIR/FhirPractitionerService.php | 16 +-
src/Services/FHIR/FhirProcedureService.php | 6 +
src/Services/FHIR/FhirProvenanceService.php | 115 +++-----
src/Services/FHIR/FhirValueSetService.php | 163 ++++++-----
.../Group/FhirPatientProviderGroupService.php | 13 +-
.../FHIR/IFhirExportableResourceService.php | 10 +
.../FhirObservationLaboratoryService.php | 12 +-
.../FhirObservationSocialHistoryService.php | 12 +-
.../FhirObservationVitalsService.php | 14 +-
.../FhirOrganizationFacilityService.php | 20 +-
.../FhirOrganizationInsuranceService.php | 20 +-
...irOrganizationProcedureProviderService.php | 20 +-
.../FhirProcedureOEProcedureService.php | 14 +-
.../Procedure/FhirProcedureSurgeryService.php | 12 +-
.../FhirBulkExportDomainResourceTrait.php | 13 +
src/Services/FHIR/UtilsService.php | 14 +
src/Services/GroupService.php | 25 +-
src/Services/ImmunizationService.php | 5 +-
src/Services/InsuranceCompanyService.php | 4 +-
src/Services/ListService.php | 50 ++++
src/Services/LocationService.php | 9 +-
src/Services/PractitionerRoleService.php | 15 +-
src/Services/PrescriptionService.php | 20 +-
src/Services/ProcedureProviderService.php | 2 +
src/Services/Search/DateSearchField.php | 2 +-
.../Search/FHIRSearchFieldFactory.php | 14 +-
src/Services/SurgeryService.php | 4 +-
src/Services/UserService.php | 8 +-
src/Services/Utils/DateFormatterUtils.php | 9 +
src/Services/VitalsService.php | 8 +-
swagger/openemr-api.yaml | 205 +++++++++++++-
71 files changed, 1480 insertions(+), 295 deletions(-)
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: