Writing rules

Contents:

Introduction
Rule types
Using service methods in the rules
Using other group/household individuals' information in the rules
Types of rules and their support/availability in Data Entry App
Types of rules and their support/availability in transaction data upload

Introduction:

Rules are just normal JavaScript functions that take some input and returns something. You can use the full power of JavaScript in these functions. We also provide you with some helper libraries that make it easier to write rules. We will introduce you to these libraries in the examples below.

All rule functions get passed an object as a parameter. The parameter object has two properties: 1. imports 2. params. The imports object is used to pass down common libraries. The params object is used to pass rule-specific parameters. In params object, we pass the relevant entity e.g. if a rule is invoked when a program encounter is being performed then we pass the ProgramEncounter object. The entities that we pass are an instance of classes defined in avni-models

Shape of common imports object:

{
  rulesConfig: {}, //It exposes everything exported by rules-config library. https://github.com/avniproject/rules-config/blob/master/rules.js.
  common: {}, // Library we have for common functions https://github.com/avniproject/avni-client/blob/master/packages/openchs-health-modules/health_modules/common.js
  lodash: {}, // lodash library
  moment: {}, // momentjs library
  motherCalculations: {}, //mother program calculations https://github.com/avniproject/avni-health-modules/blob/master/src/health_modules/mother/calculations.js
  log: {} //console.log object
}

The global variable ruleServiceLibraryInterfaceForSharingModules has been deprecated and is not supported anymore. If you are using it then do switch to use of imports object.

Entities passed to the rule

All rule receives an entity from the params object. Depending on the rule type an entity can be one of Individual, ProgramEncounter, ProgramEnrolment, Encounter, or ChecklistItem. The shape of the entity object and the supported methods can be viewed from the above links on each entity.

Rule types

  1. Enrolment summary rule
  2. Form element rule
  3. Form element group rule
  4. Visit schedule rule
  5. Decision rule
  6. Validation rule
  7. Enrolment eligibility check rule
  8. Encounter eligibility check rule
  9. Checklists rule
  10. Work list updation rule
  11. Subject summary rule
  12. Hyperlink menu item rule
  13. Message rule
  14. Dashboard Card rule
  15. Manual Programs Eligibility Check Rule
  16. Edit Form Rule

2138

Invocation of different rule types


2196

Invocation of rules affecting the form elements



1. Enrolment summary rule

  • Logical scope = Program Enrolment
  • Trigger = Before the opening of a subject dashboard with default program selection. On program change of subject dashboard.
  • In designer = Program (Enrolment Summary Rule)
  • When to use = Display important information in the subject dashboard for a program

