/// <summary>
        /// Deletes this ProductOption object from the database
        /// </summary>
        /// <returns><b>true</b> if delete successful, <b>false</b> otherwise.</returns>
        public virtual bool Delete()
        {
            bool result = this.BaseDelete();

            OptionDataSource.DeleteOrphanedOptions();
            //REBUILD VARIANT GRID FOR THIS PRODUCT
            ProductVariantManager.ResetVariantGrid(this.ProductId);
            return(result);
        }
Beispiel #2
0
        /// <summary>
        /// Gets available choices for a product for given option
        /// </summary>
        /// <param name="productId">Id of the product</param>
        /// <param name="targetOptionId">Id of the option to check for choices</param>
        /// <param name="selectedChoices">A Dictionary<int, int> of option choices already selected.  The dictionary key is the OptionId and the value is the OptionChoiceId.</param>
        /// <returns>Collection of OptionChoice objects</returns>
        public static OptionChoiceCollection GetAvailableChoices(int productId, int targetOptionId, Dictionary <int, int> selectedChoices)
        {
            OptionChoiceCollection availableOptionChoices = new OptionChoiceCollection();
            Product product = ProductDataSource.Load(productId);

            if (product != null)
            {
                // SEE IF OPTIONS EXIST, NOTE WE ARE LOADING THE OPTIONS ACCORDING TO THE SORT SPECIFIED FOR VARIANT MANAGEMENT
                // THIS WILL NOT BE THE SAME AS THE DEFAULT SORT ORDER THAT IS DETERMINED BY THE MERCHANT
                OptionCollection options = OptionDataSource.LoadForProduct(productId, ProductVariant.VARIANT_SORT_EXPRESSION);
                if (options.Count > 0)
                {
                    //CAST OFF ANY ATTRIBUTES PAST THE MAXIMUM ALLOWED COUNT
                    while (options.Count > ProductVariant.MAXIMUM_ATTRIBUTES)
                    {
                        options.RemoveAt(ProductVariant.MAXIMUM_ATTRIBUTES);
                    }

                    // DETERMINE THE INDEX USED OR VARIANT MANAGEMENT, THIS ROUGHLY CORRESPONDS TO THE COLUMN NAMES IN ac_ProductVariants TABLE
                    // BUT NOTE THIS VARIANT INDEX IS ZERO BASED, WHERE THE TABLE COLUMN NAMES ARE 1 BASED: OPTION1, OPTION2, ETC.
                    int targetOptionVariantIndex = options.IndexOf(targetOptionId);
                    if (targetOptionVariantIndex > -1)
                    {
                        // SET THE TARGET OPTION
                        Option targetOption = options[targetOptionVariantIndex];

                        // BUILD THE LIST OF CHOICES SELECTED SO FAR
                        // SELECTED CHOICES KEY IS OPTION INDEX FOR VARIANT MANAGER
                        // THE VALUE IS THE SELECTED CHOICE FOR THAT OPTION INDEX
                        Dictionary <int, int> validSelectedChoices = new Dictionary <int, int>();
                        for (int i = 0; i < options.Count; i++)
                        {
                            Option option = options[i];
                            // IGNORE ANY SELECTED CHOICE FOR THE TARGET OPTION
                            if (option.OptionId != targetOptionId && selectedChoices.ContainsKey(option.OptionId))
                            {
                                // SEE IF THERE IS A CHOICE INDICATED FOR THIS OPTION IN THE QUERY STRING
                                int choiceId    = selectedChoices[option.OptionId];
                                int choiceIndex = option.Choices.IndexOf(choiceId);
                                if (choiceIndex > -1)
                                {
                                    // THIS IS A VALID SELECTED CHOICE
                                    validSelectedChoices.Add(i, choiceId);
                                }
                            }
                        }

                        // IF INVENTORY IS ENABLED, WE SIMPLY QUERY THE VARIANT GRID
                        if (Token.Instance.Store.EnableInventory &&
                            !product.AllowBackorder &&
                            product.InventoryMode == InventoryMode.Variant)
                        {
                            List <int> probableChoices = OptionChoiceDataSource.GetAvailableChoicesFromInventory(productId, validSelectedChoices, targetOptionVariantIndex);
                            // THIS STEP IS PROBABLY UNNEEDED, BUT LETS CONFIRM THE CHOICES FROM THE DATABASE ARE VALID
                            // (THE VARIANT TABLE CANNOT BENEFIT FROM CONSTRAINTS TO ENFORCE DATA INTEGRITY)
                            foreach (int choiceId in probableChoices)
                            {
                                int choiceIndex = targetOption.Choices.IndexOf(choiceId);
                                if (choiceIndex > -1)
                                {
                                    // THIS IS A VALID OPTION CHOICE ID
                                    availableOptionChoices.Add(targetOption.Choices[choiceIndex]);
                                }
                            }
                        }
                        else
                        {
                            // CALCULATE THE REMAINING POSSIBLE OPTION COMBINATIONS FOR EACH CHOICE
                            int possibleCount = 1;
                            foreach (Option option in options)
                            {
                                // WE SHOULD NOT INCLUDE THE TARGET OPTION IN THIS COUNT
                                if (option.OptionId != targetOptionId)
                                {
                                    // WE SHOULD NOT INCLUDE ANY OPTION THAT HAS ALREADY BEEN SELECTED
                                    int variantIndex = options.IndexOf(option.OptionId);
                                    if (!validSelectedChoices.ContainsKey(variantIndex))
                                    {
                                        // WE HAVE FOUND A CHOICE THAT IS NOT SELECTED AND IS NOT
                                        // THE TARGET CHOICE.  SO WE MUST CONSIDER ALL CHOICES AS POTENTIAL
                                        // AND INCLUDE THEM IN THE COUNT
                                        possibleCount = possibleCount * option.Choices.Count;
                                    }
                                }
                            }

                            // BUILD THE DICTIONARY OF UNAVAILABLE COUNTS
                            // THIS WILL BE GROUPED BY THE CHOICEID FOR THE TARGET OPTION
                            // AND WILL TELL US HOW MANY COMBINATIONS ARE INVALID FOR THAT CHOICE
                            // IF THE NUMBER MATCHES THE POSSIBLE COUNT, THEN ALL COMBINATIONS ARE UNAVAILABLE
                            // AND THIS CHOICE SHOULD NOT BE INCLUDED AS AVAILABLE
                            Dictionary <int, int> unavailableVariantCounts = OptionChoiceDataSource.GetUnavailableVariantCounts(productId, validSelectedChoices, targetOptionVariantIndex);
                            foreach (OptionChoice possibleChoice in targetOption.Choices)
                            {
                                // CHECK IF THERE ARE NO UNAVAILABLE VARIANTS FOR THIS CHOICE
                                // OR THE UNAVAILABLE COUNT IS LESS THAN THE POSSIBLE COMBINATION COUNT
                                if (!unavailableVariantCounts.ContainsKey(possibleChoice.OptionChoiceId) ||
                                    unavailableVariantCounts[possibleChoice.OptionChoiceId] < possibleCount)
                                {
                                    // THIS IS A VALID CHOICE
                                    availableOptionChoices.Add(possibleChoice);
                                }
                            }
                        }
                    }
                }
            }
            return(availableOptionChoices);
        }
        /// <summary>
        /// Load the specified product variant
        /// </summary>
        /// <param name="productId">The Id of the product</param>
        /// <param name="optionChoices">An array of int with the option choices</param>
        /// <returns><b>true</b> if load successful, <b>false</b> otherwise</returns>
        public bool Load(int productId, int[] optionChoices)
        {
            //VALIDATE THE NUMBER OF OPTIONS
            if ((optionChoices == null) || (optionChoices.Length == 0))
            {
                return(false);
            }
            int actualProductOptionCount = OptionDataSource.CountForProduct(productId);
            //COUNT THE NUMBER OF NON-ZERO OPTION CHOICES
            int relevantOptionCount = 0;

            foreach (int choice in optionChoices)
            {
                if (choice != 0)
                {
                    relevantOptionCount++;
                }
                else
                {
                    break;
                }
            }
            //IF PASSED IN OPTION COUNT DOES NOT MATCH ACTUAL COUNT, THEY ARE INVALID
            if (relevantOptionCount != actualProductOptionCount)
            {
                return(false);
            }

            //CREATE COMPLETE OPTION ARRAY
            //THE ARRAY MUST BE AT LEAST AS LONG AS THE MAXIMUM NUMBER OF OPTIONS
            int activeCount = Math.Max(MAXIMUM_ATTRIBUTES, optionChoices.Length);

            int[] allOptions  = new int[activeCount];
            int   optionCount = optionChoices.Length;

            for (int i = 0; i < activeCount; i++)
            {
                if (i < optionCount)
                {
                    allOptions[i] = optionChoices[i];
                }
                else
                {
                    allOptions[i] = 0;
                }
            }

            //INITIALIZE THE VALUES OF THIS OBJECT
            this.ProductVariantId = 0;
            this.ProductId        = productId;
            this.Option1          = allOptions[0];
            this.Option2          = allOptions[1];
            this.Option3          = allOptions[2];
            this.Option4          = allOptions[3];
            this.Option5          = allOptions[4];
            this.Option6          = allOptions[5];
            this.Option7          = allOptions[6];
            this.Option8          = allOptions[7];
            _AdditionalOptions    = new List <int>();
            for (int i = MAXIMUM_ATTRIBUTES; i < allOptions.Length; i++)
            {
                _AdditionalOptions.Add(allOptions[i]);
            }
            this.VariantName         = string.Empty;
            this.Sku                 = string.Empty;
            this.Price               = 0;
            this.PriceMode           = ModifierMode.Modify;
            this.Weight              = 0;
            this.WeightMode          = ModifierMode.Modify;
            this.CostOfGoods         = 0;
            this.InStock             = 0;
            this.InStockWarningLevel = 0;
            this.Available           = true;
            this.IsDirty             = false;

            //CREATE THE DYNAMIC SQL TO LOAD
            StringBuilder selectQuery = new StringBuilder();

            selectQuery.Append("SELECT " + GetColumnNames(string.Empty));
            selectQuery.Append(" FROM ac_ProductVariants");
            selectQuery.Append(" WHERE ProductId = @productId");
            //ADD OPTION CRITERIA
            for (int i = 1; i <= MAXIMUM_ATTRIBUTES; i++)
            {
                selectQuery.Append(" AND Option" + i.ToString() + " = @opt" + i.ToString());
            }

            //CREATE DATABASE COMMAND
            Database  database      = Token.Instance.Database;
            DbCommand selectCommand = database.GetSqlStringCommand(selectQuery.ToString());

            database.AddInParameter(selectCommand, "@productId", System.Data.DbType.Int32, productId);
            for (int i = 1; i <= MAXIMUM_ATTRIBUTES; i++)
            {
                database.AddInParameter(selectCommand, "@opt" + i.ToString(), System.Data.DbType.Int32, allOptions[i - 1]);
            }

            //EXECUTE THE COMMAND
            using (IDataReader dr = database.ExecuteReader(selectCommand))
            {
                if (dr.Read())
                {
                    ProductVariant.LoadDataReader(this, dr);;
                }
                dr.Close();
            }

            //CALCULATE THE VARIANT VALUES REGARDLESS OF THE LOAD RESULT
            //THIS ALLOWS THE VALUES TO BE READ IF THIS RECORD DOES NOT EXIST YET IN THE DB
            return(CalculateVariant());
        }