Prerequisites

The section objects should be accessible for portal users.

Open access to custom objects in portal

 

1. On dev environment run script to create [int_RegisterSectionInPortal] stored procedure.

Note. Procedure uses string_split function which is available only under compatibility level 130 and above (see details here).

int_RegisterSectionInPortal stored procedure 

create or alter proc [int_RegisterSectionInPortal] (@EntityName NVARCHAR(MAX), @PortalId uniqueidentifier = 'C8565240-1DA3-4A68-BD4E-280F17B0D32E')
as
begin transaction;
set nocount on;
set transaction isolation level read uncommitted;
set xact_abort on;
begin try
if @EntityName = '' OR @EntityName IS NULL
throw 50001, 'Parameter @EntityName is empty', 1;
declare @PortalSysModuleEntity table (Id uniqueidentifier, BaseRecordId uniqueidentifier, TypeColumnUId uniqueidentifier, SysEntitySchemaUId uniqueidentifier);
declare @SchemaName table (Name nvarchar(250) NOT NULL);
declare @temp table (Id uniqueidentifier);
insert into @SchemaName
select value 
from string_split(@EntityName, ',')
where rtrim(value) <> '';
if (select COUNT(*) from @SchemaName) < 1
throw 50002, 'Parameter @EntityName has wrong format. It should contain entities comma separated names.', 1;
insert into @PortalSysModuleEntity (Id, BaseRecordId, TypeColumnUId, SysEntitySchemaUId)
select NEWID(), e.Id, TypeColumnUId, e.SysEntitySchemaUId
from SysModuleEntity e
where e.SysEntitySchemaUId in (
    select [uid]
    from SysSchema sh
        inner join @SchemaName n on n.Name = sh.Name
    where ExtendParent = 0
)
insert into SysModuleEntity(Id, TypeColumnUId, SysEntitySchemaUId, CreatedOn)
select Id, TypeColumnUId, SysEntitySchemaUId, GETUTCDATE()
from @PortalSysModuleEntity
select Id as SysModuleEntity
from @PortalSysModuleEntity;
insert into SysModule (
            [CreatedOn]
           ,[Caption]
           ,[SysModuleEntityId]
           ,[Image16]
           ,[Image20]
           ,[FolderModeId]
           ,[GlobalSearchAvailable]
           ,[HasAnalytics]
           ,[HasActions]
           ,[HasRecent]
           ,[Code]
           ,[HelpContextId]
           ,[ProcessListeners]
           ,[SysPageSchemaUId]
           ,[ModuleHeader]
           ,[Attribute]
           ,[CardSchemaUId]
           ,[SectionModuleSchemaUId]
           ,[SectionSchemaUId]
           ,[CardModuleUId]
           ,[TypeColumnValue]
           ,[Image32Id]
           ,[LogoId])
output inserted.Id into @temp
select      GETUTCDATE()
           ,s.[Caption] + ' Portal'
           ,p.Id
           ,s.[Image16]
           ,s.[Image20]
           ,s.[FolderModeId]
           ,s.[GlobalSearchAvailable]
           ,s.[HasAnalytics]
           ,s.[HasActions]
           ,s.[HasRecent]
           ,s.[Code]
           ,s.[HelpContextId]
           ,s.[ProcessListeners]
           ,s.[SysPageSchemaUId]
           ,s.[ModuleHeader]
           ,s.[Attribute]
           ,s.[CardSchemaUId]
           ,s.[SectionModuleSchemaUId]
           ,s.[SectionSchemaUId]
           ,s.[CardModuleUId]
           ,s.[TypeColumnValue]
           ,s.[Image32Id]
           ,s.[LogoId]
from SysModule
    inner join SysModuleEntity sme on sme.Id = SysModule.SysModuleEntityId
    inner join @PortalSysModuleEntity p on p.BaseRecordId = sme.Id
    inner join SysModule s on s.SysModuleEntityId = p.BaseRecordId
