Search code examples
asp.netlayoutmacroscartkentico

Kentico 9 shopping cart ECommerceContext amounts insconsistent between macro and layout/.NET


I have a Kentico 9 ecommerce site. On one of the pages in the checkout process, I have a static HTML web part. Its Content field contains:

<ul class="cart-total-list text-center mb0">
    <li style="text-align: left;"><span>Products</span><span>{% FormatPrice(ECommerceContext.CurrentShoppingCart.TotalItemsPrice - ECommerceContext.CurrentShoppingCart.TotalItemsTax) #%}</span></li>
    <li style="text-align: left;"><span>Tax</span><span>{% FormatPrice(ECommerceContext.CurrentShoppingCart.TotalTax) #%}</span></li>
    <li style="text-align: left;"><span>Shipping</span><span>{% FormatPrice(ECommerceContext.CurrentShoppingCart.Shipping) #%}%}</span></li>
    <li style="text-align: left;"><span>Shipping Tax</span><span>{% FormatPrice( ECommerceContext.CurrentShoppingCart.TotalShipping - ECommerceContext.CurrentShoppingCart.Shipping) #%}</span></li>
    <li style="text-align: left;"><span>Total</span><span>{% FormatPrice(ECommerceContext.CurrentShoppingCart.TotalPrice) #%}</span></li>
</ul>

which, for example, produces the following output:

Products $58.30
Tax $4.30
Shipping $0
Shipping Tax $16.19
Total $74.49

This is incorrect. Products total should be $54 (Somehow $4.30 tax is included.) Shipping should be $15. Shipping tax should be $1.19. (Because shipping is zero, the full amount is displayed.) Also, sometimes the Tax displays zero but is included in the amount in my Products line.

Now if I instead render these values using C# in the web part's layout as follows:

<%@ Control Language="C#" AutoEventWireup="true" Inherits="CMSWebParts_Text_staticHTML"  Codebehind="~/CMSWebParts/Text/staticHTML.ascx.cs" %>
<%
  var cart = CMS.Ecommerce.ECommerceContext.CurrentShoppingCart;
%>
<asp:Literal ID="ltlText" runat="server" EnableViewState="false" />
<ul class="cart-total-list text-center mb0">
    <li style="text-align: left;"><span>Products</span><span><% Response.Write(cart.GetFormattedPrice(cart.TotalItemsPrice - cart.TotalItemsTax,false)); %></span></li>
    <li style="text-align: left;"><span>Tax</span><span><% Response.Write(cart.GetFormattedPrice(cart.TotalTax,false)); %></span></li>
    <li style="text-align: left;"><span>Shipping</span><span><% Response.Write(cart.GetFormattedPrice(cart.Shipping,false)); %></span></li>
    <li style="text-align: left;"><span>Shipping Tax</span><span><% Response.Write(cart.GetFormattedPrice(cart-TotalShipping - cart.Shipping,false)); %></span></li>
    <li style="text-align: left;"><span>Total</span><span><% Response.Write(cart.GetFormattedPrice(cart.TotalPrice,false)); %></span></li>
</ul>

I get the following values:

Products $54.00
Tax $4.30
Shipping $15.00
Shipping Tax $1.19
Total $74.49

These are exactly what I expected.

Why the differences? I can certainly stick with the latter approach, but I am concerned that something is broken and may have future side-effects.


UPDATE: I went with a custom macro field instead of a custom macro method. I just added the following class to my old_app_code folder. (because I have a pre-compiled web application) It appears to work as desired.

using System;
using CMS.Base;
using CMS.MacroEngine;
using CMS.EventLog;

[MacroLoader]
public partial class CMSModuleLoader
{
    /// <summary>
    /// Attribute class for registering custom macro extensions.
    /// </summary>
    private class MacroLoaderAttribute : CMSLoaderAttribute
    {
        private const string EVENT_SOURCE = "MacroLoaderAttribute";
        private const string EVENT_CODE = "EXCEPTION";

        /// <summary>
        /// Called automatically when the application starts.
        /// </summary>
        public override void Init()
        {
            MacroContext.GlobalResolver.SetNamedSourceDataCallback("CartShipping", CartShipping);
            MacroContext.GlobalResolver.SetNamedSourceDataCallback("CartTotalItemsTax", CartTotalItemsTax);
        }

        private object CartShipping(EvaluationContext context)
        {
            double retVal = 0d;
            try
            {
                retVal = CMS.Ecommerce.ECommerceContext.CurrentShoppingCart.Shipping;
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EVENT_SOURCE, EVENT_CODE, ex);
            }
            return retVal;
        }
        public static object CartTotalItemsTax(EvaluationContext context)
        {
            double retVal = 0d;
            try
            {
                retVal = CMS.Ecommerce.ECommerceContext.CurrentShoppingCart.TotalItemsTax;
            }
            catch (Exception ex)
            {
                EventLogProvider.LogException(EVENT_SOURCE, EVENT_CODE, ex);
            }
            return retVal;
        }
    }
}

Now CartShipping and CartTotalItemsTax are directly available for use in macro expressions.


Solution

  • I can confirm the TotalItemsTax and Shipping properties are not registered for macros in ShoppingCartInfo object, therefore the macro engine will return 0 for them. This leads to incorrect calculations in macros, while in standard ascx code it works.
    Additional registration of the system object types properties for macros is not easily possible, so if you want to go with K# macros, I'd recommend to create a custom macro method (see https://docs.kentico.com/k9/macro-expressions/extending-the-macro-engine/registering-custom-macro-methods), where you would call the standard API in C# (e.g. properties of the ECommerceContext.CurrentShoppingCart) with all the properties available. You can even return the resulting value (subtraction of tax from total) directly instead of doing it in the markup or transformation.

    Hope this helps...