Is it possible to cancel a DCM transition from JS code? I believe it used to be possible in the classic UI, but I can't find any info on achieving it in Freedom UI. Trying to intercept the call with a handler for `crt.EntityStageProgressBarStageChangedHandlerRequest` allows "cancelling" it in a sense, as we can prevent the continuation of the handler chain by not performing the `return await next?.handle(request);` call, similar to how you would omit the `this.callParent(arguments)` when using classic UI, which does prevent the change from happening/further processing, but this leaves the selected stage highlighted instead of visually returning it back to the actually active stage. I'm currently using version 8.1.0

Like 0

Like

1 comments
Best reply

Hello!



In order to cancel a DCM transition you will have to add a handler for the system request crt.SaveRecordRequest, which is executed when you click the Save button on the record editing page.



In this example, we also added crt.HandleViewModelAttributeChangeRequest, which is executed whenever the value of an attribute changes, and an "IsStageChanged" attribute to control the Stage state. This way we check if a Stage attribute (LookupAttribute_ioghn6a) has been changed and prevent it from being saved. You can adjust this code according to your business needs:

 

...
	viewModelConfigDiff: /**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/[
			{
				"operation": "merge",
				"path": [
					"attributes"
				],
				"values": {
					...
					"IsStageChanged":{
						value: false
					}
				}
			},
 
		]/**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/,
 
		handlers: /**SCHEMA_HANDLERS*/[
			{
				request: "crt.SaveRecordRequest",
				handler: async (request, next) => {
 
					if(await request.$context.LookupAttribute_ioghn6a && await request.$context.IsStageChanged){
						request.$context._notifyService.show({message: 'Cannot set status'});
						return false;
					}
 
					const saveResult = await next.handle(request);
					if(saveResult){
						request.$context.IsStageChanged = false;
					}
 
					return saveResult;
				}
			}, 
			{
				request: "crt.HandleViewModelAttributeChangeRequest",
				handler: async (request, next) => {
 
					if (request.attributeName === 'LookupAttribute_ioghn6a' && request.value != request.oldValue && request.oldValue) {
						request.$context.IsStageChanged = true;
					}
					return next?.handle(request);
				},
			}
		]/**SCHEMA_HANDLERS*/,
...

 

Hello!



In order to cancel a DCM transition you will have to add a handler for the system request crt.SaveRecordRequest, which is executed when you click the Save button on the record editing page.



In this example, we also added crt.HandleViewModelAttributeChangeRequest, which is executed whenever the value of an attribute changes, and an "IsStageChanged" attribute to control the Stage state. This way we check if a Stage attribute (LookupAttribute_ioghn6a) has been changed and prevent it from being saved. You can adjust this code according to your business needs:

 

...
	viewModelConfigDiff: /**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/[
			{
				"operation": "merge",
				"path": [
					"attributes"
				],
				"values": {
					...
					"IsStageChanged":{
						value: false
					}
				}
			},
 
		]/**SCHEMA_VIEW_MODEL_CONFIG_DIFF*/,
 
		handlers: /**SCHEMA_HANDLERS*/[
			{
				request: "crt.SaveRecordRequest",
				handler: async (request, next) => {
 
					if(await request.$context.LookupAttribute_ioghn6a && await request.$context.IsStageChanged){
						request.$context._notifyService.show({message: 'Cannot set status'});
						return false;
					}
 
					const saveResult = await next.handle(request);
					if(saveResult){
						request.$context.IsStageChanged = false;
					}
 
					return saveResult;
				}
			}, 
			{
				request: "crt.HandleViewModelAttributeChangeRequest",
				handler: async (request, next) => {
 
					if (request.attributeName === 'LookupAttribute_ioghn6a' && request.value != request.oldValue && request.oldValue) {
						request.$context.IsStageChanged = true;
					}
					return next?.handle(request);
				},
			}
		]/**SCHEMA_HANDLERS*/,
...

 

Show all comments

Hi community,

 

I have this requirement where the client must be able to move one or more records from one detail to another through a business process. Following the steps shown in the image below:

 

1. The user must select one or more records from the "Product on Invoice" detail.

 

2. Next, the user executes the business process using the "Add" button. The process must receive the ids of the selected records.

 

3. Finally, all the selected records should then be added to the "Products in the Correction Invoice".

 

 

How can I achieve this requirement on the new Freedom UI?

 

If you need more information, feel free to ask.

 

Thanks in advance.

 

Best Regards,

Pedro Pinheiro

 

 

Like 2

Like

1 comments

Dear Pedro,

 

To execute this idea, you can do the following:

 

1) You can add the element that depends on user's choice in the "User's Action":

 

 

You can add an "Auto-generated/Pre-configured page" asking the user for an Id for example.

 

2) To achieve this, you can add the action on the button to start the business process in the page designer:

 

 

 

For the transfer, you can read the data from both objects, then use the modify data element.

 

You can read more about business process capabilities in the following article tree: https://academy.creatio.com/docs/8.x/no-code-customization/category/bus…

 

Have a great day!

Show all comments

We need to set the default Home Page for users in Creatio, ideally based on their functional role, but failing that a single default home page for all users would be acceptable. How can this be achieved? We're using Freedom UI and are on version 8.1.0 in our environments.

Like 1

Like

1 comments

Hello, 

 

Unfortunately, such functionality is not yet implemented. There is no oob tool to change the homepage for all the users or specific user roles. 



Such task can be implemented only with a help of SQL queries that will change the HomePageID value for the users in the SysAdminUnit system table. 

 

Best regards,

Anastasiia

Show all comments

Hi Creatio Community,



Can the elements on top of the portal page on Freedom UI be deleted for portal users  only? I only want to retain the logo and the logged in user icon.



Like 0

Like

2 comments

Hello Vishal,

To do this you'll need to override MainShell then you can either remove these elements (conditionally for portal users only) or you can add CSS that is global to the application (which I think is an easier route). You can make the global CSS apply only to portal users by using the details from this article https://customerfx.com/article/applying-global-css-styles-in-the-creati…

As for overriding the MainShell, you'll need to enable the AllowCreateAngularSchema feature (you can search these forums for "AllowCreateAngularSchema" to view details on how to do this)

Ryan

Thanks Ryan

Show all comments