select Id as SysModule
from @temp;
delete from @temp;
insert into SysModuleEdit (ActionKindCaption, ActionKindName, CardSchemaUId, HelpContextId, MiniPageSchemaUId, PageCaption, SearchRowSchemaUId, SysModuleEntityId, SysPageSchemaUId, TypeColumnValue, UseModuleDetails, CreatedOn)
output inserted.Id into @temp
select e.ActionKindCaption, e.ActionKindName, e.CardSchemaUId, e.HelpContextId, e.MiniPageSchemaUId, e.PageCaption, e.SearchRowSchemaUId, p.Id, e.SysPageSchemaUId, e.TypeColumnValue, e.UseModuleDetails, GETUTCDATE() CreatedOn
from SysModuleEdit e
    inner join @PortalSysModuleEntity p on p.BaseRecordId = e.SysModuleEntityId
where e.SysModuleEntityId in (
select Id
from SysModuleEntity e
where e.SysEntitySchemaUId in (
    select [uid]
    from SysSchema sh
        inner join @SchemaName n on n.Name = sh.Name
    where ExtendParent = 0
))
select Id as SysModuleEdit
from @temp;
delete from @temp;
insert into SysModuleEntityInPortal(SysPortalId, SysModuleEntityId, CreatedOn)
output inserted.Id into @temp
select @PortalId, p.Id, GETUTCDATE()
from @PortalSysModuleEntity p
select Id as SysModuleEntityInPortal
from @temp;
delete from @temp;
commit transaction
end try
begin catch
    select ERROR_NUMBER() as ErrorNumber 
        ,ERROR_MESSAGE() as ErrorMessage;
    if @@TRANCOUNT > 0
        rollback transaction;
end catch;
if @@TRANCOUNT > 0
    rollback transaction;

2. On dev environment execute a stored procedure to register sections in the portal.

Stored procedure [int_RegisterSectionInPortal] takes as an argument comma separated list of section table names.

Example

exec dbo.[int_RegisterSectionInPortal] 'lcBwJob,lcBwLine,lcBwVeh,lcBwCc,lcBwDevice'

3. Add sections to the portal workplace (see figure 0)

4. Bind new data to a package with a filter by ids from output(see figure 1-2)  or by CreatedOn = Today (see figure 3) for the following system tables:

  • SysModuleEntity
  • SysModule
  • SysModuleEdit
  • SysModuleEntityInPortal

Figure 0. Add sections to portal workplace

Figure 1. Retrieve new records ids from the output

Figure 2. Bind data by ids from output

Figure 3. Bind data by date

Like 0

Like

Share

0 comments
Show all comments

Symptoms

Slow operation of the [Connected to] detail of the [Accounts] section (dashboard view)

Cause

Internal implementation of the diagram.js component and the display mechanism of detail elements (RelationshipDiagramViewModel).

Solution

In version 7.7.0, the diagram.js component implementation has been considerably improved. To transfer the functionality to version 7.6., download the module from the UsrDiagram.md file to the Custom  package, which will fully override the "Terrasoft.Diagram" base class. Add the replacing client module for the AccountRelationshipDetailV2 detail and add connection with the UsrDiagram module and overriding of some view model methods of the diagram.

Example of a code:

define("AccountRelationshipDetailV2", ["UsrDiagram"], function() {
		return {
			methods: {
				getMinLevel: function(accounts) {
					return accounts[0].level;
				},
 
				getMaxLevel: function(accounts) {
					return accounts[accounts.length - 1].level;
				},
 
				buildDiagramNodes: function(accounts) {
					this.clearAllDiagramNodes();
					var nodes = this.get("Nodes");
					var accountConfigs = {};
					accounts.forEach(function(account) {
						accountConfigs[account.id] = this.getNodeConfig(account);
					}, this);
					nodes.loadAll(accountConfigs);
					this.createNodeConnections(accounts);
				},
 
				createNodeConnections: function(accounts) {
					var nodes = this.get("Nodes");
					var connectionConfigs = {};
					var maxLevel = this.getMaxLevel(accounts);
					var filteredAccounts = accounts.filter(function(item) {
						return item.level < maxLevel;
					});
					filteredAccounts.forEach(function(account) {
						account.children.forEach(function(child) {
							connectionConfigs[account.id + "/" + child.id] = this.getConnectionConfig(child, account);
						}, this);
					}, this);
					nodes.loadAll(connectionConfigs);
				}
			},
 
			diff: /**SCHEMA_DIFF*/ []/**SCHEMA_DIFF*/
		};
	}
);

For versions 7.7.0-7.8.0, it will be enough to override the view model methods. In later versions, we plan to optimize this detail further.

Necessary conditions and possible restrictions

When updating from version 7.6.0 to 7.7.0 and higher, delete the UsrDiagram schema described above as well as its links. 

 

File attachments
Like 0

Like

Share

0 comments
Show all comments

Case

When the catalogue structure is changed, catalogues cannot be opened.

Solution

In the FolderManagerViewModel  schema of the ProductOmnichannel  package, make the following changes in the getCatalogueLevelItemsSelect()  method:

repace the strings:

select.addColumn("Id", "ColumnPathValueId");
var column = select.addColumn("Name", "ColumnPathValueName");

for the following:

select.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_COLUMN, "ColumnPathValueId");
var column = select.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_DISPLAY_COLUMN,  "ColumnPathValueName");

These changes are needed because a customer might not have the [Name] field in the object at all.

Necessary conditions:

Verison 7.6

Like 0

Like

Share

0 comments
Show all comments

Question

I want to perform ESQ from rootSchemaName: "X". How do I know which object property is its text view (the column value displayed in the screenshot)?

We receive responses to some requests that are not mine. In these responses, there are objects with the displayValue property, which is exactly what I need.

Example of a code:

var select = Ext.create("Terrasoft.EntitySchemaQuery", {
    rootSchemaName: dimensionItem.get("SysSchemaName")
});
 
select.getEntityCollection(function(response) {
    if (response.success) {
        var collection = response.collection;
        if (collection && collection.getCount() > 0) {
 
        }
    }
})

Answer

To receive the column values marked as Displayed name in an object via esq, use the esq.addMacrosColumn() esq method.

Example of a code:

var esq = this.Ext.create("Terrasoft.EntitySchemaQuery", {
    rootSchemaName: 'UsrTestObj'
});
esq.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_DISPLAY_COLUMN, "displayValue");
esq.addMacrosColumn(Terrasoft.QueryMacrosType.PRIMARY_COLUMN, "value");
esq.getEntityCollection(function(result) {
    if (result.success) {
        debugger;
    }
}, this);

As a result, you receive the Id column value collection and the column marked as Displayed value from the UsrTestObj table.

Like 0

Like

Share

2 comments

Hi,



Is there a way to do this in C#?



Thank you.

Solem Khan Abdusalam,

For server-side C# ESQ, the entities returned by GetEntity and GetEntityCollection include a property for this:

var displayText = entity.PrimaryDisplayColumnValue;

Ryan

Show all comments

Symptoms

Bug report sample:

Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie"},"method":"GET","disableCaching":false},"performanceCounter":{"startDate":"2015-07-24T08:26:56.702Z"}},"headers":{"X-Terrasoft-Mobile":"true","Accept":"application/json","Content-Type":"application/json","Authorization":"Cookie"},"method":"GET","disableCaching":false},"async":true},"requestId":8,"status":500,"statusText":"Internal Server Error","responseText":"Terrasoft.Mobile.MobileModelLoopException: Models specified in the mobile application manifest have circular relationships\r\n   at Terrasoft.Mobile.MobileModelTopologicalSorter.Sort()\r\n   at Terrasoft.Mobile.MobileModelGraph.Build()\r\n   at Terrasoft.Mobile.MobileManifest.BuildModelGraph()\r\n   at Terrasoft.Mobile.MobileUtilities.GetAppMetadata(HttpContextBase context)\r\n   at Terrasoft.WebApp.Mobile.Services.MobileCodeService.ProcessRequest(HttpContext context)\r\nCycle: [Account,KnowledgeBase]","responseXML":null,"responseBytes":null} 

Calls sequence:

Solution

To solve, you must create (Add - Source code) a custom manifest (for example, MobileApplicationManifestCustom) and specify the "Javascript" language.

For version 7.6 and higher, if MobileApplicationManifest is not in Custom, you need to replace the parent manifest schema. To do this, in the mobile application wizard, select "Section settings" for the workplace and click the "Save" button.

If the custom manifest already exists, open it and add the following code:

{
    "Loops": [
        ["Account", "KnowledgeBase"],
    ]
}

where ["Account", "KnowledgeBase"] - links that must be specified.

The value is written from the value.

For example (from the bug report):

If

Cycle: [Invoice,Contract]","responseXML":null,"responseBytes":null}

Then

Specify the following code:

{
    "Loops": [
        ["Invoice”,”Contract "],                   
    ]
}

If the mobile app version is 7.5 and lower, you also need a manifest (for example, MobileApplicationManifestCustom) in the MobileApplicationManifest system setting:

And check the synchronization. If it ends with an error, then most likely it is still necessary to specify the connection in the custom manifest. As a result, add to the manifest until synchronization ends successfully.

Like 0

Like

Share

0 comments
Show all comments

Question

My question concerns changing the button availabillity without updating the list – kindly advise where I can learn about the sandbox message implementation feature?

Answer

There are a lot of examples of sandbox message implementation in bpm'online. The BasePageV2  and BaseSectionV2 modules are some of them. In these modules, when performing initialization, we call the subscribeSandboxEvents() method that implements subscription to the sandbox messages.

For example, the [CardChanged] message subscription is implemented in the BaseSectionV2  module. When the module receives this message, it sets the modified value to the corresponding attribute. The message, on the other hand, sends (publishes) the BasePageV2 module when calling the publishPropertyValueToSection() method. The publishPropertyValueToSection() method, in its turn, is called when certain attributes of the edit page model are modified.

You can act in a similar way. For example, subscribe to the modification of the [ServiceCategory] field in the init() method of your edit page (CasePage):

init: function() {
    this.callParent(arguments);
    this.on("change:ServiceCategory", function(model, value) {
        this.publishPropertyValueToSection("CurrentServiceCategory",value);
    }, this);
}

Thus, when the [ServiceCategory] page field is modified, the new value will be populated in the [CurrentServiceCategory] attribute of the CaseSection section.

After this, you will be able to receive the current category value by addressing the [CurrentServiceCategory] attribute:

isEnableButtonColumbus: function() {
    var serviceCategory = this.get("CurrentServiceCategory");
    if (!serviceCategory) {
        // Ваш код
    } else {
        return (serviceCategory.value ===UsrConsts.ServiceCategory.Dynamix);
    }
}

 

Like 0

Like

Share

0 comments
Show all comments

1. Create Source Code:

Ext.define("Terrasoft.configuration.service.UsrYourService", {
    alternateClassName: "Terrasoft.UsrYourService",
    statics: {
 
        serviceUrl: "rest/UsrYourService/",  // Name of your Service
 
        yourAction: function(config) {
            var data = {
                recordId: config.id // Input parameter(s) of service
            };
            Terrasoft.RequestManager.issueRequest({
                supressRequestEvents: false,
                requestFn: Terrasoft.Ajax.request,
                requestFnConfig: {
                    url: Terrasoft.util.encodeServiceUrl(this.serviceUrl) + config.action,
                    method: "POST",
                    jsonData: data,
                    success: function(response) {
                        var decodedResponse = Ext.JSON.decode(response.responseText, true);
                        Ext.callback(config.success, config.scope, [decodedResponse]);
                    },
                    failure: function(response) {
                        var parser = Ext.create("Terrasoft.ServiceResponseParser", response);
                        var exception = parser.getServiceFailureException();
                        Ext.callback(config.failure, config.scope, [exception]);
                    },
                    scope: this
                },
                responseToStatusCodeFn: function(response) {
                    return response.status;
                },
                scope: Terrasoft.Ajax
            });
        }
    }
});

2. Add in MobileApplicationManifest your service into CustomSchemas block:

"CustomSchemas": [
    "UsrYourService"
]

3. Call service from your code:

