Optimizely Commerce Data Enrichment Cheat Sheet

VP, Enterprise Platforms
Valtech

november 24, 2017

We all know the feeling. Having to setup tax jurisdictions, warehouses, payment methods or shipping methods can be a tedious exercise, which quickly can be interfered by human mistakes if it isn’t applied consistently. Not everyone is aware that these data aspects easily can be scripted, hence automatically moved across databases used by folks within your development team and to the more stable environments such as Integration, Pre-Production and Production.

When would these scripts be executed?

We tend to rely on Optimizely’s Initialization mechanism to apply these data enrichment scripts.



[InitializableModule] [ModuleDependency(typeof(CommerceInitialization))] public class ExampleInitialization: IInitializableModule { public void Initialize(InitializationEngine context) { //Enrichment script goes here } public void Uninitialize(InitializationEngine context) { } }

You though need to remember that these Initialization Modules are re-executed every time instances of your application starts up, hence it will always be your responsibility to ensure the same data record is not added over and over again. Please also note the ModuleDependency annotation, which forces these to be initialized after the Optimizely Commerce engine has been setup.

Let’s have a look at some data enrichment scenarios.

Tax Groups, Tax Jurisdictions, Tax Categories and Taxes

[InitializableModule]
[ModuleDependency(typeof(CommerceInitialization))]
public class SetupTaxJurisdictionInitialization : IInitializableModule
{
    private const string TaxCategory = "VAT";

    public void Initialize(InitializationEngine context)
    {
        ILanguageBranchRepository languageBranchRepository = context.Locate.LanguageBranchRepository();

        JurisdictionDto jurisdictionDto = JurisdictionManager.GetJurisdictions(JurisdictionManager.JurisdictionType.Tax);

        //Check if our VAT jurisdiction group already exists to avoid duplicate records.
        JurisdictionDto.JurisdictionGroupRow jurisdictionGroup = jurisdictionDto.JurisdictionGroup.FirstOrDefault(g => g.Code.Equals("VAT", StringComparison.OrdinalIgnoreCase));

        if (jurisdictionGroup == null)
        {
            jurisdictionDto = new JurisdictionDto();

            //Add new jurisdiction group
            jurisdictionGroup = jurisdictionDto.JurisdictionGroup.NewJurisdictionGroupRow();
            jurisdictionGroup.ApplicationId = Mediachase.Commerce.Core.AppContext.Current.ApplicationId;
            jurisdictionGroup.Code = "VAT";
            jurisdictionGroup.DisplayName = "VAT Group";
            jurisdictionGroup.JurisdictionType = (int)JurisdictionManager.JurisdictionType.Tax;
            jurisdictionDto.JurisdictionGroup.AddJurisdictionGroupRow(jurisdictionGroup);
        }

        // Get list of country codes from the website languages being supported
        // Convert these to regions to apply taxes to
        IList<RegionInfo> regions = languageBranchRepository.ListEnabled()
            .Where(l => !l.Culture.IsNeutralCulture)
            .Select(l => l.Culture.Name).Distinct()
            .Select(l => new RegionInfo(l)).ToList();

        foreach (RegionInfo region in regions)
        {
            //Validate if region already exists
            bool doesExist = jurisdictionDto.Jurisdiction
                .Any(j => !j.IsCodeNull() && j.Code.Equals(region.TwoLetterISORegionName, StringComparison.OrdinalIgnoreCase));

            //Add jurisdiction if it does not exist 
            if (!doesExist)
            {
                //Add new jurisdiction 
                JurisdictionDto.JurisdictionRow jurisdiction = jurisdictionDto.Jurisdiction.NewJurisdictionRow();
                jurisdiction.ApplicationId = Mediachase.Commerce.Core.AppContext.Current.ApplicationId;
                jurisdiction.Code = region.TwoLetterISORegionName;
                jurisdiction.DisplayName = region.DisplayName;
                jurisdiction.CountryCode = region.TwoLetterISORegionName;
                jurisdiction.JurisdictionType = (int)JurisdictionManager.JurisdictionType.Tax;
                jurisdictionDto.Jurisdiction.AddJurisdictionRow(jurisdiction);

                //Add new jurisdiction relation with group
                JurisdictionDto.JurisdictionRelationRow jurisdictionRelation = jurisdictionDto.JurisdictionRelation.NewJurisdictionRelationRow();
                jurisdictionRelation.JurisdictionId = jurisdiction.JurisdictionId;
                jurisdictionRelation.JurisdictionRow = jurisdiction;
                jurisdictionRelation.JurisdictionGroupRow = jurisdictionGroup;
                jurisdictionDto.JurisdictionRelation.AddJurisdictionRelationRow(jurisdictionRelation);
            }
        }

        if (jurisdictionDto.HasChanges())
            JurisdictionManager.SaveJurisdiction(jurisdictionDto);

        //Create our Tax Category if it doesn't exist
        this.CreateTaxCategoryIfNotExists();

        //Get tax category
        string taxCategory = this.GetTaxCategory();

        //Retrieve the tax records for Sales Tax
        TaxDto taxDto = TaxManager.GetTaxDto(TaxType.SalesTax);

        //Validate if we already have a record called "Sales Tax".
        TaxDto.TaxRow salesTaxRow = taxDto.Tax
            .FirstOrDefault(t => t.Name.Equals("Sales Tax", StringComparison.OrdinalIgnoreCase));

        //Add tax value for Sales Tax with rate 20%
        if (salesTaxRow == null)
        {
            salesTaxRow = taxDto.Tax.NewTaxRow();
            salesTaxRow.ApplicationId = Mediachase.Commerce.Core.AppContext.Current.ApplicationId;
            salesTaxRow.Name = "Sales Tax";
            salesTaxRow.SortOrder = 1;
            salesTaxRow.TaxType = (int)TaxType.SalesTax;
            taxDto.Tax.AddTaxRow(salesTaxRow);

            //Add all language labels for the sales tax value
            foreach (LanguageBranch languageBranch in languageBranchRepository.ListEnabled())
            {
                TaxDto.TaxLanguageRow taxLanguage = taxDto.TaxLanguage.NewTaxLanguageRow();
                taxLanguage.LanguageCode = languageBranch.LanguageID;
                taxLanguage.TaxRow = salesTaxRow;
                taxLanguage.DisplayName = "VAT 20%";
                taxDto.TaxLanguage.AddTaxLanguageRow(taxLanguage);
            }

            TaxDto.TaxValueRow salesTaxValue = taxDto.TaxValue.NewTaxValueRow();
            salesTaxValue.Percentage = 20;
            salesTaxValue.AffectiveDate = DateTime.UtcNow;
            salesTaxValue.TaxCategory = taxCategory;
            salesTaxValue.JurisdictionGroupId = jurisdictionGroup.JurisdictionGroupId;
            salesTaxValue.TaxRow = salesTaxRow;
            taxDto.TaxValue.AddTaxValueRow(salesTaxValue);
        }

        if (taxDto.HasChanges())
            TaxManager.SaveTax(taxDto);


        //Retrieve the tax records for Shipping Tax
        taxDto = TaxManager.GetTaxDto(TaxType.ShippingTax);

        //Validate if we already have a record called "Shipping Tax".
        TaxDto.TaxRow shippingTaxRow = taxDto.Tax
            .FirstOrDefault(t => t.Name.Equals("Shipping Tax", StringComparison.OrdinalIgnoreCase));

        if (shippingTaxRow == null)
        {
            shippingTaxRow = taxDto.Tax.NewTaxRow();
            shippingTaxRow.ApplicationId = Mediachase.Commerce.Core.AppContext.Current.ApplicationId;
            shippingTaxRow.Name = "Shipping Tax";
            shippingTaxRow.SortOrder = 2;
            shippingTaxRow.TaxType = (int)TaxType.ShippingTax;
            taxDto.Tax.AddTaxRow(shippingTaxRow);

            TaxDto.TaxValueRow shippingTaxValue = taxDto.TaxValue.NewTaxValueRow();
            shippingTaxValue.Percentage = 20;
            shippingTaxValue.AffectiveDate = DateTime.UtcNow;
            shippingTaxValue.TaxCategory = taxCategory;
            shippingTaxValue.JurisdictionGroupId = jurisdictionGroup.JurisdictionGroupId;
            shippingTaxValue.TaxRow = shippingTaxRow;
            taxDto.TaxValue.AddTaxValueRow(shippingTaxValue);
        }

        if(taxDto.HasChanges()) TaxManager.SaveTax(taxDto);
    }

    private string GetTaxCategory()
    {
        //Validate if TaxCategory exists
        CatalogTaxDto taxCategories = CatalogTaxManager.GetTaxCategories(true);

        if (taxCategories.TaxCategory.Any(t => t.Name.Equals(TaxCategory, StringComparison.OrdinalIgnoreCase)))
            return TaxCategory;

        return String.Empty;
    }

    private void CreateTaxCategoryIfNotExists()
    {
        if (String.IsNullOrWhiteSpace(this.GetTaxCategory()))
        {
            CatalogTaxManager.CreateTaxCategory(TaxCategory, true);
        }

    }
    
    public void Uninitialize(InitializationEngine context)
    {
    }
}

Warehouses

[InitializableModule]
[ModuleDependency(typeof(Optimizely.Commerce.Initialization.InitializationModule))]
public class SetupWarehouseInitialization : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        IWarehouseRepository warehouseRepository = context.Locate.Advanced.GetInstance<IWarehouseRepository>();

        if (warehouseRepository.Get("NYC") == null)
        {
            Warehouse warehouse = new Warehouse();

            warehouse.Code = "NYC";
            warehouse.IsActive = true;
            warehouse.Created = DateTime.Now;
            warehouse.Modified = DateTime.Now;
            warehouse.SortOrder = 0;
            warehouse.IsPrimary = true;
            warehouse.ApplicationId = Mediachase.Commerce.Core.AppContext.Current.ApplicationId;
            warehouse.ContactInformation = new WarehouseContactInformation()
            {
                City = "New York",
                CountryCode = "US",
                CountryName = "USA",
                DaytimePhoneNumber = "1 (123) 123 1234",
                Email = "newyork@distributor.com"
                // More information goes here...
            },
            warehouse.FulfillmentPriorityOrder = 0;
            warehouse.IsDeliveryLocation = false;
            warehouse.IsFulfillmentCenter = true;
            warehouse.IsPickupLocation = true;
            warehouse.Name = "NY Warehouse";

            warehouseRepository.Save(warehouse);
        }

    }

    public void Uninitialize(InitializationEngine context)
    {
    }
}

Payment Methods

[InitializableModule]
[ModuleDependency(typeof(Optimizely.Commerce.Initialization.InitializationModule))]
public class SetupPaymentMethodsInitialization : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        IMarketService marketService = context.Locate.Advanced.GetInstance<IMarketService>();

        //Get all enabled markets
        IEnumerable<IMarket> allMarkets = marketService.GetAllMarkets();

        //Get all the languages
        foreach (CultureInfo language in allMarkets.SelectMany(m => m.Languages).Distinct())
        {
            //Get all payment methods for this language
            PaymentMethodDto paymentMethodDto = PaymentManager.GetPaymentMethods(language.Name);

            //Get the market's to enable the method for
          IEnumerable<MarketId> marketIds = allMarkets.Where(m => m.Languages.Contains(language))
                .Select(m => m.MarketId);

            //Get the payment methods we want to create for this language
          IList<String[]> listOfPayments = this.GetPaymentMethodList(language.Name);

          listOfPayments.ForEach(p =>
          {
              String paymentCode = p[0];
              String paymentName = p[1];

                //Adjust as per your needs
                this.AddPaymentMethodIfItDoesNotExist(paymentName, paymentName, paymentCode, language,
                      typeof(GenericPaymentGateway), typeof(OtherPayment), marketIds, paymentMethodDto);
            });
        }
    }

    private void AddPaymentMethodIfItDoesNotExist(string name, string description, string systemKeyword, CultureInfo language, Type providerType, Type paymentClass, IEnumerable<MarketId> marketIds, PaymentMethodDto paymentMethodDto)
    {
        bool found = paymentMethodDto.PaymentMethod
            .Any(c => c.SystemKeyword.Equals(systemKeyword, StringComparison.OrdinalIgnoreCase));

        //Skip
        if (found)
        {
            PaymentMethodDto.PaymentMethodRow paymentMethodRow = paymentMethodDto.PaymentMethod.First(p => p.IsActive && p.SystemKeyword.Equals(systemKeyword, StringComparison.OrdinalIgnoreCase));
            PaymentMethod method = new PaymentMethod(paymentMethodRow);
            marketIds.ForEach(m =>
            {
                if(!method.MarketId.Contains(m))
                    method.MarketId.Add(m);
            });
            method.SaveChanges();
            return;
        }


        PaymentMethodDto.PaymentMethodRow row = paymentMethodDto.PaymentMethod.AddPaymentMethodRow(Guid.NewGuid(), name, description, language.Name, systemKeyword, true, false, String.Format("{0}, {1}", providerType.FullName, providerType.Assembly.GetName().Name), String.Format("{0}, {1}", paymentClass.FullName, paymentClass.Assembly.GetName().Name), false, 0, DateTime.Now, DateTime.Now, Mediachase.Commerce.Core.AppContext.Current.ApplicationId);
        PaymentMethod paymentMethod = new PaymentMethod(row);
        paymentMethod.MarketId.AddRange(marketIds);
        paymentMethod.SaveChanges();
    }

    public void Uninitialize(InitializationEngine context)
    {
    }

    private IList<String[]> GetPaymentMethodList(string languageName)
    {
        //You can separate by language if needed.

        return new List<String[]>()
            {
                new[] {"30D", "Net 30 Days Credit"},
                new[] {"60D", "Net 60 Days Credit"},
                new[] {"90D", "Net 90 Days Credit"}
            };
    }
}