Is it possible within Freedom UI to trigger a refresh of the Timeline/Feed/Next Steps components using JS? We seem to have an issue where the "Enable live data update" option does not seem to be working in Creatio CRM 8.1 for our Activities on certain pages, and while we've raised a support request for this, we were trying to find a workaround by manually updating the components from code for a specific use case, but we can't see what the data source is called for these types of components. Does anybody know what it would be? I presume refreshing the PDS would refresh them, but this seems heavy handed and might end up with data being lost on the main form page while the data actually being refreshed is just a child entity, so I would expect it to have its own data source.

Like 4

Like

4 comments

+1

Hello Harvey and Damien!

In Freedom UI Pages, the structure of the page is different and along with this a new way to refresh anything on the page. In this article you can find the way to achieve your goal:

https://customerfx.com/article/refreshing-reloading-page-or-list-data-on-a-creatio-freedom-ui-page/

Here's example of handler which you can use:

handlers: /**SCHEMA_HANDLERS*/[
 {
  request: "crt.HandleViewModelAttributeChangeRequest"/* The custom implementation of the system query handler. */
  handler: async (request, next) => {
   await next?.handle(request); 
   if (request.attributeName === 'Your Attribute Name' )
   {
    const handlerChain = sdk.HandlerChainService.instance;
    await handlerChain.process({
     type: 'crt.LoadDataRequest', $context: request.$context,
     config: { loadType: 'reload' },
    dataSourceName: 'Data Source Of Obj You Want To Refresh'
    });   
   }
  }
 },

You may also find this article useful:

https://customerfx.com/article/receiving-server-side-messages-in-a-creatio-freedom-ui-page/

 

Anhelina,

 

How can we find what the data source is for a component like a Timeline or Next Steps component though? The article suggests looking at the modelConfig elements in the page code, but none of these seem to relate to the timeline or timeline tiles. I expect there is some data source being created behind the scenes but finding out what it's called is the main problem for applying the theory of being able to reload anything on the page as is suggested.

 

Many thanks,

Harvey

Hello guys!
I have been having some kind of similar issue in my code where the next steps component does not reload. Could anyone please share the dataSourceName of the next steps component or how to find it.
Thank you!
Joan

Show all comments

Hi Creatio Community,

 

I've been trying to create custom Actions dropdown button (not FreedomUI) and I can't seem to be able to find working documentation for it or get something similar to it.  Would like your help for it.





 

Here are a couple of documentations I've tried out (First one is not working while the second one isn't exactly what we want):

 

1. https://community.creatio.com/questions/add-custom-buttons-actionbutton…



2. https://community.creatio.com/articles/how-create-and-popualte-drop-dow…

 

Thanks in advance.

 

Regards,

Abilash.S

Like 0

Like

3 comments
Best reply

Abilash,

 

Hi,

 

The task is to copy the logic of the existing "Actions" button and apply the same to the custom button. To do that (tested in the "Opportunity" section):

 

1) In the OpportunitySectionV2 section schema:

 

1.1) Add the CombinedModeCustomActionsButtonMenuItems attribute of the "Collection" dataValueType (Terrasoft.DataValueType.COLLECTION). The example is CombinedModeActionsButtonMenuItems from BaseDataView.

1.2) Create a PTP "GetCardActionsCustom" message with the SUBSCRIBE direction (as done on the BasePageV2)

1.3) In the subscribeSandboxEvents method subscribe to the "GetCardActionsCustom" message and trigger the initCustomActionButtonMenu method when the "GetCardActionsCustom" message is received (as done for the "GetCardActions" message on the BasePageV2)

1.4) Create an extension of the _initCollections method (as done for the basic "Actions" button)

1.5) Add an empty prepareCustomActionsButtonMenuItems function to methods (as done for prepareActionsButtonMenuItems method in BaseDataView)

1.6) Add the initCustomActionButtonMenu method which is a handler for the "GetCardActionsCustom" message (as done with the initActionButtonMenu method from BaseDataView).

1.7) Add the button itself to the diff array

 

The complete schema of OpportunitySectionV2:

define("OpportunitySectionV2", [], function() {
	return {
		entitySchemaName: "Opportunity",
		attributes: {
 
			"CombinedModeCustomActionsButtonMenuItems": {
				dataValueType: Terrasoft.DataValueType.COLLECTION
			}
		},
		messages: {
 
			"GetCardActionsCustom": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"name": "CustomActionsButton",
				"parentName": "CombinedModeActionButtonsCardLeftContainer",
				"propertyName": "items",
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"caption": {"bindTo": "Resources.Strings.CustomActionsButtonCaption"},
					"classes": {
						"textClass": ["actions-button-margin-right"],
						"wrapperClass": ["actions-button-margin-right"]
					},
					"prepareMenu": {"bindTo": "prepareCustomActionsButtonMenuItems"},
					"menu": {"items": {"bindTo": "CombinedModeCustomActionsButtonMenuItems"}}
				}
			},
		]/**SCHEMA_DIFF*/,
		methods: {
 
			subscribeSandboxEvents: function() {
				this.callParent(arguments);
				const cardModuleSandboxId = this.getCardModuleSandboxId();
				this.sandbox.subscribe("GetCardActionsCustom", function(actionMenuItems) {
					this.initCustomActionButtonMenu("Combined", actionMenuItems);
				}, this, [cardModuleSandboxId]);
			},
 
			prepareCustomActionsButtonMenuItems: Ext.emptyFn,
 
			_initCollections: function() {
				this.callParent(arguments);
				this.set("CombinedModeCustomActionsButtonMenuItems", this.Ext.create("Terrasoft.BaseViewModelCollection"));
			},
 
			initCustomActionButtonMenu: function(modeType, actionMenuItems) {
				const collectionName = modeType + "ModeCustomActionsButtonMenuItems";
				const collection = this.get(collectionName);
				if (actionMenuItems.getCount()) {
					this.set(modeType + "ModeActionsButtonVisible", true);
					const newCollection = this.Ext.create("Terrasoft.BaseViewModelCollection");
					actionMenuItems.each(function(item) {
						newCollection.addItem(item);
					}, this);
					if (collection) {
						collection.clear();
						collection.loadAll(newCollection);
					} else {
						this.set(collectionName, newCollection);
					}
				} else {
					this.set(modeType + "ModeActionsButtonVisible", false);
				}
			},
		}
	};
});

2) In OpportunityPageV2:

 

2.1) Create the "GetCardActionsCustom" message (as done for the "GetCardActions" existing message)

2.2) In the init method call the initCustomActionButtonMenu method

2.3) Create the initCustomActionButtonMenu method (as done for the initActionButtonMenu in BasePageV2)

