Table of Contents

Customisation

When to customise?

When considering customisation, think about the business benefit of a customisation versus the cost.

Solution Layering

Benefit Cost
Adapt to Client’s business processes Blocks future updates to that area
More direct business value Additional maintenance cost
Enhanced end-user experiences Additional complexity / update risk

Some customisations have higher costs than others

Example Cost of ownership
Reporting Customisation Low
Additional form fields Low
New entities/data structure changes Medium
Changes to OOTB JavaScript / DOM High

How to customise?

One of the key reasons to choose Altus and the Power Platform is to facilitate customisation of the product to suit your requirements. With Altus you can customise it yourself or engage us to help you.

Do it yourself

GOLDEN RULES FOR CUSTOMISATION

  • Multiple environments are required for Application Lifecycle Management (ALM)
  • Production environments should not include unmanaged layers. Unmanaged layers in Production can lead to several problems:
    • Unpredictable Behaviour: Unmanaged layers can override managed solutions, leading to unexpected behaviour and unexpected bugs.
    • Difficult to Track: Changes made directly in Production are harder to track and audit.
    • Maintenance Challenges: It complicates the process of updates and maintenance, as unmanaged customisations need to be manually reconciled with new versions of managed solutions
    • Microsoft includes a setting to enforce this.
  • Each Development environment used for solution layer creation should only host one unmanaged layer.
    • When exporting a layer into a managed layer, predictable behaviour can only be assured if there is one unmanaged layer that encapsulates all the changes and dependencies of those changes.

Solution Layers Customer

Power Apps is an open and accessible platform for citizen developers and experienced developers to perform customisations themselves. We advise that all customer customisations are performed in a Development (DEV) environment in a dedicated solution layer and then deployed to the Production (PROD) environment as a managed layer. This allows good separation and change control processes to take place.

Engage a Consulting Partner

Solution Layers Consulting

To provide the highest level of expertise and support, Altus partners can provide detailed customisation services for Altus and Client Care to ensure that your deployment remains supported and up to date.

It is recommended that Consulting led customisations to Altus are stored in a layer above the base product and in the case where there are multiple environments (DEV, TEST, PROD) that unmanaged layers are used DEV and TEST are deployed into subsequent environments as Managed. Customer maintained layers can also be present to provide a quicker way to make changes without having to engage a partner every time.

Warning

Each environment must only contain one unmanaged layer used for development.

Altus customisation

The Altus solution as a set of layers over the existing project and Power Platform components. The additional components that are provided as part of the solution have the Customizable flag enabled (where possible) to allow almost any provided Dynamics component to be tailored to meet the business requirements. Where the item is not directly customisable (Security Roles, PCF Components, and the Teams App) we have taken steps to ensure that these have configuration options available to control their behaviour.

The solution layering concept is a common technique used throughout the Power Platform, and reviewing this general introduction to Solutions from Microsoft is recommended before getting started, with a more advanced discussion of best practices for Solution Lifecycle Management available here.

In the case of Altus, the solution layering can be visualised as:

Image shows the solution layer dependency in Altus

The Solution Layers are (from the bottom up):

  • (PFTW Edition Only) Microsoft Project Roadmap. Roadmap functionality is part of the Project Plan 3 and Project Plan 5 licenses. The Roadmap solution provides base level functionality for many project related functions and was historically released first.
  • (PFTW Edition Only) Project for the Web. This layer provided by Microsoft contains the back-end Dynamics components required for the Project for the Web functionality.
  • Kaizen. This layer contains the Altus Tables and functions that are not specific to Project for the Web, and common to all Altus solutions.
  • Atsumeru. Specific functionality for the Altus execution tool independent solution.
  • (PFTW Edition Only) IQ PFTW. This layer includes Tables, relationships and functions that provide integration with Microsoft Project for the Web.
  • Customisations. This layer can be added by the customer, or by a Partner on the Customer's behalf. It is either the "Default Solution" or a solution that is managed in a customer owned DEVOPS process.

Altus applies solutions in layers to provide functionality to the end user and customer specific customisations can then be applied on top to cater for specific customer needs.

Limitations

Important

Some elements in the Dataverse do not seem to handle Solution Layering. Please see below a list of the elements we have encountered which we recommend not to modify from our Altus Solutions.

SenseiProject Javascript bundle

It is not supported to customize the 'SenseiProject.bundle.js' JavaScript bundle in Webresources within your solution. Creating a customization layer will prevent future updates to this file from being visible in your environment.

Security Roles

Because Security Roles do not seem handle solution layering (and any changes made to them would be overwritten on the next update), all of the security roles shipped with Altus are shipped as non-customisable. If you require custom security roles, you can add new security roles in your environment.

Control Configuration at the Table level

Dataverse provides a capability (via the Classic interface) to modify the default Control used for each Table. This configuration change does not handle Solution Layering and any change made to this setting will either be deleted automatically whenever the base Solution updates, or will cause the base Solution update to fail completely if the control configuration is housed in another managed Solution layer. Altus strictly recommend not modifying the Control settings at the Table level for any of the Tables that are shipped with Altus.
(Note: Modifying the control for a subgrid at the Form level does not have this limitation as the layering is handled correctly by the Form). More details here

Security Role Query

Partners have access to typescript/javascript code to query the current users security roles by ID together with a list of constants that represent all the available out-of-the-box security roles.

This query will check to see if users have a given security role, and can be used if Security Roles Display Names are changing.

Query:

