/// <summary> /// Convert items and children into cart items /// </summary> /// <param name="parentItem"></param> /// <param name="parentQuantity"></param> /// <param name="childItems"></param> /// <returns></returns> public ShoppingCartItem GetShoppingCartItem(string parentItemCode, int parentQuantity, Dictionary <string, int> childItems = null) { if (parentItemCode.IsNullOrEmpty()) { throw new ArgumentNullException("parentItemCode cannot be null"); } // get the db version of the item for our market var items = ExigoDAL.GetItems(new GetItemsRequest { Configuration = this._OrderConfiguration, ItemCodes = new List <string> { parentItemCode }, IgnoreCategoryRestrictions = true, IncludeStaticKitChildren = true, IncludeDynamicKitChildren = true }).ToList(); // If too many or too few items found, stop. if (!items.Any()) { throw new ArgumentOutOfRangeException($"Item {parentItemCode} not available in {this._OrderConfiguration.DefaultCountryCode}"); } if (items.Count() != 1) { throw new ArgumentOutOfRangeException($"Too many items found matching {parentItemCode}"); } var _parentItem = items.First(); // create our parent item ShoppingCartItem responseCartItem = new ShoppingCartItem(_parentItem) { Quantity = parentQuantity }; #region populate dynamic kit children var ignoreStaticCategoryChildren = true; // remove children that would always be added as they're the only option. (reduce property bag data) if (_parentItem.ItemTypeID == ItemTypes.DynamicKit && _parentItem.DynamicKitCategories.Any()) { var kitChildItems = new List <ShoppingCartKitItem>(); // Run through each category and populate it's children foreach (var category in _parentItem.DynamicKitCategories) { var categoryChildItems = new List <ShoppingCartKitItem>(); // if no items are available, skip the category if (category.Items == null || !category.Items.Any()) { continue; } // if only one item, add it at the category's quantity if (category.Items.Count() == 1) { if (ignoreStaticCategoryChildren) { // skip adding items for this category since no user choice is involved. continue; } categoryChildItems.Add(new ShoppingCartKitItem(category.Items.FirstOrDefault()) { // CategoryID = category.DynamicKitCategoryID, // handled in constructor Quantity = category.Quantity }); } // categories with more than one item require user choice. (verify against child items and quantities passed from user) else { // if category quantity is 1, grab any matching item and set it (ignore item quantity passed in) if (category.Quantity == 1) { // get cat items that match user selected children var matchingItems = category.Items.Where(catItem => childItems == null || childItems.Any(childRequest => childRequest.Key.Equals_IgnoreCase(catItem.ItemCode))).ToList(); // too many children selected, throw error if (matchingItems.Count() > 1) { throw new InvalidRequestException($"Too many children selected for dynamic kit category. Expected {category.Quantity}, got {matchingItems.Count()}."); } // no children selected, select default as first child if (!matchingItems.Any()) { matchingItems = new List <Item> { category.Items.First() }; } categoryChildItems.Add(new ShoppingCartKitItem(matchingItems.FirstOrDefault()) { CategoryID = category.DynamicKitCategoryID, Quantity = category.Quantity }); } // if category quantity AND category Items are more than 1, we need populate our list for as many requests that match, then verify if (category.Quantity > 1) { var maxQuantity = category.Quantity; foreach (var catItem in category.Items) { // if no child items selected, stop if (childItems == null) { throw new NullReferenceException($"No children selected for multi-choice category."); } var requestedQuantity = childItems .Where(childRequest => childRequest.Key.Equals_IgnoreCase(catItem.ItemCode)) // find matching requests .Sum(childRequest => childRequest.Value); // get total requested quantity // if this category item was not requested, skip. (ignores negative quantities as well) if (requestedQuantity <= 0) { continue; } // if requesting more than max, set requested value to max. if (requestedQuantity > maxQuantity) { requestedQuantity = (int)maxQuantity; } categoryChildItems.Add(new ShoppingCartKitItem(catItem) { CategoryID = category.DynamicKitCategoryID, Quantity = requestedQuantity }); // subtract the request from the available for the next item. maxQuantity -= requestedQuantity; // if there is no more quantity available, stop. if (maxQuantity <= 0) { break; } } } } // verify active category quantity matches added items var categoryChildItemCount = categoryChildItems.Sum(i => i.Quantity); if (categoryChildItemCount != category.Quantity) { throw new ArgumentOutOfRangeException($"Dynamic Kit children mismatch for item {_parentItem.ItemCode} in category {category.DynamicKitCategoryID}. Expected {category.Quantity}, got {categoryChildItemCount}."); } // add the children to the parent list kitChildItems.AddRange(categoryChildItems); } var activeCategories = _parentItem.DynamicKitCategories .Where(c => c.Items.Any()); // only count categories with items populated by db request. if (ignoreStaticCategoryChildren) { // filter out static categories for validating quantity activeCategories = activeCategories.Where(c => c.Items.Count() != 1); } // validate final child item count var activeCategoryCount = activeCategories.Sum(c => c.Quantity); var kitChildrenQuantity = kitChildItems.Sum(i => i.Quantity); if (activeCategoryCount != kitChildrenQuantity) { throw new ArgumentOutOfRangeException($"Dynamic Kit children mismatch for item {_parentItem.ItemCode}. Expected {activeCategoryCount}, got {kitChildrenQuantity}."); } // add all kit children to the response responseCartItem.Children = kitChildItems; } #endregion #region populate static kit children if (_parentItem.ItemTypeID == ItemTypes.StaticKit && _parentItem.StaticKitChildren != null && _parentItem.StaticKitChildren.Any()) { // do nothing (included for completeness) // static kit children are not user selected so do not need to be populated in the cart } #endregion return(responseCartItem); }
/// <summary> /// Convert ShoppingCartItems into OrderDetail Requests /// </summary> /// <param name="cartItems"></param> /// <returns></returns> public List <Api.ExigoWebService.OrderDetailRequest> GetOrderDetails(List <IShoppingCartItem> cartItems) { var parentCartItems = cartItems.ToList(); var parentItemCodes = parentCartItems .Select(c => c.ItemCode) .ToList(); // get the db version of the item for our market var dbItems = ExigoDAL.GetItems(new GetItemsRequest { Configuration = this._OrderConfiguration, ItemCodes = parentItemCodes, IncludeDynamicKitChildren = true, //~~ I had to add these properties to make this work in Enrollment SubmitCheckout - Mike M. ~~ IgnoreCategoryRestrictions = true }).ToList(); if (!dbItems.Any()) { throw new InvalidRequestException($"No requested item was available in {this._OrderConfiguration.DefaultCountryCode}"); } var responseDetails = new List <Api.ExigoWebService.OrderDetailRequest>(); #region populate Normal item requests var standardItems = dbItems.Where(i => i.ItemTypeID == ItemTypes.Standard).ToList(); foreach (var parentItem in standardItems) { // find the cart requests that match var matchingCartItems = cartItems .Where(ci => ci.ItemCode == parentItem.ItemCode); foreach (var cartItem in matchingCartItems) { responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(parentItem) { Quantity = cartItem.Quantity }); } } #endregion #region populate Static Kit item requests var staticKitItems = dbItems.Where(i => i.ItemTypeID == ItemTypes.StaticKit).ToList(); foreach (var parentItem in staticKitItems) { // find the cart requests that match var matchingCartItems = cartItems .Where(ci => ci.ItemCode == parentItem.ItemCode); foreach (var cartItem in matchingCartItems) { responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(parentItem) { Quantity = cartItem.Quantity }); foreach (var childItem in parentItem.StaticKitChildren) { responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(childItem) { ParentItemCode = parentItem.ItemCode, Quantity = childItem.Quantity * cartItem.Quantity }); } } } #endregion #region populate Dynamic Kit item requests var dynamicKitItems = dbItems.Where(i => i.ItemTypeID == ItemTypes.DynamicKit).ToList(); foreach (var parentItem in dynamicKitItems) { // find the cart requests that match var kitCartItems = cartItems .Where(ci => ci.ItemCode == parentItem.ItemCode); // add api requests for each cart kit (allows for multiple configurations for the same kit) foreach (var kitCartItem in kitCartItems) { // add the parent first responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(parentItem) { Quantity = kitCartItem.Quantity }); // add the children foreach (var category in parentItem.DynamicKitCategories) { // get the requested cart children for the category var cartCategoryChildren = kitCartItem.Children ?.Where(c => c.CategoryID == category.DynamicKitCategoryID) ?? new List <ShoppingCartKitItem>(); // add static categories (only one available item) at the category's quantity // static categories are not added to the cart. so pull this info directly from db item and parent qty if (category.Items.Count() == 1) { var childItem = category.Items.First(); responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(childItem) { ParentItemCode = parentItem.ItemCode, Quantity = category.Quantity * kitCartItem.Quantity }); } else { // add single choice category chidren if (category.Quantity == 1) { // get the first cart child that matches the category var matchingcartItem = cartCategoryChildren.FirstOrDefault(); // get the db category child item that matches the cart request var matchingCategoryItem = category.Items.FirstOrDefault(catItem => catItem.ItemCode.Equals_IgnoreCase(matchingcartItem?.ItemCode)) ?? category.Items.First(); // default to the first category item if requested item is not found responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(matchingCategoryItem) { ParentItemCode = parentItem.ItemCode, Quantity = kitCartItem.Quantity // ignore requested qty when category qty is 1. child quantity will be that of the parent. }); } // add multi-choice category children if (category.Quantity > 1) { // validate requested qty against a single kit var maxQuantity = category.Quantity; // get the cart children that matches the category foreach (var catItem in category.Items) { // Get the requested quantity (if any). child Qty should be based on a single kit regardless of parent quantity (kit configuration style) var requestedQuantity = cartCategoryChildren .Where(childRequest => childRequest.ItemCode.Equals_IgnoreCase(catItem.ItemCode)) .Sum(childRequest => childRequest.Quantity); // if none selected, skip if (requestedQuantity <= 0) { continue; } // if requested qty is too large, set it to the max allowed if (requestedQuantity > maxQuantity) { requestedQuantity = maxQuantity; } // add the child responseDetails.Add(new Api.ExigoWebService.OrderDetailRequest(catItem) { ParentItemCode = parentItem.ItemCode, Quantity = requestedQuantity * kitCartItem.Quantity // multiply the single kit child qty by the parent qty so the API gets the proper ammount }); // subtract the request from the available for the next item. maxQuantity -= requestedQuantity; // if there is no more quantity available, stop. if (maxQuantity <= 0) { break; } } if (maxQuantity != 0) { throw new ArgumentOutOfRangeException($"Dynamic kit children mismatch for item {parentItem.ItemCode} in category {category.DynamicKitCategoryID}, Expected {category.Quantity}, got {category.Quantity - maxQuantity}"); } } } } } } #endregion return(responseDetails); }