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";
35 * Default constructor.
37 public function __construct()
39 parent
::__construct(self
::DRUG_TABLE
);
40 UuidRegistry
::createMissingUuidsForTables([self
::DRUG_TABLE
]);
43 public function getUuidFields(): array
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
59 public function getAll($search = array(), $isAndCondition = true, $puuidBind = null)
62 foreach ($search as $key => $value) {
63 if (!$value instanceof ISearchField
) {
64 $newSearch[] = new StringSearchField($key, [$value], SearchModifier
::EXACT
);
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);
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
83 public function getOne($uuid)
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.
90 $search['puuid'] = new ReferenceSearchField('puuid', [new ReferenceSearchValue($puuid, 'Patient', true)]);
92 return $this->search($search);
95 public function search($search, $isAndCondition = true)
101 drug_table.ndc_number,
106 drug_table.related_code,
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
131 last_updated AS drug_last_updated,
132 date_created AS drug_date_created
136 LEFT JOIN drug_inventory
137 ON drug_table.drug_id = drug_inventory.drug_id
140 uuid AS prescription_uuid
143 ,patient_id as prescription_patient_id
147 ON drug_prescriptions.drug_id = drug_table.drug_id
153 ON patient.pid = drug_prescriptions.prescription_patient_id";
155 $processingResult = new ProcessingResult();
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']);
190 $codes = $this->addCoding($row['rxnorm_drugcode']);
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']);