// Altus Roles
export const ExecutiveRoleId = "dfcfe799-f413-eb11-a813-000d3ae11abd";
export const AdminRoleId = "4c0f0c74-8b09-ea11-a811-000d3a530fe5";
export const ProjectRoleId = "9959280b-3621-ea11-a810-000d3a530fe5";
export const ProgramManagerRoleId = "f9dfbebf-fb13-eb11-a813-000d3ae11abd";
export const ProgramManagerTeamRoleId = "35e21d64-fc13-eb11-a813-000d3ae11abd";
export const PortfolioRoleId = "0bec81ef-9b09-ea11-a811-000d3a530fe5";
export const PortfolioManagerRoleId = "6032594f-fa13-eb11-a813-000d3ae11abd";
export const PortfolioManagerTeamRoleId = "ec929215-fb13-eb11-a813-000d3ae11abd";
export const ProposalManagerRoleId = "6734a4a6-fc13-eb11-a813-000d3ae11abd";
export const IdeaUserRoleId = "b0a82810-f813-eb11-a813-000d3ae11abd";
export const ChallengUserRoleId = "73ce88a4-f713-eb11-a813-000d3ae11abd";
export const StrategyRoleId = "657aa149-9c09-ea11-a811-000d3a530fe5";
export const StrategyExecutiveRoleId = "a180425f-fd13-eb11-a813-000d3ae11abd";
export const PMOUserRoleId = "92010470-f913-eb11-a813-000d3ae11abd";
export const ResourceManagerRoleId = "e53a8843-0bcf-eb11-bacc-0022481520cd";

export function currentUserHasRole(roleID: string): Xrm.Async.PromiseLike<boolean> {
    let roles = Xrm.Utility.getGlobalContext().userSettings.roles.get();
    return Xrm.WebApi.retrieveMultipleRecords(
        "role",
        `?$select=name,roleid&$filter=_parentrootroleid_value eq '${roleID}'`)
        .then(
            (result) => {
                if (result.entities.length > 0) {
                    for (let item of result.entities) {
                        if (!(roles.find((x) => x.id == item.roleid) === undefined)) {
                            return true;
                        }
                    }

                    return false;
                } else {
                    throw new Error(`No Security Roles Found!`);
                }
            },
            (error) => {
                throw new Error(`Error - ${error.message}`);
            }
        )
        .catch((error) => {
            console.log(error);
            return false;
        });
}

usage

currentUserHasRole(StrategyExecutiveRoleId)
    .then(result => {
        console.log(result);
    });

or

const result = await currentUserHasRole(StrategyExecutiveRoleId)
console.log(result);

SharePoint group sites

It is a common requirement to want to add elements or customise the SharePoint Site that is created in conjunction to the Microsoft (M365) Group. The approach to facilitating this is Using Site Designs to Manage Project Life Cycles.

Using techniques such as Hubs Sites, Site Designs, Site Scripts and connected Power Automate Flows allow for an almost infinite level of site customisation capabilities.

Customising Altus

Any change to Altus needs to be done via a solution, and to do this requires a Publisher.

Before you make any changes to Altus you will need to decide how you are going to make this change and how this change will interact with any base updates made to Altus. If you are making a considerable change to a form within Altus in line with a Client's request, and they don't want it to be automatically updated by any Altus base changes, it is probably best to make a copy of the form and work from this. This will mean that no changes will automatically be made to the form with future Altus releases. Any changes will need to be manually updated.

Note: Some items within Altus, such as PCFs will always get updates.

Adding a Publisher

We suggest if you are going to make customisations to your environment that in your development environment you configure a custom publisher which is not prefixed with "sensei" to avoid collisions with the Altus solution.

  1. In order to create your publisher navigate to make.powerapps.com then select your Environment from the top right corner.

Image showing where to select your environment

Now we want to create our new Publisher inside the environment.

  1. Click Solutions from the left-hand side bar
  2. Select the Publishers tab
  3. Click + New publisher from the main Home bar
  4. Type the Display Name for your publisher, i.e. "Dynamics Edge"
  5. Type the Name for your publisher, this value shouldn't have any spaces. The Name will mirror the Display Name without spaces by default
  6. Type a Prefix for your publisher, as shown by marker 7 the prefix appears at start of any entities or objects created using the publisher. In this example it's "dynedge"
  7. This is a preview of what a new Object name would look like i.e. dynedge_Object
  8. Click Save and the publisher will be created if there is no collision with an existing name in the system.

Image indicating how to create a new Publisher

Adding a Unmanaged Solution

We want to create a new unmanaged solution using our new Publisher

  1. Click Solutions from the left-hand side bar
  2. Select the Solutions tab
  3. Click + New solution from the main Home bar
  4. Type the Display Name for your solution
  5. Type the Name for your customisation, this value shouldn't have any spaces. The Name will mirror the Display Name without spaces or illegal characters by default
  6. Select the Publisher you want to use: "Dynamics Edge" in our example
  7. Unmanaged refers to the type of solution being generated
  8. Click Create to create the solution.

Creation of new solution using new Publisher created earlier

Adding a column

  1. In order to create your publisher navigate to make.powerapps.com then select your Environment from the top right corner.

Image showing where to select your environment

Now we need to edit our unmanaged solution. Adding in our case the existing entity and associated form.

  1. Click Solutions from the left-hand side bar
  2. Find your unmanaged customization layer i.e. "Customization" and select the ellipsis.
  3. Click Edit.

Selecting Solution called Customization

To expand on an existing entity:

  1. Select in the Top ribbon select Add Existing
  2. Select Table (also known as Entity)

Creation of new solution using new Publisher created earlier

In our example we are going to add "sensei_risk" table to our solution.

  1. Search for "sensei_risk" in the top right search box
  2. Confirm that the Name also known as internal name matches
  3. Select the checkbox
  4. Click Next to proceed to add to unmanaged solution

Adding of existing table 'sensei_risk' to Customization solution

To add existing objects to our customisation solution:

  1. Select Select objects

Selecting objects to add from existing Table

Adding the Main Information form to a customised solution

  1. Select Forms
  2. Notice the Information form definition under display name
  3. In the Form type column it should say that it is a "Main" form
  4. In the Managed externally? it should say "Managed", this implies this will create a solution layer which can prevent further updates to this entity. So it's recommended to keep back-ups of customisations in case you even need to restore them
  5. Select the Columns checkbox
  6. Click Add to add it to the solution.