2.4) Create the getCustomActions method (as done with the getActions method in the basic OpportunityPageV2 with only difference that you don't need to call parent method but create an instance of the Terrasoft.BaseViewModelCollection here)

2.5) Add the button itself to the diff array

 

The complete code is:

define("OpportunityPageV2", [], function() {
	return {
		entitySchemaName: "Opportunity",
		attributes: {},
		messages: {
			"GetCardActionsCustom": {
				mode: this.Terrasoft.MessageMode.PTP,
				direction: this.Terrasoft.MessageDirectionType.PUBLISH
			}
		},
		modules: /**SCHEMA_MODULES*/{}/**SCHEMA_MODULES*/,
		details: /**SCHEMA_DETAILS*/{
			"UsrSchemaf6483d73Detaile560e0a4": {
				"schemaName": "UsrSchemaf6483d73Detail",
				"entitySchemaName": "UsrOppTestDet",
				"filter": {
					"detailColumn": "UsrOpportunity",
					"masterColumn": "Id"
				}
			}
		}/**SCHEMA_DETAILS*/,
		businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/,
		methods: {
 
			init: function() {
				this.callParent(arguments);
				this.initCustomActionButtonMenu();
			},
 
			initCustomActionButtonMenu: function() {
				this.publishPropertyValueToSection("IsCardInEditMode", this.isEditMode());
				var actionMenuItems = this.getCustomActions();
				var actionsButtonVisible = !actionMenuItems.isEmpty();
				this.set("CustomActionsButtonVisible", actionsButtonVisible);
				this.set("CustomActionsButtonMenuItems", actionMenuItems);
				this.sandbox.publish("GetCardActionsCustom", actionMenuItems, [this.sandbox.id]);
			},
 
			getCustomActions: function() {
				var actionMenuItems = this.Ext.create("Terrasoft.BaseViewModelCollection");
				actionMenuItems.addItem(this.getButtonMenuItem({
					"Caption": {"bindTo": "Resources.Strings.CustomButtonCaption"},
					"Tag": "clickCustomButton"
				}));
				return actionMenuItems;
			}
		},
		dataModels: /**SCHEMA_DATA_MODELS*/{}/**SCHEMA_DATA_MODELS*/,
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"parentName": "LeftContainer",
				"propertyName": "items",
				"name": "actions",
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"caption": {"bindTo": "Resources.Strings.CustomButtonCaption"},
					"classes": {
						"textClass": ["actions-button-margin-right"],
						"wrapperClass": ["actions-button-margin-right"]
					},
					"menu": {
						"items": {"bindTo": "CustomActionsButtonMenuItems"}
					},
					"visible": {"bindTo": "CustomActionsButtonVisible"}
				}
			},
			{
				"operation": "merge",
				"name": "MetricsContainer",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityClient",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 1
					}
				}
			},
			{
				"operation": "remove",
				"name": "OpportunityClient",
				"properties": [
					"tip"
				]
			},
			{
				"operation": "merge",
				"name": "LablelMetricsContainer",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "BantProfileHeaderContainer",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "BantIcon",
				"values": {
					"layout": {
						"colSpan": 5,
						"rowSpan": 1,
						"column": 0,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "BantHeaderCaption",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 5,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityBudget",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 1
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityDecisionMaker",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityLeadType",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 3
					}
				}
			},
			{
				"operation": "move",
				"name": "OpportunityLeadType",
				"parentName": "BantProfile",
				"propertyName": "items",
				"index": 3
			},
			{
				"operation": "merge",
				"name": "OpportunityDueDate",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 4
					}
				}
			},
			{
				"operation": "merge",
				"name": "ESNTab",
				"values": {
					"order": 8
				}
			},
			{
				"operation": "merge",
				"name": "GeneralInfoTab",
				"values": {
					"order": 0
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityTitle",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "Amount",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 3
					}
				}
			},
			{
				"operation": "merge",
				"name": "ResponsibleDepartment",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 3
					}
				}
			},
			{
				"operation": "move",
				"name": "ResponsibleDepartment",
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 3
			},
			{
				"operation": "merge",
				"name": "Probability",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 4
					}
				}
			},
			{
				"operation": "merge",
				"name": "Owner",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 4
					}
				}
			},
			{
				"operation": "merge",
				"name": "Category",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 5
					}
				}
			},
			{
				"operation": "merge",
				"name": "Source",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 5
					}
				}
			},
			{
				"operation": "merge",
				"name": "Type",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 6
					}
				}
			},
			{
				"operation": "move",
				"name": "Type",
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 8
			},
			{
				"operation": "merge",
				"name": "CreatedOn",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 6
					}
				}
			},
			{
				"operation": "merge",
				"name": "Partner",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 7
					}
				}
			},
			{
				"operation": "merge",
				"name": "CloseReason",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 7
					}
				}
			},
			{
				"operation": "insert",
				"name": "DATETIME26d46360-17b4-45ee-acac-da084cf0aa67",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 8,
						"layoutName": "OpportunityPageGeneralBlock"
					},
					"bindTo": "UsrTestDateTime",
					"enabled": true
				},
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 12
			},
			{
				"operation": "merge",
				"name": "Description",
				"values": {
					"layout": {
						"colSpan": 23,
						"rowSpan": 1,
						"column": 1,
						"row": 0
					}
				}
			},
			{
				"operation": "insert",
				"name": "UsrSchemaf6483d73Detaile560e0a4",
				"values": {
					"itemType": 2,
					"markerValue": "added-detail"
				},
				"parentName": "GeneralInfoTab",
				"propertyName": "items",
				"index": 5
			},
			{
				"operation": "merge",
				"name": "LeadTab",
				"values": {
					"order": 4
				}
			},
			{
				"operation": "merge",
				"name": "TacticAndCompetitorTab",
				"values": {
					"order": 1
				}
			},
			{
				"operation": "merge",
				"name": "CheckDate",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "ProductsTab",
				"values": {
					"order": 2
				}
			},
			{
				"operation": "merge",
				"name": "HistoryTab",
				"values": {
					"order": 5
				}
			},
			{
				"operation": "merge",
				"name": "HistoryAccountTab",
				"values": {
					"order": 6
				}
			},
			{
				"operation": "merge",
				"name": "NotesTab",
				"values": {
					"order": 7
				}
			},
			{
				"operation": "move",
				"name": "IsPrimary",
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 0
			}
		]/**SCHEMA_DIFF*/
	};
});

As a result when opening the page you will get a custom button with items:

Also forgot to metion that you need to add localizable strings:

 

