/// <summary>
        /// Returns the next available index for an item that can be combined with the current
        /// </summary>
        /// <param name="currentIndex">The index of the current item to match to.</param>
        /// <returns>Index of the next item that can combine with the current, or -1 if not found.</returns>
        private int FindNextIndexForCombine(int currentIndex)
        {
            // THE CURRENT INDEX MUST BE WITHIN THE BOUNDS OF THE COLLECTION
            if (currentIndex < 0 || currentIndex > this.Count)
            {
                throw new ArgumentOutOfRangeException("currentIndex");
            }

            // STARTING WITH THE CURRENT INDEX, MOVE FORWARD IN THE
            // COLLECTION LOOKING FOR AN ITEM THAT COULD COMBINE WITH THIS ONE
            BasketItem currentItem = this[currentIndex];

            for (int nextIndex = currentIndex + 1; nextIndex < this.Count; nextIndex++)
            {
                BasketItem nextItem = this[nextIndex];
                if (currentItem.CanCombine(nextItem, true))
                {
                    return(nextIndex);
                }
            }

            // NO MATCH FOUND
            return(-1);
        }
        /// <summary>
        /// Recalculates all items in the basket, for example price and kit member products
        /// </summary>
        internal void Recalculate()
        {
            // BUILD A LIST OF ANY CHILD (GENERATED) PRODUCTS
            List <int>        childItemIds  = new List <int>();
            List <BasketItem> childProducts = new List <BasketItem>();

            foreach (BasketItem item in this)
            {
                if (item.OrderItemType == OrderItemType.Product && item.IsChildItem)
                {
                    childItemIds.Add(item.BasketItemId);
                    childProducts.Add(item);
                }
            }

            // MOVE THROUGH THE COLLECTION AND REMOVE ANY ITEMS ASSOCIATED WITH A CHILD ITEM
            for (int i = this.Count - 1; i >= 0; i--)
            {
                BasketItem item      = this[i];
                List <int> childPath = item.GetPath();
                if (IsChildItemInPath(childItemIds, childPath))
                {
                    if (item.OrderItemType == OrderItemType.Product)
                    {
                        this.RemoveAt(i);
                    }
                    else
                    {
                        this.DeleteAt(i);
                    }
                }
            }

            // LOOP EACH REMAINING ITEM AND RECALCULATE
            int currentCount = this.Count;

            for (int i = 0; i < currentCount; i++)
            {
                BasketItem basketItem = this[i];
                Basket     basket     = basketItem.Basket;
                int        userId     = (basket == null ? Token.Instance.UserId : basket.UserId);

                // WE ONLY NEED TO CHECK PRODUCTS, NON-PRODUCT ITEMS HAVE NO RECALCULATION TASKS
                if (basketItem.OrderItemType == OrderItemType.Product)
                {
                    // IF WE HAVE A KIT, WE MUST REFRESH ANY CONFIGURED KIT OPTIONS
                    bool isKit = (basketItem.Product.KitStatus == KitStatus.Master);
                    if (isKit)
                    {
                        basketItem.KitList = basketItem.Product.Kit.RefreshKitProducts(basketItem.KitList);
                    }

                    // RECALCULATE THE STARTING SKU/PRICE/WEIGHT FOR THIS ITEM
                    ProductCalculator pcalc = ProductCalculator.LoadForProduct(basketItem.ProductId, basketItem.Quantity, basketItem.OptionList, basketItem.KitList, userId);
                    basketItem.Sku    = pcalc.Sku;
                    basketItem.Weight = pcalc.Weight;
                    if (isKit || !basketItem.Product.UseVariablePrice)
                    {
                        // KITS AND NONVARIABLE PRICED PRODUCTS MUST HAVE PRICE RECALCULATED
                        basketItem.Price = pcalc.Price;
                    }
                    basketItem.Save();

                    // REGENERATE THE KIT ITEMS FOR THIS ITEM
                    if (basket != null && isKit)
                    {
                        // OBTAIN THE KIT PRODUCTS THAT ARE SELECTED RATHER THAN INCLUDED
                        int[] kitProductIds = AlwaysConvert.ToIntArray(basketItem.KitList);
                        if (kitProductIds != null && kitProductIds.Length > 0)
                        {
                            //keep track of the price/weight of the master line item
                            //decrement these values for each line item registered
                            LSDecimal masterPrice  = basketItem.Price;
                            LSDecimal masterWeight = basketItem.Weight;
                            foreach (int kitProductId in kitProductIds)
                            {
                                KitProduct kitProduct = KitProductDataSource.Load(kitProductId);
                                if (kitProduct != null && kitProduct.KitComponent.InputType != KitInputType.IncludedHidden)
                                {
                                    // WE WANT TO GENERATE BASKET RECORDS FOR ALL ITEMS *EXCEPT* INCLUDED HIDDEN ITEMS
                                    // INCLUDED HIDDEN ITEMS ARE TREATED AS PART OF THE MAIN PRODUCT AND ARE NOT GENERATED
                                    // UNTIL THE ORDER IS FINALIZED
                                    Product    product    = kitProduct.Product;
                                    BasketItem searchItem = new BasketItem();
                                    searchItem.BasketId         = basket.BasketId;
                                    searchItem.OrderItemType    = OrderItemType.Product;
                                    searchItem.ParentItemId     = basketItem.BasketItemId;
                                    searchItem.ProductId        = product.ProductId;
                                    searchItem.OptionList       = kitProduct.OptionList;
                                    searchItem.BasketShipmentId = basketItem.BasketShipmentId;

                                    // LOOK FOR ITEM
                                    BasketItem childItem = FindChildProduct(childProducts, searchItem);

                                    // UPDATE CALCULATED PROPERTIES
                                    childItem.Name      = kitProduct.DisplayName;
                                    childItem.Quantity  = (short)(kitProduct.Quantity * basketItem.Quantity);
                                    childItem.TaxCodeId = product.TaxCodeId;
                                    childItem.Shippable = product.Shippable;
                                    childItem.Price     = kitProduct.CalculatedPrice / kitProduct.Quantity;;
                                    childItem.Weight    = kitProduct.CalculatedWeight / kitProduct.Quantity;
                                    // CALCULATE SKU
                                    ProductCalculator childCalc = ProductCalculator.LoadForProduct(childItem.ProductId, childItem.Quantity, childItem.OptionList, childItem.KitList, basket.UserId);
                                    childItem.Sku = childCalc.Sku;

                                    basket.Items.Add(childItem);
                                    childItem.Save();
                                    masterPrice  -= kitProduct.CalculatedPrice;
                                    masterWeight -= kitProduct.CalculatedWeight;
                                }
                            }

                            // UPDATE MASTER PRICE, FACTORING IN CHILD ITEMS
                            basketItem.Price  = masterPrice;
                            basketItem.Weight = masterWeight;
                            basketItem.Save();
                        }
                    }
                }
            }

            // DELETE ANY CHILD PRODUCTS THAT WERE NOT PRESERVED
            foreach (BasketItem bi in childProducts)
            {
                bi.Delete();
            }
        }
        /// <summary>
        /// Combines any items in the collection that have equivalent data.
        /// <param name="save">Flag indicating whether or not to persist changes.</param>
        /// </summary>
        public void Combine(bool save)
        {
            // WE NEED TO TRACK PARENT ITEMS THAT ARE COMBINED
            Dictionary <int, int> combineMapping = new Dictionary <int, int>();

            // LOOP THE BASKET ITEM COLLECTION AND COMBINE ROOT LEVEL PRODUCTS
            for (int thisIndex = 0; thisIndex < this.Count; thisIndex++)
            {
                BasketItem thisItem = this[thisIndex];
                // ONLY CHECK ROOT LEVEL PRODUCTS
                if (thisItem.OrderItemType == OrderItemType.Product && !thisItem.IsChildItem)
                {
                    // SEE IF WE HAVE ANY MATCHES TO COMBINE
                    int matchIndex = FindNextIndexForCombine(thisIndex);
                    while (matchIndex > -1)
                    {
                        BasketItem matchItem = this[matchIndex];
                        // TRACK NEW PARENT MAPPING FOR CHILD ITEMS
                        if (matchItem.BasketItemId > 0)
                        {
                            combineMapping[matchItem.BasketItemId] = thisItem.BasketItemId;
                        }
                        // COMBINE QUANTITIES, BE CAREFUL NOT TO EXCEED SHORT LIMIT
                        int newQuantity = thisItem.Quantity + matchItem.Quantity;
                        thisItem.Quantity = (newQuantity > short.MaxValue ? short.MaxValue : (short)newQuantity);
                        this.TrackedRemoveAt(matchIndex);
                        // SEE IF WE HAVE ANOTHER MATCH
                        matchIndex = FindNextIndexForCombine(thisIndex);
                    }
                }
            }

            // UPDATE PARENT IDS THAT HAVE CHANGED AS A RESULT OF THE COMBINE
            Dictionary <int, int> savedParentMappings = new Dictionary <int, int>();

            foreach (BasketItem thisItem in this)
            {
                if (thisItem.IsChildItem && combineMapping.ContainsKey(thisItem.ParentItemId))
                {
                    thisItem.ParentItemId = combineMapping[thisItem.ParentItemId];
                }
            }

            // LOOP THE BASKET ITEM COLLECTION AND TO OPERATE ON CHILD ITEMS
            OrderItemType[] combineTypes = new OrderItemType[] { OrderItemType.Product, OrderItemType.Coupon, OrderItemType.Discount };
            for (int thisIndex = 0; thisIndex < this.Count; thisIndex++)
            {
                BasketItem thisItem = this[thisIndex];
                // ONLY CHECK CHILD ITEMS OF THE SPECIFIED TYPES
                // AND WHO HAVE PARENTS THAT HAVE BEEN IDENTIFIED IN THE COLLECTION
                if (thisItem.IsChildItem)
                {
                    // COMBINE CHILD PRODUCTS, DISCOUNTS, AND COUPONS
                    if (Array.IndexOf(combineTypes, thisItem.OrderItemType) > -1)
                    {
                        // SEE IF WE HAVE ANY MATCHES TO COMBINE
                        int matchIndex = FindNextIndexForCombine(thisIndex);
                        while (matchIndex > -1)
                        {
                            BasketItem matchItem = this[matchIndex];
                            if (thisItem.OrderItemType == OrderItemType.Product)
                            {
                                // COMBINE QUANTITIES, BE CAREFUL NOT TO EXCEED SHORT LIMIT
                                int newQuantity = thisItem.Quantity + matchItem.Quantity;
                                thisItem.Quantity = (newQuantity > short.MaxValue ? short.MaxValue : (short)newQuantity);
                            }
                            else
                            {
                                // COUPON OR DISCOUNT MAINTAINS ONE QUANTITY, ALTER PRICE INSTEAD
                                thisItem.Price += matchItem.Price;
                            }
                            // REMOVE THE MATCH ITEM FROM THE COLLECTION
                            this.TrackedRemoveAt(matchIndex);
                            // SEE IF WE HAVE ANOTHER MATCH
                            matchIndex = FindNextIndexForCombine(thisIndex);
                        }
                    }
                }
            }

            // LOOP COLLECTION IN REVERSE AND REMOVE ZERO QUANTITY ITEMS
            for (int i = this.Count - 1; i >= 0; i--)
            {
                if (this[i].Quantity < 1)
                {
                    this.TrackedRemoveAt(i);
                }
            }

            // IF INDICATED, PERSIST CHANGES TO THE DATABASE
            if (save)
            {
                this.Save();
            }
        }