Adding an existing Form to included within an unmanaged solution

  1. This shows our form has been added to the customisation layer
  2. Add the selected form to the solution layer.

Adding selected form to unmanaged solution

Now we can observe that our customisation has 1 entity "Risk" added to it.

To add a Column to it

  1. Click the ellipsis
  2. Click Edit

Editing an existing table/entity to add a column

Adding a Simple Column

  1. Click the New Column button in the ribbon

Clicking 'new column' button

This will open the side pane which will allow for entering of the following details

New Column side pane

Display Name

  • This is the value that will be displayed as the Column name when added to a form

Description

  • Optional: the description of the column being created

Data type

Selecting Data Type

Format

  • Then format for this particular data type

Selecting Format of data type

Behavior

  • The options here are Simple or Calculated (Rollup is an option for other data types). Calculated/Rollup will require saving the new element in order to modify the calculated column(field)

Selection of Simple or Calculated Behavior

Required

  • Defining if the value is optional/required/recommended

Required Options for column(field)

In the following we look at "Advanced options" (2) dropdown

  1. Type a Display name in our example we have used "Custom Risk"
  2. Expand Advanced options
  3. Take note that the Schema name starts with "dynedge_" which is from the publisher for the solution and CustomRisk is the Display name removing invalid characters. Making the final "Schema name" equal "dynedge_CustomRisk". This can be changed at this point, but note that the "Schema name" cannot be changed once it has been created.
  4. Enter the Maximum characters count for the column
  5. Save the current configuration for your new column.

Advanced panel of New Column

After a few seconds our new column will be added to the display

  1. To preview it you can scroll all the way to the right
  2. Confirm its "Display Name" is present ie. "Custom Risk"

Viewing new Custom Risk in Grid View

Adding a Calculated Column

We repeat steps above but this time we will select "Calculated" from Behavior

  1. Type the Display Name
  2. Notice the "Schema name" has been created. In this example it is "dynedge_MyCustomRiskCalculated"
  3. Select "Date and Time" from the Data type field
  4. Select "Calculated" from the Behavior
  5. Note that this needs to be saved in order to access the calculation component to configure it.
  6. Click Save and edit this will save and launch a pop-up (may need to unblock) to allow editing of the calculation.

Viewing new Custom Risk in Grid View

  1. Click the new column
  2. Click Edit column

Viewing new Custom Risk in Grid View

  1. Note that Data type becomes fixed and Behavior is also fixed as calculated
  2. Click Edit

Viewing new Custom Risk in Grid View

A window will pop-up and you can then fill out the parameters

  1. Condition (Optional)
  2. Action In our case we will have it save the current time when it is created

Viewing new Custom Risk in Grid View

  1. Clicking in the box will show a number of different calculation options
  2. These are the options available out of the box. The one we are going to use is the "NOW()" one. This gets the time NOW.

Viewing what is shown when you click 'Add action'

  1. Once the command is added we need to save it by checking the check mark
  2. Save and Close the window

Saving calculated field and 'Save and Close'

To Test the above you need to create and save a Risk in the app.

Now we can confirm that this has worked as expected.

  1. Refresh the table to see the new entry, scroll to the right
  2. Observe the date should be today's date

Confirming that new Risk has today's date saved for when it was last updated

Adding a custom column to an existing form

In the below example we will walkthrough adding the custom calculated column we added above to the "Risk" form

There are two ways to do this. The old way and the new way.

Old way

Getting into the Advanced Settings from within the "Altus" app

  1. Open the cog in the top right corner
  2. Select "Advanced Settings"

Opening the Advanced Settings in the App

Navigating to Solutions

  1. Click the dropdown menu next to "Settings"
  2. Click on "Solutions"

Navigating to Solutions in Advanced Menu

Now we need to get to the edit screen for modifying our solution/s

  1. Click in our case "Customization" this will open the Solution in a new window
  2. Expand "Entities"
  3. Expand "Risks" or your corresponding entity
  4. Expand "Forms"
  5. Click in our case "Main - Altus" form
  6. The editing window will be presented

Navigating to Form Edit Pop-out

Steps to Add a new column to the form

  1. Find the requested field in the right hand column. If need be uncheck "Only show used fields" if the field already exists on the form. Then click and drag the field to the column you want it to appear in
  2. You should see it appear in the column where you release the mouse
  3. Save the change wait for the blue highlight to disapper
  4. Publish the change/s

Adding a calculated column to the form

Confirming adding the column has added it to the form

  1. Navigate to "Projects"
  2. Select "Risks" in Ribbon in our case
  3. Select any Risk, in our case "Test"
  4. Notice that "My Custom Risk Calculated" is present and locked
  5. Shows todays date
  6. Shows time you loaded this form as it will update on refresh
  7. Refresh button to update the value presented as it uses the NOW() time

Confirming Column added to Form

Removing a selected column from form

  1. Click the column you wish to remove
  2. Click the "Remove" button in the ribbon
  3. Click the "Save" button wait for the blue highlight to disapper
  4. Click the "Publish" button

Confirming Column added to Form

New way

Adding the "Altus" model driven app to our "Customization" solution

  1. Navigate to "Solutions"
  2. Expand the dots for "Customization" in our case
  3. Click the "Edit" command

Adding a Column to a New Form

Adding an existing app to the 'Customization' solution

  1. Select 'Apps'
  2. Click 'Add existing'
  3. 'App'
  4. 'Model-driven app'

Adding existing App to Solution

Adding existing 'Altus' model-driven app

  1. Select 'Altus'
  2. Click 'Add'

Adding Altus App to Customization Solution

Editing the Altus App

  1. Click the three dots
  2. Click 'Edit' option

Adding Altus App to Customization Solution

Opening up the New Edit Form

  1. Scroll down to the required Entity ie. 'Risk' and trigger dropdown
  2. Hit 'Edit' on 'Risk Forms'

