Implementing a custom reporting service for Episerver commerce
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using CsvHelper.Configuration.Attributes; | |
using System; | |
namespace EPiServer.Reference.Commerce.Site.Infrastructure.Reporting | |
{ | |
public class OrderLineItemRecord | |
{ | |
[Name("Line Item Id")] | |
public int LineItemId { get; set; } | |
[Name("Line Item Code")] | |
public string LineItemCode { get; set; } | |
[Name("Display Name")] | |
public string DisplayName { get; set; } | |
[Name("Placed Price")] | |
public decimal PlacedPrice { get; set; } | |
public decimal Quantity { get; set; } | |
[Name("Extended Price")] | |
public decimal ExtendedPrice { get; set; } | |
[Name("Entry Discount Amount")] | |
public decimal EntryDiscountAmount { get; set; } | |
[Name("Order Discount Amount")] | |
public decimal OrderDiscountAmount { get; set; } | |
[Name("Sales Tax")] | |
public decimal SalesTax { get; set; } | |
[Name("Order Group Id")] | |
public int OrderGroupId { get; set; } | |
[Name("Order Created")] | |
public DateTime OrderCreated { get; set; } | |
[Name("Order Number")] | |
public string OrderNumber { get; set; } | |
[Name("Customer Id")] | |
public Guid CustomerId { get; set; } | |
[Name("Customer Name")] | |
public string CustomerName { get; set; } | |
[Name("Market Id")] | |
public string MarketId { get; set; } | |
public string Currency { get; set; } | |
[Name("My Custom Prop")] | |
public string MyCustomProp { get; set; } | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System.Collections.Generic; | |
using System.Linq; | |
using EPiServer.Commerce.Order; | |
using EPiServer.Commerce.Reporting.Order.ReportingModels; | |
namespace EPiServer.Reference.Commerce.Site.Infrastructure.Reporting | |
{ | |
public class OrderReportingMapper | |
{ | |
private readonly IOrderRepository _orderRepository; | |
public OrderReportingMapper(IOrderRepository orderRepository) | |
{ | |
_orderRepository = orderRepository; | |
} | |
public IEnumerable<OrderLineItemRecord> Map(IEnumerable<LineItemReportingModel> orders) | |
{ | |
return orders.Select(MapItem); | |
} | |
private OrderLineItemRecord MapItem(LineItemReportingModel item) | |
{ | |
var order = _orderRepository.Load<IPurchaseOrder>(item.OrderGroupId); | |
var shipment = order.GetFirstShipment(); | |
var address = shipment.ShippingAddress; | |
return new OrderLineItemRecord | |
{ | |
LineItemId = item.LineItemId, | |
LineItemCode = item.LineItemCode, | |
DisplayName = item.DisplayName, | |
PlacedPrice = item.PlacedPrice, | |
Quantity = item.Quantity, | |
ExtendedPrice = item.ExtendedPrice, | |
EntryDiscountAmount = item.EntryDiscountAmount, | |
OrderDiscountAmount = item.OrderDiscountAmount, | |
SalesTax = item.SalesTax, | |
OrderGroupId = item.OrderGroupId, | |
OrderCreated = item.OrderCreated, | |
OrderNumber = item.OrderNumber, | |
CustomerId = item.CustomerId, | |
// Change existing fields | |
CustomerName = | |
string.IsNullOrWhiteSpace(item.CustomerName) | |
? $"{address.FirstName} {address.LastName}" | |
: item.CustomerName, | |
MarketId = item.MarketId, | |
Currency = item.Currency, | |
// Add additional fields | |
MyCustomProp = order?.Properties["MyCustomProp"]?.ToString() ?? string.Empty | |
}; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using EPiServer.Commerce.Reporting.Order; | |
using EPiServer.Commerce.Reporting.Order.Internal.DataAccess; | |
using EPiServer.Commerce.Reporting.Order.ReportingModels; | |
using EPiServer.Core; | |
using EPiServer.DataAccess; | |
using EPiServer.Framework.Blobs; | |
using EPiServer.Security; | |
using EPiServer.Web; | |
using System; | |
using System.Collections.Generic; | |
using System.Globalization; | |
using System.IO; | |
using System.IO.Compression; | |
using CsvHelper; | |
namespace EPiServer.Reference.Commerce.Site.Infrastructure.Reporting | |
{ | |
// Based on DefaultReportingService | |
public class ReportingService : IReportingService | |
{ | |
private readonly IBlobFactory _blobFactory; | |
private readonly IContentRepository _contentRepository; | |
private readonly IUrlSegmentGenerator _urlSegmentGenerator; | |
private readonly ReportingDataLoader _reportingDataLoader; | |
private readonly OrderReportingMapper _orderReportingMapper; | |
public ReportingService( | |
IBlobFactory blobFactory, | |
IContentRepository contentRepository, | |
IUrlSegmentGenerator urlSegmentGenerator, | |
ReportingDataLoader reportingDataLoader, | |
OrderReportingMapper orderReportingMapper) | |
{ | |
_blobFactory = blobFactory; | |
_contentRepository = contentRepository; | |
_urlSegmentGenerator = urlSegmentGenerator; | |
_reportingDataLoader = reportingDataLoader; | |
_orderReportingMapper = orderReportingMapper; | |
} | |
/// <inheritdoc /> | |
public virtual ContentReference ExportOrderDataAsCsv( | |
DateTime fromDate, | |
DateTime toDate) | |
{ | |
var reportingMediaData = _contentRepository.GetDefault<ReportingMediaData>(CommerceReportingFolder.ReportingRoot); | |
var blob = _blobFactory.CreateBlob(reportingMediaData.BinaryDataContainer, ".commercereport"); | |
var str = "OrderData-from-" + fromDate.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture) + "-to-" + toDate.ToString("dd-MMM-yyyy", CultureInfo.InvariantCulture); | |
using (var stream = blob.OpenWrite()) | |
using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Create, false)) | |
using (var entryStream = zipArchive.CreateEntry(str + ".csv").Open()) | |
using (var streamWriter = new StreamWriter(entryStream)) | |
using (var csv = new CsvWriter(streamWriter, CultureInfo.InvariantCulture)) | |
{ | |
// Use default functionality to load order report data | |
var data = _reportingDataLoader.GetReportingData( | |
fromDate.ToUniversalTime(), | |
toDate.ToUniversalTime() | |
); | |
// Use a custom mapper and write csv | |
csv.WriteRecords(_orderReportingMapper.Map(data)); | |
} | |
reportingMediaData.BinaryData = blob; | |
reportingMediaData.Name = str + ".zip"; | |
DeleteDuplicatedReport(reportingMediaData.Name); | |
return _contentRepository.Save(reportingMediaData, SaveAction.Publish, AccessLevel.NoAccess); | |
} | |
/// <inheritdoc /> | |
public virtual IEnumerable<SalesByDayReportingModel> GetAggregatedReportingData() | |
{ | |
return _reportingDataLoader.GetAggregatedReportingData((decimal)TimeZoneInfo.Local.GetUtcOffset(DateTime.UtcNow).TotalMinutes); | |
} | |
private void DeleteDuplicatedReport(string reportName) | |
{ | |
if (!(_contentRepository.GetBySegment(CommerceReportingFolder.ReportingRoot, _urlSegmentGenerator.Create(reportName), CultureInfo.InvariantCulture) is IContentMedia bySegment)) | |
return; | |
_blobFactory.Delete(bySegment.BinaryDataContainer); | |
_contentRepository.Delete(bySegment.ContentLink, true, AccessLevel.NoAccess); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment