From 0b2c95691ee36cbf83f493aa082351d548ba5b3a Mon Sep 17 00:00:00 2001 From: Stephen Nielson Date: Tue, 16 Jan 2024 14:25:11 -0500 Subject: [PATCH] Fixed #7164, Insurance Add/Edit/Search Made the add edit search work properly when updating an insurance policy, creating a new one, etc. It all funnels through the same javascript mechanism now and populates the provider correctly. Fixed a bug where the display name wasn't populating properly when you enter in an insurance company as it didn't respect the globals settings for insurance company display. Also fixed issue #7164 by fixing up the phone number update statements. Also includes style fixes. --- interface/practice/ins_search.php | 55 ++++++++++++------ .../insurance/EditPolicyScreenController.js | 37 +++++++++++- .../oeUI/insurance/InsurancePolicyService.js | 3 + library/patient.inc.php | 34 +---------- .../InsuranceRestController.php | 5 +- src/Services/InsuranceCompanyService.php | 57 +++++++++++++++++-- src/Services/InsuranceService.php | 3 +- src/Services/PhoneNumberService.php | 9 ++- src/Validators/CoverageValidator.php | 4 +- .../_insurance_edit_screen_edit.html.twig | 2 +- 10 files changed, 143 insertions(+), 66 deletions(-) diff --git a/interface/practice/ins_search.php b/interface/practice/ins_search.php index 53e47c647e1..5eb18304452 100644 --- a/interface/practice/ins_search.php +++ b/interface/practice/ins_search.php @@ -92,11 +92,29 @@ function dosearch() { // The ins_list.php window calls this to set the selected insurance. function set_insurance(ins_id, ins_name) { - if (opener.closed || ! opener.set_insurance) - alert('The target form was closed; I cannot apply your selection.'); - else - opener.set_insurance(ins_id, ins_name); - dlgclose('InsSaveClose',false); + window.top.restoreSession(); // make sure to restore the session before we do anything else + if (!window.opener) { + return; // nothing to do here as somehow we got here without the opener + } + let postMessage = { + action: 'insurance-search-set-insurance' + ,insuranceId: ins_id + ,insuranceName: ins_name + }; + // fire off a message so we can decouple things so we don't have to have a specific function + // name in the global scope of the opener + opener.postMessage(postMessage, window.location.origin); + if (opener.closed) { + alert('The target form was closed; I cannot apply your selection.'); + } + else if (opener.set_insurance) { + opener.set_insurance(ins_id, ins_name); + dlgclose('InsSaveClose', false); + } else { + // if we don't have a set_insurance function then we will just close the window as the opener is + // using post message to receive events. + dlgclose('InsSaveClose', false); + } } // This is set to true on a mousedown of the Save button. The @@ -140,7 +158,7 @@ function clearForm() { f.form_city.value = ''; f.form_state.value = ''; f.form_country.value = ''; - f.form_zip.value = ''; + f.form_zip.value = ''; f.form_phone.value = ''; f.form_cms_id.value = ''; f.form_ins_type_code.value = ''; @@ -225,9 +243,10 @@ function clearForm() { echo " alert(" . js_escape($info_msg) . ");\n"; } - echo " top.restoreSession();\n"; - echo " if (opener.set_insurance) opener.set_insurance(" . js_escape($ins_id) . "," . js_escape($ins_name) . ");\n"; - echo " dlgclose();\n"; + // we need to follow the global settings for the display of this name so we will return the name in the set_insurance method + $ins_name = (new InsuranceCompanyService())->getInsuranceDisplayName($ins_id); + // call the set_insurance method in our header + echo " set_insurance(" . js_escape($ins_id) . "," . js_escape($ins_name) . ");\n"; echo "\n"; exit(); } else { @@ -267,7 +286,7 @@ function clearForm() { : - ' + ' value='' /> @@ -275,7 +294,7 @@ function clearForm() { : - ' /> @@ -283,7 +302,7 @@ function clearForm() { : - ' /> @@ -292,11 +311,11 @@ function clearForm() { :
- ' />
- ' />
@@ -306,11 +325,11 @@ function clearForm() {
- ' />
- ' />
@@ -324,14 +343,14 @@ function clearForm() { ($ins_co_phone['area_code'] ?? '') . ($ins_co_phone['prefix'] ?? '') . ($ins_co_phone['number'] ?? '') - )); ?>' + )); ?>' /> : - ' /> diff --git a/library/js/oeUI/insurance/EditPolicyScreenController.js b/library/js/oeUI/insurance/EditPolicyScreenController.js index 636c665a4be..8f96671f3eb 100644 --- a/library/js/oeUI/insurance/EditPolicyScreenController.js +++ b/library/js/oeUI/insurance/EditPolicyScreenController.js @@ -109,8 +109,8 @@ export class EditPolicyScreenController } if (evt.data && evt.data.hasOwnProperty('action')) { if (evt.data.action == 'insurance-patient-browser-selected') { - let patientUuid = evt.data.patientUuid; - let insuranceUuid = evt.data.insuranceUuid; + let patientUuid = evt.data.patientUuid || null; + let insuranceUuid = evt.data.insuranceUuid || null; if (!patientUuid || !insuranceUuid) { alert(window.top.xl("No patient was selected to copy values from.")); return; @@ -129,6 +129,19 @@ export class EditPolicyScreenController }); this.render(); } + else if (evt.data.action == 'insurance-search-set-insurance') { + let insuranceCompanyId = evt.data.insuranceId || null; + let insuranceCompanyName = evt.data.insuranceName || null; + if (!insuranceCompanyId || !insuranceCompanyName) { + alert(window.top.xl("An error occurred while loading the insurance company information.")); + console.error("Failed to find insurance company id or name in event data, this should not happen and is a bug.Event: ", evt); + return; + } + this.selectedInsurance.provider = insuranceCompanyId; + this.__insurancePolicyService.addInsuranceProviderToList(insuranceCompanyId, insuranceCompanyName); + this.__insuranceProviderList = this.__insurancePolicyService.getInsuranceProvidersList(); + this.render(); + } } }); } @@ -233,6 +246,26 @@ export class EditPolicyScreenController let id = (new Date()).getTime(); window.open(dlgUrl, id, 'toolbar=0,scrollbars=1,location=0,statusbar=0,menubar=0,resizable=1,width=400,height=450,left = 440,top = 362'); }); + + insuranceInfoContainer.querySelector('.insurance-search-open-finder').addEventListener('click', (evt) => { + let input = evt.target; + let url = new URL(input.href); + if (this.selectedInsurance && this.selectedInsurance.provider) { + url.searchParams.set('ins', this.selectedInsurance.provider); + } + let relativeUrl = url.pathname + url.search; + evt.preventDefault(); + dlgopen('', '', 700, 600, '', input.dataset['modalTitle'], { + buttons: [ + {text: window.top.xl('Close'), close: true, style: 'default btn-sm'} + ], + allowResize: true, + allowDrag: true, + dialogId: '', + type: 'iframe', + url: relativeUrl + }); + }); } #setupAddressValidation(selectedInsurance) { diff --git a/library/js/oeUI/insurance/InsurancePolicyService.js b/library/js/oeUI/insurance/InsurancePolicyService.js index 40ec4a5ced3..0c247a22862 100644 --- a/library/js/oeUI/insurance/InsurancePolicyService.js +++ b/library/js/oeUI/insurance/InsurancePolicyService.js @@ -42,6 +42,9 @@ export class InsurancePolicyService }) .then(result => result.json()); } + addInsuranceProviderToList(insuranceCompanyId, insuranceCompanyName) { + this.__insuranceProviderList[insuranceCompanyId] = insuranceCompanyName; + } getInsuranceProvidersList() { return this.__insuranceProviderList; diff --git a/library/patient.inc.php b/library/patient.inc.php index 4085e15ca4b..1baa284cf24 100644 --- a/library/patient.inc.php +++ b/library/patient.inc.php @@ -21,6 +21,7 @@ use OpenEMR\Services\PatientService; use OpenEMR\Services\SocialHistoryService; use OpenEMR\Billing\InsurancePolicyTypes; +use OpenEMR\Services\InsuranceCompanyService; require_once(dirname(__FILE__) . "/dupscore.inc.php"); @@ -116,37 +117,8 @@ function getInsuranceProvidersExtra() $rez = sqlStatement($sql); for ($iter = 0; $row = sqlFetchArray($rez); $iter++) { - switch ($GLOBALS['insurance_information']) { - case $GLOBALS['insurance_information'] = '0': - $returnval[$row['id']] = $row['name']; - break; - case $GLOBALS['insurance_information'] = '1': - $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ")"; - break; - case $GLOBALS['insurance_information'] = '2': - $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['zip'] . ")"; - break; - case $GLOBALS['insurance_information'] = '3': - $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['state'] . ")"; - break; - case $GLOBALS['insurance_information'] = '4': - $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['state'] . - ", " . $row['zip'] . ")"; - break; - case $GLOBALS['insurance_information'] = '5': - $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['city'] . - ", " . $row['state'] . ", " . $row['zip'] . ")"; - break; - case $GLOBALS['insurance_information'] = '6': - $returnval[$row['id']] = $row['name'] . " (" . $row['line1'] . ", " . $row['line2'] . ", " . $row['city'] . - ", " . $row['state'] . ", " . $row['zip'] . ", " . $row['cms_id'] . ")"; - break; - case $GLOBALS['insurance_information'] = '7': - preg_match("/\d+/", $row['line1'], $matches); - $returnval[$row['id']] = $row['name'] . " (" . $row['zip'] . - "," . $matches[0] . ")"; - break; - } + $displayName = InsuranceCompanyService::getDisplayNameForInsuranceRecord($row); + $returnval[$row['id']] = $displayName; } return $returnval; diff --git a/src/RestControllers/InsuranceRestController.php b/src/RestControllers/InsuranceRestController.php index dbe5be7bf93..40dc94e7d52 100644 --- a/src/RestControllers/InsuranceRestController.php +++ b/src/RestControllers/InsuranceRestController.php @@ -38,7 +38,8 @@ public function getAll($searchParams) $serviceResult = $this->insuranceService->search($searchParams); return RestControllerHelper::handleProcessingResult($serviceResult, null, 200); } - public function getOne($insuranceUuid, $puuid) { + public function getOne($insuranceUuid, $puuid) + { $searchParams = []; // we do this again cause we have to handle the 404 result here. $searchParams['uuid'] = new TokenSearchField('uuid', $insuranceUuid, true); @@ -48,7 +49,7 @@ public function getOne($insuranceUuid, $puuid) { return RestControllerHelper::handleProcessingResult($processingResult, 404); } - return RestControllerHelper::handleProcessingResult($processingResult, 200, false ); + return RestControllerHelper::handleProcessingResult($processingResult, 200, false); } public function put($puuid, $insuranceUuid, $data) { diff --git a/src/Services/InsuranceCompanyService.php b/src/Services/InsuranceCompanyService.php index 389f30b033b..4518a486fd8 100644 --- a/src/Services/InsuranceCompanyService.php +++ b/src/Services/InsuranceCompanyService.php @@ -49,6 +49,55 @@ public function __construct() parent::__construct(self::INSURANCE_TABLE); } + public function getInsuranceDisplayName($insuranceId) + { + $searchResults = $this->search(['id' => $insuranceId]); + $insuranceCompany = null; + if ($searchResults->hasData()) { + $insuranceCompany = $searchResults->getData()[0]; + } + if (!empty($insuranceCompany)) { + return self::getDisplayNameForInsuranceRecord($insuranceCompany); + } else { + return ""; + } + } + public static function getDisplayNameForInsuranceRecord($insuranceCompany) + { + switch ($GLOBALS['insurance_information']) { + case '1': + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['line1'] . ", " . $insuranceCompany['line2'] . ")"; + break; + case '2': + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['line1'] . ", " . $insuranceCompany['line2'] . ", " . $insuranceCompany['zip'] . ")"; + break; + case '3': + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['line1'] . ", " . $insuranceCompany['line2'] . ", " . $insuranceCompany['state'] . ")"; + break; + case '4': + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['line1'] . ", " . $insuranceCompany['line2'] . ", " . $insuranceCompany['state'] . + ", " . $insuranceCompany['zip'] . ")"; + break; + case '5': + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['line1'] . ", " . $insuranceCompany['line2'] . ", " . $insuranceCompany['city'] . + ", " . $insuranceCompany['state'] . ", " . $insuranceCompany['zip'] . ")"; + break; + case '6': + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['line1'] . ", " . $insuranceCompany['line2'] . ", " . $insuranceCompany['city'] . + ", " . $insuranceCompany['state'] . ", " . $insuranceCompany['zip'] . ", " . $insuranceCompany['cms_id'] . ")"; + break; + case '7': + preg_match("/\d+/", $insuranceCompany['line1'], $matches); + $returnval = $insuranceCompany['name'] . " (" . $insuranceCompany['zip'] . + "," . $matches[0] . ")"; + break; + case '0': + default: + $returnval = $insuranceCompany['name']; + break; + } + return $returnval; + } public function getUuidFields(): array { return ['uuid']; @@ -65,7 +114,7 @@ public function search($search, $isAndCondition = true) $sql .= " i.x12_receiver_id,"; $sql .= " i.x12_default_partner_id,"; $sql .= " i.alt_cms_id,"; - $sql .= " i.inactive,work_number.id as work_id,fax_number.id AS fax_id,"; + $sql .= " i.inactive,work_number.work_id,fax_number.fax_id,"; $sql .= " CONCAT( COALESCE(work_number.country_code,'') ,COALESCE(work_number.area_code,'') @@ -85,18 +134,18 @@ public function search($search, $isAndCondition = true) $sql .= " a.zip,"; $sql .= " a.country"; $sql .= " FROM insurance_companies i "; - $sql .= " JOIN addresses a ON i.id = a.foreign_id"; + $sql .= " JOIN (SELECT line1,line2,city,state,zip,country,foreign_id FROM addresses) a ON i.id = a.foreign_id"; // the foreign_id here is a globally unique sequence so there is no conflict. // I don't like the assumption here as it should be more explicit what table we are pulling // from since OpenEMR mixes a bunch of paradigms. I initially worried about data corruption as phone_numbers // foreign id could be ambigious here... but since the sequence is globally unique @see \generate_id() we can // join here safely... $sql .= " LEFT JOIN ( - SELECT id,foreign_id,country_code, area_code, prefix, number + SELECT id AS work_id,foreign_id,country_code, area_code, prefix, number FROM phone_numbers WHERE number IS NOT NULL AND type = " . self::TYPE_WORK . " ) work_number ON i.id = work_number.foreign_id"; $sql .= " LEFT JOIN ( - SELECT id,foreign_id,country_code, area_code, prefix, number + SELECT id AS fax_id,foreign_id,country_code, area_code, prefix, number FROM phone_numbers WHERE number IS NOT NULL AND type = " . self::TYPE_FAX . " ) fax_number ON i.id = fax_number.foreign_id"; diff --git a/src/Services/InsuranceService.php b/src/Services/InsuranceService.php index bbf3fa2d4e0..503162f33e4 100644 --- a/src/Services/InsuranceService.php +++ b/src/Services/InsuranceService.php @@ -64,7 +64,8 @@ public function validate($data) return $this->coverageValidator->validate($data); } - public function getOneByPatientUuidAndInsuranceType($puuid, $type) { + public function getOneByPatientUuidAndInsuranceType($puuid, $type) + { return $this->search(['puuid' => $puuid, 'type' => $type]); } diff --git a/src/Services/PhoneNumberService.php b/src/Services/PhoneNumberService.php index 82ec87e11e2..b88ad12e532 100644 --- a/src/Services/PhoneNumberService.php +++ b/src/Services/PhoneNumberService.php @@ -91,9 +91,8 @@ public function update($data, $foreignId) $phoneNumbersSql .= " country_code=?,"; $phoneNumbersSql .= " area_code=?,"; $phoneNumbersSql .= " prefix=?,"; - $phoneNumbersSql .= " number=?,"; - $phoneNumbersSql .= " type=?"; - $phoneNumbersSql .= " WHERE foreign_id=?"; + $phoneNumbersSql .= " number=? "; + $phoneNumbersSql .= " WHERE foreign_id=? AND type=?"; $phoneNumbersSqlResults = sqlStatement( $phoneNumbersSql, @@ -102,8 +101,8 @@ public function update($data, $foreignId) $this->area_code , $this->prefix, $this->number, - $this->type, - $this->foreignId + $this->foreignId, + $this->type ) ); diff --git a/src/Validators/CoverageValidator.php b/src/Validators/CoverageValidator.php index fea460e09e3..3f2546b0ced 100644 --- a/src/Validators/CoverageValidator.php +++ b/src/Validators/CoverageValidator.php @@ -116,7 +116,7 @@ function (Validator $context) { } } return true; - }); + }); $context->optional('date_end') ->required(function ($values) { if (!empty($values['date'])) { @@ -127,7 +127,7 @@ function (Validator $context) { return false; }, null, true) ->datetime('Y-m-d') - ->callback(function($value, $values) { + ->callback(function ($value, $values) { return true; diff --git a/templates/patient/insurance/_insurance_edit_screen_edit.html.twig b/templates/patient/insurance/_insurance_edit_screen_edit.html.twig index 2acd4e8172c..50e857d30b9 100644 --- a/templates/patient/insurance/_insurance_edit_screen_edit.html.twig +++ b/templates/patient/insurance/_insurance_edit_screen_edit.html.twig @@ -93,7 +93,7 @@ - {{ 'Search/Add/Edit'|xlt }} + {{ 'Search/Add/Edit'|xlt }} {% for fieldRow in fields.left %}