Sorting OrderLines
How to sort OrderLines using the OrderTemplateExtender
When a cart template is rendered, the OrderLines are rendered through a GetLoop() iteration. Sorting of OrderLines goes fairly easy as long as you have actual fields on the OrderLine you can use for SortBy functionality. But if the sorting needs to be more complex, based on a set of rules, sorting becomes more of a hassle as there is no actual collection of OrderLine objects available. This is where the OrderTemplateExtender comes in handy.
OrderTemplateExtender
With the introduction of Razor (cshtml), the use of TemplateExtenders has been quite diminished. These extenders gives us the possibility to tamper the content before rendering, which made more sense back when templates were HTML, and less so now with Razor.
But in this particular scenario, the OrderTemplateExtender is of good use, as it holds the entire Order object, including OrderLines. The OrderTemplateExtender has two TemplateExtenderRenderingState: Before and After. Hooking up on the TemplateExtenderRenderingState.Before, we can now manipulate the order and OrderLines before rendering.
The scenario
In the example below we have some OrderLines with information of ParentLineId saved to custom OrderlineField, to indicate that they belong to a parent line. Had the OrderLines with a value in ParentLineId been of type 'ProductDiscount'/'Discount', the Dynamicweb API would have given us the functionality of nesting child-lines under parent lines: for free and automatically. Now we need to handle the sorting on our own:
using Dynamicweb.Ecommerce.Frontend;
using Dynamicweb.Ecommerce.Orders;
using Dynamicweb.Rendering;
using System.Collections.Generic;
using System.Linq;
namespace MyNamespace
{
/// <summary>
/// Ensure that all lines with a value in ParentLineId are nested under their respective parents.
/// </summary>
public class OrderTemplateExtender1 : OrderTemplateExtender
{
public override void ExtendTemplate(Template template)
{
if (RenderingState == TemplateExtenderRenderingState.Before)
{
var orderLineList = Order.OrderLines.ToList();
// Get list of sorted OrderLines to have children nested under their respective parent.
var sortedOrderLines = OrderlinesSorted(orderLineList);
// Clear current orderlines from the order.
Order.OrderLines.Clear();
// Apply the sorted orderlines.
foreach (var orderLine in sortedOrderLines)
{
Order.OrderLines.Add(orderLine);
}
}
}
/// <summary>
/// Returns a List of sorted OrderLines
/// </summary>
/// <param name="orderLines"></param>
/// <returns>List<OrderLines></returns>
private List<OrderLine> OrderlinesSorted(List<OrderLine> orderLines)
{
List<OrderLine> parents = new List<OrderLine>();
List<OrderLine> children = new List<OrderLine>();
List<OrderLine> sorted = new List<OrderLine>();
// Split OrderLines into...
foreach (var line in orderLines)
{
var parentId = line.GetOrderLineFieldValue("ParentLineId")?.Value;
if (string.IsNullOrEmpty(parentId))
{
// ..parents
parents.Add(line);
}
else
{
// ..and children
children.Add(line);
}
}
// Place children just after their respective parent
foreach (var parent in parents)
{
// Add each parent
sorted.Add(parent);
foreach (var child in children)
{
var parentId = child.GetOrderLineFieldValue("ParentLineId").Value;
if (parentId == parent.Id)
{
// For each parent, add related children
sorted.Add(child);
}
}
}
return sorted;
}
}
}