Allow reapeater services to be turned off (#6602)
[openemr.git] / src / Services / FacilityService.php
blobf8a646ecd64258c4224581e58ab02f1a090cb36f
1 <?php
3 /**
4 * FacilityService
6 * @package OpenEMR
7 * @link http://www.open-emr.org
8 * @author Matthew Vita <matthewvita48@gmail.com>
9 * @author Brady Miller <brady.g.miller@gmail.com>
10 * @author Sherwin Gaddis <sherwingaddis@gmail.com>
11 * @author Jerry Padgett <sjpadgett@gmail.com>
12 * @copyright Copyright (c) 2018 Matthew Vita <matthewvita48@gmail.com>
13 * @copyright Copyright (c) 2018 Brady Miller <brady.g.miller@gmail.com>
14 * @copyright Copyright (c) 2020 Jerry Padgett <sjpadgett@gmail.com>
15 * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3
18 namespace OpenEMR\Services;
20 use OpenEMR\Common\Database\SqlQueryException;
21 use OpenEMR\Common\Logging\SystemLogger;
22 use OpenEMR\Common\Uuid\UuidRegistry;
23 use OpenEMR\Services\Search\SearchModifier;
24 use OpenEMR\Services\Search\StringSearchField;
25 use OpenEMR\Services\Search\TokenSearchField;
26 use OpenEMR\Validators\FacilityValidator;
27 use OpenEMR\Validators\ProcessingResult;
28 use OpenEMR\Events\Facility\FacilityCreatedEvent;
29 use OpenEMR\Events\Facility\FacilityUpdatedEvent;
30 use Particle\Validator\Validator;
32 class FacilityService extends BaseService
34 private $facilityValidator;
35 private const FACILITY_TABLE = "facility";
37 /**
38 * Default constructor.
40 public function __construct()
42 parent::__construct(self::FACILITY_TABLE);
43 UuidRegistry::createMissingUuidsForTables([self::FACILITY_TABLE]);
44 $this->facilityValidator = new FacilityValidator();
47 public function getUuidFields(): array
49 return ['uuid'];
52 public function validate($facility)
54 $validator = new Validator();
56 $validator->required('name')->lengthBetween(2, 255);
57 $validator->required('phone')->lengthBetween(3, 30);
58 $validator->required('city')->lengthBetween(2, 255);
59 $validator->required('state')->lengthBetween(2, 50);
60 $validator->required('street')->lengthBetween(2, 255);
61 $validator->required('postal_code')->lengthBetween(2, 11);
62 $validator->required('email')->email();
63 $validator->required('fax')->lengthBetween(3, 30);
64 $validator->optional('country_code')->lengthBetween(2, 30);
65 $validator->optional('federal_ein')->lengthBetween(2, 15);
66 $validator->optional('website')->url();
67 $validator->optional('color')->lengthBetween(4, 7);
68 $validator->optional('service_location')->numeric();
69 $validator->optional('billing_location')->numeric();
70 $validator->optional('accepts_assignment')->numeric();
71 $validator->optional('pos_code')->numeric();
72 $validator->optional('domain_identifier')->lengthBetween(2, 60);
73 $validator->optional('attn')->lengthBetween(2, 65);
74 $validator->optional('tax_id_type')->lengthBetween(2, 31);
75 $validator->optional('primary_business_entity')->numeric();
76 $validator->optional('facility_npi')->lengthBetween(2, 15);
77 $validator->optional('facility_code')->lengthBetween(2, 31);
78 $validator->optional('facility_taxonomy')->lengthBetween(2, 15);
79 $validator->optional('iban')->lengthBetween(2, 34);
81 return $validator->validate($facility);
84 public function getAllFacility()
86 return $this->get(array("order" => "ORDER BY FAC.name ASC"));
89 public function getPrimaryBusinessEntity($options = null)
91 if (!empty($options) && !empty($options["useLegacyImplementation"])) {
92 return $this->getPrimaryBusinessEntityLegacy();
94 $searchArgs = ['primary_business_entity' => new StringSearchField('primary_business_entity', [1], SearchModifier::EXACT)];
96 if (!empty($options) && !empty($options["excludedId"])) {
97 $searchArgs['id'] = new TokenSearchField('id', $options['excludedId']);
98 $searchArgs['id']->setModifier(SearchModifier::NOT_EQUALS_EXACT);
101 $results = $this->search($searchArgs);
102 if (!empty($results->getData())) {
103 $pbe_results = $results->getData();
104 return array_pop($pbe_results);
106 return null;
109 public function getAllServiceLocations($options = null)
111 $args = array(
112 "where" => null,
113 "order" => "ORDER BY FAC.name ASC"
116 if (!empty($options) && !empty($options["orderField"])) {
117 $args["order"] = "ORDER BY FAC." . escape_sql_column_name($options["orderField"], array("facility")) . " ASC";
120 $args["where"] = "WHERE FAC.service_location = 1";
122 return $this->get($args);
125 public function getPrimaryBillingLocation()
127 $record = $this->get(array(
128 "order" => "ORDER BY FAC.billing_location DESC, FAC.id DESC",
129 "limit" => 1
131 return $record;
134 public function getAllBillingLocations()
136 return $this->get(array(
137 "where" => "WHERE FAC.billing_location = 1",
138 "order" => "ORDER BY FAC.id ASC"
142 public function getById($id)
144 if (empty($id)) {
145 // Not okay to throw exception here. Most UI are pulldowns which init to empty.
146 return false;
148 // $id has to be a string for TokenSearchField()
149 $result = $this->search(['id' => new TokenSearchField('id', (string) $id, false)]);
150 if (!empty($result->getData())) {
151 $facility_result = $result->getData();
152 $facility = array_pop($facility_result);
153 return $facility;
155 return null;
158 public function getFacilityForUser($userId)
160 $record = $this->get(array(
161 "where" => "WHERE USER.id = ?",
162 "data" => array($userId),
163 "join" => "JOIN users USER ON FAC.id = USER.facility_id",
164 "limit" => 1
166 return $record;
169 public function getFacilityForUserFormatted($userId)
171 $facility = $this->getFacilityForUser($userId);
173 if (!empty($facility)) {
174 $formatted = "";
175 $formatted .= $facility["name"];
176 $formatted .= "\n";
177 $formatted .= $facility["street"];
178 $formatted .= "\n";
179 $formatted .= $facility["city"];
180 $formatted .= "\n";
181 $formatted .= $facility["state"];
182 $formatted .= "\n";
183 $formatted .= $facility["postal_code"];
185 return array("facility_address" => $formatted);
188 return array("facility_address" => "");
191 public function getFacilityForEncounter($encounterId)
193 $record = $this->get(array(
194 "where" => "WHERE ENC.encounter = ?",
195 "data" => array($encounterId),
196 "join" => "JOIN form_encounter ENC ON FAC.id = ENC.facility_id",
197 "limit" => 1
199 return $record;
202 public function updateFacility($data)
204 $dataBeforeUpdate = $this->getById($data['id']);
205 $query = $this->buildUpdateColumns($data);
206 $sql = " UPDATE facility SET ";
207 $sql .= $query['set'];
208 $sql .= " WHERE id = ?";
209 array_push($query['bind'], $data['id']);
210 $result = sqlStatement(
211 $sql,
212 $query['bind']
215 $facilityUpdatedEvent = new FacilityUpdatedEvent($dataBeforeUpdate, $data);
216 $GLOBALS["kernel"]->getEventDispatcher()->dispatch($facilityUpdatedEvent, FacilityUpdatedEvent::EVENT_HANDLE, 10);
218 return $result;
221 public function insertFacility($data)
223 $query = $this->buildInsertColumns($data);
224 $sql = " INSERT INTO facility SET ";
225 $sql .= $query['set'];
226 $facilityId = sqlInsert(
227 $sql,
228 $query['bind']
231 $facilityCreatedEvent = new FacilityCreatedEvent(array_merge($data, ['id' => $facilityId]));
232 $GLOBALS["kernel"]->getEventDispatcher()->dispatch($facilityCreatedEvent, FacilityCreatedEvent::EVENT_HANDLE, 10);
234 return $facilityId;
237 public function updateUsersFacility($facility_name, $facility_id)
239 $sql = " UPDATE users SET";
240 $sql .= " facility=?";
241 $sql .= " WHERE facility_id=?";
243 return sqlStatement($sql, array($facility_name, $facility_id));
247 * Shared getter for the various specific facility getters.
248 * NOTE: if a limit of 1 is specified the associative array is returned
250 * @param $map - Query information.
251 * @return array of associative arrays | one associative array.
253 private function get($map)
255 try {
256 $sql = " SELECT FAC.id,";
257 $sql .= " FAC.uuid,";
258 $sql .= " FAC.name,";
259 $sql .= " FAC.phone,";
260 $sql .= " FAC.fax,";
261 $sql .= " FAC.street,";
262 $sql .= " FAC.city,";
263 $sql .= " FAC.state,";
264 $sql .= " FAC.postal_code,";
265 $sql .= " FAC.country_code,";
266 $sql .= " FAC.federal_ein,";
267 $sql .= " FAC.website,";
268 $sql .= " FAC.email,";
269 $sql .= " FAC.service_location,";
270 $sql .= " FAC.billing_location,";
271 $sql .= " FAC.accepts_assignment,";
272 $sql .= " FAC.pos_code,";
273 $sql .= " FAC.x12_sender_id,";
274 $sql .= " FAC.attn,";
275 $sql .= " FAC.domain_identifier,";
276 $sql .= " FAC.facility_npi,";
277 $sql .= " FAC.facility_taxonomy,";
278 $sql .= " FAC.tax_id_type,";
279 $sql .= " FAC.color,";
280 $sql .= " FAC.primary_business_entity,";
281 $sql .= " FAC.facility_code,";
282 $sql .= " FAC.extra_validation,";
283 $sql .= " FAC.mail_street,";
284 $sql .= " FAC.mail_street2,";
285 $sql .= " FAC.mail_city,";
286 $sql .= " FAC.mail_state,";
287 $sql .= " FAC.mail_zip,";
288 $sql .= " FAC.oid,";
289 $sql .= " FAC.iban,";
290 $sql .= " FAC.info,";
291 $sql .= " FAC.inactive";
292 $sql .= " FROM facility FAC";
294 $records = self::selectHelper($sql, $map);
295 $returnRecords = [];
296 if (!empty($records)) {
297 // base service method returns just the associative array which messes with our methods for LIMIT etc.
298 if (!empty($map['limit']) && $map['limit'] == 1) {
299 $returnRecords = $this->createResultRecordFromDatabaseResult($records);
300 } else {
301 foreach ($records as $record) {
302 $returnRecords[] = $this->createResultRecordFromDatabaseResult($record);
306 return $returnRecords;
307 } catch (SqlQueryException $exception) {
308 (new SystemLogger())->error($exception->getMessage(), ['trace' => $exception->getTraceAsString()]);
309 throw $exception;
313 private function getPrimaryBusinessEntityLegacy()
315 $record = $this->get(array(
316 "order" => "ORDER BY FAC.billing_location DESC, FAC.accepts_assignment DESC, FAC.id ASC",
317 "limit" => 1
319 return $record;
322 public function getAllWithIds(array $ids)
324 $idField = new TokenSearchField('id', $ids);
325 return $this->search(['id' => $idField]);
329 * Returns a list of facilities matching optional search criteria.
330 * Search criteria is conveyed by array where key = field/column name, value = field value.
331 * If no search criteria is provided, all records are returned.
333 * @param $search search array parameters
334 * @param $isAndCondition specifies if AND condition is used for multiple criteria. Defaults to true.
335 * @return ProcessingResult which contains validation messages, internal error messages, and the data
336 * payload.
338 public function getAll($search = array(), $isAndCondition = true)
340 $querySearch = [];
341 if (!empty($search)) {
342 if (isset($search['uuid'])) {
343 $querySearch['uuid'] = new TokenSearchField('uuid', $search['uuid']);
344 unset($search['uuid']);
346 foreach ($search as $field => $value) {
347 if (isset($search[$field])) {
348 $querySearch[$field] = new StringSearchField($field, $search[$field], SearchModifier::EXACT, $isAndCondition);
352 return $this->search($querySearch, $isAndCondition);
356 * Returns a single facility record by facility uuid.
357 * @param $uuid - The facility uuid identifier in string format.
358 * @return ProcessingResult which contains validation messages, internal error messages, and the data
359 * payload.
361 public function getOne($uuid): ProcessingResult
363 $processingResult = new ProcessingResult();
364 $isValid = $this->facilityValidator->validateId('uuid', self::FACILITY_TABLE, $uuid, true);
365 if ($isValid !== true) {
366 return $isValid;
368 return $this->search(['uuid' => new TokenSearchField('uuid', $uuid, true)]);
372 * Inserts a new facility record.
374 * @param $data The facility fields (array) to insert.
375 * @return ProcessingResult which contains validation messages, internal error messages, and the data
376 * payload.
378 public function insert($data)
380 $processingResult = $this->facilityValidator->validate(
381 $data,
382 FacilityValidator::DATABASE_INSERT_CONTEXT
385 if (!$processingResult->isValid()) {
386 return $processingResult;
389 $data['uuid'] = (new UuidRegistry(['table_name' => self::FACILITY_TABLE]))->createUuid();
391 $query = $this->buildInsertColumns($data);
392 $sql = " INSERT INTO " . self::FACILITY_TABLE . " SET ";
393 $sql .= $query['set'];
395 $results = sqlInsert(
396 $sql,
397 $query['bind']
400 if ($results) {
401 $processingResult->addData(array(
402 'id' => $results,
403 'uuid' => UuidRegistry::uuidToString($data['uuid'])
405 } else {
406 $processingResult->addInternalError("error processing SQL Insert");
409 return $processingResult;
413 * Updates an existing facility record.
415 * @param $uuid - The facility uuid identifier in string format used for update.
416 * @param $data - The updated facility data fields
417 * @return ProcessingResult which contains validation messages, internal error messages, and the data
418 * payload.
420 public function update($uuid, $data)
422 if (empty($data)) {
423 $processingResult = new ProcessingResult();
424 $processingResult->setValidationMessages("Invalid Data");
425 return $processingResult;
427 $data["uuid"] = $uuid;
428 $processingResult = $this->facilityValidator->validate(
429 $data,
430 FacilityValidator::DATABASE_UPDATE_CONTEXT
432 if (!$processingResult->isValid()) {
433 return $processingResult;
436 $query = $this->buildUpdateColumns($data);
437 $sql = " UPDATE " . self::FACILITY_TABLE . " SET ";
438 $sql .= $query['set'];
439 $sql .= " WHERE `uuid` = ?";
441 $uuidBinary = UuidRegistry::uuidToBytes($uuid);
442 array_push($query['bind'], $uuidBinary);
443 $sqlResult = sqlStatement($sql, $query['bind']);
445 if (!$sqlResult) {
446 $processingResult->addErrorMessage("error processing SQL Update");
447 } else {
448 $processingResult = $this->getOne($uuid);
450 return $processingResult;