Open Edit Form

Adding "My Custom Risk Calculated" to New Risk Form

  1. Click and drag onto the form on the center of the page dropping where you would like it
  2. In this case its shows a preview of what it will look like
  3. Save and Publish in the top right hand corner to save it

Adding Altus App to Customization Solution

This will now be visible on the 'New Risk' Form, and because its calculated it will calculate the value each time you refresh the browser window the current time

Creating a Project Type (EPT)

  • In the Altus application, navigate to the Settings area where you can access and manage project types. Image shows the Project Types view under Project Configuration Section
  • Within the Project Types section, click the "New" button in the ribbon to create a new EPT. Image shows the New button that can be found at the top of the Enterprise Project Types view
  • Enter an appropriate name and description for your new EPT to help users understand its purpose. Image shows the New Enterprise Project Type form with Name and Description fields
  • In the Form Tabs section, use the radio buttons to select the tabs that should be shown for this EPT. Note that only one Details tab should be marked as visible. Image shows the section below containing radio toggles for which tabs should be shown
    Image shows the radio toggles for tab visibility again, but with the multiple details tabs highlighted to indicate only one should be selected.
  • On the Workflow tab, a Business Process Flow (BPF) must be selected. If the BPF to be associated with this project type has not yet been created or associated with the Altus app, follow the steps in the next section. Image shows the next tab named Workflow, where a Business Process Flow may be selected and associated with the new EPT
  • Save the EPT record and ensure that all configurations are saved properly.
  • Create a new project using the new EPT to confirm that the BPF displays correctly, all expected tabs are visible, and no duplicate Details pages are present.

Creating a workflow

Custom Workflows can be added to Altus, but there are some important steps required to make them function as expected within Altus.

Create a Business Process Flow (BPF)

  1. Navigate to Solutions

    • Navigate to make.powerapps.com and ensure you have the correct environment selected.
    • From the left nav menu, select Solutions.

  2. Select the Solution

    • Select the unmanaged Solution that contains your enhancements for Altus in your environment.

  3. Create a Process

    • Select the New button and then select Automation > Process > Business process flow.

    • Ensure the following details are filled in:

      Column Value
      Display name: {Name of your BPF}
      Name: {Should be automatically entered for you when you enter the Process name. Adjust if required.}
      Table: Project
    • Once you have entered the details, click Create.

  4. Populate and Activate Your BPF

    • Populate your BPF as per your requirements.
    • Once you are happy, Save and Activate your BPF.

  5. Edit or Add a Security Role

    • Back within the Altus Enhancements Solution in the Dynamics 365 Advanced Settings area, select to either edit an existing enhancement Security Role, or select to add a new one.
    • If adding a new Role, provide a relevant Name and then select the Business Process Flows tab.
    • Locate and select the Name of your Business Process Flow. The UI should update to provide security permissions to the role across the permission set.
    • Select Save and Close.

Configure the Altus App

  1. Navigate to the Altus App

    • You will need the Admin User role to perform these actions
    • Select the Settings area of the app
    • Select Project Types from the left menu

  2. Create or Edit a Project Type

    • Select to create a New Project Type or edit an existing one if you need to change its associated BPF Workflow.
    • Enter the details for your new Project Type and the required tabs that you wish to display on the Project Form.
    • Select the Workflow tab on the Project Type form.
    • In the Workflow Column, search for and locate the BPF that you created earlier.
    • Select Save & Close.

  3. Ensure BPF Availability

    • Within the Altus Enhancements Solutions in the Dynamics 365 Advanced Settings area, check whether the Altus model driven app is already part of your Enhancements Solution.
    • Double-click the Altus model driven app to open it in Edit mode.
    • Click the Automation button on the left-nav.
    • If your flow is in the Not in this app section of the left-hand pane, click the elipsis next to it and select Add.
    • Click the Save icon.

Plugin Registration

  1. Log in to your Environment

  2. Register New Step

    • Once connected to your environment, scroll down to locate the SenseiAtsumeruPlugin assembly. Image shows the Assembly Sense Atsumeru Plugin highlighted
    • Right-click (Plugin) SenseiPlugin.Sensei_SetProjectCurrentStage and select Register New Step Image shows the Register New Step menu item highlighted
    • Configure the Step with the following settings, then press Register New Step.
    Message Create
    Primary Table { Select your BPF Table }
    Secondary Table {none}
    Filtering Attributes {none}
    Event Handler (Plugin) SenseiPlugin.Sensei_SetProjectCurrentStage
    Step Name Sensei.IQ for Project - Set Project Current Stage : Create of { Your BPF Table Name }
    Run in User's Context Calling User
    Execution Order 1
    Description SenseiPlugin.Sensei_SetProjectCurrentStage: Create of { Your BPF Table Name }
    Event Pipeline Stage of Execution PostOperation
    Execution Mode Asynchronous
    Deployment Server
    Delete AsyncOperation if StatusCode = Successful Unchecked

    Image shows the Register New Step, General Configuration Information pane

  3. Register Update Step

    • Right-click (Plugin) SenseiPlugin.Sensei_SetProjectCurrentStage again and select Register New Step - this time we will add a handler for the Update operation.
    • Configure the Step with the following settings, then press Register New Step:
    Message Update
    Primary Table { Select your BPF Table}
    Secondary Table {none}
    Filtering Attributes activestageid, completedon, modifiedon
    Event Handler (Plugin) SenseiPlugin.Sensei_SetProjectCurrentStage
    Step Name Sensei.IQ for Project - Set Project Current Stage : Update of { Your BPF Table Name }
    Run in User's Context Calling User
    Execution Order 1
    Description SenseiPlugin.Sensei_SetProjectCurrentStage: Update of { Your BPF Table Name }
    Event Pipeline Stage of Execution PostOperation
    Execution Mode Asynchronous
    Deployment Server
    Delete AsyncOperation if StatusCode = Successful Unchecked

    Image shows the Register New Step, General Configuration Information pane