1) For OpportunityPageV2

 

CustomButtonCaption with "Custom action" value

 

2) For OpportunitySectionV2

 

CustomButtonCaption with "Custom button" value

 

Hope this example help!

 

 

Hello,



Could you please write in more detail, regarding the first option that suits you, what exactly are the difficulties you encountered? 

Malika,

Hello, 



Currently, I'm just getting a button with no dropdown like this







Here's the code for it:





//I've tried two different approaches for the method (both didn't work) based on the above two documentation

methods: {





//approach1



prepareMyActionsButtonMenuItems: function(filter, list) {

                if (list === null) {

                    return;

                }

                list.clear();

                var columns = {};

 

                var value1 = {

                    displayValue: "a123",

                    value: "1"

                };

                var value2 = {

                    displayValue: "b234",

                    value: "2"

                };

                var value3 = {

                    displayValue: "c345",

                    value: "3"

                };

 

                columns[1] = value1;

                columns[2] = value2;

                columns[3] = value3;

 

                list.loadAll(columns);

            },



//approach 2



prepareMyActionsButtonMenuItems: function(){

                this.set("MyActionsButtonMenuItems", ["Sample", "test", "Test5"]);

            }



}





attributes: {



              "MyActionsButtonMenuItems": {

                dataValueType: Terrasoft.DataValueType.COLLECTION,

              }

}



diff:[

{

                "operation": "insert",

                "name": "MyActionsButton",

                "parentName": "RightContainer",

                "propertyName": "items",

                "values": {

                      "itemType": Terrasoft.ViewItemType.BUTTON,

                      "caption": "MyActionsButton",

                      "classes": {

                        "textClass": ["actions-button-margin-right"],

                        "wrapperClass": ["actions-button-margin-right"]

                      },

                      "prepareMenu": {"bindTo": "prepareMyActionsButtonMenuItems"},

                      "menu": {"items": {"bindTo": "MyActionsButtonMenuItems"}},

                      "visible": true

                }

              }

]

Abilash,

 

Hi,

 

The task is to copy the logic of the existing "Actions" button and apply the same to the custom button. To do that (tested in the "Opportunity" section):

 

1) In the OpportunitySectionV2 section schema:

 

1.1) Add the CombinedModeCustomActionsButtonMenuItems attribute of the "Collection" dataValueType (Terrasoft.DataValueType.COLLECTION). The example is CombinedModeActionsButtonMenuItems from BaseDataView.

1.2) Create a PTP "GetCardActionsCustom" message with the SUBSCRIBE direction (as done on the BasePageV2)

1.3) In the subscribeSandboxEvents method subscribe to the "GetCardActionsCustom" message and trigger the initCustomActionButtonMenu method when the "GetCardActionsCustom" message is received (as done for the "GetCardActions" message on the BasePageV2)

1.4) Create an extension of the _initCollections method (as done for the basic "Actions" button)

1.5) Add an empty prepareCustomActionsButtonMenuItems function to methods (as done for prepareActionsButtonMenuItems method in BaseDataView)

1.6) Add the initCustomActionButtonMenu method which is a handler for the "GetCardActionsCustom" message (as done with the initActionButtonMenu method from BaseDataView).

1.7) Add the button itself to the diff array

 

The complete schema of OpportunitySectionV2:

define("OpportunitySectionV2", [], function() {
	return {
		entitySchemaName: "Opportunity",
		attributes: {
 
			"CombinedModeCustomActionsButtonMenuItems": {
				dataValueType: Terrasoft.DataValueType.COLLECTION
			}
		},
		messages: {
 
			"GetCardActionsCustom": {
				mode: Terrasoft.MessageMode.PTP,
				direction: Terrasoft.MessageDirectionType.SUBSCRIBE
			}
		},
		details: /**SCHEMA_DETAILS*/{}/**SCHEMA_DETAILS*/,
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"name": "CustomActionsButton",
				"parentName": "CombinedModeActionButtonsCardLeftContainer",
				"propertyName": "items",
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"caption": {"bindTo": "Resources.Strings.CustomActionsButtonCaption"},
					"classes": {
						"textClass": ["actions-button-margin-right"],
						"wrapperClass": ["actions-button-margin-right"]
					},
					"prepareMenu": {"bindTo": "prepareCustomActionsButtonMenuItems"},
					"menu": {"items": {"bindTo": "CombinedModeCustomActionsButtonMenuItems"}}
				}
			},
		]/**SCHEMA_DIFF*/,
		methods: {
 
			subscribeSandboxEvents: function() {
				this.callParent(arguments);
				const cardModuleSandboxId = this.getCardModuleSandboxId();
				this.sandbox.subscribe("GetCardActionsCustom", function(actionMenuItems) {
					this.initCustomActionButtonMenu("Combined", actionMenuItems);
				}, this, [cardModuleSandboxId]);
			},
 
			prepareCustomActionsButtonMenuItems: Ext.emptyFn,
 
			_initCollections: function() {
				this.callParent(arguments);
				this.set("CombinedModeCustomActionsButtonMenuItems", this.Ext.create("Terrasoft.BaseViewModelCollection"));
			},
 
			initCustomActionButtonMenu: function(modeType, actionMenuItems) {
				const collectionName = modeType + "ModeCustomActionsButtonMenuItems";
				const collection = this.get(collectionName);
				if (actionMenuItems.getCount()) {
					this.set(modeType + "ModeActionsButtonVisible", true);
					const newCollection = this.Ext.create("Terrasoft.BaseViewModelCollection");
					actionMenuItems.each(function(item) {
						newCollection.addItem(item);
					}, this);
					if (collection) {
						collection.clear();
						collection.loadAll(newCollection);
					} else {
						this.set(collectionName, newCollection);
					}
				} else {
					this.set(modeType + "ModeActionsButtonVisible", false);
				}
			},
		}
	};
});

2) In OpportunityPageV2:

 

2.1) Create the "GetCardActionsCustom" message (as done for the "GetCardActions" existing message)

2.2) In the init method call the initCustomActionButtonMenu method

2.3) Create the initCustomActionButtonMenu method (as done for the initActionButtonMenu in BasePageV2)

2.4) Create the getCustomActions method (as done with the getActions method in the basic OpportunityPageV2 with only difference that you don't need to call parent method but create an instance of the Terrasoft.BaseViewModelCollection here)

2.5) Add the button itself to the diff array

 

The complete code is:

