Extend Marketing Foundation: Add an Award

Out of the box uCommerce comes with a number of handy discount types or awards as they are called in uCommerce, e.g. % off order line total, % off order total, fixed amount off, etc., but you can also add your own if you have a specific requirement uCommerce doesn't meet out of the box.

Awards

An award is responsible for modifying the order or order lines, e.g. adding a discount, new order line, or something else.

The award is triggered upon successful evaluation of the Act targets configured for a campaign item.

Awards are automatically applied to the order in the basket pipeline. Specifically by the Basket.ApplyAwards task, which is executed whenever you run the pipeline:

UCommerce.Api.TransactionLibrary.ExecuteBasketPipeline();

It's important to mention that whenever the basket pipeline is executed Marketing Foundation will remove any existing discounts on the order and re-apply everything from scratch. This is done to ensure that discounts no longer applicable to the order are removed.

Free Gift Award

As an example for this extension we're going to create a "free gift"-award, which will support a common merchandising technique: Offer a free product if a customer buys another product.

The "Free gift"-award is different from BOGOF, which is supported out of the box, because it adds a completely new item to the basket rather than adjusting price levels.

Implement Award

To implement the behavior we want from our award we need to implement the IAward interface, which has a single method Apply. The Apply method receives the order and order lines, which satisfied the targets of the campaign item and thus triggered the award.

For example, if you target a specific product on the order, the Apply method will receive the order and the order lines, which have that particular product associated. If the product is not present on the order the targets are not satisfied and thus the award will not be triggered.

namespace UCommerce.Marketing.Awards
{
/// <summary>
/// Responsible for applying discounts to orders and order lines
/// whenever the <see cref="IOrderLineTarget">order line targets</see>
/// or <see cref="IPurchaseOrderTarget"/> for a <see cref="CampaignItem"/> are satisfied.
/// </summary>
public interface IAward : IEntity
{
/// <summary>
/// Applies discounts to the order, order lines, or both.
/// </summary>
/// <param name="order">Order which satisfied the targets.</param>
/// <param name="orderLines">
/// Order lines which satifies the targets,
/// might be a subset of the order lines on the order
/// </param>
void Apply(PurchaseOrder order, IList<OrderLine> orderLines);
}
}

uCommerce already has an implementation of IAward you can inherit called Award. You want to inherit this class if you want uCommerce to store award data for you. If you want to deal with data access yourself you can implement IAward directly. More on this in the "Store Award" section of this article.

Free Gift Award Implementation

For our particular case we do not need order context for the Apply method because we're generating a completely new order line to hold the free gift.

Because Marketing Foundation will re-apply awards whenever the basket pipeline is executed, we need to make sure that we do not keep generate order lines once the award was already applied.

So the implementation of FreeGiftAward ends up like this:

/// <summary>
/// Adds a new order line to the order with the specified
/// product on it and a unit price of zero.
/// </summary>
/// <remarks>
/// Will check for existing generated order lines with the
/// SKU in question and only add the new order line if one
/// does not exist.
/// </remarks>
public class FreeGiftAward : Award
{
/// <summary>
/// Identifier of the product family to add. May not be null.
/// </summary>
public string Sku { get; set; }
/// <summary>
/// Identifier of the product variant to add. Can be null.
/// </summary>
public string VariantSku { get; set; }
/// <summary>
/// Adds a new order line to the order,
/// only if the product is not present on a generate order line.
/// </summary>
/// <param name="order"></param>
/// <param name="orderLines"></param>
public override void Apply(PurchaseOrder order, IList<OrderLine> orderLines)
{
// Check for generated order lines using dynamic order properties.
// Get order lines directly from order to ensure that we look at
// all order lines, not just the ones that satisfied the campaign
// targets.
if (AnyGeneratedOrderLinesWithThisFreeGift(order.OrderLines)) return;
// Look up SKU
var freeGiftProduct = Product.SingleOrDefault(
x => x.Sku == this.Sku && x.VariantSku == this.VariantSku);
// Guard against missing product
if (freeGiftProduct == null) return;
var priceGroup = SiteContext.Current.CatalogContext.CurrentPriceGroup;
// Free gift is not on the order so let's create a new order line to hold it.
// The customer might have one these items in the basket
// so we create a new order line to hold this one.
var generatedOrderline = order.AddProduct(
priceGroup,
freeGiftProduct,
quantity: 1,
addToExistingLine: false);
// Indicate the order line is generated.
generatedOrderline["_generated"] = "true";
// Now add a discount to new orderline to
// make it free.
var discount = new Discount
{
CampaignName = CampaignItem.Campaign.Name,
CampaignItemName = CampaignItem.Name,
Description = Name,
AmountOffTotal = generatedOrderline.Price + generatedOrderline.VAT
};
generatedOrderline.AddDiscount(discount);
order.AddDiscount(discount);
}
private bool AnyGeneratedOrderLinesWithThisFreeGift(IEnumerable<OrderLine> orderLines)
{
return orderLines.Any(
x => x.Sku == this.Sku
&& this.VariantSku == x.VariantSku
&& x.OrderProperties.Any(y => y.Key == "_generated" && y.Value == "true"));
}
}

You'll notice the code using a couple of properties Sku and VariantSku to figure out which product to use for the order line.

Where's this information coming from?

Award Configuration UI

To enable our new award to appear in the back-end of uCommerce we need a UI to configure it so store managers can interact with it.

To do that we need a new UserControl, which implements IConfigurable to provide access to the award by creating new instances of it.

To know which marketing campaign and campaign item the award is being set up for it will need to inherit ViewEnabledControl<IEditCampaignItemView>, which will give you access to the Campaign and CampaingItem properties.

The Save method employed above is inherited from the Award class and stores the information about your award.

But how does it know to do this?

Store Award

With the award properly set up we need to store it: We'll need a mapping and a new table to support this. Because our award inherits Award uCommerce will know how to instantiate it once stored in database and mapped properly.

uCommerce comes with the ability to set up custom data mappings to store custom data through the data layer
of the application.

uCommerce is a multi-lingual platform as thus you're also able to specify how the new award should be displayed in others language. You can do so by adding new entries to the uCommerce_EntityUiDescription table and link the entries ot the EntityUi entry created above.

Summary

With this in place you now have a new award ready to use in the uCommerce back-end, which will work with all of the existing awards and targets, which come out of the box.

This means that our free gift award can be used in combination with a product target to support "buy one product and get another free" or in combination with a promocode target to support "supply promocode to get a free product", e.g. a product voucher type scenario.

Of course the award to be configured many times for a single campaign item so you can trigger multiple results for the same discount.