various bugs (#4331)
[openemr.git] / interface / eRxSOAP.php
blob66b811efef85f34015b370542a99907795f3dc23
1 <?php
3 /**
4 * interface/eRxSOAP.php Functions for interacting with NewCrop SOAP calls.
6 * @package OpenEMR
7 * @link http://www.open-emr.org
8 * @author Sam Likins <sam.likins@wsi-services.com>
9 * @copyright Copyright (c) 2015 Sam Likins <sam.likins@wsi-services.com>
10 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
14 class eRxSOAP
17 const ACTION_ALLERGIES = 'allergies';
18 const ACTION_MEDICATIONS = 'medications';
20 const FLAG_PRESCRIPTION_PRESS = '1';
21 const FLAG_PRESCRIPTION_IMPORT = '2';
22 const FLAG_ALLERGY_PRESS = '3';
23 const FLAG_ALLERGY_IMPORT = '4';
25 private $globals;
26 private $store;
28 private $authUserId;
29 private $authUserDetails;
30 private $patientId;
31 private $soapClient;
32 private $soapSettings = array();
33 private $siteId;
35 /**
36 * Repair HTML/XML and return array
37 * @param string $xml XML for processing
38 * @return array|boolean Array on success, false on failure
40 public static function htmlFixXmlToArray($xml)
42 $xmltoarray = new xmltoarray_parser_htmlfix(); //create instance of class
44 $xmltoarray->xmlparser_setoption(XML_OPTION_SKIP_WHITE, 1); //set options same as xml_parser_set_option
45 $xmltoarray->xmlparser_setoption(XML_OPTION_CASE_FOLDING, 0);
47 $xmltoarray->xmlparser_fix_into_struct(base64_decode($xml)); //fixes html values for XML
49 $array = $xmltoarray->createArray(); //creates an array with fixed html values
51 foreach ($array as $key => $value) {
52 $array[$key] = $xmltoarray->fix_html_entities($value); //returns proper html values
55 if (array_key_exists('NewDataSet', $array) && array_key_exists('Table', $array['NewDataSet'])) {
56 $array = $array['NewDataSet']['Table'];
57 } else {
58 $array = false;
61 return $array;
64 /**
65 * Set Globals for retrieving eRx global configurations
66 * @param object $globals The eRx Globals object to use for processing
67 * @return eRxPage This object is returned for method chaining
69 public function setGlobals($globals)
71 $this->globals = $globals;
73 return $this;
76 /**
77 * Get Globals for retrieving eRx global configurations
78 * @return object The eRx Globals object to use for processing
80 public function getGlobals()
82 return $this->globals;
85 /**
86 * Set Store to handle eRx cashed data
87 * @param object $store The eRx Store object to use for processing
88 * @return eRxSOAP This object is returned for method chaining
90 public function setStore($store)
92 $this->store = $store;
94 return $this;
97 /**
98 * Get Store for handling eRx cashed data
99 * @return object The eRx Store object to use for processing
101 public function getStore()
103 return $this->store;
107 * Get Account Id set for SOAP communications with NewCrop
108 * @return string The Account Id sent with SOAP requests to NewCrop
110 public function getAccountId()
112 return $this->getGlobals()->getAccountId();
116 * Set SiteId for SOAP communications with NewCrop
117 * @param string $id The Site Id to send with SOAP requests to NewCrop
118 * @return eRxSOAP This object is returned for method chaining
120 public function setSiteId($id)
122 $this->siteId = $id;
124 return $this;
128 * Get Site Id set for SOAP communications with NewCrop
129 * @return string The Site Id sent with SOAP requests to NewCrop
131 public function getSiteId()
133 if (null === $this->siteId) {
134 $this->siteId = $this->getStore()
135 ->selectFederalEin();
138 return $this->siteId;
142 * Get the authenticated users ID and NPI
143 * @return array The users ID and NPI
145 public function getAuthUserDetails()
147 if (null === $this->authUserDetails) {
148 $this->authUserDetails = $this->getStore()
149 ->getUserById($this->getAuthUserId());
152 return $this->authUserDetails;
156 * Set the Id of the authenticated user
157 * @param integer $user The Id for the authenticated user
158 * @return eRxSOAP This object is returned for method chaining
160 public function setAuthUserId($user)
162 $this->authUserId = $user;
164 return $this;
168 * Get the Id of the authenticated user
169 * @return integer The Id of the authenticated user
171 public function getAuthUserId()
173 return $this->authUserId;
177 * Set the Id of the current patient
178 * @param integer $id The Id of the current patient
179 * @return eRxSOAP This object is returned for method chaining
181 public function setPatientId($id)
183 $this->patientId = (int) $id;
185 return $this;
189 * Get the Id of the current patient
190 * @return integer The Id of the current patient
192 public function getPatientId()
194 return $this->patientId;
198 * Generate and set a new SOAP client with provided Path Id
199 * @param integer $pathId Id for NewCrop eRx SOAP path: index [0 = Update, 1 = Patient]
200 * @return SoapClient Soap Client
202 public function initializeSoapClient($pathId)
204 $paths = $this->getGlobals()->getSoapPaths();
206 return $this->setSoapClient(new SoapClient($paths[(int) $pathId]));
210 * Set SOAP client for communication with NewCrop
211 * @param SoapClient $client SOAP client for communication with NewCrop
212 * @return eRxSOAP This object is returned for method chaining
214 public function setSoapClient(SoapClient $client)
216 $this->soapClient = $client;
218 return $this;
222 * Get SOAP client for communication with NewCrop
223 * @return SoapClient SOAP client for communication with NewCrop
225 public function getSoapClient()
227 return $this->soapClient;
231 * Set SOAP call settings for calls to NewCrop
232 * @param array $settings [optional] Setting to send with SOAP call to NewCrop
233 * @return eRxSOAP This object is returned for method chaining
235 public function setSoapSettings($settings = array())
237 $this->soapSettings = (array) $settings;
239 return $this;
243 * Get SOAP call settings for calls to NewCrop
244 * @return array Settings to send with SOAP call to NewCrop
246 public function &getSoapSettings()
248 return $this->soapSettings;
252 * Get TTL for provided SOAP process
253 * @param string $process SOAP process to retrieve TTL for
254 * @return number|boolean Number on success, false on failure
256 public function getTTL($process)
258 switch ($process) {
259 case self::ACTION_ALLERGIES:
260 $return = $this->getGlobals()->getTTLSoapAllergies();
261 break;
262 case self::ACTION_MEDICATIONS:
263 $return = $this->getGlobals()->getTTLSoapMedications();
264 break;
265 default:
266 $return = false;
269 return $return;
273 * Check if TTL of current patient has elapsed for provided SOAP process
274 * @param string $process SOAP process to check against for elapsed TTL of current patient
275 * @return boolean True if TTL of current patient has elapsed for provided SOAP process, otherwise false
277 public function elapsedTTL($process)
279 $ttl = $this->getTTL($process);
280 if (false === $ttl || 0 == $ttl) {
281 return true;
284 $soap = $this->getStore()->getLastSOAP($process, $this->getPatientId());
285 if (false === $soap) {
286 return true;
289 return strtotime('-' . $ttl . ' seconds') >= strtotime($soap);
293 * Update provided SOAP process TTL timestamp of current patient
294 * @param string $process SOAP process to update TTL of current patient
295 * @return eRxSOAP This object is returned for method chaining
297 public function updateTTL($process)
299 $this->getStore()->setLastSOAP($process, $this->getPatientId());
301 return $this;
305 * Check if import status of current patient is set to provided SOAP process(es)
306 * @param string|array $status SOAP process to check against import status of current patient, optionally an array of SOAP processes can be substituted
307 * @return boolean True if import status of current patient is set to provided SOAP process(es), otherwise false
309 public function checkPatientImportStatus($status)
311 $currentStatus = $this->getStore()
312 ->getPatientImportStatusByPatientId(
313 $this->getPatientId()
316 if (is_array($status)) {
317 $return = in_array($currentStatus, $status);
318 } else {
319 $return = ($currentStatus == $status);
322 return $return;
326 * [updatePatientImportStatus description]
327 * @param string $status SOAP process to update import status of current patient
328 * @return eRxSOAP This object is returned for method chaining
330 public function updatePatientImportStatus($status)
332 $this->getStore()
333 ->updatePatientImportStatusByPatientId(
334 $this->getPatientId(),
335 $status
338 return $this;
342 * Initialize SOAP settings with the credentials currently set
343 * @return eRxSOAP This object is returned for method chaining
345 public function initializeCredentials()
347 $credentials = $this->getGlobals()->getCredentials();
349 $this->soapSettings['credentials'] = array(
350 'PartnerName' => $credentials['0'],
351 'Name' => $credentials['1'],
352 'Password' => $credentials['2'],
355 return $this;
359 * Initialize SOAP settings with the NewCrop account and site Ids
360 * @return eRxSOAP This object is returned for method chaining
362 public function initializeAccountRequest()
364 $this->soapSettings['accountRequest'] = array(
365 'AccountId' => $this->getGlobals()->getAccountId(),
366 'SiteId' => $this->getSiteId(),
369 return $this;
373 * Initialize SOAP settings with patient information
374 * @return eRxSOAP This object is returned for method chaining
376 public function initializePatientInformationRequester()
378 $userDetails = $this->getAuthUserDetails();
380 $this->soapSettings['patientInformationRequester'] = array(
381 'UserId' => $userDetails['id'],
382 'UserType' => 'D',
385 return $this;
389 * Get account status information for current patient
390 * @return object SOAP client response from NewCrop call
392 public function getAccountStatus()
394 $this->setSoapSettings()
395 ->initializeCredentials()
396 ->initializeAccountRequest();
398 $userDetails = $this->getAuthUserDetails();
400 $this->soapSettings['locationId'] = $this->getPatientId();
401 $this->soapSettings['userId'] = $userDetails['npi'];
402 $this->soapSettings['userType'] = 'P';
404 $this->initializeSoapClient(1);
406 return $this->getSoapClient()
407 ->GetAccountStatus($this->soapSettings);
411 * Get allergy history for current patient
412 * @return object SOAP client response from NewCrop call
414 public function getPatientAllergyHistoryV3()
416 $this->setSoapSettings()
417 ->initializeCredentials()
418 ->initializeAccountRequest()
419 ->initializePatientInformationRequester();
421 $this->soapSettings['patientRequest']['PatientId'] = $this->getPatientId();
423 $this->initializeSoapClient(0);
425 return $this->getSoapClient()
426 ->GetPatientAllergyHistoryV3($this->soapSettings);
430 * Get full medication history for current patient
431 * @return object SOAP client response from NewCrop call
433 public function getPatientFullMedicationHistory6()
435 $this->setSoapSettings()
436 ->initializeCredentials()
437 ->initializeAccountRequest()
438 ->initializePatientInformationRequester();
440 $this->soapSettings['patientRequest']['PatientId'] = $this->getPatientId();
442 $this->soapSettings['prescriptionHistoryRequest'] = array(
443 'StartHistory' => '2011-01-01T00:00:00.000',
444 'EndHistory' => date('Y-m-d') . 'T23:59:59.000',
445 'PrescriptionStatus' => 'C',
446 'PrescriptionSubStatus' => '%',
447 'PrescriptionArchiveStatus' => 'N',
450 $this->soapSettings['patientIdType'] = '';
451 $this->soapSettings['includeSchema'] = '';
453 $this->initializeSoapClient(0);
455 return $this->getSoapClient()
456 ->GetPatientFullMedicationHistory6($this->soapSettings);
460 * Get free form allergy history for current patient
461 * @return object SOAP client response from NewCrop call
463 public function getPatientFreeFormAllergyHistory()
465 $this->setSoapSettings()
466 ->initializeCredentials()
467 ->initializeAccountRequest()
468 ->initializePatientInformationRequester();
470 $this->soapSettings['patientRequest']['PatientId'] = $this->getPatientId();
472 $client = $this->initializeSoapClient(0);
474 return $this->getSoapClient()
475 ->GetPatientFreeFormAllergyHistory($this->soapSettings);
479 * Insert list option if missing and return the associated option Id
480 * @param string $listId Id of list to reference
481 * @param string $title Title text to find
482 * @return string Option Id of selected list item
484 public function insertMissingListOptions($listId, $title)
486 $store = $this->getStore();
488 $optionId = $store->selectOptionIdByTitle($listId, $title);
490 if (false === $optionId) {
491 $optionId = 1 + $store->selectOptionIdsByListId($listId);
493 $store->insertListOptions($listId, $optionId, $title);
496 return $optionId;
500 * Trigger Allergy History SOAP call to NewCrop for current patient and update local cached data
501 * @return integer Count of newly cached records
503 public function insertUpdateAllergies()
505 $store = $this->getStore();
507 $insertedRows = 0;
509 $allergyArray = self::htmlFixXmlToArray(
510 $this->getPatientAllergyHistoryV3()
511 ->GetPatientAllergyHistoryV3Result
512 ->XmlResponse
515 if (is_array($allergyArray)) {
516 foreach ($allergyArray as $allergy) {
517 $optionId = $this->insertMissingListOptions(
518 'outcome',
519 $allergy['AllergySeverityName']
522 $allergySource = $store->selectAllergyErxSourceByPatientIdName(
523 $this->getPatientId(),
524 $allergy['AllergyName']
528 if (false === $allergySource) {
529 $store->insertAllergy(
530 $allergy['AllergyName'],
531 $allergy['AllergyId'],
532 $this->getPatientId(),
533 $this->getAuthUserId(),
534 $optionId
537 ++$insertedRows;
538 } elseif (0 == $allergySource) {
539 $store->updateAllergyOutcomeExternalIdByPatientIdName(
540 $optionId,
541 $allergy['AllergyId'],
542 $this->getPatientId(),
543 $allergy['AllergyName']
545 } else {
546 $store->updateAllergyOutcomeByPatientIdExternalIdName(
547 $optionId,
548 $this->getPatientId(),
549 $allergy['AllergyId'],
550 $allergy['AllergyName']
555 $this->updatePatientAllergyEndDate($allergyArray);
558 return $insertedRows;
562 * Iterate through provided list of allergies and update records with end dates
563 * @param array $allergyArray List of allergies
564 * @return eRxSOAP This object is returned for method chaining
566 public function updatePatientAllergyEndDate($allergyArray)
568 $store = $this->getStore();
569 $patientId = $this->getPatientId();
571 $resource = $store->selectActiveAllergiesByPatientId($patientId);
573 while ($row = sqlFetchArray($resource)) {
574 $noMatch = true;
576 foreach ($allergyArray as $allergy) {
577 if (array_key_exists('AllergyName', $allergy) && $allergy['AllergyName'] == $row['title']) {
578 $noMatch = false;
579 break;
583 if ($noMatch) {
584 $store->updateAllergyEndDateByPatientIdListId(
585 $patientId,
586 $row['id']
591 return $this;
595 * Update eRx uploaded status for current patient allergies
596 * @return boolean True on success, false on failure
598 public function updateUploadedErx()
600 $patientFreeFormAllergyHistory = $this
601 ->getPatientFreeFormAllergyHistory()
602 ->GetPatientFreeFormAllergyHistoryResult;
604 if (0 < $patientFreeFormAllergyHistory->result->RowCount) {
605 $response = $patientFreeFormAllergyHistory
606 ->patientFreeFormAllergyExtendedDetail
607 ->PatientFreeFormAllergyExtendedDetail;
609 if (!is_array($response)) {
610 $response = array($response);
613 foreach ($response as $response) {
614 $this->getStore()
615 ->updateErxUploadedByListId($response->ExternalId);
619 return isset($response);
623 * Insert or update medications for current patient
624 * @return integer Count of newly cached records
626 public function insertUpdateMedications()
628 $store = $this->getStore();
630 $insertedRows = 0;
632 $medArray = self::htmlFixXmlToArray(
633 $this->getPatientFullMedicationHistory6()
634 ->GetPatientFullMedicationHistory6Result
635 ->XmlResponse
637 $store->updatePrescriptionsActiveByPatientId($this->getPatientId());
638 if (is_array($medArray)) {
639 foreach ($medArray as $med) {
640 if ($med['DosageForm']) {
641 $optionIdDosageForm = $this->insertMissingListOptions(
642 'drug_form',
643 $med['DosageForm']
645 } else {
646 $optionIdDosageForm = null;
649 if ($med['Route']) {
650 $optionIdRoute = $this->insertMissingListOptions(
651 'drug_route',
652 $med['Route']
654 } else {
655 $optionIdRoute = null;
658 if ($med['StrengthUOM']) {
659 $optionIdStrengthUOM = $this->insertMissingListOptions(
660 'drug_units',
661 $med['StrengthUOM']
663 } else {
664 $optionIdStrengthUOM = null;
667 if ($med['DosageFrequencyDescription']) {
668 $optionIdFrequencyDescription = $this->insertMissingListOptions(
669 'drug_interval',
670 $med['DosageFrequencyDescription']
672 } else {
673 $optionIdFrequencyDescription = null;
676 $providerId = $store->selectUserIdByUserName($med['ExternalPhysicianID']);
678 $check = $store->selectPrescriptionIdByGuidPatientId(
679 $med['PrescriptionGuid'],
680 $med['ExternalPatientID']
683 $prescriptionId = '';
685 if (0 == sqlNumRows($check)) {
686 $prescriptionId = $store->insertPrescriptions(
687 $med,
688 $encounter,
689 $providerId,
690 $this->getAuthUserId(),
691 $optionIdDosageForm,
692 $optionIdRoute,
693 $optionIdStrengthUOM,
694 $optionIdFrequencyDescription
697 ++$insertedRows;
699 setListTouch($this->getPatientId(), 'prescription_erx');
700 } else {
701 $store->updatePrescriptions(
702 $med,
703 $providerId,
704 $this->getAuthUserId(),
705 $optionIdDosageForm,
706 $optionIdRoute,
707 $optionIdStrengthUOM,
708 $optionIdFrequencyDescription
712 $result = sqlFetchArray($check);
713 if ($result['id']) {
714 $prescriptionId = $result['id'];
717 // Making sure only transmitted prescriptions entry added into amc_misc_data for eRx Numerator
718 if (!empty($med['PharmacyNCPDP'])) {
719 processAmcCall(
720 'e_prescribe_amc',
721 true,
722 'add',
723 $med['ExternalPatientID'],
724 'prescriptions',
725 $prescriptionId
729 if ($med['FormularyChecked'] === 'true') {
730 processAmcCall('e_prescribe_chk_formulary_amc', true, 'add', $med['ExternalPatientID'], 'prescriptions', $prescriptionId);
735 return $insertedRows;