define("OpportunityPageV2", [], function() {
	return {
		entitySchemaName: "Opportunity",
		attributes: {},
		messages: {
			"GetCardActionsCustom": {
				mode: this.Terrasoft.MessageMode.PTP,
				direction: this.Terrasoft.MessageDirectionType.PUBLISH
			}
		},
		modules: /**SCHEMA_MODULES*/{}/**SCHEMA_MODULES*/,
		details: /**SCHEMA_DETAILS*/{
			"UsrSchemaf6483d73Detaile560e0a4": {
				"schemaName": "UsrSchemaf6483d73Detail",
				"entitySchemaName": "UsrOppTestDet",
				"filter": {
					"detailColumn": "UsrOpportunity",
					"masterColumn": "Id"
				}
			}
		}/**SCHEMA_DETAILS*/,
		businessRules: /**SCHEMA_BUSINESS_RULES*/{}/**SCHEMA_BUSINESS_RULES*/,
		methods: {
 
			init: function() {
				this.callParent(arguments);
				this.initCustomActionButtonMenu();
			},
 
			initCustomActionButtonMenu: function() {
				this.publishPropertyValueToSection("IsCardInEditMode", this.isEditMode());
				var actionMenuItems = this.getCustomActions();
				var actionsButtonVisible = !actionMenuItems.isEmpty();
				this.set("CustomActionsButtonVisible", actionsButtonVisible);
				this.set("CustomActionsButtonMenuItems", actionMenuItems);
				this.sandbox.publish("GetCardActionsCustom", actionMenuItems, [this.sandbox.id]);
			},
 
			getCustomActions: function() {
				var actionMenuItems = this.Ext.create("Terrasoft.BaseViewModelCollection");
				actionMenuItems.addItem(this.getButtonMenuItem({
					"Caption": {"bindTo": "Resources.Strings.CustomButtonCaption"},
					"Tag": "clickCustomButton"
				}));
				return actionMenuItems;
			}
		},
		dataModels: /**SCHEMA_DATA_MODELS*/{}/**SCHEMA_DATA_MODELS*/,
		diff: /**SCHEMA_DIFF*/[
			{
				"operation": "insert",
				"parentName": "LeftContainer",
				"propertyName": "items",
				"name": "actions",
				"values": {
					"itemType": Terrasoft.ViewItemType.BUTTON,
					"caption": {"bindTo": "Resources.Strings.CustomButtonCaption"},
					"classes": {
						"textClass": ["actions-button-margin-right"],
						"wrapperClass": ["actions-button-margin-right"]
					},
					"menu": {
						"items": {"bindTo": "CustomActionsButtonMenuItems"}
					},
					"visible": {"bindTo": "CustomActionsButtonVisible"}
				}
			},
			{
				"operation": "merge",
				"name": "MetricsContainer",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityClient",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 1
					}
				}
			},
			{
				"operation": "remove",
				"name": "OpportunityClient",
				"properties": [
					"tip"
				]
			},
			{
				"operation": "merge",
				"name": "LablelMetricsContainer",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "BantProfileHeaderContainer",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "BantIcon",
				"values": {
					"layout": {
						"colSpan": 5,
						"rowSpan": 1,
						"column": 0,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "BantHeaderCaption",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 5,
						"row": 0
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityBudget",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 1
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityDecisionMaker",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityLeadType",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 3
					}
				}
			},
			{
				"operation": "move",
				"name": "OpportunityLeadType",
				"parentName": "BantProfile",
				"propertyName": "items",
				"index": 3
			},
			{
				"operation": "merge",
				"name": "OpportunityDueDate",
				"values": {
					"layout": {
						"colSpan": 19,
						"rowSpan": 1,
						"column": 5,
						"row": 4
					}
				}
			},
			{
				"operation": "merge",
				"name": "ESNTab",
				"values": {
					"order": 8
				}
			},
			{
				"operation": "merge",
				"name": "GeneralInfoTab",
				"values": {
					"order": 0
				}
			},
			{
				"operation": "merge",
				"name": "OpportunityTitle",
				"values": {
					"layout": {
						"colSpan": 24,
						"rowSpan": 1,
						"column": 0,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "Amount",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 3
					}
				}
			},
			{
				"operation": "merge",
				"name": "ResponsibleDepartment",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 3
					}
				}
			},
			{
				"operation": "move",
				"name": "ResponsibleDepartment",
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 3
			},
			{
				"operation": "merge",
				"name": "Probability",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 4
					}
				}
			},
			{
				"operation": "merge",
				"name": "Owner",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 4
					}
				}
			},
			{
				"operation": "merge",
				"name": "Category",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 5
					}
				}
			},
			{
				"operation": "merge",
				"name": "Source",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 5
					}
				}
			},
			{
				"operation": "merge",
				"name": "Type",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 6
					}
				}
			},
			{
				"operation": "move",
				"name": "Type",
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 8
			},
			{
				"operation": "merge",
				"name": "CreatedOn",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 6
					}
				}
			},
			{
				"operation": "merge",
				"name": "Partner",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 7
					}
				}
			},
			{
				"operation": "merge",
				"name": "CloseReason",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 7
					}
				}
			},
			{
				"operation": "insert",
				"name": "DATETIME26d46360-17b4-45ee-acac-da084cf0aa67",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 0,
						"row": 8,
						"layoutName": "OpportunityPageGeneralBlock"
					},
					"bindTo": "UsrTestDateTime",
					"enabled": true
				},
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 12
			},
			{
				"operation": "merge",
				"name": "Description",
				"values": {
					"layout": {
						"colSpan": 23,
						"rowSpan": 1,
						"column": 1,
						"row": 0
					}
				}
			},
			{
				"operation": "insert",
				"name": "UsrSchemaf6483d73Detaile560e0a4",
				"values": {
					"itemType": 2,
					"markerValue": "added-detail"
				},
				"parentName": "GeneralInfoTab",
				"propertyName": "items",
				"index": 5
			},
			{
				"operation": "merge",
				"name": "LeadTab",
				"values": {
					"order": 4
				}
			},
			{
				"operation": "merge",
				"name": "TacticAndCompetitorTab",
				"values": {
					"order": 1
				}
			},
			{
				"operation": "merge",
				"name": "CheckDate",
				"values": {
					"layout": {
						"colSpan": 12,
						"rowSpan": 1,
						"column": 12,
						"row": 2
					}
				}
			},
			{
				"operation": "merge",
				"name": "ProductsTab",
				"values": {
					"order": 2
				}
			},
			{
				"operation": "merge",
				"name": "HistoryTab",
				"values": {
					"order": 5
				}
			},
			{
				"operation": "merge",
				"name": "HistoryAccountTab",
				"values": {
					"order": 6
				}
			},
			{
				"operation": "merge",
				"name": "NotesTab",
				"values": {
					"order": 7
				}
			},
			{
				"operation": "move",
				"name": "IsPrimary",
				"parentName": "OpportunityPageGeneralBlock",
				"propertyName": "items",
				"index": 0
			}
		]/**SCHEMA_DIFF*/
	};
});

As a result when opening the page you will get a custom button with items:

Also forgot to metion that you need to add localizable strings:

 

1) For OpportunityPageV2

 

CustomButtonCaption with "Custom action" value

 

2) For OpportunitySectionV2

 

CustomButtonCaption with "Custom button" value

 

Hope this example help!

 

 

Show all comments

How can we apply custom filters on Lookups in Freedom UI? I can see that there is some existence of a filtersConfig potential property for the object passed as the first parameter to executeRequest, but cannot find any examples or information on how this should be configured in Academy or on other Community questions. We are trying to use this to associate records to a parent record using the + button on a Freedom UI "detail" list, but we don't want any records already associated with any parent by foreign key to be eligible for association using the lookup, and also have some other conditions to apply based on the child entity. Our environment is currently on 8.1 Quantum.

 

Here is the relevant excerpt from the handler on our form page, without any filters applied and with the non-relevant logic applied in the afterClosed area removed for brevity:

request.$context.executeRequest({
	type: "crt.OpenLookupPageRequest",
	$context: request.$context,
	entitySchemaName: "Order",
	caption: "Select Quotes to associate with the Lead",
	features: {
		select: {
			multiple: true,
			selectAll: false,
			resultType: 'lookupValues'
		},
		create: {
			enabled: false
		}
	},
	afterClosed: async function(selectedItems) {
		// logic here
		return next?.handle(request)
	}
});

 

Like 0

Like

5 comments
Best reply

Hello,

Here is an example of OpenLookupPageRequest with a filtersConfig.

handlers: /**SCHEMA_HANDLERS*/[
			{
				request: "usr.OpenLookupRequest",
				handler: async (request, next) => {
					devkit.HandlerChainService.instance.process({
						type: "crt.OpenLookupPageRequest",
						scopes: [...request.scopes],
						$context: request.$context,
						entitySchemaName: "Contact",
						// caption: 'Responsible',
						schemaName: 'CustomLookupPage',
						itemAttributeName: 'LookupAttribute_2mnilrq',
						// itemsAttributeName: 'Contact_List',
						afterClosed: (result) => {
							alert(result?.displayValue ?? '');
						},
						filtersConfig: {
							filterAttributes: [
								{
									name: 'MyFilter',
									loadOnChange: false
								}
							],
							attributesConfig: {
								MyFilter: {
									value: {
										"items": {
											"29e16d42-36f1-4e04-9029-4321cbb2494d": {
												"filterType": 1,
												"comparisonType": 11,
												"isEnabled": true,
												"trimDateTimeParameterToDate": false,
												"leftExpression": {
													"expressionType": 0,
													"columnPath": "Name"
												},
												"isAggregative": false,
												"dataValueType": 1,
												"rightExpression": {
													"expressionType": 2,
													"parameter": {
														"dataValueType": 1,
														"value": "Super"
													}
												}
											}
										},
										"logicalOperation": 0,
										"isEnabled": true,
										"filterType": 6,
										"rootSchemaName": "Contact"
									}
								}
							}
						}
					});
					return next?.handle(request);
				}
			}
		]/**SCHEMA_HANDLERS*/,

If you don't know how to build a proper filter here is a small tip. On a Freedom UI page add a list with the object you want to filter. In this list add a static filter with a condition you want to apply to a OpenLookupPageRequest and save it. As a result, in the code of this page, you can find the full filter code, all you need to do is to replace "MyFilter" with it.

Hello,

Here is an example of OpenLookupPageRequest with a filtersConfig.

handlers: /**SCHEMA_HANDLERS*/[
			{
				request: "usr.OpenLookupRequest",
				handler: async (request, next) => {
					devkit.HandlerChainService.instance.process({
						type: "crt.OpenLookupPageRequest",
						scopes: [...request.scopes],
						$context: request.$context,
						entitySchemaName: "Contact",
						// caption: 'Responsible',
						schemaName: 'CustomLookupPage',
						itemAttributeName: 'LookupAttribute_2mnilrq',
						// itemsAttributeName: 'Contact_List',
						afterClosed: (result) => {
							alert(result?.displayValue ?? '');
						},
						filtersConfig: {
							filterAttributes: [
								{
									name: 'MyFilter',
									loadOnChange: false
								}
							],
							attributesConfig: {
								MyFilter: {
									value: {
										"items": {
											"29e16d42-36f1-4e04-9029-4321cbb2494d": {
												"filterType": 1,
												"comparisonType": 11,
												"isEnabled": true,
												"trimDateTimeParameterToDate": false,
												"leftExpression": {
													"expressionType": 0,
													"columnPath": "Name"
												},
												"isAggregative": false,
												"dataValueType": 1,
												"rightExpression": {
													"expressionType": 2,
													"parameter": {
														"dataValueType": 1,
														"value": "Super"
													}
												}
											}
										},
										"logicalOperation": 0,
										"isEnabled": true,
										"filterType": 6,
										"rootSchemaName": "Contact"
									}
								}
							}
						}
					});
					return next?.handle(request);
				}
			}
		]/**SCHEMA_HANDLERS*/,

If you don't know how to build a proper filter here is a small tip. On a Freedom UI page add a list with the object you want to filter. In this list add a static filter with a condition you want to apply to a OpenLookupPageRequest and save it. As a result, in the code of this page, you can find the full filter code, all you need to do is to replace "MyFilter" with it.

Question on OpenLookupPageRequest. In an edit scenario, could you pass the already selected options so that they appear already selected in the lookup list?

You can use the option selectionState to predefine selected rows, which will be pre-selected

type: "crt.OpenLookupPageRequest",
	...
	selectionState: {
		type: 'specific',
		selected: [
			'9d06bf9f-eb7a-4849-b83d-cbba994f185d',
			'49ba9a9e-2e28-48cb-b1bc-81b7871acb9d',
		],
	},

 

Dmytro Vovchenko,

 

