diff --git a/README.md b/README.md index 23911e6..26bb5bb 100644 --- a/README.md +++ b/README.md @@ -1,41 +1,34 @@ -# Description -This repository contains modules that add functionality to Openemr. - -# Installation -These instructions assume you have a working installation of Openemr. - -Add the following to the openemr/composer.json: -``` json -"repositories": [ - { - "type": "vcs", - "url": "git@github.com:israeli-moh/clinikal-backend.git" - }, - { - "type": "vcs", - "url": "git@github.com:israeli-moh/composer-installers-clinikal-extender.git" - } -], -"require": { - "clinikal/clinikal-backend": "dev-master", - "clinikal/composer-installers-clinikal-extender": "dev-master" -}, -"extra": { - "installer-types": [ - "clinikal-vertical" - ], -} -``` - -In a terminal, `cd` into the openemr root directory (where the composer.json is), and run: -``` -composer update clinikal -``` - -This downloads the modules code into the openemr/vendor/clinikal and triggers the composer installer extension in the composer-installers-clinikal-extender repository. -The extension creates links from the files in vendor/clinikal to there appropriate places in the openemr codebase. -This enables us to use any modules, styles, and menus downloaded by composer into the vendor/clinikal directory. - -All modules can now be registered and enabled in the Manage Modules screen. - -This project is sponsored by the Israeli Ministry Of Health. +## What is clinikal? + +Clinikal is a Electric Medical Records application. +Clinical offers a new experience of administrative and medical management for variety of clinics. +The foundation of the application is the popular open source [OpenEMR](https://github.com/openemr/openemr), we developed new layer of Fhir API base on ZF2 modules and new and modern React.js application to enjoy from wonderful user experience. +Clinikal continues to use OpenEMR interfaces as Content Management System for manage users, permissions, lists etc. (we doesn't supply compatibility with all Openemr screens) + +The principle that guides us is **clean and clear**. +Each user sees only the screens and forms relevant to his role in the clinic. +This ability is caused by using a system of roles and privileges for each profession in the clinic and a different installation process for each medical field which creates an innovative and convenient user experience! + +### Get started and documentation +To get started and documentation at https://clinikal-documentation.readthedocs.io/ + +### Clinikal-beckend +The product composed of a few component, Server-side modules and client-side application. +This repository is part of server-side components and contains several modules and configurations that common for all the medical fields in the system. +Main contents: +* API layer +* General functionality +* Custom menus +* Import data module +* More helpers + +### Resources +* [Docker installation](https://clinikal-documentation.readthedocs.io/en/latest/get_started/docker_installation/) +* [Manual installation](https://clinikal-documentation.readthedocs.io/en/latest/get_started/openemr_modules/) +* [Internationalization](https://clinikal-documentation.readthedocs.io/en/latest/get_started/internationalization/) +* [FHIR API](https://clinikal-documentation.readthedocs.io/en/latest/api/fhir/) + +### License +Please see the [license agreement](https://github.com/israeli-moh/clinikal-react/blob/develop/LICENSE). + + diff --git a/composer.json b/composer.json index d9b4f87..2ec3398 100644 --- a/composer.json +++ b/composer.json @@ -10,7 +10,7 @@ "repositories": [ { "type": "vcs", - "url": "git@github.com:israeli-moh/composer-installers-clinikal-extender.git" + "url": "git@github.com:MohGovIL/Clinical-composer-installers.git" } ], "require": { diff --git a/modules/ClinikalAPI/Module.php b/modules/ClinikalAPI/Module.php index ca7239f..80f7dd6 100644 --- a/modules/ClinikalAPI/Module.php +++ b/modules/ClinikalAPI/Module.php @@ -157,6 +157,24 @@ public function initApi($m) } + /** + * load global variables foe every controllers + * @param ModuleManager $manager + */ + public function init(ModuleManager $manager) + { + $events = $manager->getEventManager(); + $sharedEvents = $events->getSharedManager(); + $sharedEvents->attach(__NAMESPACE__, 'dispatch', function ($e) { + $controller = $e->getTarget(); + //$controller->layout()->setVariable('status', null); + $controller->layout('clinikalApi/layout/layout'); + + //global variable of language direction + $controller->layout()->setVariable('language_direction', $_SESSION['language_direction']); + + }, 100); + } } diff --git a/modules/ClinikalAPI/acl/acl_setup.php b/modules/ClinikalAPI/acl/acl_setup.php index c6978c8..4e44c93 100644 --- a/modules/ClinikalAPI/acl/acl_setup.php +++ b/modules/ClinikalAPI/acl/acl_setup.php @@ -15,6 +15,7 @@ AclExtended::addObjectAcl('client_app', 'Client Application', 'SearchPatient','Search Patient'); AclExtended::addObjectAcl('client_app', 'Client Application', 'Calendar','Calendar'); AclExtended::addObjectAcl('client_app', 'Client Application', 'AppointmentDetails','Appointment Details'); +AclExtended::addObjectAcl('client_app', 'Client Application', 'ManageTemplates','Manage Templates'); AclExtended::addObjectSectionAcl('clinikal_api', 'Clinikal API'); AclExtended::addObjectAcl('clinikal_api', 'Clinikal API', 'general_settings','General settings'); diff --git a/modules/ClinikalAPI/acl/acl_upgrade.php b/modules/ClinikalAPI/acl/acl_upgrade.php index b8e0ae5..0bbe648 100644 --- a/modules/ClinikalAPI/acl/acl_upgrade.php +++ b/modules/ClinikalAPI/acl/acl_upgrade.php @@ -18,5 +18,8 @@ AclExtended::addObjectSectionAcl('clinikal_api', 'Clinikal API'); AclExtended::addObjectAcl('clinikal_api', 'Clinikal API', 'general_settings','General settings'); AclExtended::addObjectAcl('clinikal_api', 'Clinikal API', 'lists','Lists'); + }, + '2.0.0' => function() { + AclExtended::addObjectAcl('client_app', 'Client Application', 'ManageTemplates','Manage Templates'); } ); diff --git a/modules/ClinikalAPI/config/module.config.php b/modules/ClinikalAPI/config/module.config.php index e10050f..e3b7861 100644 --- a/modules/ClinikalAPI/config/module.config.php +++ b/modules/ClinikalAPI/config/module.config.php @@ -18,6 +18,7 @@ */ use ClinikalAPI\Controller\ManagerApi; +use ClinikalAPI\Controller\FormTemplatesManagementController; use Interop\Container\ContainerInterface; return array( @@ -29,6 +30,9 @@ ManagerApi::class => function (ContainerInterface $container) { return new ManagerApi($container); }, + FormTemplatesManagementController::class => function (ContainerInterface $container) { + return new FormTemplatesManagementController($container); + }, ], ), @@ -53,6 +57,19 @@ ), ), ), + 'templetes_management' => array( + 'type' => 'segment', + 'options' => array( + 'route' => '/templates-management[/:action]', + 'constraints' => array( + 'action' => '[a-zA-Z][a-zA-Z0-9_-]*', + ), + 'defaults' => array( + 'controller' => FormTemplatesManagementController::class, + 'action' => 'templatesManagementIndex', + ), + ), + ), ), ), @@ -61,5 +78,9 @@ 'template_path_stack' => array( 'ClinikalAPI' => __DIR__ . '/../view', ), + 'template_map' => array( + 'clinikalApi/layout/layout' => __DIR__ . '/../view/layout/layout.phtml', + ) ), + ); diff --git a/modules/ClinikalAPI/sql/1_0_0-to-2_0_0_upgrade.sql b/modules/ClinikalAPI/sql/1_0_0-to-2_0_0_upgrade.sql new file mode 100644 index 0000000..56eb7f9 --- /dev/null +++ b/modules/ClinikalAPI/sql/1_0_0-to-2_0_0_upgrade.sql @@ -0,0 +1,128 @@ +-- +-- Comment Meta Language Constructs: +-- +-- #IfNotTable +-- argument: table_name +-- behavior: if the table_name does not exist, the block will be executed + +-- #IfTable +-- argument: table_name +-- behavior: if the table_name does exist, the block will be executed + +-- #IfColumn +-- arguments: table_name colname +-- behavior: if the table and column exist, the block will be executed + +-- #IfMissingColumn +-- arguments: table_name colname +-- behavior: if the table exists but the column does not, the block will be executed + +-- #IfNotColumnType +-- arguments: table_name colname value +-- behavior: If the table table_name does not have a column colname with a data type equal to value, then the block will be executed + +-- #IfNotRow +-- arguments: table_name colname value +-- behavior: If the table table_name does not have a row where colname = value, the block will be executed. + +-- #IfNotRow2D +-- arguments: table_name colname value colname2 value2 +-- behavior: If the table table_name does not have a row where colname = value AND colname2 = value2, the block will be executed. + +-- #IfNotRow3D +-- arguments: table_name colname value colname2 value2 colname3 value3 +-- behavior: If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed. + +-- #IfNotRow4D +-- arguments: table_name colname value colname2 value2 colname3 value3 colname4 value4 +-- behavior: If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3 AND colname4 = value4, the block will be executed. + +-- #IfNotRow2Dx2 +-- desc: This is a very specialized function to allow adding items to the list_options table to avoid both redundant option_id and title in each element. +-- arguments: table_name colname value colname2 value2 colname3 value3 +-- behavior: The block will be executed if both statements below are true: +-- 1) The table table_name does not have a row where colname = value AND colname2 = value2. +-- 2) The table table_name does not have a row where colname = value AND colname3 = value3. + +-- #IfRow2D +-- arguments: table_name colname value colname2 value2 +-- behavior: If the table table_name does have a row where colname = value AND colname2 = value2, the block will be executed. + +-- #IfRow3D +-- arguments: table_name colname value colname2 value2 colname3 value3 +-- behavior: If the table table_name does have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed. + +-- #IfIndex +-- desc: This function is most often used for dropping of indexes/keys. +-- arguments: table_name colname +-- behavior: If the table and index exist the relevant statements are executed, otherwise not. + +-- #IfNotIndex +-- desc: This function will allow adding of indexes/keys. +-- arguments: table_name colname +-- behavior: If the index does not exist, it will be created + +-- #EndIf +-- all blocks are terminated with a #EndIf statement. + +-- #IfNotListReaction +-- Custom function for creating Reaction List + +-- #IfNotListOccupation +-- Custom function for creating Occupation List + +-- #IfTextNullFixNeeded +-- desc: convert all text fields without default null to have default null. +-- arguments: none + +-- #IfTableEngine +-- desc: Execute SQL if the table has been created with given engine specified. +-- arguments: table_name engine +-- behavior: Use when engine conversion requires more than one ALTER TABLE + +-- #IfInnoDBMigrationNeeded +-- desc: find all MyISAM tables and convert them to InnoDB. +-- arguments: none +-- behavior: can take a long time. + +-- #IfTranslationNeeded +-- desc: find all MyISAM tables and convert them to InnoDB. +-- arguments: constant_name english hebrew +-- behavior: can take a long time. + +#IfMissingColumn clinikal_templates_map active +ALTER TABLE `clinikal_templates_map` ADD `active` tinyint(1) NOT NULL DEFAULT 1; +#EndIf + +#IfMissingColumn clinikal_templates_map update_by +ALTER TABLE `clinikal_templates_map` ADD `update_by` int(11) NOT NULL; +#EndIf + +#IfMissingColumn clinikal_templates_map update_date +ALTER TABLE `clinikal_templates_map` ADD `update_date` datetime NOT NULL DEFAULT current_timestamp; +#EndIf + + +#IfNotRow2D questionnaires_schemas form_name commitment_questionnaire qid 9 +INSERT INTO `questionnaires_schemas` (`qid`, `form_name`,`form_table`, `column_type`, `question`) +VALUES +('9', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Exemption reason'), +('10', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Comment'); +#EndIf + +#IfNotRow2D list_options list_id lists option_id clinikal_no_payment_reason +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `activity`) VALUES +('lists', 'clinikal_no_payment_reason', 'Reasons for encounter without payment', 0, 1), +('clinikal_no_payment_reason', 'personal', 'Personal', 20, 1), +('clinikal_no_payment_reason', 'followup_encounter', 'Follow up encounter', 30, 1); +#EndIf + +#IfNotRow fhir_value_sets id no_payment_reasons +INSERT INTO `fhir_value_sets` (`id`, `title`) +VALUES ('no_payment_reasons', 'Reasons for encounter without payment'); +#EndIf + +#IfNotRow2D fhir_value_set_systems vs_id no_payment_reasons system clinikal_no_payment_reason +INSERT INTO `fhir_value_set_systems` (`vs_id`, `system`, `type`,`filter`) +VALUES ('no_payment_reasons', 'clinikal_no_payment_reason', 'All', NULL); +#EndIf diff --git a/modules/ClinikalAPI/sql/install.sql b/modules/ClinikalAPI/sql/install.sql index 09d04c1..6338a02 100644 --- a/modules/ClinikalAPI/sql/install.sql +++ b/modules/ClinikalAPI/sql/install.sql @@ -49,6 +49,12 @@ CREATE TABLE `clinikal_templates_map` ( ALTER TABLE `clinikal_templates_map` ADD PRIMARY KEY (`form_id`,`form_field`,`service_type`,`reason_code`,`message_id`); +ALTER TABLE `clinikal_templates_map` ADD `active` tinyint(1) NOT NULL DEFAULT 1; +ALTER TABLE `clinikal_templates_map` ADD `update_by` int(11) NOT NULL; +ALTER TABLE `clinikal_templates_map` ADD `update_date` datetime NOT NULL DEFAULT current_timestamp; + + + CREATE TABLE `form_context_map` ( `form_id` INT NOT NULL, @@ -131,7 +137,9 @@ VALUES ('5', 'commitment_questionnaire','form_commitment_questionnaire', 'integer', 'doctor license number'), ('6', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Payment amount'), ('7', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Payment method'), -('8', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Receipt number'); +('8', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Receipt number'), +('9', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Exemption reason'), +('10', 'commitment_questionnaire','form_commitment_questionnaire', 'string', 'Comment'); INSERT INTO `fhir_value_sets` (`id`, `title`) @@ -150,3 +158,19 @@ CREATE TABLE manage_templates_letters( `letter_post_json` mediumtext DEFAULT NULL, PRIMARY KEY (`id`) ); + + + +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `toggle_setting_1`, `toggle_setting_2`, `activity`, `subtype`, `edit_options`) VALUES +('lists', 'clinikal_form_fields_templates', 'Form fields that use templates', 0, 0, 0, '', '', '', 0, 0, 1, '', 1); + +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `activity`) VALUES +('lists', 'clinikal_no_payment_reason', 'Reasons for encounter without payment', 0, 1), +('clinikal_no_payment_reason', 'personal', 'Personal', 20, 1), +('clinikal_no_payment_reason', 'followup_encounter', 'Follow up encounter', 30, 1); + +INSERT INTO `fhir_value_sets` (`id`, `title`) +VALUES ('no_payment_reasons', 'Reasons for encounter without payment'); + +INSERT INTO `fhir_value_set_systems` (`vs_id`, `system`, `type`,`filter`) +VALUES ('no_payment_reasons', 'clinikal_no_payment_reason', 'All', NULL); diff --git a/modules/ClinikalAPI/src/ClinikalAPI/Controller/FormTemplatesManagementController.php b/modules/ClinikalAPI/src/ClinikalAPI/Controller/FormTemplatesManagementController.php new file mode 100644 index 0000000..d5c1702 --- /dev/null +++ b/modules/ClinikalAPI/src/ClinikalAPI/Controller/FormTemplatesManagementController.php @@ -0,0 +1,351 @@ +container = $container; + if(!AclMain::aclCheckCore('client_app', 'ManageTemplates')){ + exit('Access denied'); + } + } + + public function templatesManagementIndexAction() + { + + $this->loadClientSideForms(); + $this->loadServiceTypes(); + $this->loadtemplates(); + + $langCode = ($this->container->get(LangLanguagesTable::class)->getLangCode($_SESSION['language_choice'] ? $_SESSION['language_choice'] : $this->container->get(LangLanguagesTable::class)->getLangIdByGlobals())); + $data = $this->normalizeDataForTable($this->container->get(GetTemplatesServiceTable::class)->fetchNormalizedData($langCode, ['active' => 1])); + //$this->die_r($data); + + $parameters = array( + 'title' => xlt(self::TITLE), + 'data' => $data, + 'forms' => $this->forms, + 'serviceTypes' => $this->serviceTypes, + 'templates' => $this->templates + ); + $this->layout('clinikalApi/layout/layout'); + return $parameters; + } + + public function templatesManagementAjaxAction() + { + $queryFilters = []; + if ($this->params()->fromQuery('form_name') && $this->params()->fromQuery('form_name') !== 'all') { + $queryFilters['form_id'] = $this->params()->fromQuery('form_name'); + } + if ($this->params()->fromQuery('field_name') && $this->params()->fromQuery('field_name') !== 'all') { + $queryFilters['form_field'] = $this->params()->fromQuery('field_name'); + } + if ($this->params()->fromQuery('service_type') && $this->params()->fromQuery('service_type') !== 'all') { + $queryFilters['service_type'] = $this->params()->fromQuery('service_type'); + } + if ($this->params()->fromQuery('reason_code') && $this->params()->fromQuery('reason_code') !== 'all') { + $queryFilters['reason_code'] = $this->params()->fromQuery('reason_code'); + } + if ($this->params()->fromQuery('template') && $this->params()->fromQuery('template') !== 'all') { + $queryFilters['message_id'] = $this->params()->fromQuery('template'); + } + if (!is_null($this->params()->fromQuery('active')) && $this->params()->fromQuery('active') !== 'all' && $this->params()->fromQuery('active') !== '') { + $queryFilters['active'] = $this->params()->fromQuery('active'); + } + + $langCode = ($this->container->get(LangLanguagesTable::class)->getLangCode($_SESSION['language_choice'] ? $_SESSION['language_choice'] : $this->container->get(LangLanguagesTable::class)->getLangIdByGlobals())); + $data = $this->normalizeDataForTable($this->container->get(GetTemplatesServiceTable::class)->fetchNormalizedData($langCode, $queryFilters)); + $parms = array('data' => $data); + return $this->responseWithNoLayout($parms, true); + } + + public function assignTemplateAction() + { + $this->loadClientSideForms(); + $this->loadServiceTypes(); + $this->loadtemplates(); + + $parameters = array( + 'title' => xlt(self::TITLE), + 'forms' => $this->forms, + 'serviceTypes' => $this->serviceTypes, + 'templates' => $this->templates, + 'user' => $this->getUserNameById($this->getConnectedUserId()) + ); + + if ($this->params()->fromQuery('edit')) { + $queryFilters = []; + $queryFilters['form_id'] = $this->params()->fromQuery('form_id'); + $queryFilters['form_field'] = $this->params()->fromQuery('field_id'); + $queryFilters['service_type'] = $this->params()->fromQuery('service_type'); + $queryFilters['reason_code'] = $this->params()->fromQuery('reason_code'); + $queryFilters['message_id'] = $this->params()->fromQuery('template'); + + $result = $this->container->get(GetTemplatesServiceTable::class)->get($queryFilters)[0]; + $parameters['record'] = $result; + $this->loadFormFileds($result['form_id']); + $this->loadReasonCodes($result['service_type']); + $parameters['formFiles'] = $this->fileds; + $parameters['reasonCode'] = $this->reasonCodes; + $parameters['is_edit'] = true; + } + + if ($this->params()->fromQuery('success_msg')) { + $parameters['showSuccessMsg'] = true; + } + + $this->layout('clinikalApi/layout/layout'); + return $parameters; + + } + + public function loadFiledsAction() + { + $form = $this->params()->fromQuery('filter'); + if (empty($form)) { + throw new \Exception('Missing filter parameter'); + } + $this->loadFormFileds($form); + return $this->responseWithNoLayout($this->fileds ? $this->fileds : []); + } + + public function loadReasonCodesAction() + { + $serviceType = $this->params()->fromQuery('filter'); + if (empty($serviceType)) { + throw new \Exception('Missing filter parameter'); + } + $this->loadReasonCodes($serviceType); + $reasonCodes = $this->reasonCodes ? $this->reasonCodes : []; + $allReasonsOptions = [GetTemplatesServiceTable::ALL_REASON_CODE => xlt(GetTemplatesServiceTable::ALL_REASON_CODE_STRING)]; + + return $this->responseWithNoLayout(array_merge( $reasonCodes, $allReasonsOptions)); + } + + /** + * @return \Zend\Stdlib\ResponseInterface + */ + public function saveAssignTemplateAction() + { + $data = $this->params()->fromPost('data'); + $isEdit = $this->params()->fromPost('is_edit'); + $oldData = $isEdit ? $this->params()->fromPost('old_data') : null; + + if ($this->validateData($data)) { + $isExistData = $data; + unset($isExistData['seq'], $isExistData['active']); + $isExist = !empty($this->container->get(GetTemplatesServiceTable::class)->get($isExistData)) ? true : false; + if(!$isExist && $isEdit) { + //delete old record + $this->container->get(GetTemplatesServiceTable::class)->delete($oldData); + } + if($isExist && $isEdit) { + if($this->primaryKeysNotChanged($data, $oldData)) { + $this->container->get(GetTemplatesServiceTable::class)->delete($oldData); + } else { + return $this->responseWithNoLayout('data_conflict', true, 409); + } + } + if ($isExist && !$isEdit) { + return $this->responseWithNoLayout('data_conflict', true, 409); + } + + $data['update_by'] = $this->getConnectedUserId(); + $result = $this->container->get(GetTemplatesServiceTable::class)->insert($data); + return $this->responseWithNoLayout(true, true); + + } + return $this->responseWithNoLayout(false, 500); + } + + public function deleteAssignTemplateAction() + { + $data = $this->params()->fromPost('data'); + + if ($this->validateData($data)) { + unset($data['seq'], $data['active']); + + $valid = $this->container->get(GetTemplatesServiceTable::class)->delete($data); + + return $this->responseWithNoLayout($valid ? true : false, true); + } + return $this->responseWithNoLayout(false, 500); + } + + public function addEditTemplateAction() + { + $templates = $this->container->get(ListsTable::class)->getAllList('clinikal_templates', 'title');; + $this->layout('clinikalApi/layout/layout'); + return [ + 'templates' => $templates, + 'title' => xlt(self::TITLE), + 'showSuccessMsg' => ($this->params()->fromQuery('success_msg')) ? true : false + ]; + } + + public function saveNewTemplateAction() + { + $template = trim($this->params()->fromPost('template')); + + $found = $this->container->get(ListsTable::class)->getListForViewForm(self::TEMPLATES_LIST, false, ['title' => $template], false); + if (!empty($found)) { + return $this->responseWithNoLayout('data_conflict', true, 409); + } + + $listObj = new Lists(); + $listObj->exchangeArray([ + 'list_id' => self::TEMPLATES_LIST, + 'option_id' => bin2hex(random_bytes(3)), + 'title' => $template, + 'activity' => 1, + ]); + + $insert = $this->container->get(ListsTable::class)->insert($listObj); + return $this->responseWithNoLayout($insert ? true : false); + + } + + public function updateTemplateAction() + { + $templateId = $this->params()->fromPost('id'); + $templateText = trim($this->params()->fromPost('text')); + $activity = $this->params()->fromPost('activity'); + $oldText = trim($this->params()->fromPost('old_text')); + $oldActivity = $this->params()->fromPost('old_activity'); + + if ($templateText !== $oldText) { + + $found = $this->container->get(ListsTable::class)->getListForViewForm(self::TEMPLATES_LIST, false, ['title' => $templateText], false); + if (!empty($found)) { + return $this->responseWithNoLayout('data_conflict', true, 409); + } + } + + $update = $this->container->get(ListsTable::class)->update( + ['title' => $templateText, 'activity' => $activity], + ['list_id' => self::TEMPLATES_LIST, 'option_id' => $templateId] + ); + + if ($activity == 0 && $oldActivity == 1) { + $this->container->get(GetTemplatesServiceTable::class)->update(['active' => 0], ['message_id' => $templateId]); + } + + return $this->responseWithNoLayout($update ? true : false); + + } + + public function deleteTemplateAction() + { + $templateId = $this->params()->fromPost('id'); + + $delete = $this->container->get(ListsTable::class)->delete( + ['list_id' => self::TEMPLATES_LIST, 'option_id' => $templateId] + ); + + if ($delete) { + $delete = $this->container->get(GetTemplatesServiceTable::class)->delete(['message_id' => $templateId]); + } + return $this->responseWithNoLayout($delete ? true : false); + } + + private function loadClientSideForms() + { + $this->forms = $this->container->get(RegistryTable::class)->getFormsKeyDirectoryValueName(['category' => 'React form']); + } + + private function loadFormFileds($formId = null) + { + $this->fileds = is_null($formId) + ? + $this->container->get(ListsTable::class)->getListForViewForm('clinikal_form_fields_templates', true) + : + $this->container->get(ListsTable::class)->getListForViewForm('clinikal_form_fields_templates', true, ['notes' => $formId]); + } + + private function loadServiceTypes() + { + $this->serviceTypes = $this->container->get(ListsTable::class)->getListForViewForm('clinikal_service_types', true); + } + + private function loadReasonCodes($serviceType = null) + { + $this->reasonCodes = is_null($serviceType) + ? + $this->container->get(ListsTable::class)->getListForViewForm('clinikal_reason_codes', true) + : + $this->container->get(ListsTable::class)->getListForViewForm('clinikal_reason_codes', true, ['notes' => $serviceType]); + } + + private function loadtemplates() + { + $this->templates = $this->container->get(ListsTable::class)->getListForViewForm('clinikal_templates', true, [], false); + } + + private function normalizeDataForTable($dbMapping) + { + $results = []; + foreach ($dbMapping as $item) { + $results[] = [ + $item['form'], + $item['field'], + $item['service_type'], + $item['reason_code'], + $item['template'], + (intval($item['active']) === 1) ? "" : "", + '' + ]; + } + return $results; + } + + + + private function validateData($data) + { + $requiredFileds = ['form_id','form_field','service_type','reason_code','message_id','seq','active']; + foreach ($requiredFileds as $name) { + if(!isset($data[$name]) || is_null($data[$name]) || $data[$name] == '') { + return false; + } + } + return true; + } + + private function primaryKeysNotChanged($data, $oldData) + { + $requiredFileds = ['form_id','form_field','service_type','reason_code','message_id']; + foreach ($requiredFileds as $name) { + if($data[$name] !== $oldData[$name]) return false; + } + return true; + } + +} diff --git a/modules/ClinikalAPI/src/ClinikalAPI/Controller/PdfBaseController.php b/modules/ClinikalAPI/src/ClinikalAPI/Controller/PdfBaseController.php index 326a13a..fa1eb93 100644 --- a/modules/ClinikalAPI/src/ClinikalAPI/Controller/PdfBaseController.php +++ b/modules/ClinikalAPI/src/ClinikalAPI/Controller/PdfBaseController.php @@ -6,6 +6,7 @@ use FhirAPI\FhirRestApiBuilder\Parts\Strategy\StrategyElement\ServiceRequest\FhirServiceRequestMapping; use FhirAPI\FhirRestApiBuilder\Parts\Strategy\StrategyElement\ServiceRequest\ServiceRequest; use FhirAPI\Model\FhirServiceRequestTable; +use FhirAPI\Model\QuestionnaireResponseTable; use Formhandler\View\Helper\GenericTable; use GenericTools\Controller\BaseController as GenericBaseController; use GenericTools\Model\AclTables; @@ -28,6 +29,8 @@ class PdfBaseController extends GenericBaseController const FOOTER_PATH = 'clinikal-api/pdf/default-footer'; const DOC_TYPE = "file_url"; const PDF_MINE_TYPE = "application/pdf"; + const CITIES_LIST = 'mh_cities'; + const STREETS_LIST = 'mh_streets'; private $container; public $postData = array(); @@ -71,6 +74,7 @@ public function getPatientInfo($id = null) $data['age'] = $info['age']; $data['phone'] = ($info['phone_cell'] ? $info['phone_cell'] : ($info['phone_home'] ? $info['phone_home'] : ($info['phone_contact'] ? $info['phone_contact'] :""))) ; $data['HMO'] = $info['insurance_organiz_name']; + $data['address'] = $this->patientAddress($info); return $data; } else { @@ -78,17 +82,48 @@ public function getPatientInfo($id = null) } } - public function getListsOpenEMRInfo($type=null,$pid,$outcome) + public function patientAddress($patientInfo) + { + $city = !empty($patientInfo['city']) ? xlt($this->container->get(ListsTable::class)->getSpecificTitle(self::CITIES_LIST, $patientInfo['city'])) : ''; + $street = !empty($patientInfo['street']) ? xlt($this->container->get(ListsTable::class)->getSpecificTitle(self::STREETS_LIST, $patientInfo['street'])) : ''; + $numberHouse = !empty($patientInfo['mh_house_no']) ? $patientInfo['mh_house_no'] : ''; + $pobox = !empty($patientInfo['mh_pobox']) ? $patientInfo['mh_pobox'] : ''; + + $address = ''; + if ($street !== '') { + $address .= $street . ' ' . $numberHouse . ', '; + } + if ($pobox !== '') { + $address .= xlt('PO Box') . ' ' . $pobox .', '; + } + $address .= $city; + + return $address; + } + + public function getListsOpenEMRInfo($type=null,$pid,$encounter,$outcome) { if (!is_null($type)) { - $info = $this->container->get('GenericTools\Model\ListsOpenEmrTable')->getListWithTheType($type,$pid,$outcome); - return $info; + $info = $this->container->get('GenericTools\Model\ListsOpenEmrTable')->getListWithTheType($type,$pid,$encounter,$outcome); + $result = []; + foreach ($info as $item) { + if(!in_array($item, $result)) { + $result[] = $item; + } + } + return $result; } else { return array(); } } + public function getEncounterStartDate($encId) + { + $info = $this->container->get(FormEncounterTable::class)->fetchById($encId); + return $info['date']; + } + public function getUserInfo($id = null) { if (!is_null($id)) { @@ -188,8 +223,11 @@ public function createBase64Pdf($fileName,$bodyPath,$headerPath, $footerPath,$he { foreach($bodyPath as $key=>$path) { $bodyData=$bodyDataTemp[$key]; + if ($key > 0) { + $this->getPdfService()->pagebreak(); + } $this->getPdfService()->bodyBuilder($path, $bodyData); - $this->getPdfService()->pagebreak(); + } } else { @@ -247,13 +285,13 @@ public function getServiceTypeAndReasonCodeArray(){ } public function getSensitivities(){ - return $this->getListsOpenEMRInfo("sensitive",$this->postData['patient'],1); + return $this->getListsOpenEMRInfo("sensitive",$this->postData['patient'],$this->postData['encounter'],1); } public function getMedicalProblems(){ - return $this->getListsOpenEMRInfo("medical_problem",$this->postData['patient'],1); + return $this->getListsOpenEMRInfo("medical_problem",$this->postData['patient'],$this->postData['encounter'],1); } public function getMedicine(){ - return $this->getListsOpenEMRInfo("medication",$this->postData['patient'],1); + return $this->getListsOpenEMRInfo("medication",$this->postData['patient'],$this->postData['encounter'],1); } public function createTableJsonFromVitals($vitals){ $arrTemp = []; @@ -270,15 +308,20 @@ public function createTableJsonFromVitals($vitals){ return $arrTemp; } - public function getConstantVitals($pid,$category,$acitivity,$order) + public function getConstantVitals($encounter,$pid,$category,$acitivity,$order) { - $vitals = $this->getVitals($pid,$category,$acitivity,$order); + $vitals = $this->getVitals($encounter,$pid,$category,$acitivity,$order); foreach($vitals as $k=>$v) { if($k === 0) { foreach ($v as $key => $value) { if ($key !== 'height' && $key !== 'weight') { unset($vitals[$k][$key]); + } else { + $vitals[$k][$key][2] = (is_null($value[2]) || $value[2] == "" || $value[2] == 0 && $value[2] == "0.00") ?"-":$value[2]; + if ($vitals[$k][$key][2] !== '-') { + $vitals[$k][$key][2] = $key === 'weight' ? number_format($value[2],1) : number_format($value[2]); + } } } } @@ -288,20 +331,20 @@ public function getConstantVitals($pid,$category,$acitivity,$order) } return sizeof($vitals[0]) > 1 ? $this->createTableJsonFromVitals($vitals) : null; } - public function getVariantVitals($pid,$category,$acitivity,$order) + public function getVariantVitals($encounter,$pid,$category,$acitivity,$order) { - $vitals = $this->getVitals($pid,$category,$acitivity,$order); + $vitals = $this->getVitals($encounter,$pid,$category,$acitivity,$order); foreach($vitals as $key=>$value){ foreach($value['bpd'] as $k=>$v) { - if($k==0) + if($k!==2) { $vitals[$key]['pressure'][$k] = $vitals[$key]['bpd'][$k]; } else { - $vitals[$key]['pressure'][$k] = $vitals[$key]['bpd'][$k] . "/" . $vitals[$key]['bps'][$k]; + $vitals[$key]['pressure'][$k] = !is_null($vitals[$key]['bps'][$k] ) ? $vitals[$key]['bps'][$k] . "/" . $vitals[$key]['bpd'][$k] : '-'; } } @@ -316,6 +359,17 @@ public function getVariantVitals($pid,$category,$acitivity,$order) case 'head_circ': unset($vitals[$key][$k]); break; + case 'temperature': + $vitals[$key][$k][2] = $vitals[$key][$k][2] !== '0.00' && $vitals[$key][$k][2] > 0 ? number_format($vitals[$key][$k][2],1) : '-'; + break; + case 'pulse': + case 'respiration': + case 'oxygen_saturation': + $vitals[$key][$k][2] = $vitals[$key][$k][2] !== '0.00' && $vitals[$key][$k][2] > 0 ? number_format($vitals[$key][$k][2],0) : '-'; + break; + case 'pain_severity': + $vitals[$key][$k][2] = is_null($v[2]) ? "-":$v[2]; + break; case 'date': $time = explode(":",$vitals[$key][$k][1]); unset($time[2]); @@ -335,7 +389,7 @@ public function getVariantVitals($pid,$category,$acitivity,$order) return $this->createTableJsonFromVitals($vitals); } - public function getVitals($pid,$category,$acitivity,$order){ + public function getVitals($encounter,$pid,$category,$acitivity,$order){ $observationList = $this->container->get('GenericTools\Model\ListsTable')->getList("loinc_org"); $observationTitleList = []; foreach($observationList as $key=>$value) @@ -343,7 +397,7 @@ public function getVitals($pid,$category,$acitivity,$order){ $notes = json_decode($value['notes']); $observationTitleList[$value['mapping']] = [xl($notes->label),xl($value['subtype'])]; } - $observations = $this->container->get('GenericTools\Model\FormVitalsTable')->getVitals($pid,$category,$acitivity,$order); + $observations = $this->container->get('GenericTools\Model\FormVitalsTable')->getVitals($encounter,$pid,$category,$acitivity,$order); $observedArr = []; foreach($observations as $keyParent=>$valuesParent) @@ -354,7 +408,7 @@ public function getVitals($pid,$category,$acitivity,$order){ $observedArr[$keyParent][$key] = array_merge($observationTitleList[$key], [$value]); } if($key==="date"){ - $observedArr[$keyParent][$key]=[xlt("Hour"), explode(" ",$value)[1]]; + $observedArr[$keyParent][$key]=[xlt("Hour"), oeFormatDateTime($value)]; } } @@ -406,4 +460,10 @@ public function getDrugForm(){ return $list; } + public function getQuestionareUpdatedUser($encounter, $questionaireName) + { + $result = $this->container->get(QuestionnaireResponseTable::class)->buildGenericSelect(['encounter' => $encounter, 'form_name' => $questionaireName]); + return !empty($result) ? $result[0]['update_by'] : null; + } + } diff --git a/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesService.php b/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesService.php index 8ce41f0..2ce3d33 100644 --- a/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesService.php +++ b/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesService.php @@ -18,6 +18,7 @@ class GetTemplatesService public $reason_code; public $message_id; public $order; + public $active; //lists fields for join public $option_id; @@ -37,6 +38,7 @@ public function exchangeArray($data) $this->option_id = (!empty($data['option_id'])) ? $data['option_id'] : null; $this->title = (!empty($data['title'])) ? $data['title'] : null; $this->seq = (!empty($data['seq'])) ? $data['seq'] : null; + $this->active = (!empty($data['active'])) ? $data['active'] : null; } } diff --git a/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesServiceTable.php b/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesServiceTable.php index 31fc3f4..c9f120e 100644 --- a/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesServiceTable.php +++ b/modules/ClinikalAPI/src/ClinikalAPI/Model/GetTemplatesServiceTable.php @@ -15,6 +15,8 @@ class GetTemplatesServiceTable { const LEFT_JOIN = "LEFT"; + const ALL_REASON_CODE = 'all_reasons'; + const ALL_REASON_CODE_STRING = 'All the reason codes'; protected $tableGateway; public function __construct(TableGateway $tableGateway) { @@ -35,18 +37,22 @@ public function getTemplatesForForm($form_id=null,$form_field=null,$service_type $joinExp= new Expression("clinikal_templates_map.message_id = list.option_id AND list.list_id = 'clinikal_templates' "); $select->join(array ("list"=>"list_options") , $joinExp, $this->listArr, self::LEFT_JOIN); $where = new Where(); - + $where->equalTo("active", 1); if(!is_null($service_type)){ $where->equalTo("service_type",$service_type)->AND-> - in("reason_code",explode(",",$reason_code))->AND-> + NEST-> + in("reason_code",explode(",",$reason_code))->OR-> + equalTo("reason_code",self::ALL_REASON_CODE)-> + UNNEST->AND-> equalTo("form_field",$form_field)->AND-> equalTo("form_id",$form_id); } $select->where($where); $select->order(array('clinikal_templates_map.seq ASC')); - $debug = $select->getSqlString(); + // $debug = $select->getSqlString(); + //echo $debug;die; $rs = $this->tableGateway->selectWith($select); foreach ($rs as $r) { $rsArray[] = get_object_vars($r); @@ -54,4 +60,78 @@ public function getTemplatesForForm($form_id=null,$form_field=null,$service_type return $rsArray; } + + /** + * @return array - all of the facilities from facility table + */ + public function fetchNormalizedData($langCode, $equalFilters = null) + { + $sql = " + SELECT + ctm.form_id as form_id, + translateString(r.name, ?) as form, + ctm.form_field as field_id, + translateString(GetOptionTitle('clinikal_form_fields_templates', ctm.form_field),?) as field, + ctm.service_type as service_type_id, + translateString(GetOptionTitle('clinikal_service_types', ctm.service_type),?) as service_type, + ctm.reason_code as reason_code_id, + translateString(ifnull(GetOptionTitle('clinikal_reason_codes', ctm.reason_code),'" . self::ALL_REASON_CODE_STRING . "'),?) as reason_code, + ctm.message_id as template_id, + translateString(GetOptionTitle('clinikal_templates', ctm.message_id),?) as template, + ctm.active as active + FROM clinikal_templates_map AS ctm + JOIN registry AS r ON ctm.form_id = r.directory + "; + $bindParams = [$langCode, $langCode, $langCode, $langCode, $langCode]; + + $where = []; + foreach ($equalFilters as $column => $value) { + $where[] = " ctm.$column = ? "; + $bindParams[] = $value; + } + if (!empty($equalFilters)) { + $sql .= " WHERE " . implode('AND', $where); + } + + $sql .= " ORDER BY form, field, service_type, reason_code, seq"; + + $statement = $this->tableGateway->adapter->createStatement($sql, $bindParams); + $return = $statement->execute(); + $results = array(); + foreach ($return as $row) { + $results[] = $row; + } + return $results; + } + + public function get($equalFilters) + { + $select = $this->tableGateway->getSql()->select(); + $where = new Where(); + foreach ($equalFilters as $column => $value) { + $where->equalTo($column, $value); + } + $select->where($where); + //$debug = $select->getSqlString(); + //echo $debug;die; + $rs = $this->tableGateway->selectWith($select); + $rsArray = []; + foreach ($rs as $r) { + $rsArray[] = get_object_vars($r); + } + return $rsArray; + } + + public function insert($data) { + return $this->tableGateway->insert($data) ? true : false; + } + + public function delete($data) { + return $this->tableGateway->delete($data) ? true : false; + } + + public function update($set, $where) + { + return $this->tableGateway->update($set, $where) ? true : false; + } } diff --git a/modules/ClinikalAPI/src/ClinikalAPI/Service/IndicatorSettingsService.php b/modules/ClinikalAPI/src/ClinikalAPI/Service/IndicatorSettingsService.php index 5c61472..d60c981 100644 --- a/modules/ClinikalAPI/src/ClinikalAPI/Service/IndicatorSettingsService.php +++ b/modules/ClinikalAPI/src/ClinikalAPI/Service/IndicatorSettingsService.php @@ -40,7 +40,15 @@ public function getIndicatorSettings($indicator) if($val['notes']!=='') { $notes = json_decode($val['notes']); $arrTemp['label'] = $notes->label; - $arrTemp['mask'] = $notes->mask; + if (isset($notes->mask)) { + $arrTemp['mask'] = $notes->mask; + } + if (isset($notes->placeholder)) { + $arrTemp['placeholder'] = $notes->placeholder; + } + if (isset($notes->symbol)) { + $arrTemp['symbol'] = $notes->symbol; + } $indicators['variant'][$key]=$arrTemp; }else{ $arrTemp['label'] = $val['mapping']; @@ -56,7 +64,15 @@ public function getIndicatorSettings($indicator) if($val['notes']!=='') { $notes = json_decode($val['notes']); $arrTemp['label'] = $notes->label; - $arrTemp['mask'] = $notes->mask; + if (isset($notes->mask)) { + $arrTemp['mask'] = $notes->mask; + } + if (isset($notes->placeholder)) { + $arrTemp['placeholder'] = $notes->placeholder; + } + if (isset($notes->symbol)) { + $arrTemp['symbol'] = $notes->symbol; + } $indicators['constant'][$key]=$arrTemp; }else{ $arrTemp['label'] = $val['mapping']; diff --git a/modules/ClinikalAPI/src/ClinikalAPI/Service/Settings.php b/modules/ClinikalAPI/src/ClinikalAPI/Service/Settings.php index c1cf1d9..2896193 100644 --- a/modules/ClinikalAPI/src/ClinikalAPI/Service/Settings.php +++ b/modules/ClinikalAPI/src/ClinikalAPI/Service/Settings.php @@ -58,8 +58,15 @@ public function getGlobalsSettings($uname) "clinikal_pa_arrival_way" =>$GLOBALS['clinikal_pa_arrival_way'], "clinikal_pa_next_enc_status" =>$GLOBALS['clinikal_pa_next_enc_status'], ), + "forms" => array( ) + ) ); + foreach ($GLOBALS as $key => $value) { + if (substr( $key, 0, 14 ) === "clinikal_forms") { + $settings['clinikal']['forms'][$key] = $value; + } + } return RestControllerHelper::responseHandler($settings, null, 200); } diff --git a/modules/ClinikalAPI/version.php b/modules/ClinikalAPI/version.php index e2d742c..e5e2435 100644 --- a/modules/ClinikalAPI/version.php +++ b/modules/ClinikalAPI/version.php @@ -1,5 +1,5 @@ 'go_back', 'title' => 'cancel'); +?> + +
+ + titleWithButtons("Add/edit template" , $menuBottons); ?> +
+
+ + + + + +
+
+
+
+

