public async Task <ProductDetailsAttributeChangeModel> Handle(GetProductDetailsAttributeChange request, CancellationToken cancellationToken) { var model = new ProductDetailsAttributeChangeModel(); string attributeXml = await _mediator.Send(new GetParseProductAttributes() { Product = request.Product, Form = request.Form }); string warehouseId = _shoppingCartSettings.AllowToSelectWarehouse ? request.Form["WarehouseId"].ToString() : request.Product.UseMultipleWarehouses ? request.Store.DefaultWarehouseId : (string.IsNullOrEmpty(request.Store.DefaultWarehouseId) ? request.Product.WarehouseId : request.Store.DefaultWarehouseId); //rental attributes DateTime?rentalStartDate = null; DateTime?rentalEndDate = null; if (request.Product.ProductType == ProductType.Reservation) { request.Product.ParseReservationDates(request.Form, out rentalStartDate, out rentalEndDate); } model.Sku = request.Product.FormatSku(attributeXml, _productAttributeParser); model.Mpn = request.Product.FormatMpn(attributeXml, _productAttributeParser); model.Gtin = request.Product.FormatGtin(attributeXml, _productAttributeParser); if (await _permissionService.Authorize(StandardPermissionProvider.DisplayPrices) && !request.Product.CustomerEntersPrice && request.Product.ProductType != ProductType.Auction) { //we do not calculate price of "customer enters price" option is enabled var unitprice = await _priceCalculationService.GetUnitPrice(request.Product, request.Customer, ShoppingCartType.ShoppingCart, 1, attributeXml, 0, rentalStartDate, rentalEndDate, true); decimal discountAmount = unitprice.discountAmount; List <AppliedDiscount> scDiscounts = unitprice.appliedDiscounts; decimal finalPrice = unitprice.unitprice; var productprice = await _taxService.GetProductPrice(request.Product, finalPrice); decimal finalPriceWithDiscountBase = productprice.productprice; decimal taxRate = productprice.taxRate; decimal finalPriceWithDiscount = await _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscountBase, request.Currency); model.Price = _priceFormatter.FormatPrice(finalPriceWithDiscount); } //stock model.StockAvailability = request.Product.FormatStockMessage(warehouseId, attributeXml, _localizationService, _productAttributeParser); //back in stock subscription if ((request.Product.ManageInventoryMethod == ManageInventoryMethod.ManageStockByAttributes || request.Product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) && request.Product.BackorderMode == BackorderMode.NoBackorders && request.Product.AllowBackInStockSubscriptions) { var combination = _productAttributeParser.FindProductAttributeCombination(request.Product, attributeXml); if (combination != null) { if (request.Product.GetTotalStockQuantityForCombination(combination, warehouseId: request.Store.DefaultWarehouseId) <= 0) { model.DisplayBackInStockSubscription = true; } } if (request.Product.ManageInventoryMethod == ManageInventoryMethod.ManageStock) { model.DisplayBackInStockSubscription = request.Product.AllowBackInStockSubscriptions; attributeXml = ""; } var subscription = await _backInStockSubscriptionService .FindSubscription(request.Customer.Id, request.Product.Id, attributeXml, request.Store.Id, warehouseId); if (subscription != null) { model.ButtonTextBackInStockSubscription = _localizationService.GetResource("BackInStockSubscriptions.DeleteNotifyWhenAvailable"); } else { model.ButtonTextBackInStockSubscription = _localizationService.GetResource("BackInStockSubscriptions.NotifyMeWhenAvailable"); } } //conditional attributes if (request.ValidateAttributeConditions) { var attributes = request.Product.ProductAttributeMappings; foreach (var attribute in attributes) { var conditionMet = _productAttributeParser.IsConditionMet(request.Product, attribute, attributeXml); if (conditionMet.HasValue) { if (conditionMet.Value) { model.EnabledAttributeMappingIds.Add(attribute.Id); } else { model.DisabledAttributeMappingids.Add(attribute.Id); } } } } //picture. used when we want to override a default product picture when some attribute is selected if (request.LoadPicture) { //first, try to get product attribute combination picture var pictureId = request.Product.ProductAttributeCombinations.Where(x => x.AttributesXml == attributeXml).FirstOrDefault()?.PictureId ?? ""; //then, let's see whether we have attribute values with pictures if (string.IsNullOrEmpty(pictureId)) { pictureId = _productAttributeParser.ParseProductAttributeValues(request.Product, attributeXml) .FirstOrDefault(attributeValue => !string.IsNullOrEmpty(attributeValue.PictureId))?.PictureId ?? ""; } if (!string.IsNullOrEmpty(pictureId)) { var pictureModel = new PictureModel { Id = pictureId, FullSizeImageUrl = await _pictureService.GetPictureUrl(pictureId), ImageUrl = await _pictureService.GetPictureUrl(pictureId, _mediaSettings.ProductDetailsPictureSize) }; model.PictureFullSizeUrl = pictureModel.FullSizeImageUrl; model.PictureDefaultSizeUrl = pictureModel.ImageUrl; } } return(model); }
public virtual async Task <IList <string> > GetShoppingCartItemAttributeWarnings(Customer customer, Product product, ShoppingCartItem shoppingCartItem, bool ignoreNonCombinableAttributes = false) { if (product == null) { throw new ArgumentNullException(nameof(product)); } var warnings = new List <string>(); //ensure it's our attributes var attributes1 = _productAttributeParser.ParseProductAttributeMappings(product, shoppingCartItem.Attributes).ToList(); if (product.ProductTypeId == ProductType.BundledProduct) { foreach (var bundle in product.BundleProducts) { var p1 = await _productService.GetProductById(bundle.ProductId); if (p1 != null) { var a1 = _productAttributeParser.ParseProductAttributeMappings(p1, shoppingCartItem.Attributes).ToList(); attributes1.AddRange(a1); } } } if (ignoreNonCombinableAttributes) { attributes1 = attributes1.Where(x => !x.IsNonCombinable()).ToList(); } //foreach (var attribute in attributes1) //{ // if (string.IsNullOrEmpty(attribute.ProductId)) // { // warnings.Add("Attribute error"); // return warnings; // } //} //validate required product attributes (whether they're chosen/selected/entered) var attributes2 = product.ProductAttributeMappings.ToList(); if (product.ProductTypeId == ProductType.BundledProduct) { foreach (var bundle in product.BundleProducts) { var p1 = await _productService.GetProductById(bundle.ProductId); if (p1 != null && p1.ProductAttributeMappings.Any()) { attributes2.AddRange(p1.ProductAttributeMappings); } } } if (ignoreNonCombinableAttributes) { attributes2 = attributes2.Where(x => !x.IsNonCombinable()).ToList(); } //validate conditional attributes only (if specified) attributes2 = attributes2.Where(x => { var conditionMet = _productAttributeParser.IsConditionMet(product, x, shoppingCartItem.Attributes); return(!conditionMet.HasValue || conditionMet.Value); }).ToList(); foreach (var a2 in attributes2) { if (a2.IsRequired) { bool found = false; //selected product attributes foreach (var a1 in attributes1) { if (a1.Id == a2.Id) { var attributeValuesStr = _productAttributeParser.ParseValues(shoppingCartItem.Attributes, a1.Id); foreach (string str1 in attributeValuesStr) { if (!String.IsNullOrEmpty(str1.Trim())) { found = true; break; } } } } //if not found if (!found) { var paa = await _productAttributeService.GetProductAttributeById(a2.ProductAttributeId); if (paa != null) { var notFoundWarning = !string.IsNullOrEmpty(a2.TextPrompt) ? a2.TextPrompt : string.Format(_translationService.GetResource("ShoppingCart.SelectAttribute"), paa.GetTranslation(a => a.Name, _workContext.WorkingLanguage.Id)); warnings.Add(notFoundWarning); } } } if (a2.AttributeControlTypeId == AttributeControlType.ReadonlyCheckboxes) { //customers cannot edit read-only attributes var allowedReadOnlyValueIds = a2.ProductAttributeValues .Where(x => x.IsPreSelected) .Select(x => x.Id) .ToArray(); var selectedReadOnlyValueIds = _productAttributeParser.ParseProductAttributeValues(product, shoppingCartItem.Attributes) //.Where(x => x.ProductAttributeMappingId == a2.Id) .Select(x => x.Id) .ToArray(); if (!CommonHelper.ArraysEqual(allowedReadOnlyValueIds, selectedReadOnlyValueIds)) { warnings.Add("You cannot change read-only values"); } } } //validation rules foreach (var pam in attributes2) { if (!pam.ValidationRulesAllowed()) { continue; } //minimum length if (pam.ValidationMinLength.HasValue) { if (pam.AttributeControlTypeId == AttributeControlType.TextBox || pam.AttributeControlTypeId == AttributeControlType.MultilineTextbox) { var valuesStr = _productAttributeParser.ParseValues(shoppingCartItem.Attributes, pam.Id); var enteredText = valuesStr.FirstOrDefault(); int enteredTextLength = String.IsNullOrEmpty(enteredText) ? 0 : enteredText.Length; if (pam.ValidationMinLength.Value > enteredTextLength) { var _pam = await _productAttributeService.GetProductAttributeById(pam.ProductAttributeId); warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.TextboxMinimumLength"), _pam.GetTranslation(a => a.Name, _workContext.WorkingLanguage.Id), pam.ValidationMinLength.Value)); } } } //maximum length if (pam.ValidationMaxLength.HasValue) { if (pam.AttributeControlTypeId == AttributeControlType.TextBox || pam.AttributeControlTypeId == AttributeControlType.MultilineTextbox) { var valuesStr = _productAttributeParser.ParseValues(shoppingCartItem.Attributes, pam.Id); var enteredText = valuesStr.FirstOrDefault(); int enteredTextLength = string.IsNullOrEmpty(enteredText) ? 0 : enteredText.Length; if (pam.ValidationMaxLength.Value < enteredTextLength) { var _pam = await _productAttributeService.GetProductAttributeById(pam.ProductAttributeId); warnings.Add(string.Format(_translationService.GetResource("ShoppingCart.TextboxMaximumLength"), _pam.GetTranslation(a => a.Name, _workContext.WorkingLanguage.Id), pam.ValidationMaxLength.Value)); } } } } if (warnings.Any()) { return(warnings); } //validate bundled products var attributeValues = _productAttributeParser.ParseProductAttributeValues(product, shoppingCartItem.Attributes); foreach (var attributeValue in attributeValues) { var _productAttributeMapping = product.ProductAttributeMappings.Where(x => x.ProductAttributeValues.Any(z => z.Id == attributeValue.Id)).FirstOrDefault(); //TODO - check product.ProductAttributeMappings.Where(x => x.Id == attributeValue.ProductAttributeMappingId).FirstOrDefault(); if (attributeValue.AttributeValueTypeId == AttributeValueType.AssociatedToProduct && _productAttributeMapping != null) { if (ignoreNonCombinableAttributes && _productAttributeMapping.IsNonCombinable()) { continue; } //associated product (bundle) var associatedProduct = await _productService.GetProductById(attributeValue.AssociatedProductId); if (associatedProduct != null) { var totalQty = shoppingCartItem.Quantity * attributeValue.Quantity; var associatedProductWarnings = await GetShoppingCartItemWarnings(customer, new ShoppingCartItem() { ShoppingCartTypeId = shoppingCartItem.ShoppingCartTypeId, StoreId = _workContext.CurrentStore.Id, Quantity = totalQty, WarehouseId = shoppingCartItem.WarehouseId }, associatedProduct, new ShoppingCartValidatorOptions()); foreach (var associatedProductWarning in associatedProductWarnings) { var productAttribute = await _productAttributeService.GetProductAttributeById(_productAttributeMapping.ProductAttributeId); var attributeName = productAttribute.GetTranslation(a => a.Name, _workContext.WorkingLanguage.Id); var attributeValueName = attributeValue.GetTranslation(a => a.Name, _workContext.WorkingLanguage.Id); warnings.Add(string.Format( _translationService.GetResource("ShoppingCart.AssociatedAttributeWarning"), attributeName, attributeValueName, associatedProductWarning)); } } else { warnings.Add(string.Format("Associated product cannot be loaded - {0}", attributeValue.AssociatedProductId)); } } } return(warnings); }
public async Task <string> Handle(GetParseProductAttributes request, CancellationToken cancellationToken) { string attributesXml = ""; #region Product attributes var productAttributes = request.Product.ProductAttributeMappings.ToList(); if (request.Product.ProductType == ProductType.BundledProduct) { foreach (var bundle in request.Product.BundleProducts) { var bp = await _productService.GetProductById(bundle.ProductId); if (bp.ProductAttributeMappings.Any()) { productAttributes.AddRange(bp.ProductAttributeMappings); } } } foreach (var attribute in productAttributes) { string controlId = string.Format("product_attribute_{0}", attribute.Id); switch (attribute.AttributeControlType) { case AttributeControlType.DropdownList: case AttributeControlType.RadioList: case AttributeControlType.ColorSquares: case AttributeControlType.ImageSquares: { var ctrlAttributes = request.Form[controlId]; if (!String.IsNullOrEmpty(ctrlAttributes)) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, ctrlAttributes); } } break; case AttributeControlType.Checkboxes: { var ctrlAttributes = request.Form[controlId].ToString(); if (!String.IsNullOrEmpty(ctrlAttributes)) { foreach (var item in ctrlAttributes.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { if (!String.IsNullOrEmpty(item)) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, item); } } } } break; case AttributeControlType.ReadonlyCheckboxes: { //load read-only (already server-side selected) values var attributeValues = attribute.ProductAttributeValues; foreach (var selectedAttributeId in attributeValues .Where(v => v.IsPreSelected) .Select(v => v.Id) .ToList()) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, selectedAttributeId); } } break; case AttributeControlType.TextBox: case AttributeControlType.MultilineTextbox: { var ctrlAttributes = request.Form[controlId].ToString(); if (!String.IsNullOrEmpty(ctrlAttributes)) { string enteredText = ctrlAttributes.Trim(); attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, enteredText); } } break; case AttributeControlType.Datepicker: { var day = request.Form[controlId + "_day"]; var month = request.Form[controlId + "_month"]; var year = request.Form[controlId + "_year"]; DateTime?selectedDate = null; try { selectedDate = new DateTime(Int32.Parse(year), Int32.Parse(month), Int32.Parse(day)); } catch { } if (selectedDate.HasValue) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, selectedDate.Value.ToString("D")); } } break; case AttributeControlType.FileUpload: { Guid downloadGuid; Guid.TryParse(request.Form[controlId], out downloadGuid); var download = await _downloadService.GetDownloadByGuid(downloadGuid); if (download != null) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, download.DownloadGuid.ToString()); } } break; default: break; } } //validate conditional attributes (if specified) foreach (var attribute in productAttributes) { var conditionMet = _productAttributeParser.IsConditionMet(request.Product, attribute, attributesXml); if (conditionMet.HasValue && !conditionMet.Value) { attributesXml = _productAttributeParser.RemoveProductAttribute(attributesXml, attribute); } } #endregion #region Gift cards if (request.Product.IsGiftCard) { string recipientName = ""; string recipientEmail = ""; string senderName = ""; string senderEmail = ""; string giftCardMessage = ""; foreach (string formKey in request.Form.Keys) { if (formKey.Equals(string.Format("giftcard_{0}.RecipientName", request.Product.Id), StringComparison.OrdinalIgnoreCase)) { recipientName = request.Form[formKey]; continue; } if (formKey.Equals(string.Format("giftcard_{0}.RecipientEmail", request.Product.Id), StringComparison.OrdinalIgnoreCase)) { recipientEmail = request.Form[formKey]; continue; } if (formKey.Equals(string.Format("giftcard_{0}.SenderName", request.Product.Id), StringComparison.OrdinalIgnoreCase)) { senderName = request.Form[formKey]; continue; } if (formKey.Equals(string.Format("giftcard_{0}.SenderEmail", request.Product.Id), StringComparison.OrdinalIgnoreCase)) { senderEmail = request.Form[formKey]; continue; } if (formKey.Equals(string.Format("giftcard_{0}.Message", request.Product.Id), StringComparison.OrdinalIgnoreCase)) { giftCardMessage = request.Form[formKey]; continue; } } attributesXml = _productAttributeParser.AddGiftCardAttribute(attributesXml, recipientName, recipientEmail, senderName, senderEmail, giftCardMessage); } #endregion return(attributesXml); }
// TODO // use it from ShoppingCartController private string ParseProductAttributes(Product product, IFormCollection form, List <string> errors) { var attributesXml = string.Empty; var productAttributes = _productAttributeService.GetProductAttributeMappingsByProductId(product.Id); foreach (var attribute in productAttributes) { var controlId = $"product_attribute_{attribute.Id}"; switch (attribute.AttributeControlType) { case AttributeControlType.DropdownList: case AttributeControlType.RadioList: case AttributeControlType.ColorSquares: case AttributeControlType.ImageSquares: { var ctrlAttributes = form[controlId]; if (!StringValues.IsNullOrEmpty(ctrlAttributes)) { var selectedAttributeId = int.Parse(ctrlAttributes); if (selectedAttributeId > 0) { //get quantity entered by customer var quantity = 1; var quantityStr = form[$"product_attribute_{attribute.Id}_{selectedAttributeId}_qty"]; if (!StringValues.IsNullOrEmpty(quantityStr) && (!int.TryParse(quantityStr, out quantity) || quantity < 1)) { errors.Add(_localizationService.GetResource("ShoppingCart.QuantityShouldPositive")); } attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, selectedAttributeId.ToString(), quantity > 1 ? (int?)quantity : null); } } } break; case AttributeControlType.Checkboxes: { var ctrlAttributes = form[controlId]; if (!StringValues.IsNullOrEmpty(ctrlAttributes)) { foreach (var item in ctrlAttributes.ToString().Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) { var selectedAttributeId = int.Parse(item); if (selectedAttributeId > 0) { //get quantity entered by customer var quantity = 1; var quantityStr = form[$"product_attribute_{attribute.Id}_{item}_qty"]; if (!StringValues.IsNullOrEmpty(quantityStr) && (!int.TryParse(quantityStr, out quantity) || quantity < 1)) { errors.Add(_localizationService.GetResource("ShoppingCart.QuantityShouldPositive")); } attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, selectedAttributeId.ToString(), quantity > 1 ? (int?)quantity : null); } } } } break; case AttributeControlType.ReadonlyCheckboxes: { //load read-only (already server-side selected) values var attributeValues = _productAttributeService.GetProductAttributeValues(attribute.Id); foreach (var selectedAttributeId in attributeValues .Where(v => v.IsPreSelected) .Select(v => v.Id) .ToList()) { //get quantity entered by customer var quantity = 1; var quantityStr = form[$"product_attribute_{attribute.Id}_{selectedAttributeId}_qty"]; if (!StringValues.IsNullOrEmpty(quantityStr) && (!int.TryParse(quantityStr, out quantity) || quantity < 1)) { errors.Add(_localizationService.GetResource("ShoppingCart.QuantityShouldPositive")); } attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, selectedAttributeId.ToString(), quantity > 1 ? (int?)quantity : null); } } break; case AttributeControlType.TextBox: case AttributeControlType.MultilineTextbox: { var ctrlAttributes = form[controlId]; if (!StringValues.IsNullOrEmpty(ctrlAttributes)) { var enteredText = ctrlAttributes.ToString().Trim(); attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, enteredText); } } break; case AttributeControlType.Datepicker: { var day = form[controlId + "_day"]; var month = form[controlId + "_month"]; var year = form[controlId + "_year"]; DateTime?selectedDate = null; try { selectedDate = new DateTime(Int32.Parse(year), Int32.Parse(month), Int32.Parse(day)); } catch { } if (selectedDate.HasValue) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, selectedDate.Value.ToString("D")); } } break; case AttributeControlType.FileUpload: { Guid.TryParse(form[controlId], out Guid downloadGuid); var download = _downloadService.GetDownloadByGuid(downloadGuid); if (download != null) { attributesXml = _productAttributeParser.AddProductAttribute(attributesXml, attribute, download.DownloadGuid.ToString()); } } break; default: break; } } //validate conditional attributes (if specified) foreach (var attribute in productAttributes) { var conditionMet = _productAttributeParser.IsConditionMet(attribute, attributesXml); if (conditionMet.HasValue && !conditionMet.Value) { attributesXml = _productAttributeParser.RemoveProductAttribute(attributesXml, attribute); } } return(attributesXml); }