Shipping Options

[InitializableModule]
[ModuleDependency(typeof(CommerceInitialization))]
public class SetupShippingMethodsInitialization : IInitializableModule
{
    public void Initialize(InitializationEngine context)
    {
        IMarketService marketService = context.Locate.Advanced.GetInstance<IMarketService>();

        //Get all enabled markets
        IEnumerable<IMarket> allMarkets = marketService.GetAllMarkets();

        //Get all the languages
        foreach (CultureInfo language in allMarkets.SelectMany(m => m.Languages).Distinct())
        {
            //Get all shipping methods for the language
            ShippingMethodDto shippingMethodDto = ShippingManager.GetShippingMethods(language.Name, true);

            //Create a shipping provider, as needed.
            //You can also choose to use one of the build in provider classes
            ShippingMethodDto.ShippingOptionRow shippingOption =
                this.CreateOrGetShippingOption("Calculated Shipping", "Calculated Shipping", "CalculatedRate", typeof(CalculatedShippingPlugin), shippingMethodDto);

            //Get the first market for our language 
            Currency defaultCurrency = allMarkets.First(m => m.Languages.Contains(language)).DefaultCurrency;

            //Get the market's to enable the method for
            IList<MarketId> marketIds = allMarkets.Where(m => m.Languages.Contains(language)).Select(m => m.MarketId).ToList();

            this.AddShippingMethodIfItDoesNotExist("Calculated Fee Royal Mail", "Royal Mail", "RM", language, defaultCurrency, 0m, marketIds, shippingMethodDto, shippingOption);

            //Save shipping options and methods
            if (shippingMethodDto.HasChanges())
                ShippingManager.SaveShipping(shippingMethodDto);
        }
    }