After configuring these Plugin registration steps, whenever your BPF Table is used (e.g. when a project moves from one stage to another) your project will be updated with the Current Stage value.

Your BPF should now work and display correctly within Altus. Image shows the Project BPF

Unhiding a command bar button

Various Command Bar buttons have been hidden from various Tables relating to the Altus Solution. If there is a customer requirement to unhide any of these buttons, then the following procedure can be used:

  • Create an Unmanaged Solution (if one does not already exist) in the customer environment and ensure you add to it the existing Tables that you wish to modify the ribbon for.
  • Open Ribbon Workbench 2016 (either as web add-on in Dynamics or from the XrmToolbox desktop application).
  • Select Open Solution Image shows the Ribbon Workbench 2016 Open Solution button
  • Select to open your unmanaged Solution containing the Tables you wish to modify the ribbon for.
  • Note: If Ribbon Workbench fails to load your Solution (because your Solution is large), try multiple times. Sometimes this error is transient.
  • Once your Solution has loaded, ensure the Command Bar tab is selected Image shows the Ribbon Workbench with the command bar highlighted
  • Next, select the Table that you wish to modify the ribbon for Image shows the Ribbon Workbench Command Bar with the Entity highlighted
  • Any buttons which have previously been hidden will be shown under the HIDE ACTIONS heading Image shows the Solution Elements pane with Hide Actions highlighted
  • To unhide a button, right-click the hide action that you wish to remove and select Un-Hide Image shows Hide Actions pane with the right-click un-hide menu displayed
  • Click OK on the notification. Take note that you will only see the unhidden button after a publish operation (and only in Ribbon Workbench after subsequently reloading the Solution). Image shows the Ribbon Workbench error message stating that you need to publish and re-load the solution
  • Make the required changes to whichever Tables you wish to modify the command bar for.
  • Once you have completed your changes, select Publish Image shows the Ribbon Workbench 2016 Publish button
  • Click OK to confirm Publish operation.

Image shows the Do you really want to publish confirmation message

Dealing with custom tables

As with all customisations to Altus, the recommendation is creating an unmanaged Solution which will then contain your additions and modifications to Altus artifacts. Custom Tables are no different and should be created in your Altus Enhancements solution.

This section provides guidance on how to configure Altus to ensure that your custom Table is treated the same way as out of the box Altus Tables in terms of security and in particular inheriting ownership from the project, program or portfolio that your custom Table is associated with.

The following configuration steps assume that you will have already:

  • Created your Custom Table in your enhancements solution
  • Added your Custom Table to the project, program and/or portfolio form(s) as a tab with a subgrid
  • Created a custom Security Role which provides access to your custom Table
  • Added your custom Security Role to the users who require it
  • If associated to more than one parent type, we highly recommend creating a Business Rule to ensure that your custom Table records can only relate to one project, program or portfolio at a time. (If you were to relate your custom record to a project and to a portfolio, the ownership of your table record could have unexpected results).
Note

When creating Tables that relate to a project ensure that the relationship between the custom Table and the project table is not set to 'Parental'. Instead, configure the relationship as follows: Image shows the Ribbon Workbench Command Bar with the Entity highlighted

If your custom table contains lookup columns to project and/or program and/or portfolio, you will need to configure the Altus Config setting associated with each of those 'parent' Tables to ensure Altus is aware of your custom table.

  • As an Altus Admin user, open the app and navigate to the Settings area.
  • From the left menu select Configuration Settings.
    Image shows the Configuration Settings item within the Settings menu
  • Select the {Parent} - Custom Registers setting for the Parent of your custom Table. (Note: You will need to repeat these steps for each parent. e.g. if your custom Table relates to projects and programs, you will need to perform these steps for 'Project - Custom Registers' and 'Program - Custom Registers').
    Image shows the Active Config Settings with Sensei Project Custom Registers highlighted
Note

If you do not see the corresponding settings item, switch to the Inactive Config Settings view, locate the setting and switch it to Active.

  • Select the New Items button to add your new custom Table configuration. Image shows project Custom Registers screen with the New Items button highlighted
  • Enter details relating to your custom Table to define:
    1. Table Name: The internal name of your custom Table.
    2. Required Team Root Role: The Name of your custom security role (this role will be applied to any owner teams).
    3. Parent Column Name Link: The internal Column name in your custom Table that relates the custom Table to its parent (in the case of a setting for Project - Custom Registers, this would identify the Project Column).
    4. Assigned To Column: If your custom Table has an Assigned To type Column, identify it by its internal name here. Image shows project Custom Registers screen with the New Items section displayed
  • Save your Config Settings item
  • Repeat these steps for each parent (project, program or portfolio) that relates to your custom Table.

You will also need to perform these next steps to ensure that the Altus Plugin code is triggered to correctly run when you create or update a custom Table record. These Plugin steps ensure that your custom Table records will be visible to members of a project, program or portfolio team (as applicable). To perform these steps, you will need to use the Plugin Registration Tool available here.

  • Launch the Plugin Registration Tool (PluginRegistration.exe) and select Create New Connection. Image shows Plugin Registration Tool screen with the Create new connection button highlighted
  • Ensure you select Office 365, then press Login. Image shows the Create new connection screen with the Assembly Sensei Plugin highlighted
  • Enter your credentials to log in to Microsoft 365.
  • In the list of Plugin Assemblies, locate (Assembly) SenseiPlugin and click the arrow to expand.
    • Depending on your implementation, SenseiAtsumeruPlugin is comparable to SenseiPlugin. Image shows Plugin Registration Tool screen with the Assembly Sensei Plugin highlighted
  • Locate (Plugin) SenseiPlugin.Sensei_InheritOwnershipFromProject and right-click and select Register New Step. Image shows the Registered Plugins and Custom Workflow Activities screen with the Plugin right-click menu displayed with Register New Step highlighted
  • Enter the following details, then press Register New Step. This will ensure that whenever a new record is created in your custom Table that the InheritOwnershipFromProject plugin code is run - which will ensure that your custom Table record is attributed the same ownership as the project, program or portfolio that it is associated with.
    1. Message: Create
    2. Primary Table: {Enter your custom Table by its internal name}
    3. Secondary Table: {Leave blank}
    4. Filtering Attributes: {Leave blank - unavailable for a Create action}
    5. Event Handler: {Leave this set to (Plugin) SenseiPlugin.Sensei_InheritOwnershipFromProject}
    6. Step Name: {Add a name for your Plugin Step, or leave as per the default.}
    7. Run in User's Context: Calling User
    8. Execution Order: 1
    9. Description: {Add a description for your Plugin Step, or leave as per the default.}
    10. Event Pipeline Stage Of Execution: PreValidation
    11. Execution Mode: Synchronous
    12. Deployment: Server Image shows the Register New Step screen

