Is it possible to restrict the "Owner" filter for Timeline components to only show Contacts which match a specific filter condition? We are currently seeing every Contact as an option, but for us this should be restricted to only Contacts which have a User record associated with them, and ideally we'd want to add some more customisation to the filter options beyond this.

Like 0

Like

8 comments
Best reply

I've discovered that it's possible to add this using code. To do so, you need to override the crt.LoadDataRequest handler and use the following code:

{
	request: "crt.LoadDataRequest",
	handler: async (request, next) => {
		// Filter Timeline's Owner filter to just Contacts with a User record
		if(request.dataSourceName === "ByOwnerQuickFilterInTimeline_xi3lpgm_ComboBox_List_DS") {
			const filter = new sdk.FilterGroup();
			await filter.addExistsFilter("[SysAdminUnit:Contact:Id].Id");
 
			// workaround for filters
			const newFilter = Object.assign({}, filter);
			newFilter.items = filter.items;
 
			request.parameters.push({
				type: "filter",
				value: newFilter
			});
		}
 
		return await next?.handle(request);
	}
},

You have to replace the name of the Timeline component in the above code from Timeline_xi3lpgm to whatever the component is called on your page.

Hello Harvey,



There is no such option for now, however, we've registered it in our R&D team backlog for consideration and implementation in future application releases.

 

Thank you for helping us to improve our product. 

Thanks Bogdan, it's definitely a feature that's needed - having every Contact show up in the Owner filter with no way to change this on a CRM platform isn't ideal.

I've discovered that it's possible to add this using code. To do so, you need to override the crt.LoadDataRequest handler and use the following code:

{
	request: "crt.LoadDataRequest",
	handler: async (request, next) => {
		// Filter Timeline's Owner filter to just Contacts with a User record
		if(request.dataSourceName === "ByOwnerQuickFilterInTimeline_xi3lpgm_ComboBox_List_DS") {
			const filter = new sdk.FilterGroup();
			await filter.addExistsFilter("[SysAdminUnit:Contact:Id].Id");
 
			// workaround for filters
			const newFilter = Object.assign({}, filter);
			newFilter.items = filter.items;
 
			request.parameters.push({
				type: "filter",
				value: newFilter
			});
		}
 
		return await next?.handle(request);
	}
},

You have to replace the name of the Timeline component in the above code from Timeline_xi3lpgm to whatever the component is called on your page.

Harvey Adcock,

good find, thanks!

You should mark your own answer as solution ;)

Harvey Adcock,

Well done! Thanks for sharing this Harvey. 

Also, if you've moved to 8.1.1 you no longer need the filters workaround - it's finally working properly in 8.1.1, just thought I'd mention.

Ryan

Ryan Farley,

Ahh great to hear, thanks for the info Ryan - and thanks for spreading the info about that workaround in the first place!

I'd like to share my solution on lookup filtering.

We wanted to have only contacts from our company and in certain functional role.

{
	request: "crt.LoadDataRequest",
	handler: async (request, next) => {
		if (request.dataSourceName !== "FbContact_List_DS") {
			return await next?.handle(request);
		}
 
		const ourCompanyFilter = new sdk.CompareFilter(
			sdk.ComparisonType.Equal, 
			new sdk.ColumnExpression({
				columnPath: "Account.Type"
			}), 
			new sdk.ParameterExpression({
				value: Constants.AccountType.OurCompany
			})
		);
 
		const funcRoleFilter = new sdk.CompareFilter(
			sdk.ComparisonType.Equal, 
			new sdk.ColumnExpression({
				columnPath: "[SysAdminUnit:Contact:Id].[SysUserInRole:SysUser:Id].SysRole.Id"
			}), 
			new sdk.ParameterExpression({
				value: Constants.FunctionalRole.MyCustomFunctionalRole
			})
		);
 
		request.parameters.push({
			type: sdk.ModelParameterType.Filter,
			value: ourCompanyFilter
		});
 
		request.parameters.push({
			type: sdk.ModelParameterType.Filter,
			value: funcRoleFilter
		});
 
		return await next?.handle(request);
	}
}

 

Alex Zaslavsky,

Interesting, it hadn't occurred to me to push multiple filters to the parameters. I've always added the multiple filters to a FilterGroup and then added the group to the parameters. I guess it's an array for a reason :) Nice to know it works that way as well.

Ryan

Show all comments

I have added a button on every record in detail for a particular column. I am trying to have one more button on the same detail, But the button is not visible on the UI.