    private ShippingMethodDto.ShippingOptionRow CreateOrGetShippingOption(string shippingOptionName, string shippingOptionDescription, string systemKeyword, Type shippingProviderType, ShippingMethodDto shippingMethodDto)
    {
        //Compose class name
        string providerClassName = String.Format("{0}, {1}", shippingProviderType.FullName, shippingProviderType.Assembly.GetName().Name);  

        //Try to locate the shipping provider we are trying to create
        ShippingMethodDto.ShippingOptionRow shippingOptionRow = shippingMethodDto.ShippingOption.FirstOrDefault(
            option => option.ClassName == providerClassName && option.SystemKeyword == systemKeyword && option.ApplicationId == Mediachase.Commerce.Core.AppContext.Current.ApplicationId);

        //Check if it already exist
        if (shippingOptionRow == null)
        {
            //Add shipping provider
            shippingOptionRow = shippingMethodDto.ShippingOption.AddShippingOptionRow(Guid.NewGuid(), shippingOptionName,
                shippingOptionDescription, systemKeyword, providerClassName, DateTime.Now, DateTime.Now,
                Mediachase.Commerce.Core.AppContext.Current.ApplicationId);
        }

        return shippingOptionRow;
    }

    private void AddShippingMethodIfItDoesNotExist(string shippingMethodDisplayName, string shippingMethodDescription, string shippingMethodName, CultureInfo language, Currency currency, decimal basePrice, IList<MarketId> marketIds, ShippingMethodDto shippingMethodDto, ShippingMethodDto.ShippingOptionRow shippingOption)
    {
        //Find for the shipping method that is to be added
        ShippingMethodDto.ShippingMethodRow shippingMethod = shippingMethodDto.ShippingMethod
            .FirstOrDefault(method => method.Name == shippingMethodName && method.ApplicationId == Mediachase.Commerce.Core.AppContext.Current.ApplicationId && method.ShippingOptionRow != null && method.ShippingOptionRow.ClassName == shippingOption.ClassName);

        //Check if it already exist
        if (shippingMethod == null)
        {
            //Add shipping method
            shippingMethod = shippingMethodDto.ShippingMethod.AddShippingMethodRow(Guid.NewGuid(), shippingOption,
                Mediachase.Commerce.Core.AppContext.Current.ApplicationId, language.Name, true,
                shippingMethodName,
                shippingMethodDescription, basePrice, currency, shippingMethodDisplayName, false, 0, DateTime.Now,
                DateTime.Now);
        }
        else if(shippingMethod.ShippingOptionId != shippingOption.ShippingOptionId)
        {
            shippingMethod.ShippingOptionId = shippingOption.ShippingOptionId;
        }

        IList<string> existingMarkets = shippingMethodDto.MarketShippingMethods
            .Where(m => m.ShippingMethodId == shippingMethod.ShippingMethodId)
            .Select(m => m.MarketId).ToList();

        //Add markets for shipping methods
        marketIds.ForEach(marketId =>
        {
            if(!existingMarkets.Contains(marketId.Value))
                shippingMethodDto.MarketShippingMethods.AddMarketShippingMethodsRow(marketId.Value, shippingMethod);
        });
    }


