Custom Service
Overview
The Custom Service gives you the flexibility to execute your own CCL scripts from within a Clinical Office payload. Your scripts can be run before or after the normal Clinical Office tasks, allowing you the flexibility to control the JSON output, but also control the list of visit and person records being used by Clinical Office data services.
To use this service, you must import the service, assign it to an object, execute a data collection operation (either with the load method on this or any other MPage Developer service, or with the MPage Service executeCCL method). Finally, you need to do something with the data you have retrieved.
Import
import {Component, inject, OnInit, ...etc...} from '@angular/core';
import {CustomService} from '@clinicaloffice/mpage-developer';
Object Assignment
Using the Angular inject command, assign the CustomService to a new object. It is recommended that you scope your object as "protected" to allow access to your component HTML.
@Component({...
...})
export class YourComponent implements OnInit {
protected customService = inject(CustomService);
....remaining code...
Data Collection
The examples below demonstrate data collection methods you can use.
// Typical custom service payload allowing one or more script entries.
this.customService.load({
customScript: {
script: [{
name: 'your_custom_ccl_script:group1',
run: 'pre',
id: 'PATIENT_INFO'
}]
}
});
// The load method offers a short-cut format when running a single custom script. You can pass a single object that
// you would have passed to the script array and the load method with automatically wrap it in the proper
// customScript: {script: [ ]}} format.
this.customService.load({
name: 'your_custom_ccl_script:group1',
run: 'pre',
id: 'PATIENT_INFO'
});
// Use the MPage Service to load multiple payload items including two custom CCL scripts, the first running (pre)
// before the Person Service and the second running (post) after everything else runs.
this.MPageService.executeCCL({
payload: {
patientSource: [{personId: 0, encntrId: 0}],
person: {
aliases: true,
},
customScript: {
script: [
{
name: 'your_custom_ccl_script:group1',
run: 'pre',
id: 'PATIENT_INFO'
},
{
name: 'your_second_ccl_script:group1',
run: 'post',
id: 'ORDERS',
parameters: {
orderTypes: ['PHARMACY', 'LAB']
}
}
]
}
}
});
Payload Options
The following payload options represent all available payload values for the CustomService.
{
payload: {
clearPatientSource: true,
patientSource: [
{personId: value, encntrId: value}
],
customScript: {
script: [
{name: 'ccl script name', run: 'pre', id: 'unique name', parameters: { anything you want }}
]
}
}
}
- The clearPatientSource payload tag is responsible for erasing the contents of the PATIENT_SOURCE record structure, which is used by all Clinical Office scripts to identify visit and person records. Once you have cleared the patient source, you can re-populate the record structure with your CCL script. For this to have any effect, you will also need to execute some Clinical Office payload tags such as person, allergy, etc. as the patient source is only clear for the current payload run.
- You can include multiple script tags within the customScript payload. The only requirement is that any parameters you include in your payload must have the same data types between scripts. For example, if you run two custom scripts in the same payload that have a parameter called startDate and in one script you are using a fully qualified JavaScript Date and in the second script you are using a simple string, your CCL scripts will error out and cause your MPage to fail.
- The name field in your script tag needs to be the executable name of your CCL script and include the group level the script runs at. We highly recommend that all of your custom scripts run at a level of GROUP1.
- Valid values for the run element are 'pre' and 'post'. Scripts identified as 'pre' will run before all other Clinical Office scripts. Scripts marked as 'post' will run at the end of the payload job.
- id represents a unique name that you will give your custom data. This name is the key name that will be used by the CustomService provider when using the get(key) method. Re-using an identifier will result in the old data being replaced.
-
The parameters payload tag has been designed to allow you the flexibility of passing any needed parameter
data such as date ranges to your CCL script. A parameter called fromDate in your payload will be available
in CCL as
PAYLOAD->CUSTOMSCRIPT->SCRIPT[nSCRIPT]->PARAMETERS.FROMDATE.
Using Callbacks
Both the service load method and MPageService executeCCL methods offer a callback parameter as the final parameter. You can use this callback or another option of your choosing to work with the data returned from Cerner.
this.MPageService.executeCCL({
payload: {
...
}
}, () => { ...do something here... }
Methods / Usable Objects
clear(key: string): voidClear one or all of the custom data values from memory. If '' is passed as the key, all custom data is removed from memory.
emptyDmInfo(): IDmInfoThis get method returns an empty IDmInfo object. DMInfo usage is explained later on this page.
executeDmInfoAction(id: string, action: string, data: IDmInfo[], callback: any = undefined): voidExecute a read/write/delete action on the DM_INFO table. DMInfo usage is explained later on this page.
executeDmInfoActions(id: string, actions: IDmInfoActions[], callback: any = undefined): voidPlural version of executeDMInfoAction where you can issue multiple DMInfoActions in the same call. DMInfo usage is explained later on this page.
get(key: string): anyReturns the content created by your custom CCL script as a JavaScript object where the key is equal to the name of the unique id you passed in your payload. If you pass an empty string to the "id" field in your payload, no data will be returned to your MPage.
has(key: string): booleanReturns a boolean true or false indicating if the custom service has loaded and stored data under the key name. This is a useful method for determining if your custom script has finished running.
length(): numberReturns a count containing the number of custom service values loaded in memory.
load( payload: any, patientSource: IPatientSource[] = [{personId: 0, encntrId: 0}], callback: any = undefined): voidExecutes custom script(s) inside payload for the provided patientSource.
putLog(text: string, type: string = 'info', processId: number = -1, statusText: string = ''): voidWrites a line of text to the MPage Developer log. Valid values for type include 'info', 'error', 'payload', and 'debug'. You should only use the values 'info' or 'error' as 'payload' and 'debug' are reserved for system use. The processId and statusText values can safely be ignored.
custom(): Map<number, any>Direct reference to the custom Map object.
values(): IterableIterator<any>Returns an iterable iterator representation of all loaded custom service values.
CCL
For your custom CCL data service to work, you need a CCL script that is going to perform your task(s) and either update the PATIENT_SOURCE record structure or populate your MPage JSON stream.
You can use the 1co5_mpage_template.prg script that was included with your CCL repository as a starting point for your custom CCL scripts. Simply save the script under your own custom name, modify or remove the rCustom record structure and add your own code where indicated in the source. If you chose to remove the rCustom record structure and create your own new structure, you will need to put the name of your new record structure in the call to ADD_CUSTOM_OUTPUT at the bottom of the script.
The template script is predefined with a record structure called rCustom. This record structure does not contain any useful fields and you are expected to modify it to meet your needs.
free record rCustom
record rCustom (
1 data[*]
2 custom_string = vc
2 custom_date = dq8
2 custom_cd = f8
2 custom_prsnl[*]
3 person_id = f8
3 name_full_formatted = vc
3 position_cd = f8
3 position = vc
)
Any elements you place in your custom structure or the rCustom structure will be available as a
JavaScript object in your MPage through the get(key) method. For example, if you were to run the default
template script with a custom id equal to "test" in your payload, you could access record field called
YOUR_CUSTOM_FIELD in your MPage with this.customService.get("test").yourCustomField.
The MPage Developer Reference Service provides functionality for detailing reference information about your CCL script. To allow your custom script to be included in reference service lookups, you need to populate your rCustom record structure and optionally add code value references. The custom template CCL script includes a section of code to handle reference service lookups. The highlighted sections below need to be changed to match your rCustom definitions in your custom script if you plan on using the reference service in your MPage.
If you do not plan to use the reference service, you can safely delete the entire block of code show below from your custom CCL script.
; Alternate reference structure to be used with ReferenceService. If you are not implementing
; ReferenceService, simply remove this entire IF block from your script. If you plan on using the
; ReferenceService, please ensure that you populate your entire record structure with one
; empty row for each dynamic element.
if (run_stats->reference_ind = 1)
set stat = alterlist(rCustom->data, 1)
set stat = alterlist(rCustom->data[1]->custom_prsnl, 1)
; Populate the code sets to be matched up to your record structure. This will send the entire
; reference for all code values for a table unless you specify column_name filters as done
; in the example below.
select into "nl:"
from dm_columns_doc dcd
plan dcd
where dcd.table_name in ("PRSNL")
and dcd.code_set > 0
and dcd.column_name in ("POSITION_CD") ; Filter only the fields you need
detail
call add_ref_code_set(payload->customscript->script[nscript].id,
camel_field(dcd.column_name), dcd.description, dcd.code_set)
with counter
go to skip_logic
endif
DMInfo Actions
CustomService offers a method called executeDmInfoAction which offers the ability to read, write and delete records from the DM_INFO table.
DM_INFO is a table in Cerner suitable for storing small volumes of patient or reference data. If you plan on storing significant amounts of data we highly suggest you create your own custom tables and use the CustomService service to communicate between your CCL and your MPage.
Required Parameters
-
id: string
The id parameter represents the name which your data will be stored under in the CustomService service. Passing a value of '' will result in no data being returned back to the MPage. Omitting this value will typically only be used during a write or delete action if you do not desire to have data returned.
-
action: string
There are three possible action values which are 'r', 'w' and 'd'. 'r' represents a read action, 'w' a write or update action and 'd' represents a delete action.
-
data: IDmInfo[]
The data parameter represents an array of IDMInfo objects that contain the basic structure of the DM_INFO table and associated LONG_TEXT record.
interface IDmInfo { infoDomain: string; infoName: string; infoDate: Date; infoChar: string; infoNumber: number; infoLongText: string; infoDomainId: number; }The infoDomain, infoName and infoDomainId fields are key fields and should be used to uniquely identify your application, data identifier and unique id.
-
callback: any = undefined
The optional callback method allows calling code immediately after execution of the DMInfo action has occurred. See "Read Example #1" below for an example of how to use the callback parameter.
Usage Examples
The following examples assume that you have already assigned the CustomService to an object called custom.
Read Example #1In this example, we are going to read all values for an infoDomain called "Patient Dashboard" and assign the returning CustomService data variable a name of dashboardData. Using a callback, we are going to assign a variable called dashboardReady to true.
public dashboardReady = false;
ngOnInit(): void {
this.customService.executeDmInfoAction('dashboardData', 'r', [
{
infoDomain: 'Patient Dashboard',
infoName: '',
infoDate: new Date(),
infoChar: '',
infoNumber: 0,
infoLongText: '',
infoDomainId: 0
}
], () => { this.processDashboardData() });
}
processDashboardData(): void {
this.customService.putLog('This only runs if data exists for the read action.');
this.dashboardReady = true;
}
Read Example #2
In this example, we are going to use the same "Patient Dashboard" infoDomain value but additionally filter by an infoName value called 'colorScheme' and assign the returning CustomService data variable a name of dashboardColorScheme.
this.customService.executeDmInfoAction('dashboardColorScheme', 'r', [
{
infoDomain: 'Patient Dashboard',
infoName: 'colorScheme',
infoDate: new Date(),
infoChar: '',
infoNumber: 0,
infoLongText: '',
infoDomainId: 0
}
]);
Read Example #3
The previous read example for colorScheme wasn't too bad, however, it would only be useful for global color settings. Imagine if you wanted to offer your users storage of personalized values. This would be done by including the personId of the current user as a value in the infoDomainId property. Assuming you have assigned the mPageService to an object, you could access the prsnlId object and pass it to your data object.
this.customService.executeDmInfoAction('dashboardColorScheme', 'r', [
{
infoDomain: 'Patient Dashboard',
infoName: 'colorScheme',
infoDate: new Date(),
infoChar: '',
infoNumber: 0,
infoLongText: '',
infoDomainId: this.mpage.prsnlId
}
]);
Write Example
Writing is as simple as reading. Just ensure that you have at least an infoDomain, infoName and infoDomainId available. If your values do not require and infoDomainId (e.g. Global value), simply pass a 0 for infoDomainId.
If you include any text for infoLongText, an associated LONG_TEXT record will be created. If you are storing text and don't need many characters, simply use the infoChar field instead of infoLongText.
In the example below, we will store a color scheme in the infoChar field for our prsnlId. On first execute, an INSERT will be performed and all subsequent executions will UPDATE the record.
this.customService.executeDmInfoAction('dashboardColorScheme', 'w', [
{
infoDomain: 'Patient Dashboard',
infoName: 'colorScheme',
infoDate: new Date(),
infoChar: '{menu: "hotpink", background: "aqua", font: "blue"}',
infoNumber: 0,
infoLongText: '',
infoDomainId: this.mpage.prsnlId
}
]);
Delete Example
Deleting a row requires unique values for infoDomain, infoName and infoDomainId. Any associated LONG_TEXT records will be inactivated, however, the DM_INFO record being deleted will permanently be removed.
The example below will delete the color scheme created above.
this.customService.executeDmInfoAction('dashboardColorScheme', 'd', [
{
infoDomain: 'Patient Dashboard',
infoName: 'colorScheme',
infoDate: new Date(),
infoChar: '',
infoNumber: 0,
infoLongText: '',
infoDomainId: this.mpage.prsnlId
}
]);
emptyDMInfo Template Object
The emptyDMInfo get method can be called to return a unpopulated dmInfo record. Properties can be then modified and passed to executeDmInfoAction.
For example:
const dmAction = this.customService.emptyDmInfo;
dmAction.infoDomain = 'Patient Dashboard';
this.custom.executeDmInfoAction('dashboardColorScheme', 'r', [dmAction]);
Execute multiple actions in the same payload with executeDmInfoActions
Sometimes you may wish to run multiple dm_info actions sequentially in the same payload. For example, you may have a write operation you wish to perform followed by a read operation. The executeDmInfoActions method will allow this.
The syntax is similar to executeDmInfoAction except all parameters are stored in an array.
The example below will execute a write operation under the name dashboardColorScheme followed by a read operation called dashboardData.
this.customService.executeDmInfoActions([
{
id: 'dashboardColorScheme',
action: 'w',
data: [{
infoDomain: 'Patient Dashboard',
infoName: 'colorScheme',
infoDate: new Date(),
infoChar: '{menu: "hotpink", background: "aqua", font: "blue"}',
infoNumber: 0,
infoLongText: '',
infoDomainId: this.mpage.prsnlId
}]
},
{
id: 'dashboardData',
action: 'r',
data: [{
infoDomain: 'Patient Dashboard',
infoName: 'colorScheme',
infoDate: new Date(),
infoChar: '',
infoNumber: 0,
infoLongText: '',
infoDomainId: 0
}]
}
]);
The code above could have been shortened considerably by using the emptyDmInfo() method.
Interfaces
IDmInfo {
infoDomain: string;
infoName: string;
infoDate: Date;
infoChar: string;
infoNumber: number;
infoLongText: string;
infoDomainId: number;
}
IDmInfoActions {
id: string;
action: string;
data: IDmInfo[]
}