Note: Adding an identical Update message step is optional, but should not be required because ownership of the custom entity record should be set correctly by the Create message step and should stay aligned to the parent record's ownership.

  • Perform the following steps only if your custom Table has an Assigned To Column which you have defined in the configuration settings. These steps ensure that if the person you have assigned to your Table record is not part of the project/program/portfolio team, then the individual custom Table record will be shared with that person as an individual.
  • From the Plugin Registration Tool, right-click (Plugin) SenseiPlugin.Sensei_EnsureAccessForAssignedTo and select Register New Step. Image shows the Registered Plugins and Custom Workflow Activities screen with the Plugin right-click menu displayed with Register New Step highlighted
  • Enter the following details, then press Register New Step.
    1. Message: Create
    2. Primary Table: {Enter your custom Table by its internal name}
    3. Secondary Table: {Leave blank}
    4. Filtering Attributes: {Leave blank - unavailable for a Create action}
    5. Event Handler: {Leave this set to (Plugin) SenseiPlugin.Sensei_EnsureAccessForAssignedTo}
    6. Step Name: {Add a name for your Plugin Step, or leave as per the default.}
    7. Run in User's Context: Calling User
    8. Execution Order: 1
    9. Description: {Add a description for your Plugin Step, or leave as per the default.}
    10. Event Pipeline Stage Of Execution: PostOperation
    11. Execution Mode: Asynchronous
    12. Deployment: Server Image shows Plugin Registration Tool screen with the Assembly Sensei Plugin highlighted
  • Again right-click (Plugin) SenseiPlugin.Sensei_EnsureAccessForAssignedTo and select Register New Step.
  • Enter the following details, then press Register New Step.
    1. Message: Update
    2. Primary Table: {Enter your custom Table by its internal name}
    3. Secondary Table: {Leave blank}
    4. Filtering Attributes: {Select your Assigned To Column}
    5. Event Handler: {Leave this set to (Plugin) SenseiPlugin.Sensei_EnsureAccessForAssignedTo}
    6. Step Name: {Add a name for your Plugin Step, or leave as per the default.}
    7. Run in User's Context: Calling User
    8. Execution Order: 1
    9. Description: {Add a description for your Plugin Step, or leave as per the default.}
    10. Event Pipeline Stage Of Execution: PostOperation
    11. Execution Mode: Asynchronous
    12. Deployment: Server
    13. Delete AsyncOperation if StatusCode = Successful: {Uncheck} Image shows Plugin Registration Tool screen with the Assembly Sensei Plugin highlighted
  • Following best practices, you should add this custom plugin step to your custom Solution.
    • Access your Enhancements solution and select Add Existing > More > Developer > Plug-in step, then locate the newly registered step(s). Image shows the Add Existing, More, Developer, Plug-in step menu
    • Once added, publish all customizations.

Deep deep linking

In release 2021.02.17.2 we introduced the Deep Deep Linking feature to assist with embedding scenarios including Microsoft Teams Tabs. Further information regarding Microsoft Team configuration can be found here: https://hub.sensei.cloud/Docs/Altus/Configuration/Teams/Index.html#teams-app---channel-tab-configuration-setting

To embed a reference to a particular Altus project and tab use the following URL format

https://<orgURL>/main.aspx?appid=<appID>&pagetype=entityrecord&etn=<entityName>&id=<entityID>&navbar=entity&extraqs=sensei_showTab%3D<tabName>%26sensei_hideHeader%3D1

The query string parts are:

  • etn: The short form entity name e.g. sensei_project
  • id: ID of the entity you wish to embed (guid)
  • navbar: set to entity to remove the left-side nav bar
  • extraqs: this is the parameter that holds the additionalAltus specific deep deep linking controls
    • sensei_showTab: is the name of the tab on the entity you wish to have be displayed. This is the internal name of the tab e.g. tab_tasks sensei_hideHeader: when included and set to 1 this also remove more surrounding chrome from the item.

Example:

https://iq.crm.dynamics.com/main.aspx?appid=e1e44b74-6430-eb11-bf68-000d3a799817&pagetype=entityrecord&etn=sensei_program&id=e436ab00-134d-4eed-bcc9-d3ef9a6dde28&navbar=entity&extraqs=sensei_showTab%3Dtab_risks%26sensei_hideHeader%3D1

Image shows the Altus risk App exposed in Teams

Using notes/annotations with project, programs and/or portfolios

The information in this section relates to version 2022.06.07.2 and above of Altus.

Dataverse provides the ability to turn on an attachments/notes capability on each Table/Entity. This can be turned on from the Table properties. Image shows The Edit table screen with the enable attachments section highlighted

Note

Once this has been turned on, it cannot be switched off again for that Table.

After turning on the Attachments/Notes functionality, you would then need to expose this to the UI by adding a Timeline control to your related Table/Entity form. This will provide the mechanism for users to start adding Notes to the project/program/portfolio.

