volume_up

A critical vulnerability was discovered in React Server Components (Next.js). Our systems remain protected but we advise to update packages to newest version. Learn More

AI OnAI Off

Error programmatically creating Shipping Method

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 :)
- 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 :-)
error This topic was created over six months ago and has been resolved. If you have a similar question, please create a new topic and refer to this one.
* 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.