diff --git a/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/CarecoordinationTable.php b/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/CarecoordinationTable.php index 7e364c261f4..58af6cea796 100644 --- a/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/CarecoordinationTable.php +++ b/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/CarecoordinationTable.php @@ -22,14 +22,18 @@ use Laminas\Config\Reader\ReaderInterface; use Laminas\Config\Reader\Xml; use Laminas\Db\TableGateway\AbstractTableGateway; +use OpenEMR\Common\Command\Trait\CommandLineDebugStylerTrait; use OpenEMR\Services\Cda\CdaTemplateImportDispose; use OpenEMR\Services\Cda\CdaTemplateParse; use OpenEMR\Services\Cda\CdaValidateDocuments; use OpenEMR\Services\Cda\XmlExtended; use OpenEMR\Services\CodeTypesService; +use Symfony\Component\Console\Style\SymfonyStyle; class CarecoordinationTable extends AbstractTableGateway { + use CommandLineDebugStylerTrait; + public const NPI_SAMPLE = "987654321"; public const ORGANIZATION_SAMPLE = "External Physicians Practice"; public const ORGANIZATION2_SAMPLE = "External Health and Hospitals"; @@ -51,6 +55,11 @@ public function __construct() $this->validationIsDisabled = $GLOBALS['ccda_validation_disable'] ?? false; } + public function getImportService(): CdaTemplateImportDispose + { + return $this->importService; + } + /* * Fetch the category ID using category name * diff --git a/src/Common/Command/CcdaImport.php b/src/Common/Command/CcdaImport.php index 8ed1bdff56b..2ebacc66261 100644 --- a/src/Common/Command/CcdaImport.php +++ b/src/Common/Command/CcdaImport.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; class CcdaImport extends Command { @@ -30,6 +31,7 @@ protected function configure(): void ->setDefinition( new InputDefinition([ new InputOption('document_id', null, InputOption::VALUE_REQUIRED, 'Document id that will be imported into the ccda table'), + new InputOption('debug', null, InputOption::VALUE_NONE, 'Turns on debug mode.'), new InputOption('site', null, InputOption::VALUE_REQUIRED, 'Name of site', 'default'), ]) ) @@ -44,6 +46,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $GLOBALS['modules_application']->getServiceManager()->build(CarecoordinationTable::class)->import($input->getOption('document_id')); + $symfonyStyler = new SymfonyStyle($input, $output); + + $careCoordinationTable = $GLOBALS['modules_application']->getServiceManager()->build(CarecoordinationTable::class); + if ($careCoordinationTable instanceof CarecoordinationTable) { + if ($input->getOption('debug') !== false) { + $careCoordinationTable->setCommandLineStyler($symfonyStyler); + $careCoordinationTable->getImportService()->setCommandLineStyler($symfonyStyler); + } + $careCoordinationTable->import($input->getOption('document_id')); + } return 0; } } diff --git a/src/Common/Command/CcdaNewpatient.php b/src/Common/Command/CcdaNewpatient.php index 75bf8c9885b..7acc99527b7 100644 --- a/src/Common/Command/CcdaNewpatient.php +++ b/src/Common/Command/CcdaNewpatient.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; class CcdaNewpatient extends Command { @@ -31,6 +32,7 @@ protected function configure(): void new InputDefinition([ new InputOption('am_id', null, InputOption::VALUE_REQUIRED, 'The master audit table id of patient that will be imported as a new patient'), new InputOption('document_id', null, InputOption::VALUE_REQUIRED, 'The ccda document id that was imported into the audit table'), + new InputOption('debug', null, InputOption::VALUE_NONE, 'Turns on debug mode.'), new InputOption('site', null, InputOption::VALUE_REQUIRED, 'Name of site', 'default'), ]) ) @@ -49,6 +51,16 @@ protected function execute(InputInterface $input, OutputInterface $output): int } $GLOBALS['modules_application']->getServiceManager()->build(CarecoordinationTable::class)->insert_patient($input->getOption('am_id'), $input->getOption('document_id')); + $symfonyStyler = new SymfonyStyle($input, $output); + + $careCoordinationTable = $GLOBALS['modules_application']->getServiceManager()->build(CarecoordinationTable::class); + if ($careCoordinationTable instanceof CarecoordinationTable) { + if ($input->getOption('debug') !== false) { + $careCoordinationTable->setCommandLineStyler($symfonyStyler); + $careCoordinationTable->getImportService()->setCommandLineStyler($symfonyStyler); + } + $careCoordinationTable->insert_patient($input->getOption('am_id'), $input->getOption('document_id')); + } return 0; } } diff --git a/src/Common/Command/CcdaNewpatientImport.php b/src/Common/Command/CcdaNewpatientImport.php index 2c270fd8691..5e741ed19e3 100644 --- a/src/Common/Command/CcdaNewpatientImport.php +++ b/src/Common/Command/CcdaNewpatientImport.php @@ -18,6 +18,7 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +use Symfony\Component\Console\Style\SymfonyStyle; class CcdaNewpatientImport extends Command { @@ -30,6 +31,7 @@ protected function configure(): void ->setDefinition( new InputDefinition([ new InputOption('document', null, InputOption::VALUE_REQUIRED, 'File (path) that will be imported to create the new patient'), + new InputOption('debug', null, InputOption::VALUE_NONE, 'Turns on debug mode.'), new InputOption('site', null, InputOption::VALUE_REQUIRED, 'Name of site', 'default'), ]) ) @@ -50,7 +52,18 @@ protected function execute(InputInterface $input, OutputInterface $output): int // get around a large ccda data array ini_set("memory_limit", -1); - $GLOBALS['modules_application']->getServiceManager()->build(CarecoordinationTable::class)->importNewPatient($input->getOption('document')); + + + $symfonyStyler = new SymfonyStyle($input, $output); + + $careCoordinationTable = $GLOBALS['modules_application']->getServiceManager()->build(CarecoordinationTable::class); + if ($careCoordinationTable instanceof CarecoordinationTable) { + if ($input->getOption('debug') !== false) { + $careCoordinationTable->setCommandLineStyler($symfonyStyler); + $careCoordinationTable->getImportService()->setCommandLineStyler($symfonyStyler); + } + $careCoordinationTable->importNewPatient($input->getOption('document')); + } return 0; } } diff --git a/src/Common/Command/Trait/CommandLineDebugStylerTrait.php b/src/Common/Command/Trait/CommandLineDebugStylerTrait.php new file mode 100644 index 00000000000..064bdeeea5c --- /dev/null +++ b/src/Common/Command/Trait/CommandLineDebugStylerTrait.php @@ -0,0 +1,44 @@ + + * @copyright Copyright (c) 2024 Discover and Change, Inc. + * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 + */ + +namespace OpenEMR\Common\Command\Trait; + +use Symfony\Component\Console\Style\SymfonyStyle; + +trait CommandLineDebugStylerTrait +{ + /** + * @var SymfonyStyle CLI styler used for debug mode to help in identifying issues during the import process. + */ + protected SymfonyStyle $styler; + + /** + * @var bool Whether to add additional logging / debug output to the system + */ + protected bool $cliDebug = false; + + public function setCommandLineStyler(SymfonyStyle $symfonyStyler) + { + $this->cliDebug = true; + $this->styler = $symfonyStyler; + } + + public function isCliDebug() + { + return $this->cliDebug; + } + + public function getCommandLineStyler(): SymfonyStyle + { + return $this->styler; + } +} diff --git a/src/Services/Cda/CdaTemplateImportDispose.php b/src/Services/Cda/CdaTemplateImportDispose.php index d263bd3357b..67d9b9cda21 100644 --- a/src/Services/Cda/CdaTemplateImportDispose.php +++ b/src/Services/Cda/CdaTemplateImportDispose.php @@ -17,6 +17,7 @@ use Application\Model\ApplicationTable; use Carecoordination\Model\CarecoordinationTable; use Document; +use OpenEMR\Common\Command\Trait\CommandLineDebugStylerTrait; use OpenEMR\Common\Database\QueryUtils; use OpenEMR\Common\Logging\SystemLogger; use OpenEMR\Services\CodeTypesService; @@ -28,6 +29,8 @@ class CdaTemplateImportDispose { + use CommandLineDebugStylerTrait; + protected $codeService; protected $userauthorized; @@ -822,31 +825,35 @@ public function InsertImmunization($imm_array, $pid, CarecoordinationTable $care $appTable->zQuery($qc_insert, array($value['cvx_code_text'], $value['cvx_code'], $ct_id)); } - $q1_unit = "SELECT * FROM list_options WHERE list_id='drug_units' AND title=?"; - $res_q1_unit = $appTable->zQuery($q1_unit, array($value['amount_administered_unit'])); - foreach ($res_q1_unit as $val) { - $oid_unit = $val['option_id']; - } - if ($res_q1_unit->count() == 0) { - $lres = $appTable->zQuery("SELECT IFNULL(MAX(CONVERT(SUBSTRING_INDEX(option_id,'-',-1),UNSIGNED INTEGER))+1,1) AS option_id FROM list_options WHERE list_id = ?", array('drug_units')); - foreach ($lres as $lrow) { - $oid_unit = $lrow['option_id']; + if (!empty(trim($value['amount_administered_unit'] ?? ''))) { + $q1_unit = "SELECT * FROM list_options WHERE list_id='drug_units' AND title=?"; + $res_q1_unit = $appTable->zQuery($q1_unit, array($value['amount_administered_unit'])); + foreach ($res_q1_unit as $val) { + $oid_unit = $val['option_id']; } - $q_insert_route = "INSERT INTO list_options - ( - list_id, - option_id, - title, - activity - ) - VALUES - ( - 'drug_units', - ?, - ?, - 1 - )"; - $appTable->zQuery($q_insert_route, array($oid_unit, $value['amount_administered_unit'])); + if ($res_q1_unit->count() == 0) { + $lres = $appTable->zQuery("SELECT IFNULL(MAX(CONVERT(SUBSTRING_INDEX(option_id,'-',-1),UNSIGNED INTEGER))+1,1) AS option_id FROM list_options WHERE list_id = ?", array('drug_units')); + foreach ($lres as $lrow) { + $oid_unit = $lrow['option_id']; + } + $q_insert_route = "INSERT INTO list_options + ( + list_id, + option_id, + title, + activity + ) + VALUES + ( + 'drug_units', + ?, + ?, + 1 + )"; + $appTable->zQuery($q_insert_route, array($oid_unit, $value['amount_administered_unit'])); + } + } else { + $oid_unit = null; // don't insert anything if the unit administered is an empty value } $value['completion_status'] = $value['reason_status'] ?: $value['completion_status']; @@ -910,6 +917,35 @@ public function InsertImmunization($imm_array, $pid, CarecoordinationTable $care WHERE external_id=? AND patient_id=?"; $res_q_sel_imm = $appTable->zQuery($q_sel_imm, array($value['extension'], $pid)); } + if ($this->isCliDebug()) { + $yesContinue = xl('Yes continue'); + do { + $printDebug = xl('Print debug information'); + $choice = $this->getCommandLineStyler()->choice( + "Proceed with creating immunization for cvx code " . $value['cvx_code'] . "?", + [$yesContinue, $printDebug], + $yesContinue + ); + if ($choice == $printDebug) { + $this->getCommandLineStyler()->info("Displaying CDA Value"); + var_dump($value); + $this->getCommandLineStyler()->info("Displaying immunization values"); + var_dump([ + 'pid' => $pid + , 'immunization_date_value' => $immunization_date_value + , 'cvx_code' => $value['cvx_code'] + , 'route' => $value['route_code_text'] + , 'administered_by_id' => $provider_id + , 'amount_administered' => $value['amount_administered'] + , 'amount_administered_unit' => $oid_unit + , 'manufacturer' => $value['manufacturer'] + , 'completion_status' => $value['completion_status'] + , 'external_id' => $value['extension'] + , 'refusal_reason' => $option['option_id'] ?? '' + ]); + } + } while ($choice !== $yesContinue); + } if (empty($value['extension']) || $res_q_sel_imm->count() == 0) { $query = "INSERT INTO immunizations ( patient_id, @@ -1029,69 +1065,80 @@ public function InsertPrescriptions($pres_array, $pid, CarecoordinationTable $ca $provider_id = $this->insertImportedUser($value, true); } - //unit - if ($revapprove == 1) { - $value['rate_unit'] = $carecoordinationTable->getListTitle($value['rate_unit'], 'drug_units', ''); - } - - $unit_option_id = $carecoordinationTable->getOptionId('drug_units', $value['rate_unit'], ''); - if ($unit_option_id == '' || $unit_option_id == null) { - $q_max_option_id = "SELECT MAX(CAST(option_id AS SIGNED))+1 AS option_id - FROM list_options - WHERE list_id=?"; - $res_max_option_id = $appTable->zQuery($q_max_option_id, array('drug_units')); - $res_max_option_id_cur = $res_max_option_id->current(); - $unit_option_id = $res_max_option_id_cur['option_id']; - $q_insert_units_option = "INSERT INTO list_options - ( - list_id, - option_id, - title, - activity - ) - VALUES - ( - 'drug_units', - ?, - ?, - 1 - )"; - $appTable->zQuery($q_insert_units_option, array($unit_option_id, $value['rate_unit'])); - } + //unit, only process if we have a value + if (!empty(trim($value['rate_unit'] ?? ''))) { + if ($revapprove == 1) { + $value['rate_unit'] = $carecoordinationTable->getListTitle($value['rate_unit'], 'drug_units', ''); + } - //route - $q1_route = "SELECT * - FROM list_options - WHERE list_id='drug_route' AND notes=?"; - $res_q1_route = $appTable->zQuery($q1_route, array($value['route'])); - foreach ($res_q1_route as $val) { - $oid_route = $val['option_id']; + $this->getCommandLineStyler()->info("rate unit is " . $value['rate_unit']); + $this->getCommandLineStyler()->confirm("Proceed?"); + + $unit_option_id = $carecoordinationTable->getOptionId('drug_units', $value['rate_unit'], ''); + if ($unit_option_id == '' || $unit_option_id == null) { + $q_max_option_id = "SELECT MAX(CAST(option_id AS SIGNED))+1 AS option_id + FROM list_options + WHERE list_id=?"; + $res_max_option_id = $appTable->zQuery($q_max_option_id, array('drug_units')); + $res_max_option_id_cur = $res_max_option_id->current(); + $unit_option_id = $res_max_option_id_cur['option_id']; + $q_insert_units_option = "INSERT INTO list_options + ( + list_id, + option_id, + title, + activity + ) + VALUES + ( + 'drug_units', + ?, + ?, + 1 + )"; + $appTable->zQuery($q_insert_units_option, array($unit_option_id, $value['rate_unit'])); + } + } else { + $unit_option_id = null; // leave it empty as we have no data to import here. } - if ($res_q1_route->count() == 0) { - $lres = $appTable->zQuery("SELECT IFNULL(MAX(CONVERT(SUBSTRING_INDEX(option_id,'-',-1),UNSIGNED INTEGER))+1,1) AS option_id FROM list_options WHERE list_id = ?", array('drug_route')); - foreach ($lres as $lrow) { - $oid_route = $lrow['option_id']; + //route, only process if we have a value + if (trim($value['route'] ?? '') != '') { + $q1_route = "SELECT * + FROM list_options + WHERE list_id='drug_route' AND notes=?"; + $res_q1_route = $appTable->zQuery($q1_route, array($value['route'])); + foreach ($res_q1_route as $val) { + $oid_route = $val['option_id']; } - $q_insert_route = "INSERT INTO list_options - ( - list_id, - option_id, - notes, - title, - activity - ) - VALUES - ( - 'drug_route', - ?, - ?, - ?, - 1 - )"; - $appTable->zQuery($q_insert_route, array($oid_route, $value['route'], - $value['route_display'])); + if ($res_q1_route->count() == 0) { + $lres = $appTable->zQuery("SELECT IFNULL(MAX(CONVERT(SUBSTRING_INDEX(option_id,'-',-1),UNSIGNED INTEGER))+1,1) AS option_id FROM list_options WHERE list_id = ?", array('drug_route')); + foreach ($lres as $lrow) { + $oid_route = $lrow['option_id']; + } + + $q_insert_route = "INSERT INTO list_options + ( + list_id, + option_id, + notes, + title, + activity + ) + VALUES + ( + 'drug_route', + ?, + ?, + ?, + 1 + )"; + $appTable->zQuery($q_insert_route, array($oid_route, $value['route'], + $value['route_display'])); + } + } else { + $oid_route = null; } //drug form @@ -1128,7 +1175,41 @@ public function InsertPrescriptions($pres_array, $pid, CarecoordinationTable $ca $res_q_sel_pres_r = $appTable->zQuery($q_sel_pres_r, array($pid, $value['drug_text'])); $res_q_sel_pres_r_cnt = $res_q_sel_pres_r->count(); } - + if ($this->isCliDebug()) { + $yesContinue = xl('Yes continue'); + do { + $printDebug = xl('Print debug information'); + $choice = $this->getCommandLineStyler()->choice( + "Proceed with creating prescription for drug " . $value['drug_text'] . "?", + [$yesContinue, $printDebug], + $yesContinue + ); + if ($choice == $printDebug) { + $this->getCommandLineStyler()->info("Displaying CDA Value"); + var_dump($value); + $this->getCommandLineStyler()->info("Displaying prescription values"); + var_dump([ + 'pid' => $pid + , 'date_added' => $value['begdate'] + , 'date_ended' => $value['enddate'] + , 'active' => $active + , 'drug' => $value['drug_text'] + , 'size' => $value['rate'] + , 'form' => $oidu_unit + , 'dosage' => $value['dose'] + , 'route' => $oid_route + , 'unit' => $unit_option_id + , 'indication' => $value['indication'] + , 'prn' => $value['prn'] + , 'rxnorm_drugcode' => $value['drug_code'] + , 'provider_id' => $provider_id + , 'external_id' => $value['extension'] + , 'medication' => 0 + , 'request_intent' => ($value['request_intent'] ?? null) + ]); + } + } while ($choice !== $yesContinue); + } if ((empty($value['extension']) && $res_q_sel_pres_r_cnt === 0) || ($res_q_sel_pres_cnt === 0)) { $query = "INSERT INTO prescriptions ( patient_id,