Altus Security Roles are configured with the necessary permissions for users and owner teams to access Notes. Notes are treated as a special case in Altus and you do not need to configure them in the Custom Registers configuration settings.

However, if you intend to use Attachments/Notes for projects, programs and portfolios, then you will need to configure a Plugin Step that runs on creation of new Notes to ensure that visibility of those Notes flows through correctly into Altus.

To perform these steps, you will need to use the Plugin Registration Tool available here.

  • Launch the Plugin Registration Tool (PluginRegistration.exe) and select Create New Connection. Image shows Plugin Registration Tool screen with the Create new connection button highlighted
  • Ensure you select Office 365, then press Login. Image shows the Create new connection screen with the Assembly Sensei Plugin highlighted
  • Enter your credentials to log in to Microsoft 365.
  • In the list of Plugin Assemblies, locate (Assembly) SenseiPlugin and click the arrow to expand.
    • Depending on your implementation, SenseiAtsumeruPlugin is comparable to SenseiPlugin. Image shows Plugin Registration Tool screen with the Assembly Sensei Plugin highlighted
  • Locate (Plugin) SenseiPlugin.Sensei_InheritOwnershipFromProject and right-click and select Register New Step. Image shows the Registered Plugins and Custom Workflow Activities screen with the Plugin right-click menu displayed with Register New Step highlighted
  • Enter the following details, then press Register New Step. This will ensure that whenever a new record is created in your custom Table that the InheritOwnershipFromProject plugin code is run - which will ensure that your custom Table record is attributed the same ownership as the project, program or portfolio that it is associated with.
    1. Message: Create
    2. Primary Table: annotation (this is the Dataverse internal name for the Notes Table/Entity)
    3. Secondary Table: {Leave blank}
    4. Filtering Attributes: {Leave blank - unavailable for a Create action}
    5. Event Handler: {Leave this set to (Plugin) SenseiPlugin.Sensei_InheritOwnershipFromProject}
    6. Step Name: {Add a name for your Plugin Step, or leave as per the default.}
    7. Run in User's Context: Calling User
    8. Execution Order: 1
    9. Description: {Add a description for your Plugin Step, or leave as per the default.}
    10. Event Pipeline Stage Of Execution: PreValidation
    11. Execution Mode: Synchronous
    12. Deployment: Server

Image shows the General Configuration Information pane

Form tab loading

In Kaizen Release - 2022.03.08.4 we have released some form loading improvements to reduce load time and the amount of flashing of tabs and forms. As part of this any conditional tabs have been made hidden to prevent the user from seeing the tabs and then them disappearing, instead we start them hidden and then show the ones that need to be shown.

To make your own custom tabs load smoothly like ours and not impact the user until they are actually required, please hide the tab in the form designer. Note you can make the designer show all hidden controls to allow you to work in the designer with them.

Image shows the Tasks Tab properties pane

For projects/programs/portfolios you should ensure the tabs are configured appropriately in the form tab visibility settings via the Project Types for Projects or the relevant setting for programs and portfolios.

Image shows the Agile Project Enterprise Project Type page showing it's general details and form tabs

Form tab visibility exclusions

Form tabs are hidden by default and then programatically shown. If the intention is to hide a form tab (and keep it hidden), Altus includes a Configuration Setting to facilitate this. Under the Security category in Altus Configuration Settings, select the 'Form Tab Visibility Service Exclusions' (FormTabVisibilityExclusions) setting.

To create a new form tab visibility exclusion, select 'New Form Tab'.

Image shows the Form Tab Visibility Service Exclusions setting 'New Form Tab' button

Configure the following fields to identify the tab that you wish to exclude:

Field Description
Entity Name The Entity/Table that the tab belongs to
Tab Internal Name The Name of the tab that you wish to exclude from the form tab visibility service
Form Id The Form that contains the tab that you wish to exclude from the form tab visibility service
Disabled Can be used to toggle whether the configuration is ignored (if set to Yes, the tab would again be included in the form tab visibility service).

Image shows an example configuration of a Form Tab Visibility Service exclusion

In the above example, the Decisions tab on the Project Tracking form would be excluded from the Form Tab Visibility Service - and would therefore remain hidden when the Form is displayed to users.

Note

Any tab created with an internal name that has '_ignoreftv' as the suffix will automatically be excluded from the form tab visbility service and does not need to be referenced in this configuration setting.

Role Field Check

Altus can be configured to display Fields, Tabs or Forms according to the security role(s) of the logged in user [including any security roles attributed to a Team that that user is a member of]. This can be configured via the Security > Role Field Check (roleSecurityConfiguration) configuration setting.

The configuration setting deals with three types of items that can be configured to be shown only to users with the appropriate role(s).

  • Fields (which can also include sub grids)
  • Tabs
  • Forms
Fields
Field Description
Field Name Identify the internal name of the field, or the internal name of the subgrid that you wish to show based on Security Role(s).
Entity Name Identify the name of the Entity/Table that the field/subgrid relates to.
Security Roles Select the Security Role(s) that the user will require in order to see the field/subgrid. User will see the item if they have any of the selected roles.

Image shows an example Role Field Check field configuration

In the above example, the custom field cr123_mycustomfield for the Portfolio entity will only be shown to users if they have the Altus Admin User or Altus - PMO User security role(s).

Tabs
Field Description
Tab Name Identify the internal name of the tab that you wish to show based on Security Role(s).
Entity Name Identify the name of the Entity/Table that the tab relates to.
Security Roles Select the Security Role(s) that the user will require in order to see the tab. User will see the tab if they have any of the security roles.

Image shows an example Role Field Check tab configuration

In the above example, the custom tab tab_mycustomtab for the Program entity will only be shown to users if they have the Altus Admin User or Altus - PMO User security role(s).

