EPiServer Commerce generating a custom order number

  • May 22, 2017
  • EPiServer
  • EPiServer Commerce
  • |

When developing eCommerce sites it's quite common to create a custom order number instead of the default one that EPiServer generates. With some payment providers, it's even necessary to generate an order number before a purchase order in EPiServer is created. By default, the order number is generated the same time a purchase order is created. In this blog, I'll explain how you can generate your own custom order number with the new order API. 

The order number format that EPiServer generates by default is PO1119260. We can split this format in chunks. It always starts with 'PO' then the order group id and lastly a random number between 100 and 999. Generating a custom order number can be accomplished by creating a class which inherits from IOrderNumberGenerator. To make sure EPiServer uses the custom implementation instead of the default one you should also register it with StructureMap. The IOrderNumberGenerator interface is part of the new order API. If you're using an older version of Commerce, you can create a method and define that on the OrderNumberMethod property of the Cart class.

Below the default implementation of the IOrderNumber interface.

    public class DefaultOrderNumberGenerator : IOrderNumberGenerator
    {
        public DefaultOrderNumberGenerator()
        {
        }

        public string GenerateOrderNumber(IOrderGroup orderGroup)
        {
            int num = (new Random()).Next(100, 999);
            return string.Format("PO{0}{1}", orderGroup.OrderLink.OrderGroupId, num);
        }
    }

As you can see it's a simple implementation for generating an order number. As I already explained, in some occasions you need to generate an order number before the purchase order is created. This can be done by creating your own implementation of the IOrderNumberGenerator and save the generated order number in the dictionary property on the cart. The first time this method is called the order number is generated and saved on the cart. When EPiServer creates a purchase order this method is called again internally. Now,  the order number is retrieved from the cart and returned.

    public class CustomOrderNumberGenerator : IOrderNumberGenerator
    {
        private string GenerateDefaultOrderNumber(IOrderGroup orderGroup)
        {
            int num = (new Random()).Next(100, 999);
            return string.Format("PO{0}{1}", orderGroup.OrderLink.OrderGroupId, num);
        }

        public string GenerateOrderNumber(IOrderGroup orderGroup)
        {
            var orderNumberField = orderGroup.Properties[Constants.CartOrderNumberTempField];

            if (string.IsNullOrEmpty(orderNumberField?.ToString()))
            {
                var orderNumber = GenerateDefaultOrderNumber(orderGroup);

                orderGroup.Properties[Constants.CartOrderNumberTempField] = orderNumber;

                return orderNumber;
            }
            return orderNumberField.ToString();
        }
    }

When creating a NuGet package and one of the requirements is to create an order number before a purchase order is created you need to keep in account that people that are using the package might want to create their own order number. The code snippet above will not work since it would be impossible for users to generate their own order number. The decorator pattern can solve this issue, below another implementation of the IOrderNumberGenerator interface.

    public class OrderNumberGeneratorDecorator : IOrderNumberGenerator
    {
        private readonly IOrderNumberGenerator _inner;

        public OrderNumberGeneratorDecorator(IOrderNumberGenerator inner)
        {
            _inner = inner;
        }

        public string GenerateOrderNumber(IOrderGroup orderGroup)
        {
            var orderNumberField = orderGroup.Properties[Constants.CartOrderNumberTempField];
            
            if (string.IsNullOrEmpty(orderNumberField?.ToString()))
            {
                var orderNumber = _inner.GenerateOrderNumber(orderGroup);

                orderGroup.Properties[Constants.CartOrderNumberTempField] = orderNumber;

                return orderNumber;
            }
            return orderNumberField.ToString();
        }
    }

 

If you're familiar with the decorator design pattern you should recognize it in the above code snippet. An implementation of the IOrderNumberGenerator is passed in the constructor of this class. That one is stored in a local variable. When the GenerateOrderNumber method is called the implementation in the local variable is called to generate the order number. So this class is more a wrapper around the default implementation of the IOrderNumberGenerator. Not necessary have to be the default implementation of EPiServer, can also be a custom implementation. The last thing left is to register this class as a decorator with StructureMap.

       public void ConfigureContainer(ServiceConfigurationContext context)
        {            
            context.Services.Intercept<IOrderNumberGenerator>((locator, defaultService) =>
            {
                return new OrderNumberGeneratorDecorator(defaultService);
            });
        }

 

Comments