+
+
+ +
+
+ +
+
+
+
+
+

+
+
+ + + +
+
+
+
+ +
+
+
+ +
+
+ + +
+ +
+
+ + +
+
+
+ + + diff --git a/modules/ClinikalAPI/view/clinikal-api/form-templates-management/assign-template.phtml b/modules/ClinikalAPI/view/clinikal-api/form-templates-management/assign-template.phtml new file mode 100644 index 0000000..26fe6b9 --- /dev/null +++ b/modules/ClinikalAPI/view/clinikal-api/form-templates-management/assign-template.phtml @@ -0,0 +1,456 @@ + 'save', 'title' => 'Save'); +if ($is_edit) { + $menuBottons[] = array('id' => 'delete', 'title' => 'Delete assigned'); +} +$menuBottons[] = array('id' => 'go_back', 'title' => 'cancel'); +?> + +
+ + titleWithButtons($is_edit? "Update assigned" : "Assign template" , $menuBottons); ?> + +
+
+
+ + + + + +
+
+
+
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+ +
+ + + +
+
+
+
+ + + +
+ +
+ + + +
+ +
+
+ +
+
+ + +
+ +
+
+
+
+
+
+
+ +
+
+ +
+
+
+
+
+
+ +
+
+ +
+
+
+ +
+ + + +
+ + + + + diff --git a/modules/ClinikalAPI/view/clinikal-api/form-templates-management/templates-management-index.phtml b/modules/ClinikalAPI/view/clinikal-api/form-templates-management/templates-management-index.phtml new file mode 100644 index 0000000..cc9fb6c --- /dev/null +++ b/modules/ClinikalAPI/view/clinikal-api/form-templates-management/templates-management-index.phtml @@ -0,0 +1,283 @@ +Xl('Form'), + '1'=>Xl('field'), + '2'=>Xl('Service type'), + '3'=>Xl('Reason code'), + '4'=>Xl('Template'), + '5'=>Xl('Active'), + '6' => '' +); +$table_handler= new DataTableExtended($table_id,$table_col_names); +$headers = new ClinikalHeaders(); + +?> + + +LoadEssentialCssJs(); ?> +
+ + titleWithButtons('Templates management', array( + array('id' => 'assign_template', 'title' => 'Assign template' ), + array('id' => 'add_template', 'title' => 'Add/edit template' ), + )); ?> + +
+
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + + +
+ +
+
+ + +
+ + +
+ +
+ +
+ +
+
+
+
+ echoBaseHTML(); ?> +
+
+
+
+
+ + + + diff --git a/modules/ClinikalAPI/view/clinikal-api/pdf/default-header.phtml b/modules/ClinikalAPI/view/clinikal-api/pdf/default-header.phtml index 5187d92..a7a2a74 100644 --- a/modules/ClinikalAPI/view/clinikal-api/pdf/default-header.phtml +++ b/modules/ClinikalAPI/view/clinikal-api/pdf/default-header.phtml @@ -10,7 +10,7 @@ $clinicName
- - -
+ @@ -34,19 +34,19 @@ $clinicName
-
+
+ " width="126px" height="65px"/> - + - + +
diff --git a/modules/ClinikalAPI/view/clinikal-api/pdf/doctor-signature.phtml b/modules/ClinikalAPI/view/clinikal-api/pdf/doctor-signature.phtml index c3e461a..a5b07c4 100644 --- a/modules/ClinikalAPI/view/clinikal-api/pdf/doctor-signature.phtml +++ b/modules/ClinikalAPI/view/clinikal-api/pdf/doctor-signature.phtml @@ -5,7 +5,7 @@ width: 52px; height: 18px; opacity: 0.6; font-family: OpenSansHebrew; -font-size: 12px; +font-size: 14px; font-weight: normal; font-stretch: normal; font-style: normal; diff --git a/modules/ClinikalAPI/view/clinikal-api/pdf/patient-details.phtml b/modules/ClinikalAPI/view/clinikal-api/pdf/patient-details.phtml index 0717a8c..6466302 100644 --- a/modules/ClinikalAPI/view/clinikal-api/pdf/patient-details.phtml +++ b/modules/ClinikalAPI/view/clinikal-api/pdf/patient-details.phtml @@ -12,7 +12,7 @@ height: 18px; opacity: 0.6; font-family: OpenSansHebrew; - font-size: 10px; + font-size: 13px; font-weight: normal; font-stretch: normal; font-style: normal; @@ -25,7 +25,7 @@ width: 104px; height: 18px; font-family: OpenSansHebrew; - font-size: 12px; + font-size: 15px; font-weight: normal; font-stretch: normal; font-style: normal; @@ -34,6 +34,7 @@ text-align: right; color: #000b40; } + @@ -69,8 +70,8 @@

