Error programmatically creating Shipping Method

Vote:
 

I'm getting this error:

"ForeignKeyConstraint FK_ShippingOption_ShippingMethod requires the child key values (18ede71f-3df3-4d9e-a4cb-3d25881c1ec6) to exist in the parent table."

and here is my code:

private ShippingMethodDto.ShippingMethodRow CreateShippingMethod(string shippingMethodName, Guid shippingOptionID)
        {
            ShippingMethodDto shippingMethod = new ShippingMethodDto();
            
            ShippingMethodDto.ShippingMethodRow shippingMethodRow = shippingMethod.ShippingMethod.NewShippingMethodRow();

            shippingMethodRow[shippingMethod.ShippingMethod.ShippingMethodIdColumn] = Guid.NewGuid();
            shippingMethodRow[shippingMethod.ShippingMethod.LanguageIdColumn] = LanguageId;
            shippingMethodRow[shippingMethod.ShippingMethod.NameColumn] = shippingMethodName;
            shippingMethodRow[shippingMethod.ShippingMethod.DisplayNameColumn] = shippingMethodName;
            shippingMethodRow[shippingMethod.ShippingMethod.DescriptionColumn] = "";
            //shippingMethodRow[shippingMethod.ShippingMethod.CurrencyColumn] = shippingMethodInfo.Currency;
            shippingMethodRow[shippingMethod.ShippingMethod.BasePriceColumn] = 0m;
            shippingMethodRow[shippingMethod.ShippingMethod.ShippingOptionIdColumn]= shippingOptionID;
            shippingMethodRow[shippingMethod.ShippingMethod.IsActiveColumn] = true;
            shippingMethodRow[shippingMethod.ShippingMethod.IsDefaultColumn] = false;
            //shippingMethodRow[shippingMethod.ShippingMethod.OrderingColumn] = shippingMethodInfo.Ordering;
            shippingMethodRow[shippingMethod.ShippingMethod.CreatedColumn] = DateTime.UtcNow;   //TODO
            shippingMethodRow[shippingMethod.ShippingMethod.ModifiedColumn] = DateTime.UtcNow;   //TODO
            
            shippingMethod.ShippingMethod.Rows.Add(shippingMethodRow);

            ShippingManager.SaveShipping(shippingMethod);

            return shippingMethodRow;
        }

// used like this:
 var methods = ShippingManager.GetShippingMethodsByMarket(currentMarket.GetCurrentMarket().MarketId.Value, true);
shippingMethod = CreateShippingMethod("deliveryMethodName", methods.ShippingOption[0].ShippingOptionId);

it's essentially complaining about the shipping Option ID that i'm passing in, but that ID is being loaded via EPiServer libs anyway. What am i missing?

#222226
Edited, Apr 30, 2020 15:40
Vote:
 

Hi Noel,

I am not sure why you are getting reference key error, could you please try below code.

    public class CreateShippingMethod
    {
        private readonly IMarketService _marketService;

        public CreateShippingMethod(IMarketService marketService)
        {
            _marketService = marketService ?? throw new ArgumentNullException(nameof(marketService));
        }

        public void ConfigureShippingMethods()
        {
            var enabledMarkets = _marketService.GetAllMarkets().Where(x => x.IsEnabled).ToList();

            foreach (var language in enabledMarkets.SelectMany(x => x.Languages).Distinct())
            {
                var languageId = language.TwoLetterISOLanguageName;
                var dto = ShippingManager.GetShippingMethods(languageId);
                var workingDto = (ShippingMethodDto)dto.Copy();

                this.DeleteShippingMethods(workingDto);
                ShippingManager.SaveShipping(workingDto);

                var marketsForCurrentLanguage = enabledMarkets.Where(x => x.Languages.Contains(language)).ToList();
                var shippingSet = this.CreateShippingMethodsForLanguageAndCurrencies(workingDto, marketsForCurrentLanguage, languageId);
                ShippingManager.SaveShipping(workingDto);

                AssociateShippingMethodWithMarkets(workingDto, marketsForCurrentLanguage, shippingSet);
                ShippingManager.SaveShipping(workingDto);
            }
        }

        private void DeleteShippingMethods(ShippingMethodDto dto)
        {
            foreach (var method in dto.ShippingMethod)
            {
                method.Delete();
            }
        }

        private IEnumerable<ShippingMethodDto.ShippingMethodRow> CreateShippingMethodsForLanguageAndCurrencies(ShippingMethodDto dto, IEnumerable<IMarket> markets, string languageId)
        {
            var shippingOption = dto.ShippingOption.First(x => x.Name == "Generic Gateway");
            var shippingMethods = new List<ShippingMethodDto.ShippingMethodRow>();
            var sortOrder = 1;

            //TODO: Add wishing price and currency
            var usdCostExpress = new Money(20, Currency.USD);

            foreach (var currency in markets.SelectMany(m => m.Currencies).Distinct())
            {
                shippingMethods.Add(this.Create(dto, shippingOption, languageId, sortOrder++,
                    "Express-" + currency, $"Express {currency} (1 day)({languageId})", usdCostExpress, currency));

                // TODO: Add more shipping methods.
            }

            return shippingMethods;
        }

        private ShippingMethodDto.ShippingMethodRow Create(ShippingMethodDto dto,
            ShippingMethodDto.ShippingOptionRow shippingOption, string languageId, int sortOrder, string name,
            string description, Money costInUsd, Currency currency)
        {
            Money shippingCost = CurrencyFormatter.ConvertCurrency(costInUsd, currency);
            if (shippingCost.Currency != currency)
            {
                throw new InvalidOperationException("Cannot convert to currency " + currency + " Missing conversion data.");
            }

            return dto.ShippingMethod.AddShippingMethodRow(
                Guid.NewGuid(),
                shippingOption,
                languageId,
                true,
                name,
                "",
                shippingCost.Amount,
                shippingCost.Currency,
                description,
                false,
                sortOrder,
                DateTime.Now,
                DateTime.Now);
        }

        private void AssociateShippingMethodWithMarkets(ShippingMethodDto dto, IEnumerable<IMarket> markets,
            IEnumerable<ShippingMethodDto.ShippingMethodRow> shippingSet)
        {
            foreach (var shippingMethod in shippingSet)
            {
                foreach (var market in markets?.Where(m => m.Currencies.Contains(shippingMethod.Currency)))
                {
                    dto.MarketShippingMethods.AddMarketShippingMethodsRow(market.MarketId.Value, shippingMethod);
                }
            }
        }
    }
