Hi,

I often use backend validation in Creatio - I prevent saving a record and throw an exception from an event listener. It works like a charm, but as a result, the end user sees only a toast message, which is small and disappears after a moment, so it's easy to overlook. Is it possible to intercept such an error and display a normal confirmation popup, instead of this one?

It would be great if there is a generic solution, which doesn't require me to change all of my validations.

Like 0

Like

2 comments

Hi Eryk, in your business process you can display a mini page with the process component pre-configured page. 

 

Else you have following component to add in your site:

https://marketplace.creatio.com/template/popup-element-business-processes

 

Hope this helps.

Hello Eryk,
Thanks for your question.

As an alternative you can use websockets to implement your logic. 

Here i created a custom section and a clientMessageBridge as described in this article. In this section i created a Test Field. To subscribe to message broadcasting in the HandleViewModelInitRequest handler i used schema attribute to store a function that will be called whenever a message is published. Also note, that you should unsubscribe from websocket connection when your view is destroyed. To do this please don't forget to implement such logic in HandleViewModelDestroyRequest handler. 

On the form page when user clicks Save button a custom MyCustomRequest handler is called. In this handler i realized the following logic: call a business process (where i pass Test Field value as well as expected business process parameter value) -> if there is no validation error that is passed back from business process (hasValidationErrors property) i call SaveRecordRequest request -> if there is an error - the SaveRecordRequest is not performed.

Also the function that is triggered when a websocket message is broadcasted does open a pop-up which displays error message.

Here is an example:
page designer
handlers
bp

script task

And the outcome:
3
1

2

Hope this helps and let me know if you have any question left.

Show all comments

Hi guys,

I am working on a custom "ContactFieldConverter" [the thing that splits the Contact's FullName imported by excel/ webserveces etc] in order to split new contact's names into "GivenName" and "Surname", and preventing the OOTB function from filling in the "MiddleName" column.

In order to do so, I created a custom ContactGsFieldConverter in my package and inserted the value of the Separator and Converter in the Lookup "ShowNamesBy" and sysSetting "ContactFieldConverter".

[myContactGsFieldConverter code:]

 

 namespace Terrasoft.Configuration {
	using System;
	using System.Linq;
	using System.Text;
	using Terrasoft.Common;
	using Terrasoft.Configuration;
 
	#region Class: myContactGsFieldConverter
 
	/// <summary>
	/// Contact "Full name" field converter class.
	/// Separates "Full name" string using "Given name Surname" rule.
	/// </summary>
	public class myContactGsFieldConverter : IContactFieldConverter
	{
 
		#region Properties: Public
 
		/// <summary>
		/// Contact "Full name" separator characters array.
		/// </summary>
		private char[] _separator = { ' ' };
		public char[] Separator {
			get {
				return _separator;
			}
			set {
				_separator = value;
			}
		}
 
		#endregion
 
		#region Methdos: Public
 
		/// <summary>
		/// <see cref="IContactFieldConverter.GetContactSgm"/>
		/// </summary>
		/// <remarks>
		/// After splitting <paramref name="name"/> first element will be used as <see cref="ContactSgm.GivenName"/>,
		/// Everything else as <see cref="ContactSgm.Surname"/>.
		/// </remarks>
		ContactSgm IContactFieldConverter.GetContactSgm(string name) {
			var sgm = new ContactSgm();
			if (string.IsNullOrEmpty(name)) {
				return sgm;
			}
 
			var array = name.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
			switch (array.Length) {
				case 0:
					return sgm;
				case 1:
					sgm.GivenName = array[0];
					break;
				case 2:
					sgm.GivenName = array[0];
					sgm.Surname = array[1];
					break;
				default:
					sgm.GivenName = array[0];
					StringBuilder sb = new StringBuilder();
					for (var i = 1; i <= array.Length - 1; i++) {
						sb.AppendFormat("{0} ", array[i]);
					}
					sgm.Surname = sb.ToString().Trim();
					break;
			}
			return sgm;
		}
 
		/// <summary>
		/// <see cref="IContactFieldConverter.GetContactName"/>
		/// </summary>
		/// <remarks>
		/// "Full name" string will be created using "Given name [Middle name] Surname" rule.
		/// </remarks>
		public string GetContactName(ContactSgm sgm) {
			var concatChar = Separator.FirstOrDefault();
			return new[] { sgm.GivenName, sgm.MiddleName, sgm.Surname }.ConcatIfNotEmpty(concatChar.ToString());
		}
		#endregion
	}
	#endregion	
}

The converter is not working. I tried to debug and noticed that my contact converter is returned as null to [this is a method in the Contact c# object]: 

public virtual void SetSgm(Contact contact) {
			if (contact == null) {
				return;
			}
			contact.FillSgmFields(GetContactConverter());
		}

Do you know if I need to do something specific in case of separate-assembly package?

ie: Using a different namespace for the converter/ setting the converter path in a different way within the system setting/ recreate the method on the Contact Object in my custom package.





Thank you in advance!

Like 1

Like

5 comments

Hello Federica,

 

I just completed the test - convertor works in the assembly package as well. The code (since we don't need middle name) was:

namespace Terrasoft.Configuration {
    using System;
    using System.Linq;
    using System.Text;
    using Terrasoft.Common;
    #region Class: UsrCustomContactFieldConverter
    /// &lt;summary&gt;
    /// Contact "Full name" field converter class.
    /// Separates "Full name" string using "Surname Given name" rule.
    /// &lt;/summary&gt;
    public class UsrCustomContactFieldConverter : IContactFieldConverter
    {
        #region Properties: Public
        /// &lt;summary&gt;
        /// Contact "Full name" separator characters array.
        /// &lt;/summary&gt;
        private char[] _separator = { ' ' };
        public char[] Separator {
            get {
                return _separator;
            }
            set {
                _separator = value;
            }
        }
        #endregion
        #region Methdos: Public
        /// &lt;summary&gt;
        /// &lt;see cref="IContactFieldConverter.GetContactSgm"/&gt;
        /// &lt;/summary&gt;
        /// &lt;remarks&gt;
        /// After splitting &lt;paramref name="name"/&gt; first element will be used as &lt;see cref="ContactSgm.Surname"/&gt;,
        /// second element as &lt;see cref="ContactSgm.GivenName"/&gt;
        /// &lt;/remarks&gt;
        ContactSgm IContactFieldConverter.GetContactSgm(string name) {
			var sgm = new ContactSgm();
			if (string.IsNullOrEmpty(name)) {
				return sgm;
			}
			var array = name.Split(Separator, StringSplitOptions.RemoveEmptyEntries);
			switch (array.Length) {
				case 0:
					return sgm;
				case 1:
					sgm.GivenName = array[0];
					break;
				default:
					sgm.GivenName = array[0];
					sgm.Surname = array[1];
					break;
			}
			return sgm;
		}
        /// &lt;summary&gt;
        /// &lt;see cref="IContactFieldConverter.GetContactName"/&gt;
        /// &lt;/summary&gt;
        /// &lt;remarks&gt;
        /// "Full name" string will be created using "Surname Given name" rule.
        /// &lt;/remarks&gt;
        public string GetContactName(ContactSgm sgm) {
            var concatChar = Separator.FirstOrDefault();
            return new[] { sgm.Surname, sgm.GivenName }.ConcatIfNotEmpty(concatChar.ToString());
        }
        #endregion
    }
    #endregion
 
}

and then this convertor was registered in the database (ShowNamesBy table). As a result newly created contacts where full name is Test 1 2 had the first (Test) and last (1) name only while middle name was skipped.

Hi Oleg Drobina,

Could you share with me the record registered in the database table ShowNamesBy for a separate assembly pkg?

Hence, in case the site receives a Contact "Name": "Federica Rose Cattani" I don't want to "lose" the "MiddleName" but I want it to be concat to the "Surname". That's what I did in the converter shared. 

Federica,

 

Hi,

 

Sorry, I guess I was wrong with the package. Tested the same approach today and it didn't work until I unchecked the "Compile into separate assembly" checkbox in the package settings. So in your case you need to also do the same to make the converter work.

 

The insert was:

INSERT INTO ShowNamesBy (Name, Separator, Converter) VALUES (N'UsrCustomContactFieldConverter', N' .,;', N'Terrasoft.Configuration.UsrCustomContactFieldConverter');

 

Oleg Drobina,

Ok! In my case I can't "undo" the separate assembly since it contains customer's implementation and separate assembly it's way faster to compile.

The actual possible workaround would be to implement a second package without separate assembly containing only the custom ContactGsFieldConverter, am I correct?

Hello, some conclusion regarding this, in Spain we have the same problem with names than in Italy

Show all comments

Is it possible to trigger process's signal from backend (C# code)? 

For example, I modify record of schema in C# code and I want process to become triggered when the record was modified.

I have tried to make it as shown on a picture but it didn`t work. It worked only if the record was changed from UI side.

I would be very appreciated for knowledgebase link concerning this question.

Thank you in advance.

Like 0

Like

3 comments

What does the C# code look like that you're using to trigger it?

Can you provide more details on your custom code since I've created a simple method in the Account object that modifies a contact in the process inside the Account object (using onInserted method):

var contactEntity = UserConnection.EntitySchemaManager.GetInstanceByName("Contact");
			var contactRecord = contactEntity.CreateEntity(UserConnection);
			contactRecord.FetchFromDB("Id", new Guid("9c4111d2-cbf1-49f2-8c2b-00c6a82547c0"));
			contactRecord.SetColumnValue("Name", "From the code");
			contactRecord.Save();

and it triggers the business process with the start signal that is executed when the Contact name is modified. Maybe your logic is not modifying the record in the manner that should trigger the business process.

UsrContactSaved - the process on Contact schema that triggers when Contact record is saved ContactUtilities.ChangeContactStatus - the method that should change Status on schema Contact (this method is scheduled by job)

UsrOnContactStatusChanged - the process that should be triggered when the Status field in Contact schema was changed (but it doesn`t trigger)

So, when UsrContactSaved process triggered, there is a code in it that schedule a job that must call ContactUtilities.ChangeContactStatus in 10 minutes. After 10 minutes triggers ContactUtilities.ChangeContactStatus method and it changes Status via method in C# code.

In conclusion, status changed but UsrOnContactStatusChanged didn`t work. Expected result: UsrOnContactStatusChanged has to be triggered after work of UsrChangeContactStatusProcess process.

Show all comments