yourFunction: function() {
    Terrasoft.UsrYourService.yourAction({
        id: this.record.getId(), // RecordId
        action: "Start", // Method of service
        success: this.serviceSuccess, // Success callback
        failure: this.serviceFailure, // Error callback
        scope: this
    });
},
serviceSuccess: function(response) {
    if (response.isSuccess) {
        ///TODO: do something
    } else {
        Terrasoft.MessageBox.showMessage(Terrasoft.LocalizableStrings.StartErrorMessage); // Error message
    }
},
serviceFailure: function(error) {
    var response = error.getResponse();
    Terrasoft.MessageBox.showMessage(response.statusText);
}

 

Like 0

Like

Share

0 comments
Show all comments

In order to run process GlbProcess1 from server side use code below:

IProcessExecutor processExecutor = UserConnection.ProcessEngine.ProcessExecutor;
var nameValues = new Dictionary<string, string>();
// Add your parameters
nameValues["RecordId"] = entityId.ToString();
processExecutor.Execute("GlbProcess1", nameValues);

If you want to run GlbProcess1 for all records in Case:

var esq = new EntitySchemaQuery(UserConnection.EntitySchemaManager, "Case");
esq.PrimaryQueryColumn.IsAlwaysSelect = true;
var entities = esq.GetEntityCollection(UserConnection);
foreach(var entity in entities) {
    var entityId = entity.PrimaryColumnValue;
    IProcessExecutor processExecutor = UserConnection.ProcessEngine.ProcessExecutor;
    var nameValues = new Dictionary<string, string>();
    // Add your parameters
    nameValues["RecordId"] = entityId.ToString();
    processExecutor.Execute("GlbProcess1", nameValues);
}

 

Like 0

Like

Share

3 comments

This code gives an error now. "ProcessExecutor is not usable because of it's protection level" or something like that.

Hi Jonas,

I just checked from my end and I was able to use above article to start a process.



Which version are you using? Could you please provide error screenshot?



Regards,

Dmytro

Hi Dmytro Smishchenko & Tatiana,

Have a follow up question regarding executing a BP from the server side. The article covers how to execute it by passing certain parameters. How does one get back certain out output parameters after the BP executes??



I went through the IProcessExecutor documentation here and it permits to retrieve one result parameter. What if I need to read more than 1 parameter at the end of the business process execution?? Is there any other way to execute a BP from server side code and get the output parameter?



Anastasia's comment in this question - https://community.creatio.com/questions/call-business-process recommends the use of FlowEngine to run the business process. I also went through the class documentation of FlowEngine here. Could not find a way to get BP parameters after execution.



Appreciate your assistance. 

Show all comments

Question

Can we configure calculating the response/resolution time from the moment of assigning a case assignee (it is now calculated from the moment of registering a case)?

Answer

The date of calculation is performed at the moment of changing the Service in the onServiceItemChanged() method when a case is created. You can create a method that would process chaning of the Assignee and calculate the date, but make sure all the necessary fields are populated before that (in particular, the [Service] field).

Like 0

Like

Share

0 comments
Show all comments

Question

How do I display another column in the "Accounts" list in the mobile app? For example, the "Address" coulmn?

Answer

Since the MobileAccountModuleConfig base schema cannot be changed, you need to create a custom UsrMobileAccountModuleConfig schema (in a custom package, create the page extension schema of the corresponding section). To do this, in the [Configuration] section, select Add > Source code. Fill in the schema properties ([Title], [Name], [Package]). Select JavaScript as a language for the schema, and connect it to the manifest.

 

"Models": {
"Account": {
"RequiredModels": [
. . .
],
"ModelExtensions": [
. . .
],
"ModelExtensions": [],
            "PagesExtensions": [
                "MobileAccountRecordPageSettingsDefaultWorkplace",
                "MobileAccountGridPageSettingsDefaultWorkplace",
                "MobileAccountActionsSettingsDefaultWorkplace",
                "MobileAccountModuleConfig",
                "UsrMobileAccountModuleConfig"
            ]

Paste the following code in the schema:

Terrasoft.sdk.GridPage.setSecondaryColumn("Account", {
    columns: ["Address"],
    convertFunction: function(values) {
        return values.Address;
    }
});

Register the necessary columns in the manifest:

{
            "Name": "Account",
            "SyncColumns": [
                "Address"
            ]
        }

Once done, we recommend regenerating the source code and compiling the configuration 

Like 0

Like

Share

0 comments
Show all comments