diff --git a/interface/main/tabs/menu/menus/standard.json b/interface/main/tabs/menu/menus/standard.json index ee399152dd8..d426a461096 100644 --- a/interface/main/tabs/menu/menus/standard.json +++ b/interface/main/tabs/menu/menus/standard.json @@ -501,7 +501,7 @@ { "label": "Manage Modules", "menu_id": "adm0", - "target": "pat", + "target": "adm", "url": "/interface/modules/zend_modules/public/Installer", "children": [], "requirement": 0, diff --git a/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerActionListener.php b/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerActionListener.php deleted file mode 100644 index 1f63efbf7d2..00000000000 --- a/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerActionListener.php +++ /dev/null @@ -1,106 +0,0 @@ - - * @copyright Copyright (c) 2024 Jerry Padgett - * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 - */ - -/* - * Currently register isn't supported and support should be a part of install. - * If an error needs to be reported to user, return description of error. - * However, whatever action trapped here has already occurred in Manager. - * Catch any exceptions because chances are they will be overlooked in Laminas module. - * Report them in return value. -*/ - -class ModuleManagerActionListener -{ - // Prevent instantiation - private function __construct() - { - } - - - /** - * @param $methodName - * @param $modId - * @param string $currentActionStatus - * @return string On method success a $currentAction status should be returned or error string. - */ - public static function moduleManagerAction($methodName, $modId, string $currentActionStatus = 'Success'): string - { - // Check if the action method exists - if (method_exists(self::class, $methodName)) { - return self::$methodName($modId, $currentActionStatus); - } else { - // TODO Perhaps this should be an exception! - return "Module cleanup method $methodName does not exist."; - } - } - - /** - * @param $modId - * @param $currentActionStatus - * @return mixed - */ - private static function install($modId, $currentActionStatus): mixed - { - return $currentActionStatus; - } - - /** - * @param $modId - * @param $currentActionStatus - * @return mixed - */ - private static function enable($modId, $currentActionStatus): mixed - { - return $currentActionStatus; - } - - /** - * @param $modId - * @param $currentActionStatus - * @return mixed - */ - private static function disable($modId, $currentActionStatus): mixed - { - return $currentActionStatus; - } - - /** - * @param $modId - * @param $currentActionStatus - * @return mixed - */ - private static function unregister($modId, $currentActionStatus): mixed - { - return $currentActionStatus; - } - - /** - * @param $modId - * @param $currentActionStatus - * @return mixed - */ - private static function install_sql($modId, $currentActionStatus): mixed - { - return $currentActionStatus; - } - - /** - * @param $modId - * @param $currentActionStatus - * @return mixed - */ - private static function upgrade_sql($modId, $currentActionStatus): mixed - { - return $currentActionStatus; - } -} diff --git a/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerAfterActionListener.php b/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerListener.php similarity index 95% rename from interface/modules/custom_modules/oe-module-faxsms/ModuleManagerAfterActionListener.php rename to interface/modules/custom_modules/oe-module-faxsms/ModuleManagerListener.php index 364b08299f4..7033a554240 100644 --- a/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerAfterActionListener.php +++ b/interface/modules/custom_modules/oe-module-faxsms/ModuleManagerListener.php @@ -32,7 +32,7 @@ $classLoader->registerNamespaceIfNotExists("OpenEMR\\Modules\\FaxSMS\\", __DIR__ . DIRECTORY_SEPARATOR . 'src'); */ -class ModuleManagerAfterActionListener extends AbstractModuleActionListener +class ModuleManagerListener extends AbstractModuleActionListener { public $service; private $authUser; @@ -75,9 +75,9 @@ public static function getModuleNamespace(): string * Required method to return this class object, * so it is instantiated in Laminas Manager. * - * @return ModuleManagerAfterActionListener + * @return ModuleManagerListener */ - public static function initListenerSelf(): ModuleManagerAfterActionListener + public static function initListenerSelf(): ModuleManagerListener { return new self(); } diff --git a/interface/modules/custom_modules/oe-module-weno/ModuleManagerAfterActionListener.php b/interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php similarity index 81% rename from interface/modules/custom_modules/oe-module-weno/ModuleManagerAfterActionListener.php rename to interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php index 50bbcf62341..ec2cee1275e 100644 --- a/interface/modules/custom_modules/oe-module-weno/ModuleManagerAfterActionListener.php +++ b/interface/modules/custom_modules/oe-module-weno/ModuleManagerListener.php @@ -31,10 +31,11 @@ */ use OpenEMR\Core\AbstractModuleActionListener; +use OpenEMR\Modules\WenoModule\Services\ModuleService; /* Allows maintenance of background tasks depending on Module Manager action. */ -class ModuleManagerAfterActionListener extends AbstractModuleActionListener +class ModuleManagerListener extends AbstractModuleActionListener { public function __construct() { @@ -73,9 +74,9 @@ public static function getModuleNamespace(): string * Required method to return this class object, * so it is instantiated in Laminas Manager. * - * @return ModuleManagerAfterActionListener + * @return ModuleManagerListener */ - public static function initListenerSelf(): ModuleManagerAfterActionListener + public static function initListenerSelf(): ModuleManagerListener { return new self(); } @@ -95,12 +96,33 @@ private function install($modId, $currentActionStatus): mixed * @param $currentActionStatus * @return mixed */ - private function enable($modId, $currentActionStatus): mixed + private function preenable($modId, $currentActionStatus): mixed { - $rtn = $this->setTaskState('1'); + $modService = new ModuleService(); + if ($modService->isWenoConfigured()) { + $modService::setModuleState($modId, '0', '0'); + return $currentActionStatus; + } + $modService::setModuleState($modId, '0', '1'); return $currentActionStatus; } + /** + * @param $modId + * @param $currentActionStatus + * @return mixed + */ + private function enable($modId, $currentActionStatus): mixed + { + $modService = new ModuleService(); + if ($modService->isWenoConfigured()) { + $modService::setModuleState($modId, '1', '0'); + return $currentActionStatus; + } + $modService::setModuleState($modId, '1', '1'); + return xlt("Weno eRx Service is not configured. Please configure Weno eRx Service in the Weno Module Setup."); + } + /** * @param $modId * @param $currentActionStatus @@ -108,7 +130,7 @@ private function enable($modId, $currentActionStatus): mixed */ private function disable($modId, $currentActionStatus): mixed { - $rtn = $this->setTaskState('0'); + ModuleService::setModuleState($modId, '0', '0'); return $currentActionStatus; } @@ -162,14 +184,4 @@ function getModuleRegistry($modId, $col = '*'): array return $registry; } - - /** - * @param $flag - * @return mixed - */ - private function setTaskState($flag): mixed - { - $sql_next = "UPDATE `background_services` SET `active` = ? WHERE `name` = ? OR `name` = ?"; - return sqlQuery($sql_next, array($flag, 'WenoExchange', 'WenoExchangePharmacies')); - } } diff --git a/interface/modules/custom_modules/oe-module-weno/info.txt b/interface/modules/custom_modules/oe-module-weno/info.txt index 16f17139939..930033b218f 100644 --- a/interface/modules/custom_modules/oe-module-weno/info.txt +++ b/interface/modules/custom_modules/oe-module-weno/info.txt @@ -1 +1 @@ -Weno EZ Integration Module \ No newline at end of file +Weno EZ Integration eRx Module v1.2.0 \ No newline at end of file diff --git a/interface/modules/custom_modules/oe-module-weno/moduleConfig.php b/interface/modules/custom_modules/oe-module-weno/moduleConfig.php index 6263c658fc8..60deab49f41 100644 --- a/interface/modules/custom_modules/oe-module-weno/moduleConfig.php +++ b/interface/modules/custom_modules/oe-module-weno/moduleConfig.php @@ -1,15 +1,20 @@ - * copyright Copyright (c )2021. Sherwin Gaddis - * license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 - * + * @package OpenEMR Module + * @link http://www.open-emr.org + * @author Jerry Padgett + * @copyright Copyright (c) 2023-24 Jerry Padgett + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ require_once dirname(__FILE__, 4) . '/globals.php'; $module_config = 1; +?> + + \ No newline at end of file diff --git a/interface/modules/custom_modules/oe-module-weno/openemr.bootstrap.php b/interface/modules/custom_modules/oe-module-weno/openemr.bootstrap.php index 62921dd37ef..96ce417af98 100644 --- a/interface/modules/custom_modules/oe-module-weno/openemr.bootstrap.php +++ b/interface/modules/custom_modules/oe-module-weno/openemr.bootstrap.php @@ -25,5 +25,5 @@ * @global EventDispatcher $eventDispatcher Injected by the OpenEMR module loader; */ -$bootstrap = new Bootstrap($eventDispatcher, $GLOBALS['kernel']); +$bootstrap = new Bootstrap($eventDispatcher); $bootstrap->subscribeToEvents(); diff --git a/interface/modules/custom_modules/oe-module-weno/public/assets/js/synch.js b/interface/modules/custom_modules/oe-module-weno/public/assets/js/synch.js index acce62f21af..3e6319247c0 100644 --- a/interface/modules/custom_modules/oe-module-weno/public/assets/js/synch.js +++ b/interface/modules/custom_modules/oe-module-weno/public/assets/js/synch.js @@ -1,55 +1,108 @@ -function sync_weno(){ - var syncIcon = document.getElementById("sync-icon"); - var syncAlert = document.getElementById("sync-alert"); +function sync_weno() { + top.restoreSession(); + const syncIcon = document.getElementById("sync-icon"); + const syncAlert = document.getElementById("sync-alert"); const url = '../../modules/custom_modules/oe-module-weno/templates/synch.php'; - + syncIcon.classList.add("fa-spin"); - + let formData = new FormData(); formData.append("key", "sync"); - + fetch(url, { method: 'POST', body: formData - }) - .then(response => { + }).then(response => { if (!response.ok) { - // If the response status code is not in the 200-299 range, reject the promise - throw new Error('Server responded with an error status: ' + response.status); + // If the response status code is not in the 200-299 range, reject the promise + throw new Error('Server responded with an error status: ' + response.status); } else { - //setting alert details - wenoAlertManager("success",syncAlert,syncIcon); + // setting alert details + wenoAlertManager("success", syncAlert, syncIcon); } - - }).catch(error=> { + }).catch(error => { console.log(error.message) - wenoAlertManager("failed",syncAlert,syncIcon); + wenoAlertManager("failed", syncAlert, syncIcon); }); } - -function wenoAlertManager(option, element, spinElement){ + +function wenoAlertManager(option, element, spinElement) { + top.restoreSession(); spinElement.classList.remove("fa-spin"); - if(option == "success"){ + if (option === "success") { element.classList.remove("d-none"); element.classList.add("alert", "alert-success"); - element.innerHTML = "Successfully updated"; - window.location.reload(); + element.innerHTML = "Successfully updated"; setTimeout( - function(){ + function () { element.classList.add("d-none"); element.classList.remove("alert", "alert-success"); - element.innerHTML = ""; + element.innerHTML = ""; + window.location.reload(); }, 3000 - ); - + ); + } else { - setTimeout(function(){ + setTimeout(function () { element.classList.add("d-none"); element.classList.remove("alert", "alert-danger"); - element.innerHTML = ""; - }, 3000); - element.classList.remove("d-none"); - element.classList.add("alert", "alert-danger"); - element.innerHTML = "An error occurred"; + element.innerHTML = ""; + }, 5000); + element.classList.remove("d-none"); + element.classList.add("alert", "alert-danger"); + element.innerHTML = "An error occurred possibly credentials are wrong. Please check the credentials and try again."; + } +} + +// Reserved for future use. +function renderDialog(action, uid, event) { + event.preventDefault(); + // Trim action URL + action = action.trim(); + // Get CSRF token + const csrf = document.getElementById("csrf_token_form").value || ''; + // Map URLs + const urls = { + 'demographics': '/interface/patient_file/summary/demographics_full.php', + 'user_settings': '/interface/super/edit_globals.php?mode=user', + 'weno_manage': '/interface/modules/custom_modules/oe-module-weno/templates/facilities.php', + 'users': '/interface/usergroup/user_admin.php' + }; + // Construct action URL + const urlPart = urls[action].includes('?') ? '&' : '?'; + const actionUrl = `${urls[action]}${urlPart}id=${encodeURIComponent(uid)}&csrf_token_form=${encodeURIComponent(csrf)}`; + + if (urls[action] === undefined) { + console.error('Invalid action URL'); + alert(action.toUpperCase() + " " + xl('Direct action not implemented yet.')); + return; } -} \ No newline at end of file + // Open modal dialog + dlgopen('', 'dialog-mod', '900', 'full', '', '', { + buttons: [ + /*{ + text: jsText('Click'), + close: false, + id: jsAttr('click-me'), + click: function () { + //tidyUp(); + }, + style: 'primary' + },*/ + { + text: jsText('Return to eRx Widget'), + close: true, + style: 'primary' + } + ], + allowResize: true, + allowDrag: true, + dialogId: 'error-dialog', + type: 'iframe', + resolvePromiseOn: 'close', + url: top.webroot_url + actionUrl + }).then(function (dialog) { + top.restoreSession(); + window.location.reload(); + }); +} diff --git a/interface/modules/custom_modules/oe-module-weno/scripts/file_download.php b/interface/modules/custom_modules/oe-module-weno/scripts/file_download.php index 501c3eaecca..c3bae6ab03f 100644 --- a/interface/modules/custom_modules/oe-module-weno/scripts/file_download.php +++ b/interface/modules/custom_modules/oe-module-weno/scripts/file_download.php @@ -5,12 +5,11 @@ require_once dirname(__DIR__, 4) . "/globals.php"; -use OpenEMR\Common\Logging\EventAuditLogger; - use OpenEMR\Common\Crypto\CryptoGen; -use OpenEMR\Modules\WenoModule\Services\LogDataInsert; -use OpenEMR\Modules\WenoModule\Services\WenoLogService; +use OpenEMR\Common\Logging\EventAuditLogger; use OpenEMR\Modules\WenoModule\Services\PharmacyService; +use OpenEMR\Modules\WenoModule\Services\TransmitProperties; +use OpenEMR\Modules\WenoModule\Services\WenoLogService; $cryptoGen = new CryptoGen(); $weno_username = $GLOBALS['weno_admin_username'] ?? ''; @@ -19,24 +18,18 @@ $baseurl = "https://online.wenoexchange.com/en/EPCS/DownloadPharmacyDirectory"; $data = array( - "UserEmail" => $weno_username, - "MD5Password" => md5($weno_password), - "ExcludeNonWenoTest" => "N", - "Daily" => "N" + "UserEmail" => $weno_username, + "MD5Password" => md5($weno_password), + "ExcludeNonWenoTest" => "N", + "Daily" => "N" ); if (date("l") == "Monday") { //if today is Monday download the weekly file $data["Daily"] = "N"; +} else { + $data["Daily"] = "Y"; } -//check if there is history of download, if not do a weekly file -// $pharmacyService = new PharmacyService(); -// $db_exist = $pharmacyService->checkWenoPharmacyLog(); -// if($db_exist != "empty"){ -// $data["Daily"] = "Y"; -// } - - $json_object = json_encode($data); $method = 'aes-256-cbc'; @@ -66,10 +59,21 @@ function download_zipfile($fileUrl, $zipped_file) fclose($fp); } +$comment = "User Initiated Unscheduled Daily Pharmacy Import"; +if ($data['Daily'] == 'N') { + $comment = "User Initiated Unscheduled Weekly Pharmacy Import"; +} +EventAuditLogger::instance()->newEvent( + "pharmacy_log", + $_SESSION['authUser'], + $_SESSION['authProvider'], + 1, + $comment +); + download_zipfile($fileUrl, $storelocation); $zip = new ZipArchive(); - $wenolog = new WenoLogService(); if ($zip->open($storelocation) === true) { @@ -81,18 +85,26 @@ function download_zipfile($fileUrl, $zipped_file) $filename = basename($csvFile); $csvFilename = $filename; - EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "File extracted successfully."); echo 'File extracted successfully.'; echo 'CSV filename: ' . text($csvFilename); $zip->close(); unlink($storelocation); } else { - EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "No CSV file found in the zip archive."); + EventAuditLogger::instance()->newEvent("pharmacy_log", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "No CSV file found in the zip archive."); echo 'No CSV file found in the zip archive.'; } } else { - EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "Failed to extract the file."); - echo 'Failed to extract the file.'; + $rpt = file_get_contents($storelocation); + $isError = $wenolog->scrapeWenoErrorHtml($rpt); + if ($isError['is_error']) { + error_log('Pharmacy download failed: ' . $isError['messageText']); + $wenolog->insertWenoLog("pharmacy", "loginfail"); + } + EventAuditLogger::instance()->newEvent("pharmacy_log", $_SESSION['authUser'], $_SESSION['authProvider'], 0, $isError['messageText']); + $wenolog->insertWenoLog("pharmacy", "Failed"); + // no need to continue + // send error to UI alert + die(js_escape($isError['messageText'])); } $insertPharmacy = new PharmacyService(); @@ -122,46 +134,43 @@ function download_zipfile($fileUrl, $zipped_file) if (!isset($line[1])) { continue; } - if (!isset($line[1])) { - continue; - } if (!empty($line)) { if ($data['Daily'] == 'N') { - $ncpdp = str_replace(['[', ']'], '', $line[3]); - $npi = str_replace(['[', ']'], '', $line[5]); - $business_name = $line[6]; - $address_line_1 = $line[7]; - $address_line_2 = $line[8]; - $city = $line[9]; - $state = $line[10]; + $ncpdp = str_replace(['[', ']'], '', $line[3] ?? ''); + $npi = str_replace(['[', ']'], '', $line[5] ?? ''); + $business_name = $line[6] ?? ''; + $address_line_1 = $line[7] ?? ''; + $address_line_2 = $line[8] ?? ''; + $city = $line[9] ?? ''; + $state = $line[10] ?? ''; $zipcode = str_replace(['[', ']'], '', $line[11]); - $country = $line[12]; - $international = $line[13]; + $country = $line[12] ?? ''; + $international = $line[13] ?? ''; $pharmacy_phone = str_replace(['[', ']'], '', $line[16]); - $on_weno = $line[21]; - $test_pharmacy = $line[17]; - $state_wide_mail = $line[18]; - $fullDay = $line[22]; + $on_weno = $line[21] ?? ''; + $test_pharmacy = $line[17] ?? ''; + $state_wide_mail = $line[18] ?? ''; + $fullDay = $line[22] ?? ''; } else { - $ncpdp = str_replace(['[', ']'], '', $line[3]); - $npi = str_replace(['[', ']'], '', $line[7]); - $business_name = $line[8]; - $city = $line[11]; - $state = $line[12]; - $zipcode = str_replace(['[', ']'], '', $line[14]); - $country = $line[15]; - $address_line_1 = $line[9]; - $address_line_2 = $line[10]; - $international = $line[16]; - $pharmacy_phone = str_replace(['[', ']'], '', $line[20]); - $county = $line[33]; - $on_weno = $line[37]; - $compounding = $line[41]; - $medicaid_id = $line[45]; - $dea = $line[44]; - $test_pharmacy = $line[29]; - $fullDay = $line[40]; - $state_wide_mail = $line[47]; + $ncpdp = str_replace(['[', ']'], '', $line[3] ?? ''); + $npi = str_replace(['[', ']'], '', $line[7] ?? ''); + $business_name = $line[8] ?? ''; + $city = $line[11] ?? ''; + $state = $line[12] ?? ''; + $zipcode = str_replace(['[', ']'], '', $line[14] ?? ''); + $country = $line[15] ?? ''; + $address_line_1 = $line[9] ?? ''; + $address_line_2 = $line[10] ?? ''; + $international = $line[16] ?? ''; + $pharmacy_phone = str_replace(['[', ']'], '', $line[20] ?? ''); + $county = $line[33] ?? ''; + $on_weno = $line[37] ?? ''; + $compounding = $line[41] ?? ''; + $medicaid_id = $line[45] ?? ''; + $dea = $line[44] ?? ''; + $test_pharmacy = $line[29] ?? ''; + $fullDay = $line[40] ?? ''; + $state_wide_mail = $line[47] ?? ''; } $insertdata['ncpdp'] = $ncpdp; @@ -185,25 +194,42 @@ function download_zipfile($fileUrl, $zipped_file) } else { $insertPharmacy->insertPharmacies($insertdata); } - ++$l; } } } catch (Exception $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); } - $wenolog->insertWenoLog("pharmacy", "Success"); + fclose($records); + // Start the transaction. sqlStatementNoLog('COMMIT'); sqlStatementNoLog('SET autocommit=1'); + // remove the files foreach ($files as $file) { if (is_file($file)) { unlink($file); } } - error_log("Pharmacy Imported"); + // let's brag about it. + EventAuditLogger::instance()->newEvent( + "pharmacy_log", + $_SESSION['authUser'], + $_SESSION['authProvider'], + 1, + "User Initiated Pharmacy Download was Imported Successfully." + ); + $wenolog->insertWenoLog("pharmacy", "Success"); + error_log("User Initialed Pharmacy Imported"); } else { + EventAuditLogger::instance()->newEvent( + "pharmacy_log", + $_SESSION['authUser'], + $_SESSION['authProvider'], + 0, + "Pharmacy Import download failed." + ); $wenolog->insertWenoLog("pharmacy", "Failed"); - error_log("file missing"); + error_log("User Initialed Pharmacy Import Failed File Missing"); } diff --git a/interface/modules/custom_modules/oe-module-weno/scripts/weno_log_sync.php b/interface/modules/custom_modules/oe-module-weno/scripts/weno_log_sync.php index 114c989c577..23033780f45 100644 --- a/interface/modules/custom_modules/oe-module-weno/scripts/weno_log_sync.php +++ b/interface/modules/custom_modules/oe-module-weno/scripts/weno_log_sync.php @@ -1,38 +1,61 @@ - * @copyright Copyright (c) 2021 Sherwin Gaddis - * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + * @package OpenEMR + * @link http://www.open-emr.org + * @author Sherwin Gaddis + * @author Jerry Padgett + * @copyright Copyright (c) 2021 Sherwin Gaddis + * @copyright Copyright (c) 2024 Jerry Padgett + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ use OpenEMR\Common\Crypto\CryptoGen; +use OpenEMR\Common\Logging\EventAuditLogger; use OpenEMR\Modules\WenoModule\Services\LogProperties; use OpenEMR\Modules\WenoModule\Services\WenoPharmaciesJson; function downloadWenoPharmacy() { $cryptoGen = new CryptoGen(); - $localPharmacyJson = new WenoPharmaciesJson( - $cryptoGen - ); + $localPharmacyJson = new WenoPharmaciesJson($cryptoGen); - //check if the background service is active and set intervals to once a day - //Weno has decided to not force the import of pharmacies since they are using the iframe - //and the pharmacy can be selected at the time of creating the prescription. + // Check if the background service is active. Intervals are set to once a day + // Weno has decided to not force the import of pharmacies since they are using the iframe + // and the pharmacy can be selected at the time of creating the prescription. $value = $localPharmacyJson->checkBackgroundService(); + + EventAuditLogger::instance()->newEvent( + "pharmacy_background", + $_SESSION['authUser'], + $_SESSION['authProvider'], + 1, + "Init Background Pharmacy Download Service Status:" . text(ucfirst($value)) + ); if ($value == 'active' || $value == 'live') { + error_log('Background Initiated Pharmacy Download Started.'); + $status = $localPharmacyJson->storePharmacyDataJson(); - error_log('Weno pharmacies download complete with status:' . text($status)); + + EventAuditLogger::instance()->newEvent( + "pharmacy_background", + $_SESSION['authUser'], + $_SESSION['authProvider'], + 1, + "Background Initiated Pharmacy Download Completed with Status:" . text($status) + ); + error_log('Background Initiated Weno Pharmacies download completed with status:' . text($status)); die; } } -function downloadWenoPrescriptionLog() +/** + * @throws Exception + */ +function downloadWenoPrescriptionLog(): void { - $logsync = new LogProperties(); - $logsync->logSync(); - error_log("Background services completed for prescription log"); + $logSync = new LogProperties(); + if (!$logSync->logSync()) { + error_log("Background services failed for prescription log."); + } } diff --git a/interface/modules/custom_modules/oe-module-weno/scripts/weno_pharmacy_search.php b/interface/modules/custom_modules/oe-module-weno/scripts/weno_pharmacy_search.php index ac4490daad1..a5641a68eae 100644 --- a/interface/modules/custom_modules/oe-module-weno/scripts/weno_pharmacy_search.php +++ b/interface/modules/custom_modules/oe-module-weno/scripts/weno_pharmacy_search.php @@ -6,14 +6,16 @@ * @package OpenEMR * @link https://www.open-emr.org * @author Kofi Appiah + * @author Jerry Padgett + * @copyright Copyright (c) 2024 Jerry Padgett * @copyright Copyright (c) 2023 Kofi Appiah * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ require_once(dirname(__DIR__, 5) . "/interface/globals.php"); -use OpenEMR\Common\Csrf\CsrfUtils; use OpenEMR\Common\Acl\AclMain; +use OpenEMR\Common\Csrf\CsrfUtils; use OpenEMR\Common\Twig\TwigContainer; if (!AclMain::aclCheckCore('patients', 'med')) { @@ -29,49 +31,53 @@ if (isset($_GET['searchFor']) && $_GET['searchFor'] == 'weno_city') { $return_arr = array(); - $term = filter_input(INPUT_GET, "term"); - $val = '%' . $term . '%'; - + $term = filter_input(INPUT_GET, "term"); + $val = $term . '%'; $params[] = $val; - $sql = "SELECT city, id FROM weno_pharmacy WHERE city LIKE ? LIMIT 10"; + $sql = "SELECT city, id FROM weno_pharmacy WHERE city LIKE ? GROUP BY city LIMIT 10"; $res = sqlStatement($sql, $params); while ($row = sqlFetchArray($res)) { - $return_arr[] = $row['city']; + $return_arr[] = ucwords(strtolower($row['city'])); } - echo text(json_encode($return_arr)); } if (isset($_GET['searchFor']) && $_GET['searchFor'] == 'weno_pharmacy') { - $term = filter_input(INPUT_GET, "term"); + $term = filter_input(INPUT_GET, "term"); $val = '%' . $term . '%'; $params[] = $val; $sql = "SELECT Business_Name, state, ncpdp, city, address_line_1 " . - "FROM weno_pharmacy WHERE Business_Name LIKE ?"; - - $weno_coverage = $_GET['coverage'] ?: ''; - $weno_state = $_GET['weno_state'] ?: ''; - $weno_city = $_GET['weno_city'] ?: ''; - $full_day = $_GET['full_day'] ? 'Yes' : ''; - $weno_only = $_GET['weno_only'] ? 'True' : ''; - $weno_zipcode = $_GET['weno_zipcode'] ?: ''; - $weno_test_pharmacies = $_GET['test_pharmacy'] ? 'True' : ''; + "FROM weno_pharmacy WHERE Business_Name LIKE ?"; + $weno_coverage = $_GET['coverage'] ?? false ?: ''; + $weno_state = $_GET['weno_state'] ?? false ?: ''; + $weno_city = $_GET['weno_city'] ?? false ?: ''; + $full_day = !empty($_GET['full_day']) ? 'Yes' : ''; + $weno_only = !empty($_GET['weno_only']) ? 'True' : ''; + $weno_zipcode = $_GET['weno_zipcode'] ?? false ?: ''; + $weno_test_pharmacies = !empty($_GET['test_pharmacy']) ? 'True' : ''; if (!empty($weno_coverage)) { $sql .= " AND state_wide_mail_order = ?"; $params[] = $weno_coverage; } - if (!empty($weno_state)) { - $sql .= " AND state = ?"; - $params[] = $weno_state; - } - if (!empty($weno_city)) { - $sql .= " AND city = ?"; - $params[] = $weno_city; + + // if a zip, search by it and forget city and state + if (empty($weno_zipcode)) { + if (!empty($weno_city)) { + $sql .= " AND city = ?"; + $params[] = $weno_city; + } + if (!empty($weno_state)) { + $sql .= " AND state = ?"; + $params[] = $weno_state; + } + } else { + $sql .= " AND ZipCode = ?"; + $params[] = $weno_zipcode; } if (!empty($weno_only)) { $sql .= " AND on_weno = ?"; @@ -81,10 +87,6 @@ $sql .= " AND 24HR = ?"; $params[] = $full_day; } - if (!empty($weno_zipcode)) { - $sql .= " AND ZipCode = ?"; - $params[] = $weno_zipcode; - } if (!empty($weno_test_pharmacies)) { $sql .= " AND test_pharmacy = ?"; $params[] = $weno_test_pharmacies; @@ -92,10 +94,11 @@ $sql .= " ORDER BY Business_Name ASC"; + $return_arr = []; $res = sqlStatement($sql, $params); while ($row = sqlFetchArray($res)) { $return_arr[] = array( - "name" => $row['Business_Name'] . "/ " . $row['address_line_1'] . " / " . $row['city'], + "name" => ucwords(strtolower($row['Business_Name'] . " " . $row['address_line_1'] . " " . $row['city'])), "ncpdp" => $row['ncpdp'] ); } @@ -103,32 +106,34 @@ } if (isset($_GET['searchFor']) && $_GET['searchFor'] == 'weno_drop') { - $sql = "SELECT Business_Name, state, ncpdp, city, address_line_1 " . - "FROM weno_pharmacy WHERE 1=1"; + $sql = "SELECT Business_Name, state, ncpdp, city, address_line_1 " . "FROM weno_pharmacy WHERE 1=1"; - $weno_coverage = $_GET['coverage'] ?: ''; - $weno_state = $_GET['weno_state'] ?: ''; - $weno_city = $_GET['weno_city'] ?: ''; - $full_day = $_GET['full_day'] ? 'Yes' : ''; - $weno_zipcode = $_GET['weno_zipcode'] ?: ''; + $weno_coverage = $_GET['coverage'] ?: ''; + $weno_state = $_GET['weno_state'] ?: ''; + $weno_city = $_GET['weno_city'] ?: ''; + $full_day = $_GET['full_day'] ? 'Yes' : ''; + $weno_zipcode = $_GET['weno_zipcode'] ?: ''; $weno_test_pharmacies = $_GET['test_pharmacy'] == 'true' ? 'True' : ''; - if (!empty($weno_state)) { - $sql .= " AND state = ?"; - $params[] = $weno_state; - } - if (!empty($weno_zipcode)) { + // if a zip, search by it and forget city and state + if (empty($weno_zipcode)) { + if (!empty($weno_city)) { + $sql .= " AND city = ?"; + $params[] = $weno_city; + } + if (!empty($weno_state)) { + $sql .= " AND state = ?"; + $params[] = $weno_state; + } + } else { $sql .= " AND ZipCode = ?"; $params[] = $weno_zipcode; } + if (!empty($weno_coverage)) { $sql .= " AND state_wide_mail_order = ?"; $params[] = $weno_coverage; } - if (!empty($weno_city)) { - $sql .= " AND city = ?"; - $params[] = $weno_city; - } if (!empty($full_day)) { $sql .= " AND 24HR = ?"; $params[] = $full_day; @@ -140,11 +145,11 @@ $sql .= " ORDER BY Business_Name ASC"; - $res = sqlStatement($sql, $params); $return_arr = []; + $res = sqlStatement($sql, $params); while ($row = sqlFetchArray($res)) { $return_arr[] = array( - "name" => $row['Business_Name'] . "/ " . $row['address_line_1'] . " / " . $row['city'], + "name" => ucwords(strtolower($row['Business_Name'] . " " . $row['address_line_1'] . " " . $row['city'])), "ncpdp" => $row['ncpdp'] ); } diff --git a/interface/modules/custom_modules/oe-module-weno/sql/table.sql b/interface/modules/custom_modules/oe-module-weno/sql/table.sql index 5e1bf0da3a2..0c9bdf59a8e 100644 --- a/interface/modules/custom_modules/oe-module-weno/sql/table.sql +++ b/interface/modules/custom_modules/oe-module-weno/sql/table.sql @@ -62,9 +62,9 @@ CREATE TABLE `weno_download_log` ( ) ENGINE=InnoDB; #EndIf --- For early adopters of weno, incase they need to upgrade to the next/latest release of openEMR +-- For early adopters of weno, in case they need to upgrade let's delete and add below. #IfRow background_services name WenoExchange -UPDATE `background_services` SET title="Weno Log Sync", `function`="downloadWenoPrescriptionLog", `require_once`="/interface/modules/custom_modules/oe-module-weno/scripts/weno_log_sync.php" WHERE `name`="WenoExchange"; +DELETE FROM `background_services` WHERE `name` = 'WenoExchange'; #EndIf #IfNotRow background_services name WenoExchangePharmacies @@ -78,10 +78,10 @@ VALUES ('WenoExchange', 'Weno Log Sync', '0', '0', current_timestamp(), '30', 'd #EndIf #IfRow globals gl_name weno_provider_password -UPDATE `globals` SET gl_name="weno_admin_password" WHERE gl_name="weno_provider_password"; +UPDATE `globals` SET gl_name='weno_admin_password' WHERE gl_name='weno_provider_password'; #EndIf #IfRow globals gl_name weno_provider_username -UPDATE `globals` SET gl_name="weno_admin_username" WHERE gl_name="weno_provider_username"; +UPDATE `globals` SET gl_name='weno_admin_username' WHERE gl_name='weno_provider_username'; #EndIf diff --git a/interface/modules/custom_modules/oe-module-weno/src/Bootstrap.php b/interface/modules/custom_modules/oe-module-weno/src/Bootstrap.php index f02284bb5aa..86e31b9fefb 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Bootstrap.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Bootstrap.php @@ -1,21 +1,32 @@ + * @author Jerry Padgett + * @copyright Copyright (c) 2023 Omega Systems Group + * @copyright Copyright (c) 2024 Jerry Padgett + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + */ + namespace OpenEMR\Modules\WenoModule; -use OpenEMR\Modules\WenoModule\WenoGlobalConfig; use OpenEMR\Common\Logging\SystemLogger; -use OpenEMR\Core\Kernel; use OpenEMR\Events\Globals\GlobalsInitializedEvent; +use OpenEMR\Events\Patient\PatientBeforeCreatedAuxEvent; +use OpenEMR\Events\Patient\PatientUpdatedEventAux; +use OpenEMR\Events\PatientDemographics\RenderEvent as pRenderEvent; +use OpenEMR\Events\PatientDemographics\RenderPharmacySectionEvent; +use OpenEMR\Menu\MenuEvent; +use OpenEMR\Modules\WenoModule\Services\ModuleService; +use OpenEMR\Modules\WenoModule\Services\SelectedPatientPharmacy; use OpenEMR\Services\Globals\GlobalSetting; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Twig\Environment; -use OpenEMR\Menu\MenuEvent; -use OpenEMR\Events\PatientDemographics\RenderEvent as pRenderEvent; -use OpenEMR\Events\PatientDemographics\RenderPharmacySectionEvent; -use OpenEMR\Events\Patient\PatientBeforeCreatedAuxEvent; -use OpenEMR\Events\Patient\PatientUpdatedEventAux; -use OpenEMR\Modules\WenoModule\Services\SelectedPatientPharmacy; class Bootstrap { @@ -33,6 +44,7 @@ class Bootstrap /** * The OpenEMR Twig Environment + * * @var Environment */ private $twig; @@ -47,18 +59,24 @@ class Bootstrap */ private $logger; - private $modulePath; + private string $modulePath; /** * @var SelectedPatientPharmacy */ - private $selectedPatientPharmacy; + private SelectedPatientPharmacy $selectedPatientPharmacy; /** * @return void */ - public function subscribeToEvents() + public function subscribeToEvents(): void { + $modService = new ModuleService(); + if (!$modService->isWenoConfigured()) { + // let Admin configure Weno if module is not configured. + $this->addGlobalSettings(); + return; + } $this->addGlobalSettings(); $this->registerMenuItems(); $this->registerDemographicsEvents(); @@ -66,16 +84,13 @@ public function subscribeToEvents() $this->demographicsDisplaySelectedEvents(); $this->patientSaveEvents(); $this->patientUpdateEvents(); + $modService::setModuleState('oe-module-weno', '1', '0'); } - public function __construct(EventDispatcher $dispatcher, ?Kernel $kernel = null) + public function __construct(EventDispatcher $dispatcher) { - if (empty($kernel)) { - $kernel = new Kernel(); - } $this->eventDispatcher = $dispatcher; - - $this->globalsConfig = new WenoGlobalConfig($GLOBALS); + $this->globalsConfig = new WenoGlobalConfig(); $this->moduleDirectoryName = basename(dirname(__DIR__)); $this->modulePath = dirname(__DIR__); $this->logger = new SystemLogger(); @@ -85,7 +100,7 @@ public function __construct(EventDispatcher $dispatcher, ?Kernel $kernel = null) /** * @return \Twig\Environment */ - public function getTwig() + public function getTwig(): Environment { return $this->twig; } @@ -94,7 +109,7 @@ public function getTwig() * @param GlobalsInitializedEvent $event * @return void */ - public function addGlobalWenoSettings(GlobalsInitializedEvent $event) + public function addGlobalWenoSettings(GlobalsInitializedEvent $event): void { $settings = $this->globalsConfig->getGlobalSettingSectionConfiguration(); @@ -139,7 +154,7 @@ public function addGlobalWenoSettings(GlobalsInitializedEvent $event) /** * @return void */ - public function registerDemographicsEvents() + public function registerDemographicsEvents(): void { $this->eventDispatcher->addListener(pRenderEvent::EVENT_SECTION_LIST_RENDER_BEFORE, [$this, 'renderWenoSection']); } @@ -148,49 +163,49 @@ public function registerDemographicsEvents() * @param pRenderEvent $event * @return void */ - public function renderWenoSection(pRenderEvent $event) + public function renderWenoSection(pRenderEvent $event): void { $path = __DIR__; $path = str_replace("src", "templates", $path); $pid = $event->getPid(); ?>
- - -
-
+ + +
+ eventDispatcher->addListener(GlobalsInitializedEvent::EVENT_HANDLE, [$this, 'addGlobalWenoSettings']); } @@ -198,7 +213,7 @@ public function addGlobalSettings() /** * @return void */ - public function registerMenuItems() + public function registerMenuItems(): void { $this->eventDispatcher->addListener(MenuEvent::MENU_UPDATE, [$this, 'addCustomMenuItem']); } @@ -207,7 +222,7 @@ public function registerMenuItems() * @param MenuEvent $event * @return MenuEvent */ - public function addCustomMenuItem(MenuEvent $event) + public function addCustomMenuItem(MenuEvent $event): MenuEvent { $menu = $event->getMenu(); //Prescription Log @@ -260,7 +275,7 @@ public function addCustomMenuItem(MenuEvent $event) /** * @return void */ - public function demographicsSelectorEvents() + public function demographicsSelectorEvents(): void { $this->eventDispatcher->addListener(RenderPharmacySectionEvent::RENDER_AFTER_PHARMACY_SECTION, [$this, 'renderWenoPharmacySelector']); } @@ -268,7 +283,7 @@ public function demographicsSelectorEvents() /** * @return void */ - public function renderWenoPharmacySelector() + public function renderWenoPharmacySelector(): void { include_once($this->modulePath) . "/templates/pharmacy_list_form.php"; } @@ -276,7 +291,7 @@ public function renderWenoPharmacySelector() /** * @return void */ - public function demographicsDisplaySelectedEvents() + public function demographicsDisplaySelectedEvents(): void { $this->eventDispatcher->addListener(RenderPharmacySectionEvent::RENDER_AFTER_SELECTED_PHARMACY_SECTION, [$this, 'renderSelectedWenoPharmacies']); } @@ -284,7 +299,7 @@ public function demographicsDisplaySelectedEvents() /** * @return void */ - public function renderSelectedWenoPharmacies() + public function renderSelectedWenoPharmacies(): void { echo "
"; include_once($this->modulePath) . "/templates/pharmacy_list_display.php"; @@ -293,7 +308,7 @@ public function renderSelectedWenoPharmacies() /** * @return void */ - public function patientSaveEvents() + public function patientSaveEvents(): void { $this->eventDispatcher->addListener(PatientBeforeCreatedAuxEvent::EVENT_HANDLE, [$this, 'persistPatientWenoPharmacies']); } @@ -302,7 +317,7 @@ public function patientSaveEvents() * @param PatientBeforeCreatedAuxEvent $event * @return void */ - public function persistPatientWenoPharmacies(PatientBeforeCreatedAuxEvent $event) + public function persistPatientWenoPharmacies(PatientBeforeCreatedAuxEvent $event): void { $patientData = $event->getPatientData(); $this->selectedPatientPharmacy->prepSelectedPharmacy($patientData); @@ -311,7 +326,7 @@ public function persistPatientWenoPharmacies(PatientBeforeCreatedAuxEvent $event /** * @return void */ - public function patientUpdateEvents() + public function patientUpdateEvents(): void { $this->eventDispatcher->addListener(PatientUpdatedEventAux::EVENT_HANDLE, [$this, 'updatePatientWenoPharmacies']); } @@ -320,7 +335,7 @@ public function patientUpdateEvents() * @param PatientUpdatedEventAux $event * @return void */ - public function updatePatientWenoPharmacies(PatientUpdatedEventAux $event) + public function updatePatientWenoPharmacies(PatientUpdatedEventAux $event): void { $updatedPatientData = $event->getUpdatedPatientData(); $this->selectedPatientPharmacy->prepForUpdatePharmacy($updatedPatientData); diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/Container.php b/interface/modules/custom_modules/oe-module-weno/src/Services/Container.php deleted file mode 100644 index 1bca4abed7b..00000000000 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/Container.php +++ /dev/null @@ -1,61 +0,0 @@ - - * @copyright Copyright (c) 2021 Sherwin Gaddis - * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 - */ - -namespace OpenEMR\Modules\WenoModule\Services; - -/** - * Class Container - * @package OpenEMR\Rx\Weno - */ -class Container -{ - private $transmitproperties; - private $logproperties; - private $facilityproperties; - private $wenopharmacyimport; - - public function __construct() - { - //do epic stuff here ... - } - - /** - * @return TransmitProperties - */ - public function getTransmitproperties(): TransmitProperties - { - if ($this->transmitproperties === null) { - $this->transmitproperties = new TransmitProperties(); - } - return $this->transmitproperties; - } - - /** - * @return LogProperties - */ - public function getLogproperties(): LogProperties - { - if ($this->logproperties === null) { - $this->logproperties = new LogProperties(); - } - return $this->logproperties; - } - - /** - * @return FacilityProperties - */ - public function getFacilityproperties(): FacilityProperties - { - if ($this->facilityproperties === null) { - $this->facilityproperties = new FacilityProperties(); - } - return $this->facilityproperties; - } -} diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/DownloadWenoPharmacies.php b/interface/modules/custom_modules/oe-module-weno/src/Services/DownloadWenoPharmacies.php index 553e50a3099..ba99ffdfd00 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/DownloadWenoPharmacies.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/DownloadWenoPharmacies.php @@ -1,11 +1,13 @@ - * @copyright Copyright (c) 2023 Sherwin Gaddis - * + * @package OpenEMR + * @link http://www.open-emr.org + * @author Sherwin Gaddis + * @author Jerry Padgett + * @copyright Copyright (c) 2023 Sherwin Gaddis + * @copyright Copyright (c) 2024 Jerry Padgett + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ namespace OpenEMR\Modules\WenoModule\Services; @@ -13,10 +15,11 @@ use OpenEMR\Common\Logging\EventAuditLogger; use OpenEMR\Modules\WenoModule\Services\WenoLogService; use OpenEMR\Modules\WenoModule\Services\WenoPharmaciesImport; +use ZipArchive; class DownloadWenoPharmacies { - public function retrieveDataFile($url, $storelocation) + public function retrieveDataFile($url, $storelocation): ?string { $path_to_extract = $storelocation; $storelocation .= "weno_pharmacy.zip"; @@ -26,13 +29,11 @@ public function retrieveDataFile($url, $storelocation) $fp = fopen($storelocation, 'w+'); $ch = curl_init($url); - curl_setopt($ch, CURLOPT_FILE, $fp); curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1); curl_setopt($ch, CURLOPT_TIMEOUT, 1000); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0'); curl_exec($ch); - curl_close($ch); fclose($fp); @@ -44,10 +45,10 @@ public function retrieveDataFile($url, $storelocation) } } - public function extractFile($path_to_extract, $storelocation) + public function extractFile($path_to_extract, $storelocation): ?string { - $zip = new \ZipArchive(); - $wenolog = new WenoLogService(); + $zip = new ZipArchive(); + $wenoLog = new WenoLogService(); $import = new WenoPharmaciesImport(); if ($zip->open($storelocation) === true) { @@ -59,13 +60,6 @@ public function extractFile($path_to_extract, $storelocation) $filename = basename($csvFile); $csvFilename = $filename; - EventAuditLogger::instance()->newEvent( - "prescriptions_log", - $_SESSION['authUser'], - $_SESSION['authProvider'], - 1, - "File extracted successfully." - ); echo 'File extracted successfully.'; echo 'CSV filename: ' . text($csvFilename); @@ -73,29 +67,36 @@ public function extractFile($path_to_extract, $storelocation) unlink($storelocation); $result = $import->importPharmacy($csvFile, $files); if ($result == "Imported") { - $wenolog->insertWenoLog("pharmacy", "Success"); - return $result; + $wenoLog->insertWenoLog("pharmacy", "Success"); } else { - return $result; + $wenoLog->insertWenoLog("pharmacy", "Failed"); } - - return "Imported"; + return $result; } else { EventAuditLogger::instance()->newEvent( - "prescriptions_log", + "pharmacy_log", $_SESSION['authUser'], $_SESSION['authProvider'], - 1, + 0, "No CSV file found in the zip archive." ); - $wenolog->insertWenoLog("pharmacy", "Failed"); + $wenoLog->insertWenoLog("pharmacy", "Failed"); return "Failed"; } } else { - EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "Failed to extract the file."); - echo 'Failed to extract the file.'; - error_log("Failed to extract the file."); - $wenolog->insertWenoLog("pharmacy", "Failed"); + $scrape = file_get_contents($storelocation); + $wenolog = new WenoLogService(); + $isError = $wenolog->scrapeWenoErrorHtml($scrape); + if ($isError['is_error']) { + EventAuditLogger::instance()->newEvent("pharmacy_background", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "Pharmacy Failed download! Weno error: " . $isError['messageText']); + error_log('Pharmacy download failed: ' . $isError['messageText']); + $wenolog->insertWenoLog("pharmacy", "loginfail"); + } else { + EventAuditLogger::instance()->newEvent("pharmacy_background", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "Pharmacy Failed download! Weno error Other"); + error_log("Pharmacy Failed download! Weno error: Other"); + } + $wenoLog->insertWenoLog("pharmacy", "Failed"); + die; } } } diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/LogDataInsert.php b/interface/modules/custom_modules/oe-module-weno/src/Services/LogDataInsert.php index 3b21c4ade65..cd3a226a98f 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/LogDataInsert.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/LogDataInsert.php @@ -27,7 +27,6 @@ public function insertPrescriptions($insertdata) $sql .= "patient_id = ?, "; $sql .= "provider_id = ?, "; $sql .= "drug = ?, "; - $sql .= "encounter = ?, "; $sql .= "quantity = ?, "; $sql .= "refills = ?, "; $sql .= "substitute = ?,"; @@ -42,7 +41,6 @@ public function insertPrescriptions($insertdata) $insertdata['patient_id'], $insertdata['user_id'], $insertdata['drug'], - $insertdata['encounter'], $insertdata['quantity'], $insertdata['refills'], $insertdata['substitute'], diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/LogImportBuild.php b/interface/modules/custom_modules/oe-module-weno/src/Services/LogImportBuild.php index 20abc03f089..0339132d9e0 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/LogImportBuild.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/LogImportBuild.php @@ -13,6 +13,11 @@ class LogImportBuild { public $rxsynclog; + private $insertdata; + /** + * @var mixed|null + */ + private mixed $messageid; public function __construct() { @@ -28,7 +33,7 @@ public function getUserIdByWenoId($external_provider_id) return $provider['id']; } else { // logged in user is auth weno user so let's ensure a user is set. - return $_SESSION["authUserID"] ?? null; + return "REQED:{users}" . xlt("Weno Provider Id missing. Select Admin then Users and edit the user to add Weno Provider Id"); } } @@ -54,7 +59,7 @@ public function checkMessageId() return $entry['count']; } - public function buildInsertArray() + public function buildInsertArray(): bool|string { $l = 0; if (file_exists($this->rxsynclog)) { @@ -71,29 +76,29 @@ public function buildInsertArray() continue; } if (isset($line[4])) { - $this->messageid = $line[4] ?? null; + $this->messageid = $line[4] ?? ''; $is_saved = $this->checkMessageId(); if ($is_saved > 0) { continue; } } if (!empty($line)) { - $pr = $line[2] ?? null; + $pr = $line[2] ?? ''; $provider = explode(":", $pr); - $windate = $line[16] ?? null; + $windate = $line[16] ?? ''; $idate = substr(trim($windate), 0, -5); $idate = explode(" ", $idate); $idate = explode("/", $idate[0]); - $year = $idate[2] ?? null; - $month = $idate[0] ?? null; - $day = $idate[1] ?? null; + $year = $idate[2] ?? ''; + $month = $idate[0] ?? ''; + $day = $idate[1] ?? ''; $idate = $year . '-' . $month . '-' . $day; $ida = preg_replace('/T/', ' ', $line[0]); - $p = $line[1] ?? null; + $p = $line[1] ?? ''; $pid_and_encounter = explode(":", $p); $pid = intval($pid_and_encounter[0]); - $encounter = intval($pid_and_encounter[1]); - $r = $line[22] ?? null; + $uid = intval($pid_and_encounter[1]); + $r = $line[22] ?? ''; $refills = filter_var($r, FILTER_SANITIZE_NUMBER_INT); $insertdata = []; @@ -103,28 +108,28 @@ public function buildInsertArray() $insertdata['active'] = $active; $insertdata['date_added'] = $ida; $insertdata['patient_id'] = $pid; - $insertdata['encounter'] = $encounter; + $insertdata['attached_user_id'] = $uid; $drug = isset($line[11]) ? str_replace('"', '', $line[11]) : xlt("Incomplete"); $insertdata['drug'] = $drug; - $insertdata['quantity'] = $line[18] ?? null; + $insertdata['quantity'] = $line[18] ?? ''; $insertdata['refills'] = $refills; $sub = ($line[14] = 'Allowed' ? 1 : 0); - $insertdata['substitute'] = $sub ?? null; - $insertdata['note'] = $line[21] ?? null; - $insertdata['rxnorm_drugcode'] = $line[12] ?? null; + $insertdata['substitute'] = $sub ?? ''; + $insertdata['note'] = $line[21] ?? ''; + $insertdata['rxnorm_drugcode'] = $line[12] ?? ''; $insertdata['provider_id'] = $provider[0]; - $insertdata['user_id'] = $this->getUserIdByWenoId($provider[0]); - $insertdata['prescriptionguid'] = $line[4] ?? null; + $insertdata['user_id'] = ($uid > 0) ? $uid : $this->getUserIdByWenoId($provider[0]); + $insertdata['prescriptionguid'] = $line[4] ?? ''; $insertdata['txDate'] = $ida; $loginsert = new LogDataInsert(); $loginsert->insertPrescriptions($insertdata); - ++$l; } } fclose($records); } else { - echo "File is missing!"; + return xlt("File is missing!"); } + return true; } } diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/LogProperties.php b/interface/modules/custom_modules/oe-module-weno/src/Services/LogProperties.php index 8fcd64de1e9..78efe828387 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/LogProperties.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/LogProperties.php @@ -1,11 +1,13 @@ - * @copyright Copyright (c) 2020 Sherwin Gaddis - * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + * @package OpenEMR + * @link http://www.open-emr.org + * @author Sherwin Gaddis + * @author Jerry Padgett + * @copyright Copyright (c) 2020 Sherwin Gaddis + * @copyright Copyright (c) 2024 Jerry Padgett + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ namespace OpenEMR\Modules\WenoModule\Services; @@ -13,7 +15,6 @@ use Exception; use OpenEMR\Common\Crypto\CryptoGen; use OpenEMR\Common\Logging\EventAuditLogger; -use OpenEMR\Modules\WenoModule\Services\WenoLogService; class LogProperties { @@ -70,11 +71,11 @@ public function __construct() $this->cryptoGen = new CryptoGen(); $this->method = "aes-256-cbc"; $this->rxsynclog = $GLOBALS['OE_SITE_DIR'] . "/documents/logs_and_misc/weno/logsync.csv"; - $this->enc_key = $this->cryptoGen->decryptStandard($GLOBALS['weno_encryption_key']); + $this->enc_key = $this->cryptoGen->decryptStandard($GLOBALS['weno_encryption_key'] ?? ''); $this->key = substr(hash('sha256', $this->enc_key, true), 0, 32); $this->iv = chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0); - $this->weno_admin_email = $GLOBALS['weno_admin_username']; - $this->weno_admin_password = $this->cryptoGen->decryptStandard($GLOBALS['weno_admin_password']); + $this->weno_admin_email = $GLOBALS['weno_admin_username'] ?? ''; + $this->weno_admin_password = $this->cryptoGen->decryptStandard($GLOBALS['weno_admin_password'] ?? ''); } /** @@ -83,8 +84,8 @@ public function __construct() public function logEpcs() { $email['email'] = $this->weno_admin_email; - $prov_pass = $this->weno_admin_password; // gets the password stored for the - $md5 = md5($prov_pass); // hash the current password + $prov_pass = $this->weno_admin_password; // gets the password stored for the + $md5 = md5($prov_pass); // hash the current password $workday = date("l"); //Checking Saturday for any prescriptions that were written. if ($workday == 'Monday') { @@ -147,8 +148,7 @@ public function logSync() error_log("Cipher failure check encryption key", time()); exit; } - //**warning** do not add urlencode to $provider_info['email'] per Weno design - $urlOut = $syncLogs . $provider_info['email'] . "&data=" . urlencode($logurlparam); + $urlOut = $syncLogs . urlencode($provider_info['email']) . "&data=" . urlencode($logurlparam); $ch = curl_init($urlOut); curl_setopt($ch, CURLOPT_TIMEOUT, 200); @@ -161,37 +161,50 @@ public function logSync() curl_close($ch); if ($statusCode == 200) { file_put_contents($this->rxsynclog, $rpt); - $logstring = "prescription log import initiated successfully"; - EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "$logstring"); + $isError = $wenolog->scrapeWenoErrorHtml($rpt); + if ($isError['is_error']) { + $error = $isError['messageText']; + error_log('Prescription download failed: ' . $error); + $wenolog->insertWenoLog("prescription", "loginfail"); + $wenolog->insertWenoLog("prescription", "Failed"); + EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 0, $error); + die(js_escape($error)); + } $wenolog->insertWenoLog("prescription", "Success"); } else { - EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 1, "$statusCode"); + // yes record failures. + EventAuditLogger::instance()->newEvent("prescriptions_log", $_SESSION['authUser'], $_SESSION['authProvider'], 0, "$statusCode"); $wenolog->insertWenoLog("prescription", "Failed"); return false; } if (file_exists($this->rxsynclog)) { $log = new LogImportBuild(); - $log->buildInsertArray(); - return true; + $rtn = $log->buildInsertArray(); + if (!$rtn) { + return false; + } + } else { + return false; } + return true; } /** * @return mixed */ - public function getProviderEmail() + public function getProviderEmail(): mixed { if ($_SESSION['authUser']) { $provider_info = ['email' => $GLOBALS['weno_provider_email']]; - if (!empty($provider_info)) { + if (!empty($provider_info['email'])) { return $provider_info; } else { - $error = xlt("Provider email address is missing. Go to [User settings > Email] to add provider's weno registered email address"); + $error = xlt("Provider email address is missing. Go to User settings Email to add provider's weno registered email address"); error_log($error); - TransmitProperties::errorWithDie($error); + TransmitProperties::echoError($error); } - } else if ($GLOBALS['weno_admin_username']) { + } elseif ($GLOBALS['weno_admin_username'] ?? false) { $provider_info["email"] = $GLOBALS['weno_admin_username']; return $provider_info; } else { @@ -205,7 +218,7 @@ public function getProviderEmail() /** * @return mixed */ - public function getProviderPassword() + public function getProviderPassword(): mixed { if ($_SESSION['authUser']) { if (!empty($GLOBALS['weno_admin_password'])) { @@ -214,7 +227,7 @@ public function getProviderPassword() echo xlt('Provider Password is missing'); die; } - } else if ($GLOBALS['weno_admin_password']) { + } elseif ($GLOBALS['weno_admin_password']) { return $this->cryptoGen->decryptStandard($GLOBALS['weno_admin_password']); } else { error_log("Admin password not set"); diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php b/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php new file mode 100644 index 00000000000..bc8281155e4 --- /dev/null +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/ModuleService.php @@ -0,0 +1,179 @@ + + * @copyright Copyright (c) 2023-2024 Jerry Padgett + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + */ + +namespace OpenEMR\Modules\WenoModule\Services; + +use OpenEMR\Common\Crypto\CryptoGen; + +/** + * Companion to event bootstrapping + */ +class ModuleService +{ + public function __construct() + { + } + + /** + * @param $flag + * @param bool $reset + * @return array|bool|null + */ + public static function setTaskState($flag, bool $reset = false): array|bool|null + { + $sql_next = "UPDATE `background_services` SET `active` = ? WHERE `name` = ? OR `name` = ?"; + if ($reset) { + $sql_next = "UPDATE `background_services` SET `active` = ?, next_run = NOW() WHERE `name` = ? OR `name` = ?"; + } + return sqlQuery($sql_next, array($flag, 'WenoExchange', 'WenoExchangePharmacies')); + } + + /** + * @return array + */ + public function getVendorGlobals($decrypt = true): array + { + $vendors['weno_rx_enable'] = '0'; + $vendors['weno_rx_enable_test'] = '0'; + $vendors['weno_encryption_key'] = ''; + $vendors['weno_admin_username'] = ''; + $vendors['weno_admin_password'] = ''; + $vendors['weno_secondary_encryption_key'] = ''; + $vendors['weno_secondary_admin_username'] = ''; + $vendors['weno_secondary_admin_password'] = ''; + + $gl = sqlStatementNoLog( + "SELECT gl_name, gl_value FROM `globals` WHERE `gl_name` IN(?, ?, ?, ?, ?, ?, ?, ?)", + array("weno_rx_enable", "weno_rx_enable_test", "weno_encryption_key", "weno_admin_username", "weno_admin_password", "weno_secondary_encryption_key", "weno_secondary_admin_username", "weno_secondary_admin_password") + ); + if (empty($gl)) { + $this->saveVendorGlobals($vendors); + return $vendors; + } + while ($row = sqlFetchArray($gl)) { + $vendors[$row['gl_name']] = $row['gl_value']; + } + if ($decrypt) { + $crypt = new CryptoGen(); + $vendors['weno_encryption_key'] = $crypt->decryptStandard($vendors['weno_encryption_key']); + $vendors['weno_admin_password'] = $crypt->decryptStandard($vendors['weno_admin_password']); + $vendors['weno_secondary_encryption_key'] = $crypt->decryptStandard($vendors['weno_secondary_encryption_key']); + $vendors['weno_secondary_admin_password'] = $crypt->decryptStandard($vendors['weno_secondary_admin_password']); + } + + return $vendors; + } + + /** + * @param $vendors + * @return void + */ + public function saveVendorGlobals($items): void + { + $crypt = new CryptoGen(); + $items['weno_encryption_key'] = $crypt->encryptStandard($items['weno_encryption_key']); + $items['weno_admin_password'] = $crypt->encryptStandard($items['weno_admin_password']); + $items['weno_secondary_encryption_key'] = $crypt->encryptStandard($items['weno_secondary_encryption_key']); + $items['weno_secondary_admin_password'] = $crypt->encryptStandard($items['weno_secondary_admin_password']); + $vendors['weno_rx_enable'] = $items['weno_rx_enable'] ?? '0'; + $vendors['weno_rx_enable_test'] = $items['weno_rx_enable_test'] ?? '0'; + $vendors['weno_encryption_key'] = $items['weno_encryption_key']; + $vendors['weno_admin_username'] = $items['weno_admin_username']; + $vendors['weno_admin_password'] = $items['weno_admin_password']; + $vendors['weno_secondary_encryption_key'] = $items['weno_secondary_encryption_key']; + $vendors['weno_secondary_admin_username'] = $items['weno_secondary_admin_username']; + $vendors['weno_secondary_admin_password'] = $items['weno_secondary_admin_password']; + + foreach ($vendors as $key => $vendor) { + sqlQuery( + "INSERT INTO `globals` (`gl_name`,`gl_value`) VALUES (?, ?) ON DUPLICATE KEY UPDATE `gl_name` = ?, `gl_value` = ?", + array($key, $vendor, $key, $vendor) + ); + } + } + + /** + * Grab all Laminas Module setup or columns values. + * + * @param $modId + * @param string $col + * @return array + */ + function getModuleRegistry($modId, string $col = '*'): array + { + $registry = []; + $sql = "SELECT $col FROM modules WHERE mod_id = ? OR `mod_directory` = ?"; + $results = sqlQuery($sql, array($modId, $modId)); + foreach ($results as $k => $v) { + $registry[$k] = trim((preg_replace('/\R/', '', $v))); + } + + return $registry; + } + + /** + * @return bool true if all the Admin weno settings have been configured. Otherwise, false. + */ + public function isWenoConfigured(): bool + { + self::statusPharmacyDownloadReset(); // if last failed, reset to active + $config = $this->getVendorGlobals(); + $keys = array_keys($config); + foreach ($keys as $key) { + if ($key === 'weno_rx_enable_test') { + continue; + } + $value = $GLOBALS[$key] ?? null; + + if (empty($value)) { + self::setTaskState('0', false); + return false; + } + } + self::setTaskState('1', false); + return true; + } + + public static function statusPharmacyDownloadReset(): bool + { + $logService = new WenoLogService(); + $log = $logService->getLastPharmacyDownloadStatus(); + if ($log['status'] ?? '' != 'Success') { + if (($log['count'] ?? 0) > 0) { + return true; + } + $sql = "UPDATE `background_services` SET `next_run` = current_timestamp(), `active` = '1' WHERE `name` = ? && `next_run` > current_timestamp()"; + sqlQuery($sql, array('WenoExchangePharmacies')); + return true; + } + return false; + } + + /** + * @param $modId string|int module id or directory name + * @param $flag string|int 1 or 0 to activate or deactivate module. + * @param $flag_ui string|int custom module ui flag to activate or deactivate Manager UI states. + * @return array|bool|null + */ + public static function setModuleState($modId, $flag, $flag_ui): array|bool|null + { + if (($flag_ui == '1') || ($flag == '0')) { + self::setTaskState('0', false); + } else { + // set BG tasks to active if module is active. + self::setTaskState('1', false); + } + // set module state. + $sql = "UPDATE `modules` SET `mod_active` = ?, `mod_ui_active` = ? WHERE `mod_id` = ? OR `mod_directory` = ?"; + return sqlQuery($sql, array($flag, $flag_ui, $modId, $modId)); + } +} diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php b/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php index 320ba60befb..03f39a8b166 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/TransmitProperties.php @@ -7,18 +7,22 @@ * @link http://www.open-emr.org * @author Sherwin Gaddis * @author Kofi Appiah + * @author Jerry Padgett * @copyright Copyright (c) 2016-2017 Sherwin Gaddis * @copyright Copyright (c) 2023 omega systems group international + * @copyright Copyright (c) 2024 Jerry Padgett * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 */ namespace OpenEMR\Modules\WenoModule\Services; use OpenEMR\Common\Crypto\CryptoGen; +use OpenEMR\Common\Csrf\CsrfUtils; use OpenEMR\Services\FacilityService; class TransmitProperties { + public $errors; private $payload; private $patient; private $provider_email; @@ -30,13 +34,19 @@ class TransmitProperties private $cryptoGen; private $pharmacy; private $encounter; + private mixed $wenoProviderID; + private string|false $csrf; + private mixed $responsibleParty; /** * AdminProperties constructor. */ - public function __construct() + public function __construct($returnFlag = false) { + $this->errors = ['errors' => '', 'warnings' => '', 'info' => '', 'string' => '']; + $this->csrf = js_escape(CsrfUtils::collectCsrfToken()); $this->cryptoGen = new CryptoGen(); + $this->wenoProviderID = $this->getWenoProviderID(); $this->ncpdp = $this->getPharmacy(); $this->vitals = $this->getVitals(); $this->patient = $this->getPatientInfo(); @@ -44,15 +54,115 @@ public function __construct() $this->provider_pass = $this->getProviderPassword(); $this->locid = $this->getFacilityInfo(); $this->pharmacy = $this->getPharmacy(); - $this->payload = $this->createJsonObject(); $this->subscriber = $this->getSubscriber(); + // check if patient is under 19 years old + $this->responsibleParty = ''; + if (self::getAge($this->patient['dob']) < 19) { + $this->responsibleParty = $this->getResponsibleParty(); + } $this->encounter = $this->getEncounter(); + // check for errors + $this->errors = $this->checkErrors($this); + if (!empty($this->errors['errors'])) { + // let's not create payload if there are errors. + // nip it here so to speak. + if ($returnFlag) { + return; + } + self::echoError($this->errors); + } elseif ($returnFlag) { + return; + } + // validated so create json object + $this->payload = $this->createJsonObject(); + } + + /** + * @param $value + * @return bool + */ + public function isJson($value): bool + { + return is_string($value) && json_decode($value) !== null && (json_last_error() == JSON_ERROR_NONE); } /** * @return false|string */ - public function createJsonObject() + public function getPayload(): false|string + { + return $this->payload; + } + + /** + * Generate a list of errors, warnings and info messages. + * All messages should be escaped and translated. + * + * @param $obj + * @return string[] + */ + public function checkErrors($obj): array + { + // Initialize the error array + $error = ['errors' => '', 'warnings' => '', 'info' => '', 'string' => '']; + // Check if the input is a valid object + if (!is_object($obj)) { + return $error; // Return empty error array if the input is not an object + } + // Iterate through the object properties + foreach ($obj as $property => $value) { + // Skip 'errors' property and empty values + if ($property === 'errors' || empty($value)) { + continue; + } + // Extract error type and value + $type = ''; + $v = ''; + if (is_string($value)) { + $values = [$value]; + } elseif ($this->isJson($value)) { + $values = json_decode($value, true); + } elseif (is_array($value)) { + $values = $value; + } else { + continue; // Skip non-array and non-string properties + } + // Iterate through the values + foreach ($values as $v) { + if (str_contains($v, "REQED")) { + $type = 'errors'; + } elseif (str_contains($v, "WARNS")) { + $type = 'warnings'; + } elseif (str_contains($v, "INFO")) { + $type = 'info'; + } else { + continue; // Skip if no error type detected + } + // Extract action from value + $action = ''; + if (preg_match('/{([^}]*)}/', $v, $matches)) { + $action = $matches[1]; + $v = str_replace('{' . $matches[1] . '}', '', $v); + } + // Add error to the respective error type if not already present + if (!str_contains($error[$type], $v)) { + // Append error with icon and onclick event + $uid = attr_js($_SESSION['authUserID'] ?? 0); + $action = attr_js($action); + $error[$type] .= "$v
"; + } + } + } + // Combine error messages into a single string + $error['string'] = $error['errors'] . $error['warnings'] . $error['info']; + + return $error; + } + + /** + * @return false|string + */ + public function createJsonObject(): false|string { //default is testing mode $testing = isset($GLOBALS['weno_rx_enable_test']); @@ -62,8 +172,9 @@ public function createJsonObject() $mode = 'N'; } $gender = $this->patient['sex']; - $heighDate = explode(" ", $this->vitals['date']); - $phoneprimary = preg_replace('/\D+/', '', $this->patient['phone_cell']); + $heightDate = explode(" ", $this->vitals['date'] ?? ''); + $phonePrimary = $this->formatPhoneNumber($this->patient['phone_cell']); + $age = self::getAge($this->patient['dob']); //create json array $wenObj = []; $wenObj['UserEmail'] = $this->provider_email['email']; @@ -71,7 +182,7 @@ public function createJsonObject() $wenObj['LocationID'] = $this->locid['weno_id']; $wenObj['TestPatient'] = $mode; $wenObj['PatientType'] = 'Human'; - $wenObj['OrgPatientID'] = $this->patient['pid'] . ":" . $this->getEncounter(); + $wenObj['OrgPatientID'] = $this->patient['pid'] . ":" . $_SESSION['authUserID'] ?? 0; $wenObj['LastName'] = $this->patient['lname']; $wenObj['FirstName'] = $this->patient['fname']; @@ -82,31 +193,102 @@ public function createJsonObject() $wenObj['State'] = $this->patient['state']; $wenObj['PostalCode'] = $this->patient['postal_code']; $wenObj['CountryCode'] = "US"; - $wenObj['PrimaryPhone'] = $phoneprimary; + $wenObj['PrimaryPhone'] = $phonePrimary; $wenObj['SupportsSMS'] = 'Y'; - $wenObj['PatientHeight'] = substr($this->vitals['height'], 0, -3); - $wenObj['PatientWeight'] = substr($this->vitals['weight'], 0, -3); - $wenObj['HeightWeightObservationDate'] = $heighDate[0]; - $wenObj["ResponsiblePartySameAsPatient"] = 'Y'; + $wenObj['PatientHeight'] = substr($this->vitals['height'] ?? '', 0, -3); + $wenObj['PatientWeight'] = substr($this->vitals['weight'] ?? '', 0, -3); + $wenObj['HeightWeightObservationDate'] = $heightDate[0]; + $wenObj["ResponsiblePartySameAsPatient"] = $age < 19 ? 'N' : 'Y'; + if ($age < 19 && !empty($this->responsibleParty)) { + $wenObj['ResponsiblePartyLastName'] = $this->responsibleParty['ResponsiblePartyLastName']; + $wenObj['ResponsiblePartyFirstName'] = $this->responsibleParty['ResponsiblePartyFirstName']; + $wenObj['ResponsiblePartyAddressLine1'] = $this->responsibleParty['ResponsiblePartyAddressLine1']; + if (!empty(($this->responsibleParty['ResponsiblePartyAddressLine2'] ?? ''))) { + $wenObj['ResponsiblePartyAddressLine2'] = $this->responsibleParty['ResponsiblePartyAddressLine2']; + } + $wenObj['ResponsiblePartyCity'] = $this->responsibleParty['ResponsiblePartyCity']; + $wenObj['ResponsiblePartyState'] = $this->responsibleParty['ResponsiblePartyState']; + $wenObj['ResponsiblePartyPostalCode'] = $this->responsibleParty['ResponsiblePartyPostalCode']; + $wenObj['ResponsiblePartyCountryCode'] = 'US'; + $wenObj['ResponsiblePartyPrimaryPhone'] = self::formatPhoneNumber($this->responsibleParty['ResponsiblePartyPrimaryPhone']); + } $wenObj['PatientLocation'] = "Home"; $wenObj['PrimaryPharmacyNCPCP'] = $this->pharmacy['primary']; - $wenObj['AlternativePharmacyNCPCP'] = $this->pharmacy['alternate']; + if (!empty($this->pharmacy['alternate'])) { + $wenObj['AlternativePharmacyNCPCP'] = $this->pharmacy['alternate']; + } - //add insurance return json_encode($wenObj); } /** - * @return array|void + * @return mixed + */ + private function getResponsibleParty(): mixed + { + $guardian = << '' and subscriber_relationship != 'self' and type = 'primary' +insurance; + + $relation = sqlQuery($guardian, [$_SESSION['pid']]); + // if no guardian then check for primary insurance subscriber + if (empty($relation['ResponsiblePartyLastName'])) { + $relation = sqlQuery($insurance, [$_SESSION['pid']]); + } + if (empty($relation)) { + return 'REQED:{demographics}' . xlt("Patient is under 19 years old. A Responsible Party is required. From the Patient Chart select Demographics Primary Insurance or Guardian to add a person."); + } + + return $relation; + } + + /** + * @param $dob + * @param $as_of + * @return string + */ + public static function getAge($dob, $as_of = ''): string + { + if (empty($as_of)) { + $as_of = date('Y-m-d'); + } + $a1 = explode('-', substr($dob, 0, 10)); + $a2 = explode('-', substr($as_of, 0, 10)); + $age = $a2[0] - $a1[0]; + if ($a2[1] < $a1[1] || ($a2[1] == $a1[1] && $a2[2] < $a1[2])) { + --$age; + } + + return (int)$age; + } + + /** + * @param $phone + * @return string + */ + public function formatPhoneNumber($phone): string + { + $phone = preg_replace('/\D+/', '', $phone); + if (strlen($phone) == 11) { + $phone = substr($phone, 1, 10); + } + return $phone; + } + + /** + * @return array|string */ - public function getProviderEmail() + public function getProviderEmail(): array|string { - $provider_info = ['email' => $GLOBALS['weno_provider_email']]; + $provider_info = ['email' => ($GLOBALS['weno_provider_email'] ?? '')]; if (empty($provider_info['email'])) { - echo self::styleErrors(xlt('Provider email address is missing and required. Go to User Settings select Weno Tab and enter your Weno Provider Password')); - exit; + return "REQED:{user_settings}" . (xlt('Provider Email is missing. Go to User Settings Weno Tab and enter your Weno Provider Email')); } else { return $provider_info; } @@ -131,11 +313,9 @@ public function getFacilityInfo(): array|null|false $default_facility = sqlQuery("SELECT name, street, city, state, postal_code, phone, fax, weno_id from facility order by id limit 1"); if (empty($default_facility['weno_id'])) { - echo self::styleErrors(xlt('Facility ID is missing. From Admin select Other then Weno Management. Enter the Weno ID of your facility')); - exit; - } else { - return $default_facility; + $default_facility['error'] = "REQED:{weno_manage}" . xlt('Facility ID is missing. From Admin select Other then Weno Management. Enter the Weno ID of your facility'); } + return $default_facility; } return $locId; } @@ -143,196 +323,208 @@ public function getFacilityInfo(): array|null|false /** * @return mixed */ - private function getPatientInfo() + private function getPatientInfo(): mixed { - //get patient data if in an encounter - //Since the transmitproperties is called in the logproperties - //need to check to see if in an encounter or not. Patient data is not required to view the Weno log - $log = ''; - $missing = 0; - if (empty($_SESSION['encounter'])) { - // removed requirement sjp - } - $patient = sqlQuery("select title, fname, lname, mname, street, state, city, email, phone_cell, postal_code, dob, sex, pid from patient_data where pid=?", [$_SESSION['pid']]); + // Get patient data if in an encounter + // Since the transmitProperties is called in the logproperties + // need to check to see if in an encounter or not. Patient data is not required to view the Weno log + + $patient = sqlQuery("select title, fname, lname, mname, street, state, city, email, phone_cell, phone_home, postal_code, dob, sex, pid from patient_data where pid=?", [$_SESSION['pid']]); if (empty($patient['fname'])) { - $log .= xlt("First Name Missing, From the Patient Chart select Demographics select Who. Save and retry") . ""; - ++$missing; + $patient['lname'] = "REQED:{demographics}" . xlt("Last Name Missing, From the Patient Chart select Demographics select Who."); } if (empty($patient['dob'])) { - $log .= xlt("Date of Birth Missing, From the Patient Chart select Demographics select Who. Save and retry") . "
"; - ++$missing; + $patient['dob'] = "REQED:{demographics}" . xlt("Date of Birth Missing, From the Patient Chart select Demographics select Who."); } if (empty($patient['sex'])) { - $log .= xlt("Gender Missing, From the Patient Chart select Demographics select Who. Save and retry") . "
"; - ++$missing; + $patient['sex'] = "REQED:{demographics}" . xlt("Gender Missing, From the Patient Chart select Demographics select Who."); } if (empty($patient['postal_code'])) { - $log .= xlt("Zip Code Missing, From the Patient Chart select Demographics select Contact select Postal Code. Save and retry") . "
"; - ++$missing; + $patient['postal_code'] = "REQED:{demographics}" . xlt("Zip Code Missing, From the Patient Chart select Demographics select Contact select Postal Code."); } if (empty($patient['street'])) { - $log .= xlt("Street Address incomplete Missing, From the Patient Chart select Demographics select Contact. Save and retry") . "
"; - ++$missing; + $patient['street'] = "REQED:{demographics}" . xlt("Street Address Missing, From the Patient Chart select Demographics select Contact."); + } + if (empty($patient['city'])) { + $patient['city'] = "REQED:{demographics}" . xlt("City Missing, From the Patient Chart select Demographics select Contact."); + } + if (empty($patient['state'])) { + $patient['state'] = "REQED:{demographics}" . xlt("State Missing, From the Patient Chart select Demographics select Contact."); } - if ($missing > 0) { - self::errorWithDie($log); + if (empty($patient['phone_cell'])) { + $patient['phone_cell'] = "REQED:{demographics}" . xlt("Cell or Home Phone Missing, From the Patient Chart select Demographics select Contact."); + if (!empty($patient['phone_home'])) { + $patient['phone_cell'] = $patient['phone_home']; + } } return $patient; } + /** + * @param $error + * @return string + */ public static function styleErrors($error): string { - $log = "

" . + $log = "

" . $error . "
" . xlt('Please address errors and try again!') . - "
" . xlt("Use browser Back button or Click Patient Name from top Patient bar.") . "

"; return $log; } - public static function errorWithDie($error): void + /** + * @param $errors + * @return void + */ + public static function echoError($errors): void { + if (is_array($errors)) { + $error = $errors['errors'] . $errors['warnings'] . $errors['info']; + } else { + $error = $errors; + } $log = self::styleErrors($error); - die($log); + echo($log); } /** * @return string * New Rx */ - public function cipherpayload() + public function cipherPayload(): string { $cipher = "aes-256-cbc"; // AES 256 CBC cipher $enc_key = $this->cryptoGen->decryptStandard($GLOBALS['weno_encryption_key']); - if ($enc_key) { - $key = substr(hash('sha256', $enc_key, true), 0, 32); - $iv = chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0) . chr(0x0); - $ciphertext = base64_encode(openssl_encrypt($this->payload, $cipher, $key, OPENSSL_RAW_DATA, $iv)); - return $ciphertext; - } else { + + if (!$enc_key) { return "error"; } + + $key = substr(hash('sha256', $enc_key, true), 0, 32); + $iv = str_repeat("\0", 16); // Generate an initialization vector + + return base64_encode(openssl_encrypt($this->payload, $cipher, $key, OPENSSL_RAW_DATA, $iv)); } + /** * @return mixed */ - public function getProviderPassword() + public function getProviderPassword(): mixed { if (!empty($GLOBALS['weno_provider_password'])) { return $this->cryptoGen->decryptStandard($GLOBALS['weno_provider_password']); } else { - echo xlt('Provider Password is missing'); - die; + return "REQED:{user_settings}" . xlt('Provider Password is missing. Go to User Settings Weno Tab and enter your Weno Provider Password'); } } /** - * @return mixed + * @return array|false|null */ - public function getVitals() + public function getVitals(): ?array { - $vitals = sqlQuery("select date, height, weight from form_vitals where pid = ? ORDER BY id DESC", [$_SESSION["pid"] ?? null]); + $vitals = sqlQuery("SELECT date, height, weight FROM form_vitals WHERE pid = ? ORDER BY id DESC", [$_SESSION["pid"] ?? null]); + // Check if vitals are empty or missing height and weight + if (empty($vitals) || ($vitals['height'] <= 0) || ($vitals['weight'] <= 0)) { + return [ + "REQED:{vitals}" . xlt("A Vitals Height and Weight are required to transmit a prescription. Create or add Vitals in an encounter.") + ]; + } return $vitals; } - private function getSubscriber() + /** + * @return mixed + */ + private function getSubscriber(): mixed { - $sql = sqlQuery("select subscriber_relationship from insurance_data where pid = ? and type = 'primary'", [$_SESSION['pid']]); - return $sql['subscriber_relationship']; + $relation = sqlQuery("select subscriber_relationship from insurance_data where pid = ? and type = 'primary'", [$_SESSION['pid']]); + $relation = $relation ?? ['subscriber_relationship' => '']; + + return $relation['subscriber_relationship']; } /** - * @return mixed + * @return string|array */ - public function getPharmacy() + public function getPharmacy(): string|array { $data = sqlQuery("SELECT * FROM `weno_assigned_pharmacy` WHERE `pid` = ? ", [$_SESSION["pid"]]); - if (empty($data)) { - $log = xlt("Weno Pharmacies not set. From Patient's Demographics select Choices then select Weno Pharmacy Selector to Assign Pharmacies"); - self::errorWithDie($log); - } $response = array( - "primary" => $data['primary_ncpdp'], - "alternate" => $data['alternate_ncpdp'] + "primary" => $data['primary_ncpdp'] ?? '', + "alternate" => $data['alternate_ncpdp'] ?? '' ); + if (empty($data)) { + $response['errors'] = true; + // both primary and alternate are empty + } if (empty($response['primary'])) { - $log = xlt("Weno Primary Pharmacy not set. From Patient's Demographics select Choices then select Weno Pharmacy Selector to Assign Primary Pharmacy"); - self::errorWithDie($log); + $response['errors'] = true; + $e = 'REQED:{demographics}' . xlt("Weno Primary Pharmacy not set. From Patient's Demographics Choices assign Primary Pharmacy"); + $response['primary'] = $e; } - if (empty($response['alternate'])) { - $log = xlt("Weno Alternate Pharmacy not set. From Patient's Demographics select Choices then select Weno Pharmacy Selector to Assign Primary Pharmacy"); - self::errorWithDie($log); - } return $response; } - public function wenoChr() - { - return - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0) . - chr(0x0); - } - - public function wenoMethod(): string - { - return "aes-256-cbc"; - } - /** * @return mixed */ - public function getProviderName() + public function getProviderName(): mixed { $provider_info = sqlQuery("select fname, mname, lname from users where username=? ", [$_SESSION["authUser"]]); - + $provider_info = $provider_info ?? ['fname' => '', 'mname' => '', 'lname' => '']; return $provider_info['fname'] . " " . $provider_info['mname'] . " " . $provider_info['lname']; } /** * @return mixed */ - public function getPatientName() + public function getPatientName(): mixed { $patient_info = sqlQuery("select fname, mname, lname from patient_data where pid=? ", [$_SESSION["pid"]]); - + $patient_info = $patient_info ?? ['fname' => '', 'mname' => '', 'lname' => '']; return $patient_info['fname'] . " " . $patient_info['mname'] . " " . $patient_info['lname']; } /** - * @return mixed + * @return int|mixed */ - public function getWenoAltPharmacies() + private function getEncounter(): mixed { - $data = sqlQuery("SELECT * FROM weno_assigned_pharmacy WHERE pid = ? ", [$_SESSION["pid"]]); - $response = array( - "primary" => $data['primary_ncpdp'], - "alternate" => $data['alternate_ncpdp'] - ); - return $response; + return $_SESSION['encounter'] ?? 0; } - private function getEncounter() + /** + * @param $id + * @return mixed|string + */ + public function getWenoProviderId($id = null): mixed { - return $_SESSION['encounter'] ?? 0; + if (empty($id)) { + $id = $_SESSION['authUserID'] ?? ''; + } + // get the weno provider id from the user table (weno_prov_id + $provider = sqlQuery("SELECT weno_prov_id FROM users WHERE id = ?", [$id]); + if (!empty(trim($provider['weno_prov_id'] ?? ''))) { + $doIt = $GLOBALS['weno_provider_uid'] != trim($provider['weno_prov_id']); + if ($doIt) { + $GLOBALS['weno_provider_uid'] = trim($provider['weno_prov_id']); + $sql = "UPDATE `user_settings` SET `setting_value` = ? WHERE `setting_user` = ? AND `setting_label` = 'global:weno_provider_uid'"; + //sqlQuery($sql, [$GLOBALS['weno_provider_uid'], $_SESSION['authUserID']]); + } + return $provider['weno_prov_id']; + } elseif (!empty($GLOBALS['weno_provider_uid'])) { // if not in user table then check globals + // update user table with weno provider id + //sqlQuery("UPDATE `users` SET `weno_prov_id` = ? WHERE `id` = ? OR `weno_prov_id` = ?", [$GLOBALS['weno_provider_uid'], $id, $id]); + return $GLOBALS['weno_provider_uid']; + } else { + return "REQED:{users}" . xlt("Weno Provider Id missing. Select Admin then Users and edit the user to add Weno Provider Id"); + } } } diff --git a/interface/modules/custom_modules/oe-module-weno/src/Services/WenoLogService.php b/interface/modules/custom_modules/oe-module-weno/src/Services/WenoLogService.php index 25a557b72ac..7219807c041 100644 --- a/interface/modules/custom_modules/oe-module-weno/src/Services/WenoLogService.php +++ b/interface/modules/custom_modules/oe-module-weno/src/Services/WenoLogService.php @@ -20,38 +20,70 @@ public function __construct() { } - public function getLastPrescriptionLogStatus() + public function getLastPrescriptionLogStatus(): bool|array|null { $params = "prescription"; $sql = "SELECT * FROM weno_download_log WHERE "; $sql .= "VALUE = ? ORDER BY created_at DESC LIMIT 1"; - $result = sqlQuery($sql, [$params]); - - return $result; + return sqlQuery($sql, [$params]); } - public function getLastPharmacyDownloadStatus() + public function getLastPharmacyDownloadStatus(): bool|array|null { $params = "pharmacy"; - $sql = "SELECT * FROM weno_download_log WHERE "; - $sql .= "VALUE = ? ORDER BY created_at DESC LIMIT 1"; - - $result = sqlQuery($sql, [$params]); + $v = ['count' => 0, 'created_at' => '', 'status' => '']; + $vsql = sqlQuery("SELECT * FROM `weno_download_log` WHERE `value` = ? ORDER BY `created_at` DESC LIMIT 1", [$params]); + if (!$vsql) { + return $v; + } + $v = $vsql; + $count = sqlQuery("SELECT COUNT(`id`) as count FROM `weno_pharmacy`"); + $v['count'] = $count['count'] ?? 0; - return $result; + return $v; } - public function insertWenoLog($value, $status) + public function insertWenoLog($value, $status): bool|string { - $sql = "INSERT INTO weno_download_log SET "; - $sql .= "value = ?, "; - $sql .= "status = ? "; - + $sql = "INSERT INTO weno_download_log SET value = ?, status = ?"; try { sqlInsert($sql, [$value, $status]); } catch (Exception $e) { return $e->getMessage(); } + return true; + } + + public function scrapeWenoErrorHtml($content) + { + $error = ['is_error' => false, 'type' => 'other', 'messageText' => '', 'messageHtml' => '']; + if (empty($content)) { + return $error; + } + $content = trim(preg_replace("/\r?\n|\r/", '

', $content)); + $content_html = strip_tags($content, '