---
version: "25.11"
variant: "Cloud"
language: "en"
---
# NexJ CRM Developer and Deployment Documentation

Welcome to the online help for release 25.11 for cloud-delivered NexJ CRM, for developers and deployment specialists.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Accessing JSON objects using a RESTful connection

You can access a JSON object by providing a URL to the RESTful endpoint or you can configure a channel to communicate with the endpoint.

You can view available commands for a metaclass in either of the following ways:

* By displaying the Swagger UI for the metaclass in the Swagger UI. Swagger UI is an open source tool that displays API resources in an easy-to-read format.

* By retrieving the raw Swagger Interface Definition Language (IDL) for the metaclass and pasting the raw data into an API Editor.

Info  
The Swagger UI only supports NexJ CRM version 8.4 and later.

## Accessing JSON objects using Swagger UI

You can use Swagger UI to view all available operations for a metaclass in an easy-to-read format.

To view metaclass operations in Swagger UI, you must add the following mixin to your environment file in NexJ Studio and then restart your application server:

`<Mixin namespace="nexj:openapi" version="0"/>`

To access JSON objects using Swagger UI:

1. Enter the following URL in your browser:

   http://\<application-server\>/\<context-root\>/openAPI.html

   The Swagger UI opens for `Person` metaclass.

2. Edit the filter criteria in the search box to view the Swagger UI for the desired metaclass. For example, to view the Swagger UI for the `LocalUser` metaclass, enter `json?filter=<LocalUser>`. Separate multiple classes with a space, for example,` json?filter=<LocalUser Person>`.

   You can specify additional criteria in the `json?filter` function. Use:

   * An asterisk (\*) to filter using wildcard functionality, for example, `json?filter=*User`.

   * A forward slash followed by a numeric value (`/n`) to include associated classes, where `n` is the level, for example, `json?filter=LocalUser/0`. The default value for `n`, if not specified, is `1`.

   * A greater than sign (`>`) to include the parent metaclasses of the specified class, for example,` json? filter=LocalUser>.`

   * A less than sign (`<`) to include the derived metaclasses of the specified class, for example, `json? filter=LocalUser<.`

     Info  
     The relative order of `/n` and `<` matters. For example,` /n<` includes associated subclasses and `</n` does not.
3. Click **Explore** .

   Swagger UI displays the operations you can access for the specified metaclass.

You have accessed JSON objects through a REST endpoint.

## Accessing JSON objects using an API Editor

You can access JSON objects by providing a URL to a RESTful endpoint. You can display the generated Swagger IDL in an API editor and also view and update data for the individual JSON objects defined by IDL.

To access JSON objects using an API Editor:

1. To get the Swagger IDL for a metaclass, enter the following URL in your browser:

   http://\<application-server\>/\<context-root\>/json?filter=\<filtervalue\>\&$indent

   Where:

   **application-server**

   Specifies the URL for a NexJ CRM instance.

   **context-root**

   Specifies the context root of the NexJ CRM instance, for example, \<nexj\>.

   **json?filter=\<filtervalue\>**

   Specifies filter criteria for the schema to be generated. For example, to specify that this service should be enabled for the `LocalUser` class, enter `json?filter=<LocalUser>`. Separate multiple classes with a space, for example, `json?filter=<LocalUser> Person`. You can specify additional criteria in the `json? filter` function. Use:

   An asterisk (\*) to filter using wildcard functionality, for example, json?filter=\*User.

   * A forward slash followed by a numeric value (/n) to include associated classes, where n is the level, for example, json?filter=LocalUser/0. The default value for n, if not specified, is 1.

   * A greater than sign (\>) to include the parent metaclasses of the specified class, for example, json?filter=LocalUser\>.

   * A less than sign (\<) to include the derived metaclasses of the specified class, for example, json?filter=LocalUser\<.

     Enables indenting of the RESTful response to make it easier to read the response.

   * The following URL returns the Swagger IDL for the LocalUser class on a local installation of NexJ CRM.

     http://\<localhost\>:7080/\<nexj\>/json?filter=\<LocalUser\>/0\&$indent

     The Swagger IDL is returned for the class in your browser. Copy the raw data of the Swagger output.

2. Paste the raw data into an API editor, such as Swagger Editor. In the editor, view the syntax for available commands. For example, view available commands for the `get LocalUser` command.

3. Using information from the API editor, enter a URL formatted to perform an operation using the RESTful API. For example, the following example returns instances of the `LocalUser` class.

   http://\<application-server\>/\<context-root\>/json/LocalUser?$indent

You have accessed JSON objects through a REST endpoint.

## Configuring channel security for RESTful connections

You can configure security for a RESTful connection by creating a channel that accesses the REST endpoint and a service that contains configurable parameters for the RESTful endpoint. For example, you can specify the authentication method and limit the classes which can be defined by the generated Interface Definition Language (IDL).

### Creating a service for a RESTful connection

Configure a service for a RESTful connection that contains configurable parameters for the RESTful endpoint and binds the service to the HTTP channel configured for the REST endpoint.

To create a service for a RESTful connection:

1. In NexJ Studio, in the **Integration** tab, select the **Services** tab.

2. Right-click and select **Services** .

   The New Services dialog opens.

3. In the **Name** field, provide a name for the service, for example, enter `JSONRESTService`. Click **Finish** .

   The New Service dialog closes and the service opens in the service editor.

4. Select the **Diagram** tab.

5. In the **Palette** , select **Script**.

6. Click a location on the line between the start and end of the service.

7. In the **Property Editor** tab, in the **General** tab, provide values in the following fields:

   **Name**

   Provide a script name. For example, enter `scrHandleRequest`.

   **Caption**

   Provide a script caption. For example, enter `Handle Request`.

