Flag Field Change for After Save Event

AptifyDevelopers May 19, 2024

At the February 2024 Aptify Developer RoundTable, a great question came up: How to I send a message when a field value changes on a record?

Scenario

Here’s our scenario, we want to send a magic link to members who’s address gets flagged as a Bad Address from the Person record, so that they can update the address without needing to login.

Event Flow

Ok, next lets break down the event flow that is going to occur:

  1. From a Person record, add the addresses “Bad Address” field is set to true (to simply things we’ll just do business address).
  2. On the Person’s Before Save, check if the Bad Address was set from false to true. If do, set a Temp Field that Bad Address has changed to true.
  3. On the Person’s After Save, check for the Temp Field exists and if does, send message.
  4. The Message Run will use a message template uses a Scripted Message Part to retrieve the necessary details to create an html anchor with a href value that includes a query string value of the necessary encrypted value for the magic link.

And here are all the records we will create to do this:

Message Part and Template

Check out my previous article Send Magic Link in Message for creating the message part and template with a magic link.

Process Pipeline

Little Background First

When you save a record in Aptify you would think that within the “AfterSave” event, you should be able to evaluate if a fields OldValue equals something and the CurrentValue equals something to know if was changed, right? Well. No. In Aptify, the Generic Entity (record) does store two states of the record, the previous “old” state and the current state. What happens when you save a record, there is the internal “Save” event that does the commit to the database. Ok, so now that the data been committed the GE record essentially “reloads” itself as if you were just opening the record. This means the “old” and “current” state are now the same as it was just pulled from the DB. Now the AfterSave event execute.

This is why if you want to persist a field change to AfterSave you need to implement your own system. The simplest way to do this is using the Temporary Field feature of the GE. The GE has two methods for setting a fields value:

geRecord.SetValue("FieldName", value)
geRecord.SetAddValue("FieldName", value)

The difference is that with .SetValue, if the FieldName doesn’t exist, Aptify either throws and error or just doesn’t do anything (it’s been a long while since I’ve run code that this has occurred with). With .SetAddValue, if the FieldName doesn’t exist Aptify will add the field in memory and, the field will be included if the GE is serialized (This passing the GE to the Async Server).

When using Temporary Fields, I highly suggest you come up with a name unique naming scheme for you organization so that they will never conflict with actual field names. What has worked perfectly for me over the years is: _x{DescriptiveName}_{OrgAcronym}, for this example we’ll use: _xBadAddress_sa.

Ok, now let create the Event Definitions.

Event Definitions

Wait, wait, wait! Event definition? Why would we create a event definition instead of doing Aptify 101, and creating a simple Before Save Event Handler that executes a Process Flow?? That is an excellent question and very valid point. You 100% totally can do that and there is really good reasons to do that:

Ok, what if I want to go more advanced and ask “Why not a GE Plug-in”? Again, 100% valid option. GE plug-ins though required you code and override the base GE class. If it makes sense for you org, then I’m all for it.

Ok, why did I choose to create an Event Definition? Well, I just wanted to showcase this way of using an Event Definition. Were going to create a BeforeSave and an AfterSave event definition specifically for the Persons entity. No there is a very critical thing you have to know. These event definition will execute, regardless if an Event Handler uses them or not. This actually is for a lot of the OTB base events. This means No you can not flip it on and off. Also, you only have the option of writing it as a VB script and any updates are immediately live. Please, please, please, defensive programming, catch manage your errors/exceptions and remember, if the event fails, depending on the event, it can prevent the save or the event sequence from continuing. So at the end of the day, you might prefer the normal Event Handler route instead. But overall the process is the same.

Before Save Definition

FieldValue
NamePersons Before Save - Set Temp Fields_sa
CategoryUse the one that make most sense for you org
DescriptionA custom event handler that is used to set custom Temporary Fields to be utilized by AfterSave events that take in the GE Object.
Event ScopeEntity
Base EventBeforeSave
Firing SequencePost-Process
EntityPersons
Scriptsee below
' Exception Publish Only for Testing
Aptify.Framework.ExceptionManagement.ExceptionManager.Publish(
	New System.Exception(
		String.Format("Persons Before Save - Set Temp Fields_sa - IsDirty: {0} - Value: {1} - CBool: {2}", geRecord.Fields.Item("BusinessBadAddress").GetOldValue <> geRecord.GetValue("BusinessBadAddress"), geRecord.GetValue("BusinessBadAddress"), CBool(geRecord.GetValue("BusinessBadAddress")))
	)
)

' Clear Temp Field
' We want to make sure we don't possibly start an endless loop as remember AfterSave events might
' update values and re-save the GE
geRecord.Fields.Remove("_xBadAddress_sa")

Try
	' Business Bad Address has changed and is currently set to True
	If geRecord.Fields.Item("BusinessBadAddress").GetOldValue <> geRecord.GetValue("BusinessBadAddress") AndAlso CBool(geRecord.GetValue("BusinessBadAddress")) Then
		' Set Temp Field
		geRecord.SetAddValue("_xBadAddress_sa", True)
	End If
Catch ex As System.Exception
	' Publish Exception
	Aptify.Framework.ExceptionManagement.ExceptionManager.Publish(
		New System.Exception("Persons Before Save - Set Temp Fields_sa Event Definition Exception", ex)
	)
End Try

' Always return true so Aptify can keep functioning
oResult.Success = True

After Save Definition

FieldValue
NamePersons After Save - Bad Address Flagged_sa
CategoryUse the one that make most sense for you org
DescriptionA custom event handler that looks for the custom Bad Address flag.
Event ScopeEntity
Base EventAfterSave
Firing SequencePost-Process
EntityPersons
Scriptsee below
' Exception Publish Only for Testing
Aptify.Framework.ExceptionManagement.ExceptionManager.Publish(
	New System.Exception(
		String.Format("Persons After Save - Bad Address Flagged_sa - IndexOf: {0} - Value: {1} - CBool: {2}", geRecord.Fields.IndexOf("_xBadAddress_sa"), geRecord.GetValue("_xBadAddress_sa"), CBool(geRecord.GetValue("_xBadAddress_sa")))
	)
)

Try
	' Set the result to if the Temp field exists and Business Bad Address is true
	oResult.Success = geRecord.Fields.IndexOf("_xBadAddress_sa") >= 0 AndAlso CBool(geRecord.GetValue("BusinessBadAddress"))
Catch ex As System.Exception
	' Publish exception error
	Aptify.Framework.ExceptionManagement.ExceptionManager.Publish(
		New System.Exception("Persons After Save - Bad Address Flagged_sa Event Definition Exception", ex)
	)

	' Return False so that nothing executes and Aptify can keep working
	oResult.Success = False
End Try

Process Flow that Sends Message

There are a couple different ways to send a message from Aptify, I cheated a little and just did a simple Rule Script to grab the details needed to create a message run.

FieldValue
NameSend Bad Address Notification w/Update Magic Link_sa
DescriptionSends an email to the person to update their address with a magic link so they don’t need to login.
CategoryUse the one that make most sense for you org
Event ScopeEntity
Base EventAfterSave
Firing SequencePost-Process
EntityPersons

Input Properties

NameTypeIs Required
PersonIDIntegerTrue

Result Codes

CodeDescriptionIs Success
SuccessSuccessTrue
FailedFailedTrue

Rule Script

FieldValue
NameSend Bad Address Notification w/Update Magic Link - Execution_sa
Scriptsee below
' Set default result code
Dim sResult As String = "SUCCESS"

Try
	' Exception Publish Only for Testing
	Aptify.Framework.ExceptionManagement.ExceptionManager.Publish(
		New System.Exception(
			String.Format("Send Bad Address Notification w/Update Magic Link - To: {0}", oProperties.GetProperty("PersonID"))
		)
	)

	' Get Necessary Details
	Dim dt As System.Data.DataTable

	Dim sqlParams() As System.Data.SqlClient.SqlParameter = {
		oDataAction.GetDataParameter("@PersonID", System.Data.SqlDbType.Int, oProperties.GetProperty("PersonID"))
	}

	With New System.Text.StringBuilder
		.Append("SELECT mt.ID [TemplateID], mt.Name, mt.DefaultMessageSystemID [SystemID], mt.DefaultMessageSourceID [SourceID], mt.Subject, mt.HTMLBody, mt.ToType, mt.ToValue, p.ID [PersonID]")
		.Append("FROM vwMessageTemplates mt ")
		.Append("INNER JOIN Person p ON p.ID = @PersonID ")
		.Append("WHERE mt.Name = 'Bad Address w/Update Magic Link_sa'")

		dt = oDataAction.GetDataTableParametrized(.ToString, System.Data.CommandType.Text, sqlParams)
	End With

	If dt Is Nothing OrElse dt.Rows.Count <> 1 Then
		Throw New System.Exception(String.Format("Unable to retrieve Bad Address w/Update Magic Link_sa Message Template Data for person id: {0}", oProperties.GetProperty("PersonID")))
	End If

	Dim dr As System.Data.DataRow = dt.Rows(0)

	' Create Message Run
	Dim oApp As New Aptify.Framework.Application.AptifyApplication(oDataAction.UserCredentials)
	Dim oGE As Aptify.Framework.BusinessLogic.GenericEntity.AptifyGenericEntityBase

	oGE = oApp.GetEntityObject("Message Runs", -1)
	With oGE
		' This is the most minimal needed info the Message Run GE needs to send a message
		.SetValue("MessageSystemID", dr("SystemID"))
		.SetValue("MessageSourceID", dr("SourceID"))
		.SetValue("MessageTemplateID", dr("TemplateID"))

		.SetValue("ToType", dr("ToType"))
		.SetValue("ToValue", dr("ToValue"))

		.SetValue("SourceType", "ID String")
		.SetValue("IDString", dr("PersonID"))
		.SetValue("ApprovalStatus", "Approved")

		If Not .Save(False) Then
			Throw New System.Exception(.LastError)
		End If
	End With
Catch ex As System.Exception
	Aptify.Framework.ExceptionManagement.ExceptionManager.Publish(ex)
	sResult = "FAILED"
End Try

oResultCode.Value = sResult
Action Map
CodeIs SuccessActionProcess Flow Result Code
SUCCESSTrueEnd ProcessSuccess
FAILEDTrueEnd ProcessFailed

Shameless Upsell

If your interested in doing more transactional email based on field field changes easily without needing the scripting of Event Definitions, Event Handlers and Process Flow, I highly encourage you to reach our to PerByte, Inc and ask them about their Advanced Messaging System (Message Triggers).

Yes, this is a shameless plug/upsell for PerByte. I was part of the team for 5yr, and the Message Triggers are awesome!

Aptify Clients

Continue the conversation on the Community Forums.