/// <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); }
/// <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()); }