8. In the **Source** tab, enter the following code:

   Scheme

       (define httpServer ((invocation-context)'getComponentInstance "HTTPServer.JSON"))

       (httpServer'pathOffset 1)

       ;additional httpServer properties can be set here
       (httpServer'documented #t)
       (httpServer'filter "<filtervalue>")
       (httpServer'scope "<scopetype>")
       (httpServer'indenting <#t>)

       (message
             (: body httpServer)
       )

   Where:

   **(httpServer'documented #t)**

   Specifies whether to add schema documentation to the schema that is generated when the service URL is accessed. Valid values are `#t` and `#f`. The default value is `#f`.

   **(httpServer'filter "<filtervalue>")**

   Specifies filter criteria for the generated schema. For example, to specify that this service should be enabled for the `LocalUser` class, enter (`httpServer'filter "<LocalUser>"`). Separate multiple classes with a space, for example, (`httpServer'filter "<LocalUser> Person"`). You can specify additional criteria in the `httpServer'filter` function. Use:

   • An asterisk (`*`) to filter using wildcard functionality, for example, `httpServer'filter="*User"`.

   • A forward slash followed by a numeric value (`/n`) to include associated classes, where `n` is the level, for example, `httpServer'filter="LocalUser/0"`. The default value for `n`, if not specified, is `1`.

   • A greater than sign (`>`) to include the parent metaclasses of the specified class, for example, `httpServer'filter="LocalUser>"`.

   • A less than sign (`<`) to include the derived metaclasses of the specified class, for example, `httpServer'filter="LocalUser<"`.  
   Info  
   The relative order of `/n` and `<` matters. For example, `/n<` includes associated subclasses and `</n `does not.

   **(** **httpServer'scope "<scopetype>"****)**

   Specifies the scope for the generated schema. Valid values are generic, state, and behavior. The default value is behavior:

   • `generic` sets the scope to generic schema.

   • `state` sets the scope to generic schema and metaclasses.

   • `behavior` sets the scope to generic schema, metaclasses, and events.

   **(** **httpServer'indenting <#t>****)**

   Specifies whether to indent the JSON response to make it easier to read the response. Valid values are `#t `and `#f`. The default value is `#f`.

   For example:

       ;additional httpServer properties can be set here
       (httpServer'documented <#t>)
       (httpServer'filter "<LocalUser>")
       (httpServer'scope "<behavior>")
       (httpServer'indenting <#t>)

9. Save and close the service.

You have created a service for RESTful connection. The following example shows the course for a complete sample service.

    <Service layout="startX:50;startY:50;endX:300;endY:50" stream="true">
       <Script caption="Handle Request" layout="y:50;x:130" name="scrHandleRequest"><!
    [CDATA[(define httpServer ((invocation-context)'getComponentInstance
     "HTTPServer.JSON"))

    (httpServer'pathOffset 1)

    ;additional httpServer properties can be set here
    (httpServer'documented <#t>)
    (httpServer'filter "<LocalUser>")
    (httpServer'scope "<behavior>")
    (httpServer'indenting <#t>)

    (message
          (: body httpServer)
    )]]></Script>
    </Service>

Next, bind the service to the HTTP channel configured for the REST endpoint.

The httpServer properties can also be set as parameters in the REST URL, for example, localhost:7080/nexj/ json?filter=LocalUser\&scope=generic. If the `httpServer'documented` or `httpServer'scope` parameter is specified both in the REST URL and the REST service, the REST URL parameter overrides the service. If the `httpServer'filter` parameter is specified in the service, the IDL can only access the classes specified in the service.

### Configuring a channel for a RESTful connection

You can configure security for a RESTful connection by creating a channel that accesses the REST endpoint.

You must first create a service to bind to the channel that you will configure for the RESTful endpoint.

To create a channel for a RESTful connection:

1. In NexJ Studio, in the**Integration** tab, select the **Channels** tab.

2. Right-click and select **New Channel** .

   The New Channel dialog opens.

3. In the**Name** field, provide a name for the channel. In **Channel Type** , select `HTTP`. Click` `**Finish** .

   The New Channel dialog closes and the channel opens in the channel editor.

4. In the **Overview** tab, provide values in the following fields:

   **HTTP Methods**

   Specify the HTTP methods that the service can perform on the resource. For example, select PUT to enable the service to update a resource in NexJ CRM.

   Info  
   The Send method must be set to false.

   **Content Type**

   Specify the default HTTP mime type of the body of the response or request content. For example, enter `application/json; charset=UTF-8`.

   **Authentication Component**

   Specify the authentication method, for example, enter `System.Authentication.Basic` or `System.Authentication.Perimeter`.  
   Info  
   Only basic authentication is supported with Swagger.
5. In the **Service Bindings** tab, click the **Add** button to add a new service binding.

6. In the **General** tab, provide values in the following fields:

   **Service**

   Specify the name of the service that the channel invokes, for example, `JSONRESTService`.

   **Output**

   Specify the name of this channel, for example, `JSONRESTChannel`.

7. Save and close the channel.

You have configured channel security for a RESTful connection. Changes are reflected in the **Source** tab as shown in the following example:

    <HTTP authComponent="System.Authentication.Basic" binary="false"
     contentType="application/json; charset=UTF-8" delete="true" get="true" put="true"
     send="false">
      <ServiceBindings>
         <ServiceBinding output="JSONREST" service="JSONREST"/>
      </ServiceBindings>
    </HTTP>

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Activity Management technical overview

Activity management is all about keeping track of entities' activities. The entities are typically People, Users and Companies, although activities may also be associated with opportunities and campaigns. The core class/table for activities is Act. It stores Schedule Items, Tasks, Notes, and Documents. It is related to other entities through "participation" class/tables. Users through the UserParticipation class/table and Entities through the EntityParticipation class/table. Acts can be related through followUps and ActivityPlans. In addition, Acts can have Attachments as well as links to Accounts, Campaigns, Leads, Opportunities, Products, and Service Requests.

![image2018-9-13_20-1-38.png](https://documentation.nexj.com/__attachments/a_c45bb0826eb46370df18d97a4a5b019b5ca5046105575653ab73c02f56fc75cd/image2018-9-13_20-1-38.png?cb=ef51d65a6d99bcfb037dc6d81857f93b)

* [Act](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Act)

* [ActAccount](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ActAccount)

* [ActAttachment](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ActAttachment)

* [ActAttachmentLink](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ActAttachmentLink)

* [ActionItem](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ActionItem)

* [BaseConfig](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#BaseConfig)

* [BaseOpportunity](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#BaseOpportunity)

* [Campaign](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Campaign)

* [Company](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Company)

* [Document](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Document)

* [DocumentMgrItem](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#DocumentMgrItem)

* [EntityParticipation](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#EntityParticipation)

* [ilm:Lead](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ilm:Lead)

* [Organization](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Organization)

* [Person](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Person)

* [Product](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Product)

* [ProductActLink](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ProductActLink)

* [ScheduleItem](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ScheduleItem)

* [ScheduleItemSeries](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ScheduleItemSeries)

* [ServiceRequest](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#ServiceRequest)

* [Task](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#Task)

* [TaskSeries](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#TaskSeries)

* [UserParticipation](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#UserParticipation)

* [UserPerson](https://community.nexj.com/documentation/reference/domain/latest/AllClasses.html#UserPerson)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding and removing client coverage group members

You can add or remove coverage group members from client coverage groups.

To add or remove group members from a client coverage group:

1. Navigate to the Coverage page.

2. In the **Coverage Groups** tab, select a client coverage group. A list of the current group members appears in the Coverage Group Members area.

3. In the Coverage Group Members area, click the **Select** button ![point2.png](https://documentation.nexj.com/__attachments/a_8375752089e94d7345e41053a127d0985e9193cbf7cc9ceb86634680dbc8b122/point2.png?cb=1824f439288a202d02b7a660844594bb) . The Select Coverage Group Members dialog opens.

4. Select a user from the list on the left, and click **Add** to move the user to the list on the right.

5. Assign a coverage role by double-clicking in the Coverage Role column for a contact and selecting a role from the drop-down list.

   Coverage roles are prepopulated for users who have a default client coverage defined.
6. When you are finished, click **OK**. The Select Coverage Group Members dialog closes.

You have modified coverage group members in a client coverage group.

When you add or remove a member of a client coverage group, the changes are cascaded to all clients with that coverage group.  
When you add or remove a member of a client coverage group, the changes are cascaded to all clients with that coverage group.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding and updating icons

Icons are specified as text characters, not as image files.

The material design icons used in the product are a subset of icons available from Google Material Design font (<https://material.io/resources/icons/>) and the open-source Material Design Icons font (<https://materialdesignicons.com/>).

To include material design icons in your application, you can use the three font files included in the afl project, `mdicons.ttf`, `mdicons2.ttf`, and `maticons.ttf`. These files are custom files created by NexJ and are provided in the `afl/mod/fonts` directory. The former two files are also mirrored in the `finance/mod/fonts` directory as `mdicons-finance.ttf` and `mdicons2-finance.ttf`.To view the exact contents of the `.ttf` files, you can use a font viewer tool such as <https://fontdrop.info/> or <http://torinak.com/font/lsfont.html>.

To include material design icons in your application, you can use the three font files included in the afl project, `mdicons.ttf`, `mdicons2.ttf`, and `maticons.ttf`. These files are custom files created by NexJ and are provided in the `afl/mod/fonts` directory. The former two files are also mirrored in the `finance/mod/fonts` directory as `mdicons-finance.ttf` and `mdicons2-finance.ttf`.Here are icons included in those three files.

[maticons.pdf](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-and-updating-icons.md)

[mdicons2.pdf](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-and-updating-icons.md)

[mdicons.pdf](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-and-updating-icons.md)

To view the exact contents of the `.ttf` files, you can use a font viewer tool such as <https://fontdrop.info/> or <http://torinak.com/font/lsfont.html>.

To include material design icons in your application, you can use the three font files included in the afl project, `mdicons.ttf`, `mdicons2.ttf`, and `maticons.ttf`. These files are custom files created by NexJ and are provided in the `afl/mod/fonts` directory. The former two files are also mirrored in the `finance/mod/fonts` directory as `mdicons-finance.ttf` and `mdicons2-finance.ttf`.To view the exact contents of the `.ttf` files, you can use a font viewer tool such as <https://fontdrop.info/> or <http://torinak.com/font/lsfont.html>.

For example, open the `maticons.ttf` file using the <https://fontdrop.info/> online tool. The list of all characters included in the font is displayed. Scroll until you see the icon that you want to use. For example, the icon used for contacts is ![afl_create_for_contact.png](https://documentation.nexj.com/__attachments/a_2c6e2cf6f12e4c3365e12bec902a34eac9ca45b251aaf9549cb19281b64086e8/afl_create_for_contact.png?cb=933909728a4b86b6bcc5834929517689) . When you hover or click the image, the Unicode number of E7FD is displayed. This is the number you can use when you refer to the icon.  
If you want to use an icon which is not contained in these files, you can include another font file in the `afl/mod/fonts` directory, and then refer to the names or Unicode code points when developing your application.

## Including icons

There are two ways to include icons in the application.

### Direct reference

Simply enter `icon:backup` or `icon:&#xE864;` in the `icon` property to refer to an icon included in these font files. For example, you can associate an icon with a workspace by defining the workspace with the following code:
Scheme

    <Workspace caption="IDS_HOME" icon="icon:&#xE88A;">

Performance tip  
To ensure optimal performance of the user interface, NexJ recommends that you use a direct reference to the icon whenever possible.

### Store icons in a string file

In the `icon.en.strings` file, define your icons in the following format, using either the code point or the name.
XML

    ids.icon.lock=icon\:\uE897
    ids.icon.user=icon:person_outline

The string ID should be a human-readable name that is meaningful in the context of the application. It is unrelated to either the code point of the name of the character in the font.

You can then refer to the string ID when you need to display an icon. For example, you can define the appearance of a composite label with the following code:
Scheme

    <Composite caption="ids.security" icon="lock" name="splitSecurity">

To change the icon, update the value in the strings file. You do not need to update each reference.

## Specifying tooltips for icons

When adding an icon to a table column, a list item, or a label, you can specify a tooltip. On desktop, users can see the tooltip by hovering over the icon. On mobile, users can see the tooltip by holding and pressing the icon.

To add a tooltip to an icon used as a column name, add the `tooltips` property to the column definition. For example:
Scheme

    <Column caption="ids.icon"  icons="template icon" name="colType" tooltips="colDescription"/>

In this example, `colDescription` is an attribute that is defined elsewhere.

To add a tooltip to an icon used with list items, add the tooltips property to the avatar definition. For example:
Scheme

    <Avatars codes="preferredStyle" icons="type icon" tooltips="addressFull"/>

In this example, `addressFull` is an attribute that is defined elsewhere.

If an icon is being used as a label, you can add a tooltip as long as the label does not also have a caption. To add a tooltip to an icon being used as a label, add the `tooltip` property to the label definition. For example:
Scheme

    <Label icon="ownerIcon" name="lblOwner" tooltip="&quot;ids.owner&quot;"/>

In this example, `ids.owner` is a string ID, for a string defined in a .strings file. The string ID must be enclosed in `&quot;` text. You can also reference string constants using the same format.  
If a label contains both a `caption` property and an `icon` property, it does not display correctly in the interface. An empty or blank image is displayed next to the caption. To correct this situation, remove the `icon` property from the label definition. For example, if the label definition is the following:
Scheme

    <Label caption="&quot;ids.batchUpdateCustomField.customFieldNotUnique&quot;" icon="&quot;warning_high&quot;" multiline="true" name="lblCautionReplaceMsg11"/>

change it to the following:
Scheme

    <Label caption="&quot;ids.batchUpdateCustomField.customFieldNotUnique&quot;" multiline="true" name="lblCautionReplaceMsg11"/>

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding client coverage groups

You can add client coverage groups and assign users to groups. After you add the group, business administrators manage the group in the Customize workspace in NexJ CRM, and users can assign the coverage group to contacts.

To add a new client coverage group:

1. Navigate to the Coverage page.

2. In the **Coverage Groups** tab, click the **Add** button ![plus.png](https://documentation.nexj.com/__attachments/a_5013ccbadeca21a2e1148b4c386e074b5ae2902ae3d6606c6ba2dd7601c8b190/plus.png?cb=f00f9185794f77b8aa6678864a807169) and select **Client Coverage**. The Add Coverage Group dialog opens.

3. Type the group's name in the **Name** field.

4. Add members to the group.

5. In the **Security** tab, you can change the view and edit security levels of this coverage group.

   You cannot modify the security levels for the coverage group after it has been created. If you want to restrict access to this coverage group, you must set it now as the default setting is **Public** for both view and edit security.
6. Click **OK**. The Add Coverage Group dialog closes.

A client coverage group is added to the system. The client coverage group also displays in NexJ CRM in the **Client Coverage Groups** tab in the Customize workspace.

## Related links

[Adding and removing client coverage group members](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-and-removing-client-coverage-group-members.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding communication types

You can add new communication types to use in NexJ CRM.

To add a new communication type:

1. Navigate to the Entity Codes page.

2. In the **Communication Types** tab, click the **Add** button ![add.png](https://documentation.nexj.com/__attachments/a_65b486c37638edc26feddc25f8428ed747660aaa9376ebe7c6d4a0030a2d1172/add.png?cb=f00f9185794f77b8aa6678864a807169) . The Add Communication Type dialog opens.

3. In the **Name** field, enter the name of the communication type that you want displayed in NexJ CRM.

4. In the **Description** field, enter an internal description of the communication type.

5. In the **Icon** field, select an icon to represent the communication type in NexJ CRM.

6. In the **Class** field, select a class, which tells the system what kind of information can be entered for this communication type.

   NexJ CRM has two functions available for communication information: `Send Email` and `Go To Web Site`, which will only be enabled for the `EmailAddress` and `URL` classes, respectively.
7. In the **Default to** **field** field**,**select a default communication type to assign this communication type to.

8. Enable the **End User Read Only** checkbox to prevent this communication type from being accessible in NexJ CRM.

   With this enabled, the only way that this communication type can be added to or removed from a profile is by modifying the database directly.
9. Enable the **Unique** checkbox to specify that a contact may only have a single instance of this communication type in their profile.

10. In the**Reference Name** field, enter a reference name for the communication type.

11. In the**Entity Types** tab, select entity types for which this communication type will be available. Selecting the **Pre-Populate** checkbox will make the communication type an available field in the Add Contact dialog for a new contact of that type.

12. Click **OK**. The Add Communication Type dialog closes.

The communication type is added and is displayed as specified in NexJ CRM.

## **Related links**

[Available communication type properties](https://documentation.nexj.com/crm-technical/25.11/Cloud/available-communication-type-properties.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding coverage roles to a coverage group

You can modify the roles that are available for client, event, and opportunity coverage groups.

To add coverage roles to a coverage group:

1. Navigate to the Coverage page and select the **Coverage Roles** tab.

2. Select the **Client Coverage Roles** , **Event Coverage Roles** , or **Opportunity Coverage Roles** subtab.

3. Click the **Select** button ![select.png](https://documentation.nexj.com/__attachments/a_449ca732071c772b0c18fe5c2088087fd7ecf46ee06bca499101ee880468767e/select.png?cb=1824f439288a202d02b7a660844594bb) .

   To create new roles, you must add values to the ROLEENUM enumeration.

   The Select Role dialog opens.
4. In the list on the left, select roles to add to the coverage group, then click **Add**. The roles are moved to the list on the right.

5. To restrict a role to only one user in a coverage group, select the **Unique** checkbox beside a role. For example, if you want allow users to be able to assign one manager role and multiple advisor roles to a coverage group, then make the manager role unique.

   When a role is unique, you can only assign one user to that role.
6. Click **OK**. The Select Role dialog closes.

You have added coverage roles to a coverage group.

## Related links

[Customizing enumerations](https://documentation.nexj.com/crm-technical/25.11/Cloud/customizing-enumerations.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding custom fields to the system

Add new custom fields to allow users to enter specific information for contacts in NexJ CRM. You can add text, number, currency, convertible currency, date, and relationship custom fields.

To add a custom field:

1. Navigate to the Entity Codes page.

2. In the **Custom Field Types** tab, click the**Custom Field Types** subtab.

3. Click the **Add** button ![add.png](https://documentation.nexj.com/__attachments/a_ce7d74127dc314e6212a2c90dbc75c520c4e820b5120114f8cd976fe515339ba/add.png?cb=f00f9185794f77b8aa6678864a807169) at the top of the list, and select a custom field type to define values that the field accepts:

   * **Text**

     Select to add a custom field that accepts text values.

   * **Number**

     Select to add a custom field that accepts numerical values.

   * **Currency**

     Select to add a custom field that accepts and formats numerical values using the currency defined when the field is created.

   * **Convertible Currency**

     Select to add a custom field that accepts and formats numerical values into the currency selected by users in NexJ CRM.

     You must also configure currency conversion for your system. For more information on configuring currency conversion, see [Configuring currency conversion](/crm-technical/25.11/Public-On-Prem/configuring-currency-conversion.md).

   * **Date**

     Select to add a custom field that accepts and formats date values. Users can type a date value or select a date value from a calendar.

   * **Relationship**

     Select to add a custom field that accepts a contact as a value and defines the contact as a relationship in the contact's hierarchy.

   The Add Custom Field dialog opens.
4. In the **Detail** tab, in the **Name** field, enter the custom field's display name.

5. \[Optional\] In the **Description** field, enter a description of the custom field. The description displays in NexJ CRM.

6. In the **Unit** field, select the currency to apply to the field.

   The **Unit** field is only available for currency and convertible currency custom fields. Users cannot change the currency in NexJ CRM.

7. In the **Group** field, select the custom field group to apply to the custom field.

   The custom field group defines the contact types that you can add the field to, and the tab in which the field displays on the Contacts workspace.

8. Select any of the following checkboxes:

   * **Active**

     Select to make the custom field available in NexJ CRM.

   * **Unique**

     Select to allow users to add only one custom field of this type to a contact.

   * **End User Read Only**

     Select to prevent the custom field from being accessible in NexJ CRM.

     If enabled, the only way that this custom field can be added or removed is by modifying the database directly.

   * **Allow Quick Pick Only**

     Select to only allow users to add values to a custom text field that contains quick picks by selecting from a drop-down list. Otherwise, users can also enter a text value in the custom field.

     The **Allow Quick Pick Only** checkbox is only available for text custom field types.

   * **Hierarchy Only**

     Select to specify that custom relationship fields do not display in a contact's profile, but only as relationships in the contact's hierarchy. Consider naming the custom field using the title of the position that you want to represent in the relationship hierarchy. For example, for a custom field that represents a lawyer-client relationship, name the field `Lawyer`.

     The **Hierarchy Only**checkbox is only available for relationship custom field types.

9. In the **Security** tab, you can change the view and edit security levels of this custom field.

   You cannot modify view and edit security levels for the custom field after it has been created. If you want to restrict view or edit access to this custom field, you must set it now. The default view and edit settings are Public.

10. Click **OK**. The Add Custom Field dialog closes.

The custom field is added to the system.  
Newly added custom fields may not always be immediately available for use in creating rules in the **Rules** tab on the Customize workspace. In some cases, the system cache may need to be refreshed before the fields appear. Clear the cache by going to the System page in NexJ System Admin Console and using the **Clear All Caches**option.

## **Related links**

[Adding quick picks to a custom text field](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-quick-picks-to-a-custom-text-field.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding entity types

You can add new entity types and specify details, communication and address types, categories, and custom fields for the entity type. You can also enable or disable certain portlets for the entity type.

To add a new entity type:

1. Navigate to the Entity Codes page.

2. In the **Entity Types** tab, click the **Add** button ![plus.png](https://documentation.nexj.com/__attachments/a_5f34d5862284bea62747f7822bb8456c9ca26fd49a38766a741846361733420e/plus.png?cb=f00f9185794f77b8aa6678864a807169) , and select a base entity type.

   The base entity type cannot be changed after the entity type is created.

   The Add Entity Type dialog opens.
3. In the **Detail** tab, provide a name and other display settings for the entity type. You can also define the locations in which users can add contacts from the entity type in NexJ CRM.

4. In the **Communication Types** tab, select available and default communication types for the entity type.

5. In the **Address Types** tab, select available and default address types for the entity type.

6. In the **Categories** tab, select default category groups and categories for the entity type.

7. In the**Custom Field Types** tab, select default custom field types and groups for the entity type.

8. Click **OK**. The Add Entity Type dialog closes.

9. In the **Customize Portlets** tab, enable or disable portlets for the entity type.

10. Click the **Save** button ![save_list.png](https://documentation.nexj.com/__attachments/a_6b5260753ccf2dbc3673907ef88151ea53f7313e429995d2c666440e4ec19fd7/save_list.png?cb=560e786e7a831ae62a920e6e14f1ba47) .

The new entity type is added to the Entity Types list and is available in NexJ CRM depending on the properties set for the entity type.

## Related links

[Modifying entity types](https://documentation.nexj.com/crm-technical/25.11/Cloud/modifying-entity-types.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding enumerations

You can add custom enumerations. Custom enumerations contain the custom field picklists classification.

To add a new enumeration:

1. Navigate to the Enumerations page.

2. In the **Enumerations** list, click the **Add** button ![plus.png](https://documentation.nexj.com/__attachments/a_6e08fa9e3d2ac1a321567cfe0a31064f6627c94bb88bc8cc82efc62e42143c4a/plus.png?cb=f00f9185794f77b8aa6678864a807169) . The Add Enumeration Type dialog opens.

3. In the **Detail** tab, in the**Type Code** field, enter a type code that uniquely identifies the enumeration.

4. In the **Description** field, enter a description for the enumeration that describes what it will be used for. The **Classification** field contains Custom Field Picklists as the enumeration's default classification. This cannot be changed.

5. In the **Values** tab, add the necessary values to the enumeration.

6. Click**OK**. The Add Enumeration Type dialog closes.

## **Related links**

[Adding values to enumerations](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-values-to-enumerations.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding merge fields

You can add custom merge fields to provide either a broader or a more specific range of personalized documents in NexJ CRM.

To add a new merge field:

1. Navigate to the Document Codes page.

2. In the**Merge Fields** tab, click the **Add** button ![add.png](https://documentation.nexj.com/__attachments/a_84df3ce6db13e79e1318d84b0a749a2b4bb6d349ea6aa8b1984e5e2c611e288c/add.png?cb=f00f9185794f77b8aa6678864a807169) . The Add Merge Field dialog opens.

3. Complete the fields as appropriate:

   * **Token Name**

     Specifies the placeholder name for the merge field. This should be a meaningful indication of what information will be populated.

   * **Description**

     Provides a brief description of the merge field's intended content.

   * **Category**

     Specifies the subject area that the merge field is applicable to.

   * **Expression**

     Provides a calculated code value that defines the merge field and determines what data is populated from the database.

   * **Custom Token**

     Maps a third-party mailer template token to a NexJ CRM merge field for batch printing.

     The **Custom Token** field is only available if you have enabled the Birthday Mailer feature.

4. Click **OK**. The Add Merge Field dialog closes.

Your merge field is added and available to use in personalized documents.

## Related links

[Enabling application features](https://documentation.nexj.com/crm-technical/25.11/Cloud/enabling-application-features.md)

[Support for third-party mailer template tokens](https://documentation.nexj.com/crm-technical/25.11/Cloud/providing-custom-tokens-and-templates-for-mailer-d.md#supportforthirdpartytemplatetokens)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding quick picks to a custom text field

*Quick picks*enable users to select values from a drop-down list for a custom text field.

When you add quick picks to a custom text field, you define the values that are available in the drop-down list.  
You can only enable quick picks for custom text fields.

To add quick picks to a custom text field:

1. Navigate to the Entity Codes page.

2. In the **Custom Field Types** tab, in the**Custom Field Types** subtab, select a custom text field.

3. On the right side, next to the **Detail** tab, select the **Quick Picks** tab.

4. To add an item, click the **Add** button ![add.png](https://documentation.nexj.com/__attachments/a_2ab9b014aef77914068977c2e570460a6bc3dd7e261712eb63dbc362ce8f9c57/add.png?cb=f00f9185794f77b8aa6678864a807169) at the top of the list. The Add Quick Pick Enumeration Code dialog opens.

5. In the **Caption** field, enter a name for the quick pick value. The caption name displays to users in quick pick drop-down lists in NexJ CRM.

6. Select or clear the **Active** checkbox to define whether the item displays in the quick pick list or not.

7. Click **OK**. The Add Quick Pick Enumeration Code dialog closes.

8. Add additional quick pick values as necessary.

9. To move an item up and down in the quick picks list, select it and use the ![arrow_order_up.png](https://documentation.nexj.com/__attachments/a_fbe74210239c4070472a9a18130a71411c33f403496e4b0e645244fccf422d85/arrow_order_up.png?cb=2581743d6c82f8293fcf5d1b48ebe360) and ![arrow_order_down.png](https://documentation.nexj.com/__attachments/a_310000ddf721629e30d64cfb3785afbecf57534608d79209e9c307160edb63c5/arrow_order_down.png?cb=2a7807cb7e336489d0ef4bea2af2c066) buttons at the top right of the list. To delete an item, select the item in the quick picks list and click the **Delete** button ![delete.png](https://documentation.nexj.com/__attachments/a_95ef1ea6406fd67dc53783a3ffa101b8d5019ca2247d5986f3668c4be9628533/delete.png?cb=30875f22e030a04bc8f71d77743f98aa) .

The quick pick values are added to the system. Users can now use the quick picks in this custom field to specify values. If you selected **Allow Quick Picks Only** in the **Detail** tab, users can only choose quick pick values. Otherwise, users can choose quick picks or input their own values into the custom text field.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding synchronization targets for Exchange Server

Use NexJ Admin Console to add and configure target servers that NexJ CRM will synchronize with.  
Note  
After a new Exchange server has been added to this list, it cannot be removed or deleted. However, the server can be disabled, so that no synchronization occurs.

To add and configure Exchange Server synchronization targets:

1. In NexJ Admin Console, navigate to the Synchronization page.

2. In the **Synchronization** tab, in the Targets list, click **Server** **\>** **Exchange EWS** .

   A new Exchange server appears in the Targets list and the new server's details appear in the right side of the page with the default values filled in.

3. Click the**Edit** button ![edit_service_requests.png](https://documentation.nexj.com/__attachments/a_2af3e33030cc6a50fee969f5be6c31ff0a478a6a3bc7c786fefbf5d2eadf02ea/edit_service_requests.png?cb=8e902598f0104662b506f8570153442d) on the top right of the page and complete the dialog that appears to modify the fields as needed:

   **Name**

   Specifies the name of the target Exchange server.

   **Host**

   Specifies the fully qualified domain name of the Exchange server, for example, of the form host.domain.ext.

   **NexJ URL**

   Specifies the URL of NexJ anonymous HTTP context root, http://{\<server\>}{\<Anonymous HTTP Server URL\>}. For example, http://appserver.nexjsystems.local/nexj/anon.

   **{\<server\>}**

   Specifies the address of the load balancer or node, followed by an optional port. Generally, a load balancer would listen on the default port (80 for HTTP and 443 for HTTPS), so specifying a port may be unnecessary. However, if {server} is a node, specify a port since nodes are usually not listening on default ports.

   **\<Anonymous HTTP Server URL\>}**

   Specifies the URL of the NexJ anonymous HTTP context root. The root is defined for the .server or .environment file under the **Communication** tab in NexJ Studio. The default value is `/nexj/anon`.  
   Info  
   If the Anonymous HTTP Server URL is set to a full URL, use the same URL for NexJ URL.

   **Version**

   Specifies the Exchange Server version. By default, a server will be added with the version set to Exchange Server 2016.

   **Subscription Type**

   Specifies whether NexJ CRM uses the push or streaming mode to subscribe for notifications from Exchange Server 2013 and Exchange Server 2016. For more information, see [Specifying the notification communication method](https://documentation.nexj.com/crm-technical/25.11/Cloud/specifying-the-notification-communication-method.md).

   **Method**

   Specifies the security mechanism. By default, a server will be added with the method set to Impersonation.

   **Keep items older than**

   Specifies whether NexJ CRM should delete items that are deleted in Outlook. For example, if the value is 30, then any non-recurring meeting or task with a start or end date more than 30 days ago will not be deleted from NexJ CRM if it is deleted from Outlook.  
   As of NexJ CRM 22.06, negative values are supported for the **Keep items older than** field. This provides a path to prevent future non-recurring items deleted in Microsoft Exchange from being deleted in NexJ CRM.

   **Maximum attachment size, kilobytes**

   Specifies the maximum attachment size that can be synchronized. Attachments exceeding this limit will not be synchronized.

   **Maximum allowed request rate per second**

   Specifies the maximum allowed request rate per second.

   **Maximum allowed subscriptions per minute**

   Specifies the maximum number of subscription requests allowed each minute by the subscription batch job. Increasing this value will allow more subscription requests to the Exchange server and complete the subscription batch job quicker, but will also increase the load on the Exchange server.

   **Maximum number of subscriptions per batch**

   Specifies the maximum number of subscription requests per batch used by the subscription batch job. Increasing this number will read in more subscriptions in one batch, but will cause the Exchange server to receive larger groups of subscriptions at one time. Choose a reasonably small number to more evenly distribute subscription traffic to the Exchange server.

   **Inbound**

   Enables or disables inbound synchronization.

   **Outbound**

   Enables or disables outbound synchronization.

   **Use Autodiscover Service**

   Enables configuring a recurring batch job to trigger the Autodiscover service.

   **Use Internal Client Access Servers**

   Enable to try the internal server returned by the Autodiscover service. For more information, see [Configuring auto discovery settings](https://documentation.nexj.com/crm-technical/25.11/Cloud/configuring-global-exchange-synchronization-option.md#configuringautodiscovery).

   **Subscription Renewal**

   Specifies the interval length between subscription renewals. Once the time has elapsed and no notifications or heartbeats have been received from the Exchange server for a specific folder, the system automatically renews its subscription to the Exchange server folder. The maximum value of this field is 2880. The minimum value is 2.

   **Timer**

   Specifies the length between executions of the timer script, which keeps the subscription running.

   **Subscription Batch Job Timeout**

   Specifies the interval, in minutes, used to detect a stale Exchange subscription batch job.

   **Maximum Streaming Error Count**

   Specifies the threshold for streaming errors. When you select **Streaming** in the **Subscription Type** field, inbound and outbound synchronization are automatically deactivated when the number of errors encountered while processing the stream exceeds the threshold. By default, synchronization is deactivated when 60 errors have been reached.

   **EWS Sender**

   Specifies the channel used for sending notifications. The default channel is ExchangeEWSSender.

   Info  
   The **Channels** fields are linked to the connections file during configuration.

   **Notification**

   Specifies the channel used for receiving notifications. The default channel is ExchangeEWSNotificationReceiver.  
   Info  
   The **Channels** fields are linked to the connections file during configuration.

   **Global Options**

   Specifies global settings such as batch synchronization, auto discovery, and restrictions. Click the**Configure** button to open the Global Exchange Synchronization Options dialog through which you can configure global options. For more information, see [Configuring global Exchange synchronization options](https://documentation.nexj.com/crm-technical/25.11/Cloud/configuring-global-exchange-synchronization-option.md).

   **Item Types**

   Displays the default configuration for synchronizing new tasks and schedule items to NexJ CRM. The Template column displays the action item template that will be used when a new activity (either task or schedule item) is imported from the Exchange server and created in NexJ CRM.
4. Click **OK** to close the dialog and save your changes.

The server is now configured.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding system defined queries

Add new system defined queries as needed.

To add a new system defined query:

1. In NexJ System Admin Console, navigate to the Filters page.

2. In the **Predefined Filters** tab, click the **Add** button ![add.png](https://documentation.nexj.com/__attachments/a_893c0b5da1cf2f2032eb709910aca2b8fc6f832a6f895897bde508ba3cd8eaf0/add.png?cb=f00f9185794f77b8aa6678864a807169) . The Add Predefined Filter dialog opens.

3. In the **Filter Name** field, enter a name that will be the query's identifier when setting the subject area of the contact list in NexJ CRM. This is a mandatory field.

4. In the **Description** field, enter a meaningful description of the system defined query.

5. In the **Display Order** field, enter a number that represents the list order in which the new system defined query will appear when setting the subject area of the contact list in NexJ CRM.

   A lower number means that the query appears higher in the list.
6. In the **Filter Value** field, enter an expression of condition(s) for the query to meet.

7. In the**Subject Areas** field, select an entity type to provide context for the new system defined query. This field is set to `Contacts` by default.

8. Add privilege groups to make the query accessible in NexJ CRM.

9. Click**OK**. The Add Predefined Filter dialog closes.

The new system defined query is added, and is visible to the appropriate privilege groups in NexJ CRM.

## Related links

[Managing access to queries](https://documentation.nexj.com/crm-technical/25.11/Cloud/managing-access-to-queries.md)

[Entering query expressions](https://documentation.nexj.com/crm-technical/25.11/Cloud/entering-query-expressions.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Adding values to enumerations

You can only add values to enumerations that have a classification of System, Run Time Editable, or Custom Field Picklists.

To add a value to an enumeration:

1. Navigate to the Enumerations page.

2. In the **Enumerations** list, select an enumeration. The details appear in the detail area to the right of the page.

3. Click the **Values** tab, and then click the **Add** button ![plus.png](https://documentation.nexj.com/__attachments/a_e9ecf3db1da02ef88d0cc23ce85f98c0b5744b0d195f672941bead5dc639ac6e/plus.png?cb=f00f9185794f77b8aa6678864a807169) . The Add Enumeration Value dialog opens.

4. Complete the fields as needed and add a caption to the value.

5. Click **OK**. The Add Enumeration Value dialog closes.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# AFL

The Application Foundation Layers (*AFL*) are designed for developing multi-tier applications and is a key part of the NexJ CRM interface definition. It consists of multiple frameworks, toolkits and libraries organized into dynamically loaded reusable modules.

The AFL modules include the following subsystems:

* A cross-platform UI framework using a semantic, mostly-declarative API, with client-side state management and a stateless server tier to support scalability.

* A cross-platform UI toolkit providing common UI controls.

* A DOM-based UI toolkit plug-in for browser rendering with theme support.

* Android and iOS bridges for running ECMAScript in mobile devices, dynamic bidirectional communication with native code, and implementing native UI rendering without the need for proxies, stubs, or IDL.

* A context bus plug-in for portal-driven communication between UI components.

* The NexJ Portal API bridge.

* An RPC framework with asynchronous batch request support for counteracting network latency.

* UI plug-ins for accessing the NexJ SOA and Object models through RPC.

* A business logic framework that provides a Service-Oriented Architecture for implementing server-side functionality.

* A configurable component factory framework with dependency injection.

* A dynamic module loader that implements a complete run-time application assembly to ensures only the required dependencies are retrieved.

* Object-oriented and functional programming libraries, including mix-in support.

* A date/time calculation API with complete time zone support and tools for automatic time zone database generation.

* An internationalization (I18N) framework with configurable formatters and parsers for common data types, and support for custom data formatting and parsing of inflected languages and bidirectional fonts.

* A framework for cross-platform unit testing, integration testing, and end-to-end testing. This includes UI testing through event simulation and visual verification.

* A build framework allowing application code organization into multiple source code projects.

All of these subsystems are reusable, support concurrent programming, and (with the exception of platform-specific bridges and UI renderers) can run in a server.

AFL also has the following technical features:

* It is implemented with *ECMAScript* 3, (*JavaScript).* This allows for portability. For example, AFL can run in a web browser or in an application server such as *node.js*.

* It features a small code footprint. A UI application using core UI elements with HTML DOM rendering will typically require AFL modules with fewer than 10 000 lines of source code, and a download side of less than 40 KB. The optional time zone database is only an additional 20 KB.

* The only external dependency is for certain components in the test framework and the native bridges.

* Excellent coexistence with 3rd-party frameworks, toolkits and libraries: no standard objects are modified (except for emulation of missing standard ECMAScript 5.1 methods); no global namespace pollution (only `define` is added as a global variable); no custom inheritance data structures, HTML DOM-based UI can be attached to existing web pages.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# AFL modules

Below is a list of the core modules of AFL. Plugins are indicated by the table of contents level.

## nexj/async

Asynchronous programming toolkit

Returns the default asynchronous chain processor \`async\` object.

Tests are in nexj/async/test.

**dependencies**

* nexj/sys

## nexj/b64

Base 64 string conversion

Returns the b64 object.

Tests are in nexj/b64/test.

**dependencies**

* nexj/sys

* nexj/i18n

* exports

## nexj/date/olson

Olson time zone plug-in. Module "nexj/date/olson/db" is automatically generated by "nexj/build/olson".

Returns the \`olson\` object and overrides \`date.Zone.find\`.

**dependencies**

* "nexj/sys",

* "nexj/date",

* "nexj/date/olson/db"

### nexj/date/olson/db

Olson time zone database, auto-generated by nexj/build/olson from

<https://data.iana.org/time-zones/releases/tzcode2021a.tar.gz>.

**dependencies**

* none

### nexj/build/olson

Olson time zone database module generator

Downloads the Olson database and converts it to an AMD module.

**dependencies**

* nexj/sys

* https

* fs

* tar

* zlib

* nexj/ssl/ca

## nexj/cvt

Formatting and parsing of values for display

Returns the \`cvt\` object

Tests are in nexj/cvt/test.

**dependencies**

* "nexj/sys",

* "nexj/ref",

* "nexj/i18n"

### nexj/cvt/date

Plug-in for formatting and parsing of dates

Extends the \`cvt\` object from nexj/cvt with a \`date\` converter

Tests are in nexj/cvt/date/test

**dependencies**

* "nexj/cvt",

* "nexj/sys",

* "nexj/ref",

* "nexj/date",

* "nexj/i18n"

### nexj/cvt/link

Plug-in for formatting and parsing of hyperlinked text

Extends the \`cvt\` object from nexj/cvt with a \`link\` converter.

Tests are in nexj/cvt/link/test.

**dependencies**

* "nexj/sys",

* "nexj/cvt"

### nexj/cvt/phone

Plugin for formatting and parsing of phone number strings based on country code.

Currently acts as a wrapper for the libphonenumber library

(<https://hg.nexj.com/framework.cgi/libphonenum>, maintained in a separate project):

<https://github.com/googlei18n/libphonenumber>

Uses isValidNumber() when possible as it is more strict than isPossibleNumber().

The unit specifies the ISO country code, which can be overridden by the string value's

country code if the string is prefixed with a plus sign.

see java:nexj.core.view.converter.PhoneConverter

Extends the \`cvt\` object from nexj/cvt with a \`phone\` converter.

Tests are in nexj/cvt/phone/test.

**dependencies**

* "nexj/sys",

* "nexj/cvt",

* "nexj/i18n",

* "ext/libphonenumber"

## nexj/date

Date/time manipulation API

Returns a \`date\` object.

Tests are in \`nexj/date/test\`

**dependencies**

* "nexj/sys"

## nexj/dom

HTML DOM manipulation

Returns the \`dom\` object.

Tests are in \`nexj/dom/test\`

**dependencies**

* "nexj/sys"

## nexj/end

AMD bootstrapping - this must be the last explicitly included script file

## nexj/err

Error handling

Exports the \`err\` object.

**dependencies**

* none

## nexj/http

HTTP utilities

Exports the \`http\` object.

**dependencies**

* "nexj/sys",

* "nexj/i18n"

## nexj/http/req

HTTP requestor components

Exports the \`req\` object.

**dependencies**

* "nexj/sys",

* "nexj/i18n",

* "nexj/http",

* "url",

* "require"

## nexj/http/svr

HTTP server components

Exports the \`svr\` object.

**dependencies**

* "nexj/sys",

* "nexj/http",

* "url",

* "require"

### nexj/push/http

Push server plug-in with an HTTP implementation

Extends the \`svr\` object with push capabilities with \`svr.push\` and \`svr.pushd\`.

**dependencies**

* "nexj/sys",

* "nexj/push",

* "nexj/http",

* "nexj/http/svr",

* "url",

* "crypto",

* "os"

### nexj/http/svr/json

JSON-RPC request processor interceptor plug-in for the HTTP server

Extends the \`svr\` object with \`svr.json\`.

**dependencies**

* "nexj/http",

* "nexj/http/svr",

* "nexj/soa/json",

* "querystring"

## nexj/i18n

Internationalization database

Tests are in nexj/i18n/test.

**dependencies**

* "nexj/sys"

## nexj/log

Logging

Returns the \`log\` object.

**dependencies**

* "nexj/sys"

## nexj/nlp

Natural Language Processing functions

Returns the \`nlp\` object.

Tests are in nexj/nlp/test

**dependencies**

* "nexj/sys",

* "nexj/rpc/obj",

* "nexj/i18n"

## nexj/push

Server push processor

Returns the \`push\` object.

**dependencies**

* "nexj/sys",

* "nexj/soa"

## nexj/ref

Object references

Returns the \`ref\` object.

Tests are in nexj/ref/test.

**dependencies**

* "nexj/sys"

## nexj/rpc

RPC interfaces

Returns the \`rpc\` object with \`rpc.http\`.

Tests are in rpc/test.

**dependencies**

* "nexj/sys",

* "nexj/i18n"

### nexj/rpc/bat

RPC batching plug-in

Extends the \`rpc\` object with batching capabilities.

Tests are in nexj/rpc/bat/test

**dependencies**

* "nexj/rpc",

* "nexj/sys"

### nexj/rpc/obj

Object RPC plug-in

Extends the \`rpc\` object with \`rpc.obj\` and returns \`obj\`.

Tests are found in \`nexj/obj/test\`

**dependencies**

* "nexj/rpc",

* "nexj/ref",

* "nexj/sys",

* "nexj/i18n",

* "nexj/date"

#### nexj/rpc/obj/nlp

NLP support plug-in

Extends the \`rpc.obj\` object with \`nlp\` capabilities.

**dependencies**

* "nexj/rpc/obj",

* "nexj/nlp",

* "nexj/sys"

#### nexj/rpc/obj/scm

Scheme object support plug-in

Extends the \`rpc.obj\` object with \`scm\` (scheme) capabilities.

**dependencies**

* "nexj/rpc/obj",

* "nexj/sys",

* "nexj/b64",

* "nexj/log",

* "nexj/scm"

### nexj/rpc/soa

SOA RPC plug-in

Extends the \`rpc\` object with \`rpc.soa\` and returns the \`soa\` object.

Tests are in nexj/rpc/soa/test

**dependencies**

* "nexj/rpc",

* "nexj/ref",

* "nexj/sys",

* "nexj/i18n"

## nexj/rpc/push

Push RPC client

Returns the \`push\` client object.

**dependencies**

* "nexj/sys",

* "nexj/rpc"

## nexj/scm

Scheme scripting engine

Exports the \`scm\` object.

**dependencies**

* "nexj/sys",

* "nexj/i18n",

* "nexj/cvt",

* "nexj/b64",

* "nexj/date",

* "nexj/log",

* "nexj/utf8"

## nexj/scm/dbg

Scheme scripting engine debugger

Exports the \`dbg\` object.

**dependencies**

* "nexj/sys",

* "nexj/i18n",

* "nexj/rpc",

* "nexj/scm"

## nexj/soa

SOA service registry and related functionality

Exports the \`soa\` object.

Tests are in nexj/soa/test.

**dependencies**

* "nexj/sys",

* "nexj/i18n"

### nexj/soa/file

SOA definition loader plug-in

Extends the \`soa\` object with file loading functionality.

**dependencies**

* "nexj/sys",

* "nexj/soa",

* "fs"

### nexj/soa/json

SON-RPC parser/formatter

Exports the \`json\` object.

Tests are in nexj/soa/json/test.

**dependencies**

* "nexj/sys",

* "nexj/ref",

* "nexj/soa",

* "nexj/err"

### nexj/soa/rpc

RPC plug-in for SOA

Enhances the \`soa\` object with \`soa.rpc\` and the \`svr\` object with \`svr.def\` and returns the \`rpc\` object.

**dependencies**

* "nexj/sys",

* "nexj/soa",

* "nexj/svr",

* "nexj/http",

* "nexj/http/req"

### nexj/soa/xml

JSONML/JSON SOA definition conversion plug-in

Extends the \`soa\` object and returns the \`xml\` object.

Tests are in nexj/soa/xml/test.

**dependencies**

* "nexj/sys",

* "nexj/soa",

* "nexj/xml"

## nexj/ssl/ca

CA certificates

Installs certificates in https.globalAgent.options.ca.

**dependencies**

* "https"

## nexj/stat

Performance statistics plugin

Returns the \`stat\` object.

**dependencies**

* "nexj/sys",

* "nexj/rpc/obj",

* "nexj/utf8"

## nexj/svr

Server infrastructure

Returns the \`svr\` object.

**dependencies**

* "nexj/sys",

* "require"

## nexj/sys

Object-oriented and functional programming

Tests are in nexj/sys/test.

**dependencies**

* none

### nexj/amd

AMD loader extension plug-in

Extends the \`define\` object from nexj/sys with AMD functions such as \`load\`.

**dependencies**

* nexj/sys

* nexj/rpc

## Testing

### nexj/test/dom

HTML DOM test tools

Exports the \`test\` object.

**dependencies**

* "nexj/sys"

### nexj/test/rpc

RPC test tools

Exports the \`test\` object.

**dependencies**

* "nexj/sys"

* "nexj/rpc"

### nexj/test/rpc/obj

Mock Object RPC utilities

Returns the \`respond\` object.

**dependencies**

* "nexj/sys"

### nexj/test/rpc/soa

RPC utilities for mock SOA services

Returns the \`mock\` object.

**dependencies**

* "nexj/soa/json"

### nexj/test/ui

Platform-independent UI framework test tools

**dependencies**

* none

#### nexj/test/ui/dom

DOM-specific plug-ins for UI test tools

Extends the nexj/test/ui \`test\` object and the \`dom\` object for testing.

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/dom",

* "nexj/test/ui",

* "qunit/fixture"

## nexj/ui

Platform-independent UI framework

Returns the \`ui\` object.

CSS is in nexj/ui.css.

**dependencies**

* "nexj/sys",

* "nexj/cvt",

* "nexj/val",

* "nexj/ref",

* "nexj/rpc",

* "nexj/i18n"

### nexj/ui/core

Core view plug-in

Extends the \`ui\` object with various core views.

Tests are in nexj/ui/core/tests.

CSS is in nexj/ui.core.css

**dependencies**

* "nexj/ui",

* "nexj/sys",

* "nexj/i18n",

* "nexj/cvt",

* "nexj/ui/state"

#### nexj/ui/core/dom

Core DOM renderers.

Borrows from:

<http://alistapart.com/article/expanding-text-areas-made-elegant> (elastic text box technique)

Tests are in nexj/ui/core/dom/test.

**dependencies**

* "nexj/ui",

* "nexj/ui/dom",

* "nexj/dom",

* "nexj/sys",

* "nexj/cvt",

* "nexj/i18n"

### nexj/ui/obj

Object model plug-in

Extends the \`ui\` object with object model capabilities e.g. ObjectCollection.

Tests are in nexj/ui/obj/test.

**dependencies**

* "nexj/sys",

* "nexj/ref",

* "nexj/ui",

* "nexj/rpc",

* "nexj/rpc/obj",

* "nexj/ui/req"

### nexj/ui/dom

DOM view renderer plug-in

Extends the \`ui\` object with rendering capabilities and returns the \`ren\` object.

Tests are in nexj/ui/dom/test

**dependencies**

* "nexj/ui",

* "nexj/dom",

* "nexj/sys",

* "nexj/i18n",

* "nexj/cvt"

### nexj/ui/amd

UI AMD loader plug-in

Extends the \`sys.define\` object with \`define.load.parameterizers.ui\`.

**dependencies**

* "nexj/ui",

* "nexj/amd"

#### nexj/ui/amd/dom

DOM-specific dynamic AMD loader plug-in

Extends the \`sys.define\` object with \`define.load.loaders.dom\`.

**dependencies**

* "nexj/sys",

* "nexj/rpc",

* "nexj/dom",

* "nexj/ui/amd"

### nexj/ui/angular

Angular framework adapter

See afl developer guide.

**dependencies**

* "nexj/sys",

* "nexj/ref",

* "nexj/ui",

* "nexj/i18n"

#### nexj/ui/angular/dom

Angular DOM renderers

**dependencies**

* "nexj/ui/angular",

* "nexj/sys",

* "nexj/ui",

* "nexj/ui/dom",

* "nexj/dom"

### nexj/ui/bus

Event bus/portal API plug-in

Returns the \`bus\` object.

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/ui/core"

#### nexj/ui/bus/dom

DOM plugin for the context bus/portal API

**dependencies**

* "nexj/sys",

* "nexj/i18n",

* "nexj/ui",

* "nexj/ui/dom",

* "nexj/dom",

* "nexj/ui/bus"

#### nexj/ui/bus/symphony

Symphony framework bus implementation.

Adds the ability to use the AFL bus in a Symphony module. "nexj/ui/bus/symphony"

must be referenced in a controller or module. Additionally for modules, "nexj:bus" is added

to the application initialization code e.g. {imports: \["ui", CONTROLLER, BUS\]}).

Information about Symphony and the Symphony Client Extension API is found at

<https://extension-api.symphony.com/>

Installs appropriate bus overrides for the symphony client.

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/ui/symphony",

* "nexj/ui/bus"

### nexj/ui/chart

Chart view plug-in

Adds the Chart view.

The type of chart is defined by the mode property ("line", "bar", etc).

Keys determine x values, values determine y values. Hybrid charts

(e.g., column with line) are created by setting an array for the values property.

For heat maps or bubble charts x and y are defined by an array of keys.

Color or bubble size are determined by the values property.

The series property is used to partition the data into separate groups

producing multiple lines or columns for one y-axis in one chart.

The HTML tags \<b\>, \<strong\>, \<i\>, \<em\>, \<br/\> and \<span\> are supported

in caption or hint strings; other tags are removed by the chart library.

Additional related modules

* nexj/ui/chart/3d

* nexj/ui/chart/3d/dom

* nexj/ui/chart/dom

* nexj/ui/chart/dom/test

* nexj/ui/chart/exotic

* nexj/ui/chart/exotic/dom

* nexj/ui/chart/export

* nexj/ui/chart/export/dom

* nexj/ui/chart/funnel

* nexj/ui/chart/funnel/dom

* nexj/ui/chart/guage

* nexj/ui/chart/guage/dom

* nexj/ui/chart/heatmap

* nexj/ui/chart/heatmap/dom

* nexj/ui/chart/treemap

* nexj/ui/chart/treemap/dom

CSS is in nexj/ui.chart.css and other files for various views.

**dependencies**

* "nexj/ui",

* "nexj/sys",

* "nexj/async",

* "nexj/i18n",

* "nexj/cvt"

### nexj/ui/classic

Common dependencies for the classic framework

**dependencies**

* "nexj/ui/mda/mvc",

* "nexj/rpc/bat"

### nexj/ui/date

Date view plug-in

Additional related modules

* nexj/ui/date/dom

* nexj/ui/date/dom/test

* nexj/ui/date/test

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/cvt",

* "nexj/i18n",

* "nexj/date",

* "nexj/cvt/date",

* "nexj/ui/core"

### nexj/ui/dbg

Provides recording capabilities

Additional related modules

* nexj/ui/dbg/dom

**dependencies**

* "nexj/sys",

* "nexj/ui/sim",

* "nexj/ui",

* "nexj/log",

* "exports",

* "nexj/ui/core"

### nexj/ui/entrance

Entrance (login page) view plug-in

Additional related modules

* nexj/ui/entrance/dom

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/cvt",

* "exports",

* "nexj/ui/core"

### nexj/ui/file

File view plug-in

Additional related modules

* nexj/ui/file/dom

* nexj/ui/file/dom/test

* nexj/ui/file/test

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/i18n",

* "nexj/cvt",

* "nexj/ui/picker"

### nexj/ui/filter

Filter view plug-in

Additional related modules

* nexj/ui/filter/dom

**dependencies**

* "nexj/ui",

* "nexj/sys",

* "nexj/i18n",

* "nexj/ui/picker"

### nexj/ui/picker

Picker views plug-in

Additional related modules

* nexj/ui/picker/dom

* nexj/ui/picker/dom/test

* nexj/ui/picker/test

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/i18n",

* "nexj/ui/core"

### nexj/ui/query

Query model plug-in

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/rpc",

* "nexj/rpc/bat"

### nexj/ui/react/dom

React DOM renderer

Exports the \`rren\` object.

**dependencies**

* "nexj/sys",

* "nexj/ui/dom",

* "react-dom",

* "exports"

### nexj/ui/req

Requestor model plug-in

Tests are in nexj/ui/req/test.

**dependencies**

* "nexj/sys",

* "nexj/ref",

* "nexj/ui",

* "nexj/i18n",

* "nexj/rpc",

* "nexj/rpc/bat"

### nexj/ui/richtext

Rich-Text Editor view plug-in

Tests are in nexj/ui/richtext/test.

Rendering is in nexj/ui/richtext/dom and associated /test modules.

**dependencies**

* "qunit",

* "nexj/sys",

* "nexj/ref",

* "nexj/ui",

* "nexj/dom",

* "nexj/ui/richtext/test",

* "ext/ckeditor",

* "nexj/test/dom",

* "nexj/ui/richtext/dom"

### nexj/ui/schedule

Schedule view plug-in

Texts are in nexj/ui/schedule/test.

Rendering is in nexj/ui/schedule/dom and associated /test modules.

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/cvt",

* "nexj/i18n",

* "nexj/date",

* "nexj/cvt/date",

* "nexj/ui/core",

* "nexj/ui/date"

### nexj/ui/sim

Platform-independent UI event simulation framework

Additions modules

* nexj/ui/sim/dom

* nexj/ui/sim/dom/test

* nexj/ui/sim/ev

* nexj/ui/sim/ev/dom

* nexj/ui/sim/ev/dom/test

* nexj/ui/sim/test

**dependencies**

* "nexj/sys",

* "nexj/async",

* "nexj/ui"

### nexj/ui/soa

SOA model plug-in

Tests are in nexj/ui/soa/test.

**dependencies**

* "nexj/sys",

* "nexj/ref",

* "nexj/ui",

* "nexj/rpc",

* "nexj/rpc/soa",

* "nexj/ui/req"

### nexj/ui/stat

Statistics dialog mixin

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/i18n",

* "nexj/ui/core"

### nexj/ui/state

UI state plug-in

Extends the \`ui\` object with stateful capabilities.

**dependencies**

* "nexj/ui",

* "nexj/sys",

* "nexj/i18n",

* "nexj/rpc/obj"

### nexj/ui/symphony

Symphony framework client extension API.

The Client Extension API is an ECMAScript library that consists of services containing methods

that allow developers to build apps that extend and interact with Symphony's user interface.

Information about Symphony and the Symphony Client Extension API is found at

<https://extension-api.symphony.com/>

Rendering is specified in nexj/ui/symphony/dom.

**dependencies**

* "ext/symphony"

### nexj/ui/table

Data table view plug-in

Additional modules

* nexj/ui/table/dom

* nexj/ui/table/dom/test

* nexj/ui/table/test

**dependencies**

* "nexj/sys",

* "nexj/ui",

* "nexj/i18n",

* "nexj/ui/core"

### nexj/ui/test

Qunit testing support

**dependencies**

* "qunit",

* "nexj/ui",

* "nexj/sys",

* "nexj/cvt",

* "nexj/ref",

* "nexj/i18n"

### nexj/ui/tree

Tree view plug-in

Additional modules

* nexj/ui/tree/dom

* nexj/ui/tree/dom/test

* nexj/ui/tree/test

**dependencies**

* "nexj/ui",

* "nexj/sys",

* "nexj/i18n"

## nexj/utf8

UTF-8 string conversion

Exports the \`utf8\` object.

**dependencies**

* none

## nexj/val

Validators

Exports the \`val\` object.

**dependencies**

* "nexj/sys",

* "nexj/i18n"

## nexj/xml

XML/JSONML processing

Exports and enhances the \`xml\` object.

**dependencies**

* "nexj/sys",

* "nexj/i18n",

* "exports"

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# afl-portal

Project: afl-portal

Name: NexJ Portal

Model namespace: nexj:model:portal

Module name: portal

**Portal** provides the core capabilities of the portal framework including:

* Portal Context/Event Bus and Global Variables

* Workspace, Navigator, Tab, Page, and Portlet Management

* Left Nav Bar

* Drawer management

* User based visibility

A Portal has multiple Tools, Workspaces, Drawers, and a single Broker Portlet. Tools expose functionality in the Top App Bar. Workspaces have an optional Navigator with multiple Tab layouts. Only one Tab is active at a time. Drawers can pop out important information like notifications, previews, or conversations on the right hand side. The Broker Portlet is not visible, but provides global controller scripting capabilities. Portlet Refs can host MODL Declarative Portlets, custom AFL Javascript Portlets, or iFrame in URL content.

![image2018-9-17_22-49-55.png](https://documentation.nexj.com/__attachments/a_f3811a6211cd775210de58aab74a9ffb63ff1f014883781a67a132c48a95d00c/image2018-9-17_22-49-55.png?cb=ef078b6edb25b9c9d51765bd22b4148d)

Screen Shot of a running Portal Application

![image2018-9-17_23-0-52.png](https://documentation.nexj.com/__attachments/a_4ccae52ea30f8b24dfc70837c8df1866499c3521b5504171f0b37ca8791c5860/image2018-9-17_23-0-52.png?cb=9f639100b5b5f06927a3072e6ef49a0e)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# AFL portlets

AFL portlets and controls let you work directly with JavaScript, CSS, and HTML. They leverage AFL core libraries for portal lifecycle management, data binding, context bus management, control development, internationalization and more.  
AFL - Application Foundation Layers  
AFL is a JavaScript platform for building applications. It provides the following subsystems:

* Cross-platform UI framework using semantic, mostly-declarative API, with client-side state management and a stateless server tier support for scalability.

* Cross-platform UI toolkit providing common UI controls.

* DOM-based UI toolkit plug-in for rendering on web browsers, with theme support.

* Context bus plug-in for portalized communication between UI components.

* NexJ Portal API bridge.

* RPC framework with asynchronous batch request support for counteracting network latency.

* Business logic framework providing a Service-Oriented Architecture (SOA) for implementing server-side functionality.

* Date/time calculation API with complete time zone support and tools for automatic time zone database generation.

* Internationalization (I18N) framework with configurable formatters and parsers for common data types and support for custom data formatting and parsing, languages with changing word forms and bidirectional scripts.

* And more.

Among other things, we use it to build UI.

Among other things, we use it to build UI.You can read more about its capabilities in the [AFL Developer Guide](https://nexj.atlassian.net/wiki/spaces/PUBDEV9x/pages/397051196/25.09+Application+Foundation+Layers+AFL+developer+reference).

Among other things, we use it to build UI.

## Portlets

A portlet is a UI view (control) that can contain child controls and supports the portal bus. With AFL portlets, you can build any portlet pattern provided by MDA portlets (lists, details, charts, and so on), but since you are simply working with JavaScript, CSS, and JavaScript libraries, you have more flexibility to implement less common patterns.  
MDA portlet rendering  
MDA portlets, that is, those defined with the Model Definition Language, are rendered as AFL portlets before they are sent to the browser. They are defined in simple XML elements that specify the portlet layout, binding, and behaviors, then rendering code creates AFL JavaScript that runs in your browser. You can see this by inspecting the application source with your browsers developer tools.

An AFL portlet is really just a module that returns a portlet control that, in turn, typically contains a card control, with logic, and bindings, and other controls.
JavaScript

    return function() { 
    	return ui("Portlet", {children: [ui("Card", {...
    };

AFL portlets are typically saved in `mod/app/<portletName>.js` and are available at `<server-root>/ui/<portletName>`. Any CSS rules specific to the portlet are saved alongside the module in `mod/app/<portletName>.css`.

## Controls

AFL controls are modules that contain a view definition (properties, behaviors) and a renderer to generate the control's HTML, CSS, and JavaScript. Renderers may use AFL libraries or React. The AFL core libraries contain a rich set of pre-built controls (many are in the \`nexj/ui\` and \`nexj/ui/core\` modules). If you have the demo app running, try looking at <http://localhost:7080/training/ui/demo> for examples of some of the pre-built controls. You will see charts, cards, tabs, and complex composite controls like the schedule.  
For React Developers  
For React developers, think component when you hear module.

AFL portlets and control definitions are simply modules. The following section discusses their structure.

AFL portlets and control definitions are simply modules. The following section discusses their structure. A detailed discussion is available in the [AFL Developer Guide](https://nexj.atlassian.net/wiki/spaces/PUBDEV9x/pages/397051196/25.09+Application+Foundation+Layers+AFL+developer+reference) in the modular programming section.

AFL portlets and control definitions are simply modules. The following section discusses their structure.

## Module structure

Modules begin with their name and a list of dependencies. The module dependency system is based on the Asynchronous Module Definition (AMD) specification.
JavaScript

    /** Sample Module */ 
    define("app/mysample", ["nexj/sys", "nexj/ui", "nexj/ui/core", "nexj/ui/obj", "nexj/ui/bus"], 
    	function(sys, ui) {"use strict"; 
    		return function() { 
    			return ui("Portlet", {children: [ui("Card", {...
    		};
    	}
    );

A module always contains the define(); function. It takes three parameters:

* The module name, for example, `"app/mysample"` (must match the file name of the module in your project's mod folder. "/" in the name represents either the folder structure or a "." in the name).

* Dependencies, which is a list of modules and module mix-ins that this one depends on

* A function that takes the dependent modules as parameters and returns a function that returns a view (typically a portlet for portlet modules).

In this example, the module name is "`app/mysample"` and the module should be saved in the `mod/app/mysample.js` file.

The dependencies are `["nexj/sys", "nexj/ui", "nexj/ui/core", "nexj/ui/obj", "nexj/ui/bus"]`

The function takes `sys` and `ui` as parameters which map to the contents returned from` "nexj/sys", "nexj/ui"`. The other dependent modules are mix-in extensions for these three modules. The function returns a function that returns a` ui("Portlet", ...` object. This portlet object has some layout properties and a collection of child controls.

These controls, in turn, have their own module definitions with a view definition and a renderer. These are maintained in separate files by convention. The typical arrangement is:

* `mod/nexj/ui.mySampleControl.js` - where the control's view is maintained. This includes the control's properties and behaviors.

* `mod/nexj/ui.mySampleControl.dom.js` - where the control's renderer is maintained. A control's renderer can be coded using:

  * The AFL dom renderer which uses JSONML for the layout,

  * Or the AFL React renderer, which allows you to write your rendering, and most of your control, in React.

* mod/nexj/ui.mySampleControl.css - where any control-specific css rules are maintained

This arrangement is for modularity only. The view and rendering code may be maintained in the same .js file, if desired.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# AFL UI automation

* [Custom metadata configuration](https://documentation.nexj.com/crm-technical/25.11/Cloud/custom-metadata-configuration.md)
* [Data validation methods](https://documentation.nexj.com/crm-technical/25.11/Cloud/data-validation-methods.md)
* [Environment setup and customization](https://documentation.nexj.com/crm-technical/25.11/Cloud/environment-setup-and-customization.md)
* [Functions for testing automation](https://documentation.nexj.com/crm-technical/25.11/Cloud/functions-for-testing-automation.md)
* [Recording and playback](https://documentation.nexj.com/crm-technical/25.11/Cloud/recording-and-playback.md)
* [Semantic view paths](https://documentation.nexj.com/crm-technical/25.11/Cloud/semantic-view-paths.md)
* [Troubleshooting UI automation](https://documentation.nexj.com/crm-technical/25.11/Cloud/troubleshooting-ui-automation.md)
* [UI unit test structure](https://documentation.nexj.com/crm-technical/25.11/Cloud/ui-unit-test-structure.md)
* [Writing a UI unit test](https://documentation.nexj.com/crm-technical/25.11/Cloud/writing-a-ui-unit-test.md)
* [Parallelizing unit test execution](https://documentation.nexj.com/crm-technical/25.11/Cloud/parallelizing-unit-test-execution.md)
* [Third-party web applications](https://documentation.nexj.com/crm-technical/25.11/Cloud/third-party-web-applications.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Alarm

## Overview

Use the alarm control to define the contents of the window that opens when a user receives an activity-reminder notifications such as a 10-minute warning of an upcoming meeting.

See [Configuring alarms](https://documentation.nexj.com/crm-technical/25.11/Cloud/configuring-alarms.md) to see how to configure alarms using push notifications.

## Example

The following example is taken from AlarmList.layout.
XML

          <CustomView head="true" name="ctrlAlarms" type="md:Alarm">
             <Models>
                <Collection attributes="alarmTime alarmOverdueStatus (act icon title summaryStringDateTimes (template defaultSnoozeTime) (snoozeTimes value caption))" name="alarms" order="((act summaryDueTime) . #t)" value="(@)">
                   <Bind name="subtitles" value="alarmOverdueStatus"/>
                   <Bind name="titles" value="act title"/>
                   <Bind name="descriptions" value="act summaryStringDateTimes"/>
                   <Bind name="icons" value="act icon"/>
                   <Bind name="snoozeTimes" value="act snoozeTimes"/>
                   <Bind name="defaultSnoozeTimes" value="act template defaultSnoozeTime"/>
                </Collection>
             </Models>
             <Configuration>
                <Properties clearCaption=""idsf.AlarmNotificationButtons.dismiss"" clearEvent=""alarmClear"" completedCaption=""idsf.AlarmNotificationButtons.complete"" completedEvent=""markCompleted"" defaultSnoozeTimes="alarms defaultSnoozeTimes" descriptions="alarms descriptions" icons="alarms icons" items="alarms" reminderCaption=""idsf.AlarmSnooze.RemindMeAgain"" snoozeCaption=""ids.Snooze"" snoozeEvent=""alarmSnooze"" snoozeTimes="alarms snoozeTimes" subtitles="alarms subtitles" titles="alarms titles"/>
             </Configuration>
          </CustomView>

The following events are configurable for this control. Their event handlers need to be implemented in the layout or dialog metadata.

* alarmClear - Dismisses a single reminder

* alarmClearAll - Dismisses all reminder

* alarmSnooze - Snoozes a reminder based on "snoozeTimes" property

* sysClose - Closes the alarm dialog

* markCompleted - Marks an activity as completed and dismisses the reminder.

### Explanation

For details regarding this control and available configurations, see the "md:Alarm" control defined in the finance-md project.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Appearance and themes

* [Capitalization rules](https://documentation.nexj.com/crm-technical/25.11/Cloud/capitalization-rules.md)
* [Color scheme](https://documentation.nexj.com/crm-technical/25.11/Cloud/color-scheme.md)
* [Currency formatting](https://documentation.nexj.com/crm-technical/25.11/Cloud/currency-formatting.md)
* [Elevation](https://documentation.nexj.com/crm-technical/25.11/Cloud/elevation.md)
* [Adding and updating icons](https://documentation.nexj.com/crm-technical/25.11/Cloud/adding-and-updating-icons.md)
* [Product and system icons](https://documentation.nexj.com/crm-technical/25.11/Cloud/product-and-system-icons.md)
* [UI themes and CSS](https://documentation.nexj.com/crm-technical/25.11/Cloud/ui-themes-and-css.md)
* [Typographic scale](https://documentation.nexj.com/crm-technical/25.11/Cloud/typographic-scale.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application components

Application level components are displayed or available to be used in the same location in every workspace. These include the navigation panel which allows users to navigate between workspaces; the global tools which are not workspace-specific, such as notifications; and the universal floating action button.  
* [Application drawer](https://documentation.nexj.com/crm-technical/25.11/Cloud/application-drawer.md)
* [Application toolbar](https://documentation.nexj.com/crm-technical/25.11/Cloud/application-toolbar.md)
[Floating action button](https://documentation.nexj.com/crm-technical/25.11/Cloud/floating-action-button.md)
* [Navigation drawer](https://documentation.nexj.com/crm-technical/25.11/Cloud/navigation-drawer.md)
* [Snackbar](https://documentation.nexj.com/crm-technical/25.11/Cloud/snackbar.md)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application drawer

## Description

The application drawer flies out from the right of the screen. It contains application-level content, such as the notification stream or conversations.

When the drawer is expanded, it contains a left arrow in the left top corner.

### When to use

Use if you want to provide you users with some content that is visible regardless of which workspace they are on.

### Variations

The contents of each drawer depend on the object and the context.

### Interactions

Open the drawer by clicking the appropriate button on the global application toolbar. The expanded drawer pushes the other content on the workspace to the left.

If the drawer is already open with one type of content and you click another button on the global toolbar, the content of the drawer is replaced.

To collapse the drawer, click the left arrow button in the top left corner on the drawer.

Depending on the contents on the drawer, various other commands can be available.

## Example

![Notifications Example.png](https://documentation.nexj.com/__attachments/a_62313fc5120a9899ef8a90593ccd74c6964ca9f9ada85de92f07b27cc4b6c44d/Notifications%20Example.png?cb=246d8fa943ecb7ea405a0d194f7e9e1e)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application Foundation Layers (afl)

Project: afl

Name: Application Foundation Layers

Model namespace: nexj:model:afl

Module name: \<none\>

Dependencies: enterprise

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application layers and the Model Description Language

Enterprise applications are full of both functional and technical complexity.

Enterprise applications are full of both functional and technical complexity. The model-driven engineering approach addresses this by separating \<complete later\>.

Enterprise applications are full of both functional and technical complexity.

## Model layers

We break enterprise applications design and implementation into the following layers:

* Presentation

* Business Model

* Persistence

* Security

* Integration

* Resources

* Deployment

## Model Description Language

Designs are declared in Models with the Model Description Language. It works well with source code control and large teams since it is just XML. It includes concepts for business logic, integration, persistence, presentation, security, testing, and deployment designs. The language is declarative - accelerating development by describing *what the application will do, rather than how* - and it fully documents the system design while reflecting exactly what is in production.
XML

    <Class description="Agents are best described as NLU (Natural Language Understanding) modules determining intent and entities.">
       <Attributes>
          <Attribute name="name" type="string"/>
          <Attribute name="description" type="string"/>
          <Attribute name="type" type="string"/>
          <Attribute name="icon" type="string"/>
          <Attribute collection="true" name="intents" reverse="agent" type="bot:Intent"/>
          <Attribute name="clientToken" type="string" value="(@ nlpStrategy clientToken)"/>
          <Attribute name="devToken" type="string" value="(@ nlpStrategy developerToken)&#10;"/>
          <Attribute collection="true" name="conversations" reverse="agent" type="bot:Conversation"/>
          <Attribute name="nlpStrategy" reverse="agent" type="bot:NLP"/>
          <Attribute collection="true" name="sources" reverse="agent" type="bot:Source"/>
       </Attributes>
    </Class>

*A sample declaration of a Class element*

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application pattern

The Application pattern organizes portlets, which are the basic UI building blocks (such as list, details, cards, charts and graphs) into a familiar structure and a useful application:

![image2018-10-25_13-53-12.png](https://documentation.nexj.com/__attachments/a_3c05dcd59135951c4b425f74ab15b869756b556b51f46e5f4e4077890e4ef099/image2018-10-25_13-53-12.png?cb=55cf7cdf23cd99cef06f920948f9f621)

This example is for Tablet structure, but the MODL platform makes the rendering for mobile, tablet, and desktop automatic. You simply specify your design and it will work on the target device. The structural components of an application are as follows:

    Portal
    --> Workspaces
        --> Navigator (optional list portlet)
        --> Pages
            --> Banner (optional summary portlet)
            --> Tabs (collection of portlets)
    --> Drawers
    --> Tools (slide-out portlets e.g. notifications)
    --> Broker (non-visible portlet for portal UI logic)

Each of the Navigator, Banner, Tabs, Drawers, some Tools and the Broker are structural references to Portlets (again, the basic UI building blocks). A configured and running application looks something like this.

![image2018-10-24_12-47-40.png](https://documentation.nexj.com/__attachments/a_e97590452de2c5f869e43ecf70f4d4eeda54ab9bc2c6368c1cceb82409c93785/image2018-10-24_12-47-40.png?cb=b770f14e7a93e7b5eb7101a957ead96c)

## Portal

A Portal declares an application's structure. Model Server serves the application at \*serverRoot\*/ui/portal/\*portalName\*. See <https://community.nexj.com/documentation/reference/modl/latest/Portal.html>.

![image2018-10-26_15-17-32.png](https://documentation.nexj.com/__attachments/a_a1bc4693a7eb380de40c126dad372db979239d0379e0c30214475e8c9a7320e7/image2018-10-26_15-17-32.png?cb=af15416fe47f19b1379d0403b87c1a9a)

### Navigation Drawer

The navigation drawer is automatic. It is populated by the list of workspaces in your portal. It allows users to navigate between different workspaces. For design background, see <https://material.io/design/components/navigation-drawer.html>. When expanded, the navigation drawer presents the workspaces' names and icons. When collapsed, it just presents the workspaces' icons. In addition, there is hamburger button on the top of the drawer.

![navigation_drawer_collapsed.png](https://documentation.nexj.com/__attachments/a_29f80854251003bf22035fe25e42d8c5bf38b25dbaa1b7b29927d3b5a53e60d4/navigation_drawer_collapsed.png?cb=18e80c8f73bf637aaf742a2a9013f8ef) expands to... ![navgiation_drawer_collapsed.png](https://documentation.nexj.com/__attachments/a_d3a459546e6a5bd75767a8a44f46172a204776a74ce605fa2dde1f08623a2449/navgiation_drawer_collapsed.png?cb=4bbdf18ced16ce7ea7ea55ad3766ecb6)

* To navigate to a workspace, click on the workspace name or icon. The icon for the selected workspace is highlighted in the accent color.

* To expand a collapsed drawer, click the hamburger icon.

* To collapse an expanded drawer, click the left arrow icon.

### Workspaces

A Portal Application contains a set of workspaces each of which declare the interface content, layout, and behavior for an application feature. See <https://community.nexj.com/documentation/reference/modl/latest/Workspace.html>.

There are essentially three different types of workspaces:

* Dashboard Workspaces - collection of cards presenting summary information, optionally organized into tabs. For example, Home.

* Navigator Workspaces - a data table with filtering with navigation to a detail page that contains a banner portlet and a collection of cards, optionally organized into tabs. For example, Contacts.

* Special Workspaces - something that isn't a dashboard or navigator workspace 🙂 . For example, Schedule or Reporting.

* Administration Workspaces - collection of data tables in tabs without detail views for administrative maintenance. For example, Customize.

Dashboard and administration workspace patterns are essentially a collection of Cards whose patterns are discussed in the Cards section \<link\>. The navigator workspace pattern deserves special treatment and its components (Navigator and Banner) are discussed in their own documents.

![Example Navigator Workspace](https://documentation.nexj.com/__attachments/a_0f703cb8b4e73bbf9c8a99d8ff64da836a64f6073ea279f860b3b57fed865e3b/image2018-10-26_13-35-22.png?cb=94f2dfa11475d8c3463c6a8eb46867c1)

Each workspace has its own arrangement of portlets based around a particular business function. Workspaces may be accessed directly from the Navigator Drawer or may present drill-down information from other portlets, such as Contact \> Accounts \> Holdings/Transactions where Contact is a top-level workspace with an accounts list, which can further navigate to a sub-workspace for holdings. A workspace can simply have a set of one or more tabs with portlets in them, such as the Home workspace, or can have a navigator portlet, such as the Contacts workspace.

Workspaces are composed of the following components, some of which are optional to support the different workspace types (dashboard, navigator, admin).

#### Navigator (list portlet)

The navigator is an optional workspace element referencing a portlet that implements a list pattern. See <https://community.nexj.com/documentation/reference/modl/latest/Workspace_Navigator.html>.

![image2018-10-26_13-54-38.png](https://documentation.nexj.com/__attachments/a_68334f6cc79873a61610da6a52e5aabf4e46edb1b0722d3809d4044cb1b78b03/image2018-10-26_13-54-38.png?cb=2004013f9e4f6569470c8111024a91a5)

When an item is clicked on in the list, the local context is set to that item and a detail page is displayed. The page.case property may be used to conditionally display an appropriate detail page for the selected item.

#### Pages

Detailed content is presented in one or more Pages. See <https://community.nexj.com/documentation/reference/modl/latest/Workspace_Page.html>.

![image2018-10-26_14-1-45.png](https://documentation.nexj.com/__attachments/a_3ec8f48c24c2d1d7a00d88e579f37a36ac56d091257498cf26e84a062afd19d9/image2018-10-26_14-1-45.png?cb=246cbc4e850af4e0fdf7e0110328e3fb)

A page has an optional banner and a collection of Tabs. The banner refers to a summary banner portlet, and the tabs can contain various different types of portlets for different purposes.

##### Banner (summary portlet)

An optional reference to a summary style portlet for a given page as seen in the diagram above. See <https://community.nexj.com/documentation/reference/modl/latest/t_PageType_Banner.html>.

##### Page Tabs (detail portlets)

A page may contain any number of tabs to present and group portlets as seen in the diagram above. See <https://community.nexj.com/documentation/reference/modl/latest/t_PageType_Tab.html>.

### Drawers (slide-out portlets)

Drawers can slide in and out and remain visible until closed (for example, the Notifications drawer). These are simple references to portlets that are displayed in the Application Drawer area, along with the events that will show or hide them. In desktop mode, when activated, these remain visible even when navigating from workspace to workspace. See <https://community.nexj.com/documentation/reference/modl/latest/Portal_Drawer.html>.

![image2018-10-26_14-15-16.png](https://documentation.nexj.com/__attachments/a_d579dbaaa1c06365815b5edf725a451e758de881cb548ef2b8476e5c4c690f3d/image2018-10-26_14-15-16.png?cb=b062dc03bf1977d37e617ab67d76bebc)

### Actions and Global Actions (Tools)

Tools appear in the `Global Actions` section of the `Application Toolbar`. Simply add a `Tool` element to your `Portal` application. When a user clicks on a tool, it raises a UI Event which is handled by the framework or by configured UI Actions. See <https://community.nexj.com/documentation/reference/modl/latest/Portal_Tool.html>.

![app toolbar.png](https://documentation.nexj.com/__attachments/a_1cdb2517fc9c8673efc7d8cea513fe4d7aeef9e79989ae1af36740dd5bc3776b/app%20toolbar.png?cb=923201133c721e03243c39c3037d3646)

There are also workspace actions that can show up on buttons, lists, and in the floating action button (FAB) in the bottom right-hand corner (in the general case - it may be in a different location for different locale settings).

![image2018-11-15_15-18-54.png](https://documentation.nexj.com/__attachments/a_e2f5c7e521f724e78d1cb7b0206144c61e0df630c68627a8568661958418aa12/image2018-11-15_15-18-54.png?cb=7cfa6ef53e3f6c65be8de16b62e279d5) ![image2018-11-15_15-19-11.png](https://documentation.nexj.com/__attachments/a_ff0b37c2ba8267e0e66af9397b626301b7bc2b8a3007c3baeec4541c45f0039b/image2018-11-15_15-19-11.png?cb=1eee69f2671a3defd38e775c29d608f4) ![image2018-11-15_15-19-21.png](https://documentation.nexj.com/__attachments/a_31c086c53b6ce6c03f050602a25c97687aa0edcaa2fbfd7716b697b7afd07759/image2018-11-15_15-19-21.png?cb=0cf816a862798d80413fc5fd3dc11d8c) ![image2018-11-15_15-19-39.png](https://documentation.nexj.com/__attachments/a_4818bdc9b7e1578cd63949820d594e0da732517262ce0152ac27ffc5d8559ee5/image2018-11-15_15-19-39.png?cb=e27dff9549ec5cf2b23d2ba53f0bf70b)

For background information, see <https://material.io/design/components/app-bars-top.html>.

### Dialogs

These actions may be used for pretty much anything, including displaying a full-screen or partial screen dialog, as with the edit FAB button on the Contacts workspace.

![image2018-11-15_14-29-27.png](https://documentation.nexj.com/__attachments/a_210ac3eda615c373970064292b1da93e9f596452513f2e2485b33ef2db144f0f/image2018-11-15_14-29-27.png?cb=7c7741be3a730d583fd06aea2caafe3a)

Dialogs can pop up full screen, or on top of other content, for adding, editing, providing, or collecting information. See <https://community.nexj.com/documentation/reference/modl/latest/Dialog.html>.

### Context bus

Another important application pattern concept is the *context bus* . Each workspace has a set of local context variables. They have a name, like EntityId or OpportunityId, and a current value. Portlets are not aware of each other except through the context bus. Here they can exchange information about the current context and events. There are also events associated with these contexts that are broadcast when, for instance, a context is changed. Global context is a set of variables available throughout the application. Portlets set, and listen for, changes in context variables that they are interested in. In addition, there are context events, which allow one portlet to broadcast an event with parameters, and any other portlet that is interested can respond to that event. This approach allows portlets to be designed with very little knowledge of other portlets, resulting in more flexibility and better maintainability.

### Broker (non-visible portlet for portal interface logic)

A reference to a portal that is not visible, but provides orchestration and state capabilities to the portal application. For example, when you click the **View All** button on the My Tasks portlet on the Home workspace, a navigate event is fired through the context bus. The broker portlet hears the event and handles it by navigating to the Task workspace See <https://community.nexj.com/documentation/reference/modl/latest/Portal_Broker.html>.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application services

The page (25.09) Welcome to NexJ's technical documentation was not found -- Please check/update the page name used in the MultiExcerpt-Include macro

**Development Technologies**

NexJ's products, modules and services are designed using our low code platform so that each service has it's own business logic, persistence mapping, integration logic and APIs, as well as UI. The solutions have been running at scale since 2006 in high volume, highly complex, and highly integrated environments. Written in Java and JavaScript, the platform reduces the time and effort required to design, configure, deploy, and maintain scalable, flexible applications in the enterprise. Service designs are typically expressed and modeled in NexJ Studio with the Model Description Language and are published directly to the Model Server without code generation. The model (aka the design) **is** the application, so you always know exactly what is in production. Services can be created using any technology that produces appropriate APIs and UI interfaces that can work with our other services, and some of our services are written in Springboot and React. The solution can be deployed on premise or in the cloud using physical machines, virtual machines, or Kubernetes. The system is made to work in the enterprise so it supports various standard approaches to security and single-sign-on, entitlements, testing, load-balancing, and monitoring. The code works with various source code control systems - we use Mercurial and Git internally.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Application toolbar

## Description

The application toolbar is located at the top left of the application screen. It contains icon buttons for actions that apply to the entire application and should be accessed from any workspace.

The current default commands accessed from the global application toolbar are: Global Search, Conversations, Notifications, and User Preferences.

### When to use

Use if you want to provide you users with some actions that should be available regardless of which workspace they are on.

### Variations

The contents of the toolbar can be customized at the application level, but they are not contextual to the workspace.

### Interactions

Click the button to open a dialog, menu, or workspace.

## Specifications

![Top App Bars.png](https://documentation.nexj.com/__attachments/a_72dae93df7cac21bd40b5978d541da224099319fa10ecf4386945a86572a1061/Top%20App%20Bars.png?cb=11aab39145fa000b539b5f83bfc917bd)

### Additional reference

[App bars: top - Material Design](https://material.io/design/components/app-bars-top.html)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Architectural approach

MODL is a platform for efficiently developing and maintaining enterprise applications. It is all about pattern-based design thinking with a very strong focus on integration. It allows you to capture business logic using modern object-oriented designs independent of their data sources. Once the business logic is appropriately modeled in the system, it can be easily mapped to data sources and exposed to a wide variety of client platforms. It can also be integrated into the rest of the organization using modern or legacy integration interfaces.

**Business Model**

![image2018-9-16_16-7-1.png](https://documentation.nexj.com/__attachments/a_9a9e8a47c9b6d2e7b18a1bb0287ee1ae1eda30db7a0e7f7d1dce7d55ba43f762/image2018-9-16_16-7-1.png?cb=74f1e681fbb31437ca8037db139c44b4)

First we model the concepts, relationships, business processes, and business rules without regard to where the data is or how it will be used.

**Integration**

![image2018-9-16_16-8-59.png](https://documentation.nexj.com/__attachments/a_62bb452955aa2112e662caa9c5e0c18ce09ee7a212af063a4a10a09d040891bb/image2018-9-16_16-8-59.png?cb=0838b5e8e4992911b755db77ae46b18a)

We then connect the model with the outside world. The integration layer uses a simple Send/Receive interface. It provides access to the model over direct access RESTful Model Services, contract-based SOA services, and through any protocol or wire-format with custom Integration Services. This layer also has powerful synchronization capabilities built-in.

**Persistence**

![image2018-9-16_16-12-28.png](https://documentation.nexj.com/__attachments/a_0e9883c52bf80288328994a2908714ddfcd8ea6a60ab4080c0b5fd4666dbeefc/image2018-9-16_16-12-28.png?cb=677552b46ab07211868ef3f07026ad77)

The persistence layer connects the model to physical storage using CRUD. It supports relational databases, file systems, and service persistence. The details of how persistence and integration connect to resources in a particular environment are deferred to environment files at deploy-time.

**Presentation**

![image2018-9-16_16-16-16.png](https://documentation.nexj.com/__attachments/a_293553409079d4186888e2e6f63740ba01a3b7e8d2dd32c1c0115ea7a68582d9/image2018-9-16_16-16-16.png?cb=6beb8516d82dee20da16cd407d1c257a)

The UX/presentation layer provides flexible ways of presenting the application to end-users. It supports declarative portlets, custom UI, and headless development. It is supported by various platform services, including the Portal Service for flexible, yet pattern-based and efficient UX development. This layer provides full mobile and responsive capabilities.

**Other**

Additionally, the approach supports global scripting libraries, Java libraries, internationalization, security, and more.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Architectural goals

The 6 goals of the ICM platform are centered on requirements that are key to all of the vertical industries our products are made for. The financial services and insurance industries have a number of needs that are unique, and our ICM platform was built with these needs as a central theme. This differentiates our platform because in addition to providing users with a Comprehensive Customer View, we also deliver on the following 6 goals:

## Seamless user experience

The first goal is to enable firms to provide a seamless experience by delivering a fully-integrated, single application that is completely responsive across devices and platforms.

Through integration, NexJ offers all the applications and data that users need in a single place. All NexJ functionality is exposed as port lets. The NexJ Portal Server enables firms to create role- and line of business-specific workspaces with any combination of NexJ Portlets and available third party portlets to provide a seamless user experience. Additionally, NexJ products are designed leveraging modern browser technologies, a multi-platform rendering machine, and are fully responsive on all form factors including mobile, tablet, and desktop. This allows users to have the same experience and same access regardless of what device they use.

## Robust integration

The second goal is that the architecture must allow for integration with a wide variety of client platforms, data sources, and applications. The end user should be unaware of the diversity of data sources, simply benefiting from the Comprehensive Customer View. Developers must similarly be able to design the business logic and UI without being concerned with low-level integration issues.

NexJ's open services architecture allows clients to integrate NexJ applications, legacy applications, and third party applications into a single, seamless interface by supporting flexible integration patterns and techniques. This flexibility allows the best option for any given situation to be selected and, if requirements change during the project, the approach can be adjusted, allowing the project to remain on track.

* Multiple approaches: accessing data at source, messaging, data synchronization, etc.

* Multiple technologies: database connectors, security adapters, message adapters, messaging protocols, etc.

* Integration points embedded throughout the architecture: business object lifecycle, data persistence, SQL Hooks, UI, and portal server events.

* Ability to make direct API calls to external systems and support Web Services, SOAP, TCP/ICP, and HTTP interfaces for external applications to call into the platform.

Integrated data is natively available to every aspect of the system:

* Integrated data is part of the common business model and behaves natively in the architecture.

* Integrated data is used by federated searches, integrated workflows, on mobile devices, and security entitlements.

The portal server provides a superior platform for desktop integration to deliver a seamless user experience through:

* Multiple context buses for orchestrating application integration.

* Messaging events across workspaces and portlets.

## Single tenancy

The third goal is that the architecture must provide a dedicated infrastructure with effective critical data isolation between customers.

Unlike companies who co-mingle unrelated customers' data offsite and, in some cases, out of the country, NexJ's deployments always maintain segmentation of client data. Single tenancy means that an organization's data and users are segregated into their own environments, rather than having multi-tenancy where a single instance of a software application serves multiple customers.

## Strong security

The fourth goal is that the architecture must support robust and complex security.

NexJ is designed to be secure, and enables firms to configure security to exactly match their requirements, allowing information to be shared across the company according to flexible visibility rules. These rules can include integration to existing entitlement systems that encapsulate client preferences and regulations. Security rules are centrally defined in the business model, meaning they will be consistently applied regardless of the user interface or access methods being used to retrieve or manipulate data elements.

## Flexible configuration

The fifth goal is that applications must be easy to configure, allowing clients to deploy an integrated solution that meets their requirements.

NexJ offers extensive runtime configuration of users, roles, system behavior, and look and feel. All CRM functionality is available as personalizable workspaces. Users can use the pre-built workspaces provided or build new workspaces specific to their role or preferences, by dragging and dropping portlets from the portlet library. Additionally, users can tailor a number of aspects of their experience as desired.

## Ease of deployment

Finally, the sixth goal is that deployment must be simple, and the application must be highly scalable, reliable, and run on a variety of open source and common software platforms.

NexJ's architecture abstracts away the physical environment into easily-shared and managed environment files for system configurations such as Development, QA, UAT, Staging, or Production. Environment files capture specifications for application server security, cluster and user sessions, data source types, the specific login credentials, specific physical characteristics of integration channels, and others. This approach allows applications to be deployed easily to multiple environments.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Assigning default client and opportunity coverage roles

You can assign default client and opportunity coverage roles to users. The coverage roles are assigned to users when they are added to a client coverage group in NexJ Admin Console or assigned coverage of a contact or opportunity in NexJ CRM.

You can also define default client business lines and regions for users. The business lines and regions are assigned to users when they are assigned coverage of a client in NexJ CRM.

To assign a default coverage role:

1. Navigate to the Coverage page.

2. In the **Default Roles** tab, select the user that you want to assign a coverage role to.

3. Click the **Edit** button ![pencilRH.png](https://documentation.nexj.com/__attachments/a_0acc95f143bebfb2f1f4c3e3ff0f971f69072e232a04f560b14381dc4956cd16/pencilRH.png?cb=276b26cd25f0ee95d576c2ff94744fbc) . The Edit Default Roles dialog displays.

4. Do any of the following:

   * To assign a default client coverage role, select a role from the **Client Coverage Role** drop-down list.

   * To assign a default business line for client contacts, select a business line from the**Client Business Line** drop-down list.

   * To assign a default region for client contacts, select a region from the **Client Region** drop-down list.

   * To assign a default opportunity team role, select a role from the **Opportunity Team Role** drop-down list.

5. Click **OK**. The Edit Default Roles dialog closes.

The default roles, business lines, and regions that you selected are assigned to the user.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Associated classes

**Learning objectives**

In this learning module, you will be introduced to associated classes which are attributes of one class that reference another class. By completing this module, you will learn:

* How classes can relate to each other through associations.

* How to model those associations in NexJ Studio and use the reverse property to establish a two-way association.

* How to use key indexes to capture those associations in the data store.

![](https://documentation.nexj.com/__attachments/a_5a1425d57fa6e0f61762916c4a19ec3f8dbbfd59695e606982f57f722ad5b922/image2020-1-30_16-50-58.png?cb=83296d37df8651386cdf904a95f9d4e4)

**Key concepts**

* **Primitive types** - Simple attribute types, such as string, integer, timestamp, and decimal.

* **Complex attribute** - Attribute based on another class in the business model.

* **Reverse property** - Set on a complex attribute in order to identify how the class specified by the attribute refers back to the class containing the attribute.

## Creating associated classes

In the previous lesson, Persisted classes, you created a `training:Entity` class, which models the people and companies that your business has contact with. It contains a single attribute, `lastName` defined as a string. You often need more complex data, such as a mailing address, to be stored in an attribute. In this case, you can define the attribute to be based on another class in the business model, referred to as a *complex type* or a *complex attribute*.

When you define a complex attribute, you establish an association between the classes that you are modeling. This association can be one-way, or two-way.  
In a one-way association, `ClassA` uses `ClassB` as an attribute, allowing access to instances of `ClassB` through instances of `ClassA`. However, `ClassB` instances have no way of directly accessing the `ClassA` instances with which they are associated. For example, if a class `Person` has a one-way association to a class `ContactInformation`, an instance of `Person` can access its related `ContactInformation` instances, but a given `ContactInformation` instance cannot access any information about the `Person` instances with which it is associated.

By contrast, if these classes have a two-way association, both sets of class instances can access each other. The instances of the `Person` class can access related `ContactInformation` instances and the instances of the` ContactInformation` class can access their related `Person` instances, allowing you, for example, to find out who lives at an address or the owners of a given telephone number.

To specify a two-way association in NexJ Studio, you can use the *reverse* property.

In this lesson you will expand the business model to include the ways in which a person or business can be contacted. You will add two classes to the model: `training:Telcom` and `training:TelcomType`. `training:Telcom` models the different ways that an entity can be contacted. `training:TelcomType` models the different types of telecommunication channels available.

For example, `Thing2` might have a home address and a business address. These would each be `training:Telcom` instances with a `training:TelcomType` of `Address`. However, `Thing2` might also have a phone number, which would be a `training:Telcom `instance with a `training:TelcomType` of `PhoneNumber`. The `training:Telcom` class will have a two-way association with the `training:TelcomType` class and another one with the `training:Entity` class.

### Learning activity

Create the `training:Telcom` and `training:TelcomType` classes. You can create both classes simultaneously so that in one class, you can refer to the other class while defining the class's properties.

1. Open the training:Fundamentals class diagram from the Busuness Model/Diagrams layer.

2. Right-click in the diagram and select **New \> Class**.

3. Name the class `training:TelcomType` and click **Finish**.

4. Right-click in the diagram again and select **New \> Class**.

5. Name the class `training:Telcom` and click **Finish**

You should see something like the following diagram.

![image2020-1-23_13-49-6.png](https://documentation.nexj.com/__attachments/a_b3eb32f143e5d51c32984b47760b779f44434f075425e90b531cd9e13e80d1c5/image2020-1-23_13-49-6.png?cb=af466428ae4a7b4bbeac34e6dfa436f3)

Right mouse click on the title of training:Entity and "Unhide Derivation \> training:Locking"

In the top, right corner of the diagram editor, expose the palette ![image2020-1-23_13-58-23.png](https://documentation.nexj.com/__attachments/a_b1edeb2dde3db4c2a7315a94edd7e0ff98b427eaea7a41a6dc6c5d87b146b1e4/image2020-1-23_13-58-23.png?cb=033ae715cdb7b3d6d3e238d3797e7ad5) and select the Note item. Place a note in the top left corner of your diagram and enter "Fundamentals Training Business Model" and the date. Drag and drop the classes so your diagram looks something like the following:

![image2020-1-23_14-1-9.png](https://documentation.nexj.com/__attachments/a_f16c4a83e175f4a83193cdd5f80b0eaac72c2af1f72c8a93293235966f7f927f/image2020-1-23_14-1-9.png?cb=9d7893f2620eabfc81074849cb78ad8f)

### Adding the primitive attributes

Right mouse click and select **Create Attribute** on the training:Telcom class then the training:TelcomType class and add the following attributes.

![image2020-1-23_14-8-19.png](https://documentation.nexj.com/__attachments/a_3b1ffd9fd6926c6787cceb2ed27017e72d714125e54bd5859ff3ada941f0d6f0/image2020-1-23_14-8-19.png?cb=b3c4d755e9b69b9c9cc557ec4777d5a4)

### Adding the association attributes

Right mouse click on the training:Telcom class and select **Create Association...**Fill the resulting dialog out as follows and press OK.

![image2020-1-23_14-14-18.png](https://documentation.nexj.com/__attachments/a_d648927f9a384b999565d55fe64f8827473e7959049b7f5cca3ee9e3bfe23fa1/image2020-1-23_14-14-18.png?cb=1facec582a009b5d198cbd83d0a0437e)

Right mouse click training:Telcom and select **Create Association** again. This time fill the Create Association dialog as follows and press OK.

![image2020-1-23_14-18-13.png](https://documentation.nexj.com/__attachments/a_b42742ab5795106fa3ee046a435d5267701694ec769200aca91b04554cad3a49/image2020-1-23_14-18-13.png?cb=0f786fdc8cfc045ffc2ffe398468c131)

Your diagram should now look something like...

![image2020-1-23_14-18-56.png](https://documentation.nexj.com/__attachments/a_da09a3db33580056b7d41a714ec0e71771fb1dd23e28a7240bd7034dc4fbed70/image2020-1-23_14-18-56.png?cb=b04c6f7dd35e2b56da9741079265688b)

Creating without the diagram  
Of course, creating the model visually is just an option. You can create the classes as well in the Classes tab of the Business Model layer. If you didn't use the diagram editor, you would do something like...

1. In the Business Model layer, click the **Classes** tab.

2. Right-click in the navigator and select **New Class**.

3. Name the class `training:TelcomType` and click **Finish**.

4. Right-click in the navigator again and select **New Class**.

5. Name the class `training:Telcom` and click **Finish**

6. Then you would set all of the properties of the class using the Overview and Attributes tab as follows.

Open the class editor for the `training:TelcomType` class. If your in the diagram editor, you can get to the class editor by double-clicking on the class. From the work we've already done in the diagram editor, the class should have two attributes: `name`, a primitive type, and `telcoms`, a complex type that refers to the `training:Telcom` class. There are a couple of other settings we will want to add to the class.

1. In the navigator or diagram editor, double-click the `training:TelcomType` class to open it.

2. In the **Overview** tab, set the **Caption** property using a string resource, as you did with the training:`Entity` class. Set the en value to `Telcom Type`. and call the string idsc.training.telcomType.

3. In the **Description** field, enter `Types of telcoms such as Home, Business, and Mobile`.

4. In the **Aspects** field, click the **Select** button ![Select button](https://documentation.nexj.com/__attachments/a_83c870e0e4df432e6e9ddb401c41db326fcd0720e59f85a91f96b95f648f8acc/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc) and add training:`LOCKING`.

5. In the **Attributes** tab, you should see two attributes - name and telcoms. Complete the properties as follows. Things that you will need to add are highlighted in yellow.

   **Attribute descriptions for TelcomType**

   | Attribute |  Property  |             Value             |                                                            Notes                                                             |
   |-----------|------------|-------------------------------|------------------------------------------------------------------------------------------------------------------------------|
   | name      | Type       | string                        |                                                                                                                              |
   | name      | Required   | true                          |                                                                                                                              |
   | name      | Caption    | idsc.training.telcomType.name | Set the caption on the **Common** tab. Use the selection tool to create this ID and give it an**en** value of `Telcom name`. |
   | telcoms   | Type       | training:Telcom               | This is a complex type referring to the `training:Telcom `class.                                                             |
   | telcoms   | Collection | true                          |                                                                                                                              |
   | telcoms   | Reverse    | type                          | `type` is the name of the corresponding attribute in the `Telcoms` class.                                                    |
   | telcoms   | Cascade    | cancel                        | Set the cascade property on the **Validation** tab.                                                                          |

   **Property definitions** :

   **Collection** - specifies that its attribute can hold multiple possible values. In the case of the `training:TelcomType` class, it indicates that for each instance of a `training:TelcomType`, there could be multiple different telecommunication channels (telcoms) of that type. For example, if there is a `training:TelcomType` instance of `HomePhone`, there can be multiple instances of telecommunication channels that are home phone numbers.

   **Cascade** - determines the deletion logic for the instances associated to the class through the attribute on which the property is set. In this case, setting it to `cancel` ensures that a `training:TelcomType` instance can only be deleted if there are no instances of the `training:Telcom` class associated with it. Other options are to delete the associated instances, or to clean the association between the instances.

   **Reverse** - establishes two-way associations between classes. In this case, setting the `Reverse` property on the telcoms enables you to access the `training:Telcom` instances related to a given `training:TelcomType` instance through the attribute relationship. In almost every case, the reverse property values will be reciprocal. If `ClassA:AttributeA` has a reverse that points to `ClassB:AttributeB`, then `ClassB:AttributeB` will also have a reverse to `ClassA:AttributeA`. In this lesson, because the telcoms attribute has a reverse to `Telcom:type`, when you define the `training:Telcom` class, its `type` attribute should have a reverse to `TelcomType:telcoms`.
6. Save your changes.

Open the class editor for the `training:Telcom` class. This class has five attributes, two of which are complex.

1. In the navigator or diagram editor, double-click the `training:Telcom` class to open it.

2. In the **Overview** tab, use the **Select** button ![select_icon.png](https://documentation.nexj.com/__attachments/a_83c870e0e4df432e6e9ddb401c41db326fcd0720e59f85a91f96b95f648f8acc/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc) to set the Caption property to a new string,`idsc.training.TrainingTelcom.caption`, with an en value of `Telcom`.

3. In the **Description** field, enter a value of `Telecommunications such as telephone number, email address, website address`.

4. In the **Aspects** field, click the **Select** button ![select_icon.png](https://documentation.nexj.com/__attachments/a_83c870e0e4df432e6e9ddb401c41db326fcd0720e59f85a91f96b95f648f8acc/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc) and add training:`LOCKING`.

5. You should see the following attributes with the specified properties on the `training:Telcom` class. Complete the properties as follows. Things that you will need to add are highlighted in yellow.

   **Attributes to add to the Telcom class**

   | Attribute |  Property   |        Value        |                                                                                          Notes                                                                                          |
   |-----------|-------------|---------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
   | name      | Type        | string              |                                                                                                                                                                                         |
   | name      | Required    | true                |                                                                                                                                                                                         |
   | address   | Type        | string              |                                                                                                                                                                                         |
   | address   | Required    | true                |                                                                                                                                                                                         |
   | type      | Type        | training:TelcomType |                                                                                                                                                                                         |
   | type      | Required    | true                | Enforces that every telecommunication channel must have an associated `training:TelcomType`.                                                                                            |
   | type      | Reverse     | telcoms             | Completes the reciprocal relationship by indicating that the telcoms attribute of the `training:TelcomType` class is itself of type `training:Telcom`, and has a reverse value of type. |
   | entity    | Type        | training:Entity     | Another complex type, creating an association with the `Entity` class.                                                                                                                  |
   | entity    | Required    | true                | Every `Telecom` channel must be associated with an entity-specific `Entity` instance.                                                                                                   |
   | entity    | Reverse     | telcoms             | Later, to complete the reciprocal relationship, add the telcoms attribute of type `training:Telcom `to the `Entity` class.                                                              |
   | isPrimary | Type        | boolean             |                                                                                                                                                                                         |
   | isPrimary | Initializer | #f                  | Set this property on the **Value** tab. `#f` is the Scheme symbol for "false". If you set the language to "js" for the initializer script's properties, then simply use false.          |

6. Save your changes.

At this point you have completed setting up the association between the` training:Telcom` and the `training:TelcomType` classes. However, the `training:Telcom` class also has a two-way association with the `training:Entity` class, so you must update the `training:Entity` class as well to complete your work on the Business Model for this lesson.

Complete the relationship between the `training:Telcoms` and `training:Entity` classes:

1. Open the `training:Entity` class.

2. In the **Attributes** tab, ensure the telcoms attribute has the following properties set:

   **Attribute to add to the Entity class**

   | Attribute |  Property  |      Value      |                        Notes                        |
   |-----------|------------|-----------------|-----------------------------------------------------|
   | telcoms   | Type       | training:Telcom |                                                     |
   | telcoms   | Collection | true            |                                                     |
   | telcoms   | Reverse    | entity          |                                                     |
   | telcoms   | Cascade    | delete          | Set the cascade property on the **Validation** tab. |

   Info  
   Setting the `Cascade` property to delete causes all the associated `training:``Telcom` instances for a `training:``Entity` instance to be deleted when the `training:``Entity` instance is deleted.

3. Click the **Save** button ![save_icon.png](https://documentation.nexj.com/__attachments/a_90df658c0a44861eabe0732d19847342a973854fa3eb5cebf16dd62994997ec3/save_icon.png?cb=e624b406b719682108c8a60ea0e1ed29) in the toolbar to save your changes.

## Update the class diagram

As you've seen, you can create UML class diagrams of your business model in NexJ Studio. You can then use the diagram to drill down into the model details and make model changes. If you then add attributes, derivations, or descriptions, to classes, they aren't automatically added to existing diagrams. In this section, we want to update the diagram to end up looking like the image below.

Associations in class diagrams  
You can see an association in a type diagram in two ways:

* Class with an attribute whose type is another class (e.g. `+ type :` `training:TelcomType`).

* Both classes are in the diagram, connected with a line labelled with the attribute or attributes by which the classes are associated, the cardinality of those attributes, and the direction of the association.

1. The only thing missing from the diagram are the training:LOCKING aspects that we added to the training:Telcom and training:TelcomType classes.

2. Right mouse click on each class and select "Unhide Derivations" to expose the aspects we added.

3. You could also unhide the descriptions for the classes if you want. Your diagram should then look something like...

![image2020-1-30_16-49-54.png](https://documentation.nexj.com/__attachments/a_abd317dcbada981159804b788783ac4b8ea27608aef6c54735975d44dba3058b/image2020-1-30_16-49-54.png?cb=fdffe058010b988451d188f203618ba0)

1. Save your work.

2. ![validate.png](https://documentation.nexj.com/__attachments/a_a6ee796630ac989df61909109743dc4d04a2e35c19a1314e1358b591f0d69ced/validate.png?cb=1bfd263d9a5314da8390de47dbf20045) Validate your model.

## Test the model logic

Now you test the model logic to ensure it behaves as expected.

In a scratchpad, we will create a training:Entity instance, define a `training:TelcomType` instance, and then create a `training:Telcom` instance that has associations to both of them.  
Note  
The steps in this lesson assume that you have carried out the steps in [Test your work](https://documentation.nexj.com/crm-technical/25.11/Cloud/persisted-classes.md#testyourwork), in the previous module which persists some data to the model. If you did not run those steps, or if you have subsequently carried out an action that erases the data store, such as using the Data Load Tool to reset or recreate the database, then you should rerun the code from that lesson in a console before proceeding.

1. Use an existing or create a new scratchpad. (**Resources → Scratchpads**). Add the following commands to the scratchpad:

       ; read an entity that you created in the previous lesson and assign it to the
       ; variable "anEntity". read-instance is used here to find the Entity instance
       ; that has a lastName value of "Thing1"
       (define anEntity (read-instance training:Entity '() '(= lastName "Thing1") '()))

       ; ensure that Thing1 existed, if not create it
       (when (null? anEntity) (set! anEntity (training:Entity'new (: lastName "Thing1"))))

       ; create a new TelcomType of Home
       (define aTelcomType (training:TelcomType'new (: name "Home")))

       ; create a new Telcom instance associated with the Thing1 entity and the
       ; Home telcomtype
       (define aTelcom
          (training:Telcom'new
             (: entity anEntity)
             (: type aTelcomType)
             (: name (aTelcomType'name))
             (: address "(416) 555-1234")
          )
       )

   JavaScript

       ; If you are running your console in javascript with the read -Dnexj.repl.language="js" flag, then use the following script
       ; read an entity that you created in the previous lesson and assign it to the
       ; variable "anEntity". read-instance is used here to find the Entity instance
       ; that has a lastName value of "Thing1"
       ; notice that any non-standard javascript syntax (like "-" and ":" in identifiers) is quoted with #"<expression>"
       var anEntity = #"read-instance"(#"training:Entity", null, #"'()", null);

       ; ensure that Thing1 existed, if not create it
       if (null?(anEntity)) {
        anEntity = new #"training:Entity"({lastName: "Thing1"});
       }

       ; create a new TelcomType of Home
       var aTelcomType = new #"training:TelcomType"({name: "Home"});

       ; create a new Telcom instance associated with the Thing1 entity and the
       ; Home telcomtype
       var aTelcom = new #"training:Telcom"({entity: anEntity, type: aTelcomType, name: aTelcomType.name, address: "(416) 555-1234"});

2. Run the Server Console using ![scheme_console_icon.png](https://documentation.nexj.com/__attachments/a_33749e31aa70e279f17913a772927e6e6e083826bde3b7646b9a759334fff110/scheme_console_icon.png?cb=8d34e496cf82fe5370501ee59605bdbd) .

   Info  
   The console automatically validates the model when it starts.

3. Select the statements you entered in the scratchpad and press Ctrl+U to run them in the console. You should see output similar to the following:

       ; 12:16:17,507 DEBUG [SQLAdapter] select A.id, A.locking from NJEntity A where
        A.lastName = ?
       ; 12:16:17,508 DEBUG [SQLAdapter] Bind[0] = 'Thing1'
       ; 12:16:17,512 DEBUG [<default>] Activated
        SQLConnection@1317264939(pool=RelationalDatabaseConnectionPool(fragment=RelationalDatabaseFragment
        DefaultRelationalDatabase.<default>))
       ; 12:16:17,579 DEBUG [SQLAdapter] SQL execution time: 67 ms
       ; 12:16:17,580 DEBUG [SQLAdapter] Retrieved 1 instance(s) of Entity in 1 ms

       ; #<Instance<Entity, OID:1:V32:D2F82A01DE0A44FCB5807316DFCEB65F,
       CLEAN>(locking=0)>

       > ; 12:16:17,583 DEBUG [TelcomType] Invoking Event TelcomType.new(values)
       ; 12:16:17,588 DEBUG [GenericTransactionManager] Starting new transaction
        Tx(00000000000000005A1438C0B5F97552370ADC03C577DE56)
       ; 12:16:17,588 DEBUG [InvocationContext] Started new transaction
        Tx(00000000000000005A1438C0B5F97552370ADC03C577DE56)
       ; 12:16:17,589 DEBUG [<default>] Deactivating
        SQLConnection@1317264939(pool=RelationalDatabaseConnectionPool(fragment=RelationalDatabaseFragment
        DefaultRelationalDatabase.<default>))
       ; 12:16:17,591 DEBUG [TelcomType] Invoking Event TelcomType.create()

       ; #<Instance<TelcomType, null, NEW>(name="Home")>

       > ; 12:16:17,592 DEBUG [Telcom] Invoking Event Telcom.new(values)
       ; 12:16:17,595 DEBUG [Telcom] Invoking Event Telcom.create()

       ; #<Instance<Telcom, null, NEW>(name="Home", address="(416) 555-1234",
        type=Instance<TelcomType, null, NEW>, entity=Instance<Entity,
        OID:1:V32:D2F82A01DE0A44FCB5807316DFCEB65F, CLEAN>)>

       >

   Stop the Console by clicking the **Terminate** button ![terminate_icon.png](https://documentation.nexj.com/__attachments/a_9592a70cc0c6c003efcda396c9f5e96e6a382ae662a4183cf444bb53ee0666b3/terminate_icon.png?cb=cf13647468dae207b797c07d76ad9300) .

## Create the class persistence mapping

Now, use the **Persistence Mapping** tab to manage the persistence of the class attributes to the data sources that you have defined.

To enable persistence for the `Telcom` class:

1. Open the `training:Fundamentals` class diagram (**Business Model → Diagrams**).

2. Double-click the title of the training:`Telcom` class. The training:`Telcom` class opens in the editor.

3. Click the **Persistence Mapping** tab at the bottom of the editor.

4. Set the **Data Source** property to `training:DB`.

5. Set the **Primary Table** to `Telcom`.

   Datasource Prefix  
   Because the training:DB datasource has a prefix of "TRN" (set on the datasource editor's advanced tab), the actual physical table will be TRNTelcom.

6. In the **Key Generator** field, select **KeyGenerator.GUIDGen (GUID key generator)**.

7. To define which attributes have their data stored in the database, click the **Select Attribute Mappings** button

   ![select_icon.png](https://documentation.nexj.com/__attachments/a_83c870e0e4df432e6e9ddb401c41db326fcd0720e59f85a91f96b95f648f8acc/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc)

   and select the following attributes:
   * name

   * address

   * type

   * entity

   * isPrimary

     The `name`, `address`, and `isPrimary` attributes appear in the **Primitive Attributes** list. The `type` and `entity` attributes appear in the **Association Attributes** list.

8. To complete defining the associations between the `training:Telcom` class and the associated classes, set the **Source Key** , and**Destination Key** values for the **Association Attributes** according to the following table.

   **Association attribute mapping for the Telcom class**

   | Attribute |      Source Key      | Destination Key |
   |-----------|----------------------|-----------------|
   | type      | Telcom.FK_TelcomType |                 |
   | entity    | Telcom.FK_Entity     |                 |

   Leave the key blank if you are specifying the object key for the source or destination (AKA primary key)

   The `type` and `entity` attributes are complex, and associated with the `training:TelcomType` and training:`Entity` classes, respectively. For each of these, the source key is a foreign-key index on the class you are working with, and the destination key is an index on the associated class. The values of the columns indexed by these keys will be coordinated to track the association of the class instances.
9. Save your work.

10. Click **Update Data Source** to apply these changes to the training:DB datasource.

11. Click **OK** on the **Update Data Source** window.

12. You are taken to the properties of the `training:DB datasource` in the **Data Sources** tab of the Persistence layer.

13. On the **General** tab add `Stores Telcom objects for an Entity.` as the **Description.**

14. Set the case insensitive flag for the id column to false.

Columns for the primitive attributes, including the `id` column used for the index have been created for you, as well as the primary key index. However, you need to add the columns and the indexes that are used to define the associations with other classes.

To create the columns and indexes for the associations:

1. In the **Columns** tab, click the last attribute, then click the **Add** button ![add_action.png](https://documentation.nexj.com/__attachments/a_c80da2fb23701491a241f131ded8400b3f206727aaf587db67804825193f6075/add_action.png?cb=d39030b8697ae8291cbb9b3d5fa80408) to add the following two columns:

   **Column properties for the Telcom table**

   | Column Name  |  Type  | Allocation | Allow Nulls | Precision | Case Insensitive |
   |--------------|--------|------------|-------------|-----------|------------------|
   | telcomTypeId | binary | fixed      | false       | 16        | false            |
   | entityId     | binary | fixed      | false       | 16        | false            |

2. In the **Indexes** tab, add the following two indexes:

   **Index properties for the Telcom table**

   |         Name         | Type  | Unique | Index Columns (press add in lower list) |
   |----------------------|-------|--------|-----------------------------------------|
   | Telcom.FK_TelcomType | btree | false  | telcomTypeId                            |
   | Telcom.FK_Entity     | btree | false  | entityId                                |

   Info  
   The Telcom.FK_TelcomType index is used to instantiate the association between the `training:``Telcom` and `training:``TelcomType` classes through their respective type and telcoms attributes. Likewise, the Telcom.FK_Entity index establishes the association between the `training:``Telcom` and `training:``Entity` classes. Foreign keys act by taking on values of the (usually primary) keys of other tables.

3. Save your work.

Return to the `training:Fundamentals` diagram so that you can enable persistence for the `training:TelcomType` class.

1. Double-click the `training:TelcomType` class and select the **Persistence Mapping** tab.

2. Select the **training:DB datasource**.

3. Set the **Primary Table** as `TelcomType` and specify the **Key Generator of KeyGenerator.GUIDGen**.

4. Click the **Select Attribute Mappings** button ![select_icon.png](https://documentation.nexj.com/__attachments/a_83c870e0e4df432e6e9ddb401c41db326fcd0720e59f85a91f96b95f648f8acc/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc) and add the name and `telcoms` attributes.

5. Set the **Name** , **Source Key** , and **Destination Key** values for the `telcoms` attribute according to the following

   table.

   **Association attribute mapping for the TelcomType class**

   | Attribute | Source Key |   Destination Key    |
   |-----------|------------|----------------------|
   | telcoms   |            | Telcom.FK_TelcomType |

6. Save your work.

7. Click **Update Data Source** to apply these changes to the training:DB datasource. Click **OK** on the **Update Data Source** window.

8. You are taken to the properties of the `DefaultRelationalDatabase` in the **Data Sources** tab of the Persistence layer.

9. In the **General** tab add `Stores Telcom types`. as the **Description**.

10. In the **Columns** tab, deselect the **Case Insensitive** property of the `id` column.

11. In the **Indexes** tab, add the following index:

    **Index properties for the TelcomType table**

    |        Name        | Type  | Unique | Index Columns |
    |--------------------|-------|--------|---------------|
    | TelcomType.OK_Name | btree | true   | name          |

    Info  
    The TelcomType.OK_Name index ensures that all names in the class are unique. The acronym OK is used to signify "other key," i.e. a key that is neither the primary key nor a foreign key.

12. Save your changes.

Return to the `training:Fundamentals` diagram so that you can enable persistence mapping for the `Entity` class:

1. In the `training:Entity` diagram, double-click the `Entity` class. The class opens in the editor.

2. Click the **Persistence Mapping** tab.

3. Click the **Select Attribute Mappings** button ![select_icon.png](https://documentation.nexj.com/__attachments/a_83c870e0e4df432e6e9ddb401c41db326fcd0720e59f85a91f96b95f648f8acc/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc) and add the `telcoms` attribute.

4. Set the **Source Key** , and **Destination Key** values for the `telcoms` attribute according to the following table.

   **Association attribute mapping for the Entity class**

   | Attribute | Source Key | Destination Key  |
   |-----------|------------|------------------|
   | telcoms   |            | Telcom.FK_Entity |

   Note that the source key for the `Entity.telcoms` attribute was the destination key of the `Telcom.entity `attribute. Likewise, the source key of the `Telcom.entity` attribute is the destination key of Entity.telcoms.

   This completes the mapping required to instantiate the association between the `Telcom` and `Entity` classes.

   No changes to the data source are required.
5. Save your work and validate the model.

## Edit the upgrade file

Now that you have defined the logical database structure and the persistence model, make changes to the upgrade file so that you can upgrade your physical database.

1. In the Persistence layer on the **Data Sources** tab right-click the training:DB and select **Generate Upgrade Steps**.

2. Leave the default values for **Upgrade** and **Current Data Source**.

3. Select **Local History** as the **Old Data Source**. You are going to use a published model as the old model.

4. Select **Revision Time** from before you added the TelcomType and Telcom tables and click **Next**.

5. Whenever you upgrade a database, you must increment the version number by a whole number. e.g. 3.1092.1307.167.53 to 4.1092.1307.167.53`.`

6. Enter the following as the **Description** : `Added telcoms support.`

7. One the left side you can see a list of the upgrade steps that will be applied to the database. There are two **CreateTable** steps: one for the `training:Telcom` table and one for the `training:TelcomType` table. For each table, click the **Columns** and **Indexes** tabs to review how the tables will be created.

8. Click **Finish** to create the upgrade steps.

9. You must ensure that your current model's version matches the upgrade version. Click the **Set current model** button ![rep_nav_icon.png](https://documentation.nexj.com/__attachments/a_70e89cc4dffc9c690956f4c99448ae9393085c9429c5a08709462bdc07409296/rep_nav_icon.png?cb=98aeec9adf5389ce8fe5527ba6c4dc14) in the toolbar to open the Model Library.

10. With the current model selected, click **Edit** . Update the **Model Version** field to match the version associated with the upgrade you just created. So, if your upgrade is associated with version 4.1092.1307.167.53, then change the Model version to 4.1092.1307.167.53. This enables the upgrade tool to recognize that the model has changed so that your update can be applied to existing data stores.

11. Click **OK** . Notice that the new version is now reflected in the table in the **Models** tab.

12. Click the **Save** button ![save_icon.png](https://documentation.nexj.com/__attachments/a_90df658c0a44861eabe0732d19847342a973854fa3eb5cebf16dd62994997ec3/save_icon.png?cb=e624b406b719682108c8a60ea0e1ed29) in the toolbar to save your work.

## Upgrade the database

Use the **Data Load Tool** to apply the changes from the upgrade file to the physical database:

1. Launch the **Data Load Tool** by clicking the drop-down arrow next to the **Run tool** button ![jasperreports_run.png](https://documentation.nexj.com/__attachments/a_f6bf60f689e0afd4ede58a08086eee0704035ffb4dd4d7b9254601b0bfce8b93/jasperreports_run.png?cb=7c07767ae9b7ccb779a16c8eb80bc7f6) in the toolbar.

2. In the **Model** section, select **Current**.

3. In the **Server and Connection** field, ensure that the **Test** option is cleared.

4. In the **Server** field, select **Development(development.properties)** environment.

   You must select this environment because you need to use a user with permissions to change to the database table structure.

5. In the **Command** field, select **upgrade**.

6. In the **Data Source** field, select \*.

7. Select Ignore Upgradable Flag.

8. Click **Run**.

9. You should get an Exit code = 0 as follows:

   ![image2020-2-6_17-16-45.png](https://documentation.nexj.com/__attachments/a_c45395c25d5e2b0c6db0176832d5fb45ef5dce4cc926f5a9d66c6a37f017e7d4/image2020-2-6_17-16-45.png?cb=4e9c5f7ba35b5745d320d07f9d92def2)

To verify that the upgrade worked:

1. Run your database's management tool or query client.

2. Verify that the table `TRNTelcom` exists with the proper column definitions in the training database:

   * For Microsoft SQL Server use the

     use training;

     `exec sp_help TRNTelcom;`

     statement.

## Test your work

Test your work by confirming that you can persist `training:Telcom` and `training:TelcomType` instances to the database.

To persist instances to the database:

1. Start the Server Console in Debug mode.

2. Open an existing or create a new scratchpad.

3. Previously, you created classes in the Console that were not persisted. This time you use the (`commit`) command to save the data in the database.

   Add the following command to the end of the existing code from earlier in the lesson :

       (commit) ; or commit() for javascript

4. Select all the code and press Ctrl+U to run it.

5. Inspect the console output, which should resemble the following:

       > (define anEntity (read-instance training:Entity '() '(= lastName "Thing1") '()));
        11:32:39,280 DEBUG [Entity] Invoking Event training:Entity.read(attributes, where,
        orderBy, count, offset, xlock)
       ; 11:32:39,280 DEBUG [GenericTransactionManager] Starting new transaction
        Tx(0100000000000000957F8007248ECAAF3255D10A83E37864)
       ; 11:32:39,280 DEBUG [InvocationContext] Started new transaction
        Tx(0100000000000000957F8007248ECAAF3255D10A83E37864)
       ; 11:32:39,280 DEBUG [SQLAdapter] select A.id, A.locking from TRNEntity A where
        A.lastName = ?
       ; 11:32:39,280 DEBUG [SQLAdapter] Bind[0] = 'Thing1'
       ; 11:32:39,280 DEBUG [GenericConnectionManager] Activated
        connection SQLManagedConnection@18170911(user=sa,
        factory=SQLManagedConnectionFactory({serverName=localhost, maxStatements=0,
        lobBuffer=10485760, prepareSql=3, databaseName=test101b, appName=NexJ,
        bufferMaxMemory=65536, xaEmulation=true, sendStringParametersAsUnicode=true}))
       ; 11:32:39,312 DEBUG [SQLAdapter] SQL execution time: 32 ms
       ; 11:32:39,312 DEBUG [SQLAdapter] Retrieved 1 instance(s) of Entity in 0 ms

       ; #<Instance<training:Entity, OID:1:V32:A8D16D8698BB4AE092FB24462FF3BBC1,
        CLEAN>(locking=0)>

       > (define aTelcomType (training:TelcomType'new (: name "Home"))); 11:32:41,999 DEBUG
        [TelcomType] Invoking Event TelcomType.new(values)
       ; 11:32:41,999 DEBUG [training:TelcomType] Invoking Event training:TelcomType.create()

       ; #<Instance<training:TelcomType, null, NEW>(name="Home")>

       > (define aTelcom (training:Telcom'new (: entity anEntity) (: type aTelcomType) (: name
        (aTelcomType'name)) (: address "(416) 555-1234")))
       ; 11:32:44,952 DEBUG [training:Telcom] Invoking Event training:Telcom.new(values)
       ; 11:32:44,968 DEBUG [training:Telcom] Invoking Event training:Telcom.create()
       ; #<Instance<training:Telcom, null, NEW>(name="Home", address="(416) 555-1234",
        type=Instance<TelcomType, null, NEW>, entity=Instance<training:Entity,
        OID:1:V32:A8D16D8698BB4AE092FB24462FF3BBC1, CLEAN>)>

       > (commit) ; 11:32:47,202 DEBUG [training:TelcomType] Invoking Event
        training:TelcomType.commit()
       ; 11:32:47,202 DEBUG [training:Telcom] Invoking Event training:Telcom.commit()
       ; 11:32:47,202 DEBUG [training:TelcomType] Invoking Event training:TelcomType.load(attributes)
       ; 11:32:47,202 DEBUG [training:Telcom] Invoking Event training:Telcom.load(attributes)
       ; 11:32:47,202 DEBUG [training:Telcom] Invoking Event training:Telcom.load(attributes)
       ; 11:32:47,202 DEBUG [UnitOfWork] Committing 2 instance(s)
       ; 11:32:47,202 DEBUG [SQLAdapter] insert into TRNTelcom(id, name, address,
        telcomTypeId, entityId, isPrimary, locking) values (?, ?, ?, ?, ?, ?, ?)
       ; 11:32:47,202 DEBUG [SQLAdapter] Bind[0] = 444882F001E3470EB297EF54E913351D
       ; 11:32:47,202 DEBUG [SQLAdapter] Bind[1] = 'Home'
       ; 11:32:47,202 DEBUG [SQLAdapter] Bind[2] = '(416) 555-1234'
       ; 11:32:47,202 DEBUG [SQLAdapter] Bind[3] = 1BB9C0FC578443A194BC68EC259A12DA
       ; 11:32:47,202 DEBUG [SQLAdapter] Bind[4] = A8D16D8698BB4AE092FB24462FF3BBC1
       ; 11:32:47,202 DEBUG [SQLAdapter] Bind[5] = 0
       ; 11:32:47,218 DEBUG [SQLAdapter] Bind[6] = 0
       ; 11:32:47,265 DEBUG [SQLAdapter] SQL execution time: 47 ms
       ; 11:32:47,265 DEBUG [SQLAdapter] insert into TRNTelcomType(id, name, locking)
        values (?, ?, ?)
       ; 11:32:47,265 DEBUG [SQLAdapter] Bind[0] = 1BB9C0FC578443A194BC68EC259A12DA
       ; 11:32:47,265 DEBUG [SQLAdapter] Bind[1] = 'Home'
       ; 11:32:47,265 DEBUG [SQLAdapter] Bind[2] = 0
       ; 11:32:47,280 DEBUG [SQLAdapter] SQL execution time: 15 ms
       ; 11:32:47,280 DEBUG [InvocationContext] Committing transaction
        Tx(0100000000000000957F8007248ECAAF3255D10A83E37864)
       ; 11:32:47,280 DEBUG [GenericTransactionManager] Committing transaction
        Tx(0100000000000000957F8007248ECAAF3255D10A83E37864)
       ; 11:32:47,280 DEBUG [GenericConnectionManager] Deactivating
       connection SQLManagedConnection@18170911(user=sa,
       factory=SQLManagedConnectionFactory({serverName=localhost, maxStatements=0,
       lobBuffer=10485760, prepareSql=3, databaseName=test101b, appName=NexJ,
       bufferMaxMemory=65536, xaEmulation=true, sendStringParametersAsUnicode=true}))
       ; 11:32:47,280 DEBUG [UnitOfWork] Commit completed

       ; ()

6. Verify that the data was correctly persisted by querying the database. Execute each of the following queries one by one:

   **Microsoft SQL Server**

   SQL

       select entityId, telcomTypeId, name from TRNTelcom
       select id, lastName from TRNEntity
       select id, name from TRNTelcomType

   Info  
   The` entityId` is a reference to the record id in the `Entity` table. Similarly, the `telcomTypeId` is a reference to the "Home" training:`TelcomType` record. This is where you see the*Source Key* and *Destination Key*properties in action.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Attribute validation

This lesson introduces techniques for validating attributes by enforcing business logic on the values assigned to it. By completing this module, you will learn:

* How to set up attribute validation in your model.

* How to add custom business logic to the validation of a class attribute.

## Create attribute validation

You can define attribute validation rules directly in your model. These rules validates attribute value before they can be saved. This consistently applies validation regardless of where they come from. That is, the values that arrive from a web service must validate against the same rules as values that arrive from the Console, or from a client interface.

By overriding base attributes in a subclass and applying validations to them, you can create polymorphic behavior. In this lesson, you validation for the training:`EmailAddress` class, a subclass of `training:Telcom`. The validation rule will ensure that any value submitted as an email address contains the "@" sign.

When you are writing your validation expression, two variables are immediately available to you: **this** represents the object whose attribute is being validated, and **value** contains the attribute.

Note  
This module assume that you have carried out the steps in [Test the model](https://documentation.nexj.com/crm-technical/25.11/Cloud/defining-inheritance.md#testmodel), which persists some data to the model. If you did not run those steps, or if you have subsequently carried out an action that erases data, such as using the **Data Load Tool** to reset or recreate the database, then you should rerun the code from that section in a Console before proceeding.

### Simple true-false validation

In this lesson you create and test a validation statement that returns `#t` (true) if the email address contains the "@" sign and `#f` (false) if it does not. You use the `string-match` command to test for the existence of "@". `string-match` takes a string and a regular expression as its inputs, and either returns a list of the matching string and any substrings identified by the regular expression, or returns a value of `#f (false)`.

#### Create the validation

1. Open the `training:EmailAddress` class for editing.

2. Select the **Attributes** tab.

3. In the **Attributes** list, click the **Override Base Attributes** button ![select_icon.png](https://documentation.nexj.com/__attachments/a_8e59f4aa6d798555978ada71b1a049e6472ba5213dc6f4e1c058c599b46a586a/select_icon.png?cb=d9480562f1597ef158a0f4f31de96fdc) . This opens the **Override Base Attributes** window where you can select an existing base attribute to override.

4. Select the `address` attribute, click the **Add** button, and click **OK**.

5. Select the `address` attribute and click the **Validation** tab.

6. In the **Validation** area, enter the following script:

       (string-match value "@")

   JavaScript

       #"string-match"(value, "@");

   In this example, if the validation fails, the validation statement returns `#f (false)`, which triggers a generic validation error.
7. Save your work.

Restart your server or use the "reload all" button in the toolbar (or ALT-F5 or Project/Metadata Hotswap/Reload All).

#### Test the validation

To test the validation:

1. Open and existing or create a new scratchpad and enter the following commands:

       (define e (training:Person'new (: firstName "Glen") (: lastName "Connor")))
       (define tp (read-instance training:TelcomType '() '(= name "Business") '()))
       (define telcom (training:EmailAddress'new (: entity e) (: type tp) (: name (tp'name)) (: address "glennexj.com") (: displayName "Glen Connor")))
       (commit)

   JavaScript

       var e = new #"training:Person"({firstName: "Glen", lastName: "Connor"});
       var tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
       var telcom = new #"training:EmailAddress"({entity: e, type: tp, name: tp.name, address: "glennexj.com", displayName: "Glen Connor"});
       commit();

2. Run the Server Console.

3. Execute the commands in the scratchpad one line at a time. After you run the `commit` command, the result should be...

       ;Cause: The address of Email Address must be within the allowed range.

   Note that the validation occurs when you attempt to commit the change to the database, not when the `training:Telcom` instance is defined.

   Also note that the error message used the class's caption (`Email Address`), not the name of the class (`training:EmailAddress`).
4. Stop the console.

### Validation that returns a custom string

Now you refine the validation by adding script to return more information about the error.

#### Define the error message

To define the error message that you want returned if the validation fails:

1. In the Resources layer, click the **Strings** tab.

2. Double-click the `idsc.training.en` file to open it.

3. Click the **Add** button ![add_action.png](https://documentation.nexj.com/__attachments/a_87567659c8a236d5b778957a4ce96b37090496ead02035e9585ecb2564fc34c9/add_action.png?cb=d39030b8697ae8291cbb9b3d5fa80408) to add a new string.

4. Specify an **Id** value of idsc.training.err.EmailMissingAtSymbol.

5. Specify an **en** value of `Email addresses must contain the '@' symbol`.

6. Save your changes.

#### Update the validation routine

To update the validation routine for the attribute:

1. Reopen `training:EmailAddress` class and select the address attribute.

2. Click the **Validation** tab.

3. In the **Validation** area, replace the existing Scheme statement with the following:

       (unless (string-match value "@")
          "idsc.training.err.EmailMissingAtSymbol"
       )

   JavaScript

       unless(#"string-match"(value, "@"), "idsc.training.err.EmailMissingAtSymbol");

   The validation error returns the Id of the string, which is resolved and used as the text of the validation error.
4. Save your changes.

#### Test the error message

To test the error message:

1. Run the Server Console and test the validation by running the same commands:

       (define e (training:Person'new (: firstName "Glen") (: lastName "Connor")))
       (define tp (read-instance training:TelcomType '() '(= name "Business") '()))
       (define telcom (training:EmailAddress'new (: entity e) (: type tp) (: name (tp'name)) (: address "glennexj.com") (: displayName "Glen Connor")))
       (commit)

   JavaScript

       var e = new #"training:Person"({firstName: "Glen", lastName: "Connor"});
       var tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
       var telcom = new #"training:EmailAddress"({entity: e, type: tp, name: tp.name, address: "glennexj.com", displayName: "Glen Connor"});
       commit();

2. Among the errors that are reported in console you should see the following lines:

       ;Cause: Email Addresses must contain the '@' symbol.

3. Stop the console.

### Validation that returns the problem value

Now you change the validation again so that the incorrectly formatted email is returned as part of the error message.

#### Update the error message

To update the error message that you want returned if the validation fails:

1. In the Resources layer, click the **Strings** tab.

2. Double-click the `idsc.training.en` file to open it.

3. Edit the idsc.training.err.EmailMissingAtSymbol message to read `Email addresses must contain the '@' sign. '{0}' is invalid.`

   '{0}' is a variable that will be replaced with the invalid email address.

4. Save your changes.

#### Update the validation routine

To update the validation routine for the attribute:

1. Reopen the `training:EmailAddress` class and select the address attribute.

2. Click the **Validation** tab.

3. In the **Validation** area, replace the existing Scheme statement with the following:

   Scheme

       (unless (string-match value "@")
          (list "idsc.training.err.EmailMissingAtSymbol" value)
       )

   JavaScript

       unless(#"string-match"(value, "@"), list("idsc.training.err.EmailMissingAtSymbol", value));

   The validation error now returns a list of values to be formatted. The error message string is the first value. When the string is resolved it uses the remainder of the list to supply values for its variables. In this case the `{0}` variable is replaced with the value of the incorrect string.
4. Save your changes.

#### Test your validation

To test the validation:

1. Run the Server Console.

2. Run the same commands:

       (define e (training:Person'new (: firstName "Glen") (: lastName "Connor")))
       (define tp (read-instance training:TelcomType '() '(= name "Business") '()))
       (define telcom (training:EmailAddress'new (: entity e) (: type tp) (: name (tp'name)) (: address "glennexj.com") (: displayName "Glen Connor")))
       (commit)

   JavaScript

       var e = new #"training:Person"({firstName: "Glen", lastName: "Connor"});
       var tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
       var telcom = new #"training:EmailAddress"({entity: e, type: tp, name: tp.name, address: "glennexj.com", displayName: "Glen Connor"});
       commit();

3. Among the errors that are reported in console you should now see the following line:

       ;Cause: Email Addresses must contain the '@' symbol. 'glennexj.com' is invalid.

4. Close the console.

### Optional: Validation that returns more complex values

In this validation, you make use of both the value function and arguments of the function this to provide more information about the instance that is causing the error.

#### Update the error message

To update the error message that you want returned if the validation fails:

1. In the Resources layer, click the **Strings** tab.

2. Double-click the customized `idsc.training.en` file to open it.

3. Edit the idsc.training.err.EmailMissingAtSymbol message to read `Email addresses must contain the '@' symbol. The address '{0}' for the entity '{1} ``{2}' is invalid.`

4. Save your changes.

#### Update the validation routine

To update the validation routine for the attribute:

1. Reopen the `training:EmailAddress` class and select the address attribute.

2. Click the **Validation** tab.

3. In the **Validation** area, replace the existing script with the following:

       (unless (string-match value "@")
          (list "err.trainingEmailMissingAtSymbol" value ((this'entity)'firstName)
        ((this'entity)'lastName))
       )

   JavaScript

       unless(
          #"string-match"(value, "@"), 
          list("idsc.training.err.EmailMissingAtSymbol", value, this.entity.firstName, this.entity.lastName)
       );

   The validation error now returns a list of values to be formatted. The first value, the error message string, is resolved and uses the remainder of the list to supply values for its variables. This time, the first and last name values of the entity associated with the email instance will be included in the error message.
4. Save your changes.

#### Test the validation

To test the validation:

1. Run the Server Console.

2. Run the same commands:

       (define e (training:Person'new (: firstName "Glen") (: lastName "Connor")))
       (define tp (read-instance training:TelcomType '() '(= name "Business") '()))
       (define telcom (training:EmailAddress'new (: entity e) (: type tp) (: name (tp'name)) (: address "glennexj.com") (: displayName "Glen Connor")))
       (commit)

   JavaScript

       var e = new #"training:Person"({firstName: "Glen", lastName: "Connor"});
       var tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
       var telcom = new #"training:EmailAddress"({entity: e, type: tp, name: tp.name, address: "glennexj.com", displayName: "Glen Connor"});
       commit();

3. Among the errors that are reported in console you should now see the following:

       ;Cause: Email addresses must contain the '@' symbol. The address 'glennexj.com' for the entity 'Glen Connor' is invalid.

4. Close the console.

### Commit a valid instance

Complete this lesson by committing a telcom instance with a valid email address:  
If you just restarted your server - no problem. But if you just fixed the script and ran it again, it will fail. This is because the old instance that has the bad email is still in memory and will continue to fail... to remove it from memory simply run (rollback) or rollback(), which is the opposite of (commit) or commit(). Then run your new fixed script.

1. Update the script in your scratchpad to read:

       (define e (training:Person'new (: firstName "Glen") (: lastName "Connor")))
       (define tp (read-instance training:TelcomType '() '(= name "Business") '()))
       (define telcom (training:EmailAddress'new (: entity e) (: type tp) (: name (tp'name)) (: address "glen@nexj.com") (: displayName "Glen Connor")))
       (commit)

   JavaScript

       var e = new #"training:Person"({firstName: "Glen", lastName: "Connor"});
       var tp = #"read-instance"(#"training:TelcomType", null, scm("'(= name \"Business\")"), null);
       var telcom = new #"training:EmailAddress"({entity: e, type: tp, name: tp.name, address: "glennexj@nexj.com", displayName: "Glen Connor"});
       commit();

2. Save your changes.

3. Restart the Server Console and execute the updated code. The last line of the console output should indicate that the e-mail address was successfully saved:

       ; 17:47:47,442 DEBUG [UnitOfWork] Commit completed

4. Close the console.

## Update the revision and publish (optional)

Only do this if you want some experience publishing a model. It will take some time. You did not make any changes to the database schema, so you do not need to make any changes to the upgrade file, dump file, or the database. There is no need to change the model version.

You did make a change to the way the model operates, so you should update the model revision number. Updating the revision number indicates that you have changed the metadata structure of the model since its last "release."

1. Click the **Validate model** button ![validate.png](https://documentation.nexj.com/__attachments/a_26813d413d9762f169231055409a408f394da767fd14273a469ad354c0df68f4/validate.png?cb=1bfd263d9a5314da8390de47dbf20045) in the toolbar to validate the model and ensure there are no errors.

2. Click the **Set current model** button ![rep_nav_icon.png](https://documentation.nexj.com/__attachments/a_3f95fda5e30ee8e72ca10ec7ca5c9bdd8532e14a080cbc5bb2749f1fed7bf354/rep_nav_icon.png?cb=98aeec9adf5389ce8fe5527ba6c4dc14) to open the Model Library.

3. With the current model selected, click **Edit**.

4. Update the **Revision** field by incrementing the final digit by one.

5. Click **OK.**

6. Click **Publish** . The **Publish Model** window opens.

7. Select the directory that you want to publish your model to and click **Save**.

8. Close the **Model Library**.

9. The console view provides a record of the actions taking place as NexJ Studio publishes the model. If the publish is successful, the message `BUILD SUCCESSFUL` appears.

   Info  
   If the model contains any warnings or errors, you are asked to confirm whether you want to continue publishing. In this course you can ignore warnings, but you must resolve any errors. To disable warnings, ensure checkbox found at **Window → Preferences → NexJ Studio → Disable validation** warnings is checked.

## Try it out

Modify the code in your scratchpad to enter valid email addresses and telephone numbers. Also, you will notice now that since `training:Telcom` is an abstract base (from the previous lessons) you cannot create one of them directly.

Info  
Use the `rollback` library function to clear any previous transaction that has validation errors.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Attributes and data types

A class may acquire attributes directly, or indirectly via a base class, aspect or augment. Directly defined attributes can be found in the "Attributes" tab in the class editor. Meanwhile, attributes that are acquired indirectly can be found in the "Base Attributes" tab. By default, the "Base Attributes" tab will only show 30 attributes from the immediate parent class. Developers can open the expanded search field to increase the number of attributes shown, or to narrow down attributes by name, type or other criteria.

When defining an attribute for a class, the name and type must be specified as a minimum. By convention, all attributes should have a description that describes the purpose or usage of the attribute. Ideally, any relevant use case or bug numbers should be listed too. An attribute could be of a primitive type like a boolean, or it can be an association to another class like Entity. See below for a list of available primitive types. The "Select Class..." button for an attribute type allows a developer to choose any existing class as the type.

An attribute can be calculated or persisted to a data source. In the "Value" tab, an attribute can be given an "Initializer" or "Value" expression. The initializer is commonly used to default a persisted attribute to some initial value. An attribute with a "Value" expression is otherwise known as a "calculated attribute".

A calculated attribute is usually used to store information that can be derived from one or more other attributes. Such an attribute could be marked as cached to reduce the number of times it is calculated when displayed. Other times, a calculated attribute can be used to provide a simplified facade for accessing information. For example, the entityStatus of an Entity provides a simplified view of the isClient, isProspect, and isFormerClient flags.

A transient attribute is an attribute that is neither persisted nor calculated. Such attributes are usually used as temporary placeholders in the UI, and are read during update or commit to trigger some specific logic. Another usage for this would be for passing context between events or actions. This is an uncommon scenario though, and is rarely needed.

## Primitive Data Types

The following data types are available to attributes of classes. Valid values for each data types are:

**any**

Any type of data. This is used to store information that does not fall under a traditional type. Usually this is used to store a scheme list.

IMPORTANT: Attributes of type "any" should be made protected, as such attributes are a potential security hole when left public. If a malicious lambda expression is inserted into an attribute via RPC, then it may be unknowingly executed and cause damage. If the presentation layer needs a scheme list or lambda expression from the business model, then expose it via a public event instead.

**binary**

Binary data of arbitrary length.

**boolean**

Either true or false. Boolean values are typically the result of logical expressions. For example, the expression `5 < 10` returns the Boolean value `#t`, and the expression `5 > 10` returns the Boolean value `#f`.

**currency**

Numeric values that represent a monetary value. Currency data types are persisted as decimals.

**date**

Values that represent a calendar date. Date values cannot include a specific time value and persist at 0 hour UTC.

**decimal**

Numeric values that contain decimal fractions.

**double**

Values that represent a double-precision 64-bit floating-point number. Doubles can be used to store numbers that are too small or large to be stored in a float.

**float**

Values that represent a single-precision 32-bit floating-point number. They can be either whole numbers or numbers that contain a fractional component. For example, the values `2.0` and `2.1` are both floats.

**integer**

Whole numbers that do not include a decimal place. For example, the value` 2` is an integer, while` 2.0` is not.

**long**

Values that represent a 64-bit integer. Longs can be used to store numeric values that are too large to be stored in an integer data type.

**percentage**

Values that represent a percentage. Percentage data types values are persisted as doubles. In the user interface, percentage data type values are formatted as percent when a field or label is bound to the attribute.

**string**

Sequences of characters that represent text. String values are surrounded by double quotation marks. For example, the value `"Hello World!"` is a string.

Strings may contain letters, numbers, and symbols. The symbols `"` and` \` are special characters that cannot be written directly in a string. If you want to write a double quotation mark in a string, use the token `\"`. If you want to write a backslash in a string, use the token `\\`.

**timestamp**

Values that represent a specific date and time. Timestamp values differ from date values in that they include a time component and can be mapped to time zones other than UTC.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Audit trails

Audit trails allow you to monitor the history of users' interactions with objects in NexJ CRM. An*audit trail* is an event log that records actions that users perform, such as viewing or modifying a contact. It provides you with an itemized history of each action, including who performed it, when it was performed, and a description of what was done.

You can view audit trails in NexJ CRM and NexJ Admin Console. Each application provides you with a different overview of audit trails:

* In NexJ CRM, each object has its own audit trail. Audit trails are maintained for most major types of objects in the system, such as contacts and leads. For example, when a user creates a lead, the creation action is recorded in the new lead's audit trail. You can view an object's audit trail in the object's**Audit Trail** tab.

* In NexJ Admin Console, a single audit trail is maintained for all audited objects in the system. For example, if a user deletes a contact, then creates a lead, both actions can be viewed in the audit trail in NexJ Admin Console. You can view this audit trail on the Audit Trail page.

The types of actions that are audited depend on the configuration of your system. For more information about configuring audit trails, see [Configuring audit trails](https://documentation.nexj.com/crm-technical/25.11/Public-On-Prem/configuring-audit-trails.md).

Audit trails are not visible by default in NexJ CRM or NexJ Admin Console. To allow a user to view audit trails, you must grant them the AuditLogView privilege in NexJ Admin Console.

Boolean values in the **Audit Trail** tab for activities, batch emails, business processes, entities, opportunities, and service requests display as 0 or 1 instead of displaying as a respective value associated with an attribute.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Available communication type properties

You can modify the properties of any existing communication type using the Edit Communication Type dialog accessed in the Detail area. This dialog allows you to modify the following fields:

**Name**

The name of the communication type that serves as its identifier in NexJ CRM.

**Description**

The description of the communication type.

**Icon**

The icon that represents an instance of the communication type in NexJ CRM.

**Class**

The class of the communication type which provides information on how the value can be entered in NexJ CRM for this communication type. For example, if the class is specified as `Telephone Number`, then the value entered in NexJ CRM must be in the form of a telephone number.

**Default to field**

Allows you to set the value entered for this communication type as one of the default communication type values in NexJ CRM such as work phone, home phone, and so on.

**End User Read Only**

Allows you to make this communication type read-only. If checked, this communication type will not be accessible in NexJ CRM; users will not be able to add it to any entity's profile in NexJ CRM. The only way that communication information can be added to or removed from an entity's profile with this enabled is by modifying the database directly.

**Unique**

When enabled, an associated contact type may have only one instance of the communication type entered in their profile.

**Reference Name**

The reference name of the communication type.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Background: Creating a DataSource

Optional  
This optional background material describes the steps we went through to create the new training:DB datasource used in the fundamentals training. You don't need to do this work, but it is provided so you can better understand the concepts of .datasource files, .upgrade files, and .environment files.

## Creating a DataSource

To persist your data, it needs to be stored somewhere. Data sources define the structure of the data storage, not the physical implementation. You associate your data source with a physical system using [environment file](https://community.nexj.com/documentation/reference/modl/latest/environment.html) settings.  
We are covering creating a .datasource file here so you get a good understanding of what this important concept is all about. That said, you will usually be using existing .datasource and .upgrade files and not have to create them that often.

### Learning activity

This learning activities is not to be completed during training. It is for background information only and may be used as a guide if you need to create a new module.

There are three approaches to creating a data source, depending on your use case. An important concept with MODL development, is to avoid customization wherever possible. Augmentation is always better.

* **Use Case: Working within a project** - If you are adding, or changing a class within a project, you may simply use that module's data source. For example, add your new tables to `DefaultRelationalDatabase.datasource` in finance.

* **Use Case: Enhancing a Project** (with a namespace or mixin) - If you are enhancing the behavior of a project from another module, it is better to use a data source augment.

* **Use Case: Independent Module** - If you are creating an independent module, you should create a new data source and optionally a new upgrade file. Create a new upgrade file if you are in a separate mixin project with a module name (namespace) set. The data source needs some initial settings, aspects, and a version table. This is shown in the following example.

Our training module is based on finance, but it really is independent. So we will be using folder namespacing and re-using the main.upgrade file. Let's define a new relational data source called `training:DB`.

1. In the **Persistence** \>**Data Sources** layer, select **New Data Source** and complete the information as follows.

   ![Create a new Data Source dialog](https://documentation.nexj.com/__attachments/a_4765d8cdcbf35d98f0aa4f659fadfd415c84651b1f5c751143652f50048e4c2d/image2019-11-5_17-56-51.png?cb=48a0ab7a742d5155b0bac214cbcd3435)

2. Click Finish.

3. Press the Save button.

   Important  
   This is important so you have a starting point in the source code control's local history. We will use this to generate upgrade steps going forward.

4. Paste the following into the Source tab of your data source file.

   XML

       <RelationalDatabase aspects="training:CORE_ASPECTS" prefix="TRN" versionTable="Version">
          <Tables>
             <Table aspects="!LOCKING !PARTITIONED" name="Version" primaryKey="Version.PK" type="managed">
                <Columns>
                   <Column allocation="varying" caseInsensitive="false" description="Metadata namespace" name="namespace" nullable="false" precision="128" type="string"/>
                   <Column allocation="varying" caseInsensitive="false" description="Metadata version" name="version" nullable="false" precision="128" type="string"/>
                   <Column description="Metadata upgrade step within the version, -1 if the version is up to date" name="step" nullable="false" type="integer"/>
                   <Column description="True if the schema may be upgraded automatically" name="upgradable" nullable="false" type="boolean"/>
                   <Column description="True if the schema contains test data, which can be recreated automatically" name="test" nullable="false" type="boolean"/>
                   <Column description="True if the initial (seed) data has been loaded" name="loaded" nullable="false" type="boolean"/>
                   <Column caseInsensitive="false" description="Prefix for data encrypted with the activeCipherKey" name="activeCipherPrefix" type="integer"/>
                   <Column caseInsensitive="false" description="The public key of the active cipher PKI key pair" name="activeCipherKey" type="binary"/>
                   <Column caseInsensitive="false" description="The public key of the obsolete cipher PKI key pair" name="obsoleteCipherKey" type="binary"/>
                </Columns>
                <Indexes>
                   <Index name="Version.PK" type="cluster" unique="true">
                      <IndexColumn name="namespace"/>
                   </Index>
                </Indexes>
             </Table>
          </Tables>
       </RelationalDatabase>

   Press Save. Go back to the Schema tab of the Relational Datasource Editor for your new training:DB data source and you should see something like the following image on the Columns tab.

   ![image2020-1-9_10-43-9.png](https://documentation.nexj.com/__attachments/a_ccfd238f037abeb946b5b654c08ff70661aa00d788c7a4591781cd387ab2d993/image2020-1-9_10-43-9.png?cb=30ca381197e2dbf7479370948798f683)

   Relational Datasources  
   A relational datasource is a logical representation of the structure of a database. You can read more about them in the [Model Definition Language Reference](https://community.nexj.com/documentation/reference/modl/latest/relationaldatabase.html).

   We want to be able to track changes between versions of the database for our new datasource, so we include a Version table. This is a standard table that keeps track of the current state of the database. You can look at what columns and indexes it has in the editor. During these lessons, we will be adding more tables to this datasource to support persisting our classes.

As mentioned earlier, in an independent mixin project with a module name set, we would need to create a .upgrade file, but we are using the inherited main.upgrade from finance in our training.

## Editing the upgrade file

To persist your data, you need to update the physical databases from our logical datasource to reflect the changes.

To do this, you'll need to declare the database changes in the upgrade file. An upgrade file contains one or more upgrades, each associated with a specific version of the model. Each upgrade contains steps that define the changes that take place from an old version of the model to the new one associated with the upgrade. Once an upgrade file has been defined for the current version of a model, you can use it to migrate the data store for any previous version of the model to the current one.

NexJ Studio can create the upgrade steps by comparing the current version of the model to an earlier version of the model. In this case, compare your changes (that is, the addition of the Version table) to a published model or local history.  
Upgrade Files  
An upgrade file is set of changes to a Relational Datasource from version to version. It is used by tools like the Data Load Tool and the Database Schema Tool to bring databases up to date in different environments. You can read more about them in the [Model Definition Language Reference](https://community.nexj.com/documentation/reference/modl/latest/upgrade.html).

### Learning activity

This learning activities is not to be completed during training. It is for background information only and may be used as a guide if you need to create a new module.

To update the upgrade file:

1. In the Persistence layer, click the **Data Sources** tab.

2. Right-click click on the training:DB.datasource and select **Generate Upgrade Steps...**

3. `Set the `Current Data Source to training:DB and the Upgrade should be Main.

4. Under Old Data Source, select **Local History**.

   The training version of studio is configured to keep old revisions around, even for large files. This is controlled with the Window/Preferences... General/Workspace/Local History options.

   Select the first revision time entry

   This allows you to look at the changes you've made to the file recently and find the appropriate old version to start from.  
   Another approach would be to use Old Model and use a previously published original model as the old model.

5. You want to select the Revision that doesn't have your table in it. You can look at the differences by double clicking on the Revision History items. Your compare should look something like:

6. ![image2020-1-9_17-17-38.png](https://documentation.nexj.com/__attachments/a_18d21b5302542f8d608d3de44386a3591a6be53dac5d3d8e6252868fe1f55eed/image2020-1-9_17-17-38.png?cb=54da34da1ea556497489a6a3b240f945)
7. Press Next. Change the version to 2.1092.1307.167.53, set the description to "Added the training:DB Version table" and save.

8. You always must ensure that your current model's version matches the upgrade version. Click the **Set current model** button ![Set current model button](https://documentation.nexj.com/__attachments/a_0e53ae9dbedba2973b31b6ec4864d39119cd6700c0b5b56b998b08f224d50319/rep_nav_icon.png?cb=98aeec9adf5389ce8fe5527ba6c4dc14) in the toolbar to open the **Model Library**.

9. With the training model selected, click **Edit** . Update the **Model Version** field to match the version associated with the upgrade you just created. If your upgrade is associated with version 2.1092.1307.167.53, change the Model version to 2.1092.1307.167.53. This enables NexJ Studio to recognize that the model has changed from the previous version and that your update needs to be applied to the supporting data stores.

10. Click **Apply and Close** . The new version is now reflected in the table in the **Models** tab.

11. Click **Close**.

12. We need to make our Environment file aware of our new datasource. Click the set current server button ![image2020-1-22_15-55-41.png](https://documentation.nexj.com/__attachments/a_68d49a04fcaba827e235e5d9f687ee498f8a83b0c04b743afd4c5cee973957c2/image2020-1-22_15-55-41.png?cb=728a716a0c7045e7b0d481c92c0bffb4) and navigate to the Data Source Connections bottom tab. Select the Relational Database Connection and press the arrow in the lower Data Sources list.

    ![image2020-1-22_15-57-36.png](/__attachments/a_e8e4778ddb8b1eaa533a720483a05ee13feb371494faf8f0903efab7022fce5b/image2020-1-22_15-57-36.png?cb=21ca33562f2075d571aceaea61f73f60)

    Add training:DB to the selected data sources and press OK, then Save.

    Select Run/Run Tool/Data Load Tool from the menus and run the recreate command against the training:DB. This will establish a starting point for your TRNVersion table.

    ![image2020-1-22_16-6-23.png](/__attachments/a_1ccd40cf4d9bd9fba23d9c47d51b61b7b3837422be3d37ac0b6ecbaa219d97d3/image2020-1-22_16-6-23.png?cb=d4ba85cab863fb769cf91794776dd1ad)

    Then use the same tool to run the upgrade command on the DefaultRelationalDatabase datasource.

For locking you work correctly, you need to create a new training:LOCKING (meta/training/LOCKING.meta) aspect with the following contents.
XML

    <Aspect>
       <Attributes>
          <Attribute initializer="0" name="locking" required="true" type="integer"/>
       </Attributes>
       <PersistenceMappings>
          <PersistenceMapping dataSource="training:DB" lockingAttribute="locking">
             <RelationalMapping primaryTable="LOCKING">
                <AttributeMappings>
                   <AttributeMapping column="locking" name="locking"/>
                </AttributeMappings>
             </RelationalMapping>
          </PersistenceMapping>
       </PersistenceMappings>
    </Aspect>

This background material showed you how we set up our new data source. As stated earlier, you don't need to do this often, but it's good to understand the concepts.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Banner

## Description

Use the banner on the top of a detail view to display basic details about a record. When navigating between different tabs, the banner remains consistently displayed.

The banner consists of different combinations of cards with two-line tiles and three-line tiles.

### When to use

Use in detail views to display information about an object. If a workspace has a detail view, then the detail view should include a banner above the other information.

### Variations

The contents of each banner depend on the object and the context.

#### Mini-card components

##### Main three-line tile card

Only one main three-line tile is included in the banner. If there are any object-level commands, then this tile is required. It contains the following:

* Large avatar or icon for the subject of the detail view.

* Three lines of text describing the content. If more than one piece of information is displayed on a line, the pieces of information are separated by a vertical line ( \| ).

* An overflow menu containing commands which are contextually appropriate to the workspace as well as the specific item.

##### Badge card

Contains badge icons. The card is two badges tall and can be as wide as needed to accommodate the required badges, to a maximum of 6 badges.

##### Two-line tile card

Each card contains two-line tiles, with an icon, the value, and the value description below.

### Interactions

There are no banner-level commands, but cards within a banner can have associated commands. As a rule, the first banner card is a three-line tile and it has an overflow menu containing contextual command in the top left corner.

If the contents of the banner exceed the size of the screen, you can scroll to the right and the left using the right and left arrows.

## Example

![Banner.png](https://documentation.nexj.com/__attachments/a_74d18abf8e7e8e67c4f7b2adce18c0849b5f179299182c03502e44f92cdedff5/Banner.png?cb=98f1458be7149e4fb0a7574589e16b07)

## Specifications

![Banners.png](https://documentation.nexj.com/__attachments/a_94e144f36ae80a7095da89c78b20470073629069375c3a35b0d40d7646ba06a8/Banners.png?cb=3cb293a9aca27e28c95d1b486d9987b4)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Banner

## Overview

Custom control defining a banner. This control supports multiple cards that can have associations to model binds.

## Example

This example is taken from `EntityDashboard`.
XML

                <CustomView head="true" name="PersonHTMLLabel" type="md:Banner">
                   <Toolbar name="tbPersonDashboard">
                      <Item action="true" caption="IDS_EDIT" event="sysProperties" icon="edit" name="mitEditPerson"/>
                      <Item caption="ids.email" event="sendEmailToContact" icon="email" name="mitSendMailItemPerson" visible="`(@ model item hasEmail)"/>
                   </Toolbar>
                   <Models>
                      <Instance name="item" value="(@)">
                         <Bind name="summary" value="summaryStringStatusPositionCompany"/>
                         <Bind name="birthTime" value="summaryStringBirthTimeAge"/>
                         <Bind converter="image" name="avatar" value="(image . image)(data . mimeData)(type . mimeType)(icon . icon)(default . "icon:")"/>
                         <Bind name="fullName" value="fullNameDetail"/>
                         <Bind name="addressName" value="defaultAddress name"/>
                         <Bind name="address" value="defaultAddress fullName"/>
                         <Bind name="addressIcon" value="defaultAddress type icon"/>
                         <Bind name="telcomName" value="defaultTelcom name"/>
                         <Bind name="telcom" value="defaultTelcom fullAddress"/>
                         <Bind name="telcomIcon" value="defaultTelcom type icon"/>
                      </Instance>
                      <Collection name="badges" value="(@ dashboardBadges)">
                         <Bind name="captions" value="tooltipText"/>
                         <Bind name="icons" value="icon"/>
                      </Collection>
                   </Models>
                   <Configuration>
                      <Properties vertical="#f"/>
                      <Children>
                         <TopBannerCard>
                            <VerticalTile caption="item fullName" comment="item birthTime" description="item summary" icon="item avatar" round="#t"/>
                            <BannerBadges icons="badges icons" items="badges" vertical="#t"/>
                         </TopBannerCard>
                         <BannerCard header="#t" responsive="#t" size=""l"">
                            <Tile caption="item fullName" comment="item birthTime" description="item summary" icon="item avatar" name="personBanner" size=""l"" tool=":toolbar"/>
                         </BannerCard>
                         <BannerCard responsive="#t" size=""s"">
                            <BannerBadges captions="badges captions" icons="badges icons" items="badges"/>
                         </BannerCard>
                         <BannerCard size=""m"">
                            <Tile caption="item telcom" comment="item telcomName" icon="item telcomIcon"/>
                            <Tile caption="item address" comment="item addressName" icon="item addressIcon"/>
                         </BannerCard>
                      </Children>
                   </Configuration>
                </CustomView>

### Explanation

The configuration defines multiple cards to be shown in the banner, with one `TopBannerCard`. For each card, the properties are configured using custom binds relative to `item` and `badges`.

The `Models` metadata specifies the binds for the configurations by defining `item` and `badges` relative to the instance of `Entity` that the layout containing this custom view is associated with.

If you set the `responsive` property to true, the card will be resized in smaller screens. If `responsive` is false (default), the card is not displayed in smaller screens.

For details regarding the b`anner` control, see the "Banner" custom control, which is defined in the finance-md project.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Behavior: UI events, UI actions, and data actions

**Learning objectives**

In this lesson, we will learn:

* How `UIEvents` and `UIActions` handle actions taken by the user.

* About `DataActions`, which are events raised from changes in the model.

**Key concepts**

[`UIEvent`](https://community.nexj.com/documentation/reference/modl/latest/UIEvents_UIEvent.html) - fires when a user performs an action such as pressing a button, selecting an item in a menu or list, or selecting a row in a grid. There are system `UIEvents` and custom `UIEvents`. Custom `UIEvents` are created in the Presentation layer. You can handle `UIEvents` by configuring a `UIAction`.

There are two main ways to fire UI events:

* Interaction with a control (view) on which a `UIEvent` is set.

* Running a script that uses the [`fireUIEvent`](https://confluence.nexj.com/display/PUBDEV9/.UX+Scripting+v9.2#fireuievent) API method.

A `UIEvent` can be enabled or disabled dynamically by binding it to a boolean class attribute or the `ClientModel`.

[`UIAction`](https://community.nexj.com/documentation/reference/modl/latest/t_UIActionsType_UIAction.html) - a user interface handler that responds to a `UIEvent` in portlets, dialogs, and layouts. A` UIAction` launches (and contains) one of the following when triggered by an event:

* Pop-up action - opens a dialog.

* Navigate action - navigates to another portlet in the application.

* Script action - runs a custom script.

[`DataAction`](https://community.nexj.com/documentation/reference/modl/latest/t_Controllable_DataActions_DataAction.html) - a handler for a change that occurs in an underlying model.

Based on your work from the [previous lesson](https://confluence.nexj.com/display/PUBDEV9/.Structure%3A+Portlets%2C+Dialogs%2C+and+Layouts+v9.2), your files should appear as follows. If not, copy the code below and paste into your files before continuing.
XML

    <Portal caption="idsa.training.portal.Contact.caption">
       <Tool name="toolNotifications" caption="ids.notifications" icon="icon:notifications" event="setSidebar" parameter="NOTIFICATIONS"/>   
       <WorkspaceRef name="refSandbox" workspace="training:Sandbox"/>
       <WorkspaceRef name="refHome" workspace="mda:Home"/>
       <WorkspaceRef name="refContacts" workspace="mda:Contacts"/>
       <Drawer event="setSidebar" name="refNotifications" portlet="mda:NotificationDrawer"/>
    </Portal>

XML

    <Workspace caption="idsa.training.workspace.Sandbox.caption" icon="icon:attach_money">
       <Page name="page">
          <Tab layout="cols:3 fluid:true" name="tabDetail" type="grid">
             <PortletRef name="training_EntityList_portlet" portlet="training:EntityList"/>
          </Tab>
       </Page>
    </Workspace>

XML

    <Portlet>
       <Composite name="composite">
          <LayoutRef layout="training:EntityList" name="training_EntityList_layout"/>
       </Composite>
    </Portlet>

XML

    <Layout class="Entity">
       <UIActions>
          <UIAction event="sysProperties">
             <Popup association="tblEntityList" edit="true" screen="training:mda:EntityProperties"/>
          </UIAction>
       </UIActions>
       <Composite name="composite">
          <Table caption="idsa.training.layout.EntityList.caption" name="tblEntityList" rows="(@)">
             <Menu name="mnuRightClick">
                <Item caption="ids.edit2" event="sysProperties" icon="edit" name="mitProperties"/>
             </Menu>
             <Column name="colType" caption="idsa.training.layout.EntityList.type" icons="type iconDefault"/>
             <Column name="colIcon" caption="idsa.training.layout.EntityList.icon" icons="(image . image)(data . mimeData)(type . mimeType)" texts="initial"/>
             <Column name="colName" caption="idsa.training.layout.EntityList.name" length="100" values="fullName"/>
          </Table>
       </Composite>
    </Layout>

XML

    <Dialog caption="ids.caption.editPopup">
       <Composite cols="2" name="composite">
          <LayoutRef layout="mda:EntityPropertyDashboard" head="true" name="mda_EntityPropertyDashboard_layout"/>
          <Switch caption="ids.detail" head="true" name="grpDetailLayout" span="2" value="mda_EntityPropertyDashboard_layout">
             <Composite case="UserPerson" name="compUser">
                <LayoutRef association="mda_EntityPropertyDashboard_layout" layout="mda:UserPersonEditDetail" head="true" name="mda_UserPersonEditDetail_layout"/>
             </Composite>
             <Composite case="Person" name="compPerson">
                <LayoutRef association="mda_EntityPropertyDashboard_layout" layout="mda:PersonEditDetail" head="true" name="mda_PersonEditDetail_layout"/>
             </Composite>
             <Composite case="Company" name="compOrg">
                <LayoutRef association="mda_EntityPropertyDashboard_layout" layout="mda:CompanyEditDetail" head="true" name="mda_CompanyEditDetail_layout"/>
             </Composite>
          </Switch>
          <LayoutRef association="mda_EntityPropertyDashboard_layout" head="true" layout="mda:EntityAuditFields" name="mda_EntityAuditFields_layout" span="2"/>
       </Composite>
       <Actions layout="mda:PopupButtons" name="mda_PopupButtons_layout"/>
    </Dialog>

    idsa.training.portal.Contact.caption=My Training Application
    idsa.training.workspace.Sandbox.caption=Sandbox
    idsa.training.layout.EntityList.caption=Entity List
    idsa.training.layout.EntityList.type=Type
    idsa.training.layout.EntityList.icon=Icon
    idsa.training.layout.EntityList.name=Name

## Using system events

We saw a system UI event in use in the [previous lesson](https://confluence.nexj.com/display/PUBDEV9/.Structure%3A+Portlets%2C+Dialogs%2C+and+Layouts+v9.2) about portlets and dialogs, when we overrode the system's default handling of the `sysProperties` event by adding a `UIAction` handler that opened the `training:mda:EntityProperties` dialog.
XML

    <UIAction event="sysProperties">
       <Popup association="tblEntityList" edit="true" screen="training:mda:EntityProperties"/>
    </UIAction>

### Learning activity

In this lesson, we'll raise another system event by adding the `sysRemove` event to table rows and letting the system handle it.

1. In the `training:EntityList.layout` file, add the following item to the `mnuRightClick` menu.

   XML

       <Item event="sysRemove" name="deleteItem"/>

2. Save and hotswap the UI.

   When you run the application, you should see the **Delete** button ![trash can icon](http://confluence.nexj.com/plugins/servlet/confluence/placeholder/unknown-attachment?locale=en_US&version=2) at the end of the row.

3. Click the**Delete** button.

   You should see a dialog similar to the following:

   ![Dialog seen when deleting an entity](https://documentation.nexj.com/__attachments/a_abb0dfd06d081437f2a0bbc33de237c52fe7bf306c928b075317a619a61e188e/image2019-2-26_17-34-20.png?cb=4f412d0b2314f1b3fb55fc2cffed0834)

Without having to declare very much, we have a delete pattern. The **Delete** button and Delete hover text is retrieved from the `sysRemove` UI event's declaration. The **Confirm Delete** dialog is the built-in handler for `sysRemove` and it gets the name and description of the type from the class caption metadata. This dialog is responsive, works with accessibility, is compatible with international languages and formats, is testable, and works with performance counters with very little configuration.

The platform enforces data security and privileges automatically. If you hover over a row for a user, you will see that the **Delete** button becomes disabled. This is because the logged-in user doesn't have permission to delete `User` records. We do have the ability to delete `Person` and `Company` records.

You may also find that the application does not let you delete a company or contact if there are associated opportunities, service requests, or securities. This is the system enforcing the referential integrity declared in the business model.

## Overriding system and other events

Let's override the default `sysRemove` behavior for our layout. To do this, we need to add a `UIAction` handler to our layout. Instead of a pop-up, we will use a script `UIAction`.

### Learning activity

1. Open `training:EntityList.layout` and go to the **UI Actions** tab. You should see the pop-up action we added in the previous lesson.

   ![image2019-2-26_12-2-12.png](https://documentation.nexj.com/__attachments/a_25dac8354854669361abf798af503ee1a3a5a1dd43880252a657a8ec124a3fdc/image2019-2-26_12-2-12.png?cb=aad8ebe993386318ee6627456e92e494)

2. Press the **Add** button ![Add button](https://documentation.nexj.com/__attachments/a_437a51dce9a0b0333f174cb166b18bceb75582c5b9630d7fd4905e94a183e9af/image2019-2-26_12-3-2.png?cb=4bd7c684e37ac9688e0c334e138659c7) and select **Script** . Set the event to listen for the `sysRemove` event.

   ![image2019-5-9_17-35-18.png](https://documentation.nexj.com/__attachments/a_abd9d10c244802dc2070f048e1d0db784b8d514ce13b3c0cff3bb019faffc8a3/image2019-5-9_17-35-18.png?cb=0ef89f3d746b5342dff0aebe6925b768)

3. Enter the following script:

   Scheme

       (@ confirm : "Delete is not allowed" "Delete Override" '("Ok"))

   Autocomplete in NexJ Studio UI script editing has not been fully updated for 9.x APIs. As a result, you may occasionally see the "Could not resolve symbol". You can ignore this. NexJ Studio should soon be updated to eliminate this.

   The script displays a confirmation dialog. The confirm method works as follows:
   Scheme

       confirm <message> <caption> <actions>

       Displays an info/confirmation dialog. Returns the ordinal number of the selected action. The default action is indicated by 0.

       @arg {string} message Dialog message - this can be a string id for internationalization.
       @arg {string} [caption] Dialog caption - this can be a string id for internationalization.
       @arg {string[]|scm.Symbol[]|scm.Pair} [actions] Action captions, localized string ids or event names. The first action is the default action.
       @retcc {integer|undefined} 0-based action ordinal number.
       @example (@ confirm : "message" "caption" '("Ok" "Close"))

       Note: replaces (this'createMessageBox)

   The`(@` in this script is the controller of the layout. The API of the controller can be reviewed in the [UI Scripting document](https://documentation.nexj.com/crm-technical/25.11/Cloud/ux-scripting.md).
4. Save and hotswap. When you press the **Delete** button, you should see a dialog similar to the following:

   ![Dialog for Delete override](https://documentation.nexj.com/__attachments/a_ed9b46dbfe0d5367a70947557991fb549c22de3de1fa9d18f179b75cbd5aafa7/image2019-2-26_13-19-16.png?cb=6c75646e17732ae734da926c15b2d00d)

If a `UIAction` returns `#f` (false), the event propagates onwards and can be handled by another` UIAction` or the default system event handler. If a `UIAction` returns `#t`, it stops as the action is considered complete. Our script returns whatever the call to `(@ confirm` returns, which is 0 if you press the **OK** button. Returning anything other than `#f` tells the system that the event has already been handled. If we had returned `#f`, control would have been passed up the chain of handlers.

## Enhancing system and other events

Let's modify our `UIAction` so we can conditionally pass the event on to the platform for handling.

### Learning activity

1. Modify the `UIAction` script to:

   XML

       (if (= (@ confirm : "You can stop the proccessing here by pressing STOP." "Delete Override" '("Continue" "Stop")) 0)
          #f
          (begin
             (@ confirm : "You decided not to delete the item." "Delete Override" '("Ok"))
             (@ flash : "Aborted Delete")
          )
       )

   This script checks the return value of the first confirm dialog. If the user clicks **Continue** , an `#f` (false) is returned, the platform treats the event as unhandled, and the default processing takes place. If the user clicks **Stop**, another confirm dialog and a flash will display.
2. Save and hotswap the UI.

Now we can abort the delete, or pass control to the system delete handler. Security and referential integrity rules are automatically enforced, so there may be some entries in the list that you are not able to delete.

## Adding your own UIEvents

In this exercise, you'll add a button to your table that updates the table's caption. The following high-level steps are required to implement this feature:

* Create a custom `UIEvent` called `trainingUpdateCaption`.

* Add a button to raise the `UIEvent`.

* Write the `UIAction` handler.

### Learning activity

1. In NexJ Studio, go to **File** \> **New** \> **UI Events** , and enter the name `training:UIEvents`. Go to the source tab and paste the following:

   XML

       <UIEvents>
          <UIEvent caption="UpdateCaption Caption" icon="icon:fast_rewind" name="trainingUpdateCaption" tooltip="UpdateCaption Tooltip"/>
       </UIEvents>

2. To fire the event, let's place more menu items on the rows and a new toolbar item on our table. The toolbar and menu elements should be as follows:

   XML

       ...         
       		 <Toolbar name="toolbar">
                   <Item event="trainingUpdateCaption" name="updateCaption2" value="&quot;from toolbar&quot;"/>
                </Toolbar>
                <Menu name="mnuRightClickMenu">
                   <Item caption="ids.edit2" event="sysProperties" icon="edit" name="editItem"/>
                   <Item event="sysRemove" name="deleteItem"/>
                   <Item event="trainingUpdateCaption" name="updateCaption" value="&quot;from row&quot;"/>
                </Menu>
       ...

3. Save your work and reload the UI. This will add buttons such as:

   ![Entity list with new buttons](https://documentation.nexj.com/__attachments/a_0f829dbf98027152f57ad52b78bbc9f6f1bd19c1dab067af3a18edca5f7a17c9/image2019-5-13_9-48-38.png?cb=62aebd3732e8385dfd8c59855c43c502)

   Notice the value entries for the menu items. The value property is the item value constant passed as a `UIEvent` parameter. We set the toolbar button's value to `from toolbar` and the row menu item's value to `from row`. When a `UIEvent` is raised, it carries with it the event type, the source (in this case the table), and any parameters sent along. This allows for conditions to be set up to handle specific events appropriately.

4. To handle these events and show their source and parameters in a message box, add a new `UIAction` script to `EntityList.layout` that listens for the `trainingUpdateCaption` event. In the **UIActions** tab, click the **Add** button and select **Script** . Add `trainingUpdateCaption` as the event and enter the following script:

       (@ confirm : 
          (string-append "Parameter: " parameter) 
          (string-append "Source: " (source'name)) 
          '("ok")
       )

5. Save and re-load the UI. Now, when you click on either button, you should get:

   ![image2019-3-7_22-40-51.png](https://documentation.nexj.com/__attachments/a_a017cf57faab29753c1b60dc2ca29511bbace230393291d37685e8fff158195a/image2019-3-7_22-40-51.png?cb=597be12ef5eeb6319c4bea9b49fb7213)

   or

   ![image2019-3-7_22-40-14.png](https://documentation.nexj.com/__attachments/a_667f9c54c0dea25fc76d06dc90c33e5480cbc2338b260b2868b60f245824b899/image2019-3-7_22-40-14.png?cb=2ad71cca23867ffc189f615404d0b712)

6. Update the table's caption according to where the click came from. Replace the `UIAction` script with:

       ((@ findView : 'tblEntityList)'caption parameter)

7. Reload your UI.

   This updates the table's caption to "from row" or "from table", depending on which button you click.

8. To see the `findView` controller method in action, update the script to the following:

   Scheme

       ((@ findView : 'tblEntityList)'caption (format "{0} - {1}" parameter ((((@ findView : 'tblEntityList)'items)'item)'fullName)))

   The `findView` controller method is useful for manipulating, enabling, or disabling UI controls. Similarly, the` findModel` controller method can be used to inspect the underlying model or instances with code such as:

   `((((@ findView : 'tblEntityList)'items)'item)'fullName)`

   The result of this would be:

   ![image2019-5-13_9-51-48.png](https://documentation.nexj.com/__attachments/a_810194396d0985d721ea5f7ad32c9c0900e918abe10925c6a1dcd69c43dee42d/image2019-5-13_9-51-48.png?cb=d8b47da1c252e7d9951a4c6dd74d6cdd)

For additional information about UX scripting, see the [UX Scripting API Reference](https://confluence.nexj.com/display/PUBDEV9/.UX+Scripting+v9.1#parameterized-uievents).

## Data actions

A [data action](https://community.nexj.com/documentation/reference/modl/latest/t_Controllable_DataActions_DataAction.html) is a handler similar to a UI action, but instead of listening for UI events fired from views, it listens for changes in models.

While aren't covering data actions in depth here, here is a quick example.

### Learning activity

1. Paste the following in the **Source** tab of `EntityList.layout`:

       <Layout class="Entity">
          <DataActions>
             <DataAction condition="(= locator 'selection)">
                <Script><![CDATA[(logger'info "Selection changed to " (@ model item lastName))
       ((@ findView : 'tblEntityList)'caption (string-append "Selection changed to " (@ model item lastName)))]]></Script>
             </DataAction>
          </DataActions>
       ...
       <rest of existing file>

2. Switch to the **DataActions** tab. You should see something like the following:

   ![image2019-3-10_21-38-27.png](https://documentation.nexj.com/__attachments/a_c2e1af81ab0edc999481d3d19954f67a466d1c90460f45dd5eb6dad06520aa89/image2019-3-10_21-38-27.png?cb=f0b43ec6afd6276e92d43f9a3926ab95)

   This data action will listen for a selection change in the EntityList model. When a change is detected, the caption of the table will update accordingly:

   ![image2019-5-13_9-54-51.png](https://documentation.nexj.com/__attachments/a_606d2336599adaeb5728fa96721a42b09c6b9c073fd92d9bc950fc6bf289351b/image2019-5-13_9-54-51.png?cb=74f685233f4055bc77e2aee984ec3a3f)

3. Reload and refresh your browser. When we click in the application, the console (F-12 console tab) will output something similar to the following:

   ![image2019-3-10_21-42-57.png](https://documentation.nexj.com/__attachments/a_e6bd3a28929f4c631e565c6436af1b57ea9bcad230b5a1373065d9fe7f24c64d/image2019-3-10_21-42-57.png?cb=18028fb7ef4fcb415a5bffdaf9749ebb)

   These are the messages logged to the browser's console when the clicks occurred.

Further examples of using data actions can be seen in the layouts within the finance model.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Business Model

The Business Model layer enables you to model your business including business concepts in classes, class relationships, business logic, and workflows. Classes and aspects enable you to model your enterprise concepts and their relationships, and powerful design-time workflows are used to model your business processes.

## Model Description Language business model elements

The business model is composed of structural components, including:

* [Classes](https://community.nexj.com/documentation/reference/modl/latest/Class.html) - A *class*is an abstraction of a business model concept.

* [Aspects](https://community.nexj.com/documentation/reference/modl/latest/Aspect.html) - An *aspect* is a functional grouping of additional attributes or events that may be applied to your model. Aspects are displayed in the **Classes** tab on the Business Model layer.

* [Augments](https://community.nexj.com/documentation/reference/modl/latest/Augment.html) - An *augment* contributes attributes, events, or actions to a given class. Augments are used for modularization, that is, separating the domain model into several modules without circular dependencies between them. They are displayed in the **Classes** tab on the Business Model layer.

* [Enumerations](https://community.nexj.com/documentation/reference/modl/latest/Enumeration.html) - An *enumeration* is a special type of class that provide a list of values along with localizable captions.

* Diagrams - The Diagrams tab contains a UML class diagram editor for working with the model.

Classes and aspects are used to model classes and their relationships, including related behaviors and events. They allow consistent application of business rules and behaviors and support powerful and flexible mapping to physical storage.

The business model also contains behavioral components, including:

* [Workflows](https://community.nexj.com/documentation/reference/modl/latest/Workflow.html) and [Workflow Queues](https://community.nexj.com/documentation/reference/modl/latest/WorkflowQueue.html) - For more information, see [Workflows](https://documentation.nexj.com/crm-technical/25.11/Cloud/workflows.md).

* [Rule sets](https://community.nexj.com/documentation/reference/modl/latest/RuleSet.html) - A set of rules to support run-time configurable business logic.

Workflows are tied to the object event lifecycle. For example, a workflow may be developed for when a new Case is created. Workflows support decisions, forks, queues, and wait states. They can be long-running and able to persist at specified wait states. Use workflows as an effective method of documenting and communicating system behaviors.

The Business Model layer also allows you to work with testing components, including:

* [Unit tests](https://community.nexj.com/documentation/reference/modl/latest/UnitTest.html) - A set of test cases to test model functionality.

* [Unit test suites](https://community.nexj.com/documentation/reference/modl/latest/UnitTestSuite.html) - A grouping of unit tests for different test purposes.

Other components included in the Business Model layer are:

* BI Models - Deprecated. Simplified operational views for reporting.

* [Facets](https://community.nexj.com/documentation/reference/modl/latest/Facets.html) - A set of facets that can be applied to metadata objects.

* Semantic views and semantic attributes

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Business model fundamentals

This learning module provides an introduction to the NexJ Studio platform's business modelling capabilities. It includes hands-on development with classes, associations, and application behaviors in NexJ Studio.  
As you progress through the learning activities, please note that a published model has been included in the training package and was placed in` C:\nexj_studio\13.0-training\training\out\publish` while [Setting up for training](https://confluence.nexj.com/display/PUBDEV9/.Setting+Up+for+Training+v9.3).

You will explore common development and test practices covering the following topics:

* Creating and updating models

* Persisting those models to databases

* Introducing workflows to the model

* Creating and running unit tests

You will gain exposure to model description language elements and concepts including:

* Classes and class attributes

* Calculated attributes and attribute validation

* Data persistence

* Data manipulation through class events and workflows

The course is intended for developers, and support and maintenance resources. The course is also suitable for architects and technical business analysts who will be participating in the design of system extensions and new components.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Buttons

## Description

Use buttons to allow users to take actions on any workspace or dialog.

### Variations

Both text and icon buttons are supported. Use text buttons in full-screen and popup dialogs, summary cards (lists), and in data tables for batch commands which appear after at least one record has been selected.

Use icon buttons in data tables for row-level and table-level commands, and in the search pattern.

### Interactions

Click a button to open or close a dialog, open a menu, or perform other actions.

## Specifications

![Buttons.png](https://documentation.nexj.com/__attachments/a_0b078cc432fce9da03aa7ab53a8a88b851aa80dd7d5645903e1ad2748978de1c/Buttons.png?cb=1162bb114115def9a8bd0ad9971e90aa)

### Detailed specifications

![button.png](https://documentation.nexj.com/__attachments/a_b200344089a64d8312acb5c17ab691f49577feaa9fe8c5ee34e81d06842e83e7/button.png?cb=e82dbb730d4060e1693349c8f72f935c) ![icon_button.png](https://documentation.nexj.com/__attachments/a_a4b30fdecb4439c74307b2b454104418588e9f1df58a894361efb4061e7acc92/icon_button.png?cb=349091acab5ead621844534a09e8f5be)  

|  Element   |  State   |       Prime on light - text        |        White on dark - text        |              White on dark - icons               |              Grey on light - icons               |
|------------|----------|------------------------------------|------------------------------------|--------------------------------------------------|--------------------------------------------------|
| Text       | Enabled  | Roboto, Medium, 14pt, #07589D      | Roboto, Medium, 14pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #000000, 54% |
| Background | Enabled  | none                               | none                               | none                                             | none                                             |
| Text       | Disabled | Roboto, Medium, 14pt, #000000, 38% | Roboto, Medium, 14pt, #000000, 87% | Material-Design-Iconic-Font, 24 pt, #000000, 87% | Material-Design-Iconic-Font, 24 pt, #000000, 38% |
| Background | Disabled | none                               | none                               | none                                             | none                                             |
| Text       | Hover    | Roboto, Medium, 14pt, #07589D      | Roboto, Medium, 14pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #000000, 54% |
| Background | Hover    | #000000, 12%                       | #000000, 12%                       | #000000, 12%                                     | #000000,12%                                      |
| Text       | Focused  | Roboto, Medium, 14pt, #07589D      | Roboto, Medium, 14pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #000000, 54% |
| Background | Focused  | #000000, 12%                       | #000000, 12%                       | #000000, 12%                                     | #000000,12%                                      |
| Text       | Pressed  | Roboto, Medium, 14pt, #07589D      | Roboto, Medium, 14pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #FFFFFF      | Material-Design-Iconic-Font, 24 pt, #000000, 54% |
| Background | Pressed  | #000000, 12%                       | #FFFFFF, 30%                       | #FFFFFF, 30%                                     | #000000,12%                                      |

### Additional reference

[Buttons - Material Design](https://material.io/design/components/buttons.html)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Calculated attributes

This lesson introduces the concept of a calculated attribute and shows you how to create one. By completing this module, you will learn:

* What a calculated attribute is, and when it is useful to create one.

* How to define a calculated attribute by using the Value and Dependency properties.

* How to use the @ shorthand symbol in the Scheme expressions that define a calculated attribute.

## Create a calculated attribute

All the class attributes that you have created so far have been assigned values that are persisted to the data store. Sometimes the value of an attribute is not explicitly assigned but is the result of an operation performed on other attributes. Attributes that obtain their values this way are called *calculated attributes*. Typically, the values of calculated attributes are not persisted.

Calculated attributes reduce storage redundancy by deriving values from information that has already been persisted. Since the values of calculated attributes are not persisted, they can reduce the size of your data store, and the number of commits that must be made to it. For example, if a model stores a person's date of birth, you can define their age as a calculated attribute using the birth date and the current date. Using this technique means that you do not have to separately persist an age attribute, or create logic to recalculate and repersist that value every time the person has a birthday.

Use calculated attributes when a value that you want to display or for other purposes can be computed from existing data in the business model. That existing data can be from the same business object instance, from its subcollections, or from any other relevant area. The data can also come from external web services or other integration channels.

Sometimes the calculation of an attribute's value is so complex that using a persisted value is a better choice in terms of the performance of your model. This is particularly true for calculations that require aggregation of massive amounts of data, or highly complex table joins. Persisted attributes might also be preferable if the value is repeatedly used by your application.

In this module, you add a calculated attribute called `fullName` to each of the existing classes in your model. Each attribute is defined differently, so that each `fullName` value is meaningful in the context of the class.

### Add the fullName attribute to your model's classes

Use the class diagram to quickly add the attribute to the relevant classes.

To add the `fullName` attribute to your model's classes:

1. Open the `training:Fundamentals` class diagram (**Business Model → Diagrams**).

2. Right-click the `training:Entity` class and select **Create Attribute**.

3. Edit the new attribute to read:

   `+ fullName : string [0..1]`

   This means the attribute `fullName` is a public (+) attribute of type `string`, is optional, and is not a collection (there can be zero or one instance of the attribute).
4. Add the attribute to the following additional classes in the diagram:

   * `training:Person`

   * `training:Company`

   * `training:TelephoneNumber`

   * `training:EmailAddress`

   * `training:TelcomType`

Do not add the attribute to the `Telcom` class. You are defining the attribute differently in every one of` training:Telcom's `derived classes, so there is no gain from having the attribute defined in this abstract parent class. When you are done, your model diagram should resemble the following:

![image2020-3-4_18-33-51.png](https://documentation.nexj.com/__attachments/a_1310476f672692a1b1cb85ddf45b3898b8ea47aaf79b40ec456f1884fd5ba8ad/image2020-3-4_18-33-51.png?cb=5fb9b171f757129994669b77e6c83a48)

### Set the value and dependency properties for the fullName attributes

To set the value and dependency properties for the `fullName` attribute of the `Entity` class:

1. In the class diagram, right-click the `fullName` attribute of the` training:Entity` class and select **Go to Definition** . This opens the class editor and takes you to the **Attributes** tab of the `training:Entity` class, with the `fullName` attribute selected.

2. In the **Common** sub-tab, set the **Description** to `The calculated full name for display`.

3. In the **Value** sub-tab - or in the value column in the attribute list, set the **Value** property to:

   `(format "Entity: {0}" (@ lastName))`

   **Javascript** Expand source  

   ```
   format("Entity: {0}", this.lastName);
   ```

   This specifies that the `fullName` attribute for the `training:Entity` class will be a string that reads "Entity: {0}", where {0} is a variable that is substituted with the value of the `lastName` attribute. For example, if an entity's `lastName` attribute is "Turner", then the `fullName` attribute has a value of "Entity: Turner".

4. Set the **Dependency** property to `lastName`. This indicates that the value of the `fullName` attribute should be recalculated whenever the `lastName` attribute changes. The **Dependency** property typically contains all the fields used to calculate the value of an attribute.

5. Update the value and dependency properties for the remaining classes, using the following values listed below.

   **training:Person**

   * **Value** : `(format "Person: {0} - {1}" (string-affix (@ lastName) ", " (@ firstName)) (@ company fullName))`

     JavaScript

         format("Person: {0} - {1}", #"string-affix"(this.lastName, ", ", this.firstName), this.company.fullName);

   * **Dependency** : `lastName firstName (company fullName)`

     Info  
     The value of the `fullName` attribute of the `training:``Person` class refers to both its own simple attributes as well as the `fullName` attribute of the associated `Company` class. The attribute of the associated class is accessed through the `company` complex attribute. This is a simple practical example of traversing the model through the associations set by the reverse property.

**training:Company**

* **Value** : `(format "Company: {0} (Business Number: {1}, Since: {2,date,yyyy})" (@ lastName) (@ businessNumber) (@ incorporationDate))`

  **Javascript** Expand source  

  ```
  format("Company: {0} (Business Number: {1}, Since: {2,date,yyyy})", this.lastName, this.businessNumber, this.incorporationDate);
  ```

* **Dependency** : `lastName businessNumber incorporationDate`

**training:TelephoneNumber**

* **Value**: `(format "Phone: {0}-{1} {2}" (@ name) (@ address) (@ extension))`

  JavaScript

      format("Phone: {0}-{1} {2}", this.name, this.address, this.extension);

* **Dependency**: `name address extension`

**training:EmailAddress**

* **Value**: `(format "Email: {0}-{1} ({2})" (@ name) (@ address) (@ displayName))`

  JavaScript

      format("Email: {0}-{1} ({2})", this.name, this.address, this.displayName);

* **Dependency** : `name address displayName`

**training:TelecomType**

* **Value**: `(format "TelcomType: {0}" (@ name))`

  JavaScript

      format("TelcomType: {0}", this.name);

* **Dependency** : `name`

6. Save your changes and validate the model by clicking the Validate Model button ![validate.png](https://documentation.nexj.com/__attachments/a_4d7b901dcfcb2c46b1f24b7a527f8c6ebf43ef69ea46e07be42faab44a9c1e84/validate.png?cb=1bfd263d9a5314da8390de47dbf20045) in the toolbar.

Info  
The `@` symbol in this context acts as a shorthand for accessing the current class instance. In javascript it is this and can be chained. In calculating the value of the `fullName` attribute for an Entity, the (`@ lastName`) gives the instruction to look first at the instance for which the `fullName` value is being calculated, and then to get the value of the `lastName` attribute from it. The `@ `notation can increase the legibility of a complex get instruction.

Compare:

(`@ lastName`) is equivalent to (`this'lastName`) and this.lastname in "js".

(`@ company fullName`) is equivalent to (`(this'company)'fullName`) and this.company.fullName in "js".

## Test the model

1. Start the Server Console with Debug logging enabled.

2. In a scratchpad addthe following script:

       ; Test code for calculated attributes
       ; Create all of the training:Entities and training:Telcoms
       (define entity (training:Entity'new (: lastName "Thing4")))
       (define comp (training:Company'new (: lastName "YourCo") (: businessNumber "BN-5678") (: incorporationDate (now))))
       (define person (training:Person'new (: firstName "John") (: initials "X") (: lastName "Doe") (: company comp) (: primaryLanguage (training:LanguageEnum'get'ENGLISH))))
       (define tp (training:TelcomType'new (: name "Business2")))
       (define email (training:EmailAddress'new (: entity person) (: type tp) (: name (tp'name)) (: address "john.doe@nexj.com") (: displayName "John Doe")))
       (define tel (training:TelephoneNumber'new (: type tp) (: name (tp'name)) (: address "(416) 555-1212") (: entity person) (: extension "x123")))

       (commit)

       ; Loop over all the training:Entities and (polymorphically) print their
       ; fullNames (and their Telcoms)
       (define outString "")
       (for-each
          (lambda (e)
             (set! outString (string-append outString "\n" (e'fullName)))
             (for-each
                (lambda (t)
                   (set! outString (string-append outString "\n -" (t'fullName)))
                )
                (e'telcoms)
             )
           )
           (training:Entity'read '() '(in? lastName "Thing4" "YourCo" "Doe") '() '() '() '())
       )
       (logger'info "\n\nAll the fullnames are... \n" outString)

   JavaScript

       ; Test code for calculated attributes
       ; Create all of the training:Entities and training:Telcoms
       var entity = new #"training:Entity"({lastName: "Thing4"});
       var comp = new #"training:Company"({lastName: "YourCo", businessNumber: "BN-5678", incorporationDate: now()});
       var person = new #"training:Person"({firstName: "John", initials: "X", lastName: "Doe", company: comp, primaryLanguage: #"training:LanguageEnum".get.ENGLISH});
       var tp = new #"training:TelcomType"({name: "Business2"});
       var email = new #"training:EmailAddress"({entity: person, type: tp, name: tp.name, address: "john.doe@nexj.com", displayName: "John Doe"});
       var tel = new #"training:TelephoneNumber"({type: tp, name: tp.name, address: "(416) 555-1212", entity: person, extension: "x123"});

       commit()

       ; Loop over all the training:Entities and (polymorphically) print their
       ; fullNames (and their Telcoms)
       var outString = "";
       #"for-each"(
          function (e) {
             outString = outString + "\n" + e.fullName;
             #"for-each"(
                function (t) {
                   outString = outString + "\n -" + t.fullName;
                },
                e.telcoms
             );
           },
           #"training:Entity".read(null, scm("'(in? lastName \"Thing4\" \"YourCo\" \"Doe\")"), null, null, null, null)
       );
       logger.info("\n\nAll the fullnames are... \n", outString);

   In brief, this code does the following:
   1. It defines an entity called `Thing4`, a company called `YourCo`, and a person named John` Doe`.

   2. It defines a business email and telephone number for John Doe.

   3. It commits these values to the database.

   4. It identifies all entities with a lastName value of `Thing4`, `YourCo`, or `Doe`.

   5. For each of the entities that it identifies, it adds to a string variable, `outString`, as follows:

      1. It reads in the entity.

      2. It appends the entity's `fullNam`e value to the `outString` string.

      3. If the entity has any telcoms associated with it, then it appends the `fullName` values of those telcoms to the string.

   6. It echoes the full contents of `outString` to the console as an information message.

3. Run the code. Your output should resemble the following:

       All the fullnames are...

       Entity: Thing4
       Company: YourCo (Business Number: BN-5678, Since: 2020)
       Person: Doe, John - Company: YourCo (Business Number: BN-5678, Since: 2020
        2:16 PM)
            - Email: Business-john.doe@nexj.com (John Doe)
            - Phone: Business-(416) 555-1212 x123
       ; #t

## Update the revision and publish (optional)

This will take a while. Only do it if you want practice publishing. You did not make any changes to the database schema, so you do not need to make any changes to the upgrade file or the database. There is no need to change the model version.

You did make a change to the way the model operates, so you should update the model revision number. Updating the revision number indicates that you have changed the metadata structure of the model since its last "release."

1. Click the **Validate model** button ![validate.png](https://documentation.nexj.com/__attachments/a_4d7b901dcfcb2c46b1f24b7a527f8c6ebf43ef69ea46e07be42faab44a9c1e84/validate.png?cb=1bfd263d9a5314da8390de47dbf20045) in the toolbar to validate the model and ensure there are no errors.

2. Click the **Set current model** button ![rep_nav_icon.png](https://documentation.nexj.com/__attachments/a_35ea614d4628dee6a301c20704462db1305afbcacb4038f1f04e2871a7adec9b/rep_nav_icon.png?cb=98aeec9adf5389ce8fe5527ba6c4dc14) to open the Model Library.

3. With the current model selected, click **Edit**.

4. Update the **Model** **Revision** field by incrementing the final digit by one.

5. Click **OK**.

6. Click **Publish** . The **Publish Model** window opens.

7. Select the directory that you want to publish your model to and click **Save**.

8. Close the **Model Library**.

9. The console view provides a record of the actions taking place as NexJ Studio publishes the model. If the publish is successful, the message `BUILD SUCCESSFUL` appears.

   Info  
   If the model contains any warnings or errors, you are asked to confirm whether you want to continue publishing. In this course you can ignore warnings, but you must resolve any errors. To disable warnings, ensure checkbox found at **Window → Preferences → NexJ Studio → Disable validation warnings** is checked.

## More about value behavior

In this module, you created a purely calculated attribute. The value of this attribute is never persisted to a data store.

The value of non-persisted attributes, such as the ones you defined in this module, is recalculated every time the object containing that attribute is requested. By contrast, the value of a persisted attribute is recalculated only when there is a change to the attributes identified in the **Dependency** property. However, both invalidate the value if dependent attributes change. This means that if the value is explicitly set and one of its dependent attributes change, then accessing the attribute again recalculates the value.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Capitalization rules

## Title case

* Always capitalize the first and last word of a label or sentence.

* Capitalize all principal words and all words with 4 or more letters.

* Do not capitalize short articles and prepositions, such as "and" or "in".

If you are unsure of the correct capitalization for a label, use an online capitalization tool such as <https://titlecaseconverter.com/>

## Sentence case

* Capitalize the first word of a label or sentence.

* Capitalize feature names.

## UI elements

|                    UI Element                    |               Capitalization Style               |
|--------------------------------------------------|--------------------------------------------------|
| Button                                           | ALL CAPS, appears as Title Case in documentation |
| Card title                                       | Title Case                                       |
| Dialog title                                     | Sentence case                                    |
| Field names (most)                               | Title Case                                       |
| Field names in the format of a sentence          | Sentence case                                    |
| Ghost text                                       | Sentence case                                    |
| Instructional text and error messages            | Sentence case                                    |
| Menu items                                       | Sentence case                                    |
| Menu names (what do you click to open the menu?) | Title Case                                       |
| Tab name                                         | ALL CAPS, appears as Title Case in documentation |
| Tooltips                                         | Sentence case                                    |
| Workspace name                                   | Title Case                                       |

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Cards

## Description

Several different types of cards are supported.

### Summary/List Cards

Summary cards are used to display a list of items containing information. All related details about an item are displayed in a single list item or tile.

The most common use of a simple summary card are cases where you may want to present a quick unordered list of data that is quickly accessible, well-structured, and easy to maintain. For example, communication list, address list, profile category list.

### Graph Cards

Use these cards to graphically display relationships between items. For example, a household hierarchy.

### Chart Cards

Use these cards to graphically represent information based on a mathematical formula. The card may display just a chart with the legend on the right. Alternately, the legend may be used as a filter. On click, the results of the filter are shown below the chart.

### Infographic Cards

This card has the information in the chart acting as a filter or navigator. For example, on a donut chart, each segment is clickable.

## Specifications

![Cards, Page 1 – 1.png](https://documentation.nexj.com/__attachments/a_34c69b899ad62dc8b4340ee5a8413e0948178edb9845bf3783ecafd3a4a51552/Cards,%20Page%201%20%E2%80%93%201.png?cb=0e9dc4c5080e404af7a4be19f5fcaa8b)

![Cards, Page 1 – 3.png](https://documentation.nexj.com/__attachments/a_52530455f475989be65ae236bab4370bf85c2ceacfaa9ae9cae3694850d1a170/Cards,%20Page%201%20%E2%80%93%203.png?cb=0209be0ca6f555557f9bd4fc7fde1b45)

![Cards, Page 1.png](https://documentation.nexj.com/__attachments/a_59115b07f17643e30f9d4c153400536fb2d9efda680e49a5090cafb8bb7fdf7d/Cards,%20Page%201.png?cb=c9dbcfa2baaa5fcf8fee31a47239ee74)

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Changing privilege group members

Change the members of a privilege group when you want to reassign abilities in NexJ CRM.

To change the members of a privilege group:

1. Navigate to the User page.

2. In the **Privilege Groups** tab, select the privilege group whose membership you want to change. The privilege group's information appears in the **Detail** subtab.

3. In the **Users** subtab, click the **Select** button ![point2.png](https://documentation.nexj.com/__attachments/a_17d28458b17f1539fcfafe8e4399050ad9fa96aefd759d555c349affe1321b18/point2.png?cb=1824f439288a202d02b7a660844594bb) . The Select Users dialog opens. You can search for users in this dialog by first name, last name, and login name. Users who are already part of the privilege group populate the list on the right side of the dialog.

4. Add users by selecting from the list of available users on the left side of the dialog and clicking **Add** ; remove users by selecting from the list on the right side and clicking **Remove**.

5. Click **OK**. The Select Users dialog closes.

The members of the privilege group are modified.

---
version: "25.11"
variant: "Cloud"
language: "en"
---
# Changing the content-transfer-encoding scheme for meeting invitations and emails

To ensure the text for meeting invitations and emails renders correctly, you can set the value of the `TRANSFER_ENCODING` attribute of the `EXCHANGE_CRUD` class to the required content-transfer-encoding scheme. For example, you can set the scheme to base64 or 7bit.

To change the content-transfer-encoding scheme for meeting invitations and emails:

1. In the NexJ Studio Business Model layer, open the `EXCHANGE_CRUD` metaclass.

2. In the **Attributes** tab, select the `TRANSFER_ENCODING `attribute.

3. In the **Value** tab, and in the **Value** field, change the value of the property to the type of encoding scheme desired.

4. Click **Save** ![save_icon.png](https://documentation.nexj.com/__attachments/a_3fa79d3308bc06d8dad82ff9c4ee40ae8ede864cc869ec1d4acc70f7a63f4787/save_icon.png?cb=e624b406b719682108c8a60ea0e1ed29) .

The content-transfer-encoding scheme for meeting invitations and emails is changed.

[Next Page](https://documentation.nexj.com/llms-full.txt/1)