Those are really useful, are there any other parameters that can be passed to the newly opened lookup page? I'm currently looking for a way to automatically set the initial search value of the lookup window, but I'm sure there are many other undocumented useful parameters like the selectionState that can be passed to a lookup page when opening it. A generic way to pass data into the page would be great!

 

I looked into passing a defaultSearchValue parameter into the request, but it didn't seem to affect the modal lookup.

Harvey Adcock,

 

Hello,

 

Here is all the available documentation we have:

 

Lookup window example:

In order to open the lookup window, you have to use crt.OpenSelectionWindowRequest

/**
 * @publicApi
 */
@CrtRequest({
    type: 'crt.OpenSelectionWindowRequest',
})
export class OpenSelectionWindowRequest extends BaseRequest {
    public itemAttributeName?: string;
    public itemsAttributeName?: string;
    /**
     * @publicApi
     */
    public entitySchemaName?: string;
    /**
     * @publicApi
     */
    public schemaName?: string;
    /**
     * @publicApi
     */
    public filtersConfig?: FiltersConfig;
    /**
     * @publicApi
     */
    public features?: PageLookupFeatures;
    /**
     * @publicApi
     */
    public selectionState?: SelectionState;
    /**
     * @publicApi
     */
    public afterClosed?: (result: SelectionWindowResult) => void;
    /**
     * @publicApi
     */
    public caption?: LocalizableString;
}

Add the following code to your custom handler:

handlers: /**SCHEMA_HANDLERS*/[
			{
				request: "usr.OpenLookupRequest",
				handler: async (request, next) => {
					devkit.HandlerChainService.instance.process({
						type: "crt.OpenSelectionWindowRequest",
						scopes: [...request.scopes],
						$context: request.$context,
						entitySchemaName: "Contact",
						// caption: 'Responsible',
						schemaName: 'CustomLookupPage',
						itemAttributeName: 'LookupAttribute_2mnilrq',
						// itemsAttributeName: 'Contact_List',
						afterClosed: async (result) => {
							if (!result.canceled) {
								const lookupValues = await result.getLookupValues();
								const value = lookupValues[0];
								if (value) {
									alert(value?.displayValue ?? '');
								}
							}
						},
						filtersConfig: {
							filterAttributes: [
								{
									name: 'MyFilter',
									loadOnChange: false
								}
							],
							attributesConfig: {
								MyFilter: {
									value: {
										"items": {
											"29e16d42-36f1-4e04-9029-4321cbb2494d": {
												"filterType": 1,
												"comparisonType": 11,
												"isEnabled": true,
												"trimDateTimeParameterToDate": false,
												"leftExpression": {
													"expressionType": 0,
													"columnPath": "Name"
												},
												"isAggregative": false,
												"dataValueType": 1,
												"rightExpression": {
													"expressionType": 2,
													"parameter": {
														"dataValueType": 1,
														"value": "Super"
													}
												}
											}
										},
										"logicalOperation": 0,
										"isEnabled": true,
										"filterType": 6,
										"rootSchemaName": "Contact"
									}
								}
							}
						}
					});
					return next?.handle(request);
				}
			}
		]/**SCHEMA_HANDLERS*/,

where

 

entitySchemaName - the name of the entity schema whose data is displayed in the lookup window. Optional parameter. If not specified, the data source is taken from the attribute specified in itemAttributeName

 

caption - optional parameter, displayed in the window title. If caption is not specified, the caption is taken from the attribute specified in the itemAttributeName parameter. If itemAttributeName is not specified, then from the entity schema.

 

schemaName - the name of the schema that is displayed in the lookup window. Optional parameter. Default is BaseLookupPageTemplate

 

itemAttributeName - the name of the attribute from which the title is taken, the name of the entity schema and in which the result of the selection in the window will be written. Optional parameter. As a rule, this is the attribute with which the Combobox control is associated.

 

itemsAttributeName - the name of the attribute from which the data source name is taken. Optional parameter.

 

afterClosed - callback function returning the result of the selection in the window. Optional parameter.

 

filtersConfig-  describes the filter to be applied to the data. Optional parameter

 

PageLookupFeatures

 

Additionally, the user can specify additional features for the selection window.

export interface PageLookupFeatures {
    create?: {
        enabled: boolean;                         // false by default
    };
    select: {
        multiple: boolean;                        // false by default
        selectAll: boolean;                       // false by default
    };
    showDeactivatedRecords?: boolean;
}

 

The user should provide the Create option as enabled to display the New button in the selection window (only for the FreedomUI host).

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	"features": {
		"create": {
            "enabled": true;
        }
	}
});

 

The New button in this window works best only in the Freedom UI shell. In the Classic UI, new record will be created, but the selection window will close after saving it without passing the created record selected value. If you want to use this window in the Classic UI, please consider it, or don’t show this button there.

 

SelectionWindowResult  type

export class SelectionWindowResult {
    canceled: boolean; // indicates whether selection window was canceled by user
    filter: FilterMetadata; // Filter 
    async getLookupValues(options?: { pagingConfig: DataSourcePagingConfig }): Promise<LookupValue[]>; // method to get lookup values
}

where DataSourcePagingConfig is 

interface DataSourcePagingConfig {
    rowsOffset: number;
    rowCount: number;
}

example

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	"features": {
		"select": {
            "multiple": true;
        }
	},
	afterClosed: async (selectionWindowResult) => {
		const filter = selectionWindowResult.filter // getting filter
		// or 
		const lookupValues = await selectionWindowResult.getLookupValues(); // getting lookupValues
	}
});

 

Multiselection mode

 

To enable multiselection mode user should add next feature to OpenPageLookupRequest

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	features: {
		select: {
            "multiple": true
        }
	}
});

So afterClosed will look like this 

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	features: {
		select: {
			multiple: true
		}
	},
	afterClosed: async (selectionWindowResult) => {
		const lookupValues = await selectionWindowResult.getLookupValues();
		// do something with lookup values
	}
});

 

Select all


To enable the ability to select all records, you should add the next features to OpenPageLookupRequest.

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	features: {
		select: {
            multiple: true,
            selectAll: true
        }
	}
});

 

Canceled
 

Handling cases when the user presses the cancels Selection window

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	features: {
		select: {
			multiple: true
		}
	},
	afterClosed: (selectionWindowResult) => {
		const canceled = selectionWindowResult.canceled;
		if (canceled) {
			// do some logic when user canceled selection window
		}
	}
});

 

Selecting a special set of values