diff --git a/modules/ClinikalAPI/view/layout/layout.phtml b/modules/ClinikalAPI/view/layout/layout.phtml new file mode 100644 index 0000000..5229c97 --- /dev/null +++ b/modules/ClinikalAPI/view/layout/layout.phtml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + +
+
+
+ content; ?> +
+
+
+ + + diff --git a/modules/FhirAPI/sql/1_0_0-to-2_0_0_upgrade.sql b/modules/FhirAPI/sql/1_0_0-to-2_0_0_upgrade.sql new file mode 100644 index 0000000..df04fd3 --- /dev/null +++ b/modules/FhirAPI/sql/1_0_0-to-2_0_0_upgrade.sql @@ -0,0 +1,112 @@ +-- +-- Comment Meta Language Constructs: +-- +-- #IfNotTable +-- argument: table_name +-- behavior: if the table_name does not exist, the block will be executed + +-- #IfTable +-- argument: table_name +-- behavior: if the table_name does exist, the block will be executed + +-- #IfColumn +-- arguments: table_name colname +-- behavior: if the table and column exist, the block will be executed + +-- #IfMissingColumn +-- arguments: table_name colname +-- behavior: if the table exists but the column does not, the block will be executed + +-- #IfNotColumnType +-- arguments: table_name colname value +-- behavior: If the table table_name does not have a column colname with a data type equal to value, then the block will be executed + +-- #IfNotRow +-- arguments: table_name colname value +-- behavior: If the table table_name does not have a row where colname = value, the block will be executed. + +-- #IfNotRow2D +-- arguments: table_name colname value colname2 value2 +-- behavior: If the table table_name does not have a row where colname = value AND colname2 = value2, the block will be executed. + +-- #IfNotRow3D +-- arguments: table_name colname value colname2 value2 colname3 value3 +-- behavior: If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed. + +-- #IfNotRow4D +-- arguments: table_name colname value colname2 value2 colname3 value3 colname4 value4 +-- behavior: If the table table_name does not have a row where colname = value AND colname2 = value2 AND colname3 = value3 AND colname4 = value4, the block will be executed. + +-- #IfNotRow2Dx2 +-- desc: This is a very specialized function to allow adding items to the list_options table to avoid both redundant option_id and title in each element. +-- arguments: table_name colname value colname2 value2 colname3 value3 +-- behavior: The block will be executed if both statements below are true: +-- 1) The table table_name does not have a row where colname = value AND colname2 = value2. +-- 2) The table table_name does not have a row where colname = value AND colname3 = value3. + +-- #IfRow2D +-- arguments: table_name colname value colname2 value2 +-- behavior: If the table table_name does have a row where colname = value AND colname2 = value2, the block will be executed. + +-- #IfRow3D +-- arguments: table_name colname value colname2 value2 colname3 value3 +-- behavior: If the table table_name does have a row where colname = value AND colname2 = value2 AND colname3 = value3, the block will be executed. + +-- #IfIndex +-- desc: This function is most often used for dropping of indexes/keys. +-- arguments: table_name colname +-- behavior: If the table and index exist the relevant statements are executed, otherwise not. + +-- #IfNotIndex +-- desc: This function will allow adding of indexes/keys. +-- arguments: table_name colname +-- behavior: If the index does not exist, it will be created + +-- #EndIf +-- all blocks are terminated with a #EndIf statement. + +-- #IfNotListReaction +-- Custom function for creating Reaction List + +-- #IfNotListOccupation +-- Custom function for creating Occupation List + +-- #IfTextNullFixNeeded +-- desc: convert all text fields without default null to have default null. +-- arguments: none + +-- #IfTableEngine +-- desc: Execute SQL if the table has been created with given engine specified. +-- arguments: table_name engine +-- behavior: Use when engine conversion requires more than one ALTER TABLE + +-- #IfInnoDBMigrationNeeded +-- desc: find all MyISAM tables and convert them to InnoDB. +-- arguments: none +-- behavior: can take a long time. + +-- #IfTranslationNeeded +-- desc: find all MyISAM tables and convert them to InnoDB. +-- arguments: constant_name english hebrew +-- behavior: can take a long time. + +REPLACE INTO `list_options` (`list_id`, `option_id`, `title`, `seq`,`mapping` ,`notes`, `activity`,`subtype`) VALUES +('loinc_org', '8480-6', 'Systolic blood pressure', 10,'bps','{"placeholder": "___" ,"label":"Blood pressure", "mask": "###/###"}', 1,'mmHg'), +('loinc_org', '8462-4', 'Diastolic blood pressure', 20,'bpd','{"placeholder": "___","label":"Blood pressure", "mask": "###/###"}', 1,'mmHg'), +('loinc_org', '8308-9', 'Body height --standing', 30,'height','{"label": "Height","placeholder":"___"}', 1,'cm'), +('loinc_org', '8335-2', 'Body weight Estimated', 40,'weight','{"label": "Weight","placeholder":"___._"}', 1,'Kg'), +('loinc_org', '69000-8','Heart rate --sitting', 50,'pulse','{"label": "Pulse","placeholder": "___"}', 1,'PRA'), +('loinc_org', '8310-5', 'Body temperature', 60,'temperature','{"label": "Fever","placeholder": "__._","mask": "##.#"}', 1,'C'), +('loinc_org', '8327-9', 'Body temperature measurement site', 70,'temp_method','', 1,''), +('loinc_org', '20564-1', 'Oxygen saturation in Blood', 80,'oxygen_saturation','{"label": "Saturation","placeholder": "___", "symbol": "%"}', 1,'%'), +('loinc_org', '39156-5', 'Body mass index (BMI) [Ratio]', 90,'BMI','', 1,'kg/m2'), +('loinc_org', '59574-4', 'Body mass index (BMI) [Percentile]', 100,'BMI_status','', 1,''), +('loinc_org', '8280-0', 'Waist Circumference at umbilicus by Tape measure', 110,'waist_circ','', 1,'cm'), +('loinc_org', '8287-5', 'Head Occipital-frontal circumference by Tape measure', 120,'head_circ','', 1,'cm'), +('loinc_org', '9303-9', 'Respiratory rate --resting', 130,'respiration','{"label": "Breaths per minute","placeholder": "__"}', 1,'BPM'), +('loinc_org', '72514-3', 'Pain severity - 0-10 verbal numeric rating [Score] - Reported', 140,'pain_severity','{"label": "Pain level","placeholder": "__"}', 1,''), +('loinc_org', '74774-1', 'Glucose [Mass/volume] in Serum, Plasma or Blood', 150,'glucose','{"label": "Blood sugar","placeholder": "___"}', 1,'mg/dL'); + +#IfMissingColumn fhir_value_sets language +ALTER TABLE fhir_value_sets ADD `language` VARCHAR(3) NOT NULL DEFAULT 'en'; +#EndIf diff --git a/modules/FhirAPI/sql/install.sql b/modules/FhirAPI/sql/install.sql index 502740d..442721f 100644 --- a/modules/FhirAPI/sql/install.sql +++ b/modules/FhirAPI/sql/install.sql @@ -69,6 +69,7 @@ CREATE TABLE `fhir_value_sets` ( `id` VARCHAR(125) NOT NULL, `title` VARCHAR(125) NOT NULL, `status` ENUM('active', 'retired') NOT NULL DEFAULT 'active', + `language` VARCHAR(3) NOT NULL DEFAULT 'en', PRIMARY KEY(`id`) ) ENGINE=INNODB DEFAULT CHARSET=UTF8; @@ -246,22 +247,21 @@ ADD `status_update_date` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP AFTER `seco INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`,`mapping` ,`notes`, `activity`,`subtype`) VALUES -('lists', 'loinc_org', 'http://loinc.org', 0,'','', 1,''), -('loinc_org', '8480-6', 'Systolic blood pressure', 10,'bps','{"mask": "999","label":"Blood pressure"}', 1,'mmHg'), -('loinc_org', '8462-4', 'Diastolic blood pressure', 20,'bpd','{"mask": "999","label":"Blood pressure"}', 1,'mmHg'), -('loinc_org', '8308-9', 'Body height --standing', 30,'height','{"label": "Height","mask":"999"}', 1,'cm'), -('loinc_org', '8335-2', 'Body weight Estimated', 40,'weight','{"label": "Weight","mask":"999.9"}', 1,'Kg'), -('loinc_org', '69000-8','Heart rate --sitting', 50,'pulse','{"label": "Pulse","mask": "99"}', 1,'PRA'), -('loinc_org', '8310-5', 'Body temperature', 60,'temperature','{"label": "Fever","mask": "99.9"}', 1,'C'), +('loinc_org', '8480-6', 'Systolic blood pressure', 10,'bps','{"mask": "___","label":"Blood pressure"}', 1,'mmHg'), +('loinc_org', '8462-4', 'Diastolic blood pressure', 20,'bpd','{"mask": "___","label":"Blood pressure"}', 1,'mmHg'), +('loinc_org', '8308-9', 'Body height --standing', 30,'height','{"label": "Height","mask":"___"}', 1,'cm'), +('loinc_org', '8335-2', 'Body weight Estimated', 40,'weight','{"label": "Weight","mask":"___._"}', 1,'Kg'), +('loinc_org', '69000-8','Heart rate --sitting', 50,'pulse','{"label": "Pulse","mask": "___"}', 1,'PRA'), +('loinc_org', '8310-5', 'Body temperature', 60,'temperature','{"label": "Fever","mask": "__._"}', 1,'C'), ('loinc_org', '8327-9', 'Body temperature measurement site', 70,'temp_method','', 1,''), -('loinc_org', '20564-1', 'Oxygen saturation in Blood', 80,'oxygen_saturation','{"label": "Saturation","mask": "999%"}', 1,'%'), +('loinc_org', '20564-1', 'Oxygen saturation in Blood', 80,'oxygen_saturation','{"label": "Saturation","mask": "___%"}', 1,'%'), ('loinc_org', '39156-5', 'Body mass index (BMI) [Ratio]', 90,'BMI','', 1,'kg/m2'), ('loinc_org', '59574-4', 'Body mass index (BMI) [Percentile]', 100,'BMI_status','', 1,''), ('loinc_org', '8280-0', 'Waist Circumference at umbilicus by Tape measure', 110,'waist_circ','', 1,'cm'), ('loinc_org', '8287-5', 'Head Occipital-frontal circumference by Tape measure', 120,'head_circ','', 1,'cm'), -('loinc_org', '9303-9', 'Respiratory rate --resting', 130,'respiration','{"label": "Breaths per minute","mask": "99"}', 1,'BPM'), -('loinc_org', '72514-3', 'Pain severity - 0-10 verbal numeric rating [Score] - Reported', 140,'pain_severity','{"label": "Pain level","mask": "99"}', 1,''), -('loinc_org', '74774-1', 'Glucose [Mass/volume] in Serum, Plasma or Blood', 150,'glucose','{"label": "Blood sugar","mask": "999"}', 1,'mg/dL'); +('loinc_org', '9303-9', 'Respiratory rate --resting', 130,'respiration','{"label": "Breaths per minute","mask": "__"}', 1,'BPM'), +('loinc_org', '72514-3', 'Pain severity - 0-10 verbal numeric rating [Score] - Reported', 140,'pain_severity','{"label": "Pain level","mask": "__"}', 1,''), +('loinc_org', '74774-1', 'Glucose [Mass/volume] in Serum, Plasma or Blood', 150,'glucose','{"label": "Blood sugar","mask": "___"}', 1,'mg/dL'); ALTER TABLE `form_vitals` ADD `glucose` INT NULL AFTER `external_id`, diff --git a/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Encounter/Encounter.php b/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Encounter/Encounter.php index caaeccd..d641283 100644 --- a/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Encounter/Encounter.php +++ b/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Encounter/Encounter.php @@ -18,6 +18,7 @@ use FhirAPI\FhirRestApiBuilder\Parts\Strategy\Strategy; use FhirAPI\Model\QuestionnaireResponseTable; +use GenericTools\Model\DocumentsTable; use GenericTools\Model\FormEncounterTable; use Interop\Container\ContainerInterface; diff --git a/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Observation/FhirObservationMapping.php b/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Observation/FhirObservationMapping.php index f520cf4..ae6a9fd 100644 --- a/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Observation/FhirObservationMapping.php +++ b/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/Observation/FhirObservationMapping.php @@ -194,7 +194,7 @@ public function fhirToDb($FHIRObservation) $QuantityVal=$Quantity->getValue(); if(!is_null($QuantityVal)){ $lonicCode=$comp->getValueQuantity()->getCode()->getValue(); - $dbObservation[$LonicToDbMappig[$lonicCode]]=$QuantityVal; + $dbObservation[$LonicToDbMappig[$lonicCode]]=trim($QuantityVal); } } @@ -305,8 +305,8 @@ public function DBToFhir(...$params) $firstComp=true; foreach ($DbToLonicMappig as $dbField => $code){ - if(is_numeric($observationDataFromDb[$dbField])){ - $FHIRElm=$this->createFHIRQuantity(array("code"=>$code,"value"=>$observationDataFromDb[$dbField],"system"=>self::LONIC_SYSTEM)); + if(is_numeric(trim($observationDataFromDb[$dbField])) ){ // 0.00 is default empty for several columns in openemr db + $FHIRElm=$this->createFHIRQuantity(array("code"=>$code,"value"=> $observationDataFromDb[$dbField] !== '0.00' ? trim($observationDataFromDb[$dbField]) : null,"system"=>self::LONIC_SYSTEM)); }else{ $FHIRElm=$this->createFHIRCodeableConcept(array("code"=>$observationDataFromDb[$dbField],"system"=>self::LONIC_SYSTEM."/".$code)); } diff --git a/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/ValueSet/FhirValueSetMapping.php b/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/ValueSet/FhirValueSetMapping.php index 41e0968..2787b85 100644 --- a/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/ValueSet/FhirValueSetMapping.php +++ b/modules/FhirAPI/src/FhirAPI/FhirRestApiBuilder/Parts/Strategy/StrategyElement/ValueSet/FhirValueSetMapping.php @@ -33,6 +33,7 @@ public function DBToFhir(...$params){ $FHIRValueSet->getId()->setValue($data[0]['vs_id']); $FHIRValueSet->getTitle()->setValue($data[0]['vs_title']); $FHIRValueSet->getStatus()->setValue($data[0]['vs_status']); + $FHIRValueSet->getLanguage()->setValue($data[0]['vs_lang']); $operations = $params[1]; @@ -80,6 +81,9 @@ public function initFhirObject() $FHIRString= $this->createFHIRString(null); $FHIRValueSet->setTitle($FHIRString); + $FHIRCode = $this->createFHIRCode(null); + $FHIRValueSet->setLanguage($FHIRCode); + $FHIRPublicationStatus=$this->createFHIRPublicationStatus(null); $FHIRValueSet->setStatus($FHIRPublicationStatus); diff --git a/modules/FhirAPI/src/FhirAPI/Service/FhirBaseMapping.php b/modules/FhirAPI/src/FhirAPI/Service/FhirBaseMapping.php index f1de1a1..a94574f 100644 --- a/modules/FhirAPI/src/FhirAPI/Service/FhirBaseMapping.php +++ b/modules/FhirAPI/src/FhirAPI/Service/FhirBaseMapping.php @@ -1254,7 +1254,7 @@ public function createFHIRDecimal($value) { $FHIRDecimal = new FHIRDecimal; - if (empty($value)) { + if (!is_numeric($value)) { return $FHIRDecimal; } diff --git a/modules/FhirAPI/version.php b/modules/FhirAPI/version.php index e2d742c..e5e2435 100644 --- a/modules/FhirAPI/version.php +++ b/modules/FhirAPI/version.php @@ -1,5 +1,5 @@ array (0 => array ('value' => $value, 'operator' => '=',),),); return $valueSetsTable->getValueSetById($valueSet,$where); } + + protected function getConnectedUserId() + { + return $_SESSION['authUserID']; + } + + } diff --git a/modules/GenericTools/src/GenericTools/Library/UiComponents/DataTableExtended/DataTableExtended.php b/modules/GenericTools/src/GenericTools/Library/UiComponents/DataTableExtended/DataTableExtended.php index 89618c8..48453b9 100644 --- a/modules/GenericTools/src/GenericTools/Library/UiComponents/DataTableExtended/DataTableExtended.php +++ b/modules/GenericTools/src/GenericTools/Library/UiComponents/DataTableExtended/DataTableExtended.php @@ -1,6 +1,6 @@ '; + $table_html_string='
-