You can use this rule to highlight important information about the program on the Subject Dashboard in table format. It can pull data from all the encounters of enrolment and the enrolment itself. You can use this when the information you want to show is not entered by the user in any of the forms and is also not required for any reporting purposes (hence you wouldn't also generate this data via decision rule).

Shape of params object:

{ 
  programEnrolment: {}, // ProgramEnrolment model
	services
}

You need to return an array of summary objects from this function.

Shape of the summary object:

{
  "name": "name of the summary concept",
  "value": <text> | <number> | <date> | <datetime> | <concept list in case of Coded question>
}

Example:

({params, imports}) =>  {
    const summaries = [];
    const programEnrolment = params.programEnrolment;
    const birthWeight = programEnrolment.findObservationInEntireEnrolment('Birth Weight');
    if (birthWeight) {
      summaries.push({name: 'Birth Weight', value: birthWeight.getValue()});
    }
    return summaries;
};
2332

Enrolment summary rule in App Designer


1024

Summary section displays the data returned by the enrolment summary rule



2. Form element rule

  • Logical scope = Form Element
  • Trigger = Before display of form element in the form wizard and on any change done by the user in on that page
  • In designer = Form Element (RULES tab)
  • When to use =
    • Hide/show a form element
    • auto calculate the value of a form element
    • reset value of a form element

Shape of params object:

{
  entity: {}, //it could be one of Individual, ProgramEncounter, ProgramEnrolment, Encounter and ChecklistItem depending on what type of form is this rule attached to
  formElement: {}, //form element to which this rule is attached to
  questionGroupIndex,
  services,
  entityContext
}

This function should return an instance of FormElementStatus to show/hide the element, show validation error, set its value, reset a value, or skip answers.

To reset a value, you can use FormElementStatus._resetIfValueIsNull() method.
You can either use FormElementStatusBuilder or use normal JavaScript to build the return value. FormElementStatusBuilder is a helper class provided by Avni that helps writing rules in a declarative way.

Examples using FormElementStatusBuilder.

'use strict';
({params, imports}) => {
  const individual = params.entity;
  const formElement = params.formElement;
  const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({individual, formElement});
  statusBuilder.show().when.valueInRegistration("Number of hywas required").is.greaterThan(0);
  return statusBuilder.build();
};
({params, imports}) => {
  const programEnrolment = params.entity;
  const formElement = params.formElement;
  const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({programEnrolment, formElement});
  statusBuilder.show().when.valueInEnrolment('Is child getting registered at Birth').containsAnswerConceptName("No");
  return statusBuilder.build();//this method returns FormElementStatus object with visibility true if the conditions given above matches
};
({params, imports}) => {
    const gravidaBreakup = [
        'Number of miscarriages',
        'Number of abortions',
        'Number of stillbirths',
        'Number of child deaths',
        'Number of living children'
    ];
    const computeGravida = (programEnrolment) => gravidaBreakup
        .map((cn) => programEnrolment.getObservationValue(cn))
        .filter(Number.isFinite)
        .reduce((a, b) => a + b, 1);
    
    const [formElement, programEnrolment] = params.programEnrolment;
    const firstPregnancy = programEnrolment.getObservationReadableValue('Is this your first pregnancy?');
    const value = firstPregnancy === 'Yes' ? 1 : firstPregnancy === 'No' ? computeGravida(programEnrolment) : undefined;
    return new FormElementStatus(formElement.uuid, true, value);
};
'use strict';
({params, imports}) => {
  const programEncounter = params.entity;
  const formElement = params.formElement;
  const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({programEncounter, formElement});
  const value = programEncounter.findLatestObservationInEntireEnrolment('Have you received first dose of TT');
  statusBuilder.show().whenItem( value.getReadableValue() == 'No').is.truthy;
  return statusBuilder.build();
};
'use strict';
({params, imports}) => {
  const encounter = params.entity;
  const formElement = params.formElement;
  const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({encounter, formElement});
  statusBuilder.show().when.valueInEncounter("Are machine start and end hour readings recorded").is.yes;
  return statusBuilder.build();
};
//In-order to fetch affiliatedGroups set as part of GroupAffiliation Concept in the same form,
//one needs to access params.entityContext.affiliatedGroups variable.

// Old Rule snippet
// const phulwariName = _.get(_.find(programEnrolment.individual.affiliatedGroups, ({voided}) => !voided), ['groupSubject', 'firstName'], '');

// New Rule snippet
const phulwariName = _.get(_.find(params.entityContext.affiliatedGroups, ({voided}) => !voided), ['groupSubject', 'firstName'], '');

2074
320

Skip logic in action for the field user



3. Form element group rule

  • Scope = Form Element Group
  • Trigger = Before display of form element group to the user (including previous or next)
  • In designer = Form Element Group (RULES tab)
  • When to use = Hide/show a form element group

Sometimes we want to hide the entire form element group based on some conditions. This can be done using a form element group (FEG) rule. There is a rules tab on each FEG where this type of rule can be written. Note that this rule gets executed before form element rule so if the form element is hidden by this rule then the form element rule will not get executed.

Shape of params object:

{
  entity: {}, //it could be one of Individual, ProgramEncounter, ProgramEnrolment, Encounter and ChecklistItem depending on what type of form is this rule attached to
  formElementGroup: {}, //form element group to which this rule is attached to
  services,
  entityContext
}

This function should return an array of FormElementStatus

Example:

({params, imports}) => {
    const formElementGroup = params.formElementGroup;
    return formElementGroup.formElements.map(({uuid}) => {
        return new imports.rulesConfig.FormElementStatus(uuid, false, null);
    });
};



4. Visit schedule rule

  • Logical scope = Encounter (aka Visit), Subject, or Program Enrolment
  • Trigger = On completion of an form wizard before final screen is displayed
  • In designer = Form (RULES tab)
  • When to use = For scheduling one or more encounters in the future

Shape of params object:

{
  entity: {}, //it could be one of ProgramEncounter, ProgramEnrolment, Encounter depending on what type of form is this rule attached to.
  visitSchedule: []// Array of already scheduled visits.
  entityContext
  services
}

You need to return an array of visit schedules from this function.

Shape of the return value

[
  <visit schedule object>
  ...
]

visit schedule object

{
	name: "visit name", 
	encounterType: "encounter type name", 
	earliestDate: <date>, 
	maxDate: <date>,
	visitCreationStrategy: "Optional. One of default|createNew",
	programEnrolment: "<Optional. Used if you want to create a visit in a different program enrolment. If the program enrolment is tied to another subject, the visit will be schedule for that subject. Do not pass this parameter if you want to schedule a general encounter.>",
	subjectUUID: "<Optional UUID string. Used if you want to create a general visit for another subject.>"
}

Example

({ params, imports }) => {
  const programEnrolment = params.entity;
  const scheduleBuilder = new imports.rulesConfig.VisitScheduleBuilder({
    programEnrolment
  });
  scheduleBuilder
    .add({
      name: "First Birth Registration Visit",
      encounterType: "Birth Registration",
      earliestDate: programEnrolment.enrolmentDateTime,
      maxDate: programEnrolment.enrolmentDateTime
    })
    .whenItem(programEnrolment.getEncounters(true).length)
    .equals(0);
  return scheduleBuilder.getAll();
};

Example 2 - Schedule a general visit on a household when a member completes a program enrolment

.
.
  scheduleBuilder.add({
      name: "TB Family Screening Form",
      encounterType: "TB Family Screening Form",
      earliestDate: imports.moment(programEnrolment.encounterDateTime).toDate(),
      maxDate: imports.moment(programEnrolment.encounterDateTime).add(15, 'days').toDate(),
      subjectUUID: programEnrolment.individual.groups[0].groupSubject.uuid
  });
.
.
1950

Screenshot - App Designer


1024

Returned encounters/visits from function are shown to the user as - Visits Being Scheduled

Strategies that Avni uses.

For all the visit schedules that are returned, Avni evaluates how to create a visit. Assume you provide the default visitCreationStrategy (this is the default behaviour). Avni checks if there is already a scheduled visit for the given encounter type. If it is there, then it is updated with the incoming scheduled visit's name and other parameters. This strategy works well in most cases.

  • Remember that the VisitSchedule rule gets called whether you create a visit, or edit it.
  • Remember not to send multiple visit schedule objects for the same encounter type. If you do, the last one will overwrite the previous objects.

Using the "createNew" visit strategy

Do this only if you know what you are doing. If you add visitCreationStrategy of "createNew", then a new visit will be created no matter what.

You need to be careful while using this strategy because, in edit scenarios, we might end up creating the same kind of visits multiple times.

Using the VisitScheduleBuilder.getAllUniqueVisits

VisitSchedulBuilder class has a getAllUniqueVisits method that provides some shortcuts to reduce the cruft you might have to do while creating scheduled visits. It mostly does the right thing, so you don't have to worry about its logic. However, if you think it is doing something you didn't intend, then you can replace it with your own implementation. Look up the code for more details.



5. Decision rule

  • Logical scope = Encounter (aka Visit), Subject, or Program Enrolment
  • Trigger = On completion of an form wizard before final screen is displayed
  • In designer = Form (RULES tab)
  • When to use = To create any additional observations based on all the data filled by the user in the form

Used to add decisions/recommendations to the form. The decisions are displayed on the last page of the form and are also saved in the form's observations.

Shape of params object:

{
	entity: {}, //it could be ProgramEncounter, ProgramEnrolment or Encounter depending on what type of form is this rule attached to.
 	entityContext,
  services,
  decisions: {
     	"enrolmentDecisions": [],
    	"encounterDecisions": [],
      "registrationDecisions": []
  } // Decisions object on which you need to add decisions. 
}

Shape of decisions parameter:

{
  "enrolmentDecisions": [],
  "encounterDecisions": [],
  "registrationDecisions": []
}

You need to add to decisions parameter's appropriate field and return it back.
Inside the function, you will build decisions using ComplicationsBuilder and push the decisions to the decisions parameter's appropriate field. The return value will be the modified decisions parameter. You can also choose to not use ComplicationsBuilder and directly construct the return value as per the contract shown below:

Shape of the return value

{
  "enrolmentDecisions": [<decision object>, ...],
  "encounterDecisions": [<decision object>, ...],
  "registrationDecisions": [<decision object>, ...]
}
The shape of <decision object>
{
  "name": "name of the decision concept",
  "value": <text> | <number> | <date> | <datetime> | <name of anwer concepts in case of Coded question>
}

Example

({params, imports}) => {
    const programEncounter = params.entity;
    const decisions = params.decisions;
    const complicationsBuilder = new imports.rulesConfig.complicationsBuilder({
        programEncounter: programEncounter,
        complicationsConcept: "Birth status"
    });
    complicationsBuilder
        .addComplication("Baby is over weight")
        .when.valueInEncounter("Birth Weight")
        .is.greaterThanOrEqualTo(8);
    complicationsBuilder
        .addComplication("Baby is under weight")
        .when.valueInEncounter("Birth Weight")
        .is.lessThanOrEqualTo(5);
    complicationsBuilder
        .addComplication("Baby is normal")
        .when.valueInEncounter("Birth Weight")
        .is.lessThan(8)
        .and.when.valueInEncounter("Birth Weight")
        .is.greaterThan(5);
    decisions.encounterDecisions.push(complicationsBuilder.getComplications());
    return decisions;
};
1950
1024

Decision rule output are displayed in system recommendations section.



6. Validation rule

  • Logical scope = Encounter (aka Visit), Subject, or Program Enrolment
  • Trigger = On completion of an form wizard before final screen is displayed
  • In designer = Form (RULES tab)
  • When to use = To provide validation error(s) to the user that are not specific to one form element but involved data in multiple form elements.

Used to stop users from filling invalid data

Shape of params object:

{
  entity: {}, //it could be ProgramEncounter, ProgramEnrolment or Encounter depending on what type of form is this rule attached to.
}

The return value of this function is an array with validation errors.

Example:

({params, imports}) => {
  const validationResults = [];
  if(programEncounter.getObservationReadableValue('Parity') > programEncounter.getObservationReadableValue('Gravida')) {
    validationResults.push(imports.common.createValidationError('Para Cannot be greater than Gravida'));
  }
  return validationResults;
};
2300



7. Enrolment Eligibility Check Rule

  • Logical scope = Subject
  • Trigger = On launch of program list when user enrols a subject into program
  • In designer = Program page
  • When to use = To restrict the programs which are available for enrolment based on subject's data (e.g. not allowing males to enrol in pregnancy programs)

Shape of params object:

{
  entity: {}//Subject will be passed here.
  program,
  services
}

Shape of the return value

The return value of this function should be a boolean.

Example:

({params, imports}) => {
  const individual = params.entity;
  return individual.isFemale() && individual.getAgeInYears() > 5;
};

Notes: The eligibility check is triggered only when someone tries to create a visit manually. Form stitching rules can override this default behaviour.

2300
1020

This list can be controlled by this rule.



8. Encounter Eligibility Check Rule

  • Logical scope = Subject or Program Enrolment
  • Trigger = On launch of new visit (encounter) list
  • In designer = Encounter page
  • When to use = To restrict the encounters which are available based on subject's full data (e.g. not showing postnatal care form if the delivery form has not been filed yet)

Used to hide some visit types depending on some data

Shape of params object:

{
  entity: {}//Subject will be passed here.
  services
}

Shape of the return value

The return value of this function should be a boolean.

Example:

({params, imports}) => {
  const individual = params.entity;
  const visitCount = individual.enrolments[0].encounters.filter(e => e.encounterType.uuid === 'a30afe96-cdbb-42d9-bf30-6cf4b07354d1').length;
  let visibility = true;
  if (_.isEqual(visitCount, 1)) visibility = false;
  return visibility;
};

Notes: The eligibility check is triggered only when someone tries to create a visit manually. Form stitching rules can override this default behaviour.

2300



9. Checklists rule

Used to add a checklist to an enrolment

Shape of params object:

{
  entity: {} //ProgramEnrolment
  checklistDetails": [] // Array of ChecklistDetail
  services
}

Example

({params, imports}) => {
  let vaccination = params.checklistDetails.find(cd => cd.name === 'Vaccination');
  if (vaccination === undefined) return [];
  const vaccinationList = {
    baseDate: params.entity.individual.dateOfBirth,
    detail: {uuid: vaccination.uuid},
    items: vaccination.items.map(vi => ({
      detail: {uuid: vi.uuid}
    }))
  };
  return [vaccinationList];
};



10. Work List Updation rule

  • Logical scope = Subject, Program Enrolment, or Encounters
  • Trigger = On display of system recommendation's page in form wizard
  • In designer = Main Menu
  • When to use = Stitch together multiple forms which can be filled back to back

The System Recommendations screen of Avni can be configured to direct a user to go to the next task to be done. Typically, if a new encounter is scheduled for a person on the same day, then the system automatically prompts the user to perform that encounter.
This is performed using worklists. A worklist is an array of work items.

The WorkListUpdation rule is used to customize this flow. The WorkLists object is passed on to this rule just before showing the System Recommendations screen. Any modification in the worklists is applied immediately to the flow.

You can add a new WorkItem anywhere after the currentWorkList.currentItem.

Shape of params object:

{
  worklists: {},
  context: {},
  services
}

Example

https://gist.github.com/hithacker/d0fe89107b974797fbb11ced1feda146

2210



11. Subject summary rule

  • Logical scope = Subject registration
  • Trigger = Before the opening of the subject dashboard profile tab.
  • In designer = Subject (Subject Summary Rule)
  • When to use = Display important information in the subject's profile. It can be used to show the summary if there are no programs.

This rule is very similar to the Enrolment summary rule. Except its scope is the Subject's registration.

Shape of params object:

{ 
  individual: {}, // Subject model
}

You need to return an array of summary objects from this function.

Shape of the summary object:

{
  "name": "name of the summary concept",
  "value": <text> | <number> | <date> | <datetime> | <concept list in case of Coded question>
}

Example:

({params, imports}) =>  {
    const summaries = [];
    const individual = params.individual;
    const mobileNumber = individual.findObservation('Mobile Number'); 
    if(mobileNumber) {
      summaries.push({name: 'Mobile Number', value: mobileNumber.getValueWrapper()});
    }
    return summaries;
};



12. Hyperlink menu item rule

  • Logical scope = User
  • Trigger = When More navigation is opened in the mobile app
  • In designer = Coming very soon...
  • When to use = When a dynamic link has to be provided to the user (these links cannot be specific to subjects)

Shape of params object:

{
  user: {}, // User
  moment: {}, // moment. note other parameters are not supported yet
}

User: https://github.com/avniproject/avni-models/blob/master/src/UserInfo.js

You need to return a string that is the full URL that can be opened in a browser.

Example:

({params}) => {return `https://reporting.avniproject.org/public/question/11265388-5909-438e-9d9a-6faaa0c5863f?username=${encodeURIComponent(user.username)}&name=${encodeURIComponent(user.name)}&month=${params.moment().month() + 1}&year=${params.moment().year()}`;}



13. Message rule

  • When to use = To configure sending Glific messages
  • Logical scope = User, Subject, General and Program Encounter, Program Enrolment
  • Trigger =
    • For User : Only on creation of an User .
    • For Subject, General and Program Encounter, Program Enrolment : On every save (create / update)
  • In designer = "User Messaging Config", "Subject Type" , "Encounter type" and "Programs" page

Message Rule can be configured only when 'Messaging' is enabled for the organisation. Its configuration constitutes specifying following details:

  • Name identifier name for the Message Rule
  • Template Used to indicate the Skeleton of the message with placeholders for parameters
  • Receiver Type Used to indicate the target audience for the Glific Whatsap message
  • Schedule date and time configuration should return the time to send the message.
  • Message content configuration should return the parameters to be filled in the Glific message template selected under 'Select Template' dropdown.

Any number of message Rules can be configured.

Example configuration:

Say, 'common_otp' Glific message template is 'Your OTP for {{1}} is {{2}}. This is valid for {{3}}.' If we want to send a OTP message that says 'Your OTP for receiving books is 1458. This is valid for 2 hours.' to a student after 1 day of their registration, then we need to configure for student subject type as shown in the below image (Note the shape of the return objects):

'use strict';  
({params, imports}) => ({  
  scheduledDateTime: new Date("2023-01-05T10:10:00.000+05:30")  
});
'use strict';  
({params, imports}) => {  
  const individual = params.entity;  
  return {  
    parameters: ['Verify user phone number', '0123', '1 day']  
  }  
};

Shape of params object:

{
  entity: {}, //it could be one of User, Individual, General Encounter, ProgramEncounter or Program Enrolment depending on the type of form this rule is attached to
}

14. Dashboard Card Rule

The shape of dashboard card rule

{
  db: "the realm db instance",
  user,
  myUserGroups,
  // ruleInput object can be null
  ruleInput: {
    type: "string. see 14.1 below",
    dataType: "values can be Default or Range",
    subjectType: "SubjectType model object. The subject type of the subjects to query and display to the user",
    groupSubjectTypeFilter: {
      subjectType: "SubjectType. The group subject type to filter by"
    },
    observationBasedFilter: {
      scope: "string. See 14.2 below",
      concept: "Concept. the observation value being referred to by the filter value",
      programs: {
         "UUID of the program": "Program model object"
      },
      encounterTypes: {
         "UUID of the encounter type": "Encounter Type model object"
      }
    },
    // filterValue can be null or empty array when there are no filters chosen by the user
    filterValue: "value chosen by the user. the type of data depends on the type of the filter"
  }
}

Filter Value Shapes

Address Filter

{
  "uuid":"924674dc-d32b-4276-b7b5-fb782f5511f2",
  "name":"Kerala",
  "level":4,
  "type":"State",
  "parentUuid":null,
}

14.1) https://github.com/avniproject/avni-models/blob/8613b53edbf88e9b19150eda9e13da573e2a59ba/src/CustomFilter.js#L2

14.2) https://github.com/avniproject/avni-models/blob/8613b53edbf88e9b19150eda9e13da573e2a59ba/src/CustomFilter.js#L30



15. Manual Programs Eligibility Check Rule

This rule is used when the user fills a form based on which the eligibility of given program is determined by this rule.

Shape of Input Object

params: {
  entity: typeof SubjectProgramEligibility,
  subject: typeof Individual,
  program: typeof Program,
  services
},
imports: {}

Return

boolean

16. Edit Form Rule

This rule is used when the user tries to edit a form. If non-boolean value is returned in the value, or the rule fails, then it would be treated as true and edit will be allowed. To check the places where it is available, not available, & not applicable - https://avni.readme.io/docs/rules-concept-guide#edit-form-rule

Shape of Input Object

params: {entity, services, form, myUserGroups,user},
imports: {}

Shape of return object

{
  editable : {
    value: true/false
    messageKey: 'foo'
  } 
}

Using params.db object when writing rules

In many of the rules params db object is available to query the offline database directly. The db object is an instance of type Realm on which objects is first method that will get called. This returns Realm Results instance, on which one may further call the filtered method one or more times each time returning realm results. Realm result a list with each item being of type (model object's schema name) originally passed in objects method.

Realm Query Language Reference - https://www.mongodb.com/docs/realm/realm-query-language/

Difference between filter and filtered

filtered method is like running SQL query executed closer or in the database process and hence it orders of magnitude faster than filter - which is JavaScript method ran by constructing model object for each item is JS memory and then passing it through the filter function. As much as possible filtered should be used for best performance and user experience.

Example of filtered

({params}) => {
  const db = {params};
  return db.objects("Individual").filtered(`voided = true AND subjectType.name = "Foo"`);
}

Using service methods in the rules

Often, there is the need to get the context of implementation beyond what the models themselves provide. For example, knowing other subjects in the location might be necessary to run a specific rule. For such scenarios, Avni provides querying the DB using the services passed to the rules.

The services object looks like this

{
    individualService: '',
}

Right now only individual service is injected into all the rules. One method which is implemented right now returns an array of subjects in a particular location. The method looks like the following, it takes address-level object and subject type name as its parameters and returns a list of all the subjects in that location.

getSubjectsInLocation(addressLevel, subjectTypeName) {
  const allSubjects = ....;
  return allSubjects;
}

Note that this function is not implemented for the data entry app and throws a "method not supported" error for all the rules when run from the data entry app.

Service methods available are:

Example

The view-filter rule is for the subject data type concept that displays all the subjects of type 'Person' in the passed location.

'use strict';
({params, imports}) => {
  const encounter = params.entity;
  const formElement = params.formElement;
  const statusBuilder = new imports.rulesConfig.FormElementStatusBuilder({encounter, formElement});
  const individualService = params.services.individualService;
  const subjects = individualService.getSubjectsInLocation(encounter.individual.lowestAddressLevel, 'Person');
  const uuids = _.map(subjects, ({uuid}) => uuid);
  statusBuilder.showAnswers(...uuids);
  return statusBuilder.build();
};

Using other group/household individuals' information in the rules

Say, an individual belongs to a group A. Sometimes, there is a need to use data of other individuals in the group A. For example, to auto-populate caste information in an individual's registration form (say, when navigated to individual's registration form when tried to add a member to group/household A), we might need to know the caste information of other individuals in that group/household. For such scenarios, Avni provides a way to access group object from params.entityContext.

Example

The below rule is for the case when an individual's concept named Caste needs to be auto-populated based on other member's data in the same group.

'use strict';
({params, imports}) => {
  const individual = params.entity;
  const moment = imports.moment;
  const formElement = params.formElement;
  const _ = imports.lodash;
  let visibility = true;
  let value = null;
  let answersToSkip = [];
  let validationErrors = [];
  
  const groupSubject = params.entityContext.group;
  if(groupSubject.groupSubjects.length > 0) {
     const ind = params.entityContext.group.groupSubjects[0].memberSubject;
     const caste = ind.getObservationReadableValue('Caste');
     value = caste; 
  }
  
  return new imports.rulesConfig.FormElementStatus(formElement.uuid, visibility, value, answersToSkip, validationErrors);
};

Types of rules and their support/availability in Data Entry App

Not supported / applicableSupported via rules-serverSupported in browser
Decision ruleEnrolment eligibility check ruleForm Element Rule
Dashboard Card ruleEncounter eligibility check ruleForm Element GroupRule
Checklists ruleVisit schedule ruleEnrolment Summary Rule
Work list updation ruleMessage ruleValidation rule
Hyperlink menu item ruleHyperlink menu item rule

Types of rules and their support/availability in transaction data upload

Not supportedSupported via rules-serverNot Applicable
Message ruleVisit schedule ruleHyperlink menu item rule
Decision ruleEnrolment Summary Rule
Validation ruleForm Element GroupRule
Form Element Rule
Encounter eligibility check rule
Enrolment eligibility check rule
Hyperlink menu item rule
Work list updation rule
Checklists rule
Dashboard Card rule

What’s Next