From ed3c2494dfd8e917f6971299057ab891da2b7e19 Mon Sep 17 00:00:00 2001 From: Jerry Padgett Date: Wed, 2 Jun 2021 20:59:46 -0400 Subject: [PATCH] New CCDA Templates (#4427) * New CCDA Templates - Reason for Referral - Mental Status - Functional Status - change encounter form for Mental status select. We don't need an active toggle. * - fix encounter DX - fix care team providers - ignore unpopulated sections in human readable. * - add health concern type to care plan - add Health Concerns CDA template * - conflicts * no message * - care plan items medication, health concerns - front payment misspelled class attr - style change radios templates * - fix revert --- .../oe-blue-button-generate/lib/documentLevel.js | 5 +- .../lib/entryLevel/functionalStatusEntryLevel.js | 126 ++++++ .../lib/entryLevel/index.js | 5 +- .../lib/entryLevel/planOfCareEntryLevel.js | 34 ++ .../oe-blue-button-generate/lib/htmlHeaders.js | 50 ++- .../oe-blue-button-generate/lib/sectionLevel2.js | 124 +++++- .../oe-blue-button-meta/lib/CCDA/code-systems.js | 6 + ccdaservice/serveccda.js | 217 ++++++++-- .../forms/functional_cognitive_status/new.php | 456 ++++++++++----------- .../Controller/EncounterccdadispatchController.php | 6 +- .../Model/EncounterccdadispatchTable.php | 81 ++-- interface/patient_file/front_payment.php | 2 +- portal/lib/download_template.php | 14 +- sql/6_0_0-to-6_1_0_upgrade.sql | 5 + sql/database.sql | 2 + 15 files changed, 800 insertions(+), 333 deletions(-) create mode 100644 ccdaservice/oe-blue-button-generate/lib/entryLevel/functionalStatusEntryLevel.js rewrite interface/forms/functional_cognitive_status/new.php (89%) diff --git a/ccdaservice/oe-blue-button-generate/lib/documentLevel.js b/ccdaservice/oe-blue-button-generate/lib/documentLevel.js index 8383f4d8d..598914919 100644 --- a/ccdaservice/oe-blue-button-generate/lib/documentLevel.js +++ b/ccdaservice/oe-blue-button-generate/lib/documentLevel.js @@ -85,12 +85,16 @@ exports.ccd2 = function (html_renderer) { [sectionLevel2.problemsSectionEntriesRequired(html_renderer.problemsSectionEntriesRequiredHtmlHeader, html_renderer.problemsSectionEntriesRequiredHtmlHeaderNA), required], [sectionLevel2.proceduresSectionEntriesRequired(html_renderer.proceduresSectionEntriesRequiredHtmlHeader, html_renderer.proceduresSectionEntriesRequiredHtmlHeaderNA), required], [sectionLevel2.resultsSectionEntriesRequired(html_renderer.resultsSectionEntriesRequiredHtmlHeader, html_renderer.resultsSectionEntriesRequiredHtmlHeaderNA), required], + sectionLevel2.functionalStatusSection(html_renderer.functionalStatusSectionHtmlHeader, html_renderer.functionalStatusSectionHtmlHeaderNA), sectionLevel2.encountersSectionEntriesOptional(html_renderer.encountersSectionEntriesOptionalHtmlHeader, html_renderer.encountersSectionEntriesOptionalHtmlHeaderNA), sectionLevel2.immunizationsSectionEntriesOptional(html_renderer.immunizationsSectionEntriesOptionalHtmlHeader, html_renderer.immunizationsSectionEntriesOptionalHtmlHeaderNA), //sectionLevel2.payersSection(html_renderer.payersSectionHtmlHeader, html_renderer.payersSectionHtmlHeaderNA), sectionLevel2.assessmentSection('', ''), sectionLevel2.planOfCareSection(html_renderer.planOfCareSectionHtmlHeader, html_renderer.planOfCareSectionHtmlHeaderNA), sectionLevel2.goalSection(html_renderer.goalSectionHtmlHeader, html_renderer.goalSectionHtmlHeaderNA), + sectionLevel2.healthConcernSection('', ''), + sectionLevel2.reasonForReferralSection('', ''), + sectionLevel2.mentalStatusSection('', ''), sectionLevel2.socialHistorySection(html_renderer.socialHistorySectionHtmlHeader, html_renderer.socialHistorySectionHtmlHeaderNA), sectionLevel2.vitalSignsSectionEntriesOptional(html_renderer.vitalSignsSectionEntriesOptionalHtmlHeader, html_renderer.vitalSignsSectionEntriesOptionalHtmlHeaderNA), sectionLevel2.medicalEquipmentSectionEntriesOptional(html_renderer.medicalEquipmentSectionEntriesOptionalHtmlHeader, html_renderer.medicalEquipmentSectionEntriesOptionalHtmlHeaderNA) @@ -98,7 +102,6 @@ exports.ccd2 = function (html_renderer) { notImplemented: [ "advanceDirectivesSectionEntriesOptional", "familyHistorySection", - "functionalStatusSection", ] }, dataKey: 'data' diff --git a/ccdaservice/oe-blue-button-generate/lib/entryLevel/functionalStatusEntryLevel.js b/ccdaservice/oe-blue-button-generate/lib/entryLevel/functionalStatusEntryLevel.js new file mode 100644 index 000000000..9d9a0c964 --- /dev/null +++ b/ccdaservice/oe-blue-button-generate/lib/entryLevel/functionalStatusEntryLevel.js @@ -0,0 +1,126 @@ +"use strict"; + +var fieldLevel = require('../fieldLevel'); +var leafLevel = require('../leafLevel'); +var condition = require("../condition"); +var contentModifier = require("../contentModifier"); + +var required = contentModifier.required; + +exports.mentalStatusObservation = { + key: "observation", + attributes: { + classCode: "OBS", + moodCode: "EVN" + }, + content: [ + fieldLevel.templateIdExt("2.16.840.1.113883.10.20.22.4.74", "2015-08-01"), + fieldLevel.templateId("2.16.840.1.113883.10.20.22.4.74"), + fieldLevel.id, { + key: "code", + attributes: { + code: "373930000", + codeSystem: "2.16.840.1.113883.6.96", + codeSystemName: "SNOMED-CT", + displayName: "Cognitive function" + }, + content: [{ + key: "translation", + attributes: { + code: "75275-8", + codeSystem: "2.16.840.1.113883.6.1", + codeSystemName: "LOINC", + displayName: "Cognitive function" + } + }] + }, + fieldLevel.statusCodeCompleted, + [fieldLevel.effectiveTime, required], { + key: "value", + attributes: [ + leafLevel.typeCD, + leafLevel.code + ], + dataKey: "value", + existsWhen: condition.codeOrDisplayname + } + ] +}; + +var functionalStatusObservation = { + key: "observation", + attributes: { + classCode: "OBS", + moodCode: "EVN" + }, + content: [ + fieldLevel.templateIdExt("2.16.840.1.113883.10.20.22.4.67", "2014-06-09"), + fieldLevel.templateId("2.16.840.1.113883.10.20.22.4.67"), + fieldLevel.id, { + key: "code", + attributes: { + code: "54522-8", + codeSystem: "2.16.840.1.113883.6.1", + codeSystemName: "LOINC", + displayName: "Functional status" + }, + content: [{ + key: "originalText", + content: { + key: "reference", + attributes: { + "value": leafLevel.nextReference("functional_status") + } + } + }] + }, { + key: "statusCode", + attributes: { + code: leafLevel.inputProperty("status") + } + }, + [fieldLevel.effectiveTime, required], { + key: "value", + attributes: [ + leafLevel.typeCD, + leafLevel.code + ], + dataKey: "value", + existsWhen: condition.codeOrDisplayname + } + ] +}; + +exports.functionalStatusOrganizer = { + key: "organizer", + attributes: { + classCode: "CLUSTER", + moodCode: "EVN" + }, + content: [ + fieldLevel.templateIdExt("2.16.840.1.113883.10.20.22.4.66", "2014-06-09"), + fieldLevel.templateId("2.16.840.1.113883.10.20.22.4.66"), + fieldLevel.uniqueId, + fieldLevel.id, { + key: "code", + attributes: { + code: "d5", + codeSystem: "2.16.840.1.113883.6.254", + codeSystemName: "ICF", + displayName: "Self-Care" + } + }, + { + key: "statusCode", + attributes: { + code: leafLevel.inputProperty("status") + } + }, + [{ + key: "component", + content: functionalStatusObservation, + dataKey: "observation", + required: true + }] + ], +}; diff --git a/ccdaservice/oe-blue-button-generate/lib/entryLevel/index.js b/ccdaservice/oe-blue-button-generate/lib/entryLevel/index.js index 44f510893..52406c75f 100644 --- a/ccdaservice/oe-blue-button-generate/lib/entryLevel/index.js +++ b/ccdaservice/oe-blue-button-generate/lib/entryLevel/index.js @@ -5,6 +5,7 @@ var resultEntryLevel = require("./resultEntryLevel"); var socialHistoryEntryLevel = require('./socialHistoryEntryLevel'); var payerEntryLevel = require('./payerEntryLevel'); var vitalSignEntryLevel = require('./vitalSignEntryLevel'); +var functionalStatusEntryLevel = require('./functionalStatusEntryLevel'); var planOfCareEntryLevel = require('./planOfCareEntryLevel'); var goalEntryLevel = require('./goalEntryLevel'); var procedureEntryLevel = require("./procedureEntryLevel"); @@ -35,13 +36,15 @@ exports.planOfCareActivityEncounter = planOfCareEntryLevel.planOfCareActivityEnc exports.planOfCareActivitySubstanceAdministration = planOfCareEntryLevel.planOfCareActivitySubstanceAdministration; exports.planOfCareActivitySupply = planOfCareEntryLevel.planOfCareActivitySupply; exports.planOfCareActivityInstructions = planOfCareEntryLevel.planOfCareActivityInstructions; +exports.healthConcernActivityAct = planOfCareEntryLevel.healthConcernActivityAct; exports.goalActivityObservation = goalEntryLevel.goalActivityObservation; exports.coverageActivity = payerEntryLevel.coverageActivity; exports.vitalSignsOrganizer = vitalSignEntryLevel.vitalSignsOrganizer; - +exports.functionalStatusOrganizer = functionalStatusEntryLevel.functionalStatusOrganizer; +exports.mentalStatusObservation = functionalStatusEntryLevel.mentalStatusObservation; exports.resultOrganizer = resultEntryLevel.resultOrganizer; exports.socialHistoryObservation = socialHistoryEntryLevel.socialHistoryObservation; diff --git a/ccdaservice/oe-blue-button-generate/lib/entryLevel/planOfCareEntryLevel.js b/ccdaservice/oe-blue-button-generate/lib/entryLevel/planOfCareEntryLevel.js index b19d282e2..2e4dd39dc 100644 --- a/ccdaservice/oe-blue-button-generate/lib/entryLevel/planOfCareEntryLevel.js +++ b/ccdaservice/oe-blue-button-generate/lib/entryLevel/planOfCareEntryLevel.js @@ -9,6 +9,40 @@ var key = contentModifier.key; var required = contentModifier.required; var dataKey = contentModifier.dataKey; + +exports.healthConcernActivityAct = { + key: "act", + attributes: { + classCode: "ACT", + moodCode: "EVN" + }, + content: [ + fieldLevel.templateId("2.16.840.1.113883.10.20.22.4.132", "2015-08-01"), + fieldLevel.uniqueId, + fieldLevel.id, { + key: "code", + attributes: { + code: "75310-3", + codeSystem: "2.16.840.1.113883.6.1", + codeSystemName: "LOINC", + displayName: "Health Concern" + }, + }, { + key: "text", + content: [{ + key: "reference", + attributes: { + "value": leafLevel.nextReference("concern") + } + }] + }, + fieldLevel.statusCodeActive + ], + existsWhen: function (input) { + return input.type === "act"; + } +}; + exports.planOfCareActivityAct = { key: "act", attributes: { diff --git a/ccdaservice/oe-blue-button-generate/lib/htmlHeaders.js b/ccdaservice/oe-blue-button-generate/lib/htmlHeaders.js index 815462019..9f7652470 100644 --- a/ccdaservice/oe-blue-button-generate/lib/htmlHeaders.js +++ b/ccdaservice/oe-blue-button-generate/lib/htmlHeaders.js @@ -766,7 +766,6 @@ exports.vitalSignsSectionEntriesOptionalHtmlHeader = { }; exports.medicalEquipmentSectionEntriesOptionalHtmlHeader = { - key: "text", existsWhen: condition.keyExists("medical_devices"), @@ -801,10 +800,7 @@ exports.medicalEquipmentSectionEntriesOptionalHtmlHeader = { }, { key: "td", text: leafLevel.deepInputProperty("device.udi", "na"), - }/*, { - key: "td", - text: leafLevel.deepInputDate("supply.date_time.low", nda) - }*/ + } ] }], dataKey: 'medical_devices' @@ -812,6 +808,50 @@ exports.medicalEquipmentSectionEntriesOptionalHtmlHeader = { }] }; +exports.functionalStatusSectionHtmlHeader = { + key: "text", + existsWhen: condition.keyExists("functional_status"), + + content: [{ + key: "table", + attributes: { + width: "100%", + border: "1" + }, + content: [{ + key: "thead", + content: [{ + key: "tr", + content: [{ + key: "th", + text: leafLevel.input, + dataTransform: function () { + return ["Functional Category", "Effective Date"]; + } + }] + }] + }, { + key: "tbody", + content: [{ + key: "tr", + content: [{ + key: "td", + attributes: { + ID: leafLevel.nextTableReference("functional_status") + }, + text: leafLevel.deepInputProperty("observation.value.name", nda), + }, { + key: "td", + text: leafLevel.deepInputDate("observation.date_time.point", nda), + } + ] + }], + dataKey: 'functional_status' + }] + }] +}; + +exports.functionalStatusSectionHtmlHeaderNA = "Not Available"; exports.allergiesSectionEntriesRequiredHtmlHeaderNA = "Not Available"; exports.medicationsSectionEntriesRequiredHtmlHeaderNA = "Not Available"; exports.problemsSectionEntriesRequiredHtmlHeaderNA = "Not Available"; diff --git a/ccdaservice/oe-blue-button-generate/lib/sectionLevel2.js b/ccdaservice/oe-blue-button-generate/lib/sectionLevel2.js index a2689f18d..0b23d168a 100644 --- a/ccdaservice/oe-blue-button-generate/lib/sectionLevel2.js +++ b/ccdaservice/oe-blue-button-generate/lib/sectionLevel2.js @@ -99,23 +99,6 @@ exports.allergiesSectionEntriesRequired = function (htmlHeader, na) { }; }; -/*var medicationsTextHeaders = ["Medication Class", "# fills", "Last fill date"]; -var medicationsTextRow = [ // Name, did not find class in the medication blue-button-data - function (input) { - var value = bbuo.deepValue(input, 'product.product.name'); - if (!bbuo.exists(value)) { - value = bbuo.deepValue(input, 'product.unencoded_name'); - } - if (!bbuo.exists(value)) { - return ""; - } else { - return value; - } - }, - leafLevel.deepInputProperty("supply.repeatNumber", ""), - leafLevel.deepInputDate("supply.date_time.point", "") -];*/ - exports.medicationsSectionEntriesRequired = function (htmlHeader, na) { return { key: "component", @@ -551,3 +534,110 @@ exports.medicalEquipmentSectionEntriesOptional = function (htmlHeader, na) { ] }; }; + +exports.functionalStatusSection = function (htmlHeader, na) { + return { + key: "component", + content: [{ + key: "section", + content: [ + fieldLevel.templateIdExt("2.16.840.1.113883.10.20.22.2.14", "2014-06-09"), + fieldLevel.templateId("2.16.840.1.113883.10.20.22.2.14"), + fieldLevel.templateCode("FunctionalStatusSection"), + fieldLevel.templateTitle("FunctionalStatusSection"), { + key: "text", + text: na, + existsWhen: condition.keyDoesntExist("functional_status") + + }, + htmlHeader, { + key: "entry", + attributes: { + typeCode: "DRIV" + }, + content: [ + entryLevel.functionalStatusOrganizer + ], + dataKey: "functional_status", + required: true + } + ] + }] + }; +}; + +exports.mentalStatusSection = function (htmlHeader, na) { + return { + key: "component", + content: [{ + key: "section", + content: [ + fieldLevel.templateIdExt("2.16.840.1.113883.10.20.22.2.56", "2015-08-01"), + fieldLevel.templateCode("MentalStatusSection"), + fieldLevel.templateTitle("MentalStatusSection"), { + key: "text", + text: na, + existsWhen: condition.keyDoesntExist("mental_status") + }, { // mental status does not use a header table. + key: "entry", + content: [ + entryLevel.mentalStatusObservation + ], + dataKey: "mental_status" + } + ] + }] + }; +}; + +exports.reasonForReferralSection = function (htmlHeader, na) { + return { + key: "component", + content: [{ + key: "section", + content: [ + fieldLevel.templateIdExt("1.3.6.1.4.1.19376.1.5.3.1.3.1", "2014-06-09"), + fieldLevel.templateId("1.3.6.1.4.1.19376.1.5.3.1.3.1"), + fieldLevel.templateCode("ReasonForReferralSection"), + fieldLevel.templateTitle("ReasonForReferralSection"), { + key: "text", + text: na, + existsWhen: condition.keyDoesntExist("referral_reason") + }, { + key: "text", + text: leafLevel.input, + dataKey: "reason" + } + ], + dataKey: "referral_reason" + }] + } +}; + +exports.healthConcernSection = function (htmlHeader, na) { + return { + key: "component", + content: [{ + key: "section", + content: [ + fieldLevel.templateIdExt("2.16.840.1.113883.10.20.22.2.58", "2015-08-01"), + fieldLevel.templateCode("HealthConcernSection"), + fieldLevel.templateTitle("HealthConcernSection"), { + key: "text", + text: na, + existsWhen: condition.keyDoesntExist("health_concern") + }, { + key: "text", + text: leafLevel.input, + dataKey: "health_concern.text" + }, { + key: "entry", + content: [ + entryLevel.healthConcernActivityAct // located in planofcare entry. + ], + dataKey: "health_concern" + } + ] + }] + } +}; diff --git a/ccdaservice/oe-blue-button-meta/lib/CCDA/code-systems.js b/ccdaservice/oe-blue-button-meta/lib/CCDA/code-systems.js index 7caf4521d..76992d242 100644 --- a/ccdaservice/oe-blue-button-meta/lib/CCDA/code-systems.js +++ b/ccdaservice/oe-blue-button-meta/lib/CCDA/code-systems.js @@ -290,6 +290,12 @@ var sections_entries_codes = { "code_system_name": "LOINC", "name": "Goals" }, + "MentalStatusSection": { + "code": "10190-7", + "code_system": "2.16.840.1.113883.6.1", + "code_system_name": "LOINC", + "name": "Mental Status" + }, "PlannedProcedureSection": { "code": "59772-4", "code_system": "2.16.840.1.113883.6.1", diff --git a/ccdaservice/serveccda.js b/ccdaservice/serveccda.js index b6397b3ad..329e200ff 100644 --- a/ccdaservice/serveccda.js +++ b/ccdaservice/serveccda.js @@ -69,16 +69,17 @@ function templateDate(date, precision) { } function cleanCode(code) { - if (code.length < 2) { + return code; + /*if (code.length < 2) { code = ""; } - return code.replace(/[.#]/, ""); + return code.replace(/[.#]/, "");*/ } function isOne(who) { try { if (who !== null && typeof who === 'object') { - return (who.hasOwnProperty('extension') || who.hasOwnProperty('id') || who.hasOwnProperty('date')) ? 1 : Object.keys(who).length; + return (who.hasOwnProperty('npi') || who.hasOwnProperty('code') || who.hasOwnProperty('extension') || who.hasOwnProperty('id') || who.hasOwnProperty('date')) ? 1 : Object.keys(who).length; } } catch (e) { return false; @@ -252,10 +253,15 @@ function populateProviders() { let providerArray = []; let provider = populateProvider(all.primary_care_provider.provider); providerArray.push(provider); - - for (let i in all.care_team.provider) { - provider = populateProvider(all.care_team.provider[i]); + let count = isOne(all.care_team.provider); + if (count === 1) { + provider = populateProvider(all.care_team.provider); providerArray.push(provider); + } else if (count > 1) { + for (let i in all.care_team.provider) { + provider = populateProvider(all.care_team.provider[i]); + providerArray.push(provider); + } } return { "providers": @@ -498,6 +504,27 @@ function populateMedication(pd) { } function populateEncounter(pd) { + let name = ''; + let code = ''; + let code_system_name = ""; + let status = "Active"; + // just to get diagnosis. for findings.. + if (typeof pd.encounter_procedures.encounter_diagnosis !== 'undefined') { + name = pd.encounter_procedures.encounter_diagnosis.text; + code = cleanCode(pd.encounter_procedures.encounter_diagnosis.code); + code_system_name = pd.encounter_procedures.encounter_diagnosis.code_type; + status = pd.encounter_procedures.encounter_diagnosis.status; + } else if (typeof pd.encounter_procedures.procedures !== 'undefined') { + if (isOne(pd.encounter_procedures.procedures) === 1) { + name = pd.encounter_procedures.procedures.text; + code = cleanCode(pd.encounter_procedures.procedures.code); + code_system_name = pd.encounter_procedures.procedures.code_type; + } else { + name = pd.encounter_procedures.procedures[0].text; + code = cleanCode(pd.encounter_procedures.procedures[0].code); + code_system_name = pd.encounter_procedures.procedures[0].code_type; + } + } return { "encounter": { "name": pd.visit_category ? pd.visit_category : 'UNK', @@ -564,15 +591,16 @@ function populateEncounter(pd) { } ] }], + // @todo this should be array of all procedures/DX. see about in spec! "findings": [{ "identifiers": [{ "identifier": pd.sha_extension, "extension": pd.extension }], "value": { - "name": pd.encounter_procedures.procedures.text || encounter_reason, - "code": cleanCode(pd.encounter_procedures.procedures.code), - "code_system_name": pd.encounter_procedures.procedures.code_type || "ICD10" || "CPT4" + "name": name, + "code": code, + "code_system_name": code_system_name }, "date_time": { "low": { @@ -580,7 +608,8 @@ function populateEncounter(pd) { "precision": "day" } }, - //"status": pd.encounter_procedures.procedures.status + "status": status, + "reason": pd.encounter_reason }] }; } @@ -983,7 +1012,7 @@ function getPlanOfCare(pd) { function getGoals(pd) { return { - "goal_code": { // this is weird as spec say's yes but in practice is a no. + "goal_code": { "name": pd.code_text !== "NULL" ? pd.code_text : "", "code": cleanCode(pd.code) || "", "code_system_name": pd.code_type || "" @@ -1005,12 +1034,69 @@ function getGoals(pd) { }; } +function getFunctionalStatus(pd) { + return { + "status": "completed", + "identifiers": [{ + "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809000" + }], + "observation": { + "value": { + "name": pd.code_text !== "NULL" ? pd.code_text : "", + "code": cleanCode(pd.code) || "", + "code_system_name": pd.code_type || "" + }, + "identifiers": [{ + "identifier": "9a6d1bac-17d3-4195-89a4-1121bc8090ab" + }], + "date_time": { + "point": { + "date": fDate(pd.date_formatted), + "precision": "day" + } + }, + "status": "completed" + } + }; +} + +function getMentalStatus(pd) { + return { + "value": { + "name": pd.code_text !== "NULL" ? pd.code_text : "", + "code": cleanCode(pd.code) || "", + "code_system_name": pd.code_type || "" + }, + "identifiers": [{ + "identifier": "9a6d1bac-17d3-4195-89a4-1121bc809ccc" + }], + + "date_time": { + "low": templateDate(pd.date_formatted, "day") + //"high": templateDate(pd.date, "day") + }, + }; +} + function getAssessments(pd) { return { "description": pd.description }; } +function getHealthConcerns(pd) { + return { + "type": "act", + "text": pd.text + }; +} + +function getReferralReason(pd) { + return { + "reason": pd.text + }; +} + function populateVital(pd) { return { "identifiers": [{ @@ -1448,7 +1534,7 @@ function populatePayer(pd) { } function populateHeader(pd) { - var head = { + const head = { "identifiers": [ { "identifier": oidFacility, @@ -1803,7 +1889,9 @@ function genCcda(pd) { m = populateMedication(pd.medications.medication); meds.medications.push(m); } - data.medications = Object.assign(meds.medications); + if (count !== 0) { + data.medications = Object.assign(meds.medications); + } // Encounters let encs = []; let enc = {}; @@ -1822,8 +1910,9 @@ function genCcda(pd) { enc = populateEncounter(pd.encounter_list.encounter); encs.encounters.push(enc); } - data.encounters = Object.assign(encs.encounters); - + if (count !== 0) { + data.encounters = Object.assign(encs.encounters); + } // Allergies let allergies = []; let allergy = {}; @@ -1842,8 +1931,9 @@ function genCcda(pd) { allergy = populateAllergy(pd.allergies.allergy); allergies.allergies.push(allergy); } - data.allergies = Object.assign(allergies.allergies); - + if (count !== 0) { + data.allergies = Object.assign(allergies.allergies); + } // Problems let problems = []; let problem = {}; @@ -1862,7 +1952,9 @@ function genCcda(pd) { problem = populateProblem(pd.problem_lists.problem); problems.problems.push(problem); } - data.problems = Object.assign(problems.problems); + if (count !== 0) { + data.problems = Object.assign(problems.problems); + } // Procedures many = []; theone = {}; @@ -1881,7 +1973,9 @@ function genCcda(pd) { theone = populateProcedure(pd.procedures.procedure); many.procedures.push(theone); } - data.procedures = Object.assign(many.procedures); + if (count !== 0) { + data.procedures = Object.assign(many.procedures); + } // Medical Devices many = []; theone = {}; @@ -1900,12 +1994,26 @@ function genCcda(pd) { theone = populateMedicalDevice(pd.medical_devices.device); many.medical_devices.push(theone); } - data.medical_devices = Object.assign(many.medical_devices); - + if (count !== 0) { + data.medical_devices = Object.assign(many.medical_devices); + } // Results if (pd.results) { data.results = Object.assign(getResultSet(pd.results, pd)['results']); } + +// Referral + // different referral sources. 1st is dynamic with doc gen from CCM. + // 2nd is latest referral from transactions. + if (pd.referral_reason[0].text !== "") { + data.referral_reason = Object.assign(getReferralReason(pd.referral_reason[0], pd)); + } else if (pd.referral_reason[1].text !== "") { + data.referral_reason = Object.assign(getReferralReason(pd.referral_reason[1], pd)); + } +// Health Concerns + if (pd.health_concerns.text !== "") { + data.health_concern = Object.assign(getHealthConcerns(pd.health_concerns, pd)); + } // Immunizations many = []; theone = {}; @@ -1924,8 +2032,9 @@ function genCcda(pd) { theone = populateImmunization(pd.immunizations.immunization); many.immunizations.push(theone); } - data.immunizations = Object.assign(many.immunizations); - + if (count !== 0) { + data.immunizations = Object.assign(many.immunizations); + } // Plan of Care many = []; theone = {}; @@ -1948,8 +2057,9 @@ function genCcda(pd) { theone = getPlanOfCare(pd.planofcare.item); many.plan_of_care.push(theone); } - data.plan_of_care = Object.assign(many.plan_of_care); - + if (count !== 0) { + data.plan_of_care = Object.assign(many.plan_of_care); + } // Goals many = []; theone = {}; @@ -1968,9 +2078,10 @@ function genCcda(pd) { theone = getGoals(pd.goals.item); many.goals.push(theone); } - data.goals = Object.assign(many.goals); - -// Assessments. Not part of a CCD I think. + if (count !== 0) { + data.goals = Object.assign(many.goals); + } +// Assessments. many = []; theone = {}; many.assessments = []; @@ -1993,6 +2104,50 @@ function genCcda(pd) { data.assessments = Object.assign(many.assessments); } +// Functional Status. + many = []; + theone = {}; + many.functional_status = []; + try { + count = isOne(pd.functional_status.item); + } catch (e) { + count = 0 + } + if (count > 1) { + for (let i in pd.functional_status.item) { + theone[i] = getFunctionalStatus(pd.functional_status.item[i]); + many.functional_status.push(theone[i]); + } + } else if (count !== 0) { + theone = getFunctionalStatus(pd.functional_status.item); + many.functional_status.push(theone); + } + if (count !== 0) { + data.functional_status = Object.assign(many.functional_status); + } + +// Mental Status. + many = []; + theone = {}; + many.mental_status = []; + try { + count = isOne(pd.mental_status.item); + } catch (e) { + count = 0 + } + if (count > 1) { + for (let i in pd.mental_status.item) { + theone[i] = getMentalStatus(pd.mental_status.item[i]); + many.mental_status.push(theone[i]); + } + } else if (count !== 0) { + theone = getMentalStatus(pd.mental_status.item); + many.mental_status.push(theone); + } + if (count !== 0) { + data.mental_status = Object.assign(many.mental_status); + } + // Social History many = []; theone = {}; @@ -2012,9 +2167,9 @@ function genCcda(pd) { theone = populateSocialHistory(pd.history_physical.social_history.history_element); many.social_history.push(theone); } - - data.social_history = Object.assign(many.social_history); - + if (count !== 0) { + data.social_history = Object.assign(many.social_history); + } // ------------------------------------------ End Sections ----------------------------------------// doc.data = Object.assign(data); diff --git a/interface/forms/functional_cognitive_status/new.php b/interface/forms/functional_cognitive_status/new.php dissimilarity index 89% index 30da1d507..c00512d8d 100644 --- a/interface/forms/functional_cognitive_status/new.php +++ b/interface/forms/functional_cognitive_status/new.php @@ -1,228 +1,228 @@ - - * @author Vinish K - * @author Brady Miller - * @copyright Copyright (c) 2015 Z&H Consultancy Services Private Limited - * @copyright Copyright (c) 2017-2019 Brady Miller - * @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 - */ - -require_once(__DIR__ . "/../../globals.php"); -require_once("$srcdir/api.inc"); -require_once("$srcdir/patient.inc"); -require_once("$srcdir/options.inc.php"); -require_once($GLOBALS['srcdir'] . '/csv_like_join.php'); -require_once($GLOBALS['fileroot'] . '/custom/code_types.inc.php'); - -use OpenEMR\Common\Csrf\CsrfUtils; -use OpenEMR\Core\Header; - -$returnurl = 'encounter_top.php'; -$formid = 0 + (isset($_GET['id']) ? $_GET['id'] : 0); -if ($formid) { - $sql = "SELECT * FROM `form_functional_cognitive_status` WHERE id=? AND pid = ? AND encounter = ?"; - $res = sqlStatement($sql, array($formid,$_SESSION["pid"], $_SESSION["encounter"])); - - $all = []; - for ($iter = 0; $row = sqlFetchArray($res); $iter++) { - $all[$iter] = $row; - } - $check_res = $all; -} - -$check_res = $formid ? $check_res : array(); -?> - - - <?php echo xlt("Functional and Cognitive Status"); ?> - - - - - - -
-
-
-

-
- -
- -
- $obj) { ?> -
-
-
- - " onclick='sel_code(this.parentElement.parentElement.parentElement.id);'> - - "> -
-
- - ' /> -
-
- -
- " class="activity"> - " class="activity1"> -
-
- - -
-
- - -
- -
-
- -
-
-
- - " onclick='sel_code(this.parentElement.parentElement.parentElement.id);'> - - "> -
-
- - ' title='' /> -
-
- -
- - -
-
- - -
-
- - -
- -
-
- -
-
-
-
-
- - -
- -
-
-
-
-
-
- - - + +* @author Vinish K +* @author Brady Miller +* @copyright Copyright (c) 2015 Z&H Consultancy Services Private Limited +* @copyright Copyright (c) 2017-2019 Brady Miller +* @license https://github.com/openemr/openemr/blob/master/LICENSE GNU General Public License 3 +*/ + +require_once(__DIR__ . "/../../globals.php"); +require_once("$srcdir/api.inc"); +require_once("$srcdir/patient.inc"); +require_once("$srcdir/options.inc.php"); +require_once($GLOBALS['srcdir'] . '/csv_like_join.php'); +require_once($GLOBALS['fileroot'] . '/custom/code_types.inc.php'); + +use OpenEMR\Common\Csrf\CsrfUtils; +use OpenEMR\Core\Header; + +$returnurl = 'encounter_top.php'; +$formid = 0 + (isset($_GET['id']) ? $_GET['id'] : 0); +if ($formid) { + $sql = "SELECT * FROM `form_functional_cognitive_status` WHERE id=? AND pid = ? AND encounter = ?"; + $res = sqlStatement($sql, array($formid,$_SESSION["pid"], $_SESSION["encounter"])); + + $all = []; + for ($iter = 0; $row = sqlFetchArray($res); $iter++) { + $all[$iter] = $row; + } + $check_res = $all; +} + +$check_res = $formid ? $check_res : array(); +?> + + + <?php echo xlt("Functional and Cognitive Status"); ?> + + + + + + +
+
+
+

+
+ +
+ +
+ $obj) { ?> +
+
+
+ + " onclick='sel_code(this.parentElement.parentElement.parentElement.id);'> + + "> +
+
+ + ' /> +
+
+ +
+ " class="activity" title=""> + " class="activity1"> +
+
+ + +
+
+ + +
+ +
+
+ +
+
+
+ + " onclick='sel_code(this.parentElement.parentElement.parentElement.id);'> + + "> +
+
+ + ' title='' /> +
+
+ +
+ "> + +
+
+ + +
+
+ + +
+ +
+
+ +
+
+
+
+
+ + +
+ +
+
+
+
+
+
+ + + diff --git a/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Controller/EncounterccdadispatchController.php b/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Controller/EncounterccdadispatchController.php index 348485905..c3a1f2cf8 100644 --- a/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Controller/EncounterccdadispatchController.php +++ b/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Controller/EncounterccdadispatchController.php @@ -76,10 +76,10 @@ class EncounterccdadispatchController extends AbstractActionController $sent_by = $this->getRequest()->getQuery('sent_by'); $send = $this->getRequest()->getQuery('send') ? $this->getRequest()->getQuery('send') : 0; $view = $this->getRequest()->getQuery('view') ? $this->getRequest()->getQuery('view') : 0; - $emr_transfer = $this->getRequest()->getQuery('emr_transfer') ? $this->getRequest()->getQuery('emr_transfer') : 0; + $emr_transfer = $this->getRequest()->getQuery('emr_transfer') ? $this->getRequest()->getQuery('emr_transfer') : 0; $this->recipients = $this->getRequest()->getQuery('recipient'); $this->params = $this->getRequest()->getQuery('param'); - $this->referral_reason = $this->getRequest()->getQuery('referral_reason'); + $this->referral_reason = $this->getRequest()->getQuery('referral_reason'); $this->components = $this->getRequest()->getQuery('components') ? $this->getRequest()->getQuery('components') : $this->params('components'); $downloadccda = $this->params('downloadccda'); $this->latest_ccda = $this->getRequest()->getQuery('latest_ccda') ? $this->getRequest()->getQuery('latest_ccda') : $this->params('latest_ccda'); @@ -436,7 +436,7 @@ class EncounterccdadispatchController extends AbstractActionController } if (in_array('referral', $components_list)) { - $ccd .= $this->getEncounterccdadispatchTable()->getReferals($pid, $encounter); + $ccd .= $this->getEncounterccdadispatchTable()->getReferrals($pid, $encounter); } return $ccd; } diff --git a/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/EncounterccdadispatchTable.php b/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/EncounterccdadispatchTable.php index 787c45cc5..770008315 100644 --- a/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/EncounterccdadispatchTable.php +++ b/interface/modules/zend_modules/module/Carecoordination/src/Carecoordination/Model/EncounterccdadispatchTable.php @@ -931,11 +931,11 @@ class EncounterccdadispatchTable extends AbstractTableGateway $res_procedures = $appTable_procedures->zQuery($query_procedures, array('CPT4', $pid, 'CPT4', $row['encounter'])); foreach ($res_procedures as $row_procedures) { $codes .= " - - " . xmlEscape($row_procedures['code']) . " - " . xmlEscape($row_procedures['code_text']) . " - - "; + + " . xmlEscape($row_procedures['code']) . " + " . xmlEscape("CPT4") . " + " . xmlEscape($row_procedures['code_text']) . " + "; } if ($row['encounter_diagnosis']) { @@ -949,13 +949,12 @@ class EncounterccdadispatchTable extends AbstractTableGateway } $codes .= " - - " . xmlEscape($row['encounter_diagnosis']) . " - " . xmlEscape($code_type) . " - " . xmlEscape(\Application\Listener\Listener::z_xlt($row['title'])) . " - " . xmlEscape($encounter_activity) . " - - "; + + " . xmlEscape($row['encounter_diagnosis']) . " + " . xmlEscape($code_type) . " + " . xmlEscape(\Application\Listener\Listener::z_xlt($row['title'])) . " + " . xmlEscape($encounter_activity) . " + "; } $location_details = ($row['name'] != '') ? (',' . $row['fstreet'] . ',' . $row['fcity'] . ',' . $row['fstate'] . ' ' . $row['fzip']) : ''; @@ -2285,10 +2284,15 @@ class EncounterccdadispatchTable extends AbstractTableGateway $status_entry = 'active'; $planofcare = ''; $goals = ''; + $concern = ''; foreach ($res as $row) { $tmp = explode(":", $row['code']); $code_type = $tmp[0]; $code = $tmp[1]; + if ($row['care_plan_type'] == 'health_concern') { + $concern .= $row['date'] . " " . $row['description'] . "\r\n"; + continue; + } if ($row['care_plan_type'] == 'goal') { $goals .= ' ' . xmlEscape($row['care_plan_type']) . ' @@ -2320,7 +2324,8 @@ class EncounterccdadispatchTable extends AbstractTableGateway $planofcare .= ''; $goals .= ''; - return $planofcare . $goals; + $concerns = "" . xmlEscape($concern) . ""; + return $planofcare . $goals . $concerns; } /* @@ -2339,7 +2344,8 @@ class EncounterccdadispatchTable extends AbstractTableGateway $sqlBindArray[] = $encounter; } - $functional_cognitive = ''; + $functional_status = ''; + $cognitive_status = ''; $query = "SELECT ffcs.* FROM forms AS f LEFT JOIN form_functional_cognitive_status AS ffcs ON ffcs.id = f.form_id WHERE $wherCon f.pid = ? AND f.formdir = ? AND f.deleted = ?"; @@ -2347,34 +2353,32 @@ class EncounterccdadispatchTable extends AbstractTableGateway $appTable = new ApplicationTable(); $res = $appTable->zQuery($query, $sqlBindArray); - $functional_cognitive .= ''; foreach ($res as $row) { - $status = $status_entry = ''; if ($row['activity'] == 1) { - $status = 'Active'; - $status_code = '55561003'; - $status_entry = 'completed'; + $cognitive_status .= ' + ' . xmlEscape(($row['code'] ?: '')) . ' + ' . xmlEscape(($row['codetext'] ?: '')) . ' + ' . xmlEscape($row['description']) . ' + ' . xmlEscape($row['date']) . ' + ' . xmlEscape(str_replace("-", '', $row['date'])) . ' + ' . xmlEscape('completed') . ' + ' . xmlEscape($this->getAge($pid)) . ' + '; } else { - $status = 'Inactive'; - $status_code = '73425007'; - $status_entry = 'completed'; - } - - $functional_cognitive .= ' - ' . xmlEscape(($row['code'] ? $row['code'] : 0)) . ' - ' . xmlEscape(($row['codetext'] ? $row['codetext'] : '')) . ' + $functional_status .= ' + ' . xmlEscape(($row['code'] ?: '')) . ' + ' . xmlEscape(($row['codetext'] ?: '')) . ' ' . xmlEscape($row['description']) . ' ' . xmlEscape($row['date']) . ' - ' . xmlEscape(preg_replace('/-/', '', $row['date'])) . ' - ' . xmlEscape($status) . ' - ' . xmlEscape($status_code) . ' - ' . xmlEscape($status_entry) . ' + ' . xmlEscape(str_replace("-", '', $row['date'])) . ' + ' . xmlEscape('completed') . ' ' . xmlEscape($this->getAge($pid)) . ' '; + } } - - $functional_cognitive .= ''; - return $functional_cognitive; + $functional_status .= ''; + $cognitive_status .= ''; + return $functional_status . $cognitive_status; } public function getClinicalNotes($pid, $encounter) @@ -2449,15 +2453,16 @@ class EncounterccdadispatchTable extends AbstractTableGateway return $clinical_instructions; } - public function getReferals($pid, $encounter) + public function getReferrals($pid, $encounter) { - $wherCon = ''; + // patched out because I can't think of a reason to send a list of referrals + /*$wherCon = ''; if ($encounter) { $wherCon = "ORDER BY date DESC LIMIT 1"; - } + }*/ + $wherCon = "ORDER BY date DESC LIMIT 1"; $appTable = new ApplicationTable(); - $referrals = ''; $query = "SELECT field_value FROM transactions JOIN lbt_data ON form_id=id AND field_id = 'body' WHERE pid = ? $wherCon"; $result = $appTable->zQuery($query, array($pid)); $referrals = ''; diff --git a/interface/patient_file/front_payment.php b/interface/patient_file/front_payment.php index 6162cabfc..ee6fbd242 100644 --- a/interface/patient_file/front_payment.php +++ b/interface/patient_file/front_payment.php @@ -987,7 +987,7 @@ function make_insurance() {
-
+
diff --git a/portal/lib/download_template.php b/portal/lib/download_template.php index 7cb7f15a5..6b50daa27 100644 --- a/portal/lib/download_template.php +++ b/portal/lib/download_template.php @@ -212,18 +212,16 @@ function doSubs($s) $s = keyReplace($s, $sigfld); } elseif (keySearch($s, '{ynRadioGroup}')) { $grcnt++; - $sigfld = ''; - $sigfld .= ''; - $sigfld .= ''; - $sigfld .= ''; + $sigfld = ''; + $sigfld .= ''; + $sigfld .= ''; $sigfld .= ''; $s = keyReplace($s, $sigfld); } elseif (keySearch($s, '{TrueFalseRadioGroup}')) { $grcnt++; - $sigfld = ''; - $sigfld .= ''; - $sigfld .= ''; - $sigfld .= ''; + $sigfld = ''; + $sigfld .= ''; + $sigfld .= ''; $sigfld .= ''; $s = keyReplace($s, $sigfld); } elseif (keySearch($s, '{PatientName}')) { diff --git a/sql/6_0_0-to-6_1_0_upgrade.sql b/sql/6_0_0-to-6_1_0_upgrade.sql index 70f51586c..4faee724c 100644 --- a/sql/6_0_0-to-6_1_0_upgrade.sql +++ b/sql/6_0_0-to-6_1_0_upgrade.sql @@ -670,3 +670,8 @@ INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default` #IfNotIndex audit_details audit_master_id CREATE INDEX `audit_master_id` ON `audit_details` (`audit_master_id`); #EndIf + +#IfNotRow2D list_options list_id Plan_of_Care_Type option_id health_concern +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','health_concern','Health Concern','7','0','0','','ACT','','1','0','0',''); +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','medication','Medication','8','0','0','','INT','','1','0','0',''); +#EndIf diff --git a/sql/database.sql b/sql/database.sql index c60713b2c..fa96508dd 100644 --- a/sql/database.sql +++ b/sql/database.sql @@ -10706,6 +10706,8 @@ INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default` INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','procedure','Procedure','3','0','0','','RQO','','1','0','0',''); INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','test_or_order','Test/Order','2','0','0','','RQO','','1','0','0',''); INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','goal','Goal','6','0','0','','GOL','','1','0','0',''); +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','health_concern','Health Concern','7','0','0','','ACT','','1','0','0',''); +INSERT INTO `list_options` (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `mapping`, `notes`, `codes`, `activity`, `toggle_setting_1`, `toggle_setting_2`, `subtype`) VALUES('Plan_of_Care_Type','medication','Medication','8','0','0','','INT','','1','0','0',''); INSERT INTO list_options (`list_id`, `option_id`, `title`, `seq`, `is_default`) VALUES ('lists', 'groupstat', 'Group Statuses', '1', '0'); INSERT INTO list_options (`list_id`, `option_id`, `title`, `seq`, `is_default`, `option_value`, `notes`) VALUES -- 2.11.4.GIT