public async Task <ProductAttributeMapping> GetAttributeMappingByNameAsync( string attributesXml, string name ) { var productAttributeMappings = await _productAttributeParser.ParseProductAttributeMappingsAsync(attributesXml); foreach (var pam in productAttributeMappings) { var productAttribute = await _productAttributeService.GetProductAttributeByIdAsync(pam.ProductAttributeId); if (productAttribute == null) { continue; } if (productAttribute.Name == name) { return(pam); } } return(null); }
/// <summary> /// 格式化属性描述 /// </summary> /// <param name="product">商品</param> /// <param name="attributesJson">属性json对象</param> /// <param name="serapator">分隔符</param> /// <param name="htmlEncode">是否需要Html编码</param> /// <returns>格式化实行描述</returns> public async Task<string> FormatAttributesAsync(Product product, List<JsonProductAttribute> attributesJson, string serapator = "<br />", bool htmlEncode = true) { var result = new StringBuilder(); var attributeMappings = await _productAttributeParser.ParseProductAttributeMappingsAsync(product.Id, attributesJson); foreach (var attributeMapping in attributeMappings) { await _productAttributeManager.ProductAttributeMappingRepository.EnsurePropertyLoadedAsync(attributeMapping, a => a.ProductAttribute); //attributes without values if (!attributeMapping.ShouldHaveValues()) { foreach (var value in await _productAttributeParser.ParseProductAttributeValuesAsync(product.Id, attributesJson, attributeMapping.Id)) { var formattedAttribute = string.Empty; //other attributes (textbox, datepicker) formattedAttribute = string.Format("{0}: {1}", attributeMapping.ProductAttribute.Name, value); //encode (if required) if (htmlEncode) formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute); if (!string.IsNullOrEmpty(formattedAttribute)) { if (result.Length > 0) result.Append(serapator); result.Append(formattedAttribute); } } } //product attribute values else { foreach (var attributeValue in await _productAttributeParser.ParseProductAttributeValuesAsync(product.Id, attributesJson, attributeMapping.ProductAttributeId)) { var formattedAttribute = string.Format("{0}: {1}", attributeMapping.ProductAttribute.Name, attributeValue.Name); //encode (if required) if (htmlEncode) formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute); if (!string.IsNullOrEmpty(formattedAttribute)) { if (result.Length > 0) result.Append(serapator); result.Append(formattedAttribute); } } } } return result.ToString(); }
// If non-mattress passed in, you'll get the normal price private async Task <decimal> GetMattressItemCostAsync(int productId, string attributesXml) { var product = await _productService.GetProductByIdAsync(productId); var productAttributes = await _productAttributeService.GetAllProductAttributesAsync(); var mattressProductAttributeIds = productAttributes .Where(pa => pa.Name.Contains("Mattress Size") || pa.Name.Contains("Base (")) .Select(pa => pa.Id); var pams = await _productAttributeParser.ParseProductAttributeMappingsAsync(attributesXml); return(product.Price + pams .Where(pam => mattressProductAttributeIds.Contains(pam.ProductAttributeId)) .Select(pam => _productAttributeParser.ParseValues(attributesXml, pam.Id).FirstOrDefault()) .Select(async idasstring => await _productAttributeService.GetProductAttributeValueByIdAsync(int.Parse(idasstring))) .Select(t => t.Result) .Sum(pav => pav.PriceAdjustment)); }
/// <summary> /// Copy attributes mapping /// </summary> /// <param name="product">Product</param> /// <param name="productCopy">New product</param> /// <param name="originalNewPictureIdentifiers">Identifiers of pictures</param> /// <returns>A task that represents the asynchronous operation</returns> protected virtual async Task CopyAttributesMappingAsync(Product product, Product productCopy, Dictionary <int, int> originalNewPictureIdentifiers) { var associatedAttributes = new Dictionary <int, int>(); var associatedAttributeValues = new Dictionary <int, int>(); //attribute mapping with condition attributes var oldCopyWithConditionAttributes = new List <ProductAttributeMapping>(); //all product attribute mapping copies var productAttributeMappingCopies = new Dictionary <int, ProductAttributeMapping>(); var languages = await _languageService.GetAllLanguagesAsync(true); foreach (var productAttributeMapping in await _productAttributeService.GetProductAttributeMappingsByProductIdAsync(product.Id)) { var productAttributeMappingCopy = new ProductAttributeMapping { ProductId = productCopy.Id, ProductAttributeId = productAttributeMapping.ProductAttributeId, TextPrompt = productAttributeMapping.TextPrompt, IsRequired = productAttributeMapping.IsRequired, AttributeControlTypeId = productAttributeMapping.AttributeControlTypeId, DisplayOrder = productAttributeMapping.DisplayOrder, ValidationMinLength = productAttributeMapping.ValidationMinLength, ValidationMaxLength = productAttributeMapping.ValidationMaxLength, ValidationFileAllowedExtensions = productAttributeMapping.ValidationFileAllowedExtensions, ValidationFileMaximumSize = productAttributeMapping.ValidationFileMaximumSize, DefaultValue = productAttributeMapping.DefaultValue }; await _productAttributeService.InsertProductAttributeMappingAsync(productAttributeMappingCopy); //localization foreach (var lang in languages) { var textPrompt = await _localizationService.GetLocalizedAsync(productAttributeMapping, x => x.TextPrompt, lang.Id, false, false); if (!string.IsNullOrEmpty(textPrompt)) { await _localizedEntityService.SaveLocalizedValueAsync(productAttributeMappingCopy, x => x.TextPrompt, textPrompt, lang.Id); } } productAttributeMappingCopies.Add(productAttributeMappingCopy.Id, productAttributeMappingCopy); if (!string.IsNullOrEmpty(productAttributeMapping.ConditionAttributeXml)) { oldCopyWithConditionAttributes.Add(productAttributeMapping); } //save associated value (used for combinations copying) associatedAttributes.Add(productAttributeMapping.Id, productAttributeMappingCopy.Id); // product attribute values var productAttributeValues = await _productAttributeService.GetProductAttributeValuesAsync(productAttributeMapping.Id); foreach (var productAttributeValue in productAttributeValues) { var attributeValuePictureId = 0; if (originalNewPictureIdentifiers.ContainsKey(productAttributeValue.PictureId)) { attributeValuePictureId = originalNewPictureIdentifiers[productAttributeValue.PictureId]; } var attributeValueCopy = new ProductAttributeValue { ProductAttributeMappingId = productAttributeMappingCopy.Id, AttributeValueTypeId = productAttributeValue.AttributeValueTypeId, AssociatedProductId = productAttributeValue.AssociatedProductId, Name = productAttributeValue.Name, ColorSquaresRgb = productAttributeValue.ColorSquaresRgb, PriceAdjustment = productAttributeValue.PriceAdjustment, PriceAdjustmentUsePercentage = productAttributeValue.PriceAdjustmentUsePercentage, WeightAdjustment = productAttributeValue.WeightAdjustment, Cost = productAttributeValue.Cost, CustomerEntersQty = productAttributeValue.CustomerEntersQty, Quantity = productAttributeValue.Quantity, IsPreSelected = productAttributeValue.IsPreSelected, DisplayOrder = productAttributeValue.DisplayOrder, PictureId = attributeValuePictureId, }; //picture associated to "iamge square" attribute type (if exists) if (productAttributeValue.ImageSquaresPictureId > 0) { var origImageSquaresPicture = await _pictureService.GetPictureByIdAsync(productAttributeValue.ImageSquaresPictureId); if (origImageSquaresPicture != null) { //copy the picture var imageSquaresPictureCopy = await _pictureService.InsertPictureAsync( await _pictureService.LoadPictureBinaryAsync(origImageSquaresPicture), origImageSquaresPicture.MimeType, origImageSquaresPicture.SeoFilename, origImageSquaresPicture.AltAttribute, origImageSquaresPicture.TitleAttribute); attributeValueCopy.ImageSquaresPictureId = imageSquaresPictureCopy.Id; } } await _productAttributeService.InsertProductAttributeValueAsync(attributeValueCopy); //save associated value (used for combinations copying) associatedAttributeValues.Add(productAttributeValue.Id, attributeValueCopy.Id); //localization foreach (var lang in languages) { var name = await _localizationService.GetLocalizedAsync(productAttributeValue, x => x.Name, lang.Id, false, false); if (!string.IsNullOrEmpty(name)) { await _localizedEntityService.SaveLocalizedValueAsync(attributeValueCopy, x => x.Name, name, lang.Id); } } } } //copy attribute conditions foreach (var productAttributeMapping in oldCopyWithConditionAttributes) { var oldConditionAttributeMapping = (await _productAttributeParser .ParseProductAttributeMappingsAsync(productAttributeMapping.ConditionAttributeXml)).FirstOrDefault(); if (oldConditionAttributeMapping == null) { continue; } var oldConditionValues = await _productAttributeParser.ParseProductAttributeValuesAsync( productAttributeMapping.ConditionAttributeXml, oldConditionAttributeMapping.Id); if (!oldConditionValues.Any()) { continue; } var newAttributeMappingId = associatedAttributes[oldConditionAttributeMapping.Id]; var newConditionAttributeMapping = productAttributeMappingCopies[newAttributeMappingId]; var newConditionAttributeXml = string.Empty; foreach (var oldConditionValue in oldConditionValues) { newConditionAttributeXml = _productAttributeParser.AddProductAttribute(newConditionAttributeXml, newConditionAttributeMapping, associatedAttributeValues[oldConditionValue.Id].ToString()); } var attributeMappingId = associatedAttributes[productAttributeMapping.Id]; var conditionAttribute = productAttributeMappingCopies[attributeMappingId]; conditionAttribute.ConditionAttributeXml = newConditionAttributeXml; await _productAttributeService.UpdateProductAttributeMappingAsync(conditionAttribute); } //attribute combinations foreach (var combination in await _productAttributeService.GetAllProductAttributeCombinationsAsync(product.Id)) { //generate new AttributesXml according to new value IDs var newAttributesXml = string.Empty; var parsedProductAttributes = await _productAttributeParser.ParseProductAttributeMappingsAsync(combination.AttributesXml); foreach (var oldAttribute in parsedProductAttributes) { if (!associatedAttributes.ContainsKey(oldAttribute.Id)) { continue; } var newAttribute = await _productAttributeService.GetProductAttributeMappingByIdAsync(associatedAttributes[oldAttribute.Id]); if (newAttribute == null) { continue; } var oldAttributeValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldAttribute.Id); foreach (var oldAttributeValueStr in oldAttributeValuesStr) { if (newAttribute.ShouldHaveValues()) { //attribute values var oldAttributeValue = int.Parse(oldAttributeValueStr); if (!associatedAttributeValues.ContainsKey(oldAttributeValue)) { continue; } var newAttributeValue = await _productAttributeService.GetProductAttributeValueByIdAsync(associatedAttributeValues[oldAttributeValue]); if (newAttributeValue != null) { newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newAttribute, newAttributeValue.Id.ToString()); } } else { //just a text newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml, newAttribute, oldAttributeValueStr); } } } //picture originalNewPictureIdentifiers.TryGetValue(combination.PictureId, out var combinationPictureId); var combinationCopy = new ProductAttributeCombination { ProductId = productCopy.Id, AttributesXml = newAttributesXml, StockQuantity = combination.StockQuantity, MinStockQuantity = combination.MinStockQuantity, AllowOutOfStockOrders = combination.AllowOutOfStockOrders, Sku = combination.Sku, ManufacturerPartNumber = combination.ManufacturerPartNumber, Gtin = combination.Gtin, OverriddenPrice = combination.OverriddenPrice, NotifyAdminForQuantityBelow = combination.NotifyAdminForQuantityBelow, PictureId = combinationPictureId }; await _productAttributeService.InsertProductAttributeCombinationAsync(combinationCopy); //quantity change history await _productService.AddStockQuantityHistoryEntryAsync(productCopy, combination.StockQuantity, combination.StockQuantity, message : string.Format(await _localizationService.GetResourceAsync("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id), combinationId : combination.Id); } }
/// <summary> /// Formats attributes /// </summary> /// <param name="product">Product</param> /// <param name="attributesXml">Attributes in XML format</param> /// <param name="customer">Customer</param> /// <param name="separator">Separator</param> /// <param name="htmlEncode">A value indicating whether to encode (HTML) values</param> /// <param name="renderPrices">A value indicating whether to render prices</param> /// <param name="renderProductAttributes">A value indicating whether to render product attributes</param> /// <param name="renderGiftCardAttributes">A value indicating whether to render gift card attributes</param> /// <param name="allowHyperlinks">A value indicating whether to HTML hyperink tags could be rendered (if required)</param> /// <returns> /// A task that represents the asynchronous operation /// The task result contains the attributes /// </returns> public virtual async Task <string> FormatAttributesAsync(Product product, string attributesXml, Customer customer, string separator = "<br />", bool htmlEncode = true, bool renderPrices = true, bool renderProductAttributes = true, bool renderGiftCardAttributes = true, bool allowHyperlinks = true) { var result = new StringBuilder(); //attributes if (renderProductAttributes) { foreach (var attribute in await _productAttributeParser.ParseProductAttributeMappingsAsync(attributesXml)) { var productAttribute = await _productAttributeService.GetProductAttributeByIdAsync(attribute.ProductAttributeId); var attributeName = await _localizationService.GetLocalizedAsync(productAttribute, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id); //attributes without values if (!attribute.ShouldHaveValues()) { foreach (var value in _productAttributeParser.ParseValues(attributesXml, attribute.Id)) { var formattedAttribute = string.Empty; if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox) { //encode (if required) if (htmlEncode) { attributeName = WebUtility.HtmlEncode(attributeName); } //we never encode multiline textbox input formattedAttribute = $"{attributeName}: {HtmlHelper.FormatText(value, false, true, false, false, false, false)}"; } else if (attribute.AttributeControlType == AttributeControlType.FileUpload) { //file upload Guid.TryParse(value, out var downloadGuid); var download = await _downloadService.GetDownloadByGuidAsync(downloadGuid); if (download != null) { var fileName = $"{download.Filename ?? download.DownloadGuid.ToString()}{download.Extension}"; //encode (if required) if (htmlEncode) { fileName = WebUtility.HtmlEncode(fileName); } var attributeText = allowHyperlinks ? $"<a href=\"{_webHelper.GetStoreLocation(false)}download/getfileupload/?downloadId={download.DownloadGuid}\" class=\"fileuploadattribute\">{fileName}</a>" : fileName; //encode (if required) if (htmlEncode) { attributeName = WebUtility.HtmlEncode(attributeName); } formattedAttribute = $"{attributeName}: {attributeText}"; } } else { //other attributes (textbox, datepicker) formattedAttribute = $"{attributeName}: {value}"; //encode (if required) if (htmlEncode) { formattedAttribute = WebUtility.HtmlEncode(formattedAttribute); } } if (string.IsNullOrEmpty(formattedAttribute)) { continue; } if (result.Length > 0) { result.Append(separator); } result.Append(formattedAttribute); } } //product attribute values else { foreach (var attributeValue in await _productAttributeParser.ParseProductAttributeValuesAsync(attributesXml, attribute.Id)) { var formattedAttribute = $"{attributeName}: {await _localizationService.GetLocalizedAsync(attributeValue, a => a.Name, (await _workContext.GetWorkingLanguageAsync()).Id)}"; if (renderPrices) { if (attributeValue.PriceAdjustmentUsePercentage) { if (attributeValue.PriceAdjustment > decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "+", attributeValue.PriceAdjustment.ToString("G29"), "%"); } else if (attributeValue.PriceAdjustment < decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), string.Empty, attributeValue.PriceAdjustment.ToString("G29"), "%"); } } else { var attributeValuePriceAdjustment = await _priceCalculationService.GetProductAttributeValuePriceAdjustmentAsync(product, attributeValue, customer); var(priceAdjustmentBase, _) = await _taxService.GetProductPriceAsync(product, attributeValuePriceAdjustment, customer); var priceAdjustment = await _currencyService.ConvertFromPrimaryStoreCurrencyAsync(priceAdjustmentBase, await _workContext.GetWorkingCurrencyAsync()); if (priceAdjustmentBase > decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "+", await _priceFormatter.FormatPriceAsync(priceAdjustment, false, false), string.Empty); } else if (priceAdjustmentBase < decimal.Zero) { formattedAttribute += string.Format( await _localizationService.GetResourceAsync("FormattedAttributes.PriceAdjustment"), "-", await _priceFormatter.FormatPriceAsync(-priceAdjustment, false, false), string.Empty); } } } //display quantity if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity && attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct) { //render only when more than 1 if (attributeValue.Quantity > 1) { formattedAttribute += string.Format(await _localizationService.GetResourceAsync("ProductAttributes.Quantity"), attributeValue.Quantity); } } //encode (if required) if (htmlEncode) { formattedAttribute = WebUtility.HtmlEncode(formattedAttribute); } if (string.IsNullOrEmpty(formattedAttribute)) { continue; } if (result.Length > 0) { result.Append(separator); } result.Append(formattedAttribute); } } } } //gift cards if (!renderGiftCardAttributes) { return(result.ToString()); } if (!product.IsGiftCard) { return(result.ToString()); } _productAttributeParser.GetGiftCardAttribute(attributesXml, out var giftCardRecipientName, out var giftCardRecipientEmail, out var giftCardSenderName, out var giftCardSenderEmail, out var _); //sender var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ? string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) : string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.From.Physical"), giftCardSenderName); //recipient var giftCardFor = product.GiftCardType == GiftCardType.Virtual ? string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) : string.Format(await _localizationService.GetResourceAsync("GiftCardAttribute.For.Physical"), giftCardRecipientName); //encode (if required) if (htmlEncode) { giftCardFrom = WebUtility.HtmlEncode(giftCardFrom); giftCardFor = WebUtility.HtmlEncode(giftCardFor); } if (!string.IsNullOrEmpty(result.ToString())) { result.Append(separator); } result.Append(giftCardFrom); result.Append(separator); result.Append(giftCardFor); return(result.ToString()); }
/// <summary> /// Gets available shipping options /// </summary> /// <param name="getShippingOptionRequest">A request for getting shipping options</param> /// <returns>Represents a response of getting shipping rate options</returns> public async Task <GetShippingOptionResponse> GetShippingOptionsAsync(GetShippingOptionRequest getShippingOptionRequest) { if (getShippingOptionRequest == null) { throw new ArgumentNullException("getShippingOptionRequest"); } var response = new GetShippingOptionResponse(); if (getShippingOptionRequest.Items == null) { response.AddError("No shipment items"); return(response); } if (getShippingOptionRequest.ShippingAddress == null) { response.AddError("Shipping address is not set"); return(response); } if (getShippingOptionRequest.ShippingAddress.CountryId == null) { response.AddError("Shipping country is not set"); return(response); } //TODO clone the request to prevent issues cause by modification var it = getShippingOptionRequest.Items.GetEnumerator(); var homeDeliveryList = new List <GetShippingOptionRequest.PackageItem>(); var pickupInStoreList = new List <GetShippingOptionRequest.PackageItem>(); //find and separately process all home delivery items foreach (var item in getShippingOptionRequest.Items) { //checking first for items that are pickup in store so they dont get charged as home delivery string itemAttributeXml = item.ShoppingCartItem.AttributesXml; List <ProductAttributeMapping> pam = (await _productAttributeParser.ParseProductAttributeMappingsAsync(itemAttributeXml)).ToList(); if (await pam.Select(p => p).WhereAwait(async p => ((await _productAttributeService.GetProductAttributeByIdAsync(p.ProductAttributeId)).Name == "Pickup")).CountAsync() > 0) { pickupInStoreList.Add(item); } // also checking product attribute (why not only check the product attribute?) else if (_productHomeDeliveryRepo.Table.Any(phd => phd.Product_Id == item.ShoppingCartItem.ProductId) || await pam.Select(p => p) .WhereAwait(async p => ((await _productAttributeService.GetProductAttributeByIdAsync(p.ProductAttributeId)).Name == "Home Delivery")) .AnyAsync()) { homeDeliveryList.Add(item); } } foreach (var item in pickupInStoreList) { getShippingOptionRequest.Items.Remove(item); } foreach (var item in homeDeliveryList) { getShippingOptionRequest.Items.Remove(item); } var homeDeliveryCharge = await _homeDeliveryCostService.GetHomeDeliveryCostAsync(homeDeliveryList); //if there are items to be shipped, use the base calculation service if items remain that will be shipped by ups if (getShippingOptionRequest.Items.Count > 0) { var oldProtocol = ServicePointManager.SecurityProtocol; try { ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12 | SecurityProtocolType.Tls11 | System.Net.SecurityProtocolType.Tls; response = await _baseShippingComputation.GetShippingOptionsAsync(getShippingOptionRequest); } catch { throw; } finally { ServicePointManager.SecurityProtocol = oldProtocol; } if (homeDeliveryList.Count > 0) { var zip = getShippingOptionRequest.ShippingAddress.ZipPostalCode; await CheckZipcodeAsync(zip, response); foreach (var shippingOption in response.ShippingOptions) { shippingOption.Name += " and Home Delivery"; shippingOption.Rate += homeDeliveryCharge; } } }//else the cart contains only home delivery and/or pickup in store else if (homeDeliveryList.Count > 0) { var zip = getShippingOptionRequest.ShippingAddress.ZipPostalCode; await CheckZipcodeAsync(zip, response); response.ShippingOptions.Add(new ShippingOption { Name = "Home Delivery", Rate = homeDeliveryCharge }); }//else the cart contains only pickup in store else { response.ShippingOptions.Add(new ShippingOption { Name = "Pickup in Store", Rate = decimal.Zero }); } return(response); }