Esempio n. 4
0
 /// <summary>
 /// Can this BasketItem combine with the given BasketItem?
 /// </summary>
 /// <param name="other">The BasketItem to check for combine</param>
 /// <returns><b>true</b> if it can combine, <b>false</b> otherwise</returns>
 internal bool CanCombine(BasketItem other, bool combineKitProducts)
 {
     if (other == null)
     {
         throw new ArgumentNullException("other");
     }
     if (this.ProductId != other.ProductId)
     {
         return(false);
     }
     if (this.IsChildItem)
     {
         // IF THE OTHER ITEM IS NOT A CHILD, THIS IS NOT A MATCH
         if (!other.IsChildItem)
         {
             return(false);
         }
         // IS THIS IS A CHILD PRODUCT OF A KIT?
         if (this.OrderItemType == OrderItemType.Product)
         {
             // DO NOT COMBINE CHILD PRODUCTS UNLESS INDICATED
             if (!combineKitProducts)
             {
                 return(false);
             }
             // DO NOT COMBINE IF NAME,PRICE,WEIGHT MISMATCH
             if (this.Name != other.Name ||
                 this.Price != other.Price ||
                 this.Weight != other.Weight)
             {
                 return(false);
             }
         }
         // IF THE OTHER ITEM HAS A DIFFERENT PARENT IT IS NOT A MATCH
         if (this.ParentItemId != other.ParentItemId)
         {
             return(false);
         }
     }
     else
     {
         //THIS ITEM IS NOT A CHILD, SO THE OTHER IS NOT A MATCH IF IT IS A CHILD
         if (other.IsChildItem)
         {
             return(false);
         }
     }
     if (this.OptionList != other.OptionList)
     {
         return(false);
     }
     if (this.LineMessage != other.LineMessage)
     {
         return(false);
     }
     if (this.WishlistItemId != other.WishlistItemId)
     {
         return(false);
     }
     if (this.BasketShipmentId != other.BasketShipmentId)
     {
         return(false);
     }
     if (this.GiftMessage != other.GiftMessage)
     {
         return(false);
     }
     if (this.WrapStyleId != other.WrapStyleId)
     {
         return(false);
     }
     if (this.Inputs.Count > 0)
     {
         //compare all of the input values to see if they match
         if (this.Inputs.Count != other.Inputs.Count)
         {
             return(false);
         }
         foreach (BasketItemInput input in this.Inputs)
         {
             BasketItemInput otherInput = BasketItem.GetInput(other.Inputs, input.InputFieldId);
             if (otherInput == null)
             {
                 return(false);
             }
             if (!input.InputValue.Equals(otherInput.InputValue))
             {
                 return(false);
             }
         }
     }
     if (this.KitList != other.KitList)
     {
         return(false);
     }
     if (this.OrderItemType != OrderItemType.Product)
     {
         if (this.Name != other.Name)
         {
             return(false);
         }
         if (this.Sku != other.Sku)
         {
             return(false);
         }
         if (this.Price != other.Price)
         {
             return(false);
         }
     }
     else
     {
         //NEED TO CHECK WHETHER THIS IS A VARIABLE PRICE PRODUCT
         if (this.Product != null && this.Product.UseVariablePrice)
         {
             //IS MY PRICE DIFFERENT FROM THE OTHER PRICE?
             if (this.Price != other.Price)
             {
                 return(false);
             }
         }
     }
     return(true);
 }
Esempio n. 5
0
 /// <summary>
 /// Can this BasketItem combine with the given BasketItem?
 /// </summary>
 /// <param name="other">The BasketItem to check for combine</param>
 /// <returns><b>true</b> if it can combine, <b>false</b> otherwise</returns>
 public bool CanCombine(BasketItem other)
 {
     return(CanCombine(other, false));
 }
Esempio n. 6
0
        /// <summary>
        /// Creates a basket item for a product
        /// </summary>
        /// <param name="productId">Id of the product for which to create the basket item</param>
        /// <param name="quantity">The quantity of basket item</param>
        /// <param name="optionList">List of option ids for the variant</param>
        /// <param name="kitList">List of product Ids of the kit items</param>
        /// <param name="userId">The user Id</param>
        /// <returns>The BasketItem created</returns>
        public static BasketItem CreateForProduct(int productId, short quantity, string optionList, string kitList, int userId)
        {
            // vALIDATE PARAMETERS
            Product product = ProductDataSource.Load(productId);

            if (product == null)
            {
                throw new ArgumentException("invalid product specified", "product");
            }
            if (quantity < 1)
            {
                throw new ArgumentException("quantity must be greater than 0", "quantity");
            }
            if (!string.IsNullOrEmpty(optionList))
            {
                ProductVariant v = ProductVariantDataSource.LoadForOptionList(productId, optionList);
                if (v == null)
                {
                    throw new ArgumentException("specified product options are invalid", "optionList");
                }
            }
            if (product.KitStatus == KitStatus.Master)
            {
                Kit kit = product.Kit;
                if (!kit.ValidateChoices(kitList))
                {
                    throw new ArgumentException("specified kit configuration is invalid", "kitProductIds");
                }
            }

            //GET THE QUANTITY
            short tempQuantity   = quantity;
            short basketQuantity = GetExistingBasketCount(product);

            if ((product.MinQuantity > 0) && ((tempQuantity + basketQuantity) < product.MinQuantity))
            {
                tempQuantity = (short)(product.MinQuantity - basketQuantity);
            }


            if ((product.MaxQuantity > 0) && ((tempQuantity + basketQuantity) > product.MaxQuantity))
            {
                tempQuantity = (short)(product.MaxQuantity - basketQuantity);
            }


            if (tempQuantity < 1)
            {
                return(null);
            }
            quantity = tempQuantity;

            BasketItem item = new BasketItem();
            //CALCULATE THE PRICE OF THE PRODUCT
            ProductCalculator pcalc = ProductCalculator.LoadForProduct(productId, quantity, optionList, kitList, userId);

            item.Sku    = pcalc.Sku;
            item.Price  = pcalc.Price;
            item.Weight = pcalc.Weight;
            //SET VALUES COMMON TO ALL BASKET ITEMS
            item.TaxCodeId     = product.TaxCodeId;
            item.Name          = product.Name;
            item.Quantity      = quantity;
            item.OrderItemType = CommerceBuilder.Orders.OrderItemType.Product;
            item.Shippable     = product.Shippable;
            item.ProductId     = productId;
            item.OptionList    = optionList;
            item.KitList       = kitList;

            // COPY MERCHANT INPUT FIELDS, PRODUCT TEMPLATE FIELDS
            foreach (ProductTemplateField tf in product.TemplateFields)
            {
                if (!string.IsNullOrEmpty(tf.InputValue))
                {
                    InputField field = tf.InputField;
                    if (field.IsMerchantField && field.PersistWithOrder)
                    {
                        BasketItemInput itemInput = new BasketItemInput();
                        itemInput.BasketItemId = item.BasketItemId;
                        itemInput.InputFieldId = tf.InputFieldId;
                        itemInput.InputValue   = tf.InputValue;
                        item.Inputs.Add(itemInput);
                    }
                }
            }


            return(item);
        }
Esempio n. 7
0
        /// <summary>
        /// Compares two objects and returns a value indicating whether one is less than, equal to, or greater than the other.
        /// </summary>
        /// <param name="x">The first object to compare.</param>
        /// <param name="y">The second object to compare.</param>
        /// <returns>Less than zero if x is less than y. <br/>
        /// Zero of x equals y.<br/>
        /// Greater than zero if x is greater than y.<br/>
        /// </returns>
        public int Compare(object x, object y)
        {
            BasketItem item1 = x as BasketItem;
            BasketItem item2 = y as BasketItem;

            // COMPARING WITH YOUR OWN CHILD
            if (item1.IsChildItem && item1.ParentItemId == item2.BasketItemId)
            {
                return(1);
            }
            // COMPARING WITH YOUR OWN PARENT
            if (item2.IsChildItem && item2.ParentItemId == item1.BasketItemId)
            {
                return(-1);
            }
            // USE SORT PRIORITY, TREAT CHILD ITEMS AS PRODUCTS
            int item1Priority = GetSortPriority(item1, true);
            int item2Priority = GetSortPriority(item2, true);
            int result        = item1Priority.CompareTo(item2Priority);

            if (result != 0)
            {
                return(result);
            }
            // SORT BY ROOT PARENT ID TO GROUP ALL RELATED PRODUCTS
            int item1CompareId = item1.GetParentItem(true).BasketItemId;
            int item2CompareId = item2.GetParentItem(true).BasketItemId;

            result = item1CompareId.CompareTo(item2CompareId);
            if (result != 0)
            {
                return(result);
            }
            // SORT BY BY ITEM ID, BUT USE PARENT IDS FOR TAXES
            if (item1.IsChildItem && item1.OrderItemType == OrderItemType.Tax)
            {
                item1CompareId = item1.ParentItemId;
            }
            else
            {
                item1CompareId = item1.BasketItemId;
            }
            if (item2.IsChildItem && item2.OrderItemType == OrderItemType.Tax)
            {
                item2CompareId = item2.ParentItemId;
            }
            else
            {
                item2CompareId = item2.BasketItemId;
            }
            result = item1CompareId.CompareTo(item2CompareId);
            if (result != 0)
            {
                return(result);
            }
            // USE SORT PRIORITY AND DO NOT GIVE SPECIAL HANDLING FOR CHILD ITEMS
            item1Priority = GetSortPriority(item1, false);
            item2Priority = GetSortPriority(item2, false);
            result        = item1Priority.CompareTo(item2Priority);
            if (result != 0)
            {
                return(result);
            }
            // FINALLY RESORT TO ORDER BY VALUES
            result = item1.OrderBy.CompareTo(item2.OrderBy);
            return(result);
        }