System settings
Scheduled processes and logs
Scheduled processes
Scheduled processes can be viewed and modified by users with the Altus Admin User role. The Scheduled Process menu item appears in the Settings area of the Altus app.
Scheduled Processes are a convenient way to be able to trigger Dynamics Workflows on a recurring basis for records that are returned from a FetchXML query that is defined in the Scheduled Process item details.
The details of a Scheduled Process item defines the following columns:
Column Name | Description |
---|---|
Name | The name of the Scheduled Process |
Related Table | A plain text indication of the Table that the Scheduled Process relates to or primarily deals with. |
Workflow | A lookup to a Dynamics workflow that will be triggered for each record returned from the specified Fetch XML Query. |
Configuration Setting Name | If you wish to turn on/off the functionality of the Scheduled Process from a Configuration Setting, specify the name of your boolean Configuration Setting here. Note, the 'resourceHorizon' setting is treated as a special case - other configuration setting references should just have a single boolean value. |
Job Frequency | Set whether you intend the Scheduled Process to run Hourly, Daily, Weekly or Monthly. |
Last Run Time | The Date/Time when the Scheduled Process last ran successfully. |
Next Run Time | The Date/Time when the Scheduled Process is set to run again. |
Fetch XML Query | A Fetch XML query that will return records from the table that you wish to run the specified Workflow for. If this column is blank, then the Scheduled Process will run only as a maintenance task - cleaning up old Process Logs and ensuring that the other Scheduled Process jobs are running. |
For Scheduled Processes that are shipped in Altus, where possible these processes will log feedback for each job into the Process Log table. This will include logging information about the process being followed and will include the following columns:
Column Name | Description |
---|---|
Name | The job that the Process Log relates to. |
Scheduled Process | The Scheduled Process that the log was generated from. This column will only be populated for Logs generated from the Scheduled Process itself. |
Workflow Process | The Workflow Process that the log was generated from. This column will only be populated for Logs generated from a Workflow Extension relating to an individual Workflow item (where configured). |
Related Table | The table that the Process Log relates to. |
Related Table Id | The Id of the table record that the Process Log relates to. This will only be populated for Process Logs generated from a Workflow Process. |
Process Status | Displays the outcome of the job - In Progress, Completed or Failed. |
Start | The date/time that the job commenced. |
Finish | The date/time that the job completed. |
Message | The log information for the job. Each log row is prefixed with the time that had elapsed between the job commencing and that log row being generated. Note that these logs are generated via Workflow Extensions which have a hard 120 second runtime limit. |
If a Scheduled Process job is failing before even creating a Process Log record, a System Administrator should investigate the System Job that was generated to run by the creation/update of the Scheduled Process record.
It is not unexpected for there to be times when a Scheduled Process job fails because of a timeout. This can be due to the load on the Power Platform at the time that the job is running (including during Solution deployments). If a job fails due to a timeout error, the Process Log may appear as 'In Progress' for longer than expected - until a subsequent maintenance job comes along and marks it as failed. If there is a maintenance job running as a Scheduled Process, it should recognise that the job has failed and should then automatically trigger it to run again.
Two Scheduled Process jobs are shipped with Altus and are enabled by default.
- Capacity Calculation for Bookable Resources
- Maintenance
Note
Job Frequency settings are indicative only. Next Run Time is an approximate and indicates the time that the job will be triggered into the System Jobs ready for action. The actual run time will vary depending on a number of factors, including the load on your environment at the time. Note also that over time, the Next Run Time value for your Scheduled Process will adjust based on the actual execution time of your Scheduled Process jobs. For example if a Daily Scheduled Process is scheduled to run at 1pm but for whatever reason the actual run time is 1:13pm, the Next Run Time value will be adjusted to 1:13pm for the next day.
Software boundaries and limits
Custom Altus Theme
To learn more about this feature please review the Microsoft instructions.
Selecting Colours
Clients often have one or two primary brand colours that align with their logo. These are usually good choices for a header colour. For a simple theme, you can select one colour, and the system will generate additional palette colours that ensure readability and contrast.
To find the right colour to use, consider these options:
- Ask the client if they have a preferred header and content colour scheme, or request a brand document for guidance.
- If you have access to their intranet, check for a brand document.
- Use a tool like the eyedropper in Paint to pick a colour from their logo or website.
Sometimes, the client may prefer the default theme, or a neutral colour such as black, white, or grey.
For more advanced theming, you can apply multiple colours and even different fonts. If you select just one colour, you can preview the generated palette using the Fluent Theme Designer.
Applying the Theme
Microsoft provides a detailed guide for applying custom themes here.
Here are the high-level steps:
Creating the Theme Definition
The theme definition is an XML file that holds configuration options for theming. One theme definition is applied to the whole environment, including all model-driven apps. Each environment can have its own theme definition if needed.
Example XML:
<CustomTheme basePaletteColor="#604878" lockPrimary="true" vibrancy="2" hueTorsion="13" font="Inter, Segoe UI, Roboto, Helvetica Neue, Arial, sans-serif">
<AppHeaderColors background="#604878" foreground="#FFFFFF" backgroundHover="#4D4C4C" foregroundHover="#FFFFFF" backgroundPressed="#2F2D2D" foregroundPressed="#FFFFFF" backgroundSelected="#616161" foregroundSelected="#FFFFFF"/>
</CustomTheme>
- The
basePaletteColor
is used to generate colours for UI elements like buttons and links. - The
background
value inAppHeaderColors
sets the app's header colour.
For a full list of options, see the Microsoft documentation.
Give this file a name relevant to the client and save it.
Setting the Theme
- Create a solution in the development environment called Environment Theme.
- Select Add Existing → More → Web Resource and choose the OOTB web resource Altus Custom Theme (
sensei_CustomTheme.xml
). Add it to the solution. - Apply your changes to the XML file and save it as a new file.
- Edit the web resource, select Choose file, and upload your custom XML file.
- In the solution, select Publish or Publish all customisations to apply the changes.
Note: For a simple header colour change, you only need to modify the
AppHeaderColors background
value. For a full experience, also updatebasePaletteColor
.
You should now see Altus in the new theme colours!
Each environment can have a different colour scheme by using a separate XML file for each. The Environment Theme solution should be unmanaged in all environments. An unmanaged layer will be applied on top of the OOTB item.
Adding a Logo
To complete the look, you can add a logo to replace the “Power Apps” branding text.
Acquiring the Logo
Supported formats include PNG and SVG. To get a suitable logo:
- Ask the client for an image file, ideally with a transparent background.
- Use the client’s website and inspect element to save a copy of their logo.
For best results, ensure the image has some empty space around the border. For SVGs, you can adjust the viewBox
attribute.
Applying the Logo
- Add the logo as a web resource to the environment theme solution.
- Open the environment's advanced settings.
- Go to Customisations → Theme.
- Select the CRM Default Theme and create a copy.
- Select the web resource for the logo and set the logo tooltip (e.g., “Altus”).
- The colour values here apply to the classic theme, which is no longer used after the 2025 Wave 1 release of Power Apps. To set colours, use the steps in the previous section.
- Select Publish Theme to set this as the default, displaying the logo for all apps in the environment.
Business Process Flow (BPF) field locking
This feature enables the locking of past and/or future fields within different stages of the BPF process.
In the below example the locking is enabled for future BPF steps as indicated by the locks present next to each of the fields.
Note
If current BPF step contains a field from a past/future BPF step value will remain unlocked.
Configuration
BPF Field Locking is disabled by default.
- In order to Activate. Navigate to
Settings > Configuration Settings > Inactive Altus Config Settings > Business Process Flow Field Locks
- Click Activate in the hot bar and confirm dialog.
- Click “+ New Item” then we edit it by clicking on the pencil. This will bring up the following.
- Add Entity Name.
- Click “+ New Process” – then either click on drop down arrow or the box select a business Process Name.
- Click “OK” confirm settings
- Click Save in the hot bar to save setting
- The BPF Field locking should now apply to the specified entity and past/future stages if selected to apply to future stages.
Adding Logging to a new Business Process Flow (BPF)
After configuring these Plugin registration steps, whenever your BPF Table is used (e.g. when a project moves from one stage to another) the entity/table BPF Log will be updated and track the progress of the BPF (it records the stage, and timestamps of process moves).
The table that this data is saved into is: sensei_businessprocessflowlog
Column | Column Type | Example | Details |
---|---|---|---|
Process Id | String | 1b758fbb-626e-e146-9144-26b9402b5f89 | [dbo].[workflow], [workflowid] |
Process Instance Id | String | 1b758fbb-626e-e146-9144-26b9402b5f89 | Depends on what workflow is being run: [sensei_project_minorprocess]/ [sensei_project_majorprocess]/ [businessprocessflowinstanceid] |
Date Entered | Date | 1/01/2023 | |
Stage Entered | String | ||
Primary Entity | String | sensei_project | |
Primary Entity Id | String | 1b758fbb-626e-e146-9144-26b9402b5f89 |
To add logging to a new BPF
Ensure all the steps outlined here have been completed
Take note of the new entity/table that has been created for your new BPF.
Using the Dynamics Plugin Registration Tool, select to log in to your Environment.
Once connected to your environment, scroll down to locate the SenseiAtsumeruPlugin assembly.
Expand the assembly and locate SenseiPlugin.Sensei_GenericBusinessProcessFlowAudit
Right-click select Register New Step
Configure the step with the following settings, then press Register New Step.
Message Create Primary Table {Select your BPF Table}
Secondary Table {Select your BPF Table}
Filtering Attributes {All Attributes}
Event Handler SenseiPlugin.Sensei_GenericBusinessProcessFlowAudit
Step Name Altus – Business Process Flow Log: Create of {Your BPF Table Name}
Run in User's Context Calling User
Execution Order 1
Description Altus – Business Process Flow Log: Create of {Your BPF Table Name}
Event Pipeline Stage of Execution PostOperation
Execution Mode Asynchronous
Deployment Server
Delete AsyncOperation if StatusCode = Successful Checked
Again Right-click '(Plugin) SenseiPlugin.Sensei_GenericBusinessProcessFlowAudit’ 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 Create Primary Table {Select your BPF Table}
Secondary Table {none}
Filtering Attributes {All Attributes}
Event Handler SenseiPlugin.Sensei_GenericBusinessProcessFlowAudit
Step Name Altus – Business Process Flow Log: Create of {Your BPF Table Name}
Run in User's Context Calling User
Execution Order 1
Description Altus – Business Process Flow Log: Create of {Your BPF Table Name}
Event Pipeline Stage of Execution PostOperation
Execution Mode Asynchronous
Deployment Server
Delete AsyncOperation if StatusCode = Successful Checked
Business Process Flow (BPF) Reporting
To add BPF data to your reports use the sensei_businessprocessflowlog table outlined above.
Altus Matrix
Configuration
Example for Issues Matrix on Project
Entity Form Configuration
- You will need to either create/have a customisation solution. In this solution you will need to add the desired Entity which contains the desired form you wish to customise.
- In our case that is a solution called Customisation with the existing entity, Project with the form Tracking.
- If you are looking to use the filtering component as we are of the subgrid attached we need to get the Name under "Specify a unique name" in the corresponding subgrid. This is "Subgrid_Issues" in this case. Take note of this for the configuration of the "Altus Matrix".
- Define the label of the matrix displayed by defining Label on this page under the Name section.
- Configure the entity that is mapped by specifying the Data Source. In this case its for Issues.
- Configuration of the Altus Matrix Control. Add the control Altus Matrix under controls and dont forget to change that it should be used for all forms of presentation (Web, Phone and Tablet).
- Entity field bindings is going to be automatically mapped from your selection in the Display component where a mapping is created for the issue entity.
- JSON config setting this is the name you choose for the configuration setting you will define later on. In our case we have SenseiProjectIssuesConfiguration_IQA keep this somewhere for later.
- Subgrid Name shall be the Name from part 2 above.
- Parent Relationship Field this is in this case the sensei_project as that is the parent used in part 1. This needs to be lowercase or the grid wont render. If you have created a custom entity you will need to lookup the corresponding Parent Relationship Field ie cr78f_project.
Configuration Setting Setup
An Admin will need to open two browsers to the configuration settings page. We are going to go to the inactive setting on one page and look for in our case SenseiProjectRiskConfiguration_IQA.
On the second browser navigate to the configuration settings page. Click add New configuration setting. Label it with an appropriate Category, Display Name, Description. Set the Name equal to the JSON config setting Name from earlier, ie. SenseiProjectIssuesConfiguration_IQA
NOTE: if you would like the X and/or Y Axis Labels to display the choice set labels you will need to set the value in the X Axis Label Max Length and Y Axis Label Max Length fields so that they equal >0. If they are set to 0 they will only display numbers.As a template we are now going to copy the JSON Schema and UI_Schema from first page by clicking the gear in the bottom right hand corner. Copy the JSON Schema to the second page in the Json Schema section, then also Copy the UI Schema from first page to the second page.
Customisation via Configuration Setting
Below is an example of the configuration of issues matrix further details will be explained below.
Note
For more information on how to configure the Matrix click here.
Customisation Steps
Choose your row and column you wish to display.
- You will need to get the internal name for the desired entity. ie Issues has Priority (sensei_priority) and Category (sensei_category). These need to be lowercase for it to work.
- Navigate to make.powerapps.com then select the environment that contains Altus select the Tables in left hand column. Once you select it select all then in the right hand corner search for Issue in this case. Then under schema click columns. Then you are going to be looking for a datatype of Choice. Then you can lookup Name column as shown below.
We then can adjust the values after saving the configuration for each of the axis. This involves changing the Axis Label
- Noting that if your field ends with index you can leave 'index' off and update the field ie in Risk matrix. with index otherwise it should be populated with dataset. In this example sensei_priority and sensei_category are selected with the corresponding dataset tag.
Dashboard widget
What will be covered in the below sections.
What is it?
Known as WidgetDashboard Control, this control has the capacity to display a table and/or a chart with the same data fetched from Dynamics 365 queries.
Example: Displays counts of Assigned and Unassigned tasks in Table and Pie chart formats.
Example: shows five of the different ways this one widget can be configured
This widget has two key components; N Column based Table, and an optional Chart (Pie, Bar or Donut) which will represent the data from the table above.
How to setup a new instance of the widget
Navigate to the desired form in the classic designer.
Navigate to desired tab
Click on desired tab
Insert new SubGrid
Drag in a field to be used as a dummy. i.e. "Location" field (sensei_location)
Double click on the added Field and uncheck display label.
Navigate to the Controls tab then Click Add Control and find Altus Dashboard Widget" and click Add
Configure the settings - setting Web/Phone/Tablet to use the control then we edit the JSON config setting using the pencil.
Specify the desired config setting name that we will create shortly.
When complete you should have a window that looks similar to the following. Click OK.
Now Save then Publish the Form.
We are now going to create an Altus Setting. Navigate to the Settings > Configuration Settings. Now Duplicate the current tab as we will use this later on. Now click New in the hot bar on the first tab.
The main things that need to be filled out is Display Name and Name, which Name should match what was set in step 10.
Switch to second tab we duplicated earlier and locate an existing Active/Inactive Altus Setting for Task Dashboard Widget. Double click it to open it then Click the gear in the bottom right corner.
Copy the contents of the two text boxes across to the first tabs gear icon, this will allow for easy editing of the configuration setting.
Congratulations! You're now ready for the next step which is Widget Configuration found below.
Widget configuration
If you wish to customise what is shown in the widget you need to adjust the appropriate Altus Setting.
Found under Settings > Configuration Settings > Active/Inactive Altus Config Settings
Each widget should have its own configuration setting configured while adding to form.
In the case of Planner the following configuration settings are overrides for default configuration of the widget.
There are two screens that allow for modification to how the widget is displayed.
Main Settings screen
Option | Description |
---|---|
Title | Set the title on the widget. If blank, there will be no header on the widget. |
Labels Shade Colour | This is the colour of the background of the labels found in the table. |
Show Numbers | Toggle if the Table is to be shown. |
Show Chart | Toggle if the Chart is to be shown. |
Chart Type | Select form of Chart to be displayed (Pie, Column, Donut) |
Chart Size | Increases the scale of the chart |
Padding Above Chart | Optional padding to insert above the chart if the labels are creating issues. |
Value Configurations | These are the values that are rendered in the Table and/or Chart. |
Value Configurations screen
Option | Description |
---|---|
Id | An id for the value. This can be used to overwrite built in value. |
Label | The label to use for the number or the chart. Please keep concise to fit in the limited space. |
Default Value | The value to show while the values are being calculated. |
Colour | The colour to use for the number or the chart. i.e. #FF0000 for red. |
Icon | This can either be fluent ui icon, a base64 data:string or a path to a icon file. |
Order | Determines the order to show the value. Built in orders are generally set to 10,20,30.. |
Odataurl | The dynamics O365 ODATA query that will return a collection (potentially an aggregation) |
ODatafield | The field from the first ODATA query result to return otherwise a count of results will be used. |
Hide | If you want to hide an out of the box value enter the id and check this field. |
Editing an existing setting
- Activate the Deactivated Setting by using the top hot bar.
- Click + New Value Configuration to add a new value
- Click the pencil to edit it.
- We have a few options of what we can do
Note
To overwrite a built in value the id will need to match. Generally we have used the label in lower case without spaces.
Numeric Adjustments
With numeric adjustments the dashboard widget and summary controls can now support scenarios such as:
- Calculating a variance or EAC by subtracting the result of one OData query from another
- Live updating values based on field changes on the form
- Calculating percentages using static values on top of form values as well as OData query results
- Complicated scenarios can be done in code, and as long as a form field has the result it can be show in the control as a value OData is powerful for calculating single values. Combining 2 results previously required custom code.
Example:
Calculations used:
- Budget – Actual = OData minus OData
- Proposed Costs = form field (from Project Details tab)
- Proposed – Actual = form field minus OData
- Percent complete = form field
- Proposed Progress = form field divided by static value, then multiplied by form field
The widget configuration will allow for a singe value to be configured. Specific to the value the key fields are:
- OData URL: the server relative OData URL to query for the value. This is optional and can be left blank if retrieving the value from a form field. Generally, this will be a Dynamics OData query otherwise will need to be a OData endpoint without authentication. Note the standard tokens used in status updates are supported here. The query can make use of any supported features from the server including aggregation and can return JSON containing a single object or multiple.
- Field (OData/Form): this will be the prop on the response from the OData query or a form field on the form (if not using OData) to show. Generally, the formatted value returned by the query will be used.
- Override Format: allows the user to override the formatted value. The key supported scenario here is currency where the widget will use the currency specified on the form entity, otherwise fall back to the org currency.
If this main value is a number (not text or a date), it can then be adjusted by incrementally adding/subtracting/dividing/multiplying subsequent numeric values. These values cannot only be OData or form fields, but also static numbers.
To adjust the main number, click on the “New Numeric Adjustment” button. You will note the same fields as above with the addition of:
- Operator – the operator to apply incrementally between the previous result and the specified value.
- Static Value – by leaving the OData URL and field blank you can specify a static value. This is useful when having to process percentages.
Each adjustment will be applied to the previous one in order, before applying the next one.
Possible Customisations
Hide an existing value. Enter the Id, then select Hide slider at the bottom of the page
Override an existing value. Enter the Id, then Label as these are mandatory fields. All other fields if not filled in will default to the default configuration setting.
Create a new value. Enter the desired Id (this must be unique), then Label that you want displayed. You will need to fill out all the following fields or it can result in No Data being displayed due to incorrect configuration.
Options to customize are as follows:
Example of Default configuration for Assigned:
Example of customized configuration setting for Assigned. In this example we have added a web resource as an icon to assigned. Inserted a custom yellow odata query with a custom fluent ui icon and we have changed the colour and added an icon to unassigned.
Example:
Assigned Configuration - override:
Custom Configuration:
Unassigned Configuration - override:
Default Value – The value shown while the values are being calculated
Colour – The colour used for the number of the chart starting with #
Icon – This can either be fluent ui icon, a base64 data: string or a path to a icon file
Examples are as follows:
- Fluent ui -
"6PointStar"
- base64 image -
"data:image/png;base64,*"
(* represents the base64 encoded string) - path to webresource -
"/WebResources/sensei_Green-OnTrack.svg"
Order – Determines the order to show the value. Built in order are generally set 10, 20, 30 …
- in our example we set custom to be 15 which is in between 10 and 20.
Example of where Order of 15 is used on existing Assigned widget will place it in the middle of Assigned and Unassigned.
Odataurl – The dynamics O365 ODATA query that will return a collection (potentially an aggregation)
The Odataurl query is made up of the following pattern as a minimum.
Note
{{value}}
denotes a value that should be substituted for by consultant. {parentId}
will be substituted in.
/api/data/v9.0/{{entity_name}}?filter=_sensei_project_value eq "{parentId}"
This will be attached to the end of the current dynamics environment to generate the query. The following query will be counted as no aggregation value is provided.
i.e. https://orgXXXXXXXX.crmY.dynamics.com/api/data/v9.0/sensei_tasks?filter=_sensei_project_value%20eq%20%27{parentId}%27
URI Encoded:
/api/data/v9.0/{{entity_name}}?filter=_sensei_project_value%20eq%20%27{parentId}%27
Example of:
- Simple Count
/api/data/v9.0/sensei_tasks?$filter=_sensei_project_value eq '{parentId}' and sensei_resourceassignment_task_sensei_tas/any()
- Aggregation of values
/api/data/v9.0/sensei_tasks?$filter=_sensei_project_value eq '{parentId}' and sensei_resourceassignment_task_sensei_tas/any()&$apply=aggregate(sensei_taskid with countdistinct as total)
- Sum of financials
/api/data/v9.0/sensei_financialtransactions?$filter=_sensei_project_value eq '{parentId}' and sensei_type eq 955000000&$apply=aggregate(sensei_value with sum as total)
- Max date
/api/data/v9.0/sensei_tasks?$filter=_sensei_project_value eq '{parentId}'&$apply=aggregate(sensei_duedate with max as maxdue)
Odatafield – The field from the first ODATA query result to return otherwise a count of results will be used. In the above example a value total is used.
Note
If an Odataurl and Odatafield are not provided no data will be displayed for the chart.
Example OData Queries
Note
For more information on how to use these queries please see here.
Budgets
- url =
/api/data/v9.0/sensei_financialtransactions?$filter=_sensei_project_value%20eq%20%27{parentId}%27%20and%20sensei_type%20eq%20955000000&$apply=aggregate(sensei_value%20with%20sum%20as%20total)
- field =
total
Forecasts from before the end of the current month
- url =
/api/data/v9.0/sensei_financialtransactions?$filter=_sensei_project_value%20eq%20%27{parentId}%27%20and%20sensei_date%20le%20{endOfCurrentMonth}%20and%20sensei_type%20eq%20955000001&$apply=aggregate(sensei_value%20with%20sum%20as%20total)
- field =
total
Actuals from before the start of the current month
- url =
/api/data/v9.0/sensei_financialtransactions?$filter=_sensei_project_value%20eq%20%27{parentId}%27%20and%20sensei_date%20lt%20{startOfCurrentMonth}%20and%20sensei_type%20eq%20955000002&$apply=aggregate(sensei_value%20with%20sum%20as%20total)
- field =
total
Deliverables this quarter
- url =
/api/data/v9.0/sensei_deliverables?$select=createdon&$filter=_sensei_project_value%20eq%20%27{parentId}%27%20and%20sensei_duedate%20gt%20%27{startOfCurrentQuarter}%27and%20(statuscode%20eq%20955000000%20or%20statuscode%20eq%201)
Query Tokens
The following tokens can be used in status update queries, or by the summary and dashboard widgets:
{parentId}
{utcNow}
{todayDate}
{startOfCurrentMonth}
{endOfCurrentMonth}
{startOfCurrentQuarter}
{endOfCurrentQuarter}
{startOfCurrentYear}
{endOfCurrentYear}
The following token is only relevant for the dashboard widget:
Each of the date tokens display their result in the following format: YYYY-MM-dd
Sub Tab Control
The Sub Tab Control provides the capability of configuring a Form so that each section that exists within a Tab will appear as a Sub-Tab within that Tab.
To use the Sub Tab Control you will need to:
- Configure the Form that you wish to use the control on
- Add a Configuration Setting specific to your use case which identifies the sections that the Sub Tab Control will show/hide
First, identify the Form in Dataverse that you would like the Sub Tab Control to appear on.
Ideally in an Enhancements unmanaged Solution, add a reference to that Form and then open the Form in Edit mode.
Once your Form has opened in Edit mode, select 'Switch to classic' to edit the Form in Classic mode.
Next, identify or create the tab that you wish to use the Sub Tab Control within. We will be configuring the Sub Tab Control to show/hide the Sections that exist within that Tab. So if you do not have any existing content in the tab, go ahead and create a couple of Sections and add to those Sections whatever content you want to appear within it.
Ensure that the Sections within the Tab have an appropriate Name and Label - it is this 'Label' field of the Section that will determine the name of the Sub Tab. Also unselect the checkbox for 'Visible by default' in the Section settings. This will ensure that the Sub Tab Control controls its visibility. It is recommended to use Sections which are configured with one column of full width.
Next, in a new browser tab (keep your Form open in Edit mode, you'll return here in a moment) navigate to the Altus app and from the Settings Area open Configuration Settings.
Select to add a new Configuration Setting
Enter the following details for your new Setting:
Field | Value |
---|---|
Category | { Enter the Settings category that you would like your new setting to be grouped under. } |
Display Name | { Enter a Display Name that describes your new setting. } |
Name | { Enter a unique and succinct Name for your new setting. Do not include Spaces. } |
Description | { Enter a brief Description for your new setting. } |
At this point, Save your new Configuration Setting, then select the cog icon at the bottom right corner of the Value field.
Copy and paste the following 'JSON Schema' and 'UI Schema' values (without modification) into the dialog box for your new Configuration Setting, then press OK.
JSON Shema
{
"$schema": "http://json-schema.org/draft-07/schema#",
"title": "Tabulator Configuration",
"type": "object",
"properties": {
"controls": {
"title": "Control Tabs",
"type": "array",
"items": {
"title": "Tab",
"type": "object",
"properties": {
"controlName": {
"title": "Control or Section Internal Id",
"description": "The display name for the tab will be taken from the control or section.",
"type": "string"
},
"dependentConfigSetting": {
"title": "Dependent Config Setting",
"description": "A boolean config setting that will determine if the tab should load. true = hide.",
"type": "string"
},
"dependentConfigSettingPath": {
"title": "Dependent Config Setting JSON Path",
"description": "Optional. If the config setting that will determine whether the tab should load contains the boolean value within its JSON schema (rather than having a direct boolean value), specify the JSONPath to that schema property here.",
"type": "string"
},
"flipBoolean": {
"title": "Flip Boolean",
"description": "As above, boolean value of true will hide the sub-tab. Set this value to true to switch that behaviour.",
"type": "boolean"
},
"alwaysHide": {
"title": "Always Hide",
"description": "Always hide this control",
"type": "boolean"
},
"securityRoles": {
"title": "Security Roles",
"description": "If specified, will restrict the tab visibility to those users with at least one of the selected roles.",
"type": "array",
"items": {}
}
},
"required": [
"controlName"
]
}
}
},
"required": [
"controls"
]
}
UI Schema
{
"controls": {
"useArrayItemDialog": true,
"items": {
"securityRoles": {
"ui:widget": "EntityLookupArrayWidget",
"ui:options": {
"endPoint": "roles",
"valueField": "_parentrootroleid_value",
"nameField": "name"
}
},
"dependentConfigSetting": {
"ui:widget": "EntityLookupWidget",
"ui:options": {
"endPoint": "sensei_senseiconfigsettingses",
"valueField": "sensei_name",
"nameField": "sensei_name"
}
}
}
}
}
You should now see the UI for the Sub Tab settings appear in the Value field. Select '+ New Tab' as many times as needed to create one instance for each Section that you wish to show/hide in your Form's Tab area'. In this example we have 2 Sections to show/hide in our Custom Tab, so we are adding 2 tab sections to the setting. We will configure these in a moment.
Select the Edit icon next to the 'Tab 1' placeholder setting.
In the dialog window for the configuration of your Sub Tab, enter the following details, then press OK:
Field | Value |
---|---|
Control or Section Internal Id | { Refer back to your browser tab that contains your Form in Edit mode and open the Section Properties for your first section. Enter the Name property exactly as it appears. } |
Dependent Config Setting | { Optional: Select/enter a Configuration Setting that you would like to control whether the Tab is visible at all. The Configuration Setting must contain a boolean value or a boolean value within the Schema of that setting. } |
Dependent Config Setting JSON Path | { Optional: If you have specified a 'Dependent Config Setting' and the boolean value are targeting is within the Schema of that Setting (e.g. not a direct Boolean value), then specify here the JSONPath that can be used to target that boolean value (e.g. $.nameOfProperty). If the config setting you referenced in 'Dependent Config Setting' has a direct boolean value, then leave this field blank. } |
Flip Boolean | { Optional: If a Dependent Config Setting has been specified, the default behaviour will be for a value of 'true' to Hide the SubTab. Select Yes for this setting if you would like a value of 'false' to Hide the SubTab. } |
Always Hide | { Optional: Switching this setting to Yes will allow you to Always Hide a SubTab. This is particularly useful in instances where you are choosing to Hide a SubTab setting that is shipped in Altus. } |
Security Roles | { Optional: If specified, will restrict the SubTab visibility to those users with at least one of the selected Security Roles. } |
Repeat for all remaining SubTab setting placeholders that you created.
Select to Save your Configuration Setting.
Next, return to your browser tab that contains your Form in Edit mode. Add a new single column Section to appear as the first Section in the Tab. Within that new Section, add a form single line of text field to the Section. It can be any single line of text field, including a field which already appears elsewhere on the form.
Double click the form field to open its properties.
- Ensure that 'Display label on the form' is unchecked
- Ensure that 'Visible by default' is left checked
Still within the Field Properties dialog, select the Controls tab.
Select 'Add Control'
Select 'Altus Sub Tab Control', then press 'Add'
Select the control to display for Web, Phone and Tablet, then configure the following properties:
Property Name | Value |
---|---|
Ignored Property | { This is ready only and will contain the name of the field that you selected to house the control } |
JSON config setting | { Enter the Name field (not the Display Name field) of the Configuration Setting that you created earlier } |
Hide section border | true |
Save and Publish your Form. Your SubTabs should now be viewable when used in the Altus app.
Limitations
- Sub Tabs are not currently able to participate in deep deep linking.
- When switching between SubTabs, users are not prompted to Save modified data. This could lead to perceived data loss scenarios depending on the contents of the SubTabs.
- Sub Tab control cannot be used in conjunction with any of the following Tabs that are shipped in Altus (due to those tabs having special configuration to allow a single control to appear full screen in the tab section):
- Bookable Resource form - 'Allocation' tab
- Project Information form - 'Tasks' tab
- Project Resource form - 'Resource Plan' tab
- Proposals form - 'Resource Plan' tab
- Timesheet form - 'General' tab
- Note that the 'sub tabs' that appear directly within the Tasks control on the Project form are hard-coded within that Control and are not configurable.
- Please be careful not to add too many SubTabs to a single Tab. Adding too many sub tabs to a single tab will result in some sub-tabs not being visible/selectable on the page.
- Warning: Moving existing out of the box top level tabs to become sub-tabs should be done with caution. Moving them will break any direct links you have created to those items. In addition, doing so may create ongoing technical debt to ensure that future feature releases are not hidden from you by your customization layer.
Controlling Button Visibility by Config
Controlling visibility of a button based on a configuration setting
This is achievable using a built in javascript function in the sensei_SenseiProject.bundle.js.
sensei_SenseiProject.Generic.ConfigUtils.userHasSecurityRoleInConfig (
// “Configuration Setting Name” the is what is expected to be configured inside the environments configuration settings
configSettingName: string,
// “Path” – this should be path separated by “/” between elements within the above “Configuration Setting Name”
jsonPath: string,
// “Label” – this should be the name of the array that contains a list of User defined root roles
jsonLabel: string,
// “Key” – this should be the key value within an array which contains a list of User defined root roles
jsonKey: string,
// “Default roles” – If there is no configuration setting present and active in the environment then these comma separated root roles will be able to see the configured buttons
defaultValue: string
): Promise<boolean>
Below are some example uses of the function. Each example makes use of different parameters
Example use case - Show specific button based on user having security role/s in config
In order to conditionally show the "New" button if a user has permission to do so using a "configuration setting" to add new timesheets the following demonstration will illustrate how to configure ribbon workbench using XrmToolbox.
Install XrmToolbox
Open XrmToolbox
Create Connection to environment
In the bottom left corner create a new connection
- Click "Not connected"
- Click "Create new connection"
Make a new connection via the following:
Open the Login dialog box
Login to an "Office 365" tenant
- Select "Office 365"
- Toggle "Display list of available organizations"
- "Login"
Complete standard login and password with a minimum of system Customizer role to allow for editing of the ribbon.
Set the name it should be saved as:
- Recommend saving it under the tenant name
- Select finish to create connection
Now we need to connect to this created environment
- Click "Not connected" in the bottom left corner
- Expand "CM836721" in my example
- Click "Connect"
Opening "Ribbon Workbench"
- Click on "Tools"
- Click in search box area, Search for "Ribbon Workbench"
- Click on "Ribbon Workbench"
Using Ribbon Workbench to customize the environment
- Ribbon workbench allows for customization of upto 5 entities at one time. This is done by the following steps
- Create new solution or edit an existing solution with 5 or less entities in dynamics
- Add Existing entities you wish to customize by adding references only as shown below
- Click Entities in left hand side
- Click the "Add Existing" button
- Select solution components:
- Select the Entity you wish to customize. ie. Timesheet in our case
- Click "OK" to confirm
- Select Entity Assets to Include in the Solution:
- We want to uncheck "Include entity metadata"
- We want to select the "Main" form
- Click "Finish" to add it to the solution
- Select the solution which you wish to customize
- In this case we are using "RibbonWorkbench" solution
- Click on "OK" to load solution
- Ribbon workbench allows for customization of upto 5 entities at one time. This is done by the following steps
Now we need to customize the command associated with the buttons within Ribbon Workbench
- Select from the drop down menu “sensei_timesheet”
- Left click on “+New” in the ribbon
- You should see that the “Mscrm.HomepageGrid.sensei_timesheet.NewRecord” the key part is the “NewRecord” as there are two different ones under the “HomeGrid”
- Now Right click on that new button
- Select “Customize Command” this will populate the existing configuration for that button.
Repeat the above step 6 but for “SubGrid” and “Form”
- Confirm its correct button and customizing it
- Left click “New {0}” and confirm that it ends with “NewRecord” at arrow 3
- Right click the “New {0}” and Customize button
- Confirm its correct button and customizing it
- Left click “+New” and confirm that it ends with “NewRecord” at arrow 3
- Right click the “+New” button and Customize command
- This value will change depending on which of the buttons selected. Ie HomepageGrid, SubGrid, Form
- Look to see under “Enable Rules” – “sensei.sensei_timesheet.UserHasSecurityRoleInConfig.EnableRule” a. Click on this to edit the rules configuration in the next step – modifying this one value will adjust it for all three buttons as they all reference this “Enable Rule”
- Confirm its correct button and customizing it
Now we are going to go through how to customize the “Enable Rule”
- Left Click – sensei.sensei_timesheet.UserHasSecurityRoleInConfig.EnableRule
- Based on the settings you want to test adjust the following. “InvertResult” should be True or False(blank)
- “Configuration Setting Name” the is what is expected to be configured inside the environments configuration settings
- “Path” – this should be path separated by “/” between elements within the above “Configuration Setting Name”
- “Label” – this should be the name of the array that contains a list of User defined root roles
- “Key” – this should be the key value within an array which contains a list of User defined root roles
- “Default roles” – If there is no configuration setting present and active in the environment then these comma separated root roles will be able to see the configured buttons
For this example the following are values are used
Ribbon Workbench settings
{ "InvertResult": "False(blank)", "Configuration Setting Name": "timesheetConfiguration", "Path": "", "Label": "timesheetAddNewButtonRoles", "Key": "", "Default roles": "4c0f0c74-8b09-ea11-a811-000d3a530fe5" }
Now once these are configured we need to publish the settings to the environment ~2-5 minutes at a time. It also is know to throw errors even though it has worked.
- Select “Publish” to apply the changes
Confirm dialog about making sure you back up your solution
- Select “Publish” to apply the changes
For the given configuration described above in part 9 we require a configuration setting called
timesheetConfiguration
it can be configured by using the following JSON Schema and UI Schema. The JSON Schema is the actual configuration, UI Schema configures the entity lookup to allow for easy search for roles.
JSON Schema
{
"$schema": "http://json-schema.org/draft-07/schema",
"type": "object",
"title": "",
"properties": {
"timesheetAddNewButtonRoles": {
"$id": "#root/items/timesheetAddNewButtonRoles",
"title": "Security Roles that can see the New Timesheet button",
"description": "Note: Ensure all roles who require this access are added here. When no roles are provided, the default role/s configured will have access.",
"type": "array",
"default": [
"4c0f0c74-8b09-ea11-a811-000d3a530fe5"
],
"items": {
"$id": "#root/items/timesheetAddNewButtonRoles/items",
"title": "Security Role Id",
"description": "The role id as specified in Dynamics.",
"type": "string",
"default": "",
"pattern": "^.*$"
}
}
}
}
UI Schema
{
"timesheetAddNewButtonRoles": {
"ui:widget": "EntityLookupArrayWidget",
"ui:options": {
"endPoint": "roles",
"valueField": "_parentrootroleid_value",
"nameField": "name"
}
}
}
Example where path value is used
Ribbon Workbench settings
{
"InvertResult": "False(blank)",
"Configuration Setting Name": "simpleRoot",
"Path": "level/permissions",
"Label": "securityRoleIds",
"Key": "",
"Default roles": "4c0f0c74-8b09-ea11-a811-000d3a530fe5"
}
JSON Schema
{
"$schema": "http://json-schema.org/draft-06/schema#",
"title": "Entity Role Permissions",
"type": "object",
"properties": {
"level": {
"title": "Level",
"type": "object",
"properties": {
"permissions": {
"title": "Permissions",
"type": "object",
"properties": {
"securityRoleIds": {
"title": "Security Roles",
"type": "array",
"description": "If specified will only allow the people with the role(s).",
"items": {
"title": "Security Role",
"type": "string",
"format": "uuid"
}
},
"required": [
"securityRoleIds"
]
}
}
}
}
}
}
UI Schema
{
"level": {
"permissions": {
"securityRoleIds": {
"useArrayItemDialog": true,
"ui:widget": "EntityLookupArrayWidget",
"ui:options": {
"endPoint": "roles",
"valueField": "_parentrootroleid_value",
"nameField": "name"
}
}
}
}
}
In-app notifications
- 12892 - Implement in-app notifications (toasts) to replace existing Flows
- Deployed as part of version: SenseiProjectIndependent 2022.11.15.3
Description
In-app notifications within Altus notify users when items they own have been updated.
These notifications display for a period of time that is able to be configured for each user, then get stacked within the notifications section within the Dynamics site header bar.
The goal of including this item in Altus is to replace Flows, which aren’t solution friendly and aren’t particularly well integrated with the product. Currently the following flows are deployed with Altus:
Note
The proposal approval Flow still exists currently, until we investigate replacing Flow approvals with an internal approval solution.
Configuration settings
These are the notification events that have currently been implemented.
These toggles are applied at the system level, all are enabled by default.
Display Name: Notification Config
Name: SenseiNotificationConfig
Setting | Recipient | Link | Reoccurrence |
---|---|---|---|
Proposal - Submission | Proposal Sponsor | Flow approvals portal | NA |
Proposal - Approval | Proposal Submitter | Created project | NA |
Proposal - Rejection | Project Manager | Proposal | NA |
Resource Plan - Submission | Line Manager of requested resource | Resource Requests | NA |
Resource Plan - Approval | Requestor / Project Manager | Resource Requests | NA |
Resource Plan - Change | Line Manager of requested resource | Resource Requests | NA |
Timesheet - Submission | Timesheet Manager | Timesheet Approvals | NA |
Timesheet - Approval | Timesheet Submitter | Submitted Timesheet | NA |
Timesheet - Rejection | Timesheet Submitter | Submitted Timesheet | NA |
Timesheet - Reminder of unsubmitted timesheet for previous period | any system user with a created timesheet, regardless of "required to fill timesheet" setting. (if they have started filling one for any reason, it will remind them that it is unsubmitted) | My timesheet (for the unsubmitted period) | Daily (will only notify once on day after a finished period - can investigate different behaviours here in future iteration) |
Proposal - Reminder of proposals awaiting approval | Sponsor of submitted proposal | Flow Approvals Portal | Daily |
Resource Plan - Reminder of resource plans awaiting approval | Line manager of resource that has been requested | Resource Requests | Daily |
User configuration preferences
The user level notification preferences are available via the settings cog at bottom of the notification center.
The user can:
- Enable/Disable notifications
- Set how long (in seconds) the notifications will display on their screen
Creating notifications
With the feature now enabled within the Altus app and appropriate permissions applied to all Altus roles, consultants are able to create their own notifications via Flows, the instructions for how to do this are found here: Send in-app notifications within model-driven apps - Power Apps
The Proposal > Project Flow can be inspected as an example of Flow creation of the app notification records.
Please reach out to the Altus Product Development team if you have a requirement to create additional notifications as there are more on the backlog for us to create, that we could fast-track if required.
Custom User Lookup View to filter out system resources and inactive users from user drop downs
Released
SenseiProjectIndependent release 2022.10.25.1 and SenseiProject release 2022.10.12.2
- 14435:Out of the box Assigned To fields on the Risk entity will now filter with the following rules.
- Fullname DOES NOT START with "#"
- Azure AD Object ID CONTAINS DATA
- Application ID DOES NOT CONTAIN DATA
- Status EQUALS Active Additionally, we have incorporated and surfaced a function (sensei_SenseiProject.Generic.LookupViewSwitcher.onFormLoad) which allows users (with some configuration) the ability to setup lookup fields with dynamic filters.
Description
This item has both configured a user lookup that removes all System and Inactive users from the list and also deployed the ability to filter a lookup field on a form based upon another value on the form. (i.e., A lookup field depends on the value present in a field). The Filtered User Lookups have been rolled out throughout Altus.
Dynamic look-up filtering
The Dynamic Lookup Filtering capability allows, a consultant/admin user the ability to filter a lookup based upon information available on the current form.
Some example scenarios whereby it could be used.
- A lookup field (System User) needs to be filtered according to a text input.
- A lookup field (Project) needs to be filtered according to an optionset input.
To function allows this to be achieved by registering the sensei_SenseiProject.Generic.LookupViewSwitcher.onFormLoad function on load of the desired form and setting up the configuration settings.
Configuration settings
The configuration settings sensei_DynamicLookupFiltering will need to be setup for each field that needs to be dynamic.
- Display Name: LookUpFiltering
- Name: LookUpFiltering
For example, we have setup rules for dynamic lookup filter/s field on the sensei_risk entity here.
When we edit this configuration setting, we are presented with the specific entity and any lookups defined within.
For a given Dynamic Lookup the following needs to be filled out.
- Field Logical Name – the logical name of the field to be dynamically filtered.
- Lookup Entity – the entity of the lookup field
- FetchXML – the FetchXML to be used to perform the dynamic lookup.
- LayoutXML – the LayoutXML to be used to perform the dynamic lookup.
- Variables – the variable to be used for the dynamic lookup (referenced within he FetchXML)
- Field Logical Name – The logical name of the field to be used.
- FetchXML Variable – the variable to be substituted within the FetchXML
- Field Type – the type of field.
FetchXML
<fetch>
<entity name="systemuser">
<attribute name="fullname" />
<attribute name="businessunitid" />
<filter>
<condition attribute="fullname" operator="begins-with" value="${Variable}" />
</filter>
</entity>
</fetch>
LayoutXML
<grid name="resultset" object="1" jump="fullname" select="1" preview="1" icon="1">
<row name="result" id="systemuserid">
<cell name="fullname" width="150" />
<cell name="businessunitid" width="150" />
</row>
</grid>
So, for this example here, we are filtering the sensei_assignedto lookup field here by the sensei_description by looking at the fullname of the lookup field.
How to enable audit logging for D365
Audit logging considerations and risks
Setting up Audit logging in D365 allows users to see an audit history of items within Altus or within reports.
Before you turn on Audit logging for a client you will need to discuss with them the amount of space these audit logs can produce and make sure they have the capacity to save these logs.
Log storage is separated from Database and File storage, so make sure the Client has enough space or space remaining before you turn it on. For further details: Power Platform Capacity Storage
To view how much log capacity a client has go to the Resources > Capacity > Summary page in the Power Platform Admin Centre.
Periodic clean-up of this data may need to be undertaken if it is taking up too much space.
Steps to enable D635 audit logging
Modern UI
- Navigate to Power Platform Admin Centre
- Select Environments.
- Select the Environment you wish to enable logging on.
- Locate the Auditing section and click Manage.
- Enable Start Auditing and the other Auditing Options (Log Access, Read Logs) if so required and click Save.
- For the Entities/Tables you would like to Audit. Locate the solution you keep your customisations in via PowerApps.
- Enter the Table/Entity and select Properties.
- Expand Advanced options.
- Scroll down and select Audit changes to its data and Save.
- Publish all Customizations after all required Entities/Tables have been updated.
Audit logs will now start to be collected for the system. These can be seen against a specific record or in summary.
To view them in summary, navigate to the area specified in Step 5 and select Additional Audit Settings.
In this new page select the chevron next to Settings to pop down the ribbon and select Auditing.
To see the summary, select Audit Summary View.
Classic UI
- Navigate to Advanced Settings.
- Click Auditing from the menu.
- Navigate to Global Audit Settings.
- Enable all three audit settings and select OK.
- Select Entity and Field Audit Settings and choose the Entity that you would like to record audit logging details for.
- Enable both auditing options and select Publish.
- Once audit options have been enabled for entities that require auditing you can now close this window. To view audit logs, select Audit Summary View.
Logging info can now be seen for enabled entities.
Using Synapse to record audit data
If the client requires a large amount of audit data and reporting of this data, you might need to look at using Synapse to meet their requirements.
Pros and cons
Non-Azure Synapse solution
Pro | Con |
---|---|
Easier to set up. | Limited Audit Storage. (Dependent on licenses) |
Single solution. | Storage is expensive in Dataverse, may need to set up data expiry dates. |
The default audit tables may not be fit for purpose so a logic layer would be harder to implement. | |
Data is only accessible by other teams because it sits in a single Power BI File that connects to the source via an OData connector. No TDS SQL Connector available. | |
Does not meet Altus' strategic objectives and tactical objectives. |
Azure Synapse solution
Pro | Con |
---|---|
Storage is virtually unlimited. | Very Technical expertise required to setup |
Storage is cheaper allowing for the end of time storage. | May require more governance by the client/Partner. |
Allows the implementation of business rules. | |
Allows other teams across the client's organisation to access the audit data and create bespoke reporting as required. | |
Sets us the data up in a best practice manner to meet Altus' strategic objectives and tactical objectives. |