    public void Uninitialize(InitializationEngine context)
    {
    }
}

Markets

[InitializableModule]
[ModuleDependency(typeof(CommerceInitialization))]
public class SetupMarketInitialization : IInitializableModule 
{
    public void Initialize(InitializationEngine context)
    {

        IMarketService marketService = context.Locate.Advanced.GetInstance<IMarketService>();

        //Create markets to hold pricing data
        this.CreateMarketIfDoesNotExist(marketService, Currency.USD, CultureInfo.GetCultureInfo("en-US"), "US");
    }

    public void Uninitialize(InitializationEngine context)
    {
    }

    /// <summary>
    /// Create market if it does not exist already
    /// </summary>
    /// <param name="marketConventionManager"></param>
    /// <param name="marketId">MarketId</param>
    /// <param name="marketService">Market Service</param>
    private void CreateMarket(IMarketService marketService, MarketId marketId, Currency defaultCurrency, CultureInfo defaultLanguage, string marketName)
    {
        //Create market implementation from marketid
        MarketImpl market = new MarketImpl(marketId);

        //Set default currency as USD
        market.DefaultCurrency = defaultCurrency;

        //Set default language same as preferred culture
        market.DefaultLanguage = defaultLanguage;

        market.MarketName = marketName;

        //Activate the market by setting value to true
        market.IsEnabled = true;

        //Add country codes to market
        //market.CountriesCollection.Add(c);

        //Add languages to market
        //market.LanguagesCollection.Add(l);

        //Add currencies to market
        //market.CurrenciesCollection.Add(c);

        market.MarketDescription = market.MarketName;

        //Create market
        marketService.CreateMarket(market);
    }

    private void CreateMarketIfDoesNotExist(IMarketService marketService, Currency defaultCurrency, CultureInfo defaultLanguage, string marketName)
    {
        MarketId marketId = new MarketId(marketName);

        //Check if market already exist
        if (marketService.GetMarket(marketId) == null)
        {
            //Create market
            this.CreateMarket(marketService, marketId, defaultCurrency, defaultLanguage, marketName);
        }
    }
}

This article was originally published on Optimizely Fellow Blog: Optimizely Commerce Data Enrichment Cheat Sheet 

 

Neem contact op

Let's reinvent the future