Sitecore Commerce - Create and export Monthly reports
on14. January 2022.in categorySitecore
The Task
In our Commerce implmentation, a Customer will have an EventAttendee component attached to it when purchasing a on-demand video. In the component, there will be information about the video itself souch as ProductId, SKU, OrderId, etc, as well as the DateCompleted, the field that will determine weather the Customer should be included in the monthly report or not.
So when tasked with making a report including all the customers that consumed an on-demand video in a given month, we needed to solve two problems:
- How to query only customers that consumed an on-demand video in a given month. We need to get only the Customers that have a EventAttendee component, and within it DateCompleted set within the month a report is being generated for.
- How to schedule generating said report at the beginning of each month. We need a report automatically generated on the 1st of each month.
Getting the Customers
Instead of querying all the customers and iterating trough each one, we will be using Managed lists.
This is the abstracted code snippet that we use to update the DateCompleted field. Before persisting the changes, we will add a new entry to ListMembership component, making this Customer part of the said Managed list.
//code for updating DateCompleted field
//eventAttendeeComponent is our custom component
eventAttendeeComponent.DateCompleted = DateTime.Now;
//adding new entry to ListMembership component of a customer
//customer is of type Sitecore.Commerce.Plugin.Customers.Customer
var listComponent = customer.GetComponent<TransientListMembershipsComponent>();
var listName = "OnDemand-Complete-2022-01";
listComponent.Memberships.Add(listName);
Persisting the Customer commerce entity will create the OnDemand-Complete-2022-01 list if it did not exist previously.
Now all we need to do to get all the Customers that consumed an on-demand video in January of 2022 is to query this list:
var customersList = await this.Command<FindEntitiesInListCommand>().Process<Customer>(commerceContext, "OnDemand-Complete-2022-01", 0, Int32.MaxValue);
Scheduling the Report generation
Our task should be run every 1st day of each month, at a precise time we selected. To do so, first we considered using build-in tools we have on our disposal, Sitecore's built-in task scheduler and/or Commerce minions. None of which would satisfy our requirement because of the interval-based nature of the scheduled execution.
To get the better precision and flexibility in defining our task execution schedule, we decided to use SiteCron.
Along with installing the SiteCron NuGet package (be mindful of the version, we installed 3.5.0 for our Sitecore 9.2 installation) you will get a .zip Sitecore package to be installed in your Sitecore instance, whitch will give you SiteCron tasks templates, example tasks, paths and structure.
SiteCron Tasks will take three parameters:
- A Cron Expression (e.g: 0 46 15 * * ?) in format: Seconds Minutes Hours Day-of-Month Month Day-of-Week Year (year is optional)
- Assembly to be executed (specify class namespace, assembly)
- Optional list of parameters in the form of url parameteres (e.g. test=1&p=first&t=second)
So our SiteCron Task Sitecore item will look like this:
Cron expression - this will trigger each 1st of the month at 03:00 AM
0 0 3 1 1/1 ?
Assembly name and namespace - we created ScheduledTask Feature to store all the Jobs
ThinkingEngineersWeb.Feature.ScheduledTasks.Jobs.MonthlyReport, ThinkingEngineersWeb.Feature.ScheduledTasks
Task additional parameters - we are sending url and credentials for FTP server where we want our reports stored
url=ftp://XXX.XXX.XXX.XXX/reports&username=ourUser&password=ourPassword
So in our Sitecore XP project, in ScheduledTasks Feature, we create a class inherriting from Quartz.IJob and implementing Execute method
public class MonthlyReportTask : IJob
{
public void Execute(IJobExecutionContext context)
{
//getting the parameters from the Task
var dataMap = context.JobDetail.JobDataMap;
var rawParameters = dataMap.GetString(SitecronConstants.FieldNames.Parameters);
var parameters = WebUtil.ParseUrlParameters(rawParameters);
var taskInfo = new ReportTaskInfo
{
DateToQuery = new DateTimeOffset(new DateTime(DateTimeOffset.Now.Year, DateTimeOffset.Now.Month, 1).AddDays(-1)),
FtpRepositoryInfo = new FtpRepositoryInfoModel
{
FtpRepositoryUrl = parameters["url"],
FtpRepositoryUsername = parameters["username"],
FtpRepositoryPassword = parameters["password"]
}
};
//call our Commerce ServiceProxy command
ourCommerceProxyServices.GenerateMonthlyReport(taskInfo);
}
}
So this way we can schedule our report generation in SiteCron Task, it will trigger MonthlyReportTask that will in turn call our Commerce Command. We can also manualy trigger SiteCron Tasks by clicking "Execute SiteCron Job Now!" from their context-menu.
This has been implemented and tested on Sitecore Experience Commerce 9.2