#222276
May 01, 2020 19:48
Vote:
 

Thank you Sanjay,

i actually found code very similar to that here and adapted it too only create a single shipping method with the string i give it (all my shipping methods are 'discovered' during import from another system)

    using EPiServer.ServiceLocation;
    using Mediachase.Commerce;
    using Mediachase.Commerce.Markets;
    using Mediachase.Commerce.Orders.Dto;
    using Mediachase.Commerce.Orders.Managers;
    using Mediachase.Commerce.Shared;
    using System;
    using System.Collections.Generic;
    using System.Linq;

    public class ShippingMethodHelper
    {
        #region singleton

        private static readonly Lazy<ShippingMethodHelper> lazy = new Lazy<ShippingMethodHelper>(() =>
        {            
            return new ShippingMethodHelper();
        });

        public static ShippingMethodHelper Instance => lazy.Value;

        #endregion


        public void ConfigureShippingMethods(string methodName)
        {
            var marketService = ServiceLocator.Current.GetInstance<IMarketService>();
            var enabledMarkets = marketService.GetAllMarkets().Where(x => x.IsEnabled).ToList();
            foreach (var language in enabledMarkets.SelectMany(x => x.Languages).Distinct())
            {
                var languageId = language.TwoLetterISOLanguageName;
                var dto = ShippingManager.GetShippingMethods(languageId);
                var workingDto = (ShippingMethodDto)dto.Copy();
                //DeleteShippingMethods(workingDto);
                //ShippingManager.SaveShipping(workingDto);

                var marketsForCurrentLanguage = enabledMarkets.Where(x => x.Languages.Contains(language)).ToList();
                var shippingSet = CreateShippingMethodForLanguageAndCurrencies(workingDto, marketsForCurrentLanguage, languageId, methodName);
                ShippingManager.SaveShipping(workingDto);

                AssociateShippingMethodWithMarkets(workingDto, marketsForCurrentLanguage, shippingSet);
                ShippingManager.SaveShipping(workingDto);
            }
        }

        private IEnumerable<ShippingMethodDto.ShippingMethodRow> CreateShippingMethodForLanguageAndCurrencies(ShippingMethodDto dto, IEnumerable<IMarket> markets, string languageId, string methodName)
        {
            var shippingOption = dto.ShippingOption.First(x => x.Name == "Generic Gateway");
            var shippingMethods = new List<ShippingMethodDto.ShippingMethodRow>();
            var sortOrder = 1;

            foreach (var currency in markets.SelectMany(m => m.Currencies).Distinct())
            {
                shippingMethods.Add(CreateShippingMethod(dto, shippingOption, languageId, 0, methodName, methodName, new Money(0, Currency.GBP), currency));
            }

            return shippingMethods;
        }

        private ShippingMethodDto.ShippingMethodRow CreateShippingMethod(ShippingMethodDto dto, ShippingMethodDto.ShippingOptionRow shippingOption, string languageId, int sortOrder, string name, string description, Money costInUsd, Currency currency)
        {
            Money shippingCost = CurrencyFormatter.ConvertCurrency(costInUsd, currency);
            if (shippingCost.Currency != currency)
            {
                throw new InvalidOperationException("Cannot convert to currency " + currency + " Missing conversion data.");
            }
            return dto.ShippingMethod.AddShippingMethodRow(
                Guid.NewGuid(),
                shippingOption,
                languageId,
                true,
                name,
                "",
                shippingCost.Amount,
                shippingCost.Currency,
                description,
                false,
                sortOrder,
                DateTime.Now,
                DateTime.Now);
        }

        private void AssociateShippingMethodWithMarkets(ShippingMethodDto dto, IEnumerable<IMarket> markets, IEnumerable<ShippingMethodDto.ShippingMethodRow> shippingSet)
        {
            foreach (var shippingMethod in shippingSet)
            {
                foreach (var market in markets.Where(m => m.Currencies.Contains(shippingMethod.Currency)))
                {
                    dto.MarketShippingMethods.AddMarketShippingMethodsRow(market.MarketId.Value, shippingMethod);
                }
            }
        }
#222353
May 04, 2020 8:12
Sanjay Kumar - May 04, 2020 8:54
Ahh! Yes, I think I have used Quan code in our current project, okay but it solves your error :)
Noel Sefton - May 04, 2020 9:04
yeah was a bit concerned that his code deletes everything before re-inserting (and maybe it needed to work like that), but my minor adaption above allows non-destructive inserts and works without being in an initialization module, so i'm happy :-)
* You are NOT allowed to include any hyperlinks in the post because your account hasn't associated to your company. User profile should be updated.