-

+

+

'; $table_html_string.=''; //$table_html_string.=''; @@ -56,19 +56,20 @@ public function echoBaseHTML() public function LoadEssentialCssJs() { - $uiComponentsPath='/vendor/clinikal/clinikal-backend/modules/GenericTools/src/GenericTools/Library/UiComponents'; + //$uiComponentsPath='/vendor/clinikal/clinikal-backend/modules/GenericTools/src/GenericTools/Library/UiComponents'; + $uiPath='/'.$GLOBALS['webroot'].$GLOBALS['baseModDir'].$GLOBALS['zendModDir'].'/public'; $header_html=''; - $header_html.=''; - $header_html.=''; + $header_html.=''; + $header_html.=''; - $header_html.=''; + $header_html.=''; - $header_html.=''; - $header_html.=''; - $header_html.=''; - $header_html.=''; + $header_html.=''; + $header_html.=''; + $header_html.=''; + $header_html.=''; - $header_html.=''; + $header_html.=''; //$header_html.=''; echo $header_html; diff --git a/modules/GenericTools/src/GenericTools/Library/UiComponents/Header/ClinikalHeaders.php b/modules/GenericTools/src/GenericTools/Library/UiComponents/Header/ClinikalHeaders.php index 1bd8508..96ef5ac 100644 --- a/modules/GenericTools/src/GenericTools/Library/UiComponents/Header/ClinikalHeaders.php +++ b/modules/GenericTools/src/GenericTools/Library/UiComponents/Header/ClinikalHeaders.php @@ -5,6 +5,7 @@ * Date: 04/09/18 * Time: 08:53 */ +namespace GenericTools\Library\UiComponents\Header; class ClinikalHeaders { @@ -16,18 +17,18 @@ public function singleTitle() public function titleWithButtons($title, array $buttons = array(),array $leftButtons = array()) { $html = $this->openTags(); - $html .= ''; + $html .= ''; if (!empty($buttons)) { $html .= '
  • '; foreach ($buttons as $button) { - $html .= ''; + $html .= ''; } $html .= '
  • '; } if (!empty($leftButtons)) { $html .= '
  • '; foreach ($leftButtons as $button) { - $html .= ''; + $html .= ''; } $html .= '
  • '; } @@ -42,8 +43,8 @@ public function tabsMenu() private function openTags() { - $html = '