The code that i used for this is:

{

"operation": "merge",

"name": "DataGrid",

"parentName": "DataGridContainer",

"propertyName": "items",

"values": {

"className": "Terrasoft.ControlGrid",

"controlColumnName": "UsrPlanningManagerRelevance",

"applyControlConfig": {"bindTo": "applyControlConfig"}

}

},

METHOD

applyControlConfig: function(control, activeRow) {

control.config = {

"className": "Terrasoft.Button",

"style": Terrasoft.controls.ButtonEnums.style.BLUE,

"caption": "מסמכי רקע",//this.get("Resources.Strings.FileButtonCaption"),

"imageConfig": {"bindTo": "Resources.Images.ExportToExcelBtnImage"},

"handler": this.BackgroundDocumentsClick.bind(this, activeRow.id)

};

Can anyone help me on this?

Like 0

Like

1 comments

Hi,



I think, you can do this by appending buttons in the targeted column.

 

var baseEle = "#Identifier div[id*=\"item-" + rowId + "\"]  div:nth-last-child(2)";
$(baseEle).append(Ext.String.format("<span></span>"));
 
var ele = baseSelector + ">span";
$(ele).click(function() {});

 

Loop collection in prepareResponseCollection method or onGridDataLoaded

Show all comments

We were trying to expose the automatically-created Change Log entities created by Creatio when change tracking is turned on for entities, which generally take the form of SysLog but don't actually get navigable entities created in the package. Is there any way to expose these, for example in a detail on a page in Freedom UI? We wanted to be able to specify our own sorting and filtering logic on the OOTB section, as it's quite frustrating to use - for example, every time you drill into a record, it resets the date range for that record to the current day. You also can't search for specific record IDs that you want to see modifications to unless the record never had a Name filled in which makes trying to see what happened to deleted records you know must have existed very difficult!

 

I tried manually creating an entity over the table name (specifying it as a database view so Creatio didn't try to create or update the actual table) but then adding the change log entity's columns wasn't possible due to them having to have the prefix before the names, which the automatically-created columns of the change log table didn't have (e.g. Id, ChangeTypeId etc). Any way of working around this or a way of adding a change log entity as List in the UI somehow?

Like 1

Like

5 comments
Best reply

You can avoid needing the prefix by clearing the prefix system setting. Then no prefix will be needed.

However, I'm not sure creating the object would be a good idea, perhaps? Seems risky. What I would do is create a database view wrapping the data and then expose that as an object instead. That seems like the best route to me.

Ryan

Any ideas on how this might be possible?

You can avoid needing the prefix by clearing the prefix system setting. Then no prefix will be needed.

However, I'm not sure creating the object would be a good idea, perhaps? Seems risky. What I would do is create a database view wrapping the data and then expose that as an object instead. That seems like the best route to me.

Ryan

Thanks Ryan, sounds like a great idea - then the view over the top of the change log table can have its columns renamed where the prefix isn't present to start with the prefix, which will avoid the need to turn prefix off and on when adding new columns to the change log view entity.

Harvey,

 

Can I ask, what tables the change log is saved to if you know?  Or if there is a view to read the change log?  There is an API I note but I have a scenario where a SQL query is the better option.

Hi Gareth, the name of the tables created for the change log is Sys[object name]Log, so for example for the Account entity, it would be SysAccountLog. Hope this helps!

Show all comments

In the academy documentation, there is limited information about using the ESQ/ORM server-side methods to delete data, with only 2 or 3 toy examples. We need to efficiently delete large volumes of data using server-side ESQ in one of our processes, but we can't see anything about bulk deleting while still firing off any event-based triggers on the entity or generating change log records for it. Obviously the direct Terrasoft.Core.DB delete statement would be very efficient, but would skip the Creatio logic where needed.

 

We've been able to delete records by iterating over a collection of entities matching the required filtering condition retrieved by using GetEntityCollection and then for each entity in the collection, running a FetchFromDB on its Id before deleting it, but this seems like quite a few steps more than should be required and doesn't do it in a batched way.

 

Any help would be greatly appreciated.

Like 0

Like

2 comments

Hello Harvey,

 

You can use out-of-the-box examples of bulk deletion logic. For example we can take the BulkEmailTargetArchiver class and the DeleteArchiveLevel method in it:

private void DeleteArchiveLevel(string sourceSchemaName, string targetSchemaName) {
			int processedRecords;
			do {
				var deleteQuery = new Delete(_userConnection).From(sourceSchemaName)
					.Where("Id").In(new Select(_userConnection)
						.Top(_batchSize)
						.Column("s", "Id")
						.From(sourceSchemaName).As("s")
						.Where().Exists(new Select(_userConnection)
							.Column(Column.Parameter(1))
							.From(targetSchemaName)
							.Where(targetSchemaName, "Id")
							.IsEqual("s", "Id")));
				processedRecords = deleteQuery.Execute(_dbExecutor);
			} while (processedRecords != 0);
		}

This is just one example of the logic. You can use:

 

select * from SysSchemaSource

where Source like '%DeleteQuery%'

 

to get other schemas in the application configuration with delete queries examples and build your own one using all examples you may find in the system.

Hi Oleg,

 

using these Delete classes bypasses Creatio’s internal logic though, right? So change logs will be inaccurate and any BPs that should be triggered on delete would not be triggered etc. This seems risky to me as important processes could hang off this, and I don’t know what other internal aspects of Creatio might be relying on logic that Creatio manages at an application level.

 

Is there any way to bulk delete while respecting that?

Show all comments

Hi Community,

How can we filter the Cities based on the Zip Code (string field) in Freedom UI?

Apparently dynamic filtration on Object Level works only for Lookup fields.

Sasori

Like 0

Like

4 comments

Hello Sasori,



Since a text field and there is no lookup values you can not filter it based on conditions. 

Hi Bogdan,

Your answer is something that we already now (I have already stated that in my post) . And it is very astonishing that such functionality which was oob in the Classic UI is not present in the Freedom UI.

Please give us a code example of implementing such functionality in freedom UI.

Sasori

Sasori,

 

We don't have a ready example of such implementation. 

 

However, we have already registered the idea for our R&D team to implement this functionality in further releases. I will assign your case to this project to increase its priority.  

Sasori Oshigaki,



You need to create other lookups, remove and replace the current postal code text field, so that you can add postal code to the list of cities to do the links. It is indeed weird that this is a seperate text field as an OOTB feature.



Also, accounts is in Freedom UI, with customer 360, but not account adresses , how weird is that ??

Show all comments

I tried returning false and not calling next?.handle(request), but the record still seems to get saved. In Classic UI it was possible to prevent saving by not calling the this.callParent method, which I believe is somewhat analogous to the next?.handle(request), but I guess not perfectly? Maybe I'm missing something.

Like 0

Like

1 comments

Hello Harvey,

It is possible, and you do it in the right way as I can see from the description. To prevent saving you have to declare this handler in the scheme and simply do not call next?.handle(request). 

Show all comments

I'm looking to trigger page data validation in a specific circumstance from code. I've found that you can use

request.$context.validate()

within client event handlers to trigger the OOTB fields validation for the page, but this just returns an object representing any/all errors in validation for the data. What would make sense to do in our use case after that would be to trigger the NotifyService message you usually see when saving fails due to such validation checks, but I can't see how to trigger it/how to fetch the string that should be shown in the message. I located a method that looks like it is performing this task for the OOTB save validation, called getValidationErrorMessage, which would be passed the request context and the error object returned by the validate function, but this method doesn't seem to be available for use.

 

Has anybody had any luck with triggering validation in Freedom UI and then displaying the OOTB message based on that validation result? Any help would be greatly appreciated. We're currently running on 8.1.0

Like 1

Like

1 comments



I have a section called 'Requests' that has 3 multiple edit pages based on a section FIELD value. Now, the parent edit page has been disabled (it is in a 'not in use' state). How can I enable it? I need a common page that is not based on a Field value.

Like 0

Like

2 comments

Dear Sam

 

I recommend you to keep the main base page unused.

The other three pages you can make inherit from this first one getting a base page for common problems, for example, you need to add a business rule or a field and you need it in three sections, you go to the base page, create what you need there and voila, all the pages get this new field or business rule....

 

See images:

1.- Client Model schemas  https://prnt.sc/4YfSmr4FPukL

2.- How to inherit https://prnt.sc/-owYS4INpiXe

3.- https://prnt.sc/69f_gvWJjxBW

 

Regards

Julio Falcón

Julio.Falcon_Nodos,

Thanks, Julio!

Your suggestion to keep the main base page unused and have the specific pages inherit from it makes sense. I implement it using another approach.

Thanks for your reply

 

Regards, Sam

Show all comments

We're trying to set it up so that one of our mini pages used for record creation (wherever the record creation is started from, as there are several screens with a button to create the record) always has a default value set. We can't set the default on the entity, as records coming in from the integrations shouldn't have these fields set to any default, only set to whatever comes in through the interface. Is there any configuration that can be done to set default values for pages themselves?

 

We know about setting defaults when calling the crt.CreateRecordRequest request which is done from the calling page, but would ideally like to be able to set it one time for the creation mini page itself to avoid code duplication. We are on 8.1.0

Like 1

Like

5 comments

You can setup a business rule if the attribute is not filled in then you set a static value. This will work only on the interface.

Thanks for the suggestion Franck, and interesting/concerning that the entity-level business rules only apply when creating records through the UI and not for all data!

 

Unfortunately the business rule value setting is much too limited in Creatio for what we need, only being able to set to a constant/static value which is defined when setting up the business rule. We need to be setting the value based on some logic through code, but would ideally be doing so with declarative code rather than imperative code which modifies the fields manually.

Hello Harvey,

The most general way to set the default values for a mini-page is to transfer them in ModelInitConfigs via crt.OpenPageRequest:

modelInitConfigs: [{defaultValues: [{AttributeName: 'AttributeValue'}]}]

But if you want to avoid transferring the values in every place you open the mini-page, consider the following implementation in the mini-page view model:

  1. Set the attribute value in the "crt.HandleViewModelInitRequest":
    request.$context.AttributeName = “AttributeValue”;
  2. Provide it not to be overridden to default null value in "crt.HandleViewModelAttributeChangeRequest":
    if (request.attributeName === "AttributeName" && request.value === null) {
          request.$context.AttributeName = “AttributeValue”;
    }

     

Best regards, Natalia

Hi Natalia,

 

Thanks for the reply, those will definitely be useful - for example I didn't know you could set default values when calling the crt.OpenPageRequest, I thought it was only possible to do so using the crt.CreateRecordRequest, so thanks for that!

 

I believe the workaround for putting the logic in the mini-page would mostly work, but with the following caveats:

1. You would not be able to clear the value of the field manually, which is unfortunate

2. This would make a change to the page that would cause the confirmation dialog to appear if closing the mini-page without the user making any modifications to data - not the end of the world, but it would be nice to avoid that

 

A quick question - what is the difference between simply assigning a request.$context.AttributeName using 

request.$context.AttributeName = "value"

Vs using the _setAttributeValue method in your reply? Is there any functional difference between the two, or is it just preference?

 

Many thanks,

Harvey

Harvey Adcock,

 

Hi, 

Sure, setting the mini page default values via HandleViewModel InitRequest and HandleViewModelAttributeChangeRequest has some disadvantages. That’s why we recommend using ModelInitConfigs in OpenPageRequest.

 

However, it may still be used if the user doesn’t need to set the field value to null. Especially considering the facts that:

- removing the string field means setting a value to an empty string (not null);

- the numeric field value might be set to 0 instead of removing it.

 

The question of silent saving has already been discussed in the separate feed - https://community.creatio.com/questions/it-possible-make-changes-attributes-code-freedom-ui-silently .

 

Regarding your question about setting the attribute value – I clarified it with our R&D department. There is no functional difference between the two approaches, but only the direct assigning is recommended to use as the most stable method:

request.$context.AttributeName = "value"

Thank you for such an important question. I have already changed my examples accordingly.

 

Best regards,

Natalia

Show all comments

We've got a requirement to run some async entity event listener code, but when following the academy article for that ( https://academy.creatio.com/docs/7-18/developer/back_end_development/ob… ) and trying to use the arguments.OldEntityColumnValues property of the EntityEventAsyncOperationArgs arguments parameter passed in, we get the following compilation error which we cannot seem to resolve:

 

From looking online for general resolutions, it looks like we maybe need to add this assembly to the web.config file, but we're on a cloud instance so presume this isn't a feasible resolution.

 

Any advice would be greatly appreciated.

 

Reduced version of the code (without usings etc shown) that throws the error below:

public class UsrAsyncSendLeadAssociation: IEntityEventAsyncOperation
{
    public void Execute(UserConnection userConnection, EntityEventAsyncOperationArgs arguments) {
        var oldModVal = arguments.OldEntityColumnValues;
    }
}

 

Like 0

Like

1 comments

Hello,

As a quick solution, you should disable the option "Compile into a separate assembly" in the package where the schema is located.

After this, the problem should be resolved.

Show all comments