Skip to main content

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!

Thank you for sharing this with the community @Naveen Boga!


Great work Naveen.


Thank you Naveen for sharing this,

It is very helpful.


@Naveen Boga great writeup. Thank you for sharing this.


This is great for triggering custom code.

But triggering shipment confirmation email is probably not the best example since it can just be triggered with standard Business Events (no code required).

I wonder what are better use cases?


Hi, @Ahmed  Thanks for sharing the feedback.

Yes, we do not require the custom code to trigger email notifications.

Alongside the email notification, we have the opportunity to invoke a custom code that will enable us to execute personalized logic and perform necessary insertions or updates on data within our custom tables. This added capability allows us to seamlessly integrate our unique requirements and enhance the overall functionality of our system. 

Hope this clarifies.

 


This is great. Thank you @Naveen Boga for this post! I have a question. Is it possible to save data to DB using this method? I’m calling an external API using a custom subscriber that triggers when a new Stock Item is created. The API return a string that I need to save. What would be the best way to save the external data into the Stock Item?


@aneeshantal  Yes, we can save the data. 

Again, it is always recommended not to have huge code/complex code in custom subscriber.


Reply