Forms
Field Description
Form Name Identify the form that you wish to show base on Security Role(s).
Entity Name Identify the name of the Entity/Table that the form relates to.
Security Roles Select the Security Role(s) that the user will require in order to see the form. User will see the form if they have any of the security roles.

Image shows an example Role Field Check form configuration

In the above example, the Form called Custom for the Project entity will only be shown to users if they have the Altus Admin User or Altus - PMO User security role(s).

Note

In order for these settings to take effect, the following On Load method must be called from the Form that is being shown to the user: sensei_SenseiProject.Generic.RoleSecurityHandler.FormOnLoad
Many of the out of the box forms shipped with Altus are pre-configured to call this On Load method already (e.g. Projects, Program, Portfolios, etc).

Out of the box behaviour can be altered by selecting to Load Defaults from the configuration setting form for the Role Field Check setting. This will load the default values used internally within Altus and those settings can then be overridden.

Project form UI

The Project Form UI update includes changes to the Project Form which split tab content out across multiple Forms, ensuring that projects are easy to navigate. With this update comes the introduction of 'link tabs' which provide easy navigation between Project Forms.

Create a project form

A new custom Project Form can be added in an Altus environment and integrated with the Project Form UI functionality.

  • Create a new Form for the Project Table and add the content to it that you wish to display to users.
  • Ensure that your custom Project Form references the following Form On Load events:

If New Record Switch To Form

Event Type On Load
Library sensei_SenseiProject.bundle.js
Function sensei_SenseiProject.Generic.FormSwitcher.IfNewRecordSwitchToForm
Enabled {checked}
Pass execution context as first parameter {checked}
Comma separated list of parameters... "59100881-fae9-4d5b-b520-44f769b21f6a"
Note

Ensure that you wrap the parameter (which contains the form Id of the Project Information form) in quotes

Redirect Non-Default Form On Load

Event Type On Load
Library sensei_SenseiProject.bundle.js
Function sensei_SenseiProject.Generic.FormSwitcher.RedirectNonDefaultFormOnLoad
Enabled {checked}
Pass execution context as first parameter {checked}
Comma separated list of parameters... "59100881-fae9-4d5b-b520-44f769b21f6a"
  1. To create a Link Tab on a Form, select to edit the Form you wish to add the link tab to.
  2. Then, select to add a new Tab to your Form. Give the tab an internal name something in the format of 'tab_link_' (e.g. tab_link_mycustomform). If your custom form has spaces in its display name, ensure that you replace those spaces with underscores in the internal link tab name.
  3. Add an On Tab State Change event to that Tab, as follows:
Event Type On Tab State Change
Library sensei_SenseiProject.bundle.js
Function sensei_SenseiProject.Generic.FormSwitcher.FormOnTabStateChange
Enabled {checked}
Pass execution context as first parameter {checked}
Comma separated list of parameters... "*{Id or Name of the Form you wish to redirect to when the tab is selected}*"
Note

Ensure that you wrap the parameter in quotes

Finance app data locking

There are Yes/No fields which can be set to lock fields for editing in the Finance UI.

The fields can be set on the following entities:

  • Project (sensei_project)
  • Project (msdyn_project) [for Altus PFTW Edition]
  • Financial Item (sensei_financialitem)

In each case the fields are named as follows:

  • Actual Cost Locked (sensei_actualcostlocked)
  • Budget Locked (sensei_budgetlocked)
  • Forecast Cost Locked (sensei_forecastcostlocked)

These Yes/No fields are not present on any input forms, they can currently only be set programmatically. Once set, users will be restricted from modifying the corresponding data in the UI. (e.g. if Budget Locked is set at the project level, users will not be able to modify Budget values across the entire project).

Date field formats

When creating Date based fields in a Dataverse Table, there are 2 primary options and each of those contains additional options.

The primary date types are Date Time and Date Only. By default when you create either of these field types, the Behavior field will default to 'User local'. You can optionally change this Behavior setting - but note that if you do, the UI will prevent you from changing it again.

Brief description of the types and how they might be used:

Field Type Behavior Use Cases
Date Time User local This will store the date and time relative to the user who entered the data. The data is then presented to other users according to their own timezone - or as UTC via web services. This field type can be used to indicate the date/time of an event that occurred. E.g. When an approval action occurred.
Date Time Timezone Independent This stores the date and time that was entered by a user independent of that user's timezone. E.g. If a user enters 1/1/2022 at 7pm then that same date and time will be present to any/all users regardless of that user's timezone.
Date Only User local Behind the scenes still stores a Date Time but presents just the date to a user via the UI. The date is translated into the viewing user's timezone. (So would appear as 2022-01-01 in Australia but 2021-31-12 in US). Can't think of too many cases where you would want this behaviour.
Date Only Date only Behind the scenes still stores a Date Time, but the time component will always be stored as midnight and will present to the user regardless of their timezone. This setting is useful for when a user selects a Date value in the UI and expects other users to see that same date value.
Date Only Timezone Independent Again still stores a Date Time value and can be used to store a time component other than midnight (presumably programmatically as you cannot set it through the UI), and the same Date will be presented to all users regardless of their timezone.

Known Altus Solution Upgrade Failures

Managed Layers on Cloud Flows

If you are encountering issues with solution updates and see errors like the one following:

<Message>Workflow uninstall: FAILURE, workflow id 8b3c54b8-8419-eb11-a813-000d3a7946d7 category ModernFlow name Proposal Approval Atsumeru </Message>

The error is due to a managed layer sitting on top of the OOTB (out of the box) Altus cloud flow. As part of the standard Dataverse upgrade process the layer must be removed and as there is a layer on top of which depends on this flow, this action is not possible.

This can be resolved by deploying the customisation solution to the target environment without the cloud flows. Remove the managed layer on the flow, then apply the upgrade, then re-introducing the customisations on that flow by importing the solution back in. To avoid this error, it is recommended that the OOTB cloud flows are not customised, but instead copied and altered respectively.