diff --git a/changelog.md b/changelog.md index de0776ee79..aef324eb26 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,7 @@ * #1669 Apply percentage discounts also on tier prices. * #1618 Implement ACL and multistore capability on menu item level. * #1683 Menu Builder items: implement support for icon (brand) color. +* #1584 Show bundle item images in order details like in shopping cart details. ### Improvements * #1663 Make MeasureDimension and MeasureWeight localizable. diff --git a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs index 408604aae7..aadc797de3 100644 --- a/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs +++ b/src/Libraries/SmartStore.Data/Migrations/MigrationsConfiguration.cs @@ -65,6 +65,12 @@ public void MigrateLocaleResources(LocaleResourcesBuilder builder) "Specifies whether to apply percentage discounts also on tier prices.", "Legt fest, ob prozentuale Rabatte auch auf Staffelpreise angewendet werden sollen."); + builder.AddOrUpdate("Admin.Configuration.Settings.ShoppingCart.ShowProductBundleImagesOnShoppingCart", + "Show product images of bundle items", + "Produktbilder von Bundle-Bestandteilen anzeigen", + "Specifies whether to show product images of bundle items.", + "Legt fest, ob Produktbilder von Bundle-Bestandteilen angezeigt werden sollen."); + builder.Delete( "Admin.Configuration.Measures.Weights.Fields.MarkAsPrimaryWeight", "Admin.Configuration.Measures.Dimensions.Fields.MarkAsPrimaryDimension"); diff --git a/src/Presentation/SmartStore.Web/Controllers/OrderHelper.cs b/src/Presentation/SmartStore.Web/Controllers/OrderHelper.cs index 8b5cabf591..eae6f6376d 100644 --- a/src/Presentation/SmartStore.Web/Controllers/OrderHelper.cs +++ b/src/Presentation/SmartStore.Web/Controllers/OrderHelper.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; @@ -39,6 +40,7 @@ public partial class OrderHelper private readonly ICurrencyService _currencyService; private readonly IQuantityUnitService _quantityUnitService; private readonly IPictureService _pictureService; + private readonly IProductService _productService; public OrderHelper( ICommonServices services, @@ -52,7 +54,8 @@ public OrderHelper( IPaymentService paymentService, ICurrencyService currencyService, IQuantityUnitService quantityUnitService, - IPictureService pictureService) + IPictureService pictureService, + IProductService productService) { _services = services; _dateTimeHelper = dateTimeHelper; @@ -66,6 +69,7 @@ public OrderHelper( _currencyService = currencyService; _quantityUnitService = quantityUnitService; _pictureService = pictureService; + _productService = productService; T = NullLocalizer.Instance; } @@ -144,41 +148,57 @@ private OrderDetailsModel.OrderItemModel PrepareOrderItemModel( }; var quantityUnit = _quantityUnitService.GetQuantityUnitById(orderItem.Product.QuantityUnitId); - model.QuantityUnit = (quantityUnit == null ? "" : quantityUnit.GetLocalized(x => x.Name)); + model.QuantityUnit = quantityUnit == null ? "" : quantityUnit.GetLocalized(x => x.Name); if (orderItem.Product.ProductType == ProductType.BundledProduct && orderItem.BundleData.HasValue()) { var bundleData = orderItem.GetBundleData(); + var bundleItems = shoppingCartSettings.ShowProductBundleImagesOnShoppingCart + ? _productService.GetBundleItems(orderItem.ProductId).ToDictionarySafe(x => x.Item.ProductId) + : new Dictionary(); model.BundlePerItemPricing = orderItem.Product.BundlePerItemPricing; model.BundlePerItemShoppingCart = bundleData.Any(x => x.PerItemShoppingCart); - foreach (var bundleItem in bundleData) + foreach (var bid in bundleData) { var bundleItemModel = new OrderDetailsModel.BundleItemModel { - Sku = bundleItem.Sku, - ProductName = bundleItem.ProductName, - ProductSeName = bundleItem.ProductSeName, - VisibleIndividually = bundleItem.VisibleIndividually, - Quantity = bundleItem.Quantity, - DisplayOrder = bundleItem.DisplayOrder, - AttributeInfo = bundleItem.AttributesInfo + Sku = bid.Sku, + ProductName = bid.ProductName, + ProductSeName = bid.ProductSeName, + VisibleIndividually = bid.VisibleIndividually, + Quantity = bid.Quantity, + DisplayOrder = bid.DisplayOrder, + AttributeInfo = bid.AttributesInfo }; - bundleItemModel.ProductUrl = _productUrlHelper.GetProductUrl(bundleItem.ProductId, bundleItemModel.ProductSeName, bundleItem.AttributesXml); + bundleItemModel.ProductUrl = _productUrlHelper.GetProductUrl(bid.ProductId, bundleItemModel.ProductSeName, bid.AttributesXml); if (model.BundlePerItemShoppingCart) { - decimal priceWithDiscount = _currencyService.ConvertCurrency(bundleItem.PriceWithDiscount, order.CurrencyRate); + var priceWithDiscount = _currencyService.ConvertCurrency(bid.PriceWithDiscount, order.CurrencyRate); bundleItemModel.PriceWithDiscount = _priceFormatter.FormatPrice(priceWithDiscount, true, order.CustomerCurrencyCode, language, false, false); } + // Bundle item picture. + if (shoppingCartSettings.ShowProductBundleImagesOnShoppingCart && bundleItems.TryGetValue(bid.ProductId, out var bundleItem)) + { + bundleItemModel.HideThumbnail = bundleItem.Item.HideThumbnail; + + bundleItemModel.Picture = PrepareOrderItemPictureModel( + bundleItem.Item.Product, + mediaSettings.CartThumbBundleItemPictureSize, + bid.ProductName, + bid.AttributesXml, + catalogSettings); + } + model.BundleItems.Add(bundleItemModel); } } - // Unit price, subtotal + // Unit price, subtotal. switch (order.CustomerTaxDisplayType) { case TaxDisplayType.ExcludingTax: @@ -219,8 +239,7 @@ private OrderDetailsModel.OrderItemModel PrepareOrderItemModel( public OrderDetailsModel PrepareOrderDetailsModel(Order order) { - if (order == null) - throw new ArgumentNullException("order"); + Guard.NotNull(order, nameof(order)); var store = _services.StoreService.GetStoreById(order.StoreId) ?? _services.StoreContext.CurrentStore; var language = _services.WorkContext.WorkingLanguage; @@ -249,7 +268,7 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) model.DisplayPdfInvoice = pdfSettings.Enabled; model.RenderOrderNotes = pdfSettings.RenderOrderNotes; - // Shipping info + // Shipping info. model.ShippingStatus = order.ShippingStatus.GetLocalizedEnum(_services.Localization, _services.WorkContext); if (order.ShippingStatus != ShippingStatus.ShippingNotRequired) { @@ -258,7 +277,7 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) model.ShippingMethod = order.ShippingMethod; - // Shipments (only already shipped) + // Shipments (only already shipped). var shipments = order.Shipments.Where(x => x.ShippedDateUtc.HasValue).OrderBy(x => x.CreatedOnUtc).ToList(); foreach (var shipment in shipments) { @@ -269,53 +288,54 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) }; if (shipment.ShippedDateUtc.HasValue) + { shipmentModel.ShippedDate = _dateTimeHelper.ConvertToUserTime(shipment.ShippedDateUtc.Value, DateTimeKind.Utc); + } if (shipment.DeliveryDateUtc.HasValue) + { shipmentModel.DeliveryDate = _dateTimeHelper.ConvertToUserTime(shipment.DeliveryDateUtc.Value, DateTimeKind.Utc); + } model.Shipments.Add(shipmentModel); } } - // Billing info model.BillingAddress.PrepareModel(order.BillingAddress, false, addressSettings); - - // VAT number model.VatNumber = order.VatNumber; - //payment method + // Payment method. var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName); model.PaymentMethod = paymentMethod != null ? _pluginMediator.GetLocalizedFriendlyName(paymentMethod.Metadata) : order.PaymentMethodSystemName; model.CanRePostProcessPayment = _paymentService.CanRePostProcessPayment(order); - // Purchase order number (we have to find a better to inject this information because it's related to a certain plugin) + // Purchase order number (we have to find a better to inject this information because it's related to a certain plugin). if (paymentMethod != null && paymentMethod.Metadata.SystemName.Equals("SmartStore.PurchaseOrderNumber", StringComparison.InvariantCultureIgnoreCase)) { model.DisplayPurchaseOrderNumber = true; model.PurchaseOrderNumber = order.PurchaseOrderNumber; } - // Totals + // Totals. switch (order.CustomerTaxDisplayType) { case TaxDisplayType.ExcludingTax: { - // Order subtotal + // Order subtotal. var orderSubtotalExclTax = _currencyService.ConvertCurrency(order.OrderSubtotalExclTax, order.CurrencyRate); model.OrderSubtotal = _priceFormatter.FormatPrice(orderSubtotalExclTax, true, order.CustomerCurrencyCode, language, false, false); - // Discount (applied to order subtotal) + // Discount (applied to order subtotal). var orderSubTotalDiscountExclTax = _currencyService.ConvertCurrency(order.OrderSubTotalDiscountExclTax, order.CurrencyRate); if (orderSubTotalDiscountExclTax > decimal.Zero) { model.OrderSubTotalDiscount = _priceFormatter.FormatPrice(-orderSubTotalDiscountExclTax, true, order.CustomerCurrencyCode, language, false, false); } - // Order shipping + // Order shipping. var orderShippingExclTax = _currencyService.ConvertCurrency(order.OrderShippingExclTax, order.CurrencyRate); model.OrderShipping = _priceFormatter.FormatShippingPrice(orderShippingExclTax, true, order.CustomerCurrencyCode, language, false, false); - // Payment method additional fee + // Payment method additional fee. var paymentMethodAdditionalFeeExclTax = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeExclTax, order.CurrencyRate); if (paymentMethodAdditionalFeeExclTax != decimal.Zero) { @@ -327,22 +347,22 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) case TaxDisplayType.IncludingTax: { - // Order subtotal + // Order subtotal. var orderSubtotalInclTax = _currencyService.ConvertCurrency(order.OrderSubtotalInclTax, order.CurrencyRate); model.OrderSubtotal = _priceFormatter.FormatPrice(orderSubtotalInclTax, true, order.CustomerCurrencyCode, language, true, false); - // Discount (applied to order subtotal) + // Discount (applied to order subtotal). var orderSubTotalDiscountInclTax = _currencyService.ConvertCurrency(order.OrderSubTotalDiscountInclTax, order.CurrencyRate); if (orderSubTotalDiscountInclTax > decimal.Zero) { model.OrderSubTotalDiscount = _priceFormatter.FormatPrice(-orderSubTotalDiscountInclTax, true, order.CustomerCurrencyCode, language, true, false); } - // Order shipping + // Order shipping. var orderShippingInclTax = _currencyService.ConvertCurrency(order.OrderShippingInclTax, order.CurrencyRate); model.OrderShipping = _priceFormatter.FormatShippingPrice(orderShippingInclTax, true, order.CustomerCurrencyCode, language, true, false); - // Payment method additional fee + // Payment method additional fee. var paymentMethodAdditionalFeeInclTax = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeInclTax, order.CurrencyRate); if (paymentMethodAdditionalFeeInclTax != decimal.Zero) { @@ -353,7 +373,7 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) break; } - // Tax + // Tax. var displayTax = true; var displayTaxRates = true; @@ -397,14 +417,14 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) model.DisplayTax = displayTax; - // Discount (applied to order total) + // Discount (applied to order total). var orderDiscountInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderDiscount, order.CurrencyRate); if (orderDiscountInCustomerCurrency > decimal.Zero) { model.OrderTotalDiscount = _priceFormatter.FormatPrice(-orderDiscountInCustomerCurrency, true, order.CustomerCurrencyCode, false, language); } - // Gift cards + // Gift cards. foreach (var gcuh in order.GiftCardUsageHistory) { var remainingAmountBase = gcuh.GiftCard.GetGiftCardRemainingAmount(); @@ -420,7 +440,7 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) model.GiftCards.Add(gcModel); } - // Reward points + // Reward points . if (order.RedeemedRewardPointsEntry != null) { model.RedeemedRewardPoints = -order.RedeemedRewardPointsEntry.Points; @@ -435,7 +455,7 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) model.CreditBalance = _priceFormatter.FormatPrice(-convertedCreditBalance, true, order.CustomerCurrencyCode, false, language); } - // Total + // Total. var roundingAmount = decimal.Zero; var orderTotal = order.GetOrderTotalInCustomerCurrency(_currencyService, _paymentService, out roundingAmount); @@ -446,10 +466,10 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) model.OrderTotalRounding = _priceFormatter.FormatPrice(roundingAmount, true, order.CustomerCurrencyCode, false, language); } - // Checkout attributes + // Checkout attributes. model.CheckoutAttributeInfo = HtmlUtils.ConvertPlainTextToTable(HtmlUtils.ConvertHtmlToPlainText(order.CheckoutAttributeDescription)); - // Order notes + // Order notes. foreach (var orderNote in order.OrderNotes .Where(on => on.DisplayToCustomer) .OrderByDescending(on => on.CreatedOnUtc) @@ -465,10 +485,12 @@ public OrderDetailsModel PrepareOrderDetailsModel(Order order) }); } - - // purchased products + // Purchased products. model.ShowSku = catalogSettings.ShowProductSku; model.ShowProductImages = shoppingCartSettings.ShowProductImagesOnShoppingCart; + model.ShowProductBundleImages = shoppingCartSettings.ShowProductBundleImagesOnShoppingCart; + model.BundleThumbSize = mediaSettings.CartThumbBundleItemPictureSize; + var orderItems = _orderService.GetAllOrderItems(order.Id, null, null, null, null, null, null); foreach (var orderItem in orderItems) diff --git a/src/Presentation/SmartStore.Web/Models/Order/OrderDetailsModel.cs b/src/Presentation/SmartStore.Web/Models/Order/OrderDetailsModel.cs index 194acc3f2c..cbb7f04070 100644 --- a/src/Presentation/SmartStore.Web/Models/Order/OrderDetailsModel.cs +++ b/src/Presentation/SmartStore.Web/Models/Order/OrderDetailsModel.cs @@ -2,14 +2,14 @@ using System.Collections.Generic; using SmartStore.Core.Domain.Catalog; using SmartStore.Core.Domain.Common; +using SmartStore.Services.Localization; using SmartStore.Web.Framework.Modelling; using SmartStore.Web.Models.Common; using SmartStore.Web.Models.Media; -using SmartStore.Services.Localization; namespace SmartStore.Web.Models.Order { - public partial class OrderDetailsModel : EntityModelBase + public partial class OrderDetailsModel : EntityModelBase { public OrderDetailsModel() { @@ -78,8 +78,10 @@ public OrderDetailsModel() public bool ShowSku { get; set; } public bool ShowProductImages { get; set; } - public IList Items { get; set; } + public bool ShowProductBundleImages { get; set; } + public int BundleThumbSize { get; set; } + public IList Items { get; set; } public IList OrderNotes { get; set; } #region Nested Classes @@ -106,12 +108,13 @@ public OrderItemModel() public bool BundlePerItemShoppingCart { get; set; } public PictureModel Picture { get; set; } - public IList BundleItems { get; set; } + public IList BundleItems { get; set; } } public partial class BundleItemModel : ModelBase { - public string Sku { get; set; } + public PictureModel Picture { get; set; } + public string Sku { get; set; } public string ProductName { get; set; } public string ProductSeName { get; set; } public string ProductUrl { get; set; } @@ -120,7 +123,8 @@ public partial class BundleItemModel : ModelBase public int DisplayOrder { get; set; } public string PriceWithDiscount { get; set; } public string AttributeInfo { get; set; } - } + public bool HideThumbnail { get; set; } + } public partial class TaxRate : ModelBase { @@ -149,6 +153,7 @@ public partial class ShipmentBriefModel : EntityModelBase public DateTime? ShippedDate { get; set; } public DateTime? DeliveryDate { get; set; } } + #endregion } } \ No newline at end of file diff --git a/src/Presentation/SmartStore.Web/Models/ShoppingCart/ShoppingCartModel.cs b/src/Presentation/SmartStore.Web/Models/ShoppingCart/ShoppingCartModel.cs index 14e2350643..7a43f5d842 100644 --- a/src/Presentation/SmartStore.Web/Models/ShoppingCart/ShoppingCartModel.cs +++ b/src/Presentation/SmartStore.Web/Models/ShoppingCart/ShoppingCartModel.cs @@ -77,7 +77,7 @@ public ShoppingCartItemModel() } public string Sku { get; set; } - public PictureModel Picture {get;set;} + public PictureModel Picture { get; set; } public int ProductId { get; set; } diff --git a/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistModel.cs b/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistModel.cs index be39d6a519..a1b06988a5 100644 --- a/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistModel.cs +++ b/src/Presentation/SmartStore.Web/Models/ShoppingCart/WishlistModel.cs @@ -55,7 +55,7 @@ public ShoppingCartItemModel() public string Sku { get; set; } - public PictureModel Picture {get;set;} + public PictureModel Picture { get; set; } public int ProductId { get; set; } diff --git a/src/Presentation/SmartStore.Web/Views/Order/Details.cshtml b/src/Presentation/SmartStore.Web/Views/Order/Details.cshtml index 9186769dc3..10932dd3d0 100644 --- a/src/Presentation/SmartStore.Web/Views/Order/Details.cshtml +++ b/src/Presentation/SmartStore.Web/Views/Order/Details.cshtml @@ -468,21 +468,34 @@
- @if (item.Quantity > 1 && parentItem.BundlePerItemShoppingCart) - { - @(item.Quantity) × - } - @if (item.VisibleIndividually) - { - @item.ProductName - } - else - { - @item.ProductName - } +
+ @if (Model.ShowProductBundleImages) + { +
+ @if (item.Picture.ImageUrl.HasValue() && !item.HideThumbnail) + { + @item.Picture.AlternateText + } +
+ } +
+ @if (item.Quantity > 1 && parentItem.BundlePerItemShoppingCart) + { + @(item.Quantity) × + } + @if (item.VisibleIndividually) + { + @item.ProductName + } + else + { + @item.ProductName + } +
+
@if (item.AttributeInfo.HasValue()) { -
+
@Html.Raw(item.AttributeInfo)
}