updated US CDC website link to current immunization VIS page (#7855)
[openemr.git] / src / Services / DrugService.php
blob34ff7567fda439ba23fdf28f517206fdeb64255a
1 <?php
3 /**
4 * DrugService
6 * @package OpenEMR
7 * @link http://www.open-emr.org
8 * @author Yash Bothra <yashrajbothra786gmail.com>
9 * @copyright Copyright (c) 2020 Yash Bothra <yashrajbothra786gmail.com>
10 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
13 namespace OpenEMR\Services;
15 use OpenEMR\Common\Database\QueryUtils;
16 use OpenEMR\Common\Database\SqlQueryException;
17 use OpenEMR\Common\Logging\SystemLogger;
18 use OpenEMR\Common\Uuid\UuidRegistry;
19 use OpenEMR\Services\Search\FhirSearchWhereClauseBuilder;
20 use OpenEMR\Services\Search\ISearchField;
21 use OpenEMR\Services\Search\ReferenceSearchField;
22 use OpenEMR\Services\Search\ReferenceSearchValue;
23 use OpenEMR\Services\Search\SearchFieldException;
24 use OpenEMR\Services\Search\SearchModifier;
25 use OpenEMR\Services\Search\StringSearchField;
26 use OpenEMR\Services\Search\TokenSearchField;
27 use OpenEMR\Services\Search\TokenSearchValue;
28 use OpenEMR\Validators\ProcessingResult;
30 class DrugService extends BaseService
32 private const DRUG_TABLE = "drugs";
34 /**
35 * Default constructor.
37 public function __construct()
39 parent::__construct(self::DRUG_TABLE);
40 UuidRegistry::createMissingUuidsForTables([self::DRUG_TABLE]);
43 public function getUuidFields(): array
45 return ['uuid'];
48 /**
49 * Returns a list of drugs matching optional search criteria.
50 * Search criteria is conveyed by array where key = field/column name, value = field value.
51 * If no search criteria is provided, all records are returned.
53 * @param $search search array parameters
54 * @param $isAndCondition specifies if AND condition is used for multiple criteria. Defaults to true.
55 * @param $puuidBind - Patient uuid to return drug resources that are only visible to the current patient
56 * @return ProcessingResult which contains validation messages, internal error messages, and the data
57 * payload.
59 public function getAll($search = array(), $isAndCondition = true, $puuidBind = null)
61 $newSearch = [];
62 foreach ($search as $key => $value) {
63 if (!$value instanceof ISearchField) {
64 $newSearch[] = new StringSearchField($key, [$value], SearchModifier::EXACT);
65 } else {
66 $newSearch[$key] = $value;
69 // so if we have a puuid we need to make sure we only return drugs that are connected to the current patient.
70 if (isset($puuidBind)) {
71 $newSearch['puuid'] = new TokenSearchField('puuid', $puuidBind, true);
74 return $this->search($newSearch, $isAndCondition);
77 /**
78 * Returns a single drug record by id.
79 * @param $uuid - The drug uuid identifier in string format.
80 * @return ProcessingResult which contains validation messages, internal error messages, and the data
81 * payload.
83 public function getOne($uuid)
85 $search = [
86 'uuid' => new TokenSearchField('uuid', [new TokenSearchValue($uuid, null, false)])
88 // so if we have a puuid we need to make sure we only return drugs that are connected to the current patient.
89 if (isset($puuid)) {
90 $search['puuid'] = new ReferenceSearchField('puuid', [new ReferenceSearchValue($puuid, 'Patient', true)]);
92 return $this->search($search);
95 public function search($search, $isAndCondition = true)
97 $sql = "SELECT
98 drug_table.drug_id,
99 drug_table.uuid,
100 drug_table.name,
101 drug_table.ndc_number,
102 drug_table.form,
103 drug_table.size,
104 drug_table.unit,
105 drug_table.route,
106 drug_table.related_code,
107 drug_table.active,
108 drug_table.drug_code,
109 IF(drug_prescriptions.rxnorm_drugcode!=''
110 ,drug_prescriptions.rxnorm_drugcode
111 ,IF(drug_table.drug_code IS NULL, '', drug_table.drug_code)
112 ) AS 'rxnorm_drugcode',
113 drug_inventory.manufacturer,
114 drug_inventory.lot_number,
115 drug_inventory.expiration,
116 drug_table.drug_last_updated,
117 drug_table.drug_date_created
118 FROM (
119 select
120 drug_id,
121 uuid,
122 name,
123 ndc_number,
124 form,
125 size,
126 unit,
127 route,
128 related_code,
129 active,
130 drug_code,
131 last_updated AS drug_last_updated,
132 date_created AS drug_date_created
133 FROM
134 drugs
135 ) drug_table
136 LEFT JOIN drug_inventory
137 ON drug_table.drug_id = drug_inventory.drug_id
138 LEFT JOIN (
139 select
140 uuid AS prescription_uuid
141 ,rxnorm_drugcode
142 ,drug_id
143 ,patient_id as prescription_patient_id
144 FROM
145 prescriptions
146 ) drug_prescriptions
147 ON drug_prescriptions.drug_id = drug_table.drug_id
148 LEFT JOIN (
149 select uuid AS puuid
150 ,pid
151 FROM patient_data
152 ) patient
153 ON patient.pid = drug_prescriptions.prescription_patient_id";
155 $processingResult = new ProcessingResult();
156 try {
157 $whereClause = FhirSearchWhereClauseBuilder::build($search, $isAndCondition);
159 $sql .= $whereClause->getFragment();
160 $sqlBindArray = $whereClause->getBoundValues();
161 $statementResults = QueryUtils::sqlStatementThrowException($sql, $sqlBindArray);
163 while ($row = sqlFetchArray($statementResults)) {
164 $resultRecord = $this->createResultRecordFromDatabaseResult($row);
165 $processingResult->addData($resultRecord);
167 } catch (SqlQueryException $exception) {
168 // we shouldn't hit a query exception
169 (new SystemLogger())->error($exception->getMessage(), ['trace' => $exception->getTraceAsString()]);
170 $processingResult->addInternalError("Error selecting data from database");
171 } catch (SearchFieldException $exception) {
172 (new SystemLogger())->error($exception->getMessage(), ['trace' => $exception->getTraceAsString(), 'field' => $exception->getField()]);
173 $processingResult->setValidationMessages([$exception->getField() => $exception->getMessage()]);
176 return $processingResult;
179 protected function createResultRecordFromDatabaseResult($row)
181 $record = parent::createResultRecordFromDatabaseResult($row);
183 if ($record['rxnorm_drugcode'] != "") {
184 // removed the RXCUI concatenation out of the db query and into the code here
185 // some parts of OpenEMR adds the RXCUI designation in the drug_code such as the inventory/dispensary module
186 // and this causes the FHIR medication resource to not get the actual RXCUI code.
187 if ($row['drug_code'] == $record['rxnorm_drugcode'] && strpos($row['drug_code'], ':') === false) {
188 $codes = $this->addCoding("RXCUI:" . $row['drug_code']);
189 } else {
190 $codes = $this->addCoding($row['rxnorm_drugcode']);
192 $updatedCodes = [];
193 foreach ($codes as $code => $codeValues) {
194 if (empty($codeValues['description'])) {
195 // use the drug name if for some reason we have no rxnorm description from the lookup
196 $codeValues['description'] = $row['drug'];
198 $updatedCodes[$code] = $codeValues;
200 $record['drug_code'] = $updatedCodes;
203 // TODO: @adunsulag this looks odd... why modify the original row...? look at removing this.
204 if ($row['rxnorm_drugcode'] != "") {
205 $row['drug_code'] = $this->addCoding($row['drug_code']);
207 return $record;