Introduction
I am excited to share with you the Acumatica Business Events and their powerful capabilities for automating business processes through configuration. Typically, Business Events are utilized to trigger various actions such as Email notifications, Import Scenarios, and Mobile Push Notifications. However, during my recent exploration, I came to know that we can also invoke custom code alongside these notifications.
In one of my projects, I encountered a unique requirement that I would like to share with you today. The scenario involved sending an email notification while simultaneously executing custom code to update specific details in custom tables. To achieve this, we can leverage the flexibility of Business Events by incorporating our logic within a custom subscriber. By doing so, the Acumatica framework seamlessly handles the functionality, eliminating the need to write the logic within the Confirm Shipment button or other areas.
By taking advantage of Acumatica Business Events, we can streamline our business processes and enhance automation, all while enjoying the convenience of configuration-based customization.
Create Custom Subscriber with Code
Below is the code snippet to create the Business Event Custom subscriber.
using PX.BusinessProcess.DAC;
using PX.BusinessProcess.Event;
using PX.BusinessProcess.Subscribers.ActionHandlers;
using PX.BusinessProcess.Subscribers.Factories;
using PX.BusinessProcess.UI;
using PX.Common;
using PX.Data;
using PX.Data.BusinessProcess;
using PX.PushNotifications;
using PX.SM;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
namespace MyTestCustomSubscriber
{
public class CustomSubscriberEventAction : IEventAction
{
public Guid Id { get; set; }
public string Name { get; protected set; }
private readonly Notification _notificationTemplate;
public void Process(MatchedRoww] eventRows, CancellationToken cancellation)
{
string SOShipmentType = string.Empty;
string SOShipmentNbr = string.Empty;
string SOShipmentOperation = string.Empty;
var param = @eventRows.Select(
r => Tuple.Create<IDictionary<string, object>,
IDictionary<string, object>>(
r.NewRow?.ToDictionary(c => c.Key.FieldName, c => c.Value),
r.OldRow?.ToDictionary(c => c.Key.FieldName,
c => (c.Value as ValueWithInternal)?.ExternalValue ??
c.Value))).ToArray();
SOShipmentType = param.Select(x => x.Item1.Where(y => y.Key == "Document_ShipmentType").Select(c => Convert.ToString(c.Value)).FirstOrDefault()).FirstOrDefault();
SOShipmentNbr = param.Select(x => x.Item1.Where(y => y.Key == "Document_ShipmentNbr").Select(c => Convert.ToString(c.Value)).FirstOrDefault()).FirstOrDefault();
SOShipmentOperation = param.Select(x => x.Item1.Where(y => y.Key == "Document_Operation").Select(c => Convert.ToString(c.Value)).FirstOrDefault()).FirstOrDefault();
if (!string.IsNullOrEmpty(SOShipmentType) && !string.IsNullOrEmpty(SOShipmentNbr))
{
// ADD CUSTOM LOGIC HERE
}
}
public CustomSubscriberEventAction(Guid id, Notification notification)
{
Id = id;
Name = notification.Name;
_notificationTemplate = notification;
}
#region Transaction
#endregion
}
class CustomSubscriberHandlerFactory : IBPSubscriberActionHandlerFactoryWithCreateAction
{
public IEventAction CreateActionHandler(Guid handlerId, bool stopOnError, IEventDefinitionsProvider eventDefinitionsProvider)
{
var graph = PXGraph.CreateInstance<PXGraph>();
Notification notification = PXSelect<Notification, Where<Notification.noteID, Equal<Required<Notification.noteID>>>>.Select(graph, handlerId).AsEnumerable().SingleOrDefault();
return new CustomSubscriberEventAction(handlerId, notification);
}
public IEnumerable<BPHandler> GetHandlers(PXGraph graph)
{
return PXSelect<Notification, Where<Notification.screenID, Equal<Current<BPEvent.screenID>>, Or<Current<BPEvent.screenID>, IsNull>>>
.Select(graph).FirstTableItems.Where(c => c != null)
.Select(c => new BPHandler
{
Id = c.NoteID,
Name = c.Name,
Type = LocalizableMessages.CustomNotification
});
}
public void RedirectToHandler(Guid? handlerId)
{
var notificationMaint = PXGraph.CreateInstance<SMNotificationMaint>();
notificationMaint.Message.Current = notificationMaint.Notifications.Search<Notification.noteID>(handlerId);
PXRedirectHelper.TryRedirect(notificationMaint, PXRedirectHelper.WindowMode.New);
}
public string Type
{
get { return "CTTP"; }
}
public string TypeName
{
get { return LocalizableMessages.CustomNotification; }
}
public string CreateActionName
{
get { return "NewCustomNotification"; }
}
public string CreateActionLabel
{
get { return LocalizableMessages.CreateCustomNotification; }
}
public Tuple<PXButtonDelegate, PXEventSubscriberAttributee]> getCreateActionDelegate(BusinessProcessEventMaint maintGraph)
{
PXButtonDelegate handler = (PXAdapter adapter) =>
{
if (maintGraph.Events?.Current?.ScreenID == null)
return adapter.Get();
var graph = PXGraph.CreateInstance<SMNotificationMaint>();
var cache = graph.Caches<Notification>();
var notification = (Notification)cache.CreateInstance();
var row = cache.InitNewRow(notification);
row.ScreenID = maintGraph.Events.Current.ScreenID;
cache.Insert(row);
var subscriber = new BPEventSubscriber();
var subscriberRow = maintGraph.Subscribers.Cache.InitNewRow(subscriber);
subscriberRow.Type = Type;
subscriberRow.HandlerID = row.NoteID;
graph.Cachesstypeof(BPEventSubscriber)].Insert(subscriberRow);
PXRedirectHelper.TryRedirect(graph, PXRedirectHelper.WindowMode.NewWindow);
return adapter.Get();
};
return Tuple.Create(handler, new PXEventSubscriberAttributee]
{
new PXButtonAttribute
{
OnClosingPopup = PXSpecialButtonType.Refresh
}
});
}
}
public static class LocalizableMessages
{
public const string CustomNotification = "My Custom Subscriber";
public const string CreateCustomNotification = "My Custom Subscriber";
}
}
Custom Subscriber Configuration
Created a new Business Event with Trigger Condition i.e., When the shipment is changed to CONFIRMED status, this business event will be triggered.
Configure the Business Event with the following Subscribers, along with their respective details.
- Email Notification
- My Custom Subscriber
Email Notification:
When the shipment is confirmed, the system will automatically send a Shipment Confirmation Email to the user using the Shipment Confirmation notification template.
My Custom Subscriber:
This subscriber will trigger the custom code and execute the customized logic in the background. Please refer to the debug screenshot provided below.
Summary
This article discusses the powerful capabilities of Acumatica Business Events in automating business processes through configuration. It highlights that besides triggering actions like email notifications, import scenarios, and mobile push notifications, custom code can also be invoked alongside these notifications. The article shares a unique project requirement where an email notification was sent while simultaneously executing custom code to update specific details in custom tables. It emphasizes that by incorporating logic within a custom subscriber, the Acumatica framework seamlessly handles the functionality, eliminating the need to write logic within a specific button. The use of Acumatica Business Events allows for streamlined business processes, enhanced automation, and configuration-based customization.
Happy Coding!