getLookupValues could be called with paging config where user can specify rowsOffset and rowCount to get a specific list of values

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	features: {
		select: {
			multiple: true
		}
	},
	afterClosed: async (selectionWindowResult) => {
		const pagingConfig = {
			rowsOffset: 2,
			rowCount: 2
		}
		const lookupValues = await selectionWindowResult.getLookupValues({ pagingConfig });
		// do something with received lookup values
	}
});

Example of iterating through lookupValues. Getting pair of 2 lookupValues with step of 2

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	scopes: [...request.scopes],
	$context: request.$context,
	....
	features: {
		select: {
			multiple: true
		}
	},
	afterClosed: async (selectionWindowResult) => {
		let rowsOffset = 0;
        const rowCount = 2;
        const pagingConfig = { rowsOffset, rowCount };
        let lookupValues = await selectionWindowResult.getLookupValues({ pagingConfig });
        while (lookupValues.length) {
            /*
              fn(lookupValues); // do something with received lookupValues pair
            */
            rowsOffset += 2;
            lookupValues = await selectionWindowResult.getLookupValues({ pagingConfig: { rowsOffset, rowCount }});
        }
        if (!lookupValues.length) {
            /*
              Do some logic here on end of list - when there are no more lookupValues
            */
        }
	}
});

 

Selection state


and SelectionState is:

{
    type: 'specific';
    selected: unknown[]; // array of id's
}

 

So user can predefine selected rows, which will be pre-selected

devkit.HandlerChainService.instance.process({
	type: "crt.OpenSelectionWindowRequest",
	...
	selectionState: {
		type: 'specific',
		selected: [
			'9d06bf9f-eb7a-4849-b83d-cbba994f185d',
			'49ba9a9e-2e28-48cb-b1bc-81b7871acb9d',
		],
	},
});

 

showDeactivatedRecords


When set to true, deactivated entries of the object are shown.

 

This is all the information that we have on the lookup page handling cases, I hope it helps.

Show all comments

Hi,

I am able to create the feed entry, but what I would like to include is the @user function. Simply using the Formula in the message and constructing the text in the following way does not work.

"Hey @" + [#variable for contact] 

 

Does anyone know a way to make this active notification work in a business process to the Feed message?

Like 0

Like

3 comments

anyone?

 

I really want to be able to @someone in a feed message created in a business process and I am sure others would love to be able to do this too.

Mark Roberts,



Probably you have to add record to the SocialMention object as well, as there are stored all @'s

 

Kind regards,

Vladimir

Show all comments

Hi,

I have created a business process, which is simply notifying a user that a new record has been created within a Project. I can create the notification in the way I want, however when clicking on the link in the notification window, it simply takes the user to the Project list page and not the Project page itself i.e. the detail page.

I have used the Parent entity ID column and set this to the Project ID value, as you can see in the screenshot below. This I expected it to work. Performing a trace shows the correct Project ID value for the record in question, so I am not sure what is wrong.



Thanks

Like 0

Like

3 comments

Hello,

 

In order to create an activity, please add this value to the business process in the Add Data element:

To = responsible

Title

Time

Object

Source (for example Owner)

Unique caption Id = Record Id (from the signal)

Hi Anastasia

 

Many thanks for your reply.

 

I have the following data structure in place within the 'Notification' object for creating a new record:

 

To       -       Lookup to specific contact

Object     -      Lookup.Section Object(View).Project

Time     -    System - Current Date & Time

ParentEntityID    -    Project ID

Source     -     (I have tried a number of sources - Lookup > Owner, Lookup > Contact, Specific User via Process Parameter causes a foriegn key constraint error)

Unique Caption ID     -    RecordID from signal



Before I added Source and Unique Caption ID clicking the Notification link in the notification panel took me to the Project summary page. Adding these two as indicated in your reply above causes no action i.e. no page change occurs.



The notification is being created, although it is in the System Messages section, but the link is just not taking me to the Project.



Anything else you can guide me with would be very much appreciated.

 

thanks

 

Mark



 

I have got it working!



For those that come along after this, this is how:

 

  • Title    -     What you want to display in the notification subject

  • To       -       Lookup to specific contact

  • Object     -      Lookup.Section Object(View).Project

  • Time     -    System - Current Date & Time

  • Unique Caption ID     -    RecordID from signal

 

The issue was that I had ParentEntityID included, which once I removed that allowed the link to work. I read a previous Blog on this subject which had this value.

 

 

 

 

Show all comments

We're trying to create a chart to analyse Lead progression through the different Lead Stages, but the only sorting options are alphabetically by the grouping column or by the measure value. We want this to be sorted by an additional column on the Lead Stage that defines the order within the Lead progression rather than alphabetically or by the value to keep a consistent and logical order to the data, but cannot see an option for this unless there's a trick we're missing. Could someone please advise on how this might be done? We're mostly working with Freedom UI pages and therefore charts.

Like 2

Like

6 comments

Vladimir Sokolov,

it may be related, but that question is discussing combo boxes in pages, whereas I’m looking for chart sorting (e.g. in a bar graph) so I expect any solution would be fairly different.

Harvey Adcock,

 

Good day,

 

Could you please provide more details about your business task?



Perhaps you could take a screenshot with a description of your requirements so that we can better understand them and offer advice.

 

We are looking forward to your feedback.



Best regards,

Pavlo

Hi Pavlo,

 

Sure, what the business is asking for is to be able to sort a chart's columns by the lead stage through the process, rather than by the stage's name alphabetically or by the total number of leads in each stage as can be selected by default in the chart options. For example, see this chart available in the Home section of the Sales workplace in a clean CRM trial environment:

 

It is currently sorted by the stage name alphabetically, which doesn't align with the progression you actually get through the Opportunity stages. Looking at the sorting options, we can see that the only ones available are to sort by the grouping name alphabetically, or by the measure value:

 

So we are unable to sort by another field on the Lead Stage lookup that we've created - Stage Number - which would order it correctly. What we'd like is to be able to use that additional field for sorting without using it as the labels for the x axis.

 

If there are any workarounds for this besides numbering the stage names themselves, we'd love to hear about them!

 

Many thanks,

Harvey

Harvey Adcock,

Good day,

 

Unfortunately, it's not possible to implement this with the current basic functionality. However, we have registered the idea for our development team to consider implementing this feature.

Perhaps the "Full pipeline" dashboard type may work for you, where stages are grouped according to their placement in the DCM field?





 

Thank you for reaching out!

Hi Pavlo

 

Would you know if more advance sorting of dashboards has been enabled in any further Creatio releases? 

 

Thanks,

Jacek

Show all comments