Skip to content

Instantly share code, notes, and snippets.

@brianweet
Created April 29, 2020 10:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brianweet/e5229dd34aed66875a7db39babb607b3 to your computer and use it in GitHub Desktop.
Save brianweet/e5229dd34aed66875a7db39babb607b3 to your computer and use it in GitHub Desktop.
Implementing a custom reporting service for Episerver commerce
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; }
}
}
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
};
}
}
}
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