protected void CalculateProductPrices(ProductVariant product, Currency currency, out decimal price, out decimal dicountedPrice) { decimal tax = 0, tax2 = 0; price = _priceCalculationService.GetFinalPrice(product, DefaultCustomer, false, false); dicountedPrice = _priceCalculationService.GetFinalPrice(product, DefaultCustomer, true, false); price = _taxService.GetProductPrice(product, price, DefaultCustomer, out tax); dicountedPrice = _taxService.GetProductPrice(product, dicountedPrice, DefaultCustomer, out tax2); //price = _currencyService.ConvertFromPrimaryStoreCurrency(price, currency); //dicountedPrice = _currencyService.ConvertFromPrimaryStoreCurrency(dicountedPrice, currency); }
/// <summary> /// Gets the base price info /// </summary> /// <param name="product">Product</param> /// <param name="localizationService">Localization service</param> /// <param name="priceFormatter">Price formatter</param> /// <param name="currencyService">Currency service</param> /// <param name="taxService">Tax service</param> /// <param name="priceCalculationService">Price calculation service</param> /// <param name="customer">Customer</param> /// <param name="currency">Target currency</param> /// <param name="priceAdjustment">Price adjustment</param> /// <returns>The base price info</returns> public static string GetBasePriceInfo(this Product product, ILocalizationService localizationService, IPriceFormatter priceFormatter, ICurrencyService currencyService, ITaxService taxService, IPriceCalculationService priceCalculationService, Customer customer, Currency currency, decimal priceAdjustment = decimal.Zero) { Guard.NotNull(product, nameof(product)); Guard.NotNull(currencyService, nameof(currencyService)); Guard.NotNull(taxService, nameof(taxService)); Guard.NotNull(priceCalculationService, nameof(priceCalculationService)); Guard.NotNull(customer, nameof(customer)); Guard.NotNull(currency, nameof(currency)); if (product.BasePriceHasValue && product.BasePriceAmount != decimal.Zero) { var currentPrice = priceCalculationService.GetFinalPrice(product, customer, true); var price = taxService.GetProductPrice(product, decimal.Add(currentPrice, priceAdjustment), customer, currency, out var taxrate); price = currencyService.ConvertFromPrimaryStoreCurrency(price, currency); return(product.GetBasePriceInfo(price, localizationService, priceFormatter, currency)); } return(string.Empty); }
private OrderItem PrepareDefaultOrderItemFromProduct(Order order, Product product) { var presetQty = 1; var presetPrice = _priceCalculationService.GetFinalPrice(product, order.Customer, decimal.Zero, true, presetQty); var presetPriceInclTax = _taxService.GetProductPrice(product, presetPrice, true, order.Customer, out _); var presetPriceExclTax = _taxService.GetProductPrice(product, presetPrice, false, order.Customer, out _); var orderItem = new OrderItem { OrderItemGuid = new Guid(), UnitPriceExclTax = presetPriceExclTax, UnitPriceInclTax = presetPriceInclTax, PriceInclTax = presetPriceInclTax, PriceExclTax = presetPriceExclTax, OriginalProductCost = _priceCalculationService.GetProductCost(product, null), Quantity = presetQty, Product = product, Order = order }; return(orderItem); }
/// <summary> /// Gets the base price info /// </summary> /// <param name="product">Product</param> /// <param name="localizationService">Localization service</param> /// <param name="priceFormatter">Price formatter</param> /// <param name="currencyService">Currency service</param> /// <param name="taxService">Tax service</param> /// <param name="priceCalculationService">Price calculation service</param> /// <param name="currency">Target currency</param> /// <param name="priceAdjustment">Price adjustment</param> /// <param name="languageInsensitive">Whether the result string should be language insensitive</param> /// <returns>The base price info</returns> public static string GetBasePriceInfo(this Product product, ILocalizationService localizationService, IPriceFormatter priceFormatter, ICurrencyService currencyService, ITaxService taxService, IPriceCalculationService priceCalculationService, Currency currency, decimal priceAdjustment = decimal.Zero, bool languageInsensitive = false) { Guard.NotNull(product, nameof(product)); Guard.NotNull(currencyService, nameof(currencyService)); Guard.NotNull(taxService, nameof(taxService)); Guard.NotNull(priceCalculationService, nameof(priceCalculationService)); Guard.NotNull(currency, nameof(currency)); if (product.BasePriceHasValue && product.BasePriceAmount != Decimal.Zero) { var workContext = EngineContext.Current.Resolve <IWorkContext>(); var taxrate = decimal.Zero; var currentPrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, true); var price = taxService.GetProductPrice(product, decimal.Add(currentPrice, priceAdjustment), out taxrate); price = currencyService.ConvertFromPrimaryStoreCurrency(price, currency); return(product.GetBasePriceInfo(price, localizationService, priceFormatter, currency, languageInsensitive)); } return(""); }
private async Task <(string Price, string PriceWithDiscount)> PreparePrice(Product product, GetSearchAutoComplete request) { string price, priceWithDiscount; decimal finalPriceWithoutDiscount = (await(_taxService.GetProductPrice(product, (await _priceCalculationService.GetFinalPrice(product, request.Customer, request.Currency, includeDiscounts: false)).finalPrice))).productprice; var appliedPrice = (await _priceCalculationService.GetFinalPrice(product, request.Customer, request.Currency, includeDiscounts: true)); var finalPriceWithDiscount = (await _taxService.GetProductPrice(product, appliedPrice.finalPrice)).productprice; price = _priceFormatter.FormatPrice(finalPriceWithoutDiscount); priceWithDiscount = _priceFormatter.FormatPrice(finalPriceWithDiscount); return(price, priceWithDiscount); }
public ActionResult ProductTierPrices(int productId) { if (!_services.Permissions.Authorize(StandardPermissionProvider.DisplayPrices)) { return(Content("")); } var product = _productService.GetProductById(productId); if (product == null) { throw new ArgumentException(T("Products.NotFound", productId)); } if (!product.HasTierPrices) { // No tier prices return(Content("")); } var model = product.TierPrices .OrderBy(x => x.Quantity) .FilterByStore(_services.StoreContext.CurrentStore.Id) .FilterForCustomer(_services.WorkContext.CurrentCustomer) .ToList() .RemoveDuplicatedQuantities() .Select(tierPrice => { var m = new ProductDetailsModel.TierPriceModel { Quantity = tierPrice.Quantity, }; decimal taxRate = decimal.Zero; decimal priceBase = _taxService.GetProductPrice(product, _priceCalculationService.GetFinalPrice(product, _services.WorkContext.CurrentCustomer, decimal.Zero, _catalogSettings.DisplayTierPricesWithDiscounts, tierPrice.Quantity), out taxRate); decimal price = _currencyService.ConvertFromPrimaryStoreCurrency(priceBase, _services.WorkContext.WorkingCurrency); m.Price = _priceFormatter.FormatPrice(price, true, false); return(m); }) .ToList(); return(PartialView("Product.TierPrices", model)); }
/// <summary> /// Gets the base price /// </summary> /// <param name="product">Product</param> /// <param name="localizationService">Localization service</param> /// <param name="priceFormatter">Price formatter</param> /// <param name="currencyService">Currency service</param> /// <param name="taxService">Tax service</param> /// <param name="priceCalculationService">Price calculation service</param> /// <param name="currency">Target currency</param> /// <param name="priceAdjustment">Price adjustment</param> /// <param name="languageIndependent">Whether the result string should be language independent</param> /// <returns>The base price</returns> public static string GetBasePriceInfo(this Product product, ILocalizationService localizationService, IPriceFormatter priceFormatter, ICurrencyService currencyService, ITaxService taxService, IPriceCalculationService priceCalculationService, Currency currency, decimal priceAdjustment = decimal.Zero, bool languageIndependent = false) { if (product == null) { throw new ArgumentNullException("product"); } if (localizationService == null && !languageIndependent) { throw new ArgumentNullException("localizationService"); } if (product.BasePriceHasValue && product.BasePriceAmount != Decimal.Zero) { var workContext = EngineContext.Current.Resolve <IWorkContext>(); var taxrate = decimal.Zero; var currentPrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, true); decimal price = taxService.GetProductPrice(product, decimal.Add(currentPrice, priceAdjustment), out taxrate); price = currencyService.ConvertFromPrimaryStoreCurrency(price, currency); decimal basePriceValue = Convert.ToDecimal((price / product.BasePriceAmount) * product.BasePriceBaseAmount); string basePrice = priceFormatter.FormatPrice(basePriceValue, true, currency); string unit = "{0} {1}".FormatWith(product.BasePriceBaseAmount, product.BasePriceMeasureUnit); if (languageIndependent) { return("{0} / {1}".FormatWith(basePrice, unit)); } return(localizationService.GetResource("Products.BasePriceInfo").FormatWith(basePrice, unit)); } return(""); }
public void Can_get_final_product_price() { RunWithTestServiceProvider(() => { var product = _productService.GetProductById(1); //customer var customer = new Customer(); _priceCalcService.GetFinalPrice(product, customer, 0, false).Should().Be(12.34M); _priceCalcService.GetFinalPrice(product, customer, 0, false, 2).Should().Be(9M); }); }
public void Can_get_final_product_price() { var product = new Product { Id = 1, Name = "Product name 1", Price = 12.34M, CustomerEntersPrice = false, Published = true, }; //customer var customer = new Customer(); _priceCalcService.GetFinalPrice(product, customer, 0, false, 1).ShouldEqual(12.34M); _priceCalcService.GetFinalPrice(product, customer, 0, false, 2).ShouldEqual(12.34M); }
private string WriteItem(XmlWriter writer, Store store, Product product, Currency currency) { var manu = _manufacturerService.GetProductManufacturersByProductId(product.Id).FirstOrDefault(); var mainImageUrl = Helper.MainProductImageUrl(store, product); var googleProduct = GetByProductId(product.Id); var category = ProductCategory(googleProduct); if (category.IsNullOrEmpty()) { return(Helper.Resource("MissingDefaultCategory")); } var brand = (manu != null && manu.Manufacturer.Name.HasValue() ? manu.Manufacturer.Name : Settings.Brand); var mpn = Helper.ManufacturerPartNumber(product); bool identifierExists = product.Gtin.HasValue() || brand.HasValue() || mpn.HasValue(); writer.WriteElementString("g", "id", _googleNamespace, product.Id.ToString()); writer.WriteStartElement("title"); writer.WriteCData(product.Name.Truncate(70)); writer.WriteEndElement(); var description = Helper.BuildProductDescription(product, manu, d => { if (product.FullDescription.IsNullOrEmpty() && product.ShortDescription.IsNullOrEmpty()) { var rnd = new Random(); switch (rnd.Next(1, 5)) { case 1: return(d.Grow(Settings.AppendDescriptionText1, " ")); case 2: return(d.Grow(Settings.AppendDescriptionText2, " ")); case 3: return(d.Grow(Settings.AppendDescriptionText3, " ")); case 4: return(d.Grow(Settings.AppendDescriptionText4, " ")); case 5: return(d.Grow(Settings.AppendDescriptionText5, " ")); } } return(d); }); writer.WriteStartElement("description"); writer.WriteCData(description); writer.WriteEndElement(); writer.WriteStartElement("g", "google_product_category", _googleNamespace); writer.WriteCData(category); writer.WriteFullEndElement(); string productType = _categoryService.GetCategoryBreadCrumb(product); if (productType.HasValue()) { writer.WriteStartElement("g", "product_type", _googleNamespace); writer.WriteCData(productType); writer.WriteFullEndElement(); } writer.WriteElementString("link", Helper.ProductDetailUrl(store, product)); writer.WriteElementString("g", "image_link", _googleNamespace, mainImageUrl); foreach (string additionalImageUrl in Helper.AdditionalProductImages(store, product, mainImageUrl)) { writer.WriteElementString("g", "additional_image_link", _googleNamespace, additionalImageUrl); } writer.WriteElementString("g", "condition", _googleNamespace, Condition()); writer.WriteElementString("g", "availability", _googleNamespace, Availability(product)); decimal priceBase = _priceCalculationService.GetFinalPrice(product, null, _workContext.CurrentCustomer, decimal.Zero, true, 1); decimal price = Helper.ConvertFromStoreCurrency(priceBase, currency); string specialPriceDate; if (SpecialPrice(product, out specialPriceDate)) { writer.WriteElementString("g", "sale_price", _googleNamespace, Helper.DecimalUsFormat(price) + " " + currency.CurrencyCode); writer.WriteElementString("g", "sale_price_effective_date", _googleNamespace, specialPriceDate); // get regular price decimal specialPrice = product.SpecialPrice.Value; product.SpecialPrice = null; priceBase = _priceCalculationService.GetFinalPrice(product, null, _workContext.CurrentCustomer, decimal.Zero, true, 1); product.SpecialPrice = specialPrice; price = Helper.ConvertFromStoreCurrency(priceBase, currency); } writer.WriteElementString("g", "price", _googleNamespace, Helper.DecimalUsFormat(price) + " " + currency.CurrencyCode); writer.WriteCData("gtin", product.Gtin, "g", _googleNamespace); writer.WriteCData("brand", brand, "g", _googleNamespace); writer.WriteCData("mpn", mpn, "g", _googleNamespace); writer.WriteCData("gender", Gender(googleProduct), "g", _googleNamespace); writer.WriteCData("age_group", AgeGroup(googleProduct), "g", _googleNamespace); writer.WriteCData("color", Color(googleProduct), "g", _googleNamespace); writer.WriteCData("size", Size(googleProduct), "g", _googleNamespace); writer.WriteCData("material", Material(googleProduct), "g", _googleNamespace); writer.WriteCData("pattern", Pattern(googleProduct), "g", _googleNamespace); writer.WriteCData("item_group_id", ItemGroupId(googleProduct), "g", _googleNamespace); writer.WriteElementString("g", "online_only", _googleNamespace, Settings.OnlineOnly ? "y" : "n"); writer.WriteElementString("g", "identifier_exists", _googleNamespace, identifierExists ? "TRUE" : "FALSE"); if (Settings.ExpirationDays > 0) { writer.WriteElementString("g", "expiration_date", _googleNamespace, DateTime.UtcNow.AddDays(Settings.ExpirationDays).ToString("yyyy-MM-dd")); } if (Settings.ExportShipping) { string weightInfo, weight = Helper.DecimalUsFormat(product.Weight); string systemKey = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).SystemKeyword; if (systemKey.IsCaseInsensitiveEqual("gram")) { weightInfo = weight + " g"; } else if (systemKey.IsCaseInsensitiveEqual("lb")) { weightInfo = weight + " lb"; } else if (systemKey.IsCaseInsensitiveEqual("ounce")) { weightInfo = weight + " oz"; } else { weightInfo = weight + " kg"; } writer.WriteElementString("g", "shipping_weight", _googleNamespace, weightInfo); } if (Settings.ExportBasePrice && product.BasePriceHasValue) { string measureUnit = BasePriceUnits(product.BasePriceMeasureUnit); if (BasePriceSupported(product.BasePriceBaseAmount ?? 0, measureUnit)) { string basePriceMeasure = "{0} {1}".FormatWith(Helper.DecimalUsFormat(product.BasePriceAmount ?? decimal.Zero), measureUnit); string basePriceBaseMeasure = "{0} {1}".FormatWith(product.BasePriceBaseAmount, measureUnit); writer.WriteElementString("g", "unit_pricing_measure", _googleNamespace, basePriceMeasure); writer.WriteElementString("g", "unit_pricing_base_measure", _googleNamespace, basePriceBaseMeasure); } } return(null); }
private async Task PreparePriceModel(ProductOverviewModel model, Product product, Dictionary <string, string> res, bool forceRedirectionAfterAddingToCart, bool enableShoppingCart, bool displayPrices, bool enableWishlist, bool priceIncludesTax) { #region Prepare product price var priceModel = new ProductOverviewModel.ProductPriceModel { ForceRedirectionAfterAddingToCart = forceRedirectionAfterAddingToCart }; switch (product.ProductType) { case ProductType.GroupedProduct: { #region Grouped product var associatedProducts = await _productService.GetAssociatedProducts(product.Id, _storeContext.CurrentStore.Id); //add to cart button (ignore "DisableBuyButton" property for grouped products) priceModel.DisableBuyButton = !enableShoppingCart || !displayPrices; //add to wishlist button (ignore "DisableWishlistButton" property for grouped products) priceModel.DisableWishlistButton = !enableWishlist || !displayPrices; //compare products priceModel.DisableAddToCompareListButton = !_catalogSettings.CompareProductsEnabled; //catalog price, not used in views, but it's for front developer if (product.CatalogPrice > 0) { decimal catalogPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(product.CatalogPrice, _workContext.WorkingCurrency); priceModel.CatalogPrice = _priceFormatter.FormatPrice(catalogPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); } switch (associatedProducts.Count) { case 0: { } break; default: { //we have at least one associated product //compare products priceModel.DisableAddToCompareListButton = !_catalogSettings.CompareProductsEnabled; if (displayPrices) { //find a minimum possible price decimal?minPossiblePrice = null; Product minPriceProduct = null; foreach (var associatedProduct in associatedProducts) { //calculate for the maximum quantity (in case if we have tier prices) var tmpPrice = (await _priceCalculationService.GetFinalPrice(associatedProduct, _workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue)).finalPrice; if (!minPossiblePrice.HasValue || tmpPrice < minPossiblePrice.Value) { minPriceProduct = associatedProduct; minPossiblePrice = tmpPrice; } } if (minPriceProduct != null && !minPriceProduct.CustomerEntersPrice) { if (minPriceProduct.CallForPrice) { priceModel.OldPrice = null; priceModel.Price = res["Products.CallForPrice"]; } else if (minPossiblePrice.HasValue) { //calculate prices decimal finalPriceBase = (await _taxService.GetProductPrice(minPriceProduct, minPossiblePrice.Value, priceIncludesTax, _workContext.CurrentCustomer)).productprice; decimal finalPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, _workContext.WorkingCurrency); priceModel.OldPrice = null; priceModel.Price = String.Format(res["Products.PriceRangeFrom"], _priceFormatter.FormatPrice(finalPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax)); priceModel.PriceValue = finalPrice; //PAngV baseprice (used in Germany) if (product.BasepriceEnabled) { priceModel.BasePricePAngV = await product.FormatBasePrice(finalPrice, _localizationService, _measureService, _currencyService, _workContext, _priceFormatter); } } else { //Actually it's not possible (we presume that minimalPrice always has a value) //We never should get here Debug.WriteLine("Cannot calculate minPrice for product #{0}", product.Id); } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } } break; } #endregion } break; case ProductType.SimpleProduct: case ProductType.Reservation: default: { #region Simple product //add to cart button priceModel.DisableBuyButton = product.DisableBuyButton || !enableShoppingCart || !displayPrices; //add to wishlist button priceModel.DisableWishlistButton = product.DisableWishlistButton || !enableWishlist || !displayPrices; //compare products priceModel.DisableAddToCompareListButton = !_catalogSettings.CompareProductsEnabled; //pre-order if (product.AvailableForPreOrder) { priceModel.AvailableForPreOrder = !product.PreOrderAvailabilityStartDateTimeUtc.HasValue || product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow; priceModel.PreOrderAvailabilityStartDateTimeUtc = product.PreOrderAvailabilityStartDateTimeUtc; } //catalog price, not used in views, but it's for front developer if (product.CatalogPrice > 0) { decimal catalogPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(product.CatalogPrice, _workContext.WorkingCurrency); priceModel.CatalogPrice = _priceFormatter.FormatPrice(catalogPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); } //start price for product auction if (product.StartPrice > 0) { decimal startPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(product.StartPrice, _workContext.WorkingCurrency); priceModel.StartPrice = _priceFormatter.FormatPrice(startPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); priceModel.StartPriceValue = startPrice; } //highest bid for product auction if (product.HighestBid > 0) { decimal highestBid = await _currencyService.ConvertFromPrimaryStoreCurrency(product.HighestBid, _workContext.WorkingCurrency); priceModel.HighestBid = _priceFormatter.FormatPrice(highestBid, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); priceModel.HighestBidValue = highestBid; } //prices if (displayPrices) { if (!product.CustomerEntersPrice) { if (product.CallForPrice) { //call for price priceModel.OldPrice = null; priceModel.Price = res["Products.CallForPrice"]; } else { //prices //calculate for the maximum quantity (in case if we have tier prices) var infoprice = (await _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue)); priceModel.AppliedDiscounts = infoprice.appliedDiscounts; priceModel.PreferredTierPrice = infoprice.preferredTierPrice; decimal minPossiblePrice = infoprice.finalPrice; decimal oldPriceBase = (await _taxService.GetProductPrice(product, product.OldPrice, priceIncludesTax, _workContext.CurrentCustomer)).productprice; decimal finalPriceBase = (await _taxService.GetProductPrice(product, minPossiblePrice, priceIncludesTax, _workContext.CurrentCustomer)).productprice; decimal oldPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, _workContext.WorkingCurrency); decimal finalPrice = await _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, _workContext.WorkingCurrency); //do we have tier prices configured? var tierPrices = new List <TierPrice>(); if (product.TierPrices.Any()) { tierPrices.AddRange(product.TierPrices.OrderBy(tp => tp.Quantity) .FilterByStore(_storeContext.CurrentStore.Id) .FilterForCustomer(_workContext.CurrentCustomer) .FilterByDate() .RemoveDuplicatedQuantities()); } //When there is just one tier (with qty 1), //there are no actual savings in the list. bool displayFromMessage = tierPrices.Any() && !(tierPrices.Count == 1 && tierPrices[0].Quantity <= 1); if (displayFromMessage) { priceModel.OldPrice = null; priceModel.Price = String.Format(res["Products.PriceRangeFrom"], _priceFormatter.FormatPrice(finalPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax)); priceModel.PriceValue = finalPrice; } else { if (finalPriceBase != oldPriceBase && oldPriceBase != decimal.Zero) { priceModel.OldPrice = _priceFormatter.FormatPrice(oldPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); priceModel.OldPriceValue = oldPrice; priceModel.Price = _priceFormatter.FormatPrice(finalPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); priceModel.PriceValue = finalPrice; } else { priceModel.OldPrice = null; priceModel.Price = _priceFormatter.FormatPrice(finalPrice, true, _workContext.WorkingCurrency, _workContext.WorkingLanguage, priceIncludesTax); priceModel.PriceValue = finalPrice; } } if (product.ProductType == ProductType.Reservation) { //rental product priceModel.OldPrice = _priceFormatter.FormatReservationProductPeriod(product, priceModel.OldPrice); priceModel.Price = _priceFormatter.FormatReservationProductPeriod(product, priceModel.Price); } //property for German market //we display tax/shipping info only with "shipping enabled" for this product //we also ensure this it's not free shipping priceModel.DisplayTaxShippingInfo = _catalogSettings.DisplayTaxShippingInfoProductBoxes && product.IsShipEnabled && !product.IsFreeShipping; //PAngV baseprice (used in Germany) if (product.BasepriceEnabled) { priceModel.BasePricePAngV = await product.FormatBasePrice(finalPrice, _localizationService, _measureService, _currencyService, _workContext, _priceFormatter); } } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } #endregion } break; } model.ProductPrice = priceModel; #endregion }
private ProductVariantQuoteModel PrepareProductVariantQuoteModelForList(CustomerProductVariantQuote customerProductVariantQuote) { #region Product variant price var productVariant = _productService.GetProductVariantById(customerProductVariantQuote.ProductVariantId); if (productVariant == null || productVariant.Id == 0) { return(null); } string productName = productVariant.FullProductName; Currency currency = _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId); if (productVariant != null && productVariant.CurrencyId.HasValue && (productVariant.CurrencyId.Value != _currencySettings.PrimaryStoreCurrencyId)) { currency = _currencyService.GetCurrencyById(productVariant.CurrencyId.Value); } StatefulStorage.PerSession.Add <bool?>("SkipQuoteDiscountActivationCheck", () => (bool?)true); decimal taxRate = decimal.Zero; var customer = _customerService.GetCustomerById(customerProductVariantQuote.CustomerId); decimal oldPriceBase = _taxService.GetProductPrice(productVariant, productVariant.OldPrice, out taxRate); decimal finalPriceWithoutDiscountBase = _taxService.GetProductPrice(productVariant, _priceCalculationService.GetFinalPrice(productVariant, customer, false, false), out taxRate); decimal finalPriceWithDiscountBase = _taxService.GetProductPrice(productVariant, _priceCalculationService.GetFinalPrice(productVariant, customer, true, false), out taxRate); decimal oldPrice = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, currency); decimal finalPriceWithoutDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithoutDiscountBase, currency); decimal finalPriceWithDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscountBase, currency); string productVariantPriceOldPrice = String.Empty; if (finalPriceWithoutDiscountBase != oldPriceBase && oldPriceBase > decimal.Zero) { productVariantPriceOldPrice = _priceFormatter.FormatPrice(oldPrice, false, false); } string productVariantPrice = _priceFormatter.FormatPrice(finalPriceWithoutDiscount, false, currency); string productVariantPricePriceWithDiscount = "", productVariantPriceDiscountPrice = "", productVariantDiscountPercentage = "", productVariantPriceCurrency = ""; decimal productVariantPricediscountValueBase = 0, productVariantPriceDiscountValue = 0, productVariantPriceValue = 0, productVariantPricePriceWithDiscountValue = 0; if (finalPriceWithoutDiscountBase != finalPriceWithDiscountBase) { IList <Discount> discounts = null; productVariantPricePriceWithDiscount = _priceFormatter.FormatPrice(finalPriceWithDiscount, false, currency); productVariantPricediscountValueBase = _priceCalculationService.GetDiscountAmount(productVariant, customer, 0, out discounts, false); productVariantPriceDiscountValue = _currencyService.ConvertFromPrimaryStoreCurrency(productVariantPricediscountValueBase, _workContext.WorkingCurrency); productVariantPriceDiscountPrice = _priceFormatter.FormatPrice(productVariantPricediscountValueBase, true, false); if (_workContext.WorkingLanguage.DisplayOrder == 2) { productVariantDiscountPercentage = String.Format("({0}%)", ((int)discounts.First().DiscountPercentage).ToString()); } else { productVariantDiscountPercentage = String.Format("(%{0})", ((int)discounts.First().DiscountPercentage).ToString()); } } productVariantPriceValue = finalPriceWithoutDiscount; productVariantPricePriceWithDiscountValue = finalPriceWithDiscount; productVariantPriceCurrency = CultureInfo.CurrentCulture.NumberFormat.CurrencySymbol; ////// StatefulStorage.PerSession.Remove <bool?>("SkipQuoteDiscountActivationCheck"); var pvpModel = new ProductVariantQuoteModel.ProductVariantPriceModel() { Price = productVariantPrice, PriceWithDiscount = productVariantPricePriceWithDiscount, DiscountValue = productVariantPriceDiscountValue, DiscountPrice = productVariantPriceDiscountPrice, DiscountPercentage = productVariantDiscountPercentage, PriceValue = productVariantPriceValue, PriceWithDiscountValue = productVariantPricePriceWithDiscountValue, Currency = productVariantPriceCurrency, OldPrice = productVariantPriceOldPrice, }; #endregion string name = customerProductVariantQuote.Name; string phoneNumber = customerProductVariantQuote.PhoneNumber; try { Customer cust = _customerService.GetCustomerById(customerProductVariantQuote.CustomerId); if (cust != null) { if (!cust.IsGuest()) { name = cust.GetFullName(); if (cust.Addresses.Count() > 0) { phoneNumber = cust.Addresses.FirstOrDefault().PhoneNumber; } } } } catch (Exception) { } return(new ProductVariantQuoteModel() { Id = customerProductVariantQuote.Id, ProductVariantId = customerProductVariantQuote.ProductVariantId, Email = customerProductVariantQuote.Email, PhoneNumber = phoneNumber, Enquiry = customerProductVariantQuote.Enquiry, Name = name, ManufacturerName = _productService.GetProductById(customerProductVariantQuote.ProductVariant.ProductId).GetDefaultManufacturer().Name, ProductName = productName, Sku = productVariant.Sku, ProductVariantPrice = pvpModel, Description = customerProductVariantQuote.Description, RequestDate = _dateTimeHelper.ConvertToUserTime(customerProductVariantQuote.RequestDate, DateTimeKind.Utc), PriceWithDiscount = customerProductVariantQuote.PriceWithDiscount, PriceWithoutDiscount = customerProductVariantQuote.PriceWithoutDiscount, DiscountPercentage = customerProductVariantQuote.DiscountPercentage, ActivateDate = customerProductVariantQuote.ActivateDate, }); }
public void Can_get_final_product_price() { var product = new Product { Id = "1", Name = "product name 01", Price = 49.99M, CustomerEntersPrice = false, Published = true }; var customer = new Customer(); Assert.AreEqual(49.99M, _priceCalcService.GetFinalPrice(product, customer, 0, false, 1)); //returned price FOR ONE UNIT should be the same, even if quantity is different than 1 Assert.AreEqual(49.99M, _priceCalcService.GetFinalPrice(product, customer, 0, false, 10)); }
public static IEnumerable <ProductOverviewModel> PrepareProductOverviewModels(this Controller controller, IWorkContext workContext, IStoreContext storeContext, ICategoryService categoryService, IProductService productService, ISpecificationAttributeService specificationAttributeService, IPriceCalculationService priceCalculationService, IPriceFormatter priceFormatter, IPermissionService permissionService, ILocalizationService localizationService, ITaxService taxService, ICurrencyService currencyService, IPictureService pictureService, IWebHelper webHelper, ICacheManager cacheManager, CatalogSettings catalogSettings, MediaSettings mediaSettings, IEnumerable <Product> products, bool preparePriceModel = true, bool preparePictureModel = true, int?productThumbPictureSize = null, bool prepareSpecificationAttributes = false, bool forceRedirectionAfterAddingToCart = false) { if (products == null) { throw new ArgumentNullException("products"); } var models = new List <ProductOverviewModel>(); foreach (var product in products) { var model = new ProductOverviewModel { Id = product.Id, Name = product.GetLocalized(x => x.Name), ShortDescription = product.GetLocalized(x => x.ShortDescription), FullDescription = product.GetLocalized(x => x.FullDescription), SeName = product.GetSeName(), ProductType = product.ProductType, MarkAsNew = product.MarkAsNew && (!product.MarkAsNewStartDateTimeUtc.HasValue || product.MarkAsNewStartDateTimeUtc.Value < DateTime.UtcNow) && (!product.MarkAsNewEndDateTimeUtc.HasValue || product.MarkAsNewEndDateTimeUtc.Value > DateTime.UtcNow) }; //price if (preparePriceModel) { #region Prepare product price var priceModel = new ProductOverviewModel.ProductPriceModel { ForceRedirectionAfterAddingToCart = forceRedirectionAfterAddingToCart }; switch (product.ProductType) { case ProductType.GroupedProduct: { #region Grouped product var associatedProducts = productService.GetAssociatedProducts(product.Id, storeContext.CurrentStore.Id); //add to cart button (ignore "DisableBuyButton" property for grouped products) priceModel.DisableBuyButton = !permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart) || !permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //add to wishlist button (ignore "DisableWishlistButton" property for grouped products) priceModel.DisableWishlistButton = !permissionService.Authorize(StandardPermissionProvider.EnableWishlist) || !permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //compare products priceModel.DisableAddToCompareListButton = !catalogSettings.CompareProductsEnabled; switch (associatedProducts.Count) { case 0: { //no associated products } break; default: { //we have at least one associated product //compare products priceModel.DisableAddToCompareListButton = !catalogSettings.CompareProductsEnabled; //priceModel.AvailableForPreOrder = false; if (permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { //find a minimum possible price decimal?minPossiblePrice = null; Product minPriceProduct = null; foreach (var associatedProduct in associatedProducts) { //calculate for the maximum quantity (in case if we have tier prices) var tmpPrice = priceCalculationService.GetFinalPrice(associatedProduct, workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue); if (!minPossiblePrice.HasValue || tmpPrice < minPossiblePrice.Value) { minPriceProduct = associatedProduct; minPossiblePrice = tmpPrice; } } if (minPriceProduct != null && !minPriceProduct.CustomerEntersPrice) { if (minPriceProduct.CallForPrice) { priceModel.OldPrice = null; priceModel.Price = localizationService.GetResource("Products.CallForPrice"); } else if (minPossiblePrice.HasValue) { //calculate prices decimal taxRate; decimal finalPriceBase = taxService.GetProductPrice(minPriceProduct, minPossiblePrice.Value, out taxRate); decimal finalPrice = currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, workContext.WorkingCurrency); priceModel.OldPrice = null; priceModel.Price = String.Format(localizationService.GetResource("Products.PriceRangeFrom"), priceFormatter.FormatPrice(finalPrice)); priceModel.PriceValue = finalPrice; } else { //Actually it's not possible (we presume that minimalPrice always has a value) //We never should get here Debug.WriteLine("Cannot calculate minPrice for product #{0}", product.Id); } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } } break; } #endregion } break; case ProductType.SimpleProduct: default: { #region Simple product //add to cart button priceModel.DisableBuyButton = product.DisableBuyButton || !permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart) || !permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //add to wishlist button priceModel.DisableWishlistButton = product.DisableWishlistButton || !permissionService.Authorize(StandardPermissionProvider.EnableWishlist) || !permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //compare products priceModel.DisableAddToCompareListButton = !catalogSettings.CompareProductsEnabled; //rental priceModel.IsRental = product.IsRental; //pre-order if (product.AvailableForPreOrder) { priceModel.AvailableForPreOrder = !product.PreOrderAvailabilityStartDateTimeUtc.HasValue || product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow; priceModel.PreOrderAvailabilityStartDateTimeUtc = product.PreOrderAvailabilityStartDateTimeUtc; } //prices if (permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { if (!product.CustomerEntersPrice) { if (product.CallForPrice) { //call for price priceModel.OldPrice = null; priceModel.Price = localizationService.GetResource("Products.CallForPrice"); } else { //prices //calculate for the maximum quantity (in case if we have tier prices) decimal minPossiblePrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue); decimal taxRate; decimal oldPriceBase = taxService.GetProductPrice(product, product.OldPrice, out taxRate); decimal finalPriceBase = taxService.GetProductPrice(product, minPossiblePrice, out taxRate); decimal oldPrice = currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, workContext.WorkingCurrency); decimal finalPrice = currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, workContext.WorkingCurrency); //do we have tier prices configured? var tierPrices = new List <TierPrice>(); if (product.HasTierPrices) { tierPrices.AddRange(product.TierPrices .OrderBy(tp => tp.Quantity) .ToList() .FilterByStore(storeContext.CurrentStore.Id) .FilterForCustomer(workContext.CurrentCustomer) .RemoveDuplicatedQuantities()); } //When there is just one tier (with qty 1), //there are no actual savings in the list. bool displayFromMessage = tierPrices.Count > 0 && !(tierPrices.Count == 1 && tierPrices[0].Quantity <= 1); if (displayFromMessage) { priceModel.OldPrice = null; priceModel.Price = String.Format(localizationService.GetResource("Products.PriceRangeFrom"), priceFormatter.FormatPrice(finalPrice)); priceModel.PriceValue = finalPrice; } else { if (finalPriceBase != oldPriceBase && oldPriceBase != decimal.Zero) { priceModel.OldPrice = priceFormatter.FormatPrice(oldPrice); priceModel.Price = priceFormatter.FormatPrice(finalPrice); priceModel.PriceValue = finalPrice; } else { priceModel.OldPrice = null; priceModel.Price = priceFormatter.FormatPrice(finalPrice); priceModel.PriceValue = finalPrice; } } if (product.IsRental) { //rental product priceModel.OldPrice = priceFormatter.FormatRentalProductPeriod(product, priceModel.OldPrice); priceModel.Price = priceFormatter.FormatRentalProductPeriod(product, priceModel.Price); } //property for German market //we display tax/shipping info only with "shipping enabled" for this product //we also ensure this it's not free shipping priceModel.DisplayTaxShippingInfo = catalogSettings.DisplayTaxShippingInfoProductBoxes && product.IsShipEnabled && !product.IsFreeShipping; } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } #endregion } break; } model.ProductPrice = priceModel; #endregion } //picture if (preparePictureModel) { #region Prepare product picture //If a size has been set in the view, we use it in priority int pictureSize = productThumbPictureSize.HasValue ? productThumbPictureSize.Value : mediaSettings.ProductThumbPictureSize; //prepare picture model var defaultProductPictureCacheKey = string.Format(ModelCacheEventConsumer.PRODUCT_DEFAULTPICTURE_MODEL_KEY, product.Id, pictureSize, true, workContext.WorkingLanguage.Id, webHelper.IsCurrentConnectionSecured(), storeContext.CurrentStore.Id); model.DefaultPictureModel = cacheManager.Get(defaultProductPictureCacheKey, () => { var picture = pictureService.GetPicturesByProductId(product.Id, 1).FirstOrDefault(); var pictureModel = new PictureModel { ImageUrl = pictureService.GetPictureUrl(picture, pictureSize), FullSizeImageUrl = pictureService.GetPictureUrl(picture) }; //"title" attribute pictureModel.Title = (picture != null && !string.IsNullOrEmpty(picture.TitleAttribute)) ? picture.TitleAttribute : string.Format(localizationService.GetResource("Media.Product.ImageLinkTitleFormat"), model.Name); //"alt" attribute pictureModel.AlternateText = (picture != null && !string.IsNullOrEmpty(picture.AltAttribute)) ? picture.AltAttribute : string.Format(localizationService.GetResource("Media.Product.ImageAlternateTextFormat"), model.Name); return(pictureModel); }); #endregion } //specs if (prepareSpecificationAttributes) { model.SpecificationAttributeModels = PrepareProductSpecificationModel(controller, workContext, specificationAttributeService, cacheManager, product); } //reviews model.ReviewOverviewModel = controller.PrepareProductReviewOverviewModel(storeContext, catalogSettings, cacheManager, product); models.Add(model); } return(models); }
/// <summary> /// Generate a feed /// </summary> /// <param name="stream">Stream</param> /// <param name="store">Store</param> /// <returns>Generated feed</returns> public void GenerateFeed(Stream stream, Store store) { if (stream == null) { throw new ArgumentNullException("stream"); } if (store == null) { throw new ArgumentNullException("store"); } const string zboziBaseNamespace = "http://www.zbozi.cz/ns/offer/1.0"; var settings = new XmlWriterSettings { Encoding = Encoding.UTF8 }; //language var languageId = 0; var languages = _languageService.GetAllLanguages(storeId: store.Id); //if we have only one language, let's use it if (languages.Count == 1) { //let's use the first one var language = languages.FirstOrDefault(); languageId = language != null ? language.Id : 0; } //otherwise, use the current one if (languageId == 0) { languageId = _workContext.WorkingLanguage.Id; } //we load all google products here using one SQL request (performance optimization) var allzboziProducts = _zboziService.GetAll(); var shippingMethodList = _shippingService.GetAllShippingMethods(27); using (var writer = XmlWriter.Create(stream, settings)) { //Generate feed according to the following specs: https://napoveda.seznam.cz/cz/zbozi/specifikace-xml-pro-obchody/specifikace-xml-feedu/ writer.WriteStartDocument(); writer.WriteStartElement("SHOP", zboziBaseNamespace); //writer.WriteAttributeString("xmlns", zboziBaseNamespace); //writer.WriteAttributeString("xmlns", "", null, zboziBaseNamespace); var products1 = _productService.SearchProducts(storeId: store.Id, visibleIndividuallyOnly: true); foreach (var product1 in products1) { var productsToProcess = new List <Product>(); switch (product1.ProductType) { case ProductType.SimpleProduct: { //simple product doesn't have child products productsToProcess.Add(product1); } break; case ProductType.GroupedProduct: { //grouped products could have several child products var associatedProducts = _productService.GetAssociatedProducts(product1.Id, store.Id); productsToProcess.AddRange(associatedProducts); } break; default: continue; } foreach (var product in productsToProcess) { writer.WriteStartElement("SHOPITEM"); #region Basic Product Information //id [id]- An identifier of the item writer.WriteElementString("ITEM_ID", product.Id.ToString()); //title [title] - Title of the item writer.WriteStartElement("PRODUCT"); var title = product.GetLocalized(x => x.Name, languageId); //title should be not longer than 70 characters if (title.Length > 70) { title = title.Substring(0, 70); } writer.WriteCData(title); writer.WriteEndElement(); // title //description [description] - Description of the item writer.WriteStartElement("DESCRIPTION"); string description = product.GetLocalized(x => x.FullDescription, languageId); if (String.IsNullOrEmpty(description)) { description = product.GetLocalized(x => x.ShortDescription, languageId); } if (String.IsNullOrEmpty(description)) { description = product.GetLocalized(x => x.Name, languageId); //description is required } //resolving character encoding issues in your data feed description = StripInvalidChars(description, true); writer.WriteCData(description); writer.WriteEndElement(); // description //google product category [google_product_category] - Google's category of the item //the category of the product according to Google’s product taxonomy. http://www.google.com/support/merchants/bin/answer.py?answer=160081 string googleProductCategory = ""; //var googleProduct = _googleService.GetByProductId(product.Id); var zboziProduct = allzboziProducts.FirstOrDefault(x => x.ProductId == product.Id); if (zboziProduct != null) { googleProductCategory = zboziProduct.Taxonomy; } if (String.IsNullOrEmpty(googleProductCategory)) { googleProductCategory = _zboziShoppingSettings.DefaultGoogleCategory; } if (String.IsNullOrEmpty(googleProductCategory)) { throw new NopException("Není nastavena základní kategorie"); } writer.WriteStartElement("CATEGORYTEXT"); writer.WriteCData(googleProductCategory); writer.WriteFullEndElement(); // g:google_product_category string zboziCPC = ""; if (zboziProduct != null) { zboziCPC = zboziProduct.MAX_CPC; } if (!string.IsNullOrEmpty(zboziCPC)) { decimal result = 0; if (decimal.TryParse(zboziCPC, out result)) { writer.WriteElementString("MAX_CPC", zboziCPC); } else { throw new Exception("Parametr MAX_CPC musí obsahovat číselnou hodnotu. "); } } string zboziCPC_serach = ""; if (zboziProduct != null) { zboziCPC_serach = zboziProduct.MAX_CPC_SEARCH; } if (!string.IsNullOrEmpty(zboziCPC_serach)) { decimal result = 0; if (decimal.TryParse(zboziCPC_serach, out result)) { writer.WriteElementString("MAX_CPC_SEARCH", zboziCPC_serach); } else { throw new Exception("Parametr MAX_CPC_SEARCH musí obsahovat číselnou hodnotu. "); } } string EXTRA_MESSAGE = ""; if (zboziProduct != null) { EXTRA_MESSAGE = zboziProduct.EXTRA_MESSAGE; } if (!string.IsNullOrEmpty(EXTRA_MESSAGE)) { writer.WriteElementString("EXTRA_MESSAGE", EXTRA_MESSAGE); } string productName = ""; if (zboziProduct != null) { productName = zboziProduct.ProductName; } if (!string.IsNullOrEmpty(productName)) { writer.WriteStartElement("PRODUCTNAME"); writer.WriteCData(productName); writer.WriteFullEndElement(); // } else { writer.WriteStartElement("PRODUCTNAME"); writer.WriteCData(title); writer.WriteFullEndElement(); // } // params var parameters = ""; if (zboziProduct != null) { parameters = zboziProduct.Params; } if (string.IsNullOrEmpty(parameters) && product.ProductSpecificationAttributes.Count() > 0) { // progenerujeme vsechny parametry foreach (var productSpecificAtribut in product.ProductSpecificationAttributes) { var option = productSpecificAtribut.SpecificationAttributeOption; var name = option.SpecificationAttribute.Name; var value = option.Name; writer.WriteStartElement("PARAM"); writer.WriteStartElement("PARAM_NAME"); writer.WriteString(name); writer.WriteEndElement(); writer.WriteStartElement("VAL"); writer.WriteString(value); writer.WriteEndElement(); writer.WriteFullEndElement(); // g: //var values = mapping.ProductAttributeValues.Where(e=>e.) } } else { // prepiseme hodnotu } ////product type [product_type] - Your category of the item //var defaultProductCategory = _categoryService // .GetProductCategoriesByProductId(product.Id, store.Id) // .FirstOrDefault(); //if (defaultProductCategory != null) //{ // //TODO localize categories // var category = defaultProductCategory.Category // .GetFormattedBreadCrumb(_categoryService, separator: ">", languageId: languageId); // if (!String.IsNullOrEmpty((category))) // { // writer.WriteStartElement("g", "product_type", googleBaseNamespace); // writer.WriteCData(category); // writer.WriteFullEndElement(); // g:product_type // } //} //link [link] - URL directly linking to your item's page on your website var productUrl = string.Format("{0}{1}", store.Url, product.GetSeName(languageId)); writer.WriteElementString("URL", productUrl); //image link [image_link] - URL of an image of the item //additional images [additional_image_link] //up to 10 pictures const int maximumPictures = 10; var pictures = _pictureService.GetPicturesByProductId(product.Id, maximumPictures); for (int i = 0; i < pictures.Count; i++) { var picture = pictures[i]; var imageUrl = _pictureService.GetPictureUrl(picture, _zboziShoppingSettings.ProductPictureSize, storeLocation: store.Url); if (i == 0) { //default image writer.WriteElementString("IMGURL", imageUrl); } //else //{ // //additional image // writer.WriteElementString("IMGURL_ALTERNATIVE", imageUrl); //} } if (!pictures.Any()) { //no picture? submit a default one var imageUrl = _pictureService.GetDefaultPictureUrl(_zboziShoppingSettings.ProductPictureSize, storeLocation: store.Url); writer.WriteElementString("IMGURL", imageUrl); } #endregion #region Availability & Price //price [price] - Price of the item var currency = GetUsedCurrency(); decimal finalPriceBase; if (_zboziShoppingSettings.PricesConsiderPromotions) { var minPossiblePrice = _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer); if (product.HasTierPrices) { //calculate price for the maximum quantity if we have tier prices, and choose minimal minPossiblePrice = Math.Min(minPossiblePrice, _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, quantity: int.MaxValue)); } decimal taxRate; finalPriceBase = _taxService.GetProductPrice(product, minPossiblePrice, out taxRate); } else { finalPriceBase = product.Price; } decimal price = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, currency); //round price now so it matches the product details page price = RoundingHelper.RoundPrice(price); //writer.WriteElementString("g", "price", googleBaseNamespace, // price.ToString(new CultureInfo("en-US", false).NumberFormat) + " " + // currency.CurrencyCode); if (price == 0) { writer.WriteElementString("PRICE_VAT", "0"); } else { writer.WriteElementString("PRICE_VAT", price.ToString("#,##")); } #endregion #region Unique Product Identifiers /* Unique product identifiers such as UPC, EAN, JAN or ISBN allow us to show your listing on the appropriate product page. If you don't provide the required unique product identifiers, your store may not appear on product pages, and all your items may be removed from Product Search. * We require unique product identifiers for all products - except for custom made goods. For apparel, you must submit the 'brand' attribute. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute. In all cases, we recommend you submit all three attributes. * You need to submit at least two attributes of 'brand', 'gtin' and 'mpn', but we recommend that you submit all three if available. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute, but we recommend that you include 'brand' and 'mpn' if available. */ //GTIN [gtin] - GTIN var gtin = product.Gtin; if (!String.IsNullOrEmpty(gtin)) { writer.WriteStartElement("EAN"); writer.WriteCData(gtin); writer.WriteFullEndElement(); // g:gtin } //brand [brand] - Brand of the item var defaultManufacturer = _manufacturerService.GetProductManufacturersByProductId((product.Id)).FirstOrDefault(); if (defaultManufacturer != null) { writer.WriteStartElement("MANUFACTURER"); writer.WriteCData(defaultManufacturer.Manufacturer.Name); writer.WriteFullEndElement(); // g:brand } //mpn [mpn] - Manufacturer Part Number (MPN) of the item var mpn = product.ManufacturerPartNumber; if (!String.IsNullOrEmpty(mpn)) { writer.WriteStartElement("PRODUCTNO"); writer.WriteCData(mpn); writer.WriteFullEndElement(); // g:mpn } //identifier exists [identifier_exists] - Submit custom goods //if (googleProduct != null && googleProduct.CustomGoods) //{ // writer.WriteElementString("g", "identifier_exists", googleBaseNamespace, "FALSE"); //} #endregion #region Apparel Products #endregion #region Tax & Shipping string deliveryDate = "0"; if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock && product.BackorderMode == BackorderMode.NoBackorders && product.GetTotalStockQuantity() <= 0) { // neni skladem a ma dobu pro doruceni if (product.DeliveryDateId > 0) { var deliveryDateRecord = _dateRangeService.GetDeliveryDateById(product.DeliveryDateId); deliveryDate = deliveryDateRecord.Name; } else { deliveryDate = _zboziShoppingSettings.DefaultDeliveryDate; } } writer.WriteStartElement("DELIVERY_DATE"); writer.WriteString(deliveryDate); writer.WriteFullEndElement(); // #endregion writer.WriteEndElement(); // item } } writer.WriteEndElement(); // shop writer.WriteEndDocument(); } }
/// <summary> /// Gets the base price /// </summary> /// <param name="product">Product</param> /// <param name="localizationService">Localization service</param> /// <param name="priceFormatter">Price formatter</param> /// <param name="priceAdjustment">Price adjustment</param> /// <param name="languageIndependent">Whether the result string should be language independent</param> /// <returns>The base price</returns> public static string GetBasePriceInfo(this Product product, ILocalizationService localizationService, IPriceFormatter priceFormatter, ICurrencyService currencyService, ITaxService taxService, IPriceCalculationService priceCalculationService, Currency currency, decimal priceAdjustment = decimal.Zero, bool languageIndependent = false) { if (product == null) throw new ArgumentNullException("product"); if (localizationService == null && !languageIndependent) throw new ArgumentNullException("localizationService"); if (product.BasePriceHasValue && product.BasePriceAmount != Decimal.Zero) { var workContext = EngineContext.Current.Resolve<IWorkContext>(); var taxrate = decimal.Zero; var currentPrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, true); decimal price = taxService.GetProductPrice(product, decimal.Add(currentPrice, priceAdjustment), out taxrate); price = currencyService.ConvertFromPrimaryStoreCurrency(price, currency); decimal basePriceValue = Convert.ToDecimal((price / product.BasePriceAmount) * product.BasePriceBaseAmount); string basePrice = priceFormatter.FormatPrice(basePriceValue, true, currency); string unit = "{0} {1}".FormatWith(product.BasePriceBaseAmount, product.BasePriceMeasureUnit); if (languageIndependent) { return "{0} / {1}".FormatWith(basePrice, unit); } return localizationService.GetResource("Products.BasePriceInfo").FormatWith(basePrice, unit); } return ""; }
public static string PreparePrice(this Product product, IWorkContext workContext, IStoreContext storeContext, IProductService productService, IPriceCalculationService priceCalculationService, IPriceFormatter priceFormatter, IPermissionService permissionService, ILocalizationService localizationService, ITaxService taxService, ICurrencyService currencyService) { decimal taxRate = new decimal(); //decimal taxRate = new decimal(); string str; bool flag; if (product == null) { throw new ArgumentNullException("product"); } string price = "0"; ProductType productType = product.ProductType; if (productType != ProductType.SimpleProduct) { if (productType != ProductType.GroupedProduct) { // goto Label2; } IList <Product> associatedProducts = productService.GetAssociatedProducts(product.Id, storeContext.CurrentStore.Id, 0, false); if (associatedProducts.Count == 0) { price = "0"; } else if (!permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { price = "0"; } else { decimal?minPossiblePrice = null; Product minPriceProduct = null; foreach (Product associatedProduct in associatedProducts) { decimal tmpPrice = priceCalculationService.GetFinalPrice(associatedProduct, workContext.CurrentCustomer, decimal.Zero, true, 2147483647); if ((!minPossiblePrice.HasValue ? true : tmpPrice < minPossiblePrice.Value)) { minPriceProduct = associatedProduct; minPossiblePrice = new decimal?(tmpPrice); } } if ((minPriceProduct == null ? false : !minPriceProduct.CustomerEntersPrice)) { if (minPriceProduct.CallForPrice) { price = "0"; } else if (!minPossiblePrice.HasValue) { Debug.WriteLine("Cannot calculate minPrice for product #{0}", new object[] { product.Id }); } else { decimal finalPriceBase = taxService.GetProductPrice(minPriceProduct, minPossiblePrice.Value, out taxRate); decimal finalPrice = currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, workContext.WorkingCurrency); price = string.Concat(finalPrice); } } } str = price; return(str); } if (!permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { price = "0"; } else if (!product.CustomerEntersPrice) { if (!product.CallForPrice) { decimal minPossiblePrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, decimal.Zero, true, 2147483647); decimal oldPriceBase = taxService.GetProductPrice(product, product.OldPrice, out taxRate); decimal finalPriceBase = taxService.GetProductPrice(product, minPossiblePrice, out taxRate); currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, workContext.WorkingCurrency); decimal finalPrice = currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, workContext.WorkingCurrency); List <TierPrice> tierPrices = new List <TierPrice>(); if (product.HasTierPrices) { tierPrices.AddRange(TierPriceExtensions.RemoveDuplicatedQuantities(TierPriceExtensions.FilterForCustomer(TierPriceExtensions.FilterByStore(( from tp in product.TierPrices orderby tp.Quantity select tp).ToList <TierPrice>(), storeContext.CurrentStore.Id), workContext.CurrentCustomer))); } if (tierPrices.Count <= 0) { flag = false; } else { flag = (tierPrices.Count != 1 ? true : tierPrices[0].Quantity > 1); } if (!flag) { price = ((finalPriceBase == oldPriceBase ? true : oldPriceBase == decimal.Zero) ? string.Concat(finalPrice) : string.Concat(finalPrice)); } else { price = string.Concat(finalPrice); } } else { price = "0"; } } str = price; return(str); }
/// <summary> /// Generate a feed /// </summary> /// <param name="stream">Stream</param> /// <param name="store">Store</param> /// <returns>Generated feed</returns> public void GenerateFeed(Stream stream, Store store) { if (stream == null) { throw new ArgumentNullException("stream"); } if (store == null) { throw new ArgumentNullException("store"); } const string googleBaseNamespace = "http://base.google.com/ns/1.0"; var settings = new XmlWriterSettings { Encoding = Encoding.UTF8 }; //language var languageId = 0; var languages = _languageService.GetAllLanguages(storeId: store.Id); //if we have only one language, let's use it if (languages.Count == 1) { //let's use the first one var language = languages.FirstOrDefault(); languageId = language != null ? language.Id : 0; } //otherwise, use the current one if (languageId == 0) { languageId = _workContext.WorkingLanguage.Id; } //we load all google products here using one SQL request (performance optimization) var allGoogleProducts = _googleService.GetAll(); using (var writer = XmlWriter.Create(stream, settings)) { //Generate feed according to the following specs: http://www.google.com/support/merchants/bin/answer.py?answer=188494&expand=GB writer.WriteStartDocument(); writer.WriteStartElement("rss"); writer.WriteAttributeString("version", "2.0"); writer.WriteAttributeString("xmlns", "g", null, googleBaseNamespace); writer.WriteStartElement("channel"); writer.WriteElementString("title", "Google Base feed"); writer.WriteElementString("link", "http://base.google.com/base/"); writer.WriteElementString("description", "Information about products"); var products1 = _productService.SearchProducts(storeId: store.Id, visibleIndividuallyOnly: true); foreach (var product1 in products1) { var productsToProcess = new List <Product>(); switch (product1.ProductType) { case ProductType.SimpleProduct: { //simple product doesn't have child products productsToProcess.Add(product1); } break; case ProductType.GroupedProduct: { //grouped products could have several child products var associatedProducts = _productService.GetAssociatedProducts(product1.Id, store.Id); productsToProcess.AddRange(associatedProducts); } break; default: continue; } foreach (var product in productsToProcess) { writer.WriteStartElement("item"); #region Basic Product Information //id [id]- An identifier of the item writer.WriteElementString("g", "id", googleBaseNamespace, product.Id.ToString()); //title [title] - Title of the item writer.WriteStartElement("title"); var title = product.GetLocalized(x => x.Name, languageId); //title should be not longer than 70 characters if (title.Length > 70) { title = title.Substring(0, 70); } writer.WriteCData(title); writer.WriteEndElement(); // title //description [description] - Description of the item writer.WriteStartElement("description"); string description = product.GetLocalized(x => x.FullDescription, languageId); if (String.IsNullOrEmpty(description)) { description = product.GetLocalized(x => x.ShortDescription, languageId); } if (String.IsNullOrEmpty(description)) { description = product.GetLocalized(x => x.Name, languageId); //description is required } //resolving character encoding issues in your data feed description = StripInvalidChars(description, true); writer.WriteCData(description); writer.WriteEndElement(); // description //google product category [google_product_category] - Google's category of the item //the category of the product according to Google’s product taxonomy. http://www.google.com/support/merchants/bin/answer.py?answer=160081 string googleProductCategory = ""; //var googleProduct = _googleService.GetByProductId(product.Id); var googleProduct = allGoogleProducts.FirstOrDefault(x => x.ProductId == product.Id); if (googleProduct != null) { googleProductCategory = googleProduct.Taxonomy; } if (String.IsNullOrEmpty(googleProductCategory)) { googleProductCategory = _googleShoppingSettings.DefaultGoogleCategory; } if (String.IsNullOrEmpty(googleProductCategory)) { throw new NopException("Default Google category is not set"); } writer.WriteStartElement("g", "google_product_category", googleBaseNamespace); writer.WriteCData(googleProductCategory); writer.WriteFullEndElement(); // g:google_product_category //product type [product_type] - Your category of the item var defaultProductCategory = _categoryService .GetProductCategoriesByProductId(product.Id, store.Id) .FirstOrDefault(); if (defaultProductCategory != null) { //TODO localize categories var category = defaultProductCategory.Category .GetFormattedBreadCrumb(_categoryService, separator: ">", languageId: languageId); if (!String.IsNullOrEmpty((category))) { writer.WriteStartElement("g", "product_type", googleBaseNamespace); writer.WriteCData(category); writer.WriteFullEndElement(); // g:product_type } } //link [link] - URL directly linking to your item's page on your website var productUrl = string.Format("{0}{1}", store.Url, product.GetSeName(languageId)); writer.WriteElementString("link", productUrl); //image link [image_link] - URL of an image of the item //additional images [additional_image_link] //up to 10 pictures const int maximumPictures = 10; var pictures = _pictureService.GetPicturesByProductId(product.Id, maximumPictures); for (int i = 0; i < pictures.Count; i++) { var picture = pictures[i]; var imageUrl = _pictureService.GetPictureUrl(picture, _googleShoppingSettings.ProductPictureSize, storeLocation: store.Url); if (i == 0) { //default image writer.WriteElementString("g", "image_link", googleBaseNamespace, imageUrl); } else { //additional image writer.WriteElementString("g", "additional_image_link", googleBaseNamespace, imageUrl); } } if (!pictures.Any()) { //no picture? submit a default one var imageUrl = _pictureService.GetDefaultPictureUrl(_googleShoppingSettings.ProductPictureSize, storeLocation: store.Url); writer.WriteElementString("g", "image_link", googleBaseNamespace, imageUrl); } //condition [condition] - Condition or state of the item writer.WriteElementString("g", "condition", googleBaseNamespace, "new"); writer.WriteElementString("g", "expiration_date", googleBaseNamespace, DateTime.Now.AddDays(_googleShoppingSettings.ExpirationNumberOfDays).ToString("yyyy-MM-dd")); #endregion #region Availability & Price //availability [availability] - Availability status of the item string availability = "in stock"; //in stock by default if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock && product.GetTotalStockQuantity() <= 0) { availability = "out of stock"; } //uncomment th code below in order to support "preorder" value for "availability" //if (product.AvailableForPreOrder && // (!product.PreOrderAvailabilityStartDateTimeUtc.HasValue || // product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow)) //{ // availability = "preorder"; //} writer.WriteElementString("g", "availability", googleBaseNamespace, availability); //price [price] - Price of the item var currency = GetUsedCurrency(); decimal finalPriceBase; if (_googleShoppingSettings.PricesConsiderPromotions) { var minPossiblePrice = _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer); if (product.HasTierPrices) { //calculate price for the maximum quantity if we have tier prices, and choose minimal minPossiblePrice = Math.Min(minPossiblePrice, _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, quantity: int.MaxValue)); } decimal taxRate; finalPriceBase = _taxService.GetProductPrice(product, minPossiblePrice, out taxRate); } else { finalPriceBase = product.Price; } decimal price = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, currency); //round price now so it matches the product details page price = RoundingHelper.RoundPrice(price); writer.WriteElementString("g", "price", googleBaseNamespace, price.ToString(new CultureInfo("en-US", false).NumberFormat) + " " + currency.CurrencyCode); #endregion #region Unique Product Identifiers /* Unique product identifiers such as UPC, EAN, JAN or ISBN allow us to show your listing on the appropriate product page. If you don't provide the required unique product identifiers, your store may not appear on product pages, and all your items may be removed from Product Search. * We require unique product identifiers for all products - except for custom made goods. For apparel, you must submit the 'brand' attribute. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute. In all cases, we recommend you submit all three attributes. * You need to submit at least two attributes of 'brand', 'gtin' and 'mpn', but we recommend that you submit all three if available. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute, but we recommend that you include 'brand' and 'mpn' if available. */ //brand [brand] - Brand of the item var defaultDestination = _destinationService.GetProductDestinationsByProductId((product.Id)).FirstOrDefault(); if (defaultDestination != null) { writer.WriteStartElement("g", "brand", googleBaseNamespace); writer.WriteCData(defaultDestination.Destination.Name); writer.WriteFullEndElement(); // g:brand } //identifier exists [identifier_exists] - Submit custom goods if (googleProduct != null && googleProduct.CustomGoods) { writer.WriteElementString("g", "identifier_exists", googleBaseNamespace, "FALSE"); } #endregion writer.WriteEndElement(); // item } } writer.WriteEndElement(); // channel writer.WriteEndElement(); // rss writer.WriteEndDocument(); } }
/// <summary> /// Generate a feed /// </summary> /// <param name="stream">Stream</param> /// <param name="store">Store</param> /// <returns>Generated feed</returns> public void GenerateFeed(Stream stream, Store store) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (store == null) { throw new ArgumentNullException(nameof(store)); } const string googleBaseNamespace = "http://base.google.com/ns/1.0"; var settings = new XmlWriterSettings { Encoding = Encoding.UTF8 }; //language var languageId = 0; var languages = _languageService.GetAllLanguages(storeId: store.Id); //if we have only one language, let's use it if (languages.Count == 1) { //let's use the first one var language = languages.FirstOrDefault(); languageId = language != null ? language.Id : 0; } //otherwise, use the current one if (languageId == 0) { languageId = _workContext.WorkingLanguage.Id; } //we load all Google products here using one SQL request (performance optimization) var allGoogleProducts = _googleService.GetAll(); //google GoogleWebAuthorizationBroker.Folder = "ShoppingContent.Sample"; var credential = GoogleWebAuthorizationBroker.AuthorizeAsync( new ClientSecrets { ClientId = "584676283202-qphqjj998id40caks269cjag92dsc4rn.apps.googleusercontent.com", ClientSecret = "mv3j9LBNBKuGXjM1xLCi6Tbr" }, new string[] { ShoppingContentService.Scope.Content }, "user", CancellationToken.None).Result; // Create the service. var service = new ShoppingContentService(new BaseClientService.Initializer() { HttpClientInitializer = credential, ApplicationName = "Shopping Content Arc", }); using (var writer = XmlWriter.Create(stream, settings)) { //Generate feed according to the following specs: http://www.google.com/support/merchants/bin/answer.py?answer=188494&expand=GB writer.WriteStartDocument(); writer.WriteStartElement("rss"); writer.WriteAttributeString("version", "2.0"); writer.WriteAttributeString("xmlns", "g", null, googleBaseNamespace); writer.WriteStartElement("channel"); writer.WriteElementString("title", "Google Base feed"); writer.WriteElementString("link", "http://base.google.com/base/"); writer.WriteElementString("description", "Information about products"); var products1 = _productService.SearchProducts(storeId: store.Id, visibleIndividuallyOnly: true) .Where(x => !x.ExcludeGoogleFeed); var query = from p in products1 from pc in p.ProductCategories.Where(pc => !pc.Category.Name.Contains("Copier") && !pc.Category.Name.Contains("Copiers - New")) select p; foreach (var product1 in products1) { Google.Apis.ShoppingContent.v2.Data.Product productG = new Google.Apis.ShoppingContent.v2.Data.Product(); var productsToProcess = new List <Core.Domain.Catalog.Product>(); productG.ContentLanguage = "EN"; productG.TargetCountry = "US"; productG.Channel = "online"; productG.Availability = "in stock"; productG.Condition = "new"; productG.GoogleProductCategory = "Electronics > Print, Copy, Scan & Fax Accessories > Copier Accessories"; productG.Gtin = product1.Gtin; var legacy_Reader = _dbContext.SqlQuery <string>($"SELECT LegacyCode FROM [dbo].LegacyIds where ItemID={product1.Id}").ToList(); if (legacy_Reader.Count > 0) { foreach (var id in legacy_Reader) { productG.Title += $" - {id}"; } } var categoryP = product1.ProductCategories.FirstOrDefault(); if (categoryP != null) { if (categoryP.Category.Name.Contains("Toner")) { productG.ProductType = "Electronics > Print, Copy, Scan & Fax Accessories > Printer Accessories > Toner & Inkjet Cartridges"; } else { productG.ProductType = "Electronics > Print, Copy, Scan & Fax Accessories"; } } switch (product1.ProductType) { case ProductType.SimpleProduct: { //simple product doesn't have child products productsToProcess.Add(product1); } break; case ProductType.GroupedProduct: { //grouped products could have several child products var associatedProducts = _productService.GetAssociatedProducts(product1.Id, store.Id); productsToProcess.AddRange(associatedProducts); } break; default: continue; } foreach (var product in productsToProcess) { writer.WriteStartElement("item"); #region Basic Product Information //id [id]- An identifier of the item writer.WriteElementString("g", "id", googleBaseNamespace, product.Id.ToString()); //title [title] - Title of the item writer.WriteStartElement("title"); var title = product.GetLocalized(x => x.Name, languageId); //title should be not longer than 70 characters if (title.Length > 70) { title = title.Substring(0, 70); } writer.WriteCData(title); writer.WriteEndElement(); // title //description [description] - Description of the item writer.WriteStartElement("description"); string description = product.GetLocalized(x => x.FullDescription, languageId); if (String.IsNullOrEmpty(description)) { description = product.GetLocalized(x => x.ShortDescription, languageId); } if (String.IsNullOrEmpty(description)) { description = product.GetLocalized(x => x.Name, languageId); //description is required } //resolving character encoding issues in your data feed description = StripInvalidChars(description, true); writer.WriteCData(description); writer.WriteEndElement(); // description //google product category [google_product_category] - Google's category of the item //the category of the product according to Google’s product taxonomy. http://www.google.com/support/merchants/bin/answer.py?answer=160081 string googleProductCategory = ""; //var googleProduct = _googleService.GetByProductId(product.Id); var googleProduct = allGoogleProducts.FirstOrDefault(x => x.ProductId == product.Id); if (googleProduct != null) { googleProductCategory = googleProduct.Taxonomy; } if (String.IsNullOrEmpty(googleProductCategory)) { googleProductCategory = _googleShoppingSettings.DefaultGoogleCategory; } if (String.IsNullOrEmpty(googleProductCategory)) { throw new NopException("Default Google category is not set"); } writer.WriteStartElement("g", "google_product_category", googleBaseNamespace); writer.WriteCData(googleProductCategory); writer.WriteFullEndElement(); // g:google_product_category //product type [product_type] - Your category of the item var defaultProductCategory = _categoryService .GetProductCategoriesByProductId(product.Id, store.Id) .FirstOrDefault(); if (defaultProductCategory != null) { //TODO localize categories var category = defaultProductCategory.Category .GetFormattedBreadCrumb(_categoryService, separator: ">", languageId: languageId); if (!String.IsNullOrEmpty(category)) { writer.WriteStartElement("g", "product_type", googleBaseNamespace); writer.WriteCData(category); writer.WriteFullEndElement(); // g:product_type } } //link [link] - URL directly linking to your item's page on your website var productUrl = GetUrlHelper().RouteUrl("Product", new { SeName = product.GetSeName(languageId) }, GetHttpProtocol()); productG.Link = productUrl; writer.WriteElementString("link", productUrl); //image link [image_link] - URL of an image of the item //additional images [additional_image_link] //up to 10 pictures const int maximumPictures = 10; var storeLocation = _securitySettings.ForceSslForAllPages ? (!string.IsNullOrWhiteSpace(store.SecureUrl) ? store.SecureUrl : store.Url.Replace("http://", "https://")): store.Url; var pictures = _pictureService.GetPicturesByProductId(product.Id, maximumPictures); for (int i = 0; i < pictures.Count; i++) { var picture = pictures[i]; var imageUrl = _pictureService.GetPictureUrl(picture, _googleShoppingSettings.ProductPictureSize, storeLocation: storeLocation); if (i == 0) { //default image productG.ImageLink = imageUrl; writer.WriteElementString("g", "image_link", googleBaseNamespace, imageUrl); } else { //additional image productG.AdditionalImageLinks.Add(imageUrl); writer.WriteElementString("g", "additional_image_link", googleBaseNamespace, imageUrl); } } if (!pictures.Any()) { //no picture? submit a default one var imageUrl = _pictureService.GetDefaultPictureUrl(_googleShoppingSettings.ProductPictureSize, storeLocation: storeLocation); productG.ImageLink = imageUrl; writer.WriteElementString("g", "image_link", googleBaseNamespace, imageUrl); } //condition [condition] - Condition or state of the item productG.Condition = "new"; productG.ExpirationDate = DateTime.Now.AddDays(_googleShoppingSettings.ExpirationNumberOfDays).ToString("yyyy-MM-dd"); writer.WriteElementString("g", "condition", googleBaseNamespace, "new"); writer.WriteElementString("g", "expiration_date", googleBaseNamespace, DateTime.Now.AddDays(_googleShoppingSettings.ExpirationNumberOfDays).ToString("yyyy-MM-dd")); #endregion #region Availability & Price //availability [availability] - Availability status of the item string availability = "in stock"; //in stock by default if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock && product.BackorderMode == BackorderMode.NoBackorders && product.GetTotalStockQuantity() <= 0) { availability = "out of stock"; } //uncomment th code below in order to support "preorder" value for "availability" //if (product.AvailableForPreOrder && // (!product.PreOrderAvailabilityStartDateTimeUtc.HasValue || // product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow)) //{ // availability = "preorder"; //} productG.Availability = availability; writer.WriteElementString("g", "availability", googleBaseNamespace, availability); //price [price] - Price of the item var currency = GetUsedCurrency(); decimal finalPriceBase; if (_googleShoppingSettings.PricesConsiderPromotions) { var minPossiblePrice = _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer); if (product.HasTierPrices) { //calculate price for the maximum quantity if we have tier prices, and choose minimal minPossiblePrice = Math.Min(minPossiblePrice, _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, quantity: int.MaxValue)); } finalPriceBase = _taxService.GetProductPrice(product, minPossiblePrice, out decimal _); } else { finalPriceBase = product.Price; } decimal price = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, currency); //round price now so it matches the product details page price = RoundingHelper.RoundPrice(price); Price priceG = new Price { Currency = currency.CurrencyCode, Value = price.ToString(new CultureInfo("en-US", false).NumberFormat) }; productG.Price = priceG; writer.WriteElementString("g", "price", googleBaseNamespace, price.ToString(new CultureInfo("en-US", false).NumberFormat) + " " + currency.CurrencyCode); #endregion #region Unique Product Identifiers /* Unique product identifiers such as UPC, EAN, JAN or ISBN allow us to show your listing on the appropriate product page. If you don't provide the required unique product identifiers, your store may not appear on product pages, and all your items may be removed from Product Search. * We require unique product identifiers for all products - except for custom made goods. For apparel, you must submit the 'brand' attribute. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute. In all cases, we recommend you submit all three attributes. * You need to submit at least two attributes of 'brand', 'gtin' and 'mpn', but we recommend that you submit all three if available. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute, but we recommend that you include 'brand' and 'mpn' if available. */ //GTIN [gtin] - GTIN var gtin = product.Gtin; if (!String.IsNullOrEmpty(gtin)) { productG.Gtin = gtin; writer.WriteStartElement("g", "gtin", googleBaseNamespace); writer.WriteCData(gtin); writer.WriteFullEndElement(); // g:gtin } //brand [brand] - Brand of the item var defaultManufacturer = _manufacturerService.GetProductManufacturersByProductId((product.Id)).FirstOrDefault(); if (defaultManufacturer != null) { productG.Brand = defaultManufacturer.Manufacturer.Name; writer.WriteStartElement("g", "brand", googleBaseNamespace); writer.WriteCData(defaultManufacturer.Manufacturer.Name); writer.WriteFullEndElement(); // g:brand } //mpn [mpn] - Manufacturer Part Number (MPN) of the item var mpn = product.ManufacturerPartNumber; if (!String.IsNullOrEmpty(mpn)) { productG.Mpn = mpn; writer.WriteStartElement("g", "mpn", googleBaseNamespace); writer.WriteCData(mpn); writer.WriteFullEndElement(); // g:mpn } //identifier exists [identifier_exists] - Submit custom goods if (googleProduct != null && googleProduct.CustomGoods) { productG.IdentifierExists = false; writer.WriteElementString("g", "identifier_exists", googleBaseNamespace, "FALSE"); } #endregion #region Apparel Products /* Apparel includes all products that fall under 'Apparel & Accessories' (including all sub-categories) * in Google’s product taxonomy. */ //gender [gender] - Gender of the item if (googleProduct != null && !String.IsNullOrEmpty(googleProduct.Gender)) { productG.Gender = googleProduct.Gender; writer.WriteStartElement("g", "gender", googleBaseNamespace); writer.WriteCData(googleProduct.Gender); writer.WriteFullEndElement(); // g:gender } //age group [age_group] - Target age group of the item if (googleProduct != null && !String.IsNullOrEmpty(googleProduct.AgeGroup)) { productG.AgeGroup = googleProduct.AgeGroup; writer.WriteStartElement("g", "age_group", googleBaseNamespace); writer.WriteCData(googleProduct.AgeGroup); writer.WriteFullEndElement(); // g:age_group } //color [color] - Color of the item if (googleProduct != null && !String.IsNullOrEmpty(googleProduct.Color)) { productG.Color = googleProduct.Color; writer.WriteStartElement("g", "color", googleBaseNamespace); writer.WriteCData(googleProduct.Color); writer.WriteFullEndElement(); // g:color } //size [size] - Size of the item if (googleProduct != null && !String.IsNullOrEmpty(googleProduct.Size)) { productG.Sizes.Add(googleProduct.Size); writer.WriteStartElement("g", "size", googleBaseNamespace); writer.WriteCData(googleProduct.Size); writer.WriteFullEndElement(); // g:size } #endregion #region Tax & Shipping //tax [tax] //The tax attribute is an item-level override for merchant-level tax settings as defined in your Google Merchant Center account. This attribute is only accepted in the US, if your feed targets a country outside of the US, please do not use this attribute. //IMPORTANT NOTE: Set tax in your Google Merchant Center account settings //IMPORTANT NOTE: Set shipping in your Google Merchant Center account settings //shipping weight [shipping_weight] - Weight of the item for shipping //We accept only the following units of weight: lb, oz, g, kg. if (_googleShoppingSettings.PassShippingInfoWeight) { string weightName; var shippingWeight = product.Weight; var weightSystemName = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).SystemKeyword; switch (weightSystemName) { case "ounce": weightName = "oz"; break; case "lb": weightName = "lb"; break; case "grams": weightName = "g"; break; case "kg": weightName = "kg"; break; default: //unknown weight throw new Exception("Not supported weight. Google accepts the following units: lb, oz, g, kg."); } ProductShippingDimension psd = new ProductShippingDimension { Unit = weightName, Value = double.Parse(shippingWeight.ToString()) }; productG.ShippingHeight = psd; writer.WriteElementString("g", "shipping_weight", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", shippingWeight.ToString(new CultureInfo("en-US", false).NumberFormat), weightName)); } //shipping length [shipping_length] - Length of the item for shipping //shipping width [shipping_width] - Width of the item for shipping //shipping height [shipping_height] - Height of the item for shipping //We accept only the following units of length: in, cm if (_googleShoppingSettings.PassShippingInfoDimensions) { string dimensionName; var length = product.Length; var width = product.Width; var height = product.Height; var dimensionSystemName = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).SystemKeyword; switch (dimensionSystemName) { case "inches": dimensionName = "in"; break; //TODO support other dimensions (convert to cm) default: //unknown dimension throw new Exception("Not supported dimension. Google accepts the following units: in, cm."); } ProductShippingDimension psd = new ProductShippingDimension { Unit = dimensionName, Value = double.Parse(length.ToString()) }; productG.ShippingLength = psd; psd = new ProductShippingDimension { Unit = dimensionName, Value = double.Parse(width.ToString()) }; productG.ShippingWidth = psd; psd = new ProductShippingDimension { Unit = dimensionName, Value = double.Parse(height.ToString()) }; productG.ShippingHeight = psd; writer.WriteElementString("g", "shipping_length", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", length.ToString(new CultureInfo("en-US", false).NumberFormat), dimensionName)); writer.WriteElementString("g", "shipping_width", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", width.ToString(new CultureInfo("en-US", false).NumberFormat), dimensionName)); writer.WriteElementString("g", "shipping_height", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", height.ToString(new CultureInfo("en-US", false).NumberFormat), dimensionName)); } #endregion writer.WriteEndElement(); // item //used in in description string usedinquery = $@"SELECT ItemsCompatability.ItemID FROM ItemsCompatability INNER JOIN Items ON ItemsCompatability.ItemID = Items.ItemID WHERE (ItemsCompatability.ItemIDPart ={product.Id}) UNION SELECT [Groups-Items].ItemID FROM [Groups-Items] INNER JOIN Items AS Items_1 ON [Groups-Items].ItemID = Items_1.ItemID WHERE ([Groups-Items].GroupID IN (SELECT GroupID FROM [Relations-Groups-Items] WHERE (ItemID ={product.Id}) AND (Direction = 'B'))) UNION SELECT [Relations-Groups-Items].ItemID FROM Items INNER JOIN [Relations-Groups-Items] ON Items.ItemID = [Relations-Groups-Items].ItemID WHERE ([Relations-Groups-Items].Direction = 'A') AND ([Relations-Groups-Items].GroupID IN (SELECT GroupID FROM [Groups-Items] WHERE (ItemID ={product.Id})))"; var usedin_Reader = _dbContext.SqlQuery <int>(usedinquery).ToList(); if (usedin_Reader.Count > 0) { var usedIn = _productService.GetProductsByIds(usedin_Reader.ToArray()); foreach (var item in usedIn) { productG.Description = $"{productG.Description} {item.Name}, "; } productG.Description = productG.Description.TrimEnd(','); productG.Description += "."; } else { productG.Description += "N/A"; } } } writer.WriteEndElement(); // channel writer.WriteEndElement(); // rss writer.WriteEndDocument(); } }
public void CanGetFinalProductPrice() { var product = _productService.GetProductBySku("BP_20_WSP"); //customer var customer = new Customer(); _priceCalcService.GetFinalPrice(product, customer, 0, false).Should().Be(79.99M); _priceCalcService.GetFinalPrice(product, customer, 0, false, 2).Should().Be(19M); }
protected IEnumerable <ProductOverviewModel> PrepareProductOverviewModels(IEnumerable <Product> products, bool preparePriceModel = true, bool preparePictureModel = true, int?productThumbPictureSize = null, bool prepareSpecificationAttributes = false, bool forceRedirectionAfterAddingToCart = false) { if (products == null) { throw new ArgumentNullException("products"); } var models = new List <ProductOverviewModel>(); foreach (var product in products) { var model = new ProductOverviewModel() { Id = product.Id, Name = product.GetLocalized(x => x.Name), ShortDescription = product.GetLocalized(x => x.ShortDescription), FullDescription = product.GetLocalized(x => x.FullDescription), SeName = product.GetSeName(), }; //price if (preparePriceModel) { #region Prepare product price var priceModel = new ProductOverviewModel.ProductPriceModel(); switch (product.ProductType) { case ProductType.GroupedProduct: { #region Grouped product var associatedProducts = _productService.GetAssociatedProducts(product.Id, _storeContext.CurrentStore.Id); switch (associatedProducts.Count) { case 0: { //no associated products priceModel.OldPrice = null; priceModel.Price = null; priceModel.DisableBuyButton = true; priceModel.DisableWishlistButton = true; priceModel.AvailableForPreOrder = false; } break; default: { //we have at least one associated product priceModel.DisableBuyButton = true; priceModel.DisableWishlistButton = true; priceModel.AvailableForPreOrder = false; if (_permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { //find a minimum possible price decimal?minPossiblePrice = null; Product minPriceProduct = null; foreach (var associatedProduct in associatedProducts) { //calculate for the maximum quantity (in case if we have tier prices) var tmpPrice = _priceCalculationService.GetFinalPrice(associatedProduct, _workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue); if (!minPossiblePrice.HasValue || tmpPrice < minPossiblePrice.Value) { minPriceProduct = associatedProduct; minPossiblePrice = tmpPrice; } } if (minPriceProduct != null && !minPriceProduct.CustomerEntersPrice) { if (minPriceProduct.CallForPrice) { priceModel.OldPrice = null; priceModel.Price = _localizationService.GetResource("Products.CallForPrice"); } else if (minPossiblePrice.HasValue) { //calculate prices decimal taxRate = decimal.Zero; decimal finalPriceBase = _taxService.GetProductPrice(minPriceProduct, minPossiblePrice.Value, out taxRate); decimal finalPrice = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, _workContext.WorkingCurrency); priceModel.OldPrice = null; priceModel.Price = String.Format(_localizationService.GetResource("Products.PriceRangeFrom"), _priceFormatter.FormatPrice(finalPrice)); } else { //Actually it's not possible (we presume that minimalPrice always has a value) //We never should get here Debug.WriteLine(string.Format("Cannot calculate minPrice for product #{0}", product.Id)); } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } } break; } #endregion } break; case ProductType.SimpleProduct: default: { #region Simple product //add to cart button priceModel.DisableBuyButton = product.DisableBuyButton || !_permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart) || !_permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //add to wishlist button priceModel.DisableWishlistButton = product.DisableWishlistButton || !_permissionService.Authorize(StandardPermissionProvider.EnableWishlist) || !_permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //pre-order if (product.AvailableForPreOrder) { priceModel.AvailableForPreOrder = !product.PreOrderAvailabilityStartDateTimeUtc.HasValue || product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow; priceModel.PreOrderAvailabilityStartDateTimeUtc = product.PreOrderAvailabilityStartDateTimeUtc; } //prices if (_permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { //calculate for the maximum quantity (in case if we have tier prices) decimal minPossiblePrice = _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue); if (!product.CustomerEntersPrice) { if (product.CallForPrice) { //call for price priceModel.OldPrice = null; priceModel.Price = _localizationService.GetResource("Products.CallForPrice"); } else { //calculate prices decimal taxRate = decimal.Zero; decimal oldPriceBase = _taxService.GetProductPrice(product, product.OldPrice, out taxRate); decimal finalPriceBase = _taxService.GetProductPrice(product, minPossiblePrice, out taxRate); decimal oldPrice = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, _workContext.WorkingCurrency); decimal finalPrice = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, _workContext.WorkingCurrency); //do we have tier prices configured? var tierPrices = new List <TierPrice>(); if (product.HasTierPrices) { tierPrices.AddRange(product.TierPrices .OrderBy(tp => tp.Quantity) .ToList() .FilterByStore(_storeContext.CurrentStore.Id) .FilterForCustomer(_workContext.CurrentCustomer) .RemoveDuplicatedQuantities()); } //When there is just one tier (with qty 1), //there are no actual savings in the list. bool displayFromMessage = tierPrices.Count > 0 && !(tierPrices.Count == 1 && tierPrices[0].Quantity <= 1); if (displayFromMessage) { priceModel.OldPrice = null; priceModel.Price = String.Format(_localizationService.GetResource("Products.PriceRangeFrom"), _priceFormatter.FormatPrice(finalPrice)); } else { if (finalPriceBase != oldPriceBase && oldPriceBase != decimal.Zero) { priceModel.OldPrice = _priceFormatter.FormatPrice(oldPrice); priceModel.Price = _priceFormatter.FormatPrice(finalPrice); } else { priceModel.OldPrice = null; priceModel.Price = _priceFormatter.FormatPrice(finalPrice); } } } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } #endregion } break; } model.ProductPrice = priceModel; #endregion } //picture if (preparePictureModel) { #region Prepare product picture //If a size has been set in the view, we use it in priority int pictureSize = productThumbPictureSize.HasValue ? productThumbPictureSize.Value : 125; //prepare picture model var picture = _pictureService.GetPicturesByProductId(product.Id, 1).FirstOrDefault(); model.DefaultPictureModel = new PictureModel() { ImageUrl = _pictureService.GetPictureUrl(picture, pictureSize), FullSizeImageUrl = _pictureService.GetPictureUrl(picture), Title = string.Format(_localizationService.GetResource("Media.Product.ImageLinkTitleFormat"), model.Name), AlternateText = string.Format(_localizationService.GetResource("Media.Product.ImageAlternateTextFormat"), model.Name) }; #endregion } models.Add(model); } return(models); }
/// <summary> /// Generate a feed /// </summary> /// <param name="stream">Stream</param> /// <param name="store">Store</param> /// <returns>Generated feed</returns> public void GenerateFeed(Stream stream, Store store) { if (stream == null) { throw new ArgumentNullException(nameof(stream)); } if (store == null) { throw new ArgumentNullException(nameof(store)); } const string googleBaseNamespace = "http://base.google.com/ns/1.0"; var settings = new XmlWriterSettings { Encoding = Encoding.UTF8 }; var googleShoppingSettings = _settingService.LoadSetting <GoogleShoppingSettings>(store.Id); //language var languageId = 0; var languages = _languageService.GetAllLanguages(storeId: store.Id); //if we have only one language, let's use it if (languages.Count == 1) { //let's use the first one var language = languages.FirstOrDefault(); languageId = language != null ? language.Id : 0; } //otherwise, use the current one if (languageId == 0) { languageId = _workContext.WorkingLanguage.Id; } //we load all Google products here using one SQL request (performance optimization) var allGoogleProducts = _googleService.GetAll(); using (var writer = XmlWriter.Create(stream, settings)) { //Generate feed according to the following specs: http://www.google.com/support/merchants/bin/answer.py?answer=188494&expand=GB writer.WriteStartDocument(); writer.WriteStartElement("rss"); writer.WriteAttributeString("version", "2.0"); writer.WriteAttributeString("xmlns", "g", null, googleBaseNamespace); writer.WriteStartElement("channel"); writer.WriteElementString("title", "Google Base feed"); writer.WriteElementString("link", "http://base.google.com/base/"); writer.WriteElementString("description", "Information about products"); var products1 = _productService.SearchProducts(storeId: store.Id, visibleIndividuallyOnly: true); foreach (var product1 in products1) { var productsToProcess = new List <Product>(); switch (product1.ProductType) { case ProductType.SimpleProduct: { //simple product doesn't have child products productsToProcess.Add(product1); } break; case ProductType.GroupedProduct: { //grouped products could have several child products var associatedProducts = _productService.GetAssociatedProducts(product1.Id, store.Id); productsToProcess.AddRange(associatedProducts); } break; default: continue; } foreach (var product in productsToProcess) { writer.WriteStartElement("item"); #region Basic Product Information //id [id]- An identifier of the item writer.WriteElementString("g", "id", googleBaseNamespace, product.Id.ToString()); //title [title] - Title of the item writer.WriteStartElement("title"); var title = _localizationService.GetLocalized(product, x => x.Name, languageId); //title should be not longer than 70 characters if (title.Length > 70) { title = title.Substring(0, 70); } writer.WriteCData(title); writer.WriteEndElement(); // title //description [description] - Description of the item writer.WriteStartElement("description"); var description = _localizationService.GetLocalized(product, x => x.FullDescription, languageId); if (string.IsNullOrEmpty(description)) { description = _localizationService.GetLocalized(product, x => x.ShortDescription, languageId); } if (string.IsNullOrEmpty(description)) { description = _localizationService.GetLocalized(product, x => x.Name, languageId); //description is required } //resolving character encoding issues in your data feed description = StripInvalidChars(description, true); writer.WriteCData(description); writer.WriteEndElement(); // description //google product category [google_product_category] - Google's category of the item //the category of the product according to Google’s product taxonomy. http://www.google.com/support/merchants/bin/answer.py?answer=160081 var googleProductCategory = ""; //var googleProduct = _googleService.GetByProductId(product.Id); var googleProduct = allGoogleProducts.FirstOrDefault(x => x.ProductId == product.Id); if (googleProduct != null) { googleProductCategory = googleProduct.Taxonomy; } if (string.IsNullOrEmpty(googleProductCategory)) { googleProductCategory = googleShoppingSettings.DefaultGoogleCategory; } if (string.IsNullOrEmpty(googleProductCategory)) { throw new NopException("Default Google category is not set"); } writer.WriteStartElement("g", "google_product_category", googleBaseNamespace); writer.WriteCData(googleProductCategory); writer.WriteFullEndElement(); // g:google_product_category //product type [product_type] - Your category of the item var defaultProductCategory = _categoryService .GetProductCategoriesByProductId(product.Id, store.Id) .FirstOrDefault(); if (defaultProductCategory != null) { //TODO localize categories var category = _categoryService.GetFormattedBreadCrumb(defaultProductCategory.Category, separator: ">", languageId: languageId); if (!string.IsNullOrEmpty(category)) { writer.WriteStartElement("g", "product_type", googleBaseNamespace); writer.WriteCData(category); writer.WriteFullEndElement(); // g:product_type } } //link [link] - URL directly linking to your item's page on your website var productUrl = GetUrlHelper().RouteUrl("Product", new { SeName = _urlRecordService.GetSeName(product) }, GetHttpProtocol()); writer.WriteElementString("link", productUrl); //image link [image_link] - URL of an image of the item //additional images [additional_image_link] //up to 10 pictures const int maximumPictures = 10; var storeLocation = _webHelper.GetStoreLocation(_securitySettings.ForceSslForAllPages); var pictures = _pictureService.GetPicturesByProductId(product.Id, maximumPictures); for (var i = 0; i < pictures.Count; i++) { var picture = pictures[i]; var imageUrl = _pictureService.GetPictureUrl(picture, googleShoppingSettings.ProductPictureSize, storeLocation: storeLocation); if (i == 0) { //default image writer.WriteElementString("g", "image_link", googleBaseNamespace, imageUrl); } else { //additional image writer.WriteElementString("g", "additional_image_link", googleBaseNamespace, imageUrl); } } if (!pictures.Any()) { //no picture? submit a default one var imageUrl = _pictureService.GetDefaultPictureUrl(googleShoppingSettings.ProductPictureSize, storeLocation: storeLocation); writer.WriteElementString("g", "image_link", googleBaseNamespace, imageUrl); } //condition [condition] - Condition or state of the item writer.WriteElementString("g", "condition", googleBaseNamespace, "new"); writer.WriteElementString("g", "expiration_date", googleBaseNamespace, DateTime.Now.AddDays(googleShoppingSettings.ExpirationNumberOfDays).ToString("yyyy-MM-dd")); #endregion #region Availability & Price //availability [availability] - Availability status of the item var availability = "in stock"; //in stock by default if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock && product.BackorderMode == BackorderMode.NoBackorders && _productService.GetTotalStockQuantity(product) <= 0) { availability = "out of stock"; } //uncomment th code below in order to support "preorder" value for "availability" //if (product.AvailableForPreOrder && // (!product.PreOrderAvailabilityStartDateTimeUtc.HasValue || // product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow)) //{ // availability = "preorder"; //} writer.WriteElementString("g", "availability", googleBaseNamespace, availability); //price [price] - Price of the item var currency = GetUsedCurrency(); decimal finalPriceBase; if (googleShoppingSettings.PricesConsiderPromotions) { var minPossiblePrice = _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer); if (product.HasTierPrices) { //calculate price for the maximum quantity if we have tier prices, and choose minimal minPossiblePrice = Math.Min(minPossiblePrice, _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, quantity: int.MaxValue)); } finalPriceBase = _taxService.GetProductPrice(product, minPossiblePrice, out var _); } else { finalPriceBase = product.Price; } var price = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, currency); //round price now so it matches the product details page price = _priceCalculationService.RoundPrice(price); writer.WriteElementString("g", "price", googleBaseNamespace, price.ToString(new CultureInfo("en-US", false).NumberFormat) + " " + currency.CurrencyCode); #endregion #region Unique Product Identifiers /* Unique product identifiers such as UPC, EAN, JAN or ISBN allow us to show your listing on the appropriate product page. If you don't provide the required unique product identifiers, your store may not appear on product pages, and all your items may be removed from Product Search. * We require unique product identifiers for all products - except for custom made goods. For apparel, you must submit the 'brand' attribute. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute. In all cases, we recommend you submit all three attributes. * You need to submit at least two attributes of 'brand', 'gtin' and 'mpn', but we recommend that you submit all three if available. For media (such as books, movies, music and video games), you must submit the 'gtin' attribute, but we recommend that you include 'brand' and 'mpn' if available. */ //GTIN [gtin] - GTIN var gtin = product.Gtin; if (!string.IsNullOrEmpty(gtin)) { writer.WriteStartElement("g", "gtin", googleBaseNamespace); writer.WriteCData(gtin); writer.WriteFullEndElement(); // g:gtin } //brand [brand] - Brand of the item var defaultManufacturer = _manufacturerService.GetProductManufacturersByProductId(product.Id).FirstOrDefault(); if (defaultManufacturer != null) { writer.WriteStartElement("g", "brand", googleBaseNamespace); writer.WriteCData(defaultManufacturer.Manufacturer.Name); writer.WriteFullEndElement(); // g:brand } //mpn [mpn] - Manufacturer Part Number (MPN) of the item var mpn = product.ManufacturerPartNumber; if (!string.IsNullOrEmpty(mpn)) { writer.WriteStartElement("g", "mpn", googleBaseNamespace); writer.WriteCData(mpn); writer.WriteFullEndElement(); // g:mpn } //identifier exists [identifier_exists] - Submit custom goods if (googleProduct != null && googleProduct.CustomGoods) { writer.WriteElementString("g", "identifier_exists", googleBaseNamespace, "FALSE"); } #endregion #region Apparel Products /* Apparel includes all products that fall under 'Apparel & Accessories' (including all sub-categories) * in Google’s product taxonomy. */ //gender [gender] - Gender of the item if (googleProduct != null && !string.IsNullOrEmpty(googleProduct.Gender)) { writer.WriteStartElement("g", "gender", googleBaseNamespace); writer.WriteCData(googleProduct.Gender); writer.WriteFullEndElement(); // g:gender } //age group [age_group] - Target age group of the item if (googleProduct != null && !string.IsNullOrEmpty(googleProduct.AgeGroup)) { writer.WriteStartElement("g", "age_group", googleBaseNamespace); writer.WriteCData(googleProduct.AgeGroup); writer.WriteFullEndElement(); // g:age_group } //color [color] - Color of the item if (googleProduct != null && !string.IsNullOrEmpty(googleProduct.Color)) { writer.WriteStartElement("g", "color", googleBaseNamespace); writer.WriteCData(googleProduct.Color); writer.WriteFullEndElement(); // g:color } //size [size] - Size of the item if (googleProduct != null && !string.IsNullOrEmpty(googleProduct.Size)) { writer.WriteStartElement("g", "size", googleBaseNamespace); writer.WriteCData(googleProduct.Size); writer.WriteFullEndElement(); // g:size } #endregion #region Tax & Shipping //tax [tax] //The tax attribute is an item-level override for merchant-level tax settings as defined in your Google Merchant Center account. This attribute is only accepted in the US, if your feed targets a country outside of the US, please do not use this attribute. //IMPORTANT NOTE: Set tax in your Google Merchant Center account settings //IMPORTANT NOTE: Set shipping in your Google Merchant Center account settings //shipping weight [shipping_weight] - Weight of the item for shipping //We accept only the following units of weight: lb, oz, g, kg. if (googleShoppingSettings.PassShippingInfoWeight) { string weightName; var shippingWeight = product.Weight; var weightSystemName = _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).SystemKeyword; switch (weightSystemName) { case "ounce": weightName = "oz"; break; case "lb": weightName = "lb"; break; case "grams": weightName = "g"; break; case "kg": weightName = "kg"; break; default: //unknown weight throw new Exception("Not supported weight. Google accepts the following units: lb, oz, g, kg."); } writer.WriteElementString("g", "shipping_weight", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", shippingWeight.ToString(new CultureInfo("en-US", false).NumberFormat), weightName)); } //shipping length [shipping_length] - Length of the item for shipping //shipping width [shipping_width] - Width of the item for shipping //shipping height [shipping_height] - Height of the item for shipping //We accept only the following units of length: in, cm if (googleShoppingSettings.PassShippingInfoDimensions) { string dimensionName; var length = product.Length; var width = product.Width; var height = product.Height; var dimensionSystemName = _measureService.GetMeasureDimensionById(_measureSettings.BaseDimensionId).SystemKeyword; switch (dimensionSystemName) { case "inches": dimensionName = "in"; break; //TODO support other dimensions (convert to cm) default: //unknown dimension throw new Exception("Not supported dimension. Google accepts the following units: in, cm."); } writer.WriteElementString("g", "shipping_length", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", length.ToString(new CultureInfo("en-US", false).NumberFormat), dimensionName)); writer.WriteElementString("g", "shipping_width", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", width.ToString(new CultureInfo("en-US", false).NumberFormat), dimensionName)); writer.WriteElementString("g", "shipping_height", googleBaseNamespace, string.Format(CultureInfo.InvariantCulture, "{0} {1}", height.ToString(new CultureInfo("en-US", false).NumberFormat), dimensionName)); } #endregion writer.WriteEndElement(); // item } } writer.WriteEndElement(); // channel writer.WriteEndElement(); // rss writer.WriteEndDocument(); } }
public void GetProductPrice(Product product, out decimal price, out decimal priceWithDiscount) { price = decimal.Zero; priceWithDiscount = decimal.Zero; if (_permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { if (!product.CustomerEntersPrice) { if (!product.CallForPrice) { decimal taxRate; decimal oldPriceBase = _taxService.GetProductPrice(product, product.OldPrice, out taxRate); decimal finalPriceWithDiscountBase = _taxService.GetProductPrice(product, _priceCalculationService.GetFinalPrice(product, _workContext.CurrentCustomer, includeDiscounts: true), out taxRate); price = _currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, _workContext.WorkingCurrency); priceWithDiscount = _currencyService.ConvertFromPrimaryStoreCurrency(finalPriceWithDiscountBase, _workContext.WorkingCurrency); if (price == 0) { price = priceWithDiscount; priceWithDiscount = 0; } } } } }
public static IEnumerable<ProductOverviewModel> PrepareProductOverviewModels(this Controller controller, IWorkContext workContext, IStoreContext storeContext, ICategoryService categoryService, IProductService productService, ISpecificationAttributeService specificationAttributeService, IPriceCalculationService priceCalculationService, IPriceFormatter priceFormatter, IPermissionService permissionService, ILocalizationService localizationService, ITaxService taxService, ICurrencyService currencyService, IPictureService pictureService, IWebHelper webHelper, ICacheManager cacheManager, CatalogSettings catalogSettings, MediaSettings mediaSettings, IEnumerable<Product> products, bool preparePriceModel = true, bool preparePictureModel = true, int? productThumbPictureSize = null, bool prepareSpecificationAttributes = false, bool forceRedirectionAfterAddingToCart = false) { if (products == null) throw new ArgumentNullException("products"); var models = new List<ProductOverviewModel>(); foreach (var product in products) { var model = new ProductOverviewModel { Id = product.Id, Name = product.GetLocalized(x => x.Name), ShortDescription = product.GetLocalized(x => x.ShortDescription), FullDescription = product.GetLocalized(x => x.FullDescription), SeName = product.GetSeName(), }; //price if (preparePriceModel) { #region Prepare product price var priceModel = new ProductOverviewModel.ProductPriceModel { ForceRedirectionAfterAddingToCart = forceRedirectionAfterAddingToCart }; switch (product.ProductType) { case ProductType.GroupedProduct: { #region Grouped product var associatedProducts = productService.GetAssociatedProducts(product.Id, storeContext.CurrentStore.Id); switch (associatedProducts.Count) { case 0: { //no associated products //priceModel.DisableBuyButton = true; //priceModel.DisableWishlistButton = true; //compare products priceModel.DisableAddToCompareListButton = !catalogSettings.CompareProductsEnabled; //priceModel.AvailableForPreOrder = false; } break; default: { //we have at least one associated product //priceModel.DisableBuyButton = true; //priceModel.DisableWishlistButton = true; //compare products priceModel.DisableAddToCompareListButton = !catalogSettings.CompareProductsEnabled; //priceModel.AvailableForPreOrder = false; if (permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { //find a minimum possible price decimal? minPossiblePrice = null; Product minPriceProduct = null; foreach (var associatedProduct in associatedProducts) { //calculate for the maximum quantity (in case if we have tier prices) var tmpPrice = priceCalculationService.GetFinalPrice(associatedProduct, workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue); if (!minPossiblePrice.HasValue || tmpPrice < minPossiblePrice.Value) { minPriceProduct = associatedProduct; minPossiblePrice = tmpPrice; } } if (minPriceProduct != null && !minPriceProduct.CustomerEntersPrice) { if (minPriceProduct.CallForPrice) { priceModel.OldPrice = null; priceModel.Price = localizationService.GetResource("Products.CallForPrice"); } else if (minPossiblePrice.HasValue) { //calculate prices decimal taxRate; decimal finalPriceBase = taxService.GetProductPrice(minPriceProduct, minPossiblePrice.Value, out taxRate); decimal finalPrice = currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, workContext.WorkingCurrency); priceModel.OldPrice = null; priceModel.Price = String.Format(localizationService.GetResource("Products.PriceRangeFrom"), priceFormatter.FormatPrice(finalPrice)); } else { //Actually it's not possible (we presume that minimalPrice always has a value) //We never should get here Debug.WriteLine("Cannot calculate minPrice for product #{0}", product.Id); } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } } break; } #endregion } break; case ProductType.SimpleProduct: default: { #region Simple product //add to cart button priceModel.DisableBuyButton = product.DisableBuyButton || !permissionService.Authorize(StandardPermissionProvider.EnableShoppingCart) || !permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //add to wishlist button priceModel.DisableWishlistButton = product.DisableWishlistButton || !permissionService.Authorize(StandardPermissionProvider.EnableWishlist) || !permissionService.Authorize(StandardPermissionProvider.DisplayPrices); //compare products priceModel.DisableAddToCompareListButton = !catalogSettings.CompareProductsEnabled; //rental priceModel.IsRental = product.IsRental; //pre-order if (product.AvailableForPreOrder) { priceModel.AvailableForPreOrder = !product.PreOrderAvailabilityStartDateTimeUtc.HasValue || product.PreOrderAvailabilityStartDateTimeUtc.Value >= DateTime.UtcNow; priceModel.PreOrderAvailabilityStartDateTimeUtc = product.PreOrderAvailabilityStartDateTimeUtc; } //prices if (permissionService.Authorize(StandardPermissionProvider.DisplayPrices)) { if (!product.CustomerEntersPrice) { if (product.CallForPrice) { //call for price priceModel.OldPrice = null; priceModel.Price = localizationService.GetResource("Products.CallForPrice"); } else { //prices //calculate for the maximum quantity (in case if we have tier prices) decimal minPossiblePrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, decimal.Zero, true, int.MaxValue); decimal taxRate; decimal oldPriceBase = taxService.GetProductPrice(product, product.OldPrice, out taxRate); decimal finalPriceBase = taxService.GetProductPrice(product, minPossiblePrice, out taxRate); decimal oldPrice = currencyService.ConvertFromPrimaryStoreCurrency(oldPriceBase, workContext.WorkingCurrency); decimal finalPrice = currencyService.ConvertFromPrimaryStoreCurrency(finalPriceBase, workContext.WorkingCurrency); //do we have tier prices configured? var tierPrices = new List<TierPrice>(); if (product.HasTierPrices) { tierPrices.AddRange(product.TierPrices .OrderBy(tp => tp.Quantity) .ToList() .FilterByStore(storeContext.CurrentStore.Id) .FilterForCustomer(workContext.CurrentCustomer) .RemoveDuplicatedQuantities()); } //When there is just one tier (with qty 1), //there are no actual savings in the list. bool displayFromMessage = tierPrices.Count > 0 && !(tierPrices.Count == 1 && tierPrices[0].Quantity <= 1); if (displayFromMessage) { priceModel.OldPrice = null; priceModel.Price = String.Format(localizationService.GetResource("Products.PriceRangeFrom"), priceFormatter.FormatPrice(finalPrice)); } else { if (finalPriceBase != oldPriceBase && oldPriceBase != decimal.Zero) { priceModel.OldPrice = priceFormatter.FormatPrice(oldPrice); priceModel.Price = priceFormatter.FormatPrice(finalPrice); } else { priceModel.OldPrice = null; priceModel.Price = priceFormatter.FormatPrice(finalPrice); } } if (product.IsRental) { //rental product priceModel.OldPrice = priceFormatter.FormatRentalProductPeriod(product, priceModel.OldPrice); priceModel.Price = priceFormatter.FormatRentalProductPeriod(product, priceModel.Price); } //property for German market //we display tax/shipping info only with "shipping enabled" for this product //we also ensure this it's not free shipping priceModel.DisplayTaxShippingInfo = catalogSettings.DisplayTaxShippingInfoProductBoxes && product.IsShipEnabled && !product.IsFreeShipping; } } } else { //hide prices priceModel.OldPrice = null; priceModel.Price = null; } #endregion } break; } model.ProductPrice = priceModel; #endregion } //picture if (preparePictureModel) { #region Prepare product picture //If a size has been set in the view, we use it in priority int pictureSize = productThumbPictureSize.HasValue ? productThumbPictureSize.Value : mediaSettings.ProductThumbPictureSize; //prepare picture model var defaultProductPictureCacheKey = string.Format(ModelCacheEventConsumer.PRODUCT_DEFAULTPICTURE_MODEL_KEY, product.Id, pictureSize, true, workContext.WorkingLanguage.Id, webHelper.IsCurrentConnectionSecured(), storeContext.CurrentStore.Id); model.DefaultPictureModel = cacheManager.Get(defaultProductPictureCacheKey, () => { var picture = pictureService.GetPicturesByProductId(product.Id, 1).FirstOrDefault(); var pictureModel = new PictureModel { ImageUrl = pictureService.GetPictureUrl(picture, pictureSize), FullSizeImageUrl = pictureService.GetPictureUrl(picture) }; //"title" attribute pictureModel.Title = (picture != null && !string.IsNullOrEmpty(picture.TitleAttribute)) ? picture.TitleAttribute : string.Format(localizationService.GetResource("Media.Product.ImageLinkTitleFormat"), model.Name); //"alt" attribute pictureModel.AlternateText = (picture != null && !string.IsNullOrEmpty(picture.AltAttribute)) ? picture.AltAttribute : string.Format(localizationService.GetResource("Media.Product.ImageAlternateTextFormat"), model.Name); return pictureModel; }); #endregion } //specs if (prepareSpecificationAttributes) { model.SpecificationAttributeModels = PrepareProductSpecificationModel(controller, workContext, specificationAttributeService, cacheManager, product); } //reviews model.ReviewOverviewModel = new ProductReviewOverviewModel { ProductId = product.Id, RatingSum = product.ApprovedRatingSum, TotalReviews = product.ApprovedTotalReviews, AllowCustomerReviews = product.AllowCustomerReviews }; models.Add(model); } return models; }
/// <summary> /// Gets the base price info /// </summary> /// <param name="product">Product</param> /// <param name="localizationService">Localization service</param> /// <param name="priceFormatter">Price formatter</param> /// <param name="currencyService">Currency service</param> /// <param name="taxService">Tax service</param> /// <param name="priceCalculationService">Price calculation service</param> /// <param name="currency">Target currency</param> /// <param name="priceAdjustment">Price adjustment</param> /// <param name="languageInsensitive">Whether the result string should be language insensitive</param> /// <returns>The base price info</returns> public static string GetBasePriceInfo(this Product product, ILocalizationService localizationService, IPriceFormatter priceFormatter, ICurrencyService currencyService, ITaxService taxService, IPriceCalculationService priceCalculationService, Currency currency, decimal priceAdjustment = decimal.Zero, bool languageInsensitive = false) { Guard.ArgumentNotNull(() => product); Guard.ArgumentNotNull(() => currencyService); Guard.ArgumentNotNull(() => taxService); Guard.ArgumentNotNull(() => priceCalculationService); Guard.ArgumentNotNull(() => currency); if (product.BasePriceHasValue && product.BasePriceAmount != Decimal.Zero) { var workContext = EngineContext.Current.Resolve<IWorkContext>(); var taxrate = decimal.Zero; var currentPrice = priceCalculationService.GetFinalPrice(product, workContext.CurrentCustomer, true); var price = taxService.GetProductPrice(product, decimal.Add(currentPrice, priceAdjustment), out taxrate); price = currencyService.ConvertFromPrimaryStoreCurrency(price, currency); return product.GetBasePriceInfo(price, localizationService, priceFormatter, currency, languageInsensitive); } return ""; }