()); }
+ protected set { _associatedRewardPoints = value; }
+ }
+ }
+}
diff --git a/src/Libraries/Nop.Core/Domain/Orders/OrderSettings.cs b/src/Libraries/Nop.Core/Domain/Orders/OrderSettings.cs
index adc1cf5fb00..e2a3692631e 100644
--- a/src/Libraries/Nop.Core/Domain/Orders/OrderSettings.cs
+++ b/src/Libraries/Nop.Core/Domain/Orders/OrderSettings.cs
@@ -1,134 +1,146 @@
-using Nop.Core.Configuration;
-
-namespace Nop.Core.Domain.Orders
-{
- public class OrderSettings : ISettings
- {
- ///
- /// Gets or sets a value indicating whether customer can make re-order
- ///
- public bool IsReOrderAllowed { get; set; }
-
- ///
- /// Gets or sets a minimum order subtotal amount
- ///
- public decimal MinOrderSubtotalAmount { get; set; }
- ///
- /// Gets or sets a value indicating whether 'Minimum order subtotal amount' option
- /// should be evaluated over 'X' value including tax or not
- ///
- public bool MinOrderSubtotalAmountIncludingTax { get; set; }
- ///
- /// Gets or sets a minimum order total amount
- ///
- public decimal MinOrderTotalAmount { get; set; }
-
- ///
- /// Gets or sets a value indicating whether automatically update order totals on editing an order in admin area
- ///
- public bool AutoUpdateOrderTotalsOnEditingOrder { get; set; }
-
- ///
- /// Gets or sets a value indicating whether anonymous checkout allowed
- ///
- public bool AnonymousCheckoutAllowed { get; set; }
-
- ///
- /// Gets or sets a value indicating whether 'Terms of service' enabled on the shopping cart page
- ///
- public bool TermsOfServiceOnShoppingCartPage { get; set; }
- ///
- /// Gets or sets a value indicating whether 'Terms of service' enabled on the order confirmation page
- ///
- public bool TermsOfServiceOnOrderConfirmPage { get; set; }
-
- ///
- /// Gets or sets a value indicating whether 'One-page checkout' is enabled
- ///
- public bool OnePageCheckoutEnabled { get; set; }
-
- ///
- /// Gets or sets a value indicating whether order totals should be displayed on 'Payment info' tab of 'One-page checkout' page
- ///
- public bool OnePageCheckoutDisplayOrderTotalsOnPaymentInfoTab { get; set; }
- ///
- /// Gets or sets a value indicating whether "Billing address" step should be skipped
- ///
- public bool DisableBillingAddressCheckoutStep { get; set; }
- ///
- /// Gets or sets a value indicating whether "Order completed" page should be skipped
- ///
- public bool DisableOrderCompletedPage { get; set; }
-
- ///
- /// Gets or sets a value indicating we should attach PDF invoice to "Order placed" email
- ///
- public bool AttachPdfInvoiceToOrderPlacedEmail { get; set; }
- ///
- /// Gets or sets a value indicating we should attach PDF invoice to "Order paid" email
- ///
- public bool AttachPdfInvoiceToOrderPaidEmail { get; set; }
- ///
- /// Gets or sets a value indicating we should attach PDF invoice to "Order completed" email
- ///
- public bool AttachPdfInvoiceToOrderCompletedEmail { get; set; }
- ///
- /// Gets or sets a value indicating we PDF invoices should be generated in customer language. Otherwise, use the current one
- ///
- public bool GeneratePdfInvoiceInCustomerLanguage { get; set; }
-
- ///
- /// Gets or sets a value indicating whether "Return requests" are allowed
- ///
- public bool ReturnRequestsEnabled { get; set; }
- ///
- /// Gets or sets a value indicating whether customers are allowed to upload files
- ///
- public bool ReturnRequestsAllowFiles { get; set; }
- ///
- /// Gets or sets maximum file size for upload file (return request). Set 0 to allow any file size
- ///
- public int ReturnRequestsFileMaximumSize { get; set; }
- ///
- /// Gets or sets a value "Return requests" number mask
- ///
- public string ReturnRequestNumberMask { get; set; }
- ///
- /// Gets or sets a number of days that the Return Request Link will be available for customers after order placing.
- ///
- public int NumberOfDaysReturnRequestAvailable { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to activate related gift cards after completing the order
- ///
- public bool ActivateGiftCardsAfterCompletingOrder { get; set; }
- ///
- /// Gets or sets a value indicating whether to deactivate related gift cards after cancelling the order
- ///
- public bool DeactivateGiftCardsAfterCancellingOrder { get; set; }
- ///
- /// Gets or sets a value indicating whether to deactivate related gift cards after deleting the order
- ///
- public bool DeactivateGiftCardsAfterDeletingOrder { get; set; }
-
- ///
- /// Gets or sets an order placement interval in seconds (prevent 2 orders being placed within an X seconds time frame).
- ///
- public int MinimumOrderPlacementInterval { get; set; }
-
- ///
- /// Gets or sets a value indicating whether an order status should be set to "Complete" only when its shipping status is "Delivered". Otherwise, "Shipped" status will be enough.
- ///
- public bool CompleteOrderWhenDelivered { get; set; }
-
- ///
- /// Gets or sets a custom order number mask
- ///
- public string CustomOrderNumberMask { get; set; }
-
- ///
- /// Gets or sets a value indicating whether the orders need to be exported with their products
- ///
- public bool ExportWithProducts { get; set; }
- }
+using Nop.Core.Configuration;
+
+namespace Nop.Core.Domain.Orders
+{
+ public class OrderSettings : ISettings
+ {
+ ///
+ /// Gets or sets a value indicating whether customer can make re-order
+ ///
+ public bool IsReOrderAllowed { get; set; }
+
+ ///
+ /// Gets or sets a minimum order subtotal amount
+ ///
+ public decimal MinOrderSubtotalAmount { get; set; }
+ ///
+ /// Gets or sets a value indicating whether 'Minimum order subtotal amount' option
+ /// should be evaluated over 'X' value including tax or not
+ ///
+ public bool MinOrderSubtotalAmountIncludingTax { get; set; }
+ ///
+ /// Gets or sets a minimum order total amount
+ ///
+ public decimal MinOrderTotalAmount { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether automatically update order totals on editing an order in admin area
+ ///
+ public bool AutoUpdateOrderTotalsOnEditingOrder { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether anonymous checkout allowed
+ ///
+ public bool AnonymousCheckoutAllowed { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether 'Terms of service' enabled on the shopping cart page
+ ///
+ public bool TermsOfServiceOnShoppingCartPage { get; set; }
+ ///
+ /// Gets or sets a value indicating whether 'Terms of service' enabled on the order confirmation page
+ ///
+ public bool TermsOfServiceOnOrderConfirmPage { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether 'One-page checkout' is enabled
+ ///
+ public bool OnePageCheckoutEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether order totals should be displayed on 'Payment info' tab of 'One-page checkout' page
+ ///
+ public bool OnePageCheckoutDisplayOrderTotalsOnPaymentInfoTab { get; set; }
+ ///
+ /// Gets or sets a value indicating whether "Billing address" step should be skipped
+ ///
+ public bool DisableBillingAddressCheckoutStep { get; set; }
+ ///
+ /// Gets or sets a value indicating whether "Order completed" page should be skipped
+ ///
+ public bool DisableOrderCompletedPage { get; set; }
+
+ ///
+ /// Gets or sets a value indicating we should attach PDF invoice to "Order placed" email
+ ///
+ public bool AttachPdfInvoiceToOrderPlacedEmail { get; set; }
+ ///
+ /// Gets or sets a value indicating we should attach PDF invoice to "Order paid" email
+ ///
+ public bool AttachPdfInvoiceToOrderPaidEmail { get; set; }
+ ///
+ /// Gets or sets a value indicating we should attach PDF invoice to "Order completed" email
+ ///
+ public bool AttachPdfInvoiceToOrderCompletedEmail { get; set; }
+ ///
+ /// Gets or sets a value indicating we PDF invoices should be generated in customer language. Otherwise, use the current one
+ ///
+ public bool GeneratePdfInvoiceInCustomerLanguage { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether "Return requests" are allowed
+ ///
+ public bool ReturnRequestsEnabled { get; set; }
+ ///
+ /// Gets or sets a value indicating whether customers are allowed to upload files
+ ///
+ public bool ReturnRequestsAllowFiles { get; set; }
+ ///
+ /// Gets or sets maximum file size for upload file (return request). Set 0 to allow any file size
+ ///
+ public int ReturnRequestsFileMaximumSize { get; set; }
+ ///
+ /// Gets or sets a value "Return requests" number mask
+ ///
+ public string ReturnRequestNumberMask { get; set; }
+ ///
+ /// Gets or sets a number of days that the Return Request Link will be available for customers after order placing.
+ ///
+ public int NumberOfDaysReturnRequestAvailable { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to activate related gift cards after completing the order
+ ///
+ public bool ActivateGiftCardsAfterCompletingOrder { get; set; }
+ ///
+ /// Gets or sets a value indicating whether to deactivate related gift cards after cancelling the order
+ ///
+ public bool DeactivateGiftCardsAfterCancellingOrder { get; set; }
+ ///
+ /// Gets or sets a value indicating whether to deactivate related gift cards after deleting the order
+ ///
+ public bool DeactivateGiftCardsAfterDeletingOrder { get; set; }
+
+ ///
+ /// Gets or sets an order placement interval in seconds (prevent 2 orders being placed within an X seconds time frame).
+ ///
+ public int MinimumOrderPlacementInterval { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether an order status should be set to "Complete" only when its shipping status is "Delivered". Otherwise, "Shipped" status will be enough.
+ ///
+ public bool CompleteOrderWhenDelivered { get; set; }
+ ///
+ /// Last issued Invoice Id
+ ///
+ public int InvoiceIdent { get; set; }
+ ///
+ /// Last issued Invoice Year
+ ///
+ public int InvoiceYear { get; set; }
+
+ ///
+ /// Gets or sets a custom order number mask
+ ///
+ public string CustomOrderNumberMask { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the orders need to be exported with their products
+ ///
+ public bool ExportWithProducts { get; set; }
+ ///
+ /// Gets or sets a value indicating whether invoice ident should be set from background task
+ ///
+ public bool AssignInvoiceIdentFromTask { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartItem.cs b/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartItem.cs
index 94d18561078..5ffe1b9abb7 100644
--- a/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartItem.cs
+++ b/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartItem.cs
@@ -1,149 +1,162 @@
-using System;
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Customers;
-
-namespace Nop.Core.Domain.Orders
-{
- ///
- /// Represents a shopping cart item
- ///
- public partial class ShoppingCartItem : BaseEntity
- {
- ///
- /// Gets or sets the store identifier
- ///
- public int StoreId { get; set; }
-
- ///
- /// Gets or sets the shopping cart type identifier
- ///
- public int ShoppingCartTypeId { get; set; }
-
- ///
- /// Gets or sets the customer identifier
- ///
- public int CustomerId { get; set; }
-
- ///
- /// Gets or sets the product identifier
- ///
- public int ProductId { get; set; }
-
- ///
- /// Gets or sets the product attributes in XML format
- ///
- public string AttributesXml { get; set; }
-
- ///
- /// Gets or sets the price enter by a customer
- ///
- public decimal CustomerEnteredPrice { get; set; }
-
- ///
- /// Gets or sets the quantity
- ///
- public int Quantity { get; set; }
-
- ///
- /// Gets or sets the rental product start date (null if it's not a rental product)
- ///
- public DateTime? RentalStartDateUtc { get; set; }
-
- ///
- /// Gets or sets the rental product end date (null if it's not a rental product)
- ///
- public DateTime? RentalEndDateUtc { get; set; }
-
- ///
- /// Gets or sets the date and time of instance creation
- ///
- public DateTime CreatedOnUtc { get; set; }
-
- ///
- /// Gets or sets the date and time of instance update
- ///
- public DateTime UpdatedOnUtc { get; set; }
-
- ///
- /// Gets the log type
- ///
- public ShoppingCartType ShoppingCartType
- {
- get
- {
- return (ShoppingCartType)this.ShoppingCartTypeId;
- }
- set
- {
- this.ShoppingCartTypeId = (int)value;
- }
- }
-
- ///
- /// Gets or sets the product
- ///
- public virtual Product Product { get; set; }
-
- ///
- /// Gets or sets the customer
- ///
- public virtual Customer Customer { get; set; }
-
- ///
- /// Gets a value indicating whether the shopping cart item is free shipping
- ///
- public bool IsFreeShipping
- {
- get
- {
- var product = this.Product;
- if (product != null)
- return product.IsFreeShipping;
- return true;
- }
- }
-
- ///
- /// Gets a value indicating whether the shopping cart item is ship enabled
- ///
- public bool IsShipEnabled
- {
- get
- {
- var product = this.Product;
- if (product != null)
- return product.IsShipEnabled;
- return false;
- }
- }
-
- ///
- /// Gets the additional shipping charge
- ///
- public decimal AdditionalShippingCharge
- {
- get
- {
- decimal additionalShippingCharge = decimal.Zero;
- var product = this.Product;
- if (product != null)
- additionalShippingCharge = product.AdditionalShippingCharge * Quantity;
- return additionalShippingCharge;
- }
- }
-
- ///
- /// Gets a value indicating whether the shopping cart item is tax exempt
- ///
- public bool IsTaxExempt
- {
- get
- {
- var product = this.Product;
- if (product != null)
- return product.IsTaxExempt;
- return false;
- }
- }
- }
-}
+using System;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Customers;
+
+namespace Nop.Core.Domain.Orders
+{
+ ///
+ /// Represents a shopping cart item
+ ///
+ public partial class ShoppingCartItem : BaseEntity
+ {
+ ///
+ /// Gets or sets the store identifier
+ ///
+ public int StoreId { get; set; }
+
+ ///
+ /// Gets or sets the shopping cart type identifier
+ ///
+ public int ShoppingCartTypeId { get; set; }
+
+ ///
+ /// Gets or sets the customer identifier
+ ///
+ public int CustomerId { get; set; }
+
+ ///
+ /// Gets or sets the product identifier
+ ///
+ public int ProductId { get; set; }
+
+ ///
+ /// Gets or sets the product attributes in XML format
+ ///
+ public string AttributesXml { get; set; }
+
+ ///
+ /// Gets or sets the price enter by a customer
+ ///
+ public decimal CustomerEnteredPrice { get; set; }
+
+ ///
+ /// Gets or sets the quantity
+ ///
+ public int Quantity { get; set; }
+ ///
+ /// Gets or sets the rental product start date (null if it's not a rental product)
+ ///
+ public DateTime? RentalStartDateUtc { get; set; }
+
+ ///
+ /// Gets or sets the rental product end date (null if it's not a rental product)
+ ///
+ public DateTime? RentalEndDateUtc { get; set; }
+
+ ///
+ /// Gets or sets the date and time of instance creation
+ ///
+ public DateTime CreatedOnUtc { get; set; }
+
+ ///
+ /// Gets or sets the date and time of instance update
+ ///
+ public DateTime UpdatedOnUtc { get; set; }
+
+ ///
+ /// Gets the log type
+ ///
+ public ShoppingCartType ShoppingCartType
+ {
+ get
+ {
+ return (ShoppingCartType)this.ShoppingCartTypeId;
+ }
+ set
+ {
+ this.ShoppingCartTypeId = (int)value;
+ }
+ }
+
+ ///
+ /// Gets or sets the product
+ ///
+ public virtual Product Product { get; set; }
+
+ ///
+ /// Gets or sets the customer
+ ///
+ public virtual Customer Customer { get; set; }
+
+ ///
+ /// Gets a value indicating whether the shopping cart item is free shipping
+ ///
+ public bool IsFreeShipping
+ {
+ get
+ {
+ var product = this.Product;
+ if (product != null)
+ return product.IsFreeShipping;
+ return true;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the shopping cart item is ship enabled
+ ///
+ public bool IsShipEnabled
+ {
+ get
+ {
+ var product = this.Product;
+ if (product != null)
+ return product.IsShipEnabled;
+ return false;
+ }
+ }
+
+ ///
+ /// Gets the additional shipping charge
+ ///
+ public decimal AdditionalShippingCharge
+ {
+ get
+ {
+ decimal additionalShippingCharge = decimal.Zero;
+ var product = this.Product;
+ if (product != null)
+ additionalShippingCharge = product.AdditionalShippingCharge * Quantity;
+ return additionalShippingCharge;
+ }
+ }
+
+ ///
+ /// Gets a value indicating whether the shopping cart item is tax exempt
+ ///
+ public bool IsTaxExempt
+ {
+ get
+ {
+ var product = this.Product;
+ if (product != null)
+ return product.IsTaxExempt;
+ return false;
+ }
+ }
+ //fields for restored cart
+ ///
+ /// TaxRate for restored cart. Only used by UpdateOrderTotal and can be null
+ ///
+ public decimal? TaxRate { get; set; }
+ ///
+ /// Subtotal of item with tax
+ ///
+ public decimal? SubTotalInclTax { get; set; }
+
+ ///
+ /// Subtotal of item without tax
+ ///
+ public decimal? SubTotalExclTax { get; set; }
+ }
+}
diff --git a/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartSettings.cs b/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartSettings.cs
index 65493a6169c..11655ef6266 100644
--- a/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartSettings.cs
+++ b/src/Libraries/Nop.Core/Domain/Orders/ShoppingCartSettings.cs
@@ -1,115 +1,119 @@
-
-using Nop.Core.Configuration;
-
-namespace Nop.Core.Domain.Orders
-{
- public class ShoppingCartSettings : ISettings
- {
- ///
- /// Gets or sets a value indicating whether a custoemr should be redirected to the shopping cart page after adding a product to the cart/wishlist
- ///
- public bool DisplayCartAfterAddingProduct { get; set; }
-
- ///
- /// Gets or sets a value indicating whether a custoemr should be redirected to the shopping cart page after adding a product to the cart/wishlist
- ///
- public bool DisplayWishlistAfterAddingProduct { get; set; }
-
- ///
- /// Gets or sets a value indicating maximum number of items in the shopping cart
- ///
- public int MaximumShoppingCartItems { get; set; }
-
- ///
- /// Gets or sets a value indicating maximum number of items in the wishlist
- ///
- public int MaximumWishlistItems { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to show product images in the mini-shopping cart block
- ///
- public bool AllowOutOfStockItemsToBeAddedToWishlist { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to move items from wishlist to cart when clicking "Add to cart" button. Otherwise, they are copied.
- ///
- public bool MoveItemsFromWishlistToCart { get; set; }
-
- ///
- /// Gets or sets a value indicating whether shopping carts (and wishlist) are shared between stores (in multi-store environment)
- ///
- public bool CartsSharedBetweenStores { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to show product image on shopping cart page
- ///
- public bool ShowProductImagesOnShoppingCart { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to show product image on wishlist page
- ///
- public bool ShowProductImagesOnWishList { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to show discount box on shopping cart page
- ///
- public bool ShowDiscountBox { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to show gift card box on shopping cart page
- ///
- public bool ShowGiftCardBox { get; set; }
-
- ///
- /// Gets or sets a number of "Cross-sells" on shopping cart page
- ///
- public int CrossSellsNumber { get; set; }
-
- ///
- /// Gets or sets a value indicating whether "email a wishlist" feature is enabled
- ///
- public bool EmailWishlistEnabled { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to enabled "email a wishlist" for anonymous users.
- ///
- public bool AllowAnonymousUsersToEmailWishlist { get; set; }
-
- /// Gets or sets a value indicating whether mini-shopping cart is enabled
- ///
- public bool MiniShoppingCartEnabled { get; set; }
-
- ///
- /// Gets or sets a value indicating whether to show product images in the mini-shopping cart block
- ///
- public bool ShowProductImagesInMiniShoppingCart { get; set; }
-
- /// Gets or sets a maximum number of products which can be displayed in the mini-shopping cart block
- ///
- public int MiniShoppingCartProductNumber { get; set; }
-
- //Round is already an issue.
- //When enabled it can cause one issue: http://www.nopcommerce.com/boards/t/7679/vattax-rounding-error-important-fix.aspx
- //When disable it causes another one: http://www.nopcommerce.com/boards/t/11419/nop-20-order-of-steps-in-checkout.aspx?p=3#46924
- ///
- /// Gets or sets a value indicating whether to round calculated prices and total during calculation
- ///
- public bool RoundPricesDuringCalculation { get; set; }
-
- ///
- /// Gets or sets a value indicating whether we should group shopping cart items for the same products
- /// For example, a customer could have two shopping cart items for the same products (different product attributes)
- ///
- public bool GroupTierPricesForDistinctShoppingCartItems { get; set; }
-
- ///
- /// Gets or sets a value indicating whether a customer will beable to edit products in the cart
- ///
- public bool AllowCartItemEditing { get; set; }
-
- ///
- /// Gets or sets a value indicating whether a customer will see quantity of attribute values associated to products (when qty > 1)
- ///
- public bool RenderAssociatedAttributeValueQuantity { get; set; }
- }
+
+using Nop.Core.Configuration;
+
+namespace Nop.Core.Domain.Orders
+{
+ public class ShoppingCartSettings : ISettings
+ {
+ ///
+ /// Gets or sets a value indicating whether a custoemr should be redirected to the shopping cart page after adding a product to the cart/wishlist
+ ///
+ public bool DisplayCartAfterAddingProduct { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether a custoemr should be redirected to the shopping cart page after adding a product to the cart/wishlist
+ ///
+ public bool DisplayWishlistAfterAddingProduct { get; set; }
+
+ ///
+ /// Gets or sets a value indicating maximum number of items in the shopping cart
+ ///
+ public int MaximumShoppingCartItems { get; set; }
+
+ ///
+ /// Gets or sets a value indicating maximum number of items in the wishlist
+ ///
+ public int MaximumWishlistItems { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to show product images in the mini-shopping cart block
+ ///
+ public bool AllowOutOfStockItemsToBeAddedToWishlist { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to move items from wishlist to cart when clicking "Add to cart" button. Otherwise, they are copied.
+ ///
+ public bool MoveItemsFromWishlistToCart { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether shopping carts (and wishlist) are shared between stores (in multi-store environment)
+ ///
+ public bool CartsSharedBetweenStores { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to show product image on shopping cart page
+ ///
+ public bool ShowProductImagesOnShoppingCart { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to show product image on wishlist page
+ ///
+ public bool ShowProductImagesOnWishList { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to show discount box on shopping cart page
+ ///
+ public bool ShowDiscountBox { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to show gift card box on shopping cart page
+ ///
+ public bool ShowGiftCardBox { get; set; }
+
+ ///
+ /// Gets or sets a number of "Cross-sells" on shopping cart page
+ ///
+ public int CrossSellsNumber { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether "email a wishlist" feature is enabled
+ ///
+ public bool EmailWishlistEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to enabled "email a wishlist" for anonymous users.
+ ///
+ public bool AllowAnonymousUsersToEmailWishlist { get; set; }
+
+ /// Gets or sets a value indicating whether mini-shopping cart is enabled
+ ///
+ public bool MiniShoppingCartEnabled { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether to show product images in the mini-shopping cart block
+ ///
+ public bool ShowProductImagesInMiniShoppingCart { get; set; }
+
+ /// Gets or sets a maximum number of products which can be displayed in the mini-shopping cart block
+ ///
+ public int MiniShoppingCartProductNumber { get; set; }
+
+ //Round is already an issue.
+ //When enabled it can cause one issue: http://www.nopcommerce.com/boards/t/7679/vattax-rounding-error-important-fix.aspx
+ //When disable it causes another one: http://www.nopcommerce.com/boards/t/11419/nop-20-order-of-steps-in-checkout.aspx?p=3#46924
+ ///
+ /// Gets or sets a value indicating whether to round calculated prices and total during calculation
+ ///
+ public bool RoundPricesDuringCalculation { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether we should group shopping cart items for the same products
+ /// For example, a customer could have two shopping cart items for the same products (different product attributes)
+ ///
+ public bool GroupTierPricesForDistinctShoppingCartItems { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether a customer will beable to edit products in the cart
+ ///
+ public bool AllowCartItemEditing { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether a customer will see quantity of attribute values associated to products (when qty > 1)
+ ///
+ public bool RenderAssociatedAttributeValueQuantity { get; set; }
+ ///
+ /// Gets or sets a value indicating whether a customer will see the price of attribute values associated to products
+ ///
+ public bool RenderProductAttributePrices { get; set; }
+ }
}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Core/Html/HtmlHelper.cs b/src/Libraries/Nop.Core/Html/HtmlHelper.cs
index af886767b2a..5defb54fe6c 100644
--- a/src/Libraries/Nop.Core/Html/HtmlHelper.cs
+++ b/src/Libraries/Nop.Core/Html/HtmlHelper.cs
@@ -1,227 +1,232 @@
-using System;
-using System.Text;
-using System.Text.RegularExpressions;
-using System.Web;
-
-namespace Nop.Core.Html
-{
- ///
- /// Represents a HTML helper
- ///
- public partial class HtmlHelper
- {
- #region Fields
- private readonly static Regex paragraphStartRegex = new Regex("", RegexOptions.IgnoreCase);
- private readonly static Regex paragraphEndRegex = new Regex("
", RegexOptions.IgnoreCase);
- //private static Regex ampRegex = new Regex("&(?!(?:#[0-9]{2,4};|[a-z0-9]+;))", RegexOptions.Compiled | RegexOptions.IgnoreCase);
-
- #endregion
-
- #region Utilities
-
- private static string EnsureOnlyAllowedHtml(string text)
- {
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- const string allowedTags = "br,hr,b,i,u,a,div,ol,ul,li,blockquote,img,span,p,em,strong,font,pre,h1,h2,h3,h4,h5,h6,address,cite";
-
- var m = Regex.Matches(text, "<.*?>", RegexOptions.IgnoreCase);
- for (int i = m.Count - 1; i >= 0; i--)
- {
- string tag = text.Substring(m[i].Index + 1, m[i].Length - 1).Trim().ToLower();
-
- if (!IsValidTag(tag, allowedTags))
- {
- text = text.Remove(m[i].Index, m[i].Length);
- }
- }
-
- return text;
- }
-
- private static bool IsValidTag(string tag, string tags)
- {
- string[] allowedTags = tags.Split(',');
- if (tag.IndexOf("javascript") >= 0) return false;
- if (tag.IndexOf("vbscript") >= 0) return false;
- if (tag.IndexOf("onclick") >= 0) return false;
-
- var endchars = new [] { ' ', '>', '/', '\t' };
-
- int pos = tag.IndexOfAny(endchars, 1);
- if (pos > 0) tag = tag.Substring(0, pos);
- if (tag[0] == '/') tag = tag.Substring(1);
-
- foreach (string aTag in allowedTags)
- {
- if (tag == aTag) return true;
- }
-
- return false;
- }
- #endregion
-
- #region Methods
- ///
- /// Formats the text
- ///
- /// Text
- /// A value indicating whether to strip tags
- /// A value indicating whether HTML is allowed
- /// A value indicating whether HTML is allowed
- /// A value indicating whether BBCode is allowed
- /// A value indicating whether to resolve links
- /// A value indicating whether to add "noFollow" tag
- /// Formatted text
- public static string FormatText(string text, bool stripTags,
- bool convertPlainTextToHtml, bool allowHtml,
- bool allowBBCode, bool resolveLinks, bool addNoFollowTag)
- {
-
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- try
- {
- if (stripTags)
- {
- text = StripTags(text);
- }
-
- text = allowHtml ? EnsureOnlyAllowedHtml(text) : HttpUtility.HtmlEncode(text);
-
- if (convertPlainTextToHtml)
- {
- text = ConvertPlainTextToHtml(text);
- }
-
- if (allowBBCode)
- {
- text = BBCodeHelper.FormatText(text, true, true, true, true, true, true, true);
- }
-
- if (resolveLinks)
- {
- text = ResolveLinksHelper.FormatText(text);
- }
-
- if (addNoFollowTag)
- {
- //add noFollow tag. not implemented
- }
- }
- catch (Exception exc)
- {
- text = string.Format("Text cannot be formatted. Error: {0}", exc.Message);
- }
- return text;
- }
-
- ///
- /// Strips tags
- ///
- /// Text
- /// Formatted text
- public static string StripTags(string text)
- {
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- text = Regex.Replace(text, @"(>)(\r|\n)*(<)", "><");
- text = Regex.Replace(text, "(<[^>]*>)([^<]*)", "$2");
- text = Regex.Replace(text, "(?[0-9]{2,4};|"|&| |<|>|€|©|®|‰|‡|†|‹|›|„|”|“|‚|’|‘|—|–||||| | | |˜|ˆ|Ÿ|š|Š)", "@");
-
- return text;
- }
-
- ///
- /// replace anchor text (remove a tag from the following url Name and output only the string "Name")
- ///
- /// Text
- /// Text
- public static string ReplaceAnchorTags(string text)
- {
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- text = Regex.Replace(text, @"]+>([^<]*(?:(?!", "$1", RegexOptions.IgnoreCase);
- return text;
- }
-
- ///
- /// Converts plain text to HTML
- ///
- /// Text
- /// Formatted text
- public static string ConvertPlainTextToHtml(string text)
- {
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- text = text.Replace("\r\n", "
");
- text = text.Replace("\r", "
");
- text = text.Replace("\n", "
");
- text = text.Replace("\t", " ");
- text = text.Replace(" ", " ");
-
- return text;
- }
-
- ///
- /// Converts HTML to plain text
- ///
- /// Text
- /// A value indicating whether to decode text
- /// A value indicating whether to replace anchor text (remove a tag from the following url Name and output only the string "Name")
- /// Formatted text
- public static string ConvertHtmlToPlainText(string text,
- bool decode = false, bool replaceAnchorTags = false)
- {
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- if (decode)
- text = HttpUtility.HtmlDecode(text);
-
- text = text.Replace("
", "\n");
- text = text.Replace("
", "\n");
- text = text.Replace("
", "\n");
- text = text.Replace(" ", "\t");
- text = text.Replace(" ", " ");
-
- if (replaceAnchorTags)
- text = ReplaceAnchorTags(text);
-
- return text;
- }
-
- ///
- /// Converts text to paragraph
- ///
- /// Text
- /// Formatted text
- public static string ConvertPlainTextToParagraph(string text)
- {
- if (String.IsNullOrEmpty(text))
- return string.Empty;
-
- text = paragraphStartRegex.Replace(text, string.Empty);
- text = paragraphEndRegex.Replace(text, "\n");
- text = text.Replace("\r\n", "\n").Replace("\r", "\n");
- text = text + "\n\n";
- text = text.Replace("\n\n", "\n");
- var strArray = text.Split(new [] { '\n' });
- var builder = new StringBuilder();
- foreach (string str in strArray)
- {
- if ((str != null) && (str.Trim().Length > 0))
- {
- builder.AppendFormat("{0}
\n", str);
- }
- }
- return builder.ToString();
- }
- #endregion
- }
-}
+using System;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.Web;
+
+namespace Nop.Core.Html
+{
+ ///
+ /// Represents a HTML helper
+ ///
+ public partial class HtmlHelper
+ {
+ #region Fields
+ private readonly static Regex paragraphStartRegex = new Regex("", RegexOptions.IgnoreCase);
+ private readonly static Regex paragraphEndRegex = new Regex("
", RegexOptions.IgnoreCase);
+ //private static Regex ampRegex = new Regex("&(?!(?:#[0-9]{2,4};|[a-z0-9]+;))", RegexOptions.Compiled | RegexOptions.IgnoreCase);
+
+ #endregion
+
+ #region Utilities
+
+ private static string EnsureOnlyAllowedHtml(string text)
+ {
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ const string allowedTags = "br,hr,b,i,u,a,div,ol,ul,li,blockquote,img,span,p,em,strong,font,pre,h1,h2,h3,h4,h5,h6,address,cite";
+
+ var m = Regex.Matches(text, "<.*?>", RegexOptions.IgnoreCase);
+ for (int i = m.Count - 1; i >= 0; i--)
+ {
+ string tag = text.Substring(m[i].Index + 1, m[i].Length - 1).Trim().ToLower();
+
+ if (!IsValidTag(tag, allowedTags))
+ {
+ text = text.Remove(m[i].Index, m[i].Length);
+ }
+ }
+
+ return text;
+ }
+
+ private static bool IsValidTag(string tag, string tags)
+ {
+ string[] allowedTags = tags.Split(',');
+ if (tag.IndexOf("javascript") >= 0) return false;
+ if (tag.IndexOf("vbscript") >= 0) return false;
+ if (tag.IndexOf("onclick") >= 0) return false;
+
+ var endchars = new [] { ' ', '>', '/', '\t' };
+
+ int pos = tag.IndexOfAny(endchars, 1);
+ if (pos > 0) tag = tag.Substring(0, pos);
+ if (tag[0] == '/') tag = tag.Substring(1);
+
+ foreach (string aTag in allowedTags)
+ {
+ if (tag == aTag) return true;
+ }
+
+ return false;
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Formats the text
+ ///
+ /// Text
+ /// A value indicating whether to strip tags
+ /// A value indicating whether HTML is allowed
+ /// A value indicating whether HTML is allowed
+ /// A value indicating whether BBCode is allowed
+ /// A value indicating whether to resolve links
+ /// A value indicating whether to add "noFollow" tag
+ /// Formatted text
+ public static string FormatText(string text, bool stripTags,
+ bool convertPlainTextToHtml, bool allowHtml,
+ bool allowBBCode, bool resolveLinks, bool addNoFollowTag)
+ {
+
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ try
+ {
+ if (stripTags)
+ {
+ text = StripTags(text);
+ }
+
+ text = allowHtml ? EnsureOnlyAllowedHtml(text) : HttpUtility.HtmlEncode(text);
+
+ if (convertPlainTextToHtml)
+ {
+ text = ConvertPlainTextToHtml(text);
+ }
+
+ if (allowBBCode)
+ {
+ text = BBCodeHelper.FormatText(text, true, true, true, true, true, true, true);
+ }
+
+ if (resolveLinks)
+ {
+ text = ResolveLinksHelper.FormatText(text);
+ }
+
+ if (addNoFollowTag)
+ {
+ //add noFollow tag. not implemented
+ }
+ }
+ catch (Exception exc)
+ {
+ text = string.Format("Text cannot be formatted. Error: {0}", exc.Message);
+ }
+ return text;
+ }
+
+ ///
+ /// Strips tags
+ ///
+ /// Text
+ /// Formatted text
+ public static string StripTags(string text)
+ {
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ text = Regex.Replace(text, @"(>)(\r|\n)*(<)", "><");
+ text = Regex.Replace(text, "(<[^>]*>)([^<]*)", "$2");
+ text = Regex.Replace(text, "(?[0-9]{2,4};|"|&| |<|>|€|©|®|‰|‡|†|‹|›|„|”|“|‚|’|‘|—|–||||| | | |˜|ˆ|Ÿ|š|Š)", "@");
+
+ return text;
+ }
+
+ ///
+ /// replace anchor text (remove a tag from the following url Name and output only the string "Name")
+ ///
+ /// Text
+ /// Text
+ public static string ReplaceAnchorTags(string text)
+ {
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ text = Regex.Replace(text, @"]+>([^<]*(?:(?!", "$1", RegexOptions.IgnoreCase);
+ return text;
+ }
+
+ ///
+ /// Converts plain text to HTML
+ ///
+ /// Text
+ /// Formatted text
+ public static string ConvertPlainTextToHtml(string text)
+ {
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ text = text.Replace("\r\n", "
");
+ text = text.Replace("\r", "
");
+ text = text.Replace("\n", "
");
+ text = text.Replace("\t", " ");
+ text = text.Replace(" ", " ");
+
+ return text;
+ }
+
+ ///
+ /// Converts HTML to plain text
+ ///
+ /// Text
+ /// A value indicating whether to decode text
+ /// A value indicating whether to replace anchor text (remove a tag from the following url Name and output only the string "Name")
+ /// Formatted text
+ public static string ConvertHtmlToPlainText(string text,
+ bool decode = false, bool replaceAnchorTags = false)
+ {
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ if (decode)
+ text = HttpUtility.HtmlDecode(text);
+
+ text = text.Replace("
", "\n");
+ text = text.Replace("
", "\n");
+ text = text.Replace("
", "\n");
+ text = text.Replace(" ", "\t");
+ text = text.Replace(" ", " ");
+ //attribute Vat tags to remove
+ text = text.Replace("", "");
+ text = text.Replace("", "\t");
+ text = text.Replace("", "");
+
+
+ if (replaceAnchorTags)
+ text = ReplaceAnchorTags(text);
+
+ return text;
+ }
+
+ ///
+ /// Converts text to paragraph
+ ///
+ /// Text
+ /// Formatted text
+ public static string ConvertPlainTextToParagraph(string text)
+ {
+ if (String.IsNullOrEmpty(text))
+ return string.Empty;
+
+ text = paragraphStartRegex.Replace(text, string.Empty);
+ text = paragraphEndRegex.Replace(text, "\n");
+ text = text.Replace("\r\n", "\n").Replace("\r", "\n");
+ text = text + "\n\n";
+ text = text.Replace("\n\n", "\n");
+ var strArray = text.Split(new [] { '\n' });
+ var builder = new StringBuilder();
+ foreach (string str in strArray)
+ {
+ if ((str != null) && (str.Trim().Length > 0))
+ {
+ builder.AppendFormat("{0}
\n", str);
+ }
+ }
+ return builder.ToString();
+ }
+ #endregion
+ }
+}
diff --git a/src/Libraries/Nop.Data/Mapping/Customers/RewardPointsHistoryMap.cs b/src/Libraries/Nop.Data/Mapping/Customers/RewardPointsHistoryMap.cs
index 73d3f16ac9a..0444c3fc9cc 100644
--- a/src/Libraries/Nop.Data/Mapping/Customers/RewardPointsHistoryMap.cs
+++ b/src/Libraries/Nop.Data/Mapping/Customers/RewardPointsHistoryMap.cs
@@ -1,23 +1,28 @@
-using Nop.Core.Domain.Customers;
-
-namespace Nop.Data.Mapping.Customers
-{
- public partial class RewardPointsHistoryMap : NopEntityTypeConfiguration
- {
- public RewardPointsHistoryMap()
- {
- this.ToTable("RewardPointsHistory");
- this.HasKey(rph => rph.Id);
-
- this.Property(rph => rph.UsedAmount).HasPrecision(18, 4);
-
- this.HasRequired(rph => rph.Customer)
- .WithMany()
- .HasForeignKey(rph => rph.CustomerId);
-
- this.HasOptional(rph => rph.UsedWithOrder)
- .WithOptionalDependent(o => o.RedeemedRewardPointsEntry)
- .WillCascadeOnDelete(false);
- }
- }
+using Nop.Core.Domain.Customers;
+
+namespace Nop.Data.Mapping.Customers
+{
+ public partial class RewardPointsHistoryMap : NopEntityTypeConfiguration
+ {
+ public RewardPointsHistoryMap()
+ {
+ this.ToTable("RewardPointsHistory");
+ this.HasKey(rph => rph.Id);
+
+ this.Property(rph => rph.UsedAmount).HasPrecision(18, 4);
+ this.Property(rph => rph.UsedAmountPurchased).HasPrecision(18, 4);
+
+ this.HasRequired(rph => rph.Customer)
+ .WithMany()
+ .HasForeignKey(rph => rph.CustomerId);
+
+ this.HasOptional(rph => rph.UsedWithOrder)
+ .WithOptionalDependent(o => o.RedeemedRewardPointsEntry)
+ .WillCascadeOnDelete(false);
+
+ this.HasOptional(rph => rph.PurchasedWithOrderItem)
+ .WithMany(orderItem => orderItem.AssociatedRewardPoints)
+ .HasForeignKey(rph => rph.PurchasedWithOrderItemId);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Data/Mapping/Orders/OrderItemMap.cs b/src/Libraries/Nop.Data/Mapping/Orders/OrderItemMap.cs
index bc9b4de313a..7e8d477815c 100644
--- a/src/Libraries/Nop.Data/Mapping/Orders/OrderItemMap.cs
+++ b/src/Libraries/Nop.Data/Mapping/Orders/OrderItemMap.cs
@@ -1,31 +1,32 @@
-using Nop.Core.Domain.Orders;
-
-namespace Nop.Data.Mapping.Orders
-{
- public partial class OrderItemMap : NopEntityTypeConfiguration
- {
- public OrderItemMap()
- {
- this.ToTable("OrderItem");
- this.HasKey(orderItem => orderItem.Id);
-
- this.Property(orderItem => orderItem.UnitPriceInclTax).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.UnitPriceExclTax).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.PriceInclTax).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.PriceExclTax).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.DiscountAmountInclTax).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.DiscountAmountExclTax).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.OriginalProductCost).HasPrecision(18, 4);
- this.Property(orderItem => orderItem.ItemWeight).HasPrecision(18, 4);
-
-
- this.HasRequired(orderItem => orderItem.Order)
- .WithMany(o => o.OrderItems)
- .HasForeignKey(orderItem => orderItem.OrderId);
-
- this.HasRequired(orderItem => orderItem.Product)
- .WithMany()
- .HasForeignKey(orderItem => orderItem.ProductId);
- }
- }
+using Nop.Core.Domain.Orders;
+
+namespace Nop.Data.Mapping.Orders
+{
+ public partial class OrderItemMap : NopEntityTypeConfiguration
+ {
+ public OrderItemMap()
+ {
+ this.ToTable("OrderItem");
+ this.HasKey(orderItem => orderItem.Id);
+
+ this.Property(orderItem => orderItem.UnitPriceInclTax).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.UnitPriceExclTax).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.PriceInclTax).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.PriceExclTax).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.DiscountAmountInclTax).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.DiscountAmountExclTax).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.OriginalProductCost).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.ItemWeight).HasPrecision(18, 4);
+ this.Property(orderItem => orderItem.TaxRate).HasPrecision(18, 4); //MF 25.11.16
+
+
+ this.HasRequired(orderItem => orderItem.Order)
+ .WithMany(o => o.OrderItems)
+ .HasForeignKey(orderItem => orderItem.OrderId);
+
+ this.HasRequired(orderItem => orderItem.Product)
+ .WithMany()
+ .HasForeignKey(orderItem => orderItem.ProductId);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Data/Mapping/Orders/OrderMap.cs b/src/Libraries/Nop.Data/Mapping/Orders/OrderMap.cs
index 74eb3da187a..d9221fd4b13 100644
--- a/src/Libraries/Nop.Data/Mapping/Orders/OrderMap.cs
+++ b/src/Libraries/Nop.Data/Mapping/Orders/OrderMap.cs
@@ -1,53 +1,61 @@
-using Nop.Core.Domain.Orders;
-
-namespace Nop.Data.Mapping.Orders
-{
- public partial class OrderMap : NopEntityTypeConfiguration
- {
- public OrderMap()
- {
- this.ToTable("Order");
- this.HasKey(o => o.Id);
- this.Property(o => o.CurrencyRate).HasPrecision(18, 8);
- this.Property(o => o.OrderSubtotalInclTax).HasPrecision(18, 4);
- this.Property(o => o.OrderSubtotalExclTax).HasPrecision(18, 4);
- this.Property(o => o.OrderSubTotalDiscountInclTax).HasPrecision(18, 4);
- this.Property(o => o.OrderSubTotalDiscountExclTax).HasPrecision(18, 4);
- this.Property(o => o.OrderShippingInclTax).HasPrecision(18, 4);
- this.Property(o => o.OrderShippingExclTax).HasPrecision(18, 4);
- this.Property(o => o.PaymentMethodAdditionalFeeInclTax).HasPrecision(18, 4);
- this.Property(o => o.PaymentMethodAdditionalFeeExclTax).HasPrecision(18, 4);
- this.Property(o => o.OrderTax).HasPrecision(18, 4);
- this.Property(o => o.OrderDiscount).HasPrecision(18, 4);
- this.Property(o => o.OrderTotal).HasPrecision(18, 4);
- this.Property(o => o.RefundedAmount).HasPrecision(18, 4);
- this.Property(o => o.CustomOrderNumber).IsRequired();
-
- this.Ignore(o => o.OrderStatus);
- this.Ignore(o => o.PaymentStatus);
- this.Ignore(o => o.ShippingStatus);
- this.Ignore(o => o.CustomerTaxDisplayType);
- this.Ignore(o => o.TaxRatesDictionary);
-
- this.HasRequired(o => o.Customer)
- .WithMany()
- .HasForeignKey(o => o.CustomerId);
-
- //code below is commented because it causes some issues on big databases - http://www.nopcommerce.com/boards/t/11126/bug-version-20-command-confirm-takes-several-minutes-using-big-databases.aspx
- //this.HasRequired(o => o.BillingAddress).WithOptional().Map(x => x.MapKey("BillingAddressId")).WillCascadeOnDelete(false);
- //this.HasOptional(o => o.ShippingAddress).WithOptionalDependent().Map(x => x.MapKey("ShippingAddressId")).WillCascadeOnDelete(false);
- this.HasRequired(o => o.BillingAddress)
- .WithMany()
- .HasForeignKey(o => o.BillingAddressId)
- .WillCascadeOnDelete(false);
- this.HasOptional(o => o.ShippingAddress)
- .WithMany()
- .HasForeignKey(o => o.ShippingAddressId)
- .WillCascadeOnDelete(false);
- this.HasOptional(o => o.PickupAddress)
- .WithMany()
- .HasForeignKey(o => o.PickupAddressId)
- .WillCascadeOnDelete(false);
- }
- }
+using Nop.Core.Domain.Orders;
+
+namespace Nop.Data.Mapping.Orders
+{
+ public partial class OrderMap : NopEntityTypeConfiguration
+ {
+ public OrderMap()
+ {
+ this.ToTable("Order");
+ this.HasKey(o => o.Id);
+ this.Property(o => o.CurrencyRate).HasPrecision(18, 8);
+ this.Property(o => o.OrderSubtotalInclTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderSubtotalExclTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderSubTotalDiscountInclTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderSubTotalDiscountExclTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderShippingInclTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderShippingExclTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderShippingNonTaxable).HasPrecision(18, 4);
+ this.Property(o => o.PaymentMethodAdditionalFeeInclTax).HasPrecision(18, 4);
+ this.Property(o => o.PaymentMethodAdditionalFeeExclTax).HasPrecision(18, 4);
+ this.Property(o => o.PaymentMethodAdditionalFeeNonTaxable).HasPrecision(18, 4);
+ this.Property(o => o.OrderTax).HasPrecision(18, 4);
+ this.Property(o => o.OrderDiscount).HasPrecision(18, 4);
+ this.Property(o => o.OrderAmount).HasPrecision(18, 4); //MF 09.12.16
+ this.Property(o => o.OrderAmountIncl).HasPrecision(18, 4); //MF 09.12.16
+ this.Property(o => o.OrderDiscountIncl).HasPrecision(18, 4);
+ this.Property(o => o.EarnedRewardPointsBaseAmountIncl).HasPrecision(18, 4);
+ this.Property(o => o.EarnedRewardPointsBaseAmountExcl).HasPrecision(18, 4);
+ this.Property(o => o.OrderTotal).HasPrecision(18, 4);
+ this.Property(o => o.RefundedAmount).HasPrecision(18, 4);
+ this.Property(o => o.CustomOrderNumber).IsRequired();
+
+ this.Ignore(o => o.OrderStatus);
+ this.Ignore(o => o.PaymentStatus);
+ this.Ignore(o => o.ShippingStatus);
+ this.Ignore(o => o.CustomerTaxDisplayType);
+ this.Ignore(o => o.TaxRatesDictionary);
+ this.Ignore(o => o.OrderTotalAmountIncl);
+
+ this.HasRequired(o => o.Customer)
+ .WithMany()
+ .HasForeignKey(o => o.CustomerId);
+
+ //code below is commented because it causes some issues on big databases - http://www.nopcommerce.com/boards/t/11126/bug-version-20-command-confirm-takes-several-minutes-using-big-databases.aspx
+ //this.HasRequired(o => o.BillingAddress).WithOptional().Map(x => x.MapKey("BillingAddressId")).WillCascadeOnDelete(false);
+ //this.HasOptional(o => o.ShippingAddress).WithOptionalDependent().Map(x => x.MapKey("ShippingAddressId")).WillCascadeOnDelete(false);
+ this.HasRequired(o => o.BillingAddress)
+ .WithMany()
+ .HasForeignKey(o => o.BillingAddressId)
+ .WillCascadeOnDelete(false);
+ this.HasOptional(o => o.ShippingAddress)
+ .WithMany()
+ .HasForeignKey(o => o.ShippingAddressId)
+ .WillCascadeOnDelete(false);
+ this.HasOptional(o => o.PickupAddress)
+ .WithMany()
+ .HasForeignKey(o => o.PickupAddressId)
+ .WillCascadeOnDelete(false);
+ }
+ }
}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Data/Mapping/Orders/ShoppingCartItemMap.cs b/src/Libraries/Nop.Data/Mapping/Orders/ShoppingCartItemMap.cs
index 4f14308ba1b..55a6cba7e16 100644
--- a/src/Libraries/Nop.Data/Mapping/Orders/ShoppingCartItemMap.cs
+++ b/src/Libraries/Nop.Data/Mapping/Orders/ShoppingCartItemMap.cs
@@ -1,29 +1,32 @@
-using Nop.Core.Domain.Orders;
-
-namespace Nop.Data.Mapping.Orders
-{
- public partial class ShoppingCartItemMap : NopEntityTypeConfiguration
- {
- public ShoppingCartItemMap()
- {
- this.ToTable("ShoppingCartItem");
- this.HasKey(sci => sci.Id);
-
- this.Property(sci => sci.CustomerEnteredPrice).HasPrecision(18, 4);
-
- this.Ignore(sci => sci.ShoppingCartType);
- this.Ignore(sci => sci.IsFreeShipping);
- this.Ignore(sci => sci.IsShipEnabled);
- this.Ignore(sci => sci.AdditionalShippingCharge);
- this.Ignore(sci => sci.IsTaxExempt);
-
- this.HasRequired(sci => sci.Customer)
- .WithMany(c => c.ShoppingCartItems)
- .HasForeignKey(sci => sci.CustomerId);
-
- this.HasRequired(sci => sci.Product)
- .WithMany()
- .HasForeignKey(sci => sci.ProductId);
- }
- }
-}
+using Nop.Core.Domain.Orders;
+
+namespace Nop.Data.Mapping.Orders
+{
+ public partial class ShoppingCartItemMap : NopEntityTypeConfiguration
+ {
+ public ShoppingCartItemMap()
+ {
+ this.ToTable("ShoppingCartItem");
+ this.HasKey(sci => sci.Id);
+
+ this.Property(sci => sci.CustomerEnteredPrice).HasPrecision(18, 4);
+
+ this.Ignore(sci => sci.ShoppingCartType);
+ this.Ignore(sci => sci.IsFreeShipping);
+ this.Ignore(sci => sci.IsShipEnabled);
+ this.Ignore(sci => sci.AdditionalShippingCharge);
+ this.Ignore(sci => sci.IsTaxExempt);
+ this.Ignore(sci => sci.TaxRate);
+ this.Ignore(sci => sci.SubTotalExclTax);
+ this.Ignore(sci => sci.SubTotalInclTax);
+
+ this.HasRequired(sci => sci.Customer)
+ .WithMany(c => c.ShoppingCartItems)
+ .HasForeignKey(sci => sci.CustomerId);
+
+ this.HasRequired(sci => sci.Product)
+ .WithMany()
+ .HasForeignKey(sci => sci.ProductId);
+ }
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/CopyProductService.cs b/src/Libraries/Nop.Services/Catalog/CopyProductService.cs
index 2d9aefa3470..0a2bb4d12a4 100644
--- a/src/Libraries/Nop.Services/Catalog/CopyProductService.cs
+++ b/src/Libraries/Nop.Services/Catalog/CopyProductService.cs
@@ -1,635 +1,638 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Media;
-using Nop.Services.Localization;
-using Nop.Services.Media;
-using Nop.Services.Seo;
-using Nop.Services.Stores;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Copy Product service
- ///
- public partial class CopyProductService : ICopyProductService
- {
- #region Fields
-
- private readonly IProductService _productService;
- private readonly IProductAttributeService _productAttributeService;
- private readonly ILanguageService _languageService;
- private readonly ILocalizedEntityService _localizedEntityService;
- private readonly ILocalizationService _localizationService;
- private readonly IPictureService _pictureService;
- private readonly ICategoryService _categoryService;
- private readonly IManufacturerService _manufacturerService;
- private readonly ISpecificationAttributeService _specificationAttributeService;
- private readonly IDownloadService _downloadService;
- private readonly IProductAttributeParser _productAttributeParser;
- private readonly IUrlRecordService _urlRecordService;
- private readonly IStoreMappingService _storeMappingService;
-
- #endregion
-
- #region Ctor
-
- public CopyProductService(IProductService productService,
- IProductAttributeService productAttributeService,
- ILanguageService languageService,
- ILocalizedEntityService localizedEntityService,
- ILocalizationService localizationService,
- IPictureService pictureService,
- ICategoryService categoryService,
- IManufacturerService manufacturerService,
- ISpecificationAttributeService specificationAttributeService,
- IDownloadService downloadService,
- IProductAttributeParser productAttributeParser,
- IUrlRecordService urlRecordService,
- IStoreMappingService storeMappingService)
- {
- this._productService = productService;
- this._productAttributeService = productAttributeService;
- this._languageService = languageService;
- this._localizedEntityService = localizedEntityService;
- this._localizationService = localizationService;
- this._pictureService = pictureService;
- this._categoryService = categoryService;
- this._manufacturerService = manufacturerService;
- this._specificationAttributeService = specificationAttributeService;
- this._downloadService = downloadService;
- this._productAttributeParser = productAttributeParser;
- this._urlRecordService = urlRecordService;
- this._storeMappingService = storeMappingService;
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Create a copy of product with all depended data
- ///
- /// The product to copy
- /// The name of product duplicate
- /// A value indicating whether the product duplicate should be published
- /// A value indicating whether the product images should be copied
- /// A value indicating whether the copy associated products
- /// Product copy
- public virtual Product CopyProduct(Product product, string newName,
- bool isPublished = true, bool copyImages = true, bool copyAssociatedProducts = true)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- if (String.IsNullOrEmpty(newName))
- throw new ArgumentException("Product name is required");
-
- //product download & sample download
- int downloadId = product.DownloadId;
- int sampleDownloadId = product.SampleDownloadId;
- if (product.IsDownload)
- {
- var download = _downloadService.GetDownloadById(product.DownloadId);
- if (download != null)
- {
- var downloadCopy = new Download
- {
- DownloadGuid = Guid.NewGuid(),
- UseDownloadUrl = download.UseDownloadUrl,
- DownloadUrl = download.DownloadUrl,
- DownloadBinary = download.DownloadBinary,
- ContentType = download.ContentType,
- Filename = download.Filename,
- Extension = download.Extension,
- IsNew = download.IsNew,
- };
- _downloadService.InsertDownload(downloadCopy);
- downloadId = downloadCopy.Id;
- }
-
- if (product.HasSampleDownload)
- {
- var sampleDownload = _downloadService.GetDownloadById(product.SampleDownloadId);
- if (sampleDownload != null)
- {
- var sampleDownloadCopy = new Download
- {
- DownloadGuid = Guid.NewGuid(),
- UseDownloadUrl = sampleDownload.UseDownloadUrl,
- DownloadUrl = sampleDownload.DownloadUrl,
- DownloadBinary = sampleDownload.DownloadBinary,
- ContentType = sampleDownload.ContentType,
- Filename = sampleDownload.Filename,
- Extension = sampleDownload.Extension,
- IsNew = sampleDownload.IsNew
- };
- _downloadService.InsertDownload(sampleDownloadCopy);
- sampleDownloadId = sampleDownloadCopy.Id;
- }
- }
- }
-
- var newSku = !String.IsNullOrWhiteSpace(product.Sku)
- ? string.Format(_localizationService.GetResource("Admin.Catalog.Products.Copy.SKU.New"), product.Sku) :
- product.Sku;
- // product
- var productCopy = new Product
- {
- ProductTypeId = product.ProductTypeId,
- ParentGroupedProductId = product.ParentGroupedProductId,
- VisibleIndividually = product.VisibleIndividually,
- Name = newName,
- ShortDescription = product.ShortDescription,
- FullDescription = product.FullDescription,
- VendorId = product.VendorId,
- ProductTemplateId = product.ProductTemplateId,
- AdminComment = product.AdminComment,
- ShowOnHomePage = product.ShowOnHomePage,
- MetaKeywords = product.MetaKeywords,
- MetaDescription = product.MetaDescription,
- MetaTitle = product.MetaTitle,
- AllowCustomerReviews = product.AllowCustomerReviews,
- LimitedToStores = product.LimitedToStores,
- Sku = newSku,
- ManufacturerPartNumber = product.ManufacturerPartNumber,
- Gtin = product.Gtin,
- IsGiftCard = product.IsGiftCard,
- GiftCardType = product.GiftCardType,
- OverriddenGiftCardAmount = product.OverriddenGiftCardAmount,
- RequireOtherProducts = product.RequireOtherProducts,
- RequiredProductIds = product.RequiredProductIds,
- AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts,
- IsDownload = product.IsDownload,
- DownloadId = downloadId,
- UnlimitedDownloads = product.UnlimitedDownloads,
- MaxNumberOfDownloads = product.MaxNumberOfDownloads,
- DownloadExpirationDays = product.DownloadExpirationDays,
- DownloadActivationType = product.DownloadActivationType,
- HasSampleDownload = product.HasSampleDownload,
- SampleDownloadId = sampleDownloadId,
- HasUserAgreement = product.HasUserAgreement,
- UserAgreementText = product.UserAgreementText,
- IsRecurring = product.IsRecurring,
- RecurringCycleLength = product.RecurringCycleLength,
- RecurringCyclePeriod = product.RecurringCyclePeriod,
- RecurringTotalCycles = product.RecurringTotalCycles,
- IsRental = product.IsRental,
- RentalPriceLength = product.RentalPriceLength,
- RentalPricePeriod = product.RentalPricePeriod,
- IsShipEnabled = product.IsShipEnabled,
- IsFreeShipping = product.IsFreeShipping,
- ShipSeparately = product.ShipSeparately,
- AdditionalShippingCharge = product.AdditionalShippingCharge,
- DeliveryDateId = product.DeliveryDateId,
- IsTaxExempt = product.IsTaxExempt,
- TaxCategoryId = product.TaxCategoryId,
- IsTelecommunicationsOrBroadcastingOrElectronicServices = product.IsTelecommunicationsOrBroadcastingOrElectronicServices,
- ManageInventoryMethod = product.ManageInventoryMethod,
- ProductAvailabilityRangeId = product.ProductAvailabilityRangeId,
- UseMultipleWarehouses = product.UseMultipleWarehouses,
- WarehouseId = product.WarehouseId,
- StockQuantity = product.StockQuantity,
- DisplayStockAvailability = product.DisplayStockAvailability,
- DisplayStockQuantity = product.DisplayStockQuantity,
- MinStockQuantity = product.MinStockQuantity,
- LowStockActivityId = product.LowStockActivityId,
- NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow,
- BackorderMode = product.BackorderMode,
- AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions,
- OrderMinimumQuantity = product.OrderMinimumQuantity,
- OrderMaximumQuantity = product.OrderMaximumQuantity,
- AllowedQuantities = product.AllowedQuantities,
- AllowAddingOnlyExistingAttributeCombinations = product.AllowAddingOnlyExistingAttributeCombinations,
- NotReturnable = product.NotReturnable,
- DisableBuyButton = product.DisableBuyButton,
- DisableWishlistButton = product.DisableWishlistButton,
- AvailableForPreOrder = product.AvailableForPreOrder,
- PreOrderAvailabilityStartDateTimeUtc = product.PreOrderAvailabilityStartDateTimeUtc,
- CallForPrice = product.CallForPrice,
- Price = product.Price,
- OldPrice = product.OldPrice,
- ProductCost = product.ProductCost,
- CustomerEntersPrice = product.CustomerEntersPrice,
- MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice,
- MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice,
- BasepriceEnabled = product.BasepriceEnabled,
- BasepriceAmount = product.BasepriceAmount,
- BasepriceUnitId = product.BasepriceUnitId,
- BasepriceBaseAmount = product.BasepriceBaseAmount,
- BasepriceBaseUnitId = product.BasepriceBaseUnitId,
- MarkAsNew = product.MarkAsNew,
- MarkAsNewStartDateTimeUtc = product.MarkAsNewStartDateTimeUtc,
- MarkAsNewEndDateTimeUtc = product.MarkAsNewEndDateTimeUtc,
- Weight = product.Weight,
- Length = product.Length,
- Width = product.Width,
- Height = product.Height,
- AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc,
- AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc,
- DisplayOrder = product.DisplayOrder,
- Published = isPublished,
- Deleted = product.Deleted,
- CreatedOnUtc = DateTime.UtcNow,
- UpdatedOnUtc = DateTime.UtcNow
- };
-
- //validate search engine name
- _productService.InsertProduct(productCopy);
-
- //search engine name
- _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", productCopy.Name, true), 0);
-
- var languages = _languageService.GetAllLanguages(true);
-
- //localization
- foreach (var lang in languages)
- {
- var name = product.GetLocalized(x => x.Name, lang.Id, false, false);
- if (!String.IsNullOrEmpty(name))
- _localizedEntityService.SaveLocalizedValue(productCopy, x => x.Name, name, lang.Id);
-
- var shortDescription = product.GetLocalized(x => x.ShortDescription, lang.Id, false, false);
- if (!String.IsNullOrEmpty(shortDescription))
- _localizedEntityService.SaveLocalizedValue(productCopy, x => x.ShortDescription, shortDescription, lang.Id);
-
- var fullDescription = product.GetLocalized(x => x.FullDescription, lang.Id, false, false);
- if (!String.IsNullOrEmpty(fullDescription))
- _localizedEntityService.SaveLocalizedValue(productCopy, x => x.FullDescription, fullDescription, lang.Id);
-
- var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang.Id, false, false);
- if (!String.IsNullOrEmpty(metaKeywords))
- _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaKeywords, metaKeywords, lang.Id);
-
- var metaDescription = product.GetLocalized(x => x.MetaDescription, lang.Id, false, false);
- if (!String.IsNullOrEmpty(metaDescription))
- _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaDescription, metaDescription, lang.Id);
-
- var metaTitle = product.GetLocalized(x => x.MetaTitle, lang.Id, false, false);
- if (!String.IsNullOrEmpty(metaTitle))
- _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaTitle, metaTitle, lang.Id);
-
- //search engine name
- _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", name, false), lang.Id);
- }
-
- //product tags
- foreach (var productTag in product.ProductTags)
- {
- productCopy.ProductTags.Add(productTag);
- }
- _productService.UpdateProduct(productCopy);
-
- //product pictures
- //variable to store original and new picture identifiers
- var originalNewPictureIdentifiers = new Dictionary();
- if (copyImages)
- {
- foreach (var productPicture in product.ProductPictures)
- {
- var picture = productPicture.Picture;
- var pictureCopy = _pictureService.InsertPicture(
- _pictureService.LoadPictureBinary(picture),
- picture.MimeType,
- _pictureService.GetPictureSeName(newName),
- picture.AltAttribute,
- picture.TitleAttribute);
- _productService.InsertProductPicture(new ProductPicture
- {
- ProductId = productCopy.Id,
- PictureId = pictureCopy.Id,
- DisplayOrder = productPicture.DisplayOrder
- });
- originalNewPictureIdentifiers.Add(picture.Id, pictureCopy.Id);
- }
- }
-
- //quantity change history
- _productService.AddStockQuantityHistoryEntry(productCopy, product.StockQuantity, product.StockQuantity, product.WarehouseId,
- string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id));
-
- // product <-> warehouses mappings
- foreach (var pwi in product.ProductWarehouseInventory)
- {
- var pwiCopy = new ProductWarehouseInventory
- {
- ProductId = productCopy.Id,
- WarehouseId = pwi.WarehouseId,
- StockQuantity = pwi.StockQuantity,
- ReservedQuantity = 0,
- };
-
- productCopy.ProductWarehouseInventory.Add(pwiCopy);
-
- //quantity change history
- var message = string.Format("{0} {1}", _localizationService.GetResource("Admin.StockQuantityHistory.Messages.MultipleWarehouses"),
- string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id));
- _productService.AddStockQuantityHistoryEntry(productCopy, pwi.StockQuantity, pwi.StockQuantity, pwi.WarehouseId, message);
- }
- _productService.UpdateProduct(productCopy);
-
- // product <-> categories mappings
- foreach (var productCategory in product.ProductCategories)
- {
- var productCategoryCopy = new ProductCategory
- {
- ProductId = productCopy.Id,
- CategoryId = productCategory.CategoryId,
- IsFeaturedProduct = productCategory.IsFeaturedProduct,
- DisplayOrder = productCategory.DisplayOrder
- };
-
- _categoryService.InsertProductCategory(productCategoryCopy);
- }
-
- // product <-> manufacturers mappings
- foreach (var productManufacturers in product.ProductManufacturers)
- {
- var productManufacturerCopy = new ProductManufacturer
- {
- ProductId = productCopy.Id,
- ManufacturerId = productManufacturers.ManufacturerId,
- IsFeaturedProduct = productManufacturers.IsFeaturedProduct,
- DisplayOrder = productManufacturers.DisplayOrder
- };
-
- _manufacturerService.InsertProductManufacturer(productManufacturerCopy);
- }
-
- // product <-> releated products mappings
- foreach (var relatedProduct in _productService.GetRelatedProductsByProductId1(product.Id, true))
- {
- _productService.InsertRelatedProduct(
- new RelatedProduct
- {
- ProductId1 = productCopy.Id,
- ProductId2 = relatedProduct.ProductId2,
- DisplayOrder = relatedProduct.DisplayOrder
- });
- }
-
- // product <-> cross sells mappings
- foreach (var csProduct in _productService.GetCrossSellProductsByProductId1(product.Id, true))
- {
- _productService.InsertCrossSellProduct(
- new CrossSellProduct
- {
- ProductId1 = productCopy.Id,
- ProductId2 = csProduct.ProductId2,
- });
- }
-
- // product specifications
- foreach (var productSpecificationAttribute in product.ProductSpecificationAttributes)
- {
- var psaCopy = new ProductSpecificationAttribute
- {
- ProductId = productCopy.Id,
- AttributeTypeId = productSpecificationAttribute.AttributeTypeId,
- SpecificationAttributeOptionId = productSpecificationAttribute.SpecificationAttributeOptionId,
- CustomValue = productSpecificationAttribute.CustomValue,
- AllowFiltering = productSpecificationAttribute.AllowFiltering,
- ShowOnProductPage = productSpecificationAttribute.ShowOnProductPage,
- DisplayOrder = productSpecificationAttribute.DisplayOrder
- };
- _specificationAttributeService.InsertProductSpecificationAttribute(psaCopy);
- }
-
- //store mapping
- var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product);
- foreach (var id in selectedStoreIds)
- {
- _storeMappingService.InsertStoreMapping(productCopy, id);
- }
-
- //product <-> attributes mappings
- var associatedAttributes = new Dictionary();
- var associatedAttributeValues = new Dictionary();
-
- //attribute mapping with condition attributes
- var oldCopyWithConditionAttributes = new List();
-
- //all product attribute mapping copies
- var productAttributeMappingCopies = new Dictionary();
-
- foreach (var productAttributeMapping in _productAttributeService.GetProductAttributeMappingsByProductId(product.Id))
- {
- var productAttributeMappingCopy = new ProductAttributeMapping
- {
- ProductId = productCopy.Id,
- ProductAttributeId = productAttributeMapping.ProductAttributeId,
- TextPrompt = productAttributeMapping.TextPrompt,
- IsRequired = productAttributeMapping.IsRequired,
- AttributeControlTypeId = productAttributeMapping.AttributeControlTypeId,
- DisplayOrder = productAttributeMapping.DisplayOrder,
- ValidationMinLength = productAttributeMapping.ValidationMinLength,
- ValidationMaxLength = productAttributeMapping.ValidationMaxLength,
- ValidationFileAllowedExtensions = productAttributeMapping.ValidationFileAllowedExtensions,
- ValidationFileMaximumSize = productAttributeMapping.ValidationFileMaximumSize,
- DefaultValue = productAttributeMapping.DefaultValue
- };
- _productAttributeService.InsertProductAttributeMapping(productAttributeMappingCopy);
-
- productAttributeMappingCopies.Add(productAttributeMappingCopy.Id, productAttributeMappingCopy);
-
- if (!string.IsNullOrEmpty(productAttributeMapping.ConditionAttributeXml))
- {
- oldCopyWithConditionAttributes.Add(productAttributeMapping);
- }
-
- //save associated value (used for combinations copying)
- associatedAttributes.Add(productAttributeMapping.Id, productAttributeMappingCopy.Id);
-
- // product attribute values
- var productAttributeValues = _productAttributeService.GetProductAttributeValues(productAttributeMapping.Id);
- foreach (var productAttributeValue in productAttributeValues)
- {
- int attributeValuePictureId = 0;
- if (originalNewPictureIdentifiers.ContainsKey(productAttributeValue.PictureId))
- {
- attributeValuePictureId = originalNewPictureIdentifiers[productAttributeValue.PictureId];
- }
- var attributeValueCopy = new ProductAttributeValue
- {
- ProductAttributeMappingId = productAttributeMappingCopy.Id,
- AttributeValueTypeId = productAttributeValue.AttributeValueTypeId,
- AssociatedProductId = productAttributeValue.AssociatedProductId,
- Name = productAttributeValue.Name,
- ColorSquaresRgb = productAttributeValue.ColorSquaresRgb,
- PriceAdjustment = productAttributeValue.PriceAdjustment,
- WeightAdjustment = productAttributeValue.WeightAdjustment,
- Cost = productAttributeValue.Cost,
- CustomerEntersQty = productAttributeValue.CustomerEntersQty,
- Quantity = productAttributeValue.Quantity,
- IsPreSelected = productAttributeValue.IsPreSelected,
- DisplayOrder = productAttributeValue.DisplayOrder,
- PictureId = attributeValuePictureId,
- };
- //picture associated to "iamge square" attribute type (if exists)
- if (productAttributeValue.ImageSquaresPictureId > 0)
- {
- var origImageSquaresPicture = _pictureService.GetPictureById(productAttributeValue.ImageSquaresPictureId);
- if (origImageSquaresPicture != null)
- {
- //copy the picture
- var imageSquaresPictureCopy = _pictureService.InsertPicture(
- _pictureService.LoadPictureBinary(origImageSquaresPicture),
- origImageSquaresPicture.MimeType,
- origImageSquaresPicture.SeoFilename,
- origImageSquaresPicture.AltAttribute,
- origImageSquaresPicture.TitleAttribute);
- attributeValueCopy.ImageSquaresPictureId = imageSquaresPictureCopy.Id;
- }
- }
-
- _productAttributeService.InsertProductAttributeValue(attributeValueCopy);
-
- //save associated value (used for combinations copying)
- associatedAttributeValues.Add(productAttributeValue.Id, attributeValueCopy.Id);
-
- //localization
- foreach (var lang in languages)
- {
- var name = productAttributeValue.GetLocalized(x => x.Name, lang.Id, false, false);
- if (!String.IsNullOrEmpty(name))
- _localizedEntityService.SaveLocalizedValue(attributeValueCopy, x => x.Name, name, lang.Id);
- }
- }
- }
-
- //copy attribute conditions
- foreach (var productAttributeMapping in oldCopyWithConditionAttributes)
- {
- var oldConditionAttributeMapping = _productAttributeParser.ParseProductAttributeMappings(productAttributeMapping.ConditionAttributeXml).FirstOrDefault();
-
- if (oldConditionAttributeMapping == null)
- continue;
-
- var oldConditionValues = _productAttributeParser.ParseProductAttributeValues(productAttributeMapping.ConditionAttributeXml, oldConditionAttributeMapping.Id);
-
- if (!oldConditionValues.Any())
- continue;
-
- var newAttributeMappingId = associatedAttributes[oldConditionAttributeMapping.Id];
- var newConditionAttributeMapping = productAttributeMappingCopies[newAttributeMappingId];
-
- var newConditionAttributeXml = string.Empty;
-
- foreach (var oldConditionValue in oldConditionValues)
- {
- newConditionAttributeXml = _productAttributeParser.AddProductAttribute(newConditionAttributeXml, newConditionAttributeMapping, associatedAttributeValues[oldConditionValue.Id].ToString());
- }
-
- var attributeMappingId = associatedAttributes[productAttributeMapping.Id];
- var conditionAttribute = productAttributeMappingCopies[attributeMappingId];
- conditionAttribute.ConditionAttributeXml = newConditionAttributeXml;
-
- _productAttributeService.UpdateProductAttributeMapping(conditionAttribute);
- }
-
- //attribute combinations
- foreach (var combination in _productAttributeService.GetAllProductAttributeCombinations(product.Id))
- {
- //generate new AttributesXml according to new value IDs
- string newAttributesXml = "";
- var parsedProductAttributes = _productAttributeParser.ParseProductAttributeMappings(combination.AttributesXml);
- foreach (var oldAttribute in parsedProductAttributes)
- {
- if (associatedAttributes.ContainsKey(oldAttribute.Id))
- {
- var newAttribute = _productAttributeService.GetProductAttributeMappingById(associatedAttributes[oldAttribute.Id]);
- if (newAttribute != null)
- {
- var oldAttributeValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldAttribute.Id);
- foreach (var oldAttributeValueStr in oldAttributeValuesStr)
- {
- if (newAttribute.ShouldHaveValues())
- {
- //attribute values
- int oldAttributeValue = int.Parse(oldAttributeValueStr);
- if (associatedAttributeValues.ContainsKey(oldAttributeValue))
- {
- var newAttributeValue = _productAttributeService.GetProductAttributeValueById(associatedAttributeValues[oldAttributeValue]);
- if (newAttributeValue != null)
- {
- newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml,
- newAttribute, newAttributeValue.Id.ToString());
- }
- }
- }
- else
- {
- //just a text
- newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml,
- newAttribute, oldAttributeValueStr);
- }
- }
- }
- }
- }
- var combinationCopy = new ProductAttributeCombination
- {
- ProductId = productCopy.Id,
- AttributesXml = newAttributesXml,
- StockQuantity = combination.StockQuantity,
- AllowOutOfStockOrders = combination.AllowOutOfStockOrders,
- Sku = combination.Sku,
- ManufacturerPartNumber = combination.ManufacturerPartNumber,
- Gtin = combination.Gtin,
- OverriddenPrice = combination.OverriddenPrice,
- NotifyAdminForQuantityBelow = combination.NotifyAdminForQuantityBelow
- };
- _productAttributeService.InsertProductAttributeCombination(combinationCopy);
-
- //quantity change history
- _productService.AddStockQuantityHistoryEntry(productCopy, combination.StockQuantity, combination.StockQuantity,
- message: string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id), combinationId: combination.Id);
- }
-
- //tier prices
- foreach (var tierPrice in product.TierPrices)
- {
- _productService.InsertTierPrice(
- new TierPrice
- {
- ProductId = productCopy.Id,
- StoreId = tierPrice.StoreId,
- CustomerRoleId = tierPrice.CustomerRoleId,
- Quantity = tierPrice.Quantity,
- Price = tierPrice.Price,
- StartDateTimeUtc = tierPrice.StartDateTimeUtc,
- EndDateTimeUtc = tierPrice.EndDateTimeUtc
- });
- }
-
- // product <-> discounts mapping
- foreach (var discount in product.AppliedDiscounts)
- {
- productCopy.AppliedDiscounts.Add(discount);
- _productService.UpdateProduct(productCopy);
- }
-
- //update "HasTierPrices" and "HasDiscountsApplied" properties
- _productService.UpdateHasTierPricesProperty(productCopy);
- _productService.UpdateHasDiscountsApplied(productCopy);
-
- //associated products
- if (copyAssociatedProducts)
- {
- var associatedProducts = _productService.GetAssociatedProducts(product.Id, showHidden: true);
- foreach (var associatedProduct in associatedProducts)
- {
- var associatedProductCopy = CopyProduct(associatedProduct, string.Format("Copy of {0}", associatedProduct.Name),
- isPublished, copyImages, false);
- associatedProductCopy.ParentGroupedProductId = productCopy.Id;
- _productService.UpdateProduct(productCopy);
- }
- }
-
- return productCopy;
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Media;
+using Nop.Services.Localization;
+using Nop.Services.Media;
+using Nop.Services.Seo;
+using Nop.Services.Stores;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Copy Product service
+ ///
+ public partial class CopyProductService : ICopyProductService
+ {
+ #region Fields
+
+ private readonly IProductService _productService;
+ private readonly IProductAttributeService _productAttributeService;
+ private readonly ILanguageService _languageService;
+ private readonly ILocalizedEntityService _localizedEntityService;
+ private readonly ILocalizationService _localizationService;
+ private readonly IPictureService _pictureService;
+ private readonly ICategoryService _categoryService;
+ private readonly IManufacturerService _manufacturerService;
+ private readonly ISpecificationAttributeService _specificationAttributeService;
+ private readonly IDownloadService _downloadService;
+ private readonly IProductAttributeParser _productAttributeParser;
+ private readonly IUrlRecordService _urlRecordService;
+ private readonly IStoreMappingService _storeMappingService;
+
+ #endregion
+
+ #region Ctor
+
+ public CopyProductService(IProductService productService,
+ IProductAttributeService productAttributeService,
+ ILanguageService languageService,
+ ILocalizedEntityService localizedEntityService,
+ ILocalizationService localizationService,
+ IPictureService pictureService,
+ ICategoryService categoryService,
+ IManufacturerService manufacturerService,
+ ISpecificationAttributeService specificationAttributeService,
+ IDownloadService downloadService,
+ IProductAttributeParser productAttributeParser,
+ IUrlRecordService urlRecordService,
+ IStoreMappingService storeMappingService)
+ {
+ this._productService = productService;
+ this._productAttributeService = productAttributeService;
+ this._languageService = languageService;
+ this._localizedEntityService = localizedEntityService;
+ this._localizationService = localizationService;
+ this._pictureService = pictureService;
+ this._categoryService = categoryService;
+ this._manufacturerService = manufacturerService;
+ this._specificationAttributeService = specificationAttributeService;
+ this._downloadService = downloadService;
+ this._productAttributeParser = productAttributeParser;
+ this._urlRecordService = urlRecordService;
+ this._storeMappingService = storeMappingService;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Create a copy of product with all depended data
+ ///
+ /// The product to copy
+ /// The name of product duplicate
+ /// A value indicating whether the product duplicate should be published
+ /// A value indicating whether the product images should be copied
+ /// A value indicating whether the copy associated products
+ /// Product copy
+ public virtual Product CopyProduct(Product product, string newName,
+ bool isPublished = true, bool copyImages = true, bool copyAssociatedProducts = true)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ if (String.IsNullOrEmpty(newName))
+ throw new ArgumentException("Product name is required");
+
+ //product download & sample download
+ int downloadId = product.DownloadId;
+ int sampleDownloadId = product.SampleDownloadId;
+ if (product.IsDownload)
+ {
+ var download = _downloadService.GetDownloadById(product.DownloadId);
+ if (download != null)
+ {
+ var downloadCopy = new Download
+ {
+ DownloadGuid = Guid.NewGuid(),
+ UseDownloadUrl = download.UseDownloadUrl,
+ DownloadUrl = download.DownloadUrl,
+ DownloadBinary = download.DownloadBinary,
+ ContentType = download.ContentType,
+ Filename = download.Filename,
+ Extension = download.Extension,
+ IsNew = download.IsNew,
+ };
+ _downloadService.InsertDownload(downloadCopy);
+ downloadId = downloadCopy.Id;
+ }
+
+ if (product.HasSampleDownload)
+ {
+ var sampleDownload = _downloadService.GetDownloadById(product.SampleDownloadId);
+ if (sampleDownload != null)
+ {
+ var sampleDownloadCopy = new Download
+ {
+ DownloadGuid = Guid.NewGuid(),
+ UseDownloadUrl = sampleDownload.UseDownloadUrl,
+ DownloadUrl = sampleDownload.DownloadUrl,
+ DownloadBinary = sampleDownload.DownloadBinary,
+ ContentType = sampleDownload.ContentType,
+ Filename = sampleDownload.Filename,
+ Extension = sampleDownload.Extension,
+ IsNew = sampleDownload.IsNew
+ };
+ _downloadService.InsertDownload(sampleDownloadCopy);
+ sampleDownloadId = sampleDownloadCopy.Id;
+ }
+ }
+ }
+
+ var newSku = !String.IsNullOrWhiteSpace(product.Sku)
+ ? string.Format(_localizationService.GetResource("Admin.Catalog.Products.Copy.SKU.New"), product.Sku) :
+ product.Sku;
+ // product
+ var productCopy = new Product
+ {
+ ProductTypeId = product.ProductTypeId,
+ ParentGroupedProductId = product.ParentGroupedProductId,
+ VisibleIndividually = product.VisibleIndividually,
+ Name = newName,
+ ShortDescription = product.ShortDescription,
+ FullDescription = product.FullDescription,
+ VendorId = product.VendorId,
+ ProductTemplateId = product.ProductTemplateId,
+ AdminComment = product.AdminComment,
+ ShowOnHomePage = product.ShowOnHomePage,
+ MetaKeywords = product.MetaKeywords,
+ MetaDescription = product.MetaDescription,
+ MetaTitle = product.MetaTitle,
+ AllowCustomerReviews = product.AllowCustomerReviews,
+ LimitedToStores = product.LimitedToStores,
+ Sku = newSku,
+ ManufacturerPartNumber = product.ManufacturerPartNumber,
+ Gtin = product.Gtin,
+ IsGiftCard = product.IsGiftCard,
+ GiftCardType = product.GiftCardType,
+ OverriddenGiftCardAmount = product.OverriddenGiftCardAmount,
+ IsRewardPoints = product.IsRewardPoints,
+ OverriddenRPExchangeRate = product.OverriddenRPExchangeRate,
+ ExcludeFromRewardPoints = product.ExcludeFromRewardPoints,
+ RequireOtherProducts = product.RequireOtherProducts,
+ RequiredProductIds = product.RequiredProductIds,
+ AutomaticallyAddRequiredProducts = product.AutomaticallyAddRequiredProducts,
+ IsDownload = product.IsDownload,
+ DownloadId = downloadId,
+ UnlimitedDownloads = product.UnlimitedDownloads,
+ MaxNumberOfDownloads = product.MaxNumberOfDownloads,
+ DownloadExpirationDays = product.DownloadExpirationDays,
+ DownloadActivationType = product.DownloadActivationType,
+ HasSampleDownload = product.HasSampleDownload,
+ SampleDownloadId = sampleDownloadId,
+ HasUserAgreement = product.HasUserAgreement,
+ UserAgreementText = product.UserAgreementText,
+ IsRecurring = product.IsRecurring,
+ RecurringCycleLength = product.RecurringCycleLength,
+ RecurringCyclePeriod = product.RecurringCyclePeriod,
+ RecurringTotalCycles = product.RecurringTotalCycles,
+ IsRental = product.IsRental,
+ RentalPriceLength = product.RentalPriceLength,
+ RentalPricePeriod = product.RentalPricePeriod,
+ IsShipEnabled = product.IsShipEnabled,
+ IsFreeShipping = product.IsFreeShipping,
+ ShipSeparately = product.ShipSeparately,
+ AdditionalShippingCharge = product.AdditionalShippingCharge,
+ DeliveryDateId = product.DeliveryDateId,
+ IsTaxExempt = product.IsTaxExempt,
+ TaxCategoryId = product.TaxCategoryId,
+ IsTelecommunicationsOrBroadcastingOrElectronicServices = product.IsTelecommunicationsOrBroadcastingOrElectronicServices,
+ ManageInventoryMethod = product.ManageInventoryMethod,
+ ProductAvailabilityRangeId = product.ProductAvailabilityRangeId,
+ UseMultipleWarehouses = product.UseMultipleWarehouses,
+ WarehouseId = product.WarehouseId,
+ StockQuantity = product.StockQuantity,
+ DisplayStockAvailability = product.DisplayStockAvailability,
+ DisplayStockQuantity = product.DisplayStockQuantity,
+ MinStockQuantity = product.MinStockQuantity,
+ LowStockActivityId = product.LowStockActivityId,
+ NotifyAdminForQuantityBelow = product.NotifyAdminForQuantityBelow,
+ BackorderMode = product.BackorderMode,
+ AllowBackInStockSubscriptions = product.AllowBackInStockSubscriptions,
+ OrderMinimumQuantity = product.OrderMinimumQuantity,
+ OrderMaximumQuantity = product.OrderMaximumQuantity,
+ AllowedQuantities = product.AllowedQuantities,
+ AllowAddingOnlyExistingAttributeCombinations = product.AllowAddingOnlyExistingAttributeCombinations,
+ NotReturnable = product.NotReturnable,
+ DisableBuyButton = product.DisableBuyButton,
+ DisableWishlistButton = product.DisableWishlistButton,
+ AvailableForPreOrder = product.AvailableForPreOrder,
+ PreOrderAvailabilityStartDateTimeUtc = product.PreOrderAvailabilityStartDateTimeUtc,
+ CallForPrice = product.CallForPrice,
+ Price = product.Price,
+ OldPrice = product.OldPrice,
+ ProductCost = product.ProductCost,
+ CustomerEntersPrice = product.CustomerEntersPrice,
+ MinimumCustomerEnteredPrice = product.MinimumCustomerEnteredPrice,
+ MaximumCustomerEnteredPrice = product.MaximumCustomerEnteredPrice,
+ BasepriceEnabled = product.BasepriceEnabled,
+ BasepriceAmount = product.BasepriceAmount,
+ BasepriceUnitId = product.BasepriceUnitId,
+ BasepriceBaseAmount = product.BasepriceBaseAmount,
+ BasepriceBaseUnitId = product.BasepriceBaseUnitId,
+ MarkAsNew = product.MarkAsNew,
+ MarkAsNewStartDateTimeUtc = product.MarkAsNewStartDateTimeUtc,
+ MarkAsNewEndDateTimeUtc = product.MarkAsNewEndDateTimeUtc,
+ Weight = product.Weight,
+ Length = product.Length,
+ Width = product.Width,
+ Height = product.Height,
+ AvailableStartDateTimeUtc = product.AvailableStartDateTimeUtc,
+ AvailableEndDateTimeUtc = product.AvailableEndDateTimeUtc,
+ DisplayOrder = product.DisplayOrder,
+ Published = isPublished,
+ Deleted = product.Deleted,
+ CreatedOnUtc = DateTime.UtcNow,
+ UpdatedOnUtc = DateTime.UtcNow
+ };
+
+ //validate search engine name
+ _productService.InsertProduct(productCopy);
+
+ //search engine name
+ _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", productCopy.Name, true), 0);
+
+ var languages = _languageService.GetAllLanguages(true);
+
+ //localization
+ foreach (var lang in languages)
+ {
+ var name = product.GetLocalized(x => x.Name, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(name))
+ _localizedEntityService.SaveLocalizedValue(productCopy, x => x.Name, name, lang.Id);
+
+ var shortDescription = product.GetLocalized(x => x.ShortDescription, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(shortDescription))
+ _localizedEntityService.SaveLocalizedValue(productCopy, x => x.ShortDescription, shortDescription, lang.Id);
+
+ var fullDescription = product.GetLocalized(x => x.FullDescription, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(fullDescription))
+ _localizedEntityService.SaveLocalizedValue(productCopy, x => x.FullDescription, fullDescription, lang.Id);
+
+ var metaKeywords = product.GetLocalized(x => x.MetaKeywords, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(metaKeywords))
+ _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaKeywords, metaKeywords, lang.Id);
+
+ var metaDescription = product.GetLocalized(x => x.MetaDescription, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(metaDescription))
+ _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaDescription, metaDescription, lang.Id);
+
+ var metaTitle = product.GetLocalized(x => x.MetaTitle, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(metaTitle))
+ _localizedEntityService.SaveLocalizedValue(productCopy, x => x.MetaTitle, metaTitle, lang.Id);
+
+ //search engine name
+ _urlRecordService.SaveSlug(productCopy, productCopy.ValidateSeName("", name, false), lang.Id);
+ }
+
+ //product tags
+ foreach (var productTag in product.ProductTags)
+ {
+ productCopy.ProductTags.Add(productTag);
+ }
+ _productService.UpdateProduct(productCopy);
+
+ //product pictures
+ //variable to store original and new picture identifiers
+ var originalNewPictureIdentifiers = new Dictionary();
+ if (copyImages)
+ {
+ foreach (var productPicture in product.ProductPictures)
+ {
+ var picture = productPicture.Picture;
+ var pictureCopy = _pictureService.InsertPicture(
+ _pictureService.LoadPictureBinary(picture),
+ picture.MimeType,
+ _pictureService.GetPictureSeName(newName),
+ picture.AltAttribute,
+ picture.TitleAttribute);
+ _productService.InsertProductPicture(new ProductPicture
+ {
+ ProductId = productCopy.Id,
+ PictureId = pictureCopy.Id,
+ DisplayOrder = productPicture.DisplayOrder
+ });
+ originalNewPictureIdentifiers.Add(picture.Id, pictureCopy.Id);
+ }
+ }
+
+ //quantity change history
+ _productService.AddStockQuantityHistoryEntry(productCopy, product.StockQuantity, product.StockQuantity, product.WarehouseId,
+ string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id));
+
+ // product <-> warehouses mappings
+ foreach (var pwi in product.ProductWarehouseInventory)
+ {
+ var pwiCopy = new ProductWarehouseInventory
+ {
+ ProductId = productCopy.Id,
+ WarehouseId = pwi.WarehouseId,
+ StockQuantity = pwi.StockQuantity,
+ ReservedQuantity = 0,
+ };
+
+ productCopy.ProductWarehouseInventory.Add(pwiCopy);
+
+ //quantity change history
+ var message = string.Format("{0} {1}", _localizationService.GetResource("Admin.StockQuantityHistory.Messages.MultipleWarehouses"),
+ string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id));
+ _productService.AddStockQuantityHistoryEntry(productCopy, pwi.StockQuantity, pwi.StockQuantity, pwi.WarehouseId, message);
+ }
+ _productService.UpdateProduct(productCopy);
+
+ // product <-> categories mappings
+ foreach (var productCategory in product.ProductCategories)
+ {
+ var productCategoryCopy = new ProductCategory
+ {
+ ProductId = productCopy.Id,
+ CategoryId = productCategory.CategoryId,
+ IsFeaturedProduct = productCategory.IsFeaturedProduct,
+ DisplayOrder = productCategory.DisplayOrder
+ };
+
+ _categoryService.InsertProductCategory(productCategoryCopy);
+ }
+
+ // product <-> manufacturers mappings
+ foreach (var productManufacturers in product.ProductManufacturers)
+ {
+ var productManufacturerCopy = new ProductManufacturer
+ {
+ ProductId = productCopy.Id,
+ ManufacturerId = productManufacturers.ManufacturerId,
+ IsFeaturedProduct = productManufacturers.IsFeaturedProduct,
+ DisplayOrder = productManufacturers.DisplayOrder
+ };
+
+ _manufacturerService.InsertProductManufacturer(productManufacturerCopy);
+ }
+
+ // product <-> releated products mappings
+ foreach (var relatedProduct in _productService.GetRelatedProductsByProductId1(product.Id, true))
+ {
+ _productService.InsertRelatedProduct(
+ new RelatedProduct
+ {
+ ProductId1 = productCopy.Id,
+ ProductId2 = relatedProduct.ProductId2,
+ DisplayOrder = relatedProduct.DisplayOrder
+ });
+ }
+
+ // product <-> cross sells mappings
+ foreach (var csProduct in _productService.GetCrossSellProductsByProductId1(product.Id, true))
+ {
+ _productService.InsertCrossSellProduct(
+ new CrossSellProduct
+ {
+ ProductId1 = productCopy.Id,
+ ProductId2 = csProduct.ProductId2,
+ });
+ }
+
+ // product specifications
+ foreach (var productSpecificationAttribute in product.ProductSpecificationAttributes)
+ {
+ var psaCopy = new ProductSpecificationAttribute
+ {
+ ProductId = productCopy.Id,
+ AttributeTypeId = productSpecificationAttribute.AttributeTypeId,
+ SpecificationAttributeOptionId = productSpecificationAttribute.SpecificationAttributeOptionId,
+ CustomValue = productSpecificationAttribute.CustomValue,
+ AllowFiltering = productSpecificationAttribute.AllowFiltering,
+ ShowOnProductPage = productSpecificationAttribute.ShowOnProductPage,
+ DisplayOrder = productSpecificationAttribute.DisplayOrder
+ };
+ _specificationAttributeService.InsertProductSpecificationAttribute(psaCopy);
+ }
+
+ //store mapping
+ var selectedStoreIds = _storeMappingService.GetStoresIdsWithAccess(product);
+ foreach (var id in selectedStoreIds)
+ {
+ _storeMappingService.InsertStoreMapping(productCopy, id);
+ }
+
+ //product <-> attributes mappings
+ var associatedAttributes = new Dictionary();
+ var associatedAttributeValues = new Dictionary();
+
+ //attribute mapping with condition attributes
+ var oldCopyWithConditionAttributes = new List();
+
+ //all product attribute mapping copies
+ var productAttributeMappingCopies = new Dictionary();
+
+ foreach (var productAttributeMapping in _productAttributeService.GetProductAttributeMappingsByProductId(product.Id))
+ {
+ var productAttributeMappingCopy = new ProductAttributeMapping
+ {
+ ProductId = productCopy.Id,
+ ProductAttributeId = productAttributeMapping.ProductAttributeId,
+ TextPrompt = productAttributeMapping.TextPrompt,
+ IsRequired = productAttributeMapping.IsRequired,
+ AttributeControlTypeId = productAttributeMapping.AttributeControlTypeId,
+ DisplayOrder = productAttributeMapping.DisplayOrder,
+ ValidationMinLength = productAttributeMapping.ValidationMinLength,
+ ValidationMaxLength = productAttributeMapping.ValidationMaxLength,
+ ValidationFileAllowedExtensions = productAttributeMapping.ValidationFileAllowedExtensions,
+ ValidationFileMaximumSize = productAttributeMapping.ValidationFileMaximumSize,
+ DefaultValue = productAttributeMapping.DefaultValue
+ };
+ _productAttributeService.InsertProductAttributeMapping(productAttributeMappingCopy);
+
+ productAttributeMappingCopies.Add(productAttributeMappingCopy.Id, productAttributeMappingCopy);
+
+ if (!string.IsNullOrEmpty(productAttributeMapping.ConditionAttributeXml))
+ {
+ oldCopyWithConditionAttributes.Add(productAttributeMapping);
+ }
+
+ //save associated value (used for combinations copying)
+ associatedAttributes.Add(productAttributeMapping.Id, productAttributeMappingCopy.Id);
+
+ // product attribute values
+ var productAttributeValues = _productAttributeService.GetProductAttributeValues(productAttributeMapping.Id);
+ foreach (var productAttributeValue in productAttributeValues)
+ {
+ int attributeValuePictureId = 0;
+ if (originalNewPictureIdentifiers.ContainsKey(productAttributeValue.PictureId))
+ {
+ attributeValuePictureId = originalNewPictureIdentifiers[productAttributeValue.PictureId];
+ }
+ var attributeValueCopy = new ProductAttributeValue
+ {
+ ProductAttributeMappingId = productAttributeMappingCopy.Id,
+ AttributeValueTypeId = productAttributeValue.AttributeValueTypeId,
+ AssociatedProductId = productAttributeValue.AssociatedProductId,
+ Name = productAttributeValue.Name,
+ ColorSquaresRgb = productAttributeValue.ColorSquaresRgb,
+ PriceAdjustment = productAttributeValue.PriceAdjustment,
+ WeightAdjustment = productAttributeValue.WeightAdjustment,
+ Cost = productAttributeValue.Cost,
+ CustomerEntersQty = productAttributeValue.CustomerEntersQty,
+ Quantity = productAttributeValue.Quantity,
+ IsPreSelected = productAttributeValue.IsPreSelected,
+ DisplayOrder = productAttributeValue.DisplayOrder,
+ PictureId = attributeValuePictureId,
+ };
+ //picture associated to "iamge square" attribute type (if exists)
+ if (productAttributeValue.ImageSquaresPictureId > 0)
+ {
+ var origImageSquaresPicture = _pictureService.GetPictureById(productAttributeValue.ImageSquaresPictureId);
+ if (origImageSquaresPicture != null)
+ {
+ //copy the picture
+ var imageSquaresPictureCopy = _pictureService.InsertPicture(
+ _pictureService.LoadPictureBinary(origImageSquaresPicture),
+ origImageSquaresPicture.MimeType,
+ origImageSquaresPicture.SeoFilename,
+ origImageSquaresPicture.AltAttribute,
+ origImageSquaresPicture.TitleAttribute);
+ attributeValueCopy.ImageSquaresPictureId = imageSquaresPictureCopy.Id;
+ }
+ }
+
+ _productAttributeService.InsertProductAttributeValue(attributeValueCopy);
+
+ //save associated value (used for combinations copying)
+ associatedAttributeValues.Add(productAttributeValue.Id, attributeValueCopy.Id);
+
+ //localization
+ foreach (var lang in languages)
+ {
+ var name = productAttributeValue.GetLocalized(x => x.Name, lang.Id, false, false);
+ if (!String.IsNullOrEmpty(name))
+ _localizedEntityService.SaveLocalizedValue(attributeValueCopy, x => x.Name, name, lang.Id);
+ }
+ }
+ }
+
+ //copy attribute conditions
+ foreach (var productAttributeMapping in oldCopyWithConditionAttributes)
+ {
+ var oldConditionAttributeMapping = _productAttributeParser.ParseProductAttributeMappings(productAttributeMapping.ConditionAttributeXml).FirstOrDefault();
+
+ if (oldConditionAttributeMapping == null)
+ continue;
+
+ var oldConditionValues = _productAttributeParser.ParseProductAttributeValues(productAttributeMapping.ConditionAttributeXml, oldConditionAttributeMapping.Id);
+
+ if (!oldConditionValues.Any())
+ continue;
+
+ var newAttributeMappingId = associatedAttributes[oldConditionAttributeMapping.Id];
+ var newConditionAttributeMapping = productAttributeMappingCopies[newAttributeMappingId];
+
+ var newConditionAttributeXml = string.Empty;
+
+ foreach (var oldConditionValue in oldConditionValues)
+ {
+ newConditionAttributeXml = _productAttributeParser.AddProductAttribute(newConditionAttributeXml, newConditionAttributeMapping, associatedAttributeValues[oldConditionValue.Id].ToString());
+ }
+
+ var attributeMappingId = associatedAttributes[productAttributeMapping.Id];
+ var conditionAttribute = productAttributeMappingCopies[attributeMappingId];
+ conditionAttribute.ConditionAttributeXml = newConditionAttributeXml;
+
+ _productAttributeService.UpdateProductAttributeMapping(conditionAttribute);
+ }
+
+ //attribute combinations
+ foreach (var combination in _productAttributeService.GetAllProductAttributeCombinations(product.Id))
+ {
+ //generate new AttributesXml according to new value IDs
+ string newAttributesXml = "";
+ var parsedProductAttributes = _productAttributeParser.ParseProductAttributeMappings(combination.AttributesXml);
+ foreach (var oldAttribute in parsedProductAttributes)
+ {
+ if (associatedAttributes.ContainsKey(oldAttribute.Id))
+ {
+ var newAttribute = _productAttributeService.GetProductAttributeMappingById(associatedAttributes[oldAttribute.Id]);
+ if (newAttribute != null)
+ {
+ var oldAttributeValuesStr = _productAttributeParser.ParseValues(combination.AttributesXml, oldAttribute.Id);
+ foreach (var oldAttributeValueStr in oldAttributeValuesStr)
+ {
+ if (newAttribute.ShouldHaveValues())
+ {
+ //attribute values
+ int oldAttributeValue = int.Parse(oldAttributeValueStr);
+ if (associatedAttributeValues.ContainsKey(oldAttributeValue))
+ {
+ var newAttributeValue = _productAttributeService.GetProductAttributeValueById(associatedAttributeValues[oldAttributeValue]);
+ if (newAttributeValue != null)
+ {
+ newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml,
+ newAttribute, newAttributeValue.Id.ToString());
+ }
+ }
+ }
+ else
+ {
+ //just a text
+ newAttributesXml = _productAttributeParser.AddProductAttribute(newAttributesXml,
+ newAttribute, oldAttributeValueStr);
+ }
+ }
+ }
+ }
+ }
+ var combinationCopy = new ProductAttributeCombination
+ {
+ ProductId = productCopy.Id,
+ AttributesXml = newAttributesXml,
+ StockQuantity = combination.StockQuantity,
+ AllowOutOfStockOrders = combination.AllowOutOfStockOrders,
+ Sku = combination.Sku,
+ ManufacturerPartNumber = combination.ManufacturerPartNumber,
+ Gtin = combination.Gtin,
+ OverriddenPrice = combination.OverriddenPrice,
+ NotifyAdminForQuantityBelow = combination.NotifyAdminForQuantityBelow
+ };
+ _productAttributeService.InsertProductAttributeCombination(combinationCopy);
+
+ //quantity change history
+ _productService.AddStockQuantityHistoryEntry(productCopy, combination.StockQuantity, combination.StockQuantity,
+ message: string.Format(_localizationService.GetResource("Admin.StockQuantityHistory.Messages.CopyProduct"), product.Id), combinationId: combination.Id);
+ }
+
+ //tier prices
+ foreach (var tierPrice in product.TierPrices)
+ {
+ _productService.InsertTierPrice(
+ new TierPrice
+ {
+ ProductId = productCopy.Id,
+ StoreId = tierPrice.StoreId,
+ CustomerRoleId = tierPrice.CustomerRoleId,
+ Quantity = tierPrice.Quantity,
+ Price = tierPrice.Price,
+ StartDateTimeUtc = tierPrice.StartDateTimeUtc,
+ EndDateTimeUtc = tierPrice.EndDateTimeUtc
+ });
+ }
+
+ // product <-> discounts mapping
+ foreach (var discount in product.AppliedDiscounts)
+ {
+ productCopy.AppliedDiscounts.Add(discount);
+ _productService.UpdateProduct(productCopy);
+ }
+
+ //update "HasTierPrices" and "HasDiscountsApplied" properties
+ _productService.UpdateHasTierPricesProperty(productCopy);
+ _productService.UpdateHasDiscountsApplied(productCopy);
+
+ //associated products
+ if (copyAssociatedProducts)
+ {
+ var associatedProducts = _productService.GetAssociatedProducts(product.Id, showHidden: true);
+ foreach (var associatedProduct in associatedProducts)
+ {
+ var associatedProductCopy = CopyProduct(associatedProduct, string.Format("Copy of {0}", associatedProduct.Name),
+ isPublished, copyImages, false);
+ associatedProductCopy.ParentGroupedProductId = productCopy.Id;
+ _productService.UpdateProduct(productCopy);
+ }
+ }
+
+ return productCopy;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/IPriceCalculationService.cs b/src/Libraries/Nop.Services/Catalog/IPriceCalculationService.cs
index 9012932b96e..0a83e1b11ce 100644
--- a/src/Libraries/Nop.Services/Catalog/IPriceCalculationService.cs
+++ b/src/Libraries/Nop.Services/Catalog/IPriceCalculationService.cs
@@ -1,158 +1,181 @@
-using System;
-using System.Collections.Generic;
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Customers;
-using Nop.Core.Domain.Orders;
-using Nop.Services.Discounts;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Price calculation service
- ///
- public partial interface IPriceCalculationService
- {
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Final price
- decimal GetFinalPrice(Product product,
- Customer customer,
- decimal additionalCharge = decimal.Zero,
- bool includeDiscounts = true,
- int quantity = 1);
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Applied discount amount
- /// Applied discounts
- /// Final price
- decimal GetFinalPrice(Product product,
- Customer customer,
- decimal additionalCharge,
- bool includeDiscounts,
- int quantity,
- out decimal discountAmount,
- out List appliedDiscounts);
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Rental period start date (for rental products)
- /// Rental period end date (for rental products)
- /// Applied discount amount
- /// Applied discounts
- /// Final price
- decimal GetFinalPrice(Product product,
- Customer customer,
- decimal additionalCharge,
- bool includeDiscounts,
- int quantity,
- DateTime? rentalStartDate,
- DateTime? rentalEndDate,
- out decimal discountAmount,
- out List appliedDiscounts);
-
-
-
- ///
- /// Gets the shopping cart unit price (one item)
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Shopping cart unit price (one item)
- decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts = true);
- ///
- /// Gets the shopping cart unit price (one item)
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Applied discount amount
- /// Applied discounts
- /// Shopping cart unit price (one item)
- decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts,
- out decimal discountAmount,
- out List appliedDiscounts);
- ///
- /// Gets the shopping cart unit price (one item)
- ///
- /// Product
- /// Customer
- /// Shopping cart type
- /// Quantity
- /// Product atrributes (XML format)
- /// Customer entered price (if specified)
- /// Rental start date (null for not rental products)
- /// Rental end date (null for not rental products)
- /// A value indicating whether include discounts or not for price computation
- /// Applied discount amount
- /// Applied discounts
- /// Shopping cart unit price (one item)
- decimal GetUnitPrice(Product product,
- Customer customer,
- ShoppingCartType shoppingCartType,
- int quantity,
- string attributesXml,
- decimal customerEnteredPrice,
- DateTime? rentalStartDate, DateTime? rentalEndDate,
- bool includeDiscounts,
- out decimal discountAmount,
- out List appliedDiscounts);
- ///
- /// Gets the shopping cart item sub total
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Shopping cart item sub total
- decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts = true);
- ///
- /// Gets the shopping cart item sub total
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Applied discount amount
- /// Applied discounts
- /// Maximum discounted qty. Return not nullable value if discount cannot be applied to ALL items
- /// Shopping cart item sub total
- decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts,
- out decimal discountAmount,
- out List appliedDiscounts,
- out int? maximumDiscountQty);
-
- ///
- /// Gets the product cost (one item)
- ///
- /// Product
- /// Shopping cart item attributes in XML
- /// Product cost (one item)
- decimal GetProductCost(Product product, string attributesXml);
-
-
-
-
- ///
- /// Get a price adjustment of a product attribute value
- ///
- /// Product attribute value
- /// Price adjustment
- decimal GetProductAttributeValuePriceAdjustment(ProductAttributeValue value);
- }
-}
+using System;
+using System.Collections.Generic;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Customers;
+using Nop.Core.Domain.Orders;
+using Nop.Services.Discounts;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Price calculation service
+ ///
+ public partial interface IPriceCalculationService
+ {
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Final price
+ decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge = decimal.Zero,
+ bool includeDiscounts = true,
+ int quantity = 1);
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Applied discount amount
+ /// Applied discounts
+ /// Final price
+ decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge,
+ bool includeDiscounts,
+ int quantity,
+ out decimal discountAmount,
+ out List appliedDiscounts);
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Rental period start date (for rental products)
+ /// Rental period end date (for rental products)
+ /// Applied discount amount
+ /// Applied discounts
+ /// Final price
+ decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge,
+ bool includeDiscounts,
+ int quantity,
+ DateTime? rentalStartDate,
+ DateTime? rentalEndDate,
+ out decimal discountAmount,
+ out List appliedDiscounts);
+
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Rental period start date (for rental products)
+ /// Rental period end date (for rental products)
+ /// Applied discount amount
+ /// Applied discounts
+ /// Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations
+ /// Final price
+ decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge,
+ bool includeDiscounts,
+ int quantity,
+ DateTime? rentalStartDate,
+ DateTime? rentalEndDate,
+ out decimal discountAmount,
+ out List appliedDiscounts,
+ decimal? overriddenProductPrice);
+
+ ///
+ /// Gets the shopping cart unit price (one item)
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Shopping cart unit price (one item)
+ decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts = true);
+ ///
+ /// Gets the shopping cart unit price (one item)
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Applied discount amount
+ /// Applied discounts
+ /// Shopping cart unit price (one item)
+ decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts,
+ out decimal discountAmount,
+ out List appliedDiscounts);
+ ///
+ /// Gets the shopping cart unit price (one item)
+ ///
+ /// Product
+ /// Customer
+ /// Shopping cart type
+ /// Quantity
+ /// Product atrributes (XML format)
+ /// Customer entered price (if specified)
+ /// Rental start date (null for not rental products)
+ /// Rental end date (null for not rental products)
+ /// A value indicating whether include discounts or not for price computation
+ /// Applied discount amount
+ /// Applied discounts
+ /// Shopping cart unit price (one item)
+ decimal GetUnitPrice(Product product,
+ Customer customer,
+ ShoppingCartType shoppingCartType,
+ int quantity,
+ ref string attributesXml,
+ decimal customerEnteredPrice,
+ DateTime? rentalStartDate, DateTime? rentalEndDate,
+ bool includeDiscounts,
+ out decimal discountAmount,
+ out List appliedDiscounts);
+ ///
+ /// Gets the shopping cart item sub total
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Shopping cart item sub total
+ decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts = true);
+ ///
+ /// Gets the shopping cart item sub total
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Applied discount amount
+ /// Applied discounts
+ /// Maximum discounted qty. Return not nullable value if discount cannot be applied to ALL items
+ /// Shopping cart item sub total
+ decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts,
+ out decimal discountAmount,
+ out List appliedDiscounts,
+ out int? maximumDiscountQty);
+
+ ///
+ /// Gets the product cost (one item)
+ ///
+ /// Product
+ /// Shopping cart item attributes in XML
+ /// Product cost (one item)
+ decimal GetProductCost(Product product, string attributesXml);
+
+
+
+
+ ///
+ /// Get a price adjustment of a product attribute value
+ ///
+ /// Product attribute value
+ /// Price adjustment
+ decimal GetProductAttributeValuePriceAdjustment(ProductAttributeValue value);
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/IPriceFormatter.cs b/src/Libraries/Nop.Services/Catalog/IPriceFormatter.cs
index 1ff03d84f61..1ce377eb4d5 100644
--- a/src/Libraries/Nop.Services/Catalog/IPriceFormatter.cs
+++ b/src/Libraries/Nop.Services/Catalog/IPriceFormatter.cs
@@ -43,7 +43,7 @@ public partial interface IPriceFormatter
/// A value indicating whether to show tax suffix
/// Language
/// Price
- string FormatPrice(decimal price, bool showCurrency,
+ string FormatPrice(decimal price, bool showCurrency,
string currencyCode, bool showTax, Language language);
///
@@ -67,7 +67,7 @@ string FormatPrice(decimal price, bool showCurrency,
/// Language
/// A value indicating whether price includes tax
/// Price
- string FormatPrice(decimal price, bool showCurrency,
+ string FormatPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax);
///
@@ -80,7 +80,7 @@ string FormatPrice(decimal price, bool showCurrency,
/// A value indicating whether price includes tax
/// A value indicating whether to show tax suffix
/// Price
- string FormatPrice(decimal price, bool showCurrency,
+ string FormatPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax);
///
@@ -110,7 +110,7 @@ string FormatPrice(decimal price, bool showCurrency,
/// Language
/// A value indicating whether price includes tax
/// Price
- string FormatShippingPrice(decimal price, bool showCurrency,
+ string FormatShippingPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax);
///
/// Formats the shipping price
@@ -122,9 +122,9 @@ string FormatShippingPrice(decimal price, bool showCurrency,
/// A value indicating whether price includes tax
/// A value indicating whether to show tax suffix
/// Price
- string FormatShippingPrice(decimal price, bool showCurrency,
+ string FormatShippingPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax);
-
+
///
/// Formats the shipping price
///
@@ -134,7 +134,7 @@ string FormatShippingPrice(decimal price, bool showCurrency,
/// Language
/// A value indicating whether price includes tax
/// Price
- string FormatShippingPrice(decimal price, bool showCurrency,
+ string FormatShippingPrice(decimal price, bool showCurrency,
string currencyCode, Language language, bool priceIncludesTax);
@@ -169,7 +169,7 @@ string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
/// A value indicating whether price includes tax
/// A value indicating whether to show tax suffix
/// Price
- string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
+ string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax);
///
@@ -181,7 +181,7 @@ string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
/// Language
/// A value indicating whether price includes tax
/// Price
- string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
+ string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
string currencyCode, Language language, bool priceIncludesTax);
@@ -192,5 +192,24 @@ string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
/// Tax rate
/// Formatted tax rate
string FormatTaxRate(decimal taxRate);
+
+ ///
+ /// Adds tax suffix to text
+ ///
+ /// Text to format
+ /// Language
+ /// A value indicating whether price includes tax
+ ///
+ string FormatTaxString(string text, Language language, bool priceIncludesTax);
+
+ ///
+ /// Adds tax suffix to text
+ ///
+ /// Text to format
+ /// Language
+ /// A value indicating whether price includes tax
+ /// Optional. A value indicating whether to show tax suffix.
+ ///
+ string FormatTaxString(string text, Language language, bool priceIncludesTax, bool showTax);
}
}
diff --git a/src/Libraries/Nop.Services/Catalog/IProductAttributeFormatter.cs b/src/Libraries/Nop.Services/Catalog/IProductAttributeFormatter.cs
index 25996258a40..86839fbf5aa 100644
--- a/src/Libraries/Nop.Services/Catalog/IProductAttributeFormatter.cs
+++ b/src/Libraries/Nop.Services/Catalog/IProductAttributeFormatter.cs
@@ -1,37 +1,39 @@
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Customers;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Product attribute formatter interface
- ///
- public partial interface IProductAttributeFormatter
- {
- ///
- /// Formats attributes
- ///
- /// Product
- /// Attributes in XML format
- /// Attributes
- string FormatAttributes(Product product, string attributesXml);
-
- ///
- /// Formats attributes
- ///
- /// Product
- /// Attributes in XML format
- /// Customer
- /// Serapator
- /// A value indicating whether to encode (HTML) values
- /// A value indicating whether to render prices
- /// A value indicating whether to render product attributes
- /// A value indicating whether to render gift card attributes
- /// A value indicating whether to HTML hyperink tags could be rendered (if required)
- /// Attributes
- string FormatAttributes(Product product, string attributesXml,
- Customer customer, string serapator = "
", bool htmlEncode = true, bool renderPrices = true,
- bool renderProductAttributes = true, bool renderGiftCardAttributes = true,
- bool allowHyperlinks = true);
- }
-}
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Customers;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Product attribute formatter interface
+ ///
+ public partial interface IProductAttributeFormatter
+ {
+ ///
+ /// Formats attributes
+ ///
+ /// Product
+ /// Attributes in XML format
+ /// Attributes
+ string FormatAttributes(Product product, string attributesXml);
+
+ ///
+ /// Formats attributes
+ ///
+ /// Product
+ /// Attributes in XML format
+ /// Customer
+ /// Serapator
+ /// A value indicating whether to encode (HTML) values
+ /// A value indicating whether to render prices
+ /// A value indicating whether to render product attributes
+ /// A value indicating whether to render gift card attributes
+ /// A value indicating whether to HTML hyperink tags could be rendered (if required)
+ /// A value indicating if attribute VAT should be rendered with price. Null is default, i.e. don't render
+ /// Attributes
+ string FormatAttributes(Product product, string attributesXml,
+ Customer customer, string serapator = "
", bool htmlEncode = true, bool renderPrices = true,
+ bool renderProductAttributes = true, bool renderGiftCardAttributes = true,
+ bool allowHyperlinks = true,
+ decimal? subTotal = null);
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/IProductAttributeParser.cs b/src/Libraries/Nop.Services/Catalog/IProductAttributeParser.cs
index e68aa2553de..037409e56c8 100644
--- a/src/Libraries/Nop.Services/Catalog/IProductAttributeParser.cs
+++ b/src/Libraries/Nop.Services/Catalog/IProductAttributeParser.cs
@@ -1,122 +1,147 @@
-using System.Collections.Generic;
-using Nop.Core.Domain.Catalog;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Product attribute parser interface
- ///
- public partial interface IProductAttributeParser
- {
- #region Product attributes
-
- ///
- /// Gets selected product attribute mappings
- ///
- /// Attributes in XML format
- /// Selected product attribute mappings
- IList ParseProductAttributeMappings(string attributesXml);
-
- ///
- /// Get product attribute values
- ///
- /// Attributes in XML format
- /// Product attribute mapping identifier; pass 0 to load all values
- /// Product attribute values
- IList ParseProductAttributeValues(string attributesXml, int productAttributeMappingId = 0);
-
- ///
- /// Gets selected product attribute values
- ///
- /// Attributes in XML format
- /// Product attribute mapping identifier
- /// Product attribute values
- IList ParseValues(string attributesXml, int productAttributeMappingId);
-
- ///
- /// Adds an attribute
- ///
- /// Attributes in XML format
- /// Product attribute mapping
- /// Value
- /// Quantity (used with AttributeValueType.AssociatedToProduct to specify the quantity entered by the customer)
- /// Updated result (XML format)
- string AddProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping, string value, int? quantity = null);
-
- ///
- /// Remove an attribute
- ///
- /// Attributes in XML format
- /// Product attribute mapping
- /// Updated result (XML format)
- string RemoveProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping);
-
- ///
- /// Are attributes equal
- ///
- /// The attributes of the first product
- /// The attributes of the second product
- /// A value indicating whether we should ignore non-combinable attributes
- /// A value indicating whether we should ignore the quantity of attribute value entered by the customer
- /// Result
- bool AreProductAttributesEqual(string attributesXml1, string attributesXml2, bool ignoreNonCombinableAttributes, bool ignoreQuantity = true);
-
- ///
- /// Check whether condition of some attribute is met (if specified). Return "null" if not condition is specified
- ///
- /// Product attribute
- /// Selected attributes (XML format)
- /// Result
- bool? IsConditionMet(ProductAttributeMapping pam, string selectedAttributesXml);
-
- ///
- /// Finds a product attribute combination by attributes stored in XML
- ///
- /// Product
- /// Attributes in XML format
- /// A value indicating whether we should ignore non-combinable attributes
- /// Found product attribute combination
- ProductAttributeCombination FindProductAttributeCombination(Product product,
- string attributesXml, bool ignoreNonCombinableAttributes = true);
-
- ///
- /// Generate all combinations
- ///
- /// Product
- /// A value indicating whether we should ignore non-combinable attributes
- /// Attribute combinations in XML format
- IList GenerateAllCombinations(Product product, bool ignoreNonCombinableAttributes = false);
-
- #endregion
-
- #region Gift card attributes
-
- ///
- /// Add gift card attrbibutes
- ///
- /// Attributes in XML format
- /// Recipient name
- /// Recipient email
- /// Sender name
- /// Sender email
- /// Message
- /// Attributes
- string AddGiftCardAttribute(string attributesXml, string recipientName,
- string recipientEmail, string senderName, string senderEmail, string giftCardMessage);
-
- ///
- /// Get gift card attrbibutes
- ///
- /// Attributes in XML format
- /// Recipient name
- /// Recipient email
- /// Sender name
- /// Sender email
- /// Message
- void GetGiftCardAttribute(string attributesXml, out string recipientName,
- out string recipientEmail, out string senderName,
- out string senderEmail, out string giftCardMessage);
-
- #endregion
- }
-}
+using System.Collections.Generic;
+using Nop.Core.Domain.Catalog;
+using Nop.Services.Tax;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Product attribute parser interface
+ ///
+ public partial interface IProductAttributeParser
+ {
+ #region Product attributes
+
+ ///
+ /// Gets selected product attribute mappings
+ ///
+ /// Attributes in XML format
+ /// Selected product attribute mappings
+ IList ParseProductAttributeMappings(string attributesXml);
+
+ ///
+ /// Get product attribute values
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping identifier; pass 0 to load all values
+ /// Product attribute values
+ IList ParseProductAttributeValues(string attributesXml, int productAttributeMappingId = 0);
+
+ ///
+ /// Gets selected product attribute values
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping identifier
+ /// Product attribute values
+ IList ParseValues(string attributesXml, int productAttributeMappingId);
+
+ ///
+ /// Adds an attribute
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping
+ /// Value
+ /// Quantity (used with AttributeValueType.AssociatedToProduct to specify the quantity entered by the customer)
+ /// Updated result (XML format)
+ string AddProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping, string value, int? quantity = null);
+
+ ///
+ /// Remove an attribute
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping
+ /// Updated result (XML format)
+ string RemoveProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping);
+
+ ///
+ /// Are attributes equal
+ ///
+ /// The attributes of the first product
+ /// The attributes of the second product
+ /// A value indicating whether we should ignore non-combinable attributes
+ /// A value indicating whether we should ignore the quantity of attribute value entered by the customer
+ /// Result
+ bool AreProductAttributesEqual(string attributesXml1, string attributesXml2, bool ignoreNonCombinableAttributes, bool ignoreQuantity = true);
+
+ ///
+ /// Check whether condition of some attribute is met (if specified). Return "null" if not condition is specified
+ ///
+ /// Product attribute
+ /// Selected attributes (XML format)
+ /// Result
+ bool? IsConditionMet(ProductAttributeMapping pam, string selectedAttributesXml);
+
+ ///
+ /// Finds a product attribute combination by attributes stored in XML
+ ///
+ /// Product
+ /// Attributes in XML format
+ /// A value indicating whether we should ignore non-combinable attributes
+ /// Found product attribute combination
+ ProductAttributeCombination FindProductAttributeCombination(Product product,
+ string attributesXml, bool ignoreNonCombinableAttributes = true);
+
+ ///
+ /// Generate all combinations
+ ///
+ /// Product
+ /// A value indicating whether we should ignore non-combinable attributes
+ /// Attribute combinations in XML format
+ IList GenerateAllCombinations(Product product, bool ignoreNonCombinableAttributes = false);
+
+ #endregion
+
+ #region taxAttribute
+ ///
+ /// Adds tax subdivision to existing attributesXml
+ ///
+ /// Attributes in XML format
+ /// Set Product tax subdivision
+ /// Updated result (XML format)
+ string AddTaxAttribute(string attributesXml, TaxSummary taxSummary);
+
+ ///
+ /// Parse ProductAttributesTax
+ ///
+ /// Attributes in XML format
+ /// SortedDictionary with taxRate and taxRateWeight
+ SortedDictionary ParseTaxAttribute(string attributesXml);
+
+ ///
+ /// Check if attributesXml contains tax attribute information
+ ///
+ /// Attributes in XML format
+ /// A boolean value indicating if tax attribute info is present
+ bool hasTaxInfoInAttributeXML (string attributesXml);
+ #endregion
+
+ #region Gift card attributes
+
+ ///
+ /// Add gift card attrbibutes
+ ///
+ /// Attributes in XML format
+ /// Recipient name
+ /// Recipient email
+ /// Sender name
+ /// Sender email
+ /// Message
+ /// Attributes
+ string AddGiftCardAttribute(string attributesXml, string recipientName,
+ string recipientEmail, string senderName, string senderEmail, string giftCardMessage);
+
+ ///
+ /// Get gift card attrbibutes
+ ///
+ /// Attributes in XML format
+ /// Recipient name
+ /// Recipient email
+ /// Sender name
+ /// Sender email
+ /// Message
+ void GetGiftCardAttribute(string attributesXml, out string recipientName,
+ out string recipientEmail, out string senderName,
+ out string senderEmail, out string giftCardMessage);
+
+ #endregion
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/PriceCalculationService.cs b/src/Libraries/Nop.Services/Catalog/PriceCalculationService.cs
index 4169a8fe86d..d2aa285daa9 100644
--- a/src/Libraries/Nop.Services/Catalog/PriceCalculationService.cs
+++ b/src/Libraries/Nop.Services/Catalog/PriceCalculationService.cs
@@ -1,749 +1,804 @@
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.Linq;
-using Nop.Core;
-using Nop.Core.Caching;
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Customers;
-using Nop.Core.Domain.Discounts;
-using Nop.Core.Domain.Orders;
-using Nop.Services.Catalog.Cache;
-using Nop.Services.Customers;
-using Nop.Services.Discounts;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Price calculation service
- ///
- public partial class PriceCalculationService : IPriceCalculationService
- {
- #region Fields
-
- private readonly IWorkContext _workContext;
- private readonly IStoreContext _storeContext;
- private readonly IDiscountService _discountService;
- private readonly ICategoryService _categoryService;
- private readonly IManufacturerService _manufacturerService;
- private readonly IProductAttributeParser _productAttributeParser;
- private readonly IProductService _productService;
- private readonly ICacheManager _cacheManager;
- private readonly ShoppingCartSettings _shoppingCartSettings;
- private readonly CatalogSettings _catalogSettings;
-
- #endregion
-
- #region Ctor
-
- public PriceCalculationService(IWorkContext workContext,
- IStoreContext storeContext,
- IDiscountService discountService,
- ICategoryService categoryService,
- IManufacturerService manufacturerService,
- IProductAttributeParser productAttributeParser,
- IProductService productService,
- ICacheManager cacheManager,
- ShoppingCartSettings shoppingCartSettings,
- CatalogSettings catalogSettings)
- {
- this._workContext = workContext;
- this._storeContext = storeContext;
- this._discountService = discountService;
- this._categoryService = categoryService;
- this._manufacturerService = manufacturerService;
- this._productAttributeParser = productAttributeParser;
- this._productService = productService;
- this._cacheManager = cacheManager;
- this._shoppingCartSettings = shoppingCartSettings;
- this._catalogSettings = catalogSettings;
- }
-
- #endregion
-
- #region Nested classes
-
- [Serializable]
- protected class ProductPriceForCaching
- {
- public ProductPriceForCaching()
- {
- this.AppliedDiscounts = new List();
- }
-
- public decimal Price { get; set; }
- public decimal AppliedDiscountAmount { get; set; }
- public List AppliedDiscounts { get; set; }
- }
- #endregion
-
- #region Utilities
-
- ///
- /// Gets allowed discounts applied to product
- ///
- /// Product
- /// Customer
- /// Discounts
- protected virtual IList GetAllowedDiscountsAppliedToProduct(Product product, Customer customer)
- {
- var allowedDiscounts = new List();
- if (_catalogSettings.IgnoreDiscounts)
- return allowedDiscounts;
-
- if (product.HasDiscountsApplied)
- {
- //we use this property ("HasDiscountsApplied") for performance optimization to avoid unnecessary database calls
- foreach (var discount in product.AppliedDiscounts)
- {
- if (_discountService.ValidateDiscount(discount, customer).IsValid &&
- discount.DiscountType == DiscountType.AssignedToSkus)
- allowedDiscounts.Add(discount.MapDiscount());
- }
- }
-
- return allowedDiscounts;
- }
-
- ///
- /// Gets allowed discounts applied to categories
- ///
- /// Product
- /// Customer
- /// Discounts
- protected virtual IList GetAllowedDiscountsAppliedToCategories(Product product, Customer customer)
- {
- var allowedDiscounts = new List();
- if (_catalogSettings.IgnoreDiscounts)
- return allowedDiscounts;
-
- //load cached discount models (performance optimization)
- foreach (var discount in _discountService.GetAllDiscountsForCaching(DiscountType.AssignedToCategories))
- {
- //load identifier of categories with this discount applied to
- var discountCategoryIds = _discountService.GetAppliedCategoryIds(discount, customer);
-
- //compare with categories of this product
- var productCategoryIds = new List();
- if (discountCategoryIds.Any())
- {
- //load identifier of categories of this product
- var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_CATEGORY_IDS_MODEL_KEY,
- product.Id,
- string.Join(",", customer.GetCustomerRoleIds()),
- _storeContext.CurrentStore.Id);
- productCategoryIds = _cacheManager.Get(cacheKey, () =>
- _categoryService
- .GetProductCategoriesByProductId(product.Id)
- .Select(x => x.CategoryId)
- .ToList());
- }
-
- foreach (var categoryId in productCategoryIds)
- {
- if (discountCategoryIds.Contains(categoryId))
- {
- if (_discountService.ValidateDiscount(discount, customer).IsValid &&
- !allowedDiscounts.ContainsDiscount(discount))
- allowedDiscounts.Add(discount);
- }
- }
- }
-
- return allowedDiscounts;
- }
-
- ///
- /// Gets allowed discounts applied to manufacturers
- ///
- /// Product
- /// Customer
- /// Discounts
- protected virtual IList GetAllowedDiscountsAppliedToManufacturers(Product product, Customer customer)
- {
- var allowedDiscounts = new List();
- if (_catalogSettings.IgnoreDiscounts)
- return allowedDiscounts;
-
- foreach (var discount in _discountService.GetAllDiscountsForCaching(DiscountType.AssignedToManufacturers))
- {
- //load identifier of manufacturers with this discount applied to
- var discountManufacturerIds = _discountService.GetAppliedManufacturerIds(discount, customer);
-
- //compare with manufacturers of this product
- var productManufacturerIds = new List();
- if (discountManufacturerIds.Any())
- {
- //load identifier of manufacturers of this product
- var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_MANUFACTURER_IDS_MODEL_KEY,
- product.Id,
- string.Join(",", customer.GetCustomerRoleIds()),
- _storeContext.CurrentStore.Id);
- productManufacturerIds = _cacheManager.Get(cacheKey, () =>
- _manufacturerService
- .GetProductManufacturersByProductId(product.Id)
- .Select(x => x.ManufacturerId)
- .ToList());
- }
-
- foreach (var manufacturerId in productManufacturerIds)
- {
- if (discountManufacturerIds.Contains(manufacturerId))
- {
- if (_discountService.ValidateDiscount(discount, customer).IsValid &&
- !allowedDiscounts.ContainsDiscount(discount))
- allowedDiscounts.Add(discount);
- }
- }
- }
-
- return allowedDiscounts;
- }
-
- ///
- /// Gets allowed discounts
- ///
- /// Product
- /// Customer
- /// Discounts
- protected virtual IList GetAllowedDiscounts(Product product, Customer customer)
- {
- var allowedDiscounts = new List();
- if (_catalogSettings.IgnoreDiscounts)
- return allowedDiscounts;
-
- //discounts applied to products
- foreach (var discount in GetAllowedDiscountsAppliedToProduct(product, customer))
- if (!allowedDiscounts.ContainsDiscount(discount))
- allowedDiscounts.Add(discount);
-
- //discounts applied to categories
- foreach (var discount in GetAllowedDiscountsAppliedToCategories(product, customer))
- if (!allowedDiscounts.ContainsDiscount(discount))
- allowedDiscounts.Add(discount);
-
- //discounts applied to manufacturers
- foreach (var discount in GetAllowedDiscountsAppliedToManufacturers(product, customer))
- if (!allowedDiscounts.ContainsDiscount(discount))
- allowedDiscounts.Add(discount);
-
- return allowedDiscounts;
- }
-
- ///
- /// Gets discount amount
- ///
- /// Product
- /// The customer
- /// Already calculated product price without discount
- /// Applied discounts
- /// Discount amount
- protected virtual decimal GetDiscountAmount(Product product,
- Customer customer,
- decimal productPriceWithoutDiscount,
- out List appliedDiscounts)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- appliedDiscounts = null;
- decimal appliedDiscountAmount = decimal.Zero;
-
- //we don't apply discounts to products with price entered by a customer
- if (product.CustomerEntersPrice)
- return appliedDiscountAmount;
-
- //discounts are disabled
- if (_catalogSettings.IgnoreDiscounts)
- return appliedDiscountAmount;
-
- var allowedDiscounts = GetAllowedDiscounts(product, customer);
-
- //no discounts
- if (!allowedDiscounts.Any())
- return appliedDiscountAmount;
-
- appliedDiscounts = allowedDiscounts.GetPreferredDiscount(productPriceWithoutDiscount, out appliedDiscountAmount);
- return appliedDiscountAmount;
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Final price
- public virtual decimal GetFinalPrice(Product product,
- Customer customer,
- decimal additionalCharge = decimal.Zero,
- bool includeDiscounts = true,
- int quantity = 1)
- {
- decimal discountAmount;
- List appliedDiscounts;
- return GetFinalPrice(product, customer, additionalCharge, includeDiscounts,
- quantity, out discountAmount, out appliedDiscounts);
- }
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Applied discount amount
- /// Applied discounts
- /// Final price
- public virtual decimal GetFinalPrice(Product product,
- Customer customer,
- decimal additionalCharge,
- bool includeDiscounts,
- int quantity,
- out decimal discountAmount,
- out List appliedDiscounts)
- {
- return GetFinalPrice(product, customer,
- additionalCharge, includeDiscounts, quantity,
- null, null,
- out discountAmount, out appliedDiscounts);
- }
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Rental period start date (for rental products)
- /// Rental period end date (for rental products)
- /// Applied discount amount
- /// Applied discounts
- /// Final price
- public virtual decimal GetFinalPrice(Product product,
- Customer customer,
- decimal additionalCharge,
- bool includeDiscounts,
- int quantity,
- DateTime? rentalStartDate,
- DateTime? rentalEndDate,
- out decimal discountAmount,
- out List appliedDiscounts)
- {
- return GetFinalPrice(product, customer, null, additionalCharge, includeDiscounts, quantity,
- rentalStartDate, rentalEndDate, out discountAmount, out appliedDiscounts);
- }
- ///
- /// Gets the final price
- ///
- /// Product
- /// The customer
- /// Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations
- /// Additional charge
- /// A value indicating whether include discounts or not for final price computation
- /// Shopping cart item quantity
- /// Rental period start date (for rental products)
- /// Rental period end date (for rental products)
- /// Applied discount amount
- /// Applied discounts
- /// Final price
- public virtual decimal GetFinalPrice(Product product,
- Customer customer,
- decimal? overriddenProductPrice,
- decimal additionalCharge,
- bool includeDiscounts,
- int quantity,
- DateTime? rentalStartDate,
- DateTime? rentalEndDate,
- out decimal discountAmount,
- out List appliedDiscounts)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- discountAmount = decimal.Zero;
- appliedDiscounts = new List();
-
- var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
- product.Id,
- overriddenProductPrice.HasValue ? overriddenProductPrice.Value.ToString(CultureInfo.InvariantCulture) : null,
- additionalCharge.ToString(CultureInfo.InvariantCulture),
- includeDiscounts,
- quantity,
- string.Join(",", customer.GetCustomerRoleIds()),
- _storeContext.CurrentStore.Id);
- var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;
- //we do not cache price for rental products
- //otherwise, it can cause memory leaks (to store all possible date period combinations)
- if (product.IsRental)
- cacheTime = 0;
- var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
- {
- var result = new ProductPriceForCaching();
-
- //initial price
- decimal price = overriddenProductPrice.HasValue ? overriddenProductPrice.Value : product.Price;
-
- //tier prices
- var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);
- if (tierPrice != null)
- price = tierPrice.Price;
-
- //additional charge
- price = price + additionalCharge;
-
- //rental products
- if (product.IsRental)
- if (rentalStartDate.HasValue && rentalEndDate.HasValue)
- price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);
-
- if (includeDiscounts)
- {
- //discount
- List tmpAppliedDiscounts;
- decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscounts);
- price = price - tmpDiscountAmount;
-
- if (tmpAppliedDiscounts != null)
- {
- result.AppliedDiscounts = tmpAppliedDiscounts;
- result.AppliedDiscountAmount = tmpDiscountAmount;
- }
- }
-
- if (price < decimal.Zero)
- price = decimal.Zero;
-
- result.Price = price;
- return result;
- });
-
- if (includeDiscounts)
- {
- if (cachedPrice.AppliedDiscounts.Any())
- {
- appliedDiscounts.AddRange(cachedPrice.AppliedDiscounts);
- discountAmount = cachedPrice.AppliedDiscountAmount;
- }
- }
-
- return cachedPrice.Price;
- }
-
-
-
- ///
- /// Gets the shopping cart unit price (one item)
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Shopping cart unit price (one item)
- public virtual decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts = true)
- {
- decimal discountAmount;
- List appliedDiscounts;
- return GetUnitPrice(shoppingCartItem, includeDiscounts,
- out discountAmount, out appliedDiscounts);
- }
- ///
- /// Gets the shopping cart unit price (one item)
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Applied discount amount
- /// Applied discounts
- /// Shopping cart unit price (one item)
- public virtual decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts,
- out decimal discountAmount,
- out List appliedDiscounts)
- {
- if (shoppingCartItem == null)
- throw new ArgumentNullException("shoppingCartItem");
-
- return GetUnitPrice(shoppingCartItem.Product,
- shoppingCartItem.Customer,
- shoppingCartItem.ShoppingCartType,
- shoppingCartItem.Quantity,
- shoppingCartItem.AttributesXml,
- shoppingCartItem.CustomerEnteredPrice,
- shoppingCartItem.RentalStartDateUtc,
- shoppingCartItem.RentalEndDateUtc,
- includeDiscounts,
- out discountAmount,
- out appliedDiscounts);
- }
- ///
- /// Gets the shopping cart unit price (one item)
- ///
- /// Product
- /// Customer
- /// Shopping cart type
- /// Quantity
- /// Product atrributes (XML format)
- /// Customer entered price (if specified)
- /// Rental start date (null for not rental products)
- /// Rental end date (null for not rental products)
- /// A value indicating whether include discounts or not for price computation
- /// Applied discount amount
- /// Applied discounts
- /// Shopping cart unit price (one item)
- public virtual decimal GetUnitPrice(Product product,
- Customer customer,
- ShoppingCartType shoppingCartType,
- int quantity,
- string attributesXml,
- decimal customerEnteredPrice,
- DateTime? rentalStartDate, DateTime? rentalEndDate,
- bool includeDiscounts,
- out decimal discountAmount,
- out List appliedDiscounts)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- if (customer == null)
- throw new ArgumentNullException("customer");
-
- discountAmount = decimal.Zero;
- appliedDiscounts = new List();
-
- decimal finalPrice;
-
- var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml);
- if (combination != null && combination.OverriddenPrice.HasValue)
- {
- finalPrice = GetFinalPrice(product,
- customer,
- combination.OverriddenPrice.Value,
- decimal.Zero,
- includeDiscounts,
- quantity,
- product.IsRental ? rentalStartDate : null,
- product.IsRental ? rentalEndDate : null,
- out discountAmount, out appliedDiscounts);
- }
- else
- {
- //summarize price of all attributes
- decimal attributesTotalPrice = decimal.Zero;
- var attributeValues = _productAttributeParser.ParseProductAttributeValues(attributesXml);
- if (attributeValues != null)
- {
- foreach (var attributeValue in attributeValues)
- {
- attributesTotalPrice += GetProductAttributeValuePriceAdjustment(attributeValue);
- }
- }
-
- //get price of a product (with previously calculated price of all attributes)
- if (product.CustomerEntersPrice)
- {
- finalPrice = customerEnteredPrice;
- }
- else
- {
- int qty;
- if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems)
- {
- //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records
- //so let's find how many of the current products are in the cart
- qty = customer.ShoppingCartItems
- .Where(x => x.ProductId == product.Id)
- .Where(x => x.ShoppingCartType == shoppingCartType)
- .Sum(x => x.Quantity);
- if (qty == 0)
- {
- qty = quantity;
- }
- }
- else
- {
- qty = quantity;
- }
- finalPrice = GetFinalPrice(product,
- customer,
- attributesTotalPrice,
- includeDiscounts,
- qty,
- product.IsRental ? rentalStartDate : null,
- product.IsRental ? rentalEndDate : null,
- out discountAmount, out appliedDiscounts);
- }
- }
-
- //rounding
- if (_shoppingCartSettings.RoundPricesDuringCalculation)
- finalPrice = RoundingHelper.RoundPrice(finalPrice);
-
- return finalPrice;
- }
- ///
- /// Gets the shopping cart item sub total
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Shopping cart item sub total
- public virtual decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts = true)
- {
- decimal discountAmount;
- List appliedDiscounts;
- int? maximumDiscountQty;
- return GetSubTotal(shoppingCartItem, includeDiscounts, out discountAmount, out appliedDiscounts, out maximumDiscountQty);
- }
- ///
- /// Gets the shopping cart item sub total
- ///
- /// The shopping cart item
- /// A value indicating whether include discounts or not for price computation
- /// Applied discount amount
- /// Applied discounts
- /// Maximum discounted qty. Return not nullable value if discount cannot be applied to ALL items
- /// Shopping cart item sub total
- public virtual decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
- bool includeDiscounts,
- out decimal discountAmount,
- out List appliedDiscounts,
- out int? maximumDiscountQty)
- {
- if (shoppingCartItem == null)
- throw new ArgumentNullException("shoppingCartItem");
-
- decimal subTotal;
- maximumDiscountQty = null;
-
- //unit price
- var unitPrice = GetUnitPrice(shoppingCartItem, includeDiscounts,
- out discountAmount, out appliedDiscounts);
-
- //discount
- if (appliedDiscounts.Any())
- {
- //we can properly use "MaximumDiscountedQuantity" property only for one discount (not cumulative ones)
- DiscountForCaching oneAndOnlyDiscount = null;
- if (appliedDiscounts.Count == 1)
- oneAndOnlyDiscount = appliedDiscounts.First();
-
- if (oneAndOnlyDiscount != null &&
- oneAndOnlyDiscount.MaximumDiscountedQuantity.HasValue &&
- shoppingCartItem.Quantity > oneAndOnlyDiscount.MaximumDiscountedQuantity.Value)
- {
- maximumDiscountQty = oneAndOnlyDiscount.MaximumDiscountedQuantity.Value;
- //we cannot apply discount for all shopping cart items
- var discountedQuantity = oneAndOnlyDiscount.MaximumDiscountedQuantity.Value;
- var discountedSubTotal = unitPrice * discountedQuantity;
- discountAmount = discountAmount * discountedQuantity;
-
- var notDiscountedQuantity = shoppingCartItem.Quantity - discountedQuantity;
- var notDiscountedUnitPrice = GetUnitPrice(shoppingCartItem, false);
- var notDiscountedSubTotal = notDiscountedUnitPrice*notDiscountedQuantity;
-
- subTotal = discountedSubTotal + notDiscountedSubTotal;
- }
- else
- {
- //discount is applied to all items (quantity)
- //calculate discount amount for all items
- discountAmount = discountAmount * shoppingCartItem.Quantity;
-
- subTotal = unitPrice * shoppingCartItem.Quantity;
- }
- }
- else
- {
- subTotal = unitPrice * shoppingCartItem.Quantity;
- }
- return subTotal;
- }
-
-
- ///
- /// Gets the product cost (one item)
- ///
- /// Product
- /// Shopping cart item attributes in XML
- /// Product cost (one item)
- public virtual decimal GetProductCost(Product product, string attributesXml)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- decimal cost = product.ProductCost;
- var attributeValues = _productAttributeParser.ParseProductAttributeValues(attributesXml);
- foreach (var attributeValue in attributeValues)
- {
- switch (attributeValue.AttributeValueType)
- {
- case AttributeValueType.Simple:
- {
- //simple attribute
- cost += attributeValue.Cost;
- }
- break;
- case AttributeValueType.AssociatedToProduct:
- {
- //bundled product
- var associatedProduct = _productService.GetProductById(attributeValue.AssociatedProductId);
- if (associatedProduct != null)
- cost += associatedProduct.ProductCost * attributeValue.Quantity;
- }
- break;
- default:
- break;
- }
- }
-
- return cost;
- }
-
-
-
- ///
- /// Get a price adjustment of a product attribute value
- ///
- /// Product attribute value
- /// Price adjustment
- public virtual decimal GetProductAttributeValuePriceAdjustment(ProductAttributeValue value)
- {
- if (value == null)
- throw new ArgumentNullException("value");
-
- var adjustment = decimal.Zero;
- switch (value.AttributeValueType)
- {
- case AttributeValueType.Simple:
- {
- //simple attribute
- adjustment = value.PriceAdjustment;
- }
- break;
- case AttributeValueType.AssociatedToProduct:
- {
- //bundled product
- var associatedProduct = _productService.GetProductById(value.AssociatedProductId);
- if (associatedProduct != null)
- {
- adjustment = GetFinalPrice(associatedProduct, _workContext.CurrentCustomer, includeDiscounts: true) * value.Quantity;
- }
- }
- break;
- default:
- break;
- }
-
- return adjustment;
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.Linq;
+using Nop.Core;
+using Nop.Core.Caching;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Customers;
+using Nop.Core.Domain.Discounts;
+using Nop.Core.Domain.Orders;
+using Nop.Services.Catalog.Cache;
+using Nop.Services.Customers;
+using Nop.Services.Discounts;
+using Nop.Services.Tax;
+using Nop.Core.Domain.Tax;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Price calculation service
+ ///
+ public partial class PriceCalculationService : IPriceCalculationService
+ {
+ #region Fields
+
+ private readonly IWorkContext _workContext;
+ private readonly IStoreContext _storeContext;
+ private readonly IDiscountService _discountService;
+ private readonly ICategoryService _categoryService;
+ private readonly IManufacturerService _manufacturerService;
+ private readonly IProductAttributeParser _productAttributeParser;
+ private readonly IProductService _productService;
+ private readonly ICacheManager _cacheManager;
+ private readonly ShoppingCartSettings _shoppingCartSettings;
+ private readonly CatalogSettings _catalogSettings;
+ private readonly ITaxService _taxService;
+
+ #endregion
+
+ #region Ctor
+
+ public PriceCalculationService(IWorkContext workContext,
+ IStoreContext storeContext,
+ IDiscountService discountService,
+ ICategoryService categoryService,
+ IManufacturerService manufacturerService,
+ IProductAttributeParser productAttributeParser,
+ IProductService productService,
+ ICacheManager cacheManager,
+ ShoppingCartSettings shoppingCartSettings,
+ CatalogSettings catalogSettings,
+ ITaxService taxService)
+ {
+ this._workContext = workContext;
+ this._storeContext = storeContext;
+ this._discountService = discountService;
+ this._categoryService = categoryService;
+ this._manufacturerService = manufacturerService;
+ this._productAttributeParser = productAttributeParser;
+ this._productService = productService;
+ this._cacheManager = cacheManager;
+ this._shoppingCartSettings = shoppingCartSettings;
+ this._catalogSettings = catalogSettings;
+ this._taxService = taxService;
+ }
+
+ #endregion
+
+ #region Nested classes
+
+ [Serializable]
+ protected class ProductPriceForCaching
+ {
+ public ProductPriceForCaching()
+ {
+ this.AppliedDiscounts = new List();
+ }
+
+ public decimal Price { get; set; }
+ public decimal AppliedDiscountAmount { get; set; }
+ public List AppliedDiscounts { get; set; }
+ }
+ #endregion
+
+ #region Utilities
+
+ ///
+ /// Gets allowed discounts applied to product
+ ///
+ /// Product
+ /// Customer
+ /// Discounts
+ protected virtual IList GetAllowedDiscountsAppliedToProduct(Product product, Customer customer)
+ {
+ var allowedDiscounts = new List();
+ if (_catalogSettings.IgnoreDiscounts)
+ return allowedDiscounts;
+
+ if (product.HasDiscountsApplied)
+ {
+ //we use this property ("HasDiscountsApplied") for performance optimization to avoid unnecessary database calls
+ foreach (var discount in product.AppliedDiscounts)
+ {
+ if (_discountService.ValidateDiscount(discount, customer).IsValid &&
+ discount.DiscountType == DiscountType.AssignedToSkus)
+ allowedDiscounts.Add(discount.MapDiscount());
+ }
+ }
+
+ return allowedDiscounts;
+ }
+
+ ///
+ /// Gets allowed discounts applied to categories
+ ///
+ /// Product
+ /// Customer
+ /// Discounts
+ protected virtual IList GetAllowedDiscountsAppliedToCategories(Product product, Customer customer)
+ {
+ var allowedDiscounts = new List();
+ if (_catalogSettings.IgnoreDiscounts)
+ return allowedDiscounts;
+
+ //load cached discount models (performance optimization)
+ foreach (var discount in _discountService.GetAllDiscountsForCaching(DiscountType.AssignedToCategories))
+ {
+ //load identifier of categories with this discount applied to
+ var discountCategoryIds = _discountService.GetAppliedCategoryIds(discount, customer);
+
+ //compare with categories of this product
+ var productCategoryIds = new List();
+ if (discountCategoryIds.Any())
+ {
+ //load identifier of categories of this product
+ var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_CATEGORY_IDS_MODEL_KEY,
+ product.Id,
+ string.Join(",", customer.GetCustomerRoleIds()),
+ _storeContext.CurrentStore.Id);
+ productCategoryIds = _cacheManager.Get(cacheKey, () =>
+ _categoryService
+ .GetProductCategoriesByProductId(product.Id)
+ .Select(x => x.CategoryId)
+ .ToList());
+ }
+
+ foreach (var categoryId in productCategoryIds)
+ {
+ if (discountCategoryIds.Contains(categoryId))
+ {
+ if (_discountService.ValidateDiscount(discount, customer).IsValid &&
+ !allowedDiscounts.ContainsDiscount(discount))
+ allowedDiscounts.Add(discount);
+ }
+ }
+ }
+
+ return allowedDiscounts;
+ }
+
+ ///
+ /// Gets allowed discounts applied to manufacturers
+ ///
+ /// Product
+ /// Customer
+ /// Discounts
+ protected virtual IList GetAllowedDiscountsAppliedToManufacturers(Product product, Customer customer)
+ {
+ var allowedDiscounts = new List();
+ if (_catalogSettings.IgnoreDiscounts)
+ return allowedDiscounts;
+
+ foreach (var discount in _discountService.GetAllDiscountsForCaching(DiscountType.AssignedToManufacturers))
+ {
+ //load identifier of manufacturers with this discount applied to
+ var discountManufacturerIds = _discountService.GetAppliedManufacturerIds(discount, customer);
+
+ //compare with manufacturers of this product
+ var productManufacturerIds = new List();
+ if (discountManufacturerIds.Any())
+ {
+ //load identifier of manufacturers of this product
+ var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_MANUFACTURER_IDS_MODEL_KEY,
+ product.Id,
+ string.Join(",", customer.GetCustomerRoleIds()),
+ _storeContext.CurrentStore.Id);
+ productManufacturerIds = _cacheManager.Get(cacheKey, () =>
+ _manufacturerService
+ .GetProductManufacturersByProductId(product.Id)
+ .Select(x => x.ManufacturerId)
+ .ToList());
+ }
+
+ foreach (var manufacturerId in productManufacturerIds)
+ {
+ if (discountManufacturerIds.Contains(manufacturerId))
+ {
+ if (_discountService.ValidateDiscount(discount, customer).IsValid &&
+ !allowedDiscounts.ContainsDiscount(discount))
+ allowedDiscounts.Add(discount);
+ }
+ }
+ }
+
+ return allowedDiscounts;
+ }
+
+ ///
+ /// Gets allowed discounts
+ ///
+ /// Product
+ /// Customer
+ /// Discounts
+ protected virtual IList GetAllowedDiscounts(Product product, Customer customer)
+ {
+ var allowedDiscounts = new List();
+ if (_catalogSettings.IgnoreDiscounts)
+ return allowedDiscounts;
+
+ //discounts applied to products
+ foreach (var discount in GetAllowedDiscountsAppliedToProduct(product, customer))
+ if (!allowedDiscounts.ContainsDiscount(discount))
+ allowedDiscounts.Add(discount);
+
+ //discounts applied to categories
+ foreach (var discount in GetAllowedDiscountsAppliedToCategories(product, customer))
+ if (!allowedDiscounts.ContainsDiscount(discount))
+ allowedDiscounts.Add(discount);
+
+ //discounts applied to manufacturers
+ foreach (var discount in GetAllowedDiscountsAppliedToManufacturers(product, customer))
+ if (!allowedDiscounts.ContainsDiscount(discount))
+ allowedDiscounts.Add(discount);
+
+ return allowedDiscounts;
+ }
+
+ ///
+ /// Gets discount amount
+ ///
+ /// Product
+ /// The customer
+ /// Already calculated product price without discount
+ /// Applied discounts
+ /// Discount amount
+ protected virtual decimal GetDiscountAmount(Product product,
+ Customer customer,
+ decimal productPriceWithoutDiscount,
+ out List appliedDiscounts)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ appliedDiscounts = null;
+ decimal appliedDiscountAmount = decimal.Zero;
+
+ //we don't apply discounts to products with price entered by a customer
+ if (product.CustomerEntersPrice)
+ return appliedDiscountAmount;
+
+ //discounts are disabled
+ if (_catalogSettings.IgnoreDiscounts)
+ return appliedDiscountAmount;
+
+ var allowedDiscounts = GetAllowedDiscounts(product, customer);
+
+ //no discounts
+ if (!allowedDiscounts.Any())
+ return appliedDiscountAmount;
+
+ appliedDiscounts = allowedDiscounts.GetPreferredDiscount(productPriceWithoutDiscount, out appliedDiscountAmount);
+ return appliedDiscountAmount;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Final price
+ public virtual decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge = decimal.Zero,
+ bool includeDiscounts = true,
+ int quantity = 1)
+ {
+ decimal discountAmount;
+ List appliedDiscounts;
+ return GetFinalPrice(product, customer, additionalCharge, includeDiscounts,
+ quantity, out discountAmount, out appliedDiscounts);
+ }
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Applied discount amount
+ /// Applied discounts
+ /// Final price
+ public virtual decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge,
+ bool includeDiscounts,
+ int quantity,
+ out decimal discountAmount,
+ out List appliedDiscounts)
+ {
+ return GetFinalPrice(product, customer,
+ additionalCharge, includeDiscounts, quantity,
+ null, null,
+ out discountAmount, out appliedDiscounts);
+ }
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Rental period start date (for rental products)
+ /// Rental period end date (for rental products)
+ /// Applied discount amount
+ /// Applied discounts
+ /// Final price
+ public virtual decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge,
+ bool includeDiscounts,
+ int quantity,
+ DateTime? rentalStartDate,
+ DateTime? rentalEndDate,
+ out decimal discountAmount,
+ out List appliedDiscounts)
+ {
+ return GetFinalPrice(product, customer, additionalCharge, includeDiscounts, quantity,
+ rentalStartDate, rentalEndDate, out discountAmount, out appliedDiscounts, null);
+ }
+ ///
+ /// Gets the final price
+ ///
+ /// Product
+ /// The customer
+ /// Additional charge
+ /// A value indicating whether include discounts or not for final price computation
+ /// Shopping cart item quantity
+ /// Rental period start date (for rental products)
+ /// Rental period end date (for rental products)
+ /// Applied discount amount
+ /// Applied discounts
+ /// Overridden product price. If specified, then it'll be used instead of a product price. For example, used with product attribute combinations
+ /// Final price
+ public virtual decimal GetFinalPrice(Product product,
+ Customer customer,
+ decimal additionalCharge,
+ bool includeDiscounts,
+ int quantity,
+ DateTime? rentalStartDate,
+ DateTime? rentalEndDate,
+ out decimal discountAmount,
+ out List appliedDiscounts,
+ decimal? overriddenProductPrice)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ discountAmount = decimal.Zero;
+ appliedDiscounts = new List();
+
+ var cacheKey = string.Format(PriceCacheEventConsumer.PRODUCT_PRICE_MODEL_KEY,
+ product.Id,
+ overriddenProductPrice.HasValue ? overriddenProductPrice.Value.ToString(CultureInfo.InvariantCulture) : null,
+ additionalCharge.ToString(CultureInfo.InvariantCulture),
+ includeDiscounts,
+ quantity,
+ string.Join(",", customer.GetCustomerRoleIds()),
+ _storeContext.CurrentStore.Id);
+ var cacheTime = _catalogSettings.CacheProductPrices ? 60 : 0;
+ //we do not cache price for rental products
+ //otherwise, it can cause memory leaks (to store all possible date period combinations)
+ if (product.IsRental)
+ cacheTime = 0;
+ var cachedPrice = _cacheManager.Get(cacheKey, cacheTime, () =>
+ {
+ var result = new ProductPriceForCaching();
+
+ //initial price
+ decimal price = overriddenProductPrice.HasValue ? overriddenProductPrice.Value : product.Price;
+
+ //tier prices
+ var tierPrice = product.GetPreferredTierPrice(customer, _storeContext.CurrentStore.Id, quantity);
+ if (tierPrice != null)
+ price = tierPrice.Price;
+
+ //additional charge
+ price = price + additionalCharge;
+
+ //rental products
+ if (product.IsRental)
+ if (rentalStartDate.HasValue && rentalEndDate.HasValue)
+ price = price * product.GetRentalPeriods(rentalStartDate.Value, rentalEndDate.Value);
+
+ if (includeDiscounts)
+ {
+ //discount
+ List tmpAppliedDiscounts;
+ decimal tmpDiscountAmount = GetDiscountAmount(product, customer, price, out tmpAppliedDiscounts);
+ price = price - tmpDiscountAmount;
+
+ if (tmpAppliedDiscounts != null)
+ {
+ result.AppliedDiscounts = tmpAppliedDiscounts;
+ result.AppliedDiscountAmount = tmpDiscountAmount;
+ }
+ }
+
+ if (price < decimal.Zero)
+ price = decimal.Zero;
+
+ result.Price = price;
+ return result;
+ });
+
+ if (includeDiscounts)
+ {
+ if (cachedPrice.AppliedDiscounts.Any())
+ {
+ appliedDiscounts.AddRange(cachedPrice.AppliedDiscounts);
+ discountAmount = cachedPrice.AppliedDiscountAmount;
+ }
+ }
+
+ return cachedPrice.Price;
+ }
+
+
+
+ ///
+ /// Gets the shopping cart unit price (one item)
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Shopping cart unit price (one item)
+ public virtual decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts = true)
+ {
+ decimal discountAmount;
+ List appliedDiscounts;
+ return GetUnitPrice(shoppingCartItem, includeDiscounts,
+ out discountAmount, out appliedDiscounts);
+ }
+ ///
+ /// Gets the shopping cart unit price (one item)
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Applied discount amount
+ /// Applied discounts
+ /// Shopping cart unit price (one item)
+ public virtual decimal GetUnitPrice(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts,
+ out decimal discountAmount,
+ out List appliedDiscounts)
+ {
+ if (shoppingCartItem == null)
+ throw new ArgumentNullException("shoppingCartItem");
+ var attributesXml = shoppingCartItem.AttributesXml;
+ bool includingTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax;
+ var unitPrice = GetUnitPrice(shoppingCartItem.Product,
+ shoppingCartItem.Customer,
+ shoppingCartItem.ShoppingCartType,
+ shoppingCartItem.Quantity,
+ ref attributesXml,
+ shoppingCartItem.CustomerEnteredPrice,
+ shoppingCartItem.RentalStartDateUtc,
+ shoppingCartItem.RentalEndDateUtc,
+ includeDiscounts,
+ out discountAmount,
+ out appliedDiscounts);
+ //update modified attributesXml with attributes taxinfo
+ shoppingCartItem.AttributesXml = attributesXml;
+ return unitPrice;
+ }
+ ///
+ /// Gets the shopping cart unit price (one item)
+ ///
+ /// Product
+ /// Customer
+ /// Shopping cart type
+ /// Quantity
+ /// Product atrributes (XML format)
+ /// Customer entered price (if specified)
+ /// Rental start date (null for not rental products)
+ /// Rental end date (null for not rental products)
+ /// A value indicating whether include discounts or not for price computation
+ /// Applied discount amount
+ /// Applied discounts
+ /// Shopping cart unit price (one item)
+ public virtual decimal GetUnitPrice(Product product,
+ Customer customer,
+ ShoppingCartType shoppingCartType,
+ int quantity,
+ ref string attributesXml,
+ decimal customerEnteredPrice,
+ DateTime? rentalStartDate, DateTime? rentalEndDate,
+ bool includeDiscounts,
+ out decimal discountAmount,
+ out List appliedDiscounts)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ if (customer == null)
+ throw new ArgumentNullException("customer");
+
+ discountAmount = decimal.Zero;
+ appliedDiscounts = new List();
+
+ //taxWeights will use price tax settings
+ var taxWeightSummary = new TaxSummary(_taxService.PricesIncludeTax());
+
+ decimal finalPrice = decimal.Zero;
+
+ var combination = _productAttributeParser.FindProductAttributeCombination(product, attributesXml);
+
+ //get taxrate and summarize price of all attributes to build up taxWeightSummary
+ //needed for VAT calculation of product bundles
+ //taxrate comes from associated product and not from product
+ decimal attributesTotalPrice = decimal.Zero;
+ var attributeValues = _productAttributeParser.ParseProductAttributeValues(attributesXml);
+ if (attributeValues != null)
+ {
+ decimal taxRate;
+ foreach (var attributeValue in attributeValues)
+ {
+ var attributePrice = GetProductAttributeValuePriceAdjustment(attributeValue);
+ attributesTotalPrice += attributePrice;
+
+ if (attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct)
+ {
+ //bundled product
+ var associatedProduct = _productService.GetProductById(attributeValue.AssociatedProductId);
+ if (associatedProduct != null)
+ {
+ //get only the taxrate of associate product, price is not needed
+ var attributePriceTax = _taxService.GetProductPrice(associatedProduct, 0, customer, out taxRate);
+ //build taxSummary for tax subdivision
+ taxWeightSummary.AddRate(taxRate, attributePrice);
+ }
+ }
+ }
+ }
+
+
+ if (combination != null && combination.OverriddenPrice.HasValue)
+ {
+ finalPrice = GetFinalPrice(product,
+ customer,
+ decimal.Zero,
+ includeDiscounts,
+ quantity,
+ product.IsRental ? rentalStartDate : null,
+ product.IsRental ? rentalEndDate : null,
+ out discountAmount, out appliedDiscounts,
+ combination.OverriddenPrice.Value);
+ }
+ else
+ {
+ //get price of a product (with previously calculated price of all attributes)
+ if (product.CustomerEntersPrice)
+ {
+ finalPrice = customerEnteredPrice;
+ }
+ else
+ {
+ int qty;
+ if (_shoppingCartSettings.GroupTierPricesForDistinctShoppingCartItems)
+ {
+ //the same products with distinct product attributes could be stored as distinct "ShoppingCartItem" records
+ //so let's find how many of the current products are in the cart
+ qty = customer.ShoppingCartItems
+ .Where(x => x.ProductId == product.Id)
+ .Where(x => x.ShoppingCartType == shoppingCartType)
+ .Sum(x => x.Quantity);
+ if (qty == 0)
+ {
+ qty = quantity;
+ }
+ }
+ else
+ {
+ qty = quantity;
+ }
+ finalPrice = GetFinalPrice(product,
+ customer,
+ attributesTotalPrice,
+ includeDiscounts,
+ qty,
+ product.IsRental ? rentalStartDate : null,
+ product.IsRental ? rentalEndDate : null,
+ out discountAmount, out appliedDiscounts);
+ }
+ }
+
+ //rounding
+ if (_shoppingCartSettings.RoundPricesDuringCalculation)
+ finalPrice = RoundingHelper.RoundPrice(finalPrice);
+
+ //add tax attributes if needed
+ if (taxWeightSummary != null && taxWeightSummary.TaxRates.Any())
+ {
+ decimal prodcutTaxRate;
+ //get taxrate and product price
+ var productPrice = _taxService.GetProductPrice(product, product.Price, customer, out prodcutTaxRate);
+
+ //price is overridden or it is product price + attributes price
+ if (combination == null || combination != null && !combination.OverriddenPrice.HasValue)
+ {
+ var baseProductPrice = productPrice; // finalPrice - attributesTotalPrice;
+ //add product price if exists
+ if (baseProductPrice > decimal.Zero)
+ taxWeightSummary.AddRate(prodcutTaxRate, baseProductPrice);
+ }
+ if (taxWeightSummary.TaxRates.Count() > 1 ||
+ (taxWeightSummary.TaxRates.FirstOrDefault().Key != prodcutTaxRate) //use associated product taxRate when different
+ )
+ {
+ taxWeightSummary.CalculateWeights();
+ attributesXml = _productAttributeParser.AddTaxAttribute(attributesXml, taxWeightSummary);
+ }
+ }
+
+ return finalPrice;
+ }
+ ///
+ /// Gets the shopping cart item sub total
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Shopping cart item sub total
+ public virtual decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts = true)
+ {
+ decimal discountAmount;
+ List appliedDiscounts;
+ int? maximumDiscountQty;
+ return GetSubTotal(shoppingCartItem, includeDiscounts, out discountAmount, out appliedDiscounts, out maximumDiscountQty);
+ }
+ ///
+ /// Gets the shopping cart item sub total
+ ///
+ /// The shopping cart item
+ /// A value indicating whether include discounts or not for price computation
+ /// Applied discount amount
+ /// Applied discounts
+ /// Maximum discounted qty. Return not nullable value if discount cannot be applied to ALL items
+ /// Shopping cart item sub total
+ public virtual decimal GetSubTotal(ShoppingCartItem shoppingCartItem,
+ bool includeDiscounts,
+ out decimal discountAmount,
+ out List appliedDiscounts,
+ out int? maximumDiscountQty)
+ {
+ if (shoppingCartItem == null)
+ throw new ArgumentNullException("shoppingCartItem");
+
+ decimal subTotal;
+ maximumDiscountQty = null;
+
+ //unit price
+ var unitPrice = GetUnitPrice(shoppingCartItem, includeDiscounts,
+ out discountAmount, out appliedDiscounts);
+
+ //discount
+ if (appliedDiscounts.Any())
+ {
+ //we can properly use "MaximumDiscountedQuantity" property only for one discount (not cumulative ones)
+ DiscountForCaching oneAndOnlyDiscount = null;
+ if (appliedDiscounts.Count == 1)
+ oneAndOnlyDiscount = appliedDiscounts.First();
+
+ if (oneAndOnlyDiscount != null &&
+ oneAndOnlyDiscount.MaximumDiscountedQuantity.HasValue &&
+ shoppingCartItem.Quantity > oneAndOnlyDiscount.MaximumDiscountedQuantity.Value)
+ {
+ maximumDiscountQty = oneAndOnlyDiscount.MaximumDiscountedQuantity.Value;
+ //we cannot apply discount for all shopping cart items
+ var discountedQuantity = oneAndOnlyDiscount.MaximumDiscountedQuantity.Value;
+ var discountedSubTotal = unitPrice * discountedQuantity;
+ discountAmount = discountAmount * discountedQuantity;
+
+ var notDiscountedQuantity = shoppingCartItem.Quantity - discountedQuantity;
+ var notDiscountedUnitPrice = GetUnitPrice(shoppingCartItem, false);
+ var notDiscountedSubTotal = notDiscountedUnitPrice*notDiscountedQuantity;
+
+ subTotal = discountedSubTotal + notDiscountedSubTotal;
+ }
+ else
+ {
+ //discount is applied to all items (quantity)
+ //calculate discount amount for all items
+ discountAmount = discountAmount * shoppingCartItem.Quantity;
+
+ subTotal = unitPrice * shoppingCartItem.Quantity;
+ }
+ }
+ else
+ {
+ subTotal = unitPrice * shoppingCartItem.Quantity;
+ }
+ return subTotal;
+ }
+
+
+ ///
+ /// Gets the product cost (one item)
+ ///
+ /// Product
+ /// Shopping cart item attributes in XML
+ /// Product cost (one item)
+ public virtual decimal GetProductCost(Product product, string attributesXml)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ decimal cost = product.ProductCost;
+ var attributeValues = _productAttributeParser.ParseProductAttributeValues(attributesXml);
+ foreach (var attributeValue in attributeValues)
+ {
+ switch (attributeValue.AttributeValueType)
+ {
+ case AttributeValueType.Simple:
+ {
+ //simple attribute
+ cost += attributeValue.Cost;
+ }
+ break;
+ case AttributeValueType.AssociatedToProduct:
+ {
+ //bundled product
+ var associatedProduct = _productService.GetProductById(attributeValue.AssociatedProductId);
+ if (associatedProduct != null)
+ cost += associatedProduct.ProductCost * attributeValue.Quantity;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return cost;
+ }
+
+
+
+ ///
+ /// Get a price adjustment of a product attribute value
+ ///
+ /// Product attribute value
+ /// Price adjustment
+ public virtual decimal GetProductAttributeValuePriceAdjustment(ProductAttributeValue value)
+ {
+ if (value == null)
+ throw new ArgumentNullException("value");
+
+ var adjustment = decimal.Zero;
+ switch (value.AttributeValueType)
+ {
+ case AttributeValueType.Simple:
+ {
+ //simple attribute
+ adjustment = value.PriceAdjustment;
+ }
+ break;
+ case AttributeValueType.AssociatedToProduct:
+ {
+ //bundled product
+ var associatedProduct = _productService.GetProductById(value.AssociatedProductId);
+ if (associatedProduct != null)
+ {
+ adjustment = GetFinalPrice(associatedProduct, _workContext.CurrentCustomer, includeDiscounts: true) * value.Quantity;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+
+ return adjustment;
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/PriceFormatter.cs b/src/Libraries/Nop.Services/Catalog/PriceFormatter.cs
index 0f026f4b19a..e0b37650166 100644
--- a/src/Libraries/Nop.Services/Catalog/PriceFormatter.cs
+++ b/src/Libraries/Nop.Services/Catalog/PriceFormatter.cs
@@ -169,7 +169,7 @@ public virtual string FormatPrice(decimal price, bool showCurrency,
public virtual string FormatPrice(decimal price, bool showCurrency,
string currencyCode, Language language, bool priceIncludesTax)
{
- var currency = _currencyService.GetCurrencyByCode(currencyCode)
+ var currency = _currencyService.GetCurrencyByCode(currencyCode)
?? new Currency
{
CurrencyCode = currencyCode
@@ -186,10 +186,10 @@ public virtual string FormatPrice(decimal price, bool showCurrency,
/// Language
/// A value indicating whether price includes tax
/// Price
- public virtual string FormatPrice(decimal price, bool showCurrency,
+ public virtual string FormatPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax)
{
- return FormatPrice(price, showCurrency, targetCurrency, language,
+ return FormatPrice(price, showCurrency, targetCurrency, language,
priceIncludesTax, _taxSettings.DisplayTaxSuffix);
}
@@ -203,12 +203,12 @@ public virtual string FormatPrice(decimal price, bool showCurrency,
/// A value indicating whether price includes tax
/// A value indicating whether to show tax suffix
/// Price
- public virtual string FormatPrice(decimal price, bool showCurrency,
+ public virtual string FormatPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax)
{
//we should round it no matter of "ShoppingCartSettings.RoundPricesDuringCalculation" setting
price = RoundingHelper.RoundPrice(price);
-
+
string currencyString = GetCurrencyString(price, showCurrency, targetCurrency);
if (showTax)
{
@@ -228,7 +228,7 @@ public virtual string FormatPrice(decimal price, bool showCurrency,
}
return string.Format(formatStr, currencyString);
}
-
+
return currencyString;
}
@@ -293,7 +293,7 @@ public virtual string FormatShippingPrice(decimal price, bool showCurrency)
/// Language
/// A value indicating whether price includes tax
/// Price
- public virtual string FormatShippingPrice(decimal price, bool showCurrency,
+ public virtual string FormatShippingPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax)
{
bool showTax = _taxSettings.ShippingIsTaxable && _taxSettings.DisplayTaxSuffix;
@@ -310,12 +310,12 @@ public virtual string FormatShippingPrice(decimal price, bool showCurrency,
/// A value indicating whether price includes tax
/// A value indicating whether to show tax suffix
/// Price
- public virtual string FormatShippingPrice(decimal price, bool showCurrency,
+ public virtual string FormatShippingPrice(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax)
{
return FormatPrice(price, showCurrency, targetCurrency, language, priceIncludesTax, showTax);
}
-
+
///
/// Formats the shipping price
///
@@ -325,10 +325,10 @@ public virtual string FormatShippingPrice(decimal price, bool showCurrency,
/// Language
/// A value indicating whether price includes tax
/// Price
- public virtual string FormatShippingPrice(decimal price, bool showCurrency,
+ public virtual string FormatShippingPrice(decimal price, bool showCurrency,
string currencyCode, Language language, bool priceIncludesTax)
{
- var currency = _currencyService.GetCurrencyByCode(currencyCode)
+ var currency = _currencyService.GetCurrencyByCode(currencyCode)
?? new Currency
{
CurrencyCode = currencyCode
@@ -347,7 +347,7 @@ public virtual string FormatShippingPrice(decimal price, bool showCurrency,
public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency)
{
bool priceIncludesTax = _workContext.TaxDisplayType == TaxDisplayType.IncludingTax;
- return FormatPaymentMethodAdditionalFee(price, showCurrency, _workContext.WorkingCurrency,
+ return FormatPaymentMethodAdditionalFee(price, showCurrency, _workContext.WorkingCurrency,
_workContext.WorkingLanguage, priceIncludesTax);
}
@@ -377,10 +377,10 @@ public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showC
/// A value indicating whether price includes tax
/// A value indicating whether to show tax suffix
/// Price
- public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
+ public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
Currency targetCurrency, Language language, bool priceIncludesTax, bool showTax)
{
- return FormatPrice(price, showCurrency, targetCurrency, language,
+ return FormatPrice(price, showCurrency, targetCurrency, language,
priceIncludesTax, showTax);
}
@@ -393,7 +393,7 @@ public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showC
/// Language
/// A value indicating whether price includes tax
/// Price
- public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
+ public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showCurrency,
string currencyCode, Language language, bool priceIncludesTax)
{
var currency = _currencyService.GetCurrencyByCode(currencyCode)
@@ -401,7 +401,7 @@ public virtual string FormatPaymentMethodAdditionalFee(decimal price, bool showC
{
CurrencyCode = currencyCode
};
- return FormatPaymentMethodAdditionalFee(price, showCurrency, currency,
+ return FormatPaymentMethodAdditionalFee(price, showCurrency, currency,
language, priceIncludesTax);
}
@@ -417,6 +417,50 @@ public virtual string FormatTaxRate(decimal taxRate)
return taxRate.ToString("G29");
}
+ ///
+ /// Adds tax suffix to text
+ ///
+ /// Text to format
+ /// Language
+ /// A value indicating whether price includes tax
+ ///
+ public virtual string FormatTaxString(string text, Language language, bool priceIncludesTax)
+ {
+ bool showTax = _taxSettings.DisplayTaxSuffix;
+ return FormatTaxString(text, language, priceIncludesTax, showTax);
+ }
+ ///
+ /// Adds tax suffix to text
+ ///
+ /// Text to format
+ /// Language
+ /// A value indicating whether price includes tax
+ /// Optional. A value indicating whether to show tax suffix.
+ ///
+ public virtual string FormatTaxString(string text, Language language, bool priceIncludesTax, bool showTax)
+ {
+
+ if (showTax)
+ {
+ //show tax suffix
+ string formatStr;
+ if (priceIncludesTax)
+ {
+ formatStr = _localizationService.GetResource("Products.InclTaxSuffix", language.Id, false);
+ if (String.IsNullOrEmpty(formatStr))
+ formatStr = "{0} incl tax";
+ }
+ else
+ {
+ formatStr = _localizationService.GetResource("Products.ExclTaxSuffix", language.Id, false);
+ if (String.IsNullOrEmpty(formatStr))
+ formatStr = "{0} excl tax";
+ }
+ return string.Format(formatStr, text);
+ }
+
+ return text;
+ }
#endregion
}
}
diff --git a/src/Libraries/Nop.Services/Catalog/ProductAttributeFormatter.cs b/src/Libraries/Nop.Services/Catalog/ProductAttributeFormatter.cs
index 1b06a5c15db..9f3e6101b09 100644
--- a/src/Libraries/Nop.Services/Catalog/ProductAttributeFormatter.cs
+++ b/src/Libraries/Nop.Services/Catalog/ProductAttributeFormatter.cs
@@ -1,243 +1,276 @@
-using System;
-using System.Text;
-using System.Web;
-using Nop.Core;
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Customers;
-using Nop.Core.Domain.Orders;
-using Nop.Core.Html;
-using Nop.Services.Directory;
-using Nop.Services.Localization;
-using Nop.Services.Media;
-using Nop.Services.Tax;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Product attribute formatter
- ///
- public partial class ProductAttributeFormatter : IProductAttributeFormatter
- {
- private readonly IWorkContext _workContext;
- private readonly IProductAttributeService _productAttributeService;
- private readonly IProductAttributeParser _productAttributeParser;
- private readonly ICurrencyService _currencyService;
- private readonly ILocalizationService _localizationService;
- private readonly ITaxService _taxService;
- private readonly IPriceFormatter _priceFormatter;
- private readonly IDownloadService _downloadService;
- private readonly IWebHelper _webHelper;
- private readonly IPriceCalculationService _priceCalculationService;
- private readonly ShoppingCartSettings _shoppingCartSettings;
-
- public ProductAttributeFormatter(IWorkContext workContext,
- IProductAttributeService productAttributeService,
- IProductAttributeParser productAttributeParser,
- ICurrencyService currencyService,
- ILocalizationService localizationService,
- ITaxService taxService,
- IPriceFormatter priceFormatter,
- IDownloadService downloadService,
- IWebHelper webHelper,
- IPriceCalculationService priceCalculationService,
- ShoppingCartSettings shoppingCartSettings)
- {
- this._workContext = workContext;
- this._productAttributeService = productAttributeService;
- this._productAttributeParser = productAttributeParser;
- this._currencyService = currencyService;
- this._localizationService = localizationService;
- this._taxService = taxService;
- this._priceFormatter = priceFormatter;
- this._downloadService = downloadService;
- this._webHelper = webHelper;
- this._priceCalculationService = priceCalculationService;
- this._shoppingCartSettings = shoppingCartSettings;
- }
-
- ///
- /// Formats attributes
- ///
- /// Product
- /// Attributes in XML format
- /// Attributes
- public virtual string FormatAttributes(Product product, string attributesXml)
- {
- var customer = _workContext.CurrentCustomer;
- return FormatAttributes(product, attributesXml, customer);
- }
-
- ///
- /// Formats attributes
- ///
- /// Product
- /// Attributes in XML format
- /// Customer
- /// Serapator
- /// A value indicating whether to encode (HTML) values
- /// A value indicating whether to render prices
- /// A value indicating whether to render product attributes
- /// A value indicating whether to render gift card attributes
- /// A value indicating whether to HTML hyperink tags could be rendered (if required)
- /// Attributes
- public virtual string FormatAttributes(Product product, string attributesXml,
- Customer customer, string serapator = "
", bool htmlEncode = true, bool renderPrices = true,
- bool renderProductAttributes = true, bool renderGiftCardAttributes = true,
- bool allowHyperlinks = true)
- {
- var result = new StringBuilder();
-
- //attributes
- if (renderProductAttributes)
- {
- foreach (var attribute in _productAttributeParser.ParseProductAttributeMappings(attributesXml))
- {
- //attributes without values
- if (!attribute.ShouldHaveValues())
- {
- foreach (var value in _productAttributeParser.ParseValues(attributesXml, attribute.Id))
- {
- var formattedAttribute = string.Empty;
- if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox)
- {
- //multiline textbox
- var attributeName = attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id);
-
- //encode (if required)
- if (htmlEncode)
- attributeName = HttpUtility.HtmlEncode(attributeName);
-
- //we never encode multiline textbox input
- formattedAttribute = string.Format("{0}: {1}", attributeName, HtmlHelper.FormatText(value, false, true, false, false, false, false));
- }
- else if (attribute.AttributeControlType == AttributeControlType.FileUpload)
- {
- //file upload
- Guid downloadGuid;
- Guid.TryParse(value, out downloadGuid);
- var download = _downloadService.GetDownloadByGuid(downloadGuid);
- if (download != null)
- {
- var fileName = string.Format("{0}{1}", download.Filename ?? download.DownloadGuid.ToString(), download.Extension);
-
- //encode (if required)
- if (htmlEncode)
- fileName = HttpUtility.HtmlEncode(fileName);
-
- //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs)
- var attributeText = allowHyperlinks ? string.Format("{2}",
- _webHelper.GetStoreLocation(false), download.DownloadGuid, fileName) : fileName;
-
- var attributeName = attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id);
-
- //encode (if required)
- if (htmlEncode)
- attributeName = HttpUtility.HtmlEncode(attributeName);
-
- formattedAttribute = string.Format("{0}: {1}", attributeName, attributeText);
- }
- }
- else
- {
- //other attributes (textbox, datepicker)
- formattedAttribute = string.Format("{0}: {1}", attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id), value);
-
- //encode (if required)
- if (htmlEncode)
- formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute);
- }
-
- if (!string.IsNullOrEmpty(formattedAttribute))
- {
- if (result.Length > 0)
- result.Append(serapator);
- result.Append(formattedAttribute);
- }
- }
- }
- //product attribute values
- else
- {
- foreach (var attributeValue in _productAttributeParser.ParseProductAttributeValues(attributesXml, attribute.Id))
- {
- var formattedAttribute = string.Format("{0}: {1}",
- attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id),
- attributeValue.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id));
-
- if (renderPrices)
- {
- decimal taxRate;
- var attributeValuePriceAdjustment = _priceCalculationService.GetProductAttributeValuePriceAdjustment(attributeValue);
- var priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, customer, out taxRate);
- var priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency);
- if (priceAdjustmentBase > 0)
- formattedAttribute += string.Format(" [+{0}]", _priceFormatter.FormatPrice(priceAdjustment, false, false));
- else if (priceAdjustmentBase < decimal.Zero)
- formattedAttribute += string.Format(" [-{0}]", _priceFormatter.FormatPrice(-priceAdjustment, false, false));
- }
-
- //display quantity
- if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity && attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct)
- {
- //render only when more than 1
- if (attributeValue.Quantity > 1)
- formattedAttribute += string.Format(_localizationService.GetResource("ProductAttributes.Quantity"), attributeValue.Quantity);
- }
-
- //encode (if required)
- if (htmlEncode)
- formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute);
-
- if (!string.IsNullOrEmpty(formattedAttribute))
- {
- if (result.Length > 0)
- result.Append(serapator);
- result.Append(formattedAttribute);
- }
- }
- }
- }
- }
-
- //gift cards
- if (renderGiftCardAttributes)
- {
- if (product.IsGiftCard)
- {
- string giftCardRecipientName;
- string giftCardRecipientEmail;
- string giftCardSenderName;
- string giftCardSenderEmail;
- string giftCardMessage;
- _productAttributeParser.GetGiftCardAttribute(attributesXml, out giftCardRecipientName, out giftCardRecipientEmail,
- out giftCardSenderName, out giftCardSenderEmail, out giftCardMessage);
-
- //sender
- var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ?
- string.Format(_localizationService.GetResource("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) :
- string.Format(_localizationService.GetResource("GiftCardAttribute.From.Physical"), giftCardSenderName);
- //recipient
- var giftCardFor = product.GiftCardType == GiftCardType.Virtual ?
- string.Format(_localizationService.GetResource("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) :
- string.Format(_localizationService.GetResource("GiftCardAttribute.For.Physical"), giftCardRecipientName);
-
- //encode (if required)
- if (htmlEncode)
- {
- giftCardFrom = HttpUtility.HtmlEncode(giftCardFrom);
- giftCardFor = HttpUtility.HtmlEncode(giftCardFor);
- }
-
- if (!String.IsNullOrEmpty(result.ToString()))
- {
- result.Append(serapator);
- }
- result.Append(giftCardFrom);
- result.Append(serapator);
- result.Append(giftCardFor);
- }
- }
- return result.ToString();
- }
- }
-}
+using System;
+using System.Text;
+using System.Web;
+using Nop.Core;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Customers;
+using Nop.Core.Domain.Orders;
+using Nop.Core.Html;
+using Nop.Services.Directory;
+using Nop.Services.Localization;
+using Nop.Services.Media;
+using Nop.Services.Tax;
+using System.Collections.Generic;
+using Nop.Core.Domain.Tax;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Product attribute formatter
+ ///
+ public partial class ProductAttributeFormatter : IProductAttributeFormatter
+ {
+ private readonly IWorkContext _workContext;
+ private readonly IProductAttributeService _productAttributeService;
+ private readonly IProductAttributeParser _productAttributeParser;
+ private readonly ICurrencyService _currencyService;
+ private readonly ILocalizationService _localizationService;
+ private readonly ITaxService _taxService;
+ private readonly IPriceFormatter _priceFormatter;
+ private readonly IDownloadService _downloadService;
+ private readonly IWebHelper _webHelper;
+ private readonly IPriceCalculationService _priceCalculationService;
+ private readonly ShoppingCartSettings _shoppingCartSettings;
+
+ public ProductAttributeFormatter(IWorkContext workContext,
+ IProductAttributeService productAttributeService,
+ IProductAttributeParser productAttributeParser,
+ ICurrencyService currencyService,
+ ILocalizationService localizationService,
+ ITaxService taxService,
+ IPriceFormatter priceFormatter,
+ IDownloadService downloadService,
+ IWebHelper webHelper,
+ IPriceCalculationService priceCalculationService,
+ ShoppingCartSettings shoppingCartSettings)
+ {
+ this._workContext = workContext;
+ this._productAttributeService = productAttributeService;
+ this._productAttributeParser = productAttributeParser;
+ this._currencyService = currencyService;
+ this._localizationService = localizationService;
+ this._taxService = taxService;
+ this._priceFormatter = priceFormatter;
+ this._downloadService = downloadService;
+ this._webHelper = webHelper;
+ this._priceCalculationService = priceCalculationService;
+ this._shoppingCartSettings = shoppingCartSettings;
+ }
+
+ ///
+ /// Formats attributes
+ ///
+ /// Product
+ /// Attributes in XML format
+ /// Attributes
+ public virtual string FormatAttributes(Product product, string attributesXml)
+ {
+ var customer = _workContext.CurrentCustomer;
+ return FormatAttributes(product, attributesXml, customer);
+ }
+
+ ///
+ /// Formats attributes
+ ///
+ /// Product
+ /// Attributes in XML format
+ /// Customer
+ /// Serapator
+ /// A value indicating whether to encode (HTML) values
+ /// A value indicating whether to render prices
+ /// A value indicating whether to render product attributes
+ /// A value indicating whether to render gift card attributes
+ /// A value indicating whether to HTML hyperink tags could be rendered (if required)
+ /// /// A value indicating if attribute VAT should be rendered with price. Null is default, i.e. don't render
+ /// Attributes
+ public virtual string FormatAttributes(Product product, string attributesXml,
+ Customer customer, string serapator = "
", bool htmlEncode = true, bool renderPrices = true,
+ bool renderProductAttributes = true, bool renderGiftCardAttributes = true,
+ bool allowHyperlinks = true,
+ decimal? subTotal = null)
+ {
+ var result = new StringBuilder();
+
+ //attributes
+ if (renderProductAttributes)
+ {
+ foreach (var attribute in _productAttributeParser.ParseProductAttributeMappings(attributesXml))
+ {
+ //attributes without values
+ if (!attribute.ShouldHaveValues())
+ {
+ foreach (var value in _productAttributeParser.ParseValues(attributesXml, attribute.Id))
+ {
+ var formattedAttribute = string.Empty;
+ if (attribute.AttributeControlType == AttributeControlType.MultilineTextbox)
+ {
+ //multiline textbox
+ var attributeName = attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id);
+
+ //encode (if required)
+ if (htmlEncode)
+ attributeName = HttpUtility.HtmlEncode(attributeName);
+
+ //we never encode multiline textbox input
+ formattedAttribute = string.Format("{0}: {1}", attributeName, HtmlHelper.FormatText(value, false, true, false, false, false, false));
+ }
+ else if (attribute.AttributeControlType == AttributeControlType.FileUpload)
+ {
+ //file upload
+ Guid downloadGuid;
+ Guid.TryParse(value, out downloadGuid);
+ var download = _downloadService.GetDownloadByGuid(downloadGuid);
+ if (download != null)
+ {
+ var fileName = string.Format("{0}{1}", download.Filename ?? download.DownloadGuid.ToString(), download.Extension);
+
+ //encode (if required)
+ if (htmlEncode)
+ fileName = HttpUtility.HtmlEncode(fileName);
+
+ //TODO add a method for getting URL (use routing because it handles all SEO friendly URLs)
+ var attributeText = allowHyperlinks ? string.Format("{2}",
+ _webHelper.GetStoreLocation(false), download.DownloadGuid, fileName) : fileName;
+
+ var attributeName = attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id);
+
+ //encode (if required)
+ if (htmlEncode)
+ attributeName = HttpUtility.HtmlEncode(attributeName);
+
+ formattedAttribute = string.Format("{0}: {1}", attributeName, attributeText);
+ }
+ }
+ else
+ {
+ //other attributes (textbox, datepicker)
+ formattedAttribute = string.Format("{0}: {1}", attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id), value);
+
+ //encode (if required)
+ if (htmlEncode)
+ formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute);
+ }
+
+ if (!string.IsNullOrEmpty(formattedAttribute))
+ {
+ if (result.Length > 0)
+ result.Append(serapator);
+ result.Append(formattedAttribute);
+ }
+ }
+ }
+ //product attribute values
+ else
+ {
+ foreach (var attributeValue in _productAttributeParser.ParseProductAttributeValues(attributesXml, attribute.Id))
+ {
+ var formattedAttribute = string.Format("{0}: {1}",
+ attribute.ProductAttribute.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id),
+ attributeValue.GetLocalized(a => a.Name, _workContext.WorkingLanguage.Id));
+
+ if (renderPrices && _shoppingCartSettings.RenderProductAttributePrices && attribute.AttributeControlType != AttributeControlType.ReadonlyCheckboxes) //prices are off for readonly attributes
+ {
+ decimal taxRate;
+ var attributeValuePriceAdjustment = _priceCalculationService.GetProductAttributeValuePriceAdjustment(attributeValue);
+ var priceAdjustmentBase = _taxService.GetProductPrice(product, attributeValuePriceAdjustment, customer, out taxRate);
+ var priceAdjustment = _currencyService.ConvertFromPrimaryStoreCurrency(priceAdjustmentBase, _workContext.WorkingCurrency);
+ if (priceAdjustmentBase > 0)
+ formattedAttribute += string.Format(" [+{0}]", _priceFormatter.FormatPrice(priceAdjustment, false, false));
+ else if (priceAdjustmentBase < decimal.Zero)
+ formattedAttribute += string.Format(" [-{0}]", _priceFormatter.FormatPrice(-priceAdjustment, false, false));
+ }
+
+ //display quantity
+ if (_shoppingCartSettings.RenderAssociatedAttributeValueQuantity && attributeValue.AttributeValueType == AttributeValueType.AssociatedToProduct)
+ {
+ //render only when more than 1
+ if (attributeValue.Quantity > 1)
+ formattedAttribute += string.Format(_localizationService.GetResource("ProductAttributes.Quantity"), attributeValue.Quantity);
+ }
+
+ //encode (if required)
+ if (htmlEncode)
+ formattedAttribute = HttpUtility.HtmlEncode(formattedAttribute);
+
+ if (!string.IsNullOrEmpty(formattedAttribute))
+ {
+ if (result.Length > 0)
+ result.Append(serapator);
+ result.Append(formattedAttribute);
+ }
+ }
+ }
+ }
+ }
+
+ //gift cards
+ if (renderGiftCardAttributes)
+ {
+ if (product.IsGiftCard)
+ {
+ string giftCardRecipientName;
+ string giftCardRecipientEmail;
+ string giftCardSenderName;
+ string giftCardSenderEmail;
+ string giftCardMessage;
+ _productAttributeParser.GetGiftCardAttribute(attributesXml, out giftCardRecipientName, out giftCardRecipientEmail,
+ out giftCardSenderName, out giftCardSenderEmail, out giftCardMessage);
+
+ //sender
+ var giftCardFrom = product.GiftCardType == GiftCardType.Virtual ?
+ string.Format(_localizationService.GetResource("GiftCardAttribute.From.Virtual"), giftCardSenderName, giftCardSenderEmail) :
+ string.Format(_localizationService.GetResource("GiftCardAttribute.From.Physical"), giftCardSenderName);
+ //recipient
+ var giftCardFor = product.GiftCardType == GiftCardType.Virtual ?
+ string.Format(_localizationService.GetResource("GiftCardAttribute.For.Virtual"), giftCardRecipientName, giftCardRecipientEmail) :
+ string.Format(_localizationService.GetResource("GiftCardAttribute.For.Physical"), giftCardRecipientName);
+
+ //encode (if required)
+ if (htmlEncode)
+ {
+ giftCardFrom = HttpUtility.HtmlEncode(giftCardFrom);
+ giftCardFor = HttpUtility.HtmlEncode(giftCardFor);
+ }
+
+ if (!String.IsNullOrEmpty(result.ToString()))
+ {
+ result.Append(serapator);
+ }
+ result.Append(giftCardFrom);
+ result.Append(serapator);
+ result.Append(giftCardFor);
+ }
+ }
+
+ //attribute tax
+ if (subTotal != null & subTotal != decimal.Zero)
+ {
+ var taxAttributes = _productAttributeParser.ParseTaxAttribute(attributesXml);
+ if (taxAttributes.Count != 0)
+ {
+ var attribTaxSummary = new TaxSummary(_workContext.TaxDisplayType == TaxDisplayType.IncludingTax);
+ var attribTax = attribTaxSummary.ApplyAttributeRate(subTotal ?? decimal.Zero, taxAttributes);
+ string formattedAttribute = "";
+ if (result.Length > 0)
+ result.Append(serapator);
+ result.Append(String.Format("{0} {1}
",
+ _localizationService.GetResource("ShoppingCart.TaxRate"),
+ _localizationService.GetResource("Shoppingcart.Totals.OrderAmount")));
+
+ foreach (KeyValuePair kvp in attribTax)
+ {
+ decimal vatpercentage = kvp.Key;
+ decimal rateAmount = kvp.Value;
+ var priceBase = _currencyService.ConvertFromPrimaryStoreCurrency(rateAmount, _workContext.WorkingCurrency);
+ var price = _priceFormatter.FormatPrice(priceBase, false, false);
+ price = htmlEncode ? HttpUtility.HtmlEncode(price) : price;
+ formattedAttribute = String.Format("{0} {1}
", kvp.Key.ToString(), price);
+ result.Append(formattedAttribute);
+ }
+ }
+
+ }
+ return result.ToString();
+ }
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/ProductAttributeParser.cs b/src/Libraries/Nop.Services/Catalog/ProductAttributeParser.cs
index df203866037..42ab1c3dbdd 100644
--- a/src/Libraries/Nop.Services/Catalog/ProductAttributeParser.cs
+++ b/src/Libraries/Nop.Services/Catalog/ProductAttributeParser.cs
@@ -1,783 +1,889 @@
-using System;
-using System.Collections.Generic;
-using System.Diagnostics;
-using System.Linq;
-using System.Xml;
-using Nop.Core.Domain.Catalog;
-using Nop.Data;
-
-namespace Nop.Services.Catalog
-{
- ///
- /// Product attribute parser
- ///
- public partial class ProductAttributeParser : IProductAttributeParser
- {
- #region Fields
-
- private readonly IDbContext _context;
- private readonly IProductAttributeService _productAttributeService;
-
- #endregion
-
- #region Ctor
-
- public ProductAttributeParser(IDbContext context,
- IProductAttributeService productAttributeService)
- {
- this._context = context;
- this._productAttributeService = productAttributeService;
- }
-
- #endregion
-
- #region Product attributes
-
- ///
- /// Gets selected product attribute mapping identifiers
- ///
- /// Attributes in XML format
- /// Selected product attribute mapping identifiers
- protected virtual IList ParseProductAttributeMappingIds(string attributesXml)
- {
- var ids = new List();
- if (String.IsNullOrEmpty(attributesXml))
- return ids;
-
- try
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.LoadXml(attributesXml);
-
- var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
- foreach (XmlNode node1 in nodeList1)
- {
- if (node1.Attributes != null && node1.Attributes["ID"] != null)
- {
- string str1 = node1.Attributes["ID"].InnerText.Trim();
- int id;
- if (int.TryParse(str1, out id))
- {
- ids.Add(id);
- }
- }
- }
- }
- catch (Exception exc)
- {
- Debug.Write(exc.ToString());
- }
- return ids;
- }
-
- ///
- /// Gets selected product attribute values with the quantity entered by the customer
- ///
- /// Attributes in XML format
- /// Product attribute mapping identifier
- /// Collections of pairs of product attribute values and their quantity
- protected IList> ParseValuesWithQuantity(string attributesXml, int productAttributeMappingId)
- {
- var selectedValues = new List>();
- if (string.IsNullOrEmpty(attributesXml))
- return selectedValues;
-
- try
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.LoadXml(attributesXml);
-
- foreach (XmlNode attributeNode in xmlDoc.SelectNodes(@"//Attributes/ProductAttribute"))
- {
- if (attributeNode.Attributes != null && attributeNode.Attributes["ID"] != null)
- {
- int attributeId;
- if (int.TryParse(attributeNode.Attributes["ID"].InnerText.Trim(), out attributeId) && attributeId == productAttributeMappingId)
- {
- foreach (XmlNode attributeValue in attributeNode.SelectNodes("ProductAttributeValue"))
- {
- var value = attributeValue.SelectSingleNode("Value").InnerText.Trim();
- var quantityNode = attributeValue.SelectSingleNode("Quantity");
- selectedValues.Add(new Tuple(value, quantityNode != null ? quantityNode.InnerText.Trim() : string.Empty));
- }
- }
- }
- }
- }
- catch { }
-
- return selectedValues;
- }
-
- ///
- /// Gets selected product attribute mappings
- ///
- /// Attributes in XML format
- /// Selected product attribute mappings
- public virtual IList ParseProductAttributeMappings(string attributesXml)
- {
- var result = new List();
- if (String.IsNullOrEmpty(attributesXml))
- return result;
-
- var ids = ParseProductAttributeMappingIds(attributesXml);
- foreach (int id in ids)
- {
- var attribute = _productAttributeService.GetProductAttributeMappingById(id);
- if (attribute != null)
- {
- result.Add(attribute);
- }
- }
- return result;
- }
-
- ///
- /// Get product attribute values
- ///
- /// Attributes in XML format
- /// Product attribute mapping identifier; pass 0 to load all values
- /// Product attribute values
- public virtual IList ParseProductAttributeValues(string attributesXml, int productAttributeMappingId = 0)
- {
- var values = new List();
- if (string.IsNullOrEmpty(attributesXml))
- return values;
-
- var attributes = ParseProductAttributeMappings(attributesXml);
-
- //to load values only for the passed product attribute mapping
- if (productAttributeMappingId > 0)
- attributes = attributes.Where(attribute => attribute.Id == productAttributeMappingId).ToList();
-
- foreach (var attribute in attributes)
- {
- if (!attribute.ShouldHaveValues())
- continue;
-
- foreach (var attributeValue in ParseValuesWithQuantity(attributesXml, attribute.Id))
- {
- int attributeValueId;
- if (!string.IsNullOrEmpty(attributeValue.Item1) && int.TryParse(attributeValue.Item1, out attributeValueId))
- {
- var value = _productAttributeService.GetProductAttributeValueById(attributeValueId);
- if (value != null)
- {
- int quantity;
- if (!string.IsNullOrEmpty(attributeValue.Item2) && int.TryParse(attributeValue.Item2, out quantity) && quantity != value.Quantity)
- {
- //if customer enters quantity, use new entity with new quantity
- var oldValue = _context.LoadOriginalCopy(value);
- oldValue.ProductAttributeMapping = attribute;
- oldValue.Quantity = quantity;
- values.Add(oldValue);
- }
- else
- values.Add(value);
- }
- }
- }
- }
- return values;
- }
-
- ///
- /// Gets selected product attribute values
- ///
- /// Attributes in XML format
- /// Product attribute mapping identifier
- /// Product attribute values
- public virtual IList ParseValues(string attributesXml, int productAttributeMappingId)
- {
- var selectedValues = new List();
- if (String.IsNullOrEmpty(attributesXml))
- return selectedValues;
-
- try
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.LoadXml(attributesXml);
-
- var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
- foreach (XmlNode node1 in nodeList1)
- {
- if (node1.Attributes != null && node1.Attributes["ID"] != null)
- {
- string str1 =node1.Attributes["ID"].InnerText.Trim();
- int id;
- if (int.TryParse(str1, out id))
- {
- if (id == productAttributeMappingId)
- {
- var nodeList2 = node1.SelectNodes(@"ProductAttributeValue/Value");
- foreach (XmlNode node2 in nodeList2)
- {
- string value = node2.InnerText.Trim();
- selectedValues.Add(value);
- }
- }
- }
- }
- }
- }
- catch (Exception exc)
- {
- Debug.Write(exc.ToString());
- }
- return selectedValues;
- }
-
- ///
- /// Adds an attribute
- ///
- /// Attributes in XML format
- /// Product attribute mapping
- /// Value
- /// Quantity (used with AttributeValueType.AssociatedToProduct to specify the quantity entered by the customer)
- /// Updated result (XML format)
- public virtual string AddProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping, string value, int? quantity = null)
- {
- string result = string.Empty;
- try
- {
- var xmlDoc = new XmlDocument();
- if (String.IsNullOrEmpty(attributesXml))
- {
- var element1 = xmlDoc.CreateElement("Attributes");
- xmlDoc.AppendChild(element1);
- }
- else
- {
- xmlDoc.LoadXml(attributesXml);
- }
- var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
-
- XmlElement attributeElement = null;
- //find existing
- var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
- foreach (XmlNode node1 in nodeList1)
- {
- if (node1.Attributes != null && node1.Attributes["ID"] != null)
- {
- string str1 =node1.Attributes["ID"].InnerText.Trim();
- int id;
- if (int.TryParse(str1, out id))
- {
- if (id == productAttributeMapping.Id)
- {
- attributeElement = (XmlElement)node1;
- break;
- }
- }
- }
- }
-
- //create new one if not found
- if (attributeElement == null)
- {
- attributeElement = xmlDoc.CreateElement("ProductAttribute");
- attributeElement.SetAttribute("ID", productAttributeMapping.Id.ToString());
- rootElement.AppendChild(attributeElement);
- }
- var attributeValueElement = xmlDoc.CreateElement("ProductAttributeValue");
- attributeElement.AppendChild(attributeValueElement);
-
- var attributeValueValueElement = xmlDoc.CreateElement("Value");
- attributeValueValueElement.InnerText = value;
- attributeValueElement.AppendChild(attributeValueValueElement);
-
- //the quantity entered by the customer
- if (quantity.HasValue)
- {
- var attributeValueQuantity = xmlDoc.CreateElement("Quantity");
- attributeValueQuantity.InnerText = quantity.ToString();
- attributeValueElement.AppendChild(attributeValueQuantity);
- }
-
- result = xmlDoc.OuterXml;
- }
- catch (Exception exc)
- {
- Debug.Write(exc.ToString());
- }
- return result;
- }
-
- ///
- /// Remove an attribute
- ///
- /// Attributes in XML format
- /// Product attribute mapping
- /// Updated result (XML format)
- public virtual string RemoveProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping)
- {
- string result = string.Empty;
- try
- {
- var xmlDoc = new XmlDocument();
- if (String.IsNullOrEmpty(attributesXml))
- {
- var element1 = xmlDoc.CreateElement("Attributes");
- xmlDoc.AppendChild(element1);
- }
- else
- {
- xmlDoc.LoadXml(attributesXml);
- }
- var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
-
- XmlElement attributeElement = null;
- //find existing
- var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
- foreach (XmlNode node1 in nodeList1)
- {
- if (node1.Attributes != null && node1.Attributes["ID"] != null)
- {
- string str1 = node1.Attributes["ID"].InnerText.Trim();
- int id;
- if (int.TryParse(str1, out id))
- {
- if (id == productAttributeMapping.Id)
- {
- attributeElement = (XmlElement)node1;
- break;
- }
- }
- }
- }
-
- //found
- if (attributeElement != null)
- {
- rootElement.RemoveChild(attributeElement);
- }
-
- result = xmlDoc.OuterXml;
- }
- catch (Exception exc)
- {
- Debug.Write(exc.ToString());
- }
- return result;
- }
-
- ///
- /// Are attributes equal
- ///
- /// The attributes of the first product
- /// The attributes of the second product
- /// A value indicating whether we should ignore non-combinable attributes
- /// A value indicating whether we should ignore the quantity of attribute value entered by the customer
- /// Result
- public virtual bool AreProductAttributesEqual(string attributesXml1, string attributesXml2, bool ignoreNonCombinableAttributes, bool ignoreQuantity = true)
- {
- var attributes1 = ParseProductAttributeMappings(attributesXml1);
- if (ignoreNonCombinableAttributes)
- {
- attributes1 = attributes1.Where(x => !x.IsNonCombinable()).ToList();
- }
- var attributes2 = ParseProductAttributeMappings(attributesXml2);
- if (ignoreNonCombinableAttributes)
- {
- attributes2 = attributes2.Where(x => !x.IsNonCombinable()).ToList();
- }
- if (attributes1.Count != attributes2.Count)
- return false;
-
- bool attributesEqual = true;
- foreach (var a1 in attributes1)
- {
- bool hasAttribute = false;
- foreach (var a2 in attributes2)
- {
- if (a1.Id == a2.Id)
- {
- hasAttribute = true;
- var values1Str = ParseValuesWithQuantity(attributesXml1, a1.Id);
- var values2Str = ParseValuesWithQuantity(attributesXml2, a2.Id);
- if (values1Str.Count == values2Str.Count)
- {
- foreach (var str1 in values1Str)
- {
- bool hasValue = false;
- foreach (var str2 in values2Str)
- {
- //case insensitive?
- //if (str1.Trim().ToLower() == str2.Trim().ToLower())
- if (str1.Item1.Trim() == str2.Item1.Trim())
- {
- hasValue = ignoreQuantity ? true : str1.Item2.Trim() == str2.Item2.Trim();
- break;
- }
- }
-
- if (!hasValue)
- {
- attributesEqual = false;
- break;
- }
- }
- }
- else
- {
- attributesEqual = false;
- break;
- }
- }
- }
-
- if (hasAttribute == false)
- {
- attributesEqual = false;
- break;
- }
- }
-
- return attributesEqual;
- }
-
- ///
- /// Check whether condition of some attribute is met (if specified). Return "null" if not condition is specified
- ///
- /// Product attribute
- /// Selected attributes (XML format)
- /// Result
- public virtual bool? IsConditionMet(ProductAttributeMapping pam, string selectedAttributesXml)
- {
- if (pam == null)
- throw new ArgumentNullException("pam");
-
- var conditionAttributeXml = pam.ConditionAttributeXml;
- if (String.IsNullOrEmpty(conditionAttributeXml))
- //no condition
- return null;
-
- //load an attribute this one depends on
- var dependOnAttribute = ParseProductAttributeMappings(conditionAttributeXml).FirstOrDefault();
- if (dependOnAttribute == null)
- return true;
-
- var valuesThatShouldBeSelected = ParseValues(conditionAttributeXml, dependOnAttribute.Id)
- //a workaround here:
- //ConditionAttributeXml can contain "empty" values (nothing is selected)
- //but in other cases (like below) we do not store empty values
- //that's why we remove empty values here
- .Where(x => !String.IsNullOrEmpty(x))
- .ToList();
- var selectedValues = ParseValues(selectedAttributesXml, dependOnAttribute.Id);
- if (valuesThatShouldBeSelected.Count != selectedValues.Count)
- return false;
-
- //compare values
- var allFound = true;
- foreach (var t1 in valuesThatShouldBeSelected)
- {
- bool found = false;
- foreach (var t2 in selectedValues)
- if (t1 == t2)
- found = true;
- if (!found)
- allFound = false;
- }
-
- return allFound;
- }
-
- ///
- /// Finds a product attribute combination by attributes stored in XML
- ///
- /// Product
- /// Attributes in XML format
- /// A value indicating whether we should ignore non-combinable attributes
- /// Found product attribute combination
- public virtual ProductAttributeCombination FindProductAttributeCombination(Product product,
- string attributesXml, bool ignoreNonCombinableAttributes = true)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- var combinations = _productAttributeService.GetAllProductAttributeCombinations(product.Id);
- return combinations.FirstOrDefault(x =>
- AreProductAttributesEqual(x.AttributesXml, attributesXml, ignoreNonCombinableAttributes));
- }
-
- ///
- /// Generate all combinations
- ///
- /// Product
- /// A value indicating whether we should ignore non-combinable attributes
- /// Attribute combinations in XML format
- public virtual IList GenerateAllCombinations(Product product, bool ignoreNonCombinableAttributes = false)
- {
- if (product == null)
- throw new ArgumentNullException("product");
-
- var allProductAttributMappings = _productAttributeService.GetProductAttributeMappingsByProductId(product.Id);
- if (ignoreNonCombinableAttributes)
- {
- allProductAttributMappings = allProductAttributMappings.Where(x => !x.IsNonCombinable()).ToList();
- }
- var allPossibleAttributeCombinations = new List>();
- for (int counter = 0; counter < (1 << allProductAttributMappings.Count); ++counter)
- {
- var combination = new List();
- for (int i = 0; i < allProductAttributMappings.Count; ++i)
- {
- if ((counter & (1 << i)) == 0)
- {
- combination.Add(allProductAttributMappings[i]);
- }
- }
-
- allPossibleAttributeCombinations.Add(combination);
- }
-
- var allAttributesXml = new List();
- foreach (var combination in allPossibleAttributeCombinations)
- {
- var attributesXml = new List();
- foreach (var pam in combination)
- {
- if (!pam.ShouldHaveValues())
- continue;
-
- var attributeValues = _productAttributeService.GetProductAttributeValues(pam.Id);
- if (!attributeValues.Any())
- continue;
-
- //checkboxes could have several values ticked
- var allPossibleCheckboxCombinations = new List>();
- if (pam.AttributeControlType == AttributeControlType.Checkboxes ||
- pam.AttributeControlType == AttributeControlType.ReadonlyCheckboxes)
- {
- for (int counter = 0; counter < (1 << attributeValues.Count); ++counter)
- {
- var checkboxCombination = new List();
- for (int i = 0; i < attributeValues.Count; ++i)
- {
- if ((counter & (1 << i)) == 0)
- {
- checkboxCombination.Add(attributeValues[i]);
- }
- }
-
- allPossibleCheckboxCombinations.Add(checkboxCombination);
- }
- }
-
- if (!attributesXml.Any())
- {
- //first set of values
- if (pam.AttributeControlType == AttributeControlType.Checkboxes ||
- pam.AttributeControlType == AttributeControlType.ReadonlyCheckboxes)
- {
- //checkboxes could have several values ticked
- foreach (var checkboxCombination in allPossibleCheckboxCombinations)
- {
- var tmp1 = "";
- foreach (var checkboxValue in checkboxCombination)
- {
- tmp1 = AddProductAttribute(tmp1, pam, checkboxValue.Id.ToString());
- }
- if (!String.IsNullOrEmpty(tmp1))
- {
- attributesXml.Add(tmp1);
- }
- }
- }
- else
- {
- //other attribute types (dropdownlist, radiobutton, color squares)
- foreach (var attributeValue in attributeValues)
- {
- var tmp1 = AddProductAttribute("", pam, attributeValue.Id.ToString());
- attributesXml.Add(tmp1);
- }
- }
- }
- else
- {
- //next values. let's "append" them to already generated attribute combinations in XML format
- var attributesXmlTmp = new List();
- if (pam.AttributeControlType == AttributeControlType.Checkboxes ||
- pam.AttributeControlType == AttributeControlType.ReadonlyCheckboxes)
- {
- //checkboxes could have several values ticked
- foreach (var str1 in attributesXml)
- {
- foreach (var checkboxCombination in allPossibleCheckboxCombinations)
- {
- var tmp1 = str1;
- foreach (var checkboxValue in checkboxCombination)
- {
- tmp1 = AddProductAttribute(tmp1, pam, checkboxValue.Id.ToString());
- }
- if (!String.IsNullOrEmpty(tmp1))
- {
- attributesXmlTmp.Add(tmp1);
- }
- }
- }
- }
- else
- {
- //other attribute types (dropdownlist, radiobutton, color squares)
- foreach (var attributeValue in attributeValues)
- {
- foreach (var str1 in attributesXml)
- {
- var tmp1 = AddProductAttribute(str1, pam, attributeValue.Id.ToString());
- attributesXmlTmp.Add(tmp1);
- }
- }
- }
- attributesXml.Clear();
- attributesXml.AddRange(attributesXmlTmp);
- }
- }
- allAttributesXml.AddRange(attributesXml);
- }
-
- //validate conditional attributes (if specified)
- //minor workaround:
- //once it's done (validation), then we could have some duplicated combinations in result
- //we don't remove them here (for performance optimization) because anyway it'll be done in the "GenerateAllAttributeCombinations" method of ProductController
- for (int i = 0; i < allAttributesXml.Count; i++)
- {
- var attributesXml = allAttributesXml[i];
- foreach (var attribute in allProductAttributMappings)
- {
- var conditionMet = IsConditionMet(attribute, attributesXml);
- if (conditionMet.HasValue && !conditionMet.Value)
- {
- allAttributesXml[i] = RemoveProductAttribute(attributesXml, attribute);
- }
- }
- }
- return allAttributesXml;
- }
-
- #endregion
-
- #region Gift card attributes
-
- ///
- /// Add gift card attrbibutes
- ///
- /// Attributes in XML format
- /// Recipient name
- /// Recipient email
- /// Sender name
- /// Sender email
- /// Message
- /// Attributes
- public string AddGiftCardAttribute(string attributesXml, string recipientName,
- string recipientEmail, string senderName, string senderEmail, string giftCardMessage)
- {
- string result = string.Empty;
- try
- {
- recipientName = recipientName.Trim();
- recipientEmail = recipientEmail.Trim();
- senderName = senderName.Trim();
- senderEmail = senderEmail.Trim();
-
- var xmlDoc = new XmlDocument();
- if (String.IsNullOrEmpty(attributesXml))
- {
- var element1 = xmlDoc.CreateElement("Attributes");
- xmlDoc.AppendChild(element1);
- }
- else
- {
- xmlDoc.LoadXml(attributesXml);
- }
-
- var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
-
- var giftCardElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo");
- if (giftCardElement == null)
- {
- giftCardElement = xmlDoc.CreateElement("GiftCardInfo");
- rootElement.AppendChild(giftCardElement);
- }
-
- var recipientNameElement = xmlDoc.CreateElement("RecipientName");
- recipientNameElement.InnerText = recipientName;
- giftCardElement.AppendChild(recipientNameElement);
-
- var recipientEmailElement = xmlDoc.CreateElement("RecipientEmail");
- recipientEmailElement.InnerText = recipientEmail;
- giftCardElement.AppendChild(recipientEmailElement);
-
- var senderNameElement = xmlDoc.CreateElement("SenderName");
- senderNameElement.InnerText = senderName;
- giftCardElement.AppendChild(senderNameElement);
-
- var senderEmailElement = xmlDoc.CreateElement("SenderEmail");
- senderEmailElement.InnerText = senderEmail;
- giftCardElement.AppendChild(senderEmailElement);
-
- var messageElement = xmlDoc.CreateElement("Message");
- messageElement.InnerText = giftCardMessage;
- giftCardElement.AppendChild(messageElement);
-
- result = xmlDoc.OuterXml;
- }
- catch (Exception exc)
- {
- Debug.Write(exc.ToString());
- }
- return result;
- }
-
- ///
- /// Get gift card attrbibutes
- ///
- /// Attributes
- /// Recipient name
- /// Recipient email
- /// Sender name
- /// Sender email
- /// Message
- public void GetGiftCardAttribute(string attributesXml, out string recipientName,
- out string recipientEmail, out string senderName,
- out string senderEmail, out string giftCardMessage)
- {
- recipientName = string.Empty;
- recipientEmail = string.Empty;
- senderName = string.Empty;
- senderEmail = string.Empty;
- giftCardMessage = string.Empty;
-
- try
- {
- var xmlDoc = new XmlDocument();
- xmlDoc.LoadXml(attributesXml);
-
- var recipientNameElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/RecipientName");
- var recipientEmailElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/RecipientEmail");
- var senderNameElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/SenderName");
- var senderEmailElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/SenderEmail");
- var messageElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/Message");
-
- if (recipientNameElement != null)
- recipientName = recipientNameElement.InnerText;
- if (recipientEmailElement != null)
- recipientEmail = recipientEmailElement.InnerText;
- if (senderNameElement != null)
- senderName = senderNameElement.InnerText;
- if (senderEmailElement != null)
- senderEmail = senderEmailElement.InnerText;
- if (messageElement != null)
- giftCardMessage = messageElement.InnerText;
- }
- catch (Exception exc)
- {
- Debug.Write(exc.ToString());
- }
- }
-
- #endregion
- }
-}
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Linq;
+using System.Xml;
+using Nop.Core.Domain.Catalog;
+using Nop.Data;
+using Nop.Services.Tax;
+using System.Globalization;
+
+namespace Nop.Services.Catalog
+{
+ ///
+ /// Product attribute parser
+ ///
+ public partial class ProductAttributeParser : IProductAttributeParser
+ {
+ #region Fields
+
+ private readonly IDbContext _context;
+ private readonly IProductAttributeService _productAttributeService;
+
+ #endregion
+
+ #region Ctor
+
+ public ProductAttributeParser(IDbContext context,
+ IProductAttributeService productAttributeService)
+ {
+ this._context = context;
+ this._productAttributeService = productAttributeService;
+ }
+
+ #endregion
+
+ #region Product attributes
+
+ ///
+ /// Gets selected product attribute mapping identifiers
+ ///
+ /// Attributes in XML format
+ /// Selected product attribute mapping identifiers
+ protected virtual IList ParseProductAttributeMappingIds(string attributesXml)
+ {
+ var ids = new List();
+ if (String.IsNullOrEmpty(attributesXml))
+ return ids;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(attributesXml);
+
+ var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
+ foreach (XmlNode node1 in nodeList1)
+ {
+ if (node1.Attributes != null && node1.Attributes["ID"] != null)
+ {
+ string str1 = node1.Attributes["ID"].InnerText.Trim();
+ int id;
+ if (int.TryParse(str1, out id))
+ {
+ ids.Add(id);
+ }
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ return ids;
+ }
+
+ ///
+ /// Gets selected product attribute values with the quantity entered by the customer
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping identifier
+ /// Collections of pairs of product attribute values and their quantity
+ protected IList> ParseValuesWithQuantity(string attributesXml, int productAttributeMappingId)
+ {
+ var selectedValues = new List>();
+ if (string.IsNullOrEmpty(attributesXml))
+ return selectedValues;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(attributesXml);
+
+ foreach (XmlNode attributeNode in xmlDoc.SelectNodes(@"//Attributes/ProductAttribute"))
+ {
+ if (attributeNode.Attributes != null && attributeNode.Attributes["ID"] != null)
+ {
+ int attributeId;
+ if (int.TryParse(attributeNode.Attributes["ID"].InnerText.Trim(), out attributeId) && attributeId == productAttributeMappingId)
+ {
+ foreach (XmlNode attributeValue in attributeNode.SelectNodes("ProductAttributeValue"))
+ {
+ var value = attributeValue.SelectSingleNode("Value").InnerText.Trim();
+ var quantityNode = attributeValue.SelectSingleNode("Quantity");
+ selectedValues.Add(new Tuple(value, quantityNode != null ? quantityNode.InnerText.Trim() : string.Empty));
+ }
+ }
+ }
+ }
+ }
+ catch { }
+
+ return selectedValues;
+ }
+
+ ///
+ /// Gets selected product attribute mappings
+ ///
+ /// Attributes in XML format
+ /// Selected product attribute mappings
+ public virtual IList ParseProductAttributeMappings(string attributesXml)
+ {
+ var result = new List();
+ if (String.IsNullOrEmpty(attributesXml))
+ return result;
+
+ var ids = ParseProductAttributeMappingIds(attributesXml);
+ foreach (int id in ids)
+ {
+ var attribute = _productAttributeService.GetProductAttributeMappingById(id);
+ if (attribute != null)
+ {
+ result.Add(attribute);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// Get product attribute values
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping identifier; pass 0 to load all values
+ /// Product attribute values
+ public virtual IList ParseProductAttributeValues(string attributesXml, int productAttributeMappingId = 0)
+ {
+ var values = new List();
+ if (string.IsNullOrEmpty(attributesXml))
+ return values;
+
+ var attributes = ParseProductAttributeMappings(attributesXml);
+
+ //to load values only for the passed product attribute mapping
+ if (productAttributeMappingId > 0)
+ attributes = attributes.Where(attribute => attribute.Id == productAttributeMappingId).ToList();
+
+ foreach (var attribute in attributes)
+ {
+ if (!attribute.ShouldHaveValues())
+ continue;
+
+ foreach (var attributeValue in ParseValuesWithQuantity(attributesXml, attribute.Id))
+ {
+ int attributeValueId;
+ if (!string.IsNullOrEmpty(attributeValue.Item1) && int.TryParse(attributeValue.Item1, out attributeValueId))
+ {
+ var value = _productAttributeService.GetProductAttributeValueById(attributeValueId);
+ if (value != null)
+ {
+ int quantity;
+ if (!string.IsNullOrEmpty(attributeValue.Item2) && int.TryParse(attributeValue.Item2, out quantity) && quantity != value.Quantity)
+ {
+ //if customer enters quantity, use new entity with new quantity
+ var oldValue = _context.LoadOriginalCopy(value);
+ oldValue.ProductAttributeMapping = attribute;
+ oldValue.Quantity = quantity;
+ values.Add(oldValue);
+ }
+ else
+ values.Add(value);
+ }
+ }
+ }
+ }
+ return values;
+ }
+
+ ///
+ /// Gets selected product attribute values
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping identifier
+ /// Product attribute values
+ public virtual IList ParseValues(string attributesXml, int productAttributeMappingId)
+ {
+ var selectedValues = new List();
+ if (String.IsNullOrEmpty(attributesXml))
+ return selectedValues;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(attributesXml);
+
+ var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
+ foreach (XmlNode node1 in nodeList1)
+ {
+ if (node1.Attributes != null && node1.Attributes["ID"] != null)
+ {
+ string str1 =node1.Attributes["ID"].InnerText.Trim();
+ int id;
+ if (int.TryParse(str1, out id))
+ {
+ if (id == productAttributeMappingId)
+ {
+ var nodeList2 = node1.SelectNodes(@"ProductAttributeValue/Value");
+ foreach (XmlNode node2 in nodeList2)
+ {
+ string value = node2.InnerText.Trim();
+ selectedValues.Add(value);
+ }
+ }
+ }
+ }
+ }
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ return selectedValues;
+ }
+
+ ///
+ /// Adds an attribute
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping
+ /// Value
+ /// Quantity (used with AttributeValueType.AssociatedToProduct to specify the quantity entered by the customer)
+ /// Updated result (XML format)
+ public virtual string AddProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping, string value, int? quantity = null)
+ {
+ string result = string.Empty;
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ if (String.IsNullOrEmpty(attributesXml))
+ {
+ var element1 = xmlDoc.CreateElement("Attributes");
+ xmlDoc.AppendChild(element1);
+ }
+ else
+ {
+ xmlDoc.LoadXml(attributesXml);
+ }
+ var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
+
+ XmlElement attributeElement = null;
+ //find existing
+ var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
+ foreach (XmlNode node1 in nodeList1)
+ {
+ if (node1.Attributes != null && node1.Attributes["ID"] != null)
+ {
+ string str1 =node1.Attributes["ID"].InnerText.Trim();
+ int id;
+ if (int.TryParse(str1, out id))
+ {
+ if (id == productAttributeMapping.Id)
+ {
+ attributeElement = (XmlElement)node1;
+ break;
+ }
+ }
+ }
+ }
+
+ //create new one if not found
+ if (attributeElement == null)
+ {
+ attributeElement = xmlDoc.CreateElement("ProductAttribute");
+ attributeElement.SetAttribute("ID", productAttributeMapping.Id.ToString());
+ rootElement.AppendChild(attributeElement);
+ }
+ var attributeValueElement = xmlDoc.CreateElement("ProductAttributeValue");
+ attributeElement.AppendChild(attributeValueElement);
+
+ var attributeValueValueElement = xmlDoc.CreateElement("Value");
+ attributeValueValueElement.InnerText = value;
+ attributeValueElement.AppendChild(attributeValueValueElement);
+
+ //the quantity entered by the customer
+ if (quantity.HasValue)
+ {
+ var attributeValueQuantity = xmlDoc.CreateElement("Quantity");
+ attributeValueQuantity.InnerText = quantity.ToString();
+ attributeValueElement.AppendChild(attributeValueQuantity);
+ }
+
+ result = xmlDoc.OuterXml;
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ return result;
+ }
+
+ ///
+ /// Remove an attribute
+ ///
+ /// Attributes in XML format
+ /// Product attribute mapping
+ /// Updated result (XML format)
+ public virtual string RemoveProductAttribute(string attributesXml, ProductAttributeMapping productAttributeMapping)
+ {
+ string result = string.Empty;
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ if (String.IsNullOrEmpty(attributesXml))
+ {
+ var element1 = xmlDoc.CreateElement("Attributes");
+ xmlDoc.AppendChild(element1);
+ }
+ else
+ {
+ xmlDoc.LoadXml(attributesXml);
+ }
+ var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
+
+ XmlElement attributeElement = null;
+ //find existing
+ var nodeList1 = xmlDoc.SelectNodes(@"//Attributes/ProductAttribute");
+ foreach (XmlNode node1 in nodeList1)
+ {
+ if (node1.Attributes != null && node1.Attributes["ID"] != null)
+ {
+ string str1 = node1.Attributes["ID"].InnerText.Trim();
+ int id;
+ if (int.TryParse(str1, out id))
+ {
+ if (id == productAttributeMapping.Id)
+ {
+ attributeElement = (XmlElement)node1;
+ break;
+ }
+ }
+ }
+ }
+
+ //found
+ if (attributeElement != null)
+ {
+ rootElement.RemoveChild(attributeElement);
+ }
+
+ result = xmlDoc.OuterXml;
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ return result;
+ }
+
+ ///
+ /// Are attributes equal
+ ///
+ /// The attributes of the first product
+ /// The attributes of the second product
+ /// A value indicating whether we should ignore non-combinable attributes
+ /// A value indicating whether we should ignore the quantity of attribute value entered by the customer
+ /// Result
+ public virtual bool AreProductAttributesEqual(string attributesXml1, string attributesXml2, bool ignoreNonCombinableAttributes, bool ignoreQuantity = true)
+ {
+ var attributes1 = ParseProductAttributeMappings(attributesXml1);
+ if (ignoreNonCombinableAttributes)
+ {
+ attributes1 = attributes1.Where(x => !x.IsNonCombinable()).ToList();
+ }
+ var attributes2 = ParseProductAttributeMappings(attributesXml2);
+ if (ignoreNonCombinableAttributes)
+ {
+ attributes2 = attributes2.Where(x => !x.IsNonCombinable()).ToList();
+ }
+ if (attributes1.Count != attributes2.Count)
+ return false;
+
+ bool attributesEqual = true;
+ foreach (var a1 in attributes1)
+ {
+ bool hasAttribute = false;
+ foreach (var a2 in attributes2)
+ {
+ if (a1.Id == a2.Id)
+ {
+ hasAttribute = true;
+ var values1Str = ParseValuesWithQuantity(attributesXml1, a1.Id);
+ var values2Str = ParseValuesWithQuantity(attributesXml2, a2.Id);
+ if (values1Str.Count == values2Str.Count)
+ {
+ foreach (var str1 in values1Str)
+ {
+ bool hasValue = false;
+ foreach (var str2 in values2Str)
+ {
+ //case insensitive?
+ //if (str1.Trim().ToLower() == str2.Trim().ToLower())
+ if (str1.Item1.Trim() == str2.Item1.Trim())
+ {
+ hasValue = ignoreQuantity ? true : str1.Item2.Trim() == str2.Item2.Trim();
+ break;
+ }
+ }
+
+ if (!hasValue)
+ {
+ attributesEqual = false;
+ break;
+ }
+ }
+ }
+ else
+ {
+ attributesEqual = false;
+ break;
+ }
+ }
+ }
+
+ if (hasAttribute == false)
+ {
+ attributesEqual = false;
+ break;
+ }
+ }
+
+ return attributesEqual;
+ }
+
+ ///
+ /// Check whether condition of some attribute is met (if specified). Return "null" if not condition is specified
+ ///
+ /// Product attribute
+ /// Selected attributes (XML format)
+ /// Result
+ public virtual bool? IsConditionMet(ProductAttributeMapping pam, string selectedAttributesXml)
+ {
+ if (pam == null)
+ throw new ArgumentNullException("pam");
+
+ var conditionAttributeXml = pam.ConditionAttributeXml;
+ if (String.IsNullOrEmpty(conditionAttributeXml))
+ //no condition
+ return null;
+
+ //load an attribute this one depends on
+ var dependOnAttribute = ParseProductAttributeMappings(conditionAttributeXml).FirstOrDefault();
+ if (dependOnAttribute == null)
+ return true;
+
+ var valuesThatShouldBeSelected = ParseValues(conditionAttributeXml, dependOnAttribute.Id)
+ //a workaround here:
+ //ConditionAttributeXml can contain "empty" values (nothing is selected)
+ //but in other cases (like below) we do not store empty values
+ //that's why we remove empty values here
+ .Where(x => !String.IsNullOrEmpty(x))
+ .ToList();
+ var selectedValues = ParseValues(selectedAttributesXml, dependOnAttribute.Id);
+ if (valuesThatShouldBeSelected.Count != selectedValues.Count)
+ return false;
+
+ //compare values
+ var allFound = true;
+ foreach (var t1 in valuesThatShouldBeSelected)
+ {
+ bool found = false;
+ foreach (var t2 in selectedValues)
+ if (t1 == t2)
+ found = true;
+ if (!found)
+ allFound = false;
+ }
+
+ return allFound;
+ }
+
+ ///
+ /// Finds a product attribute combination by attributes stored in XML
+ ///
+ /// Product
+ /// Attributes in XML format
+ /// A value indicating whether we should ignore non-combinable attributes
+ /// Found product attribute combination
+ public virtual ProductAttributeCombination FindProductAttributeCombination(Product product,
+ string attributesXml, bool ignoreNonCombinableAttributes = true)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ var combinations = _productAttributeService.GetAllProductAttributeCombinations(product.Id);
+ return combinations.FirstOrDefault(x =>
+ AreProductAttributesEqual(x.AttributesXml, attributesXml, ignoreNonCombinableAttributes));
+ }
+
+ ///
+ /// Generate all combinations
+ ///
+ /// Product
+ /// A value indicating whether we should ignore non-combinable attributes
+ /// Attribute combinations in XML format
+ public virtual IList GenerateAllCombinations(Product product, bool ignoreNonCombinableAttributes = false)
+ {
+ if (product == null)
+ throw new ArgumentNullException("product");
+
+ var allProductAttributMappings = _productAttributeService.GetProductAttributeMappingsByProductId(product.Id);
+ if (ignoreNonCombinableAttributes)
+ {
+ allProductAttributMappings = allProductAttributMappings.Where(x => !x.IsNonCombinable()).ToList();
+ }
+ var allPossibleAttributeCombinations = new List>();
+ for (int counter = 0; counter < (1 << allProductAttributMappings.Count); ++counter)
+ {
+ var combination = new List();
+ for (int i = 0; i < allProductAttributMappings.Count; ++i)
+ {
+ if ((counter & (1 << i)) == 0)
+ {
+ combination.Add(allProductAttributMappings[i]);
+ }
+ }
+
+ allPossibleAttributeCombinations.Add(combination);
+ }
+
+ var allAttributesXml = new List();
+ foreach (var combination in allPossibleAttributeCombinations)
+ {
+ var attributesXml = new List();
+ foreach (var pam in combination)
+ {
+ if (!pam.ShouldHaveValues())
+ continue;
+
+ var attributeValues = _productAttributeService.GetProductAttributeValues(pam.Id);
+ if (!attributeValues.Any())
+ continue;
+
+ //checkboxes could have several values ticked
+ var allPossibleCheckboxCombinations = new List>();
+ if (pam.AttributeControlType == AttributeControlType.Checkboxes ||
+ pam.AttributeControlType == AttributeControlType.ReadonlyCheckboxes)
+ {
+ for (int counter = 0; counter < (1 << attributeValues.Count); ++counter)
+ {
+ var checkboxCombination = new List();
+ for (int i = 0; i < attributeValues.Count; ++i)
+ {
+ if ((counter & (1 << i)) == 0)
+ {
+ checkboxCombination.Add(attributeValues[i]);
+ }
+ }
+
+ allPossibleCheckboxCombinations.Add(checkboxCombination);
+ }
+ }
+
+ if (!attributesXml.Any())
+ {
+ //first set of values
+ if (pam.AttributeControlType == AttributeControlType.Checkboxes ||
+ pam.AttributeControlType == AttributeControlType.ReadonlyCheckboxes)
+ {
+ //checkboxes could have several values ticked
+ foreach (var checkboxCombination in allPossibleCheckboxCombinations)
+ {
+ var tmp1 = "";
+ foreach (var checkboxValue in checkboxCombination)
+ {
+ tmp1 = AddProductAttribute(tmp1, pam, checkboxValue.Id.ToString());
+ }
+ if (!String.IsNullOrEmpty(tmp1))
+ {
+ attributesXml.Add(tmp1);
+ }
+ }
+ }
+ else
+ {
+ //other attribute types (dropdownlist, radiobutton, color squares)
+ foreach (var attributeValue in attributeValues)
+ {
+ var tmp1 = AddProductAttribute("", pam, attributeValue.Id.ToString());
+ attributesXml.Add(tmp1);
+ }
+ }
+ }
+ else
+ {
+ //next values. let's "append" them to already generated attribute combinations in XML format
+ var attributesXmlTmp = new List();
+ if (pam.AttributeControlType == AttributeControlType.Checkboxes ||
+ pam.AttributeControlType == AttributeControlType.ReadonlyCheckboxes)
+ {
+ //checkboxes could have several values ticked
+ foreach (var str1 in attributesXml)
+ {
+ foreach (var checkboxCombination in allPossibleCheckboxCombinations)
+ {
+ var tmp1 = str1;
+ foreach (var checkboxValue in checkboxCombination)
+ {
+ tmp1 = AddProductAttribute(tmp1, pam, checkboxValue.Id.ToString());
+ }
+ if (!String.IsNullOrEmpty(tmp1))
+ {
+ attributesXmlTmp.Add(tmp1);
+ }
+ }
+ }
+ }
+ else
+ {
+ //other attribute types (dropdownlist, radiobutton, color squares)
+ foreach (var attributeValue in attributeValues)
+ {
+ foreach (var str1 in attributesXml)
+ {
+ var tmp1 = AddProductAttribute(str1, pam, attributeValue.Id.ToString());
+ attributesXmlTmp.Add(tmp1);
+ }
+ }
+ }
+ attributesXml.Clear();
+ attributesXml.AddRange(attributesXmlTmp);
+ }
+ }
+ allAttributesXml.AddRange(attributesXml);
+ }
+
+ //validate conditional attributes (if specified)
+ //minor workaround:
+ //once it's done (validation), then we could have some duplicated combinations in result
+ //we don't remove them here (for performance optimization) because anyway it'll be done in the "GenerateAllAttributeCombinations" method of ProductController
+ for (int i = 0; i < allAttributesXml.Count; i++)
+ {
+ var attributesXml = allAttributesXml[i];
+ foreach (var attribute in allProductAttributMappings)
+ {
+ var conditionMet = IsConditionMet(attribute, attributesXml);
+ if (conditionMet.HasValue && !conditionMet.Value)
+ {
+ allAttributesXml[i] = RemoveProductAttribute(attributesXml, attribute);
+ }
+ }
+ }
+ return allAttributesXml;
+ }
+
+ #endregion
+
+ #region taxAttribute
+ ///
+ /// Adds tax subdivision to existing attributesXml
+ ///
+ /// Attributes in XML format
+ /// Set Product tax subdivision
+ /// Updated result (XML format)
+ public virtual string AddTaxAttribute(string attributesXml, TaxSummary taxSummary)
+ {
+ string result = string.Empty;
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ if (String.IsNullOrEmpty(attributesXml))
+ {
+ var element1 = xmlDoc.CreateElement("Attributes");
+ xmlDoc.AppendChild(element1);
+ }
+ else
+ {
+ xmlDoc.LoadXml(attributesXml);
+ }
+ var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
+ var attributeTaxElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/ProductAttributesTax");
+
+ if (taxSummary.TaxRates.Any())
+ {
+ //create new ProductAttributesTax element if not found
+ if (attributeTaxElement == null)
+ {
+ attributeTaxElement = xmlDoc.CreateElement("ProductAttributesTax");
+ rootElement.AppendChild(attributeTaxElement);
+ }
+
+ foreach (KeyValuePair kvp in taxSummary.TaxRates)
+ {
+ decimal vatpercentage = kvp.Key;
+ TaxRateEntry taxrate = kvp.Value;
+
+ //find existing
+ var strVatPer = vatpercentage.ToString(CultureInfo.InvariantCulture);
+ var strNode = String.Format("//Attributes/ProductAttributesTax/TaxRate[@Rate='{0}']", strVatPer);
+ var attributeTaxRateElement = (XmlElement)xmlDoc.SelectSingleNode(@strNode);
+
+ //create new one if not found
+ if (attributeTaxRateElement == null)
+ {
+ attributeTaxRateElement = xmlDoc.CreateElement("TaxRate");
+ attributeTaxRateElement.SetAttribute("Rate", strVatPer);
+ attributeTaxElement.AppendChild(attributeTaxRateElement);
+ }
+ attributeTaxRateElement.SetAttribute("RateWeight", taxrate.TaxRateWeight.ToString(CultureInfo.InvariantCulture));
+ }
+ }
+ else
+ {
+ //remove attributeTaxElement when no taxRates
+ if (attributeTaxElement != null)
+ {
+ rootElement.RemoveChild(attributeTaxElement);
+ }
+ }
+ result = xmlDoc.OuterXml;
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ return result;
+ }
+ ///
+ /// Parse ProductAttributesTax
+ ///
+ /// Attributes in XML format
+ /// SortedDictionary with taxRate and taxRateWeight
+ public SortedDictionary ParseTaxAttribute(string attributesXml)
+ {
+ var taxDict = new SortedDictionary();
+ var xmlDoc = new XmlDocument();
+ if (!String.IsNullOrEmpty(attributesXml))
+ {
+ xmlDoc.LoadXml(attributesXml);
+ foreach (XmlElement e in xmlDoc.SelectNodes("//Attributes/ProductAttributesTax/TaxRate"))
+ {
+ var style = NumberStyles.AllowDecimalPoint;
+ decimal rate = decimal.Zero; decimal rateWeight = decimal.Zero;
+ if (decimal.TryParse(e.GetAttribute("Rate"), style, CultureInfo.InvariantCulture, out rate) && decimal.TryParse(e.GetAttribute("RateWeight"), style, CultureInfo.InvariantCulture, out rateWeight))
+ taxDict.Add(rate, rateWeight);
+ }
+
+ }
+ return taxDict;
+ }
+
+ ///
+ /// Check if attributesXml contains tax attribute info
+ ///
+ /// Attributes in XML format
+ /// A boolean value indicating if tax attribute info is present
+ public bool hasTaxInfoInAttributeXML(string attributesXml)
+ {
+ return attributesXml.Contains("Attributes in XML format
+ /// Recipient name
+ /// Recipient email
+ /// Sender name
+ /// Sender email
+ /// Message
+ /// Attributes
+ public string AddGiftCardAttribute(string attributesXml, string recipientName,
+ string recipientEmail, string senderName, string senderEmail, string giftCardMessage)
+ {
+ string result = string.Empty;
+ try
+ {
+ recipientName = recipientName.Trim();
+ recipientEmail = recipientEmail.Trim();
+ senderName = senderName.Trim();
+ senderEmail = senderEmail.Trim();
+
+ var xmlDoc = new XmlDocument();
+ if (String.IsNullOrEmpty(attributesXml))
+ {
+ var element1 = xmlDoc.CreateElement("Attributes");
+ xmlDoc.AppendChild(element1);
+ }
+ else
+ {
+ xmlDoc.LoadXml(attributesXml);
+ }
+
+ var rootElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes");
+
+ var giftCardElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo");
+ if (giftCardElement == null)
+ {
+ giftCardElement = xmlDoc.CreateElement("GiftCardInfo");
+ rootElement.AppendChild(giftCardElement);
+ }
+
+ var recipientNameElement = xmlDoc.CreateElement("RecipientName");
+ recipientNameElement.InnerText = recipientName;
+ giftCardElement.AppendChild(recipientNameElement);
+
+ var recipientEmailElement = xmlDoc.CreateElement("RecipientEmail");
+ recipientEmailElement.InnerText = recipientEmail;
+ giftCardElement.AppendChild(recipientEmailElement);
+
+ var senderNameElement = xmlDoc.CreateElement("SenderName");
+ senderNameElement.InnerText = senderName;
+ giftCardElement.AppendChild(senderNameElement);
+
+ var senderEmailElement = xmlDoc.CreateElement("SenderEmail");
+ senderEmailElement.InnerText = senderEmail;
+ giftCardElement.AppendChild(senderEmailElement);
+
+ var messageElement = xmlDoc.CreateElement("Message");
+ messageElement.InnerText = giftCardMessage;
+ giftCardElement.AppendChild(messageElement);
+
+ result = xmlDoc.OuterXml;
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ return result;
+ }
+
+ ///
+ /// Get gift card attrbibutes
+ ///
+ /// Attributes
+ /// Recipient name
+ /// Recipient email
+ /// Sender name
+ /// Sender email
+ /// Message
+ public void GetGiftCardAttribute(string attributesXml, out string recipientName,
+ out string recipientEmail, out string senderName,
+ out string senderEmail, out string giftCardMessage)
+ {
+ recipientName = string.Empty;
+ recipientEmail = string.Empty;
+ senderName = string.Empty;
+ senderEmail = string.Empty;
+ giftCardMessage = string.Empty;
+
+ try
+ {
+ var xmlDoc = new XmlDocument();
+ xmlDoc.LoadXml(attributesXml);
+
+ var recipientNameElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/RecipientName");
+ var recipientEmailElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/RecipientEmail");
+ var senderNameElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/SenderName");
+ var senderEmailElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/SenderEmail");
+ var messageElement = (XmlElement)xmlDoc.SelectSingleNode(@"//Attributes/GiftCardInfo/Message");
+
+ if (recipientNameElement != null)
+ recipientName = recipientNameElement.InnerText;
+ if (recipientEmailElement != null)
+ recipientEmail = recipientEmailElement.InnerText;
+ if (senderNameElement != null)
+ senderName = senderNameElement.InnerText;
+ if (senderEmailElement != null)
+ senderEmail = senderEmailElement.InnerText;
+ if (messageElement != null)
+ giftCardMessage = messageElement.InnerText;
+ }
+ catch (Exception exc)
+ {
+ Debug.Write(exc.ToString());
+ }
+ }
+
+ #endregion
+ }
+}
diff --git a/src/Libraries/Nop.Services/Catalog/RoundingHelper.cs b/src/Libraries/Nop.Services/Catalog/RoundingHelper.cs
index beceb364206..7a8df355381 100644
--- a/src/Libraries/Nop.Services/Catalog/RoundingHelper.cs
+++ b/src/Libraries/Nop.Services/Catalog/RoundingHelper.cs
@@ -102,5 +102,18 @@ public static decimal Round(this decimal value, RoundingType roundingType)
return rez;
}
+
+ public static decimal RoundTax(decimal value)
+ {
+ //we use this method because some currencies (e.g. Gungarian Forint or Swiss Franc) use non-standard rules for rounding
+ //you can implement any rounding logic here
+ //use EngineContext.Current.Resolve() to get current currency
+
+ //using Swiss Franc (CHF)? just uncomment the line below
+ //return Math.Round(value * 20, 0) / 20;
+
+ //default round
+ return Math.Round(value, 2, MidpointRounding.AwayFromZero);
+ }
}
}
diff --git a/src/Libraries/Nop.Services/Common/PdfService.cs b/src/Libraries/Nop.Services/Common/PdfService.cs
index 8ae14490aa7..fa99196b7a4 100644
--- a/src/Libraries/Nop.Services/Common/PdfService.cs
+++ b/src/Libraries/Nop.Services/Common/PdfService.cs
@@ -1,1347 +1,1382 @@
-// RTL Support provided by Credo inc (www.credo.co.il || info@credo.co.il)
-
-using System;
-using System.Collections.Generic;
-using System.Globalization;
-using System.IO;
-using System.Linq;
-using iTextSharp.text;
-using iTextSharp.text.pdf;
-using Nop.Core;
-using Nop.Core.Domain.Catalog;
-using Nop.Core.Domain.Common;
-using Nop.Core.Domain.Directory;
-using Nop.Core.Domain.Localization;
-using Nop.Core.Domain.Orders;
-using Nop.Core.Domain.Shipping;
-using Nop.Core.Domain.Tax;
-using Nop.Core.Html;
-using Nop.Services.Catalog;
-using Nop.Services.Configuration;
-using Nop.Services.Directory;
-using Nop.Services.Helpers;
-using Nop.Services.Localization;
-using Nop.Services.Media;
-using Nop.Services.Orders;
-using Nop.Services.Payments;
-using Nop.Services.Stores;
-
-namespace Nop.Services.Common
-{
- ///
- /// PDF service
- ///
- public partial class PdfService : IPdfService
- {
- #region Fields
-
- private readonly ILocalizationService _localizationService;
- private readonly ILanguageService _languageService;
- private readonly IWorkContext _workContext;
- private readonly IOrderService _orderService;
- private readonly IPaymentService _paymentService;
- private readonly IDateTimeHelper _dateTimeHelper;
- private readonly IPriceFormatter _priceFormatter;
- private readonly ICurrencyService _currencyService;
- private readonly IMeasureService _measureService;
- private readonly IPictureService _pictureService;
- private readonly IProductService _productService;
- private readonly IProductAttributeParser _productAttributeParser;
- private readonly IStoreService _storeService;
- private readonly IStoreContext _storeContext;
- private readonly ISettingService _settingContext;
- private readonly IAddressAttributeFormatter _addressAttributeFormatter;
-
- private readonly CatalogSettings _catalogSettings;
- private readonly CurrencySettings _currencySettings;
- private readonly MeasureSettings _measureSettings;
- private readonly PdfSettings _pdfSettings;
- private readonly TaxSettings _taxSettings;
- private readonly AddressSettings _addressSettings;
-
- #endregion
-
- #region Ctor
-
- public PdfService(ILocalizationService localizationService,
- ILanguageService languageService,
- IWorkContext workContext,
- IOrderService orderService,
- IPaymentService paymentService,
- IDateTimeHelper dateTimeHelper,
- IPriceFormatter priceFormatter,
- ICurrencyService currencyService,
- IMeasureService measureService,
- IPictureService pictureService,
- IProductService productService,
- IProductAttributeParser productAttributeParser,
- IStoreService storeService,
- IStoreContext storeContext,
- ISettingService settingContext,
- IAddressAttributeFormatter addressAttributeFormatter,
- CatalogSettings catalogSettings,
- CurrencySettings currencySettings,
- MeasureSettings measureSettings,
- PdfSettings pdfSettings,
- TaxSettings taxSettings,
- AddressSettings addressSettings)
- {
- this._localizationService = localizationService;
- this._languageService = languageService;
- this._workContext = workContext;
- this._orderService = orderService;
- this._paymentService = paymentService;
- this._dateTimeHelper = dateTimeHelper;
- this._priceFormatter = priceFormatter;
- this._currencyService = currencyService;
- this._measureService = measureService;
- this._pictureService = pictureService;
- this._productService = productService;
- this._productAttributeParser = productAttributeParser;
- this._storeService = storeService;
- this._storeContext = storeContext;
- this._settingContext = settingContext;
- this._addressAttributeFormatter = addressAttributeFormatter;
- this._currencySettings = currencySettings;
- this._catalogSettings = catalogSettings;
- this._measureSettings = measureSettings;
- this._pdfSettings = pdfSettings;
- this._taxSettings = taxSettings;
- this._addressSettings = addressSettings;
- }
-
- #endregion
-
- #region Utilities
-
- ///
- /// Get font
- ///
- /// Font
- protected virtual Font GetFont()
- {
- //nopCommerce supports unicode characters
- //nopCommerce uses Free Serif font by default (~/App_Data/Pdf/FreeSerif.ttf file)
- //It was downloaded from http://savannah.gnu.org/projects/freefont
- return GetFont(_pdfSettings.FontFileName);
- }
- ///
- /// Get font
- ///
- /// Font file name
- /// Font
- protected virtual Font GetFont(string fontFileName)
- {
- if (fontFileName == null)
- throw new ArgumentNullException("fontFileName");
-
- string fontPath = Path.Combine(CommonHelper.MapPath("~/App_Data/Pdf/"), fontFileName);
- var baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
- var font = new Font(baseFont, 10, Font.NORMAL);
- return font;
- }
-
- ///
- /// Get font direction
- ///
- /// Language
- /// Font direction
- protected virtual int GetDirection(Language lang)
- {
- return lang.Rtl ? PdfWriter.RUN_DIRECTION_RTL : PdfWriter.RUN_DIRECTION_LTR;
- }
-
- ///
- /// Get element alignment
- ///
- /// Language
- /// Is opposite?
- /// Element alignment
- protected virtual int GetAlignment(Language lang, bool isOpposite = false)
- {
- //if we need the element to be opposite, like logo etc`.
- if (!isOpposite)
- return lang.Rtl ? Element.ALIGN_RIGHT : Element.ALIGN_LEFT;
-
- return lang.Rtl ? Element.ALIGN_LEFT : Element.ALIGN_RIGHT;
- }
-
- #endregion
-
- #region Methods
-
- ///
- /// Print an order to PDF
- ///
- /// Order
- /// Language identifier; 0 to use a language used when placing an order
- /// Vendor identifier to limit products; 0 to to print all products. If specified, then totals won't be printed
- /// A path of generated file
- public virtual string PrintOrderToPdf(Order order, int languageId = 0, int vendorId = 0)
- {
- if (order == null)
- throw new ArgumentNullException("order");
-
- string fileName = string.Format("order_{0}_{1}.pdf", order.OrderGuid, CommonHelper.GenerateRandomDigitCode(4));
- string filePath = Path.Combine(CommonHelper.MapPath("~/content/files/ExportImport"), fileName);
- using (var fileStream = new FileStream(filePath, FileMode.Create))
- {
- var orders = new List();
- orders.Add(order);
- PrintOrdersToPdf(fileStream, orders, languageId, vendorId);
- }
- return filePath;
- }
-
- ///
- /// Print orders to PDF
- ///
- /// Stream
- /// Orders
- /// Language identifier; 0 to use a language used when placing an order
- /// Vendor identifier to limit products; 0 to to print all products. If specified, then totals won't be printed
- public virtual void PrintOrdersToPdf(Stream stream, IList orders, int languageId = 0, int vendorId = 0)
- {
- if (stream == null)
- throw new ArgumentNullException("stream");
-
- if (orders == null)
- throw new ArgumentNullException("orders");
-
- var pageSize = PageSize.A4;
-
- if (_pdfSettings.LetterPageSizeEnabled)
- {
- pageSize = PageSize.LETTER;
- }
-
-
- var doc = new Document(pageSize);
- var pdfWriter = PdfWriter.GetInstance(doc, stream);
- doc.Open();
-
- //fonts
- var titleFont = GetFont();
- titleFont.SetStyle(Font.BOLD);
- titleFont.Color = BaseColor.BLACK;
- var font = GetFont();
- var attributesFont = GetFont();
- attributesFont.SetStyle(Font.ITALIC);
-
- int ordCount = orders.Count;
- int ordNum = 0;
-
- foreach (var order in orders)
- {
- //by default _pdfSettings contains settings for the current active store
- //and we need PdfSettings for the store which was used to place an order
- //so let's load it based on a store of the current order
- var pdfSettingsByStore = _settingContext.LoadSetting(order.StoreId);
-
-
- var lang = _languageService.GetLanguageById(languageId == 0 ? order.CustomerLanguageId : languageId);
- if (lang == null || !lang.Published)
- lang = _workContext.WorkingLanguage;
-
- #region Header
-
- //logo
- var logoPicture = _pictureService.GetPictureById(pdfSettingsByStore.LogoPictureId);
- var logoExists = logoPicture != null;
-
- //header
- var headerTable = new PdfPTable(logoExists ? 2 : 1);
- headerTable.RunDirection = GetDirection(lang);
- headerTable.DefaultCell.Border = Rectangle.NO_BORDER;
-
- //store info
- var store = _storeService.GetStoreById(order.StoreId) ?? _storeContext.CurrentStore;
- var anchor = new Anchor(store.Url.Trim(new [] { '/' }), font);
- anchor.Reference = store.Url;
-
- var cellHeader = new PdfPCell(new Phrase(String.Format(_localizationService.GetResource("PDFInvoice.Order#", lang.Id), order.CustomOrderNumber), titleFont));
- cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
- cellHeader.Phrase.Add(new Phrase(anchor));
- cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
- cellHeader.Phrase.Add(new Phrase(String.Format(_localizationService.GetResource("PDFInvoice.OrderDate", lang.Id), _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc).ToString("D", new CultureInfo(lang.LanguageCulture))), font));
- cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
- cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
- cellHeader.HorizontalAlignment = Element.ALIGN_LEFT;
- cellHeader.Border = Rectangle.NO_BORDER;
-
- headerTable.AddCell(cellHeader);
-
- if (logoExists)
- if (lang.Rtl)
- headerTable.SetWidths(new[] { 0.2f, 0.8f });
- else
- headerTable.SetWidths(new[] { 0.8f, 0.2f });
- headerTable.WidthPercentage = 100f;
-
- //logo
- if (logoExists)
- {
- var logoFilePath = _pictureService.GetThumbLocalPath(logoPicture, 0, false);
- var logo = Image.GetInstance(logoFilePath);
- logo.Alignment = GetAlignment(lang, true);
- logo.ScaleToFit(65f, 65f);
-
- var cellLogo = new PdfPCell();
- cellLogo.Border = Rectangle.NO_BORDER;
- cellLogo.AddElement(logo);
- headerTable.AddCell(cellLogo);
- }
- doc.Add(headerTable);
-
- #endregion
-
- #region Addresses
-
- var addressTable = new PdfPTable(2);
- addressTable.RunDirection = GetDirection(lang);
- addressTable.DefaultCell.Border = Rectangle.NO_BORDER;
- addressTable.WidthPercentage = 100f;
- addressTable.SetWidths(new[] { 50, 50 });
-
- //billing info
- var billingAddress = new PdfPTable(1);
- billingAddress.DefaultCell.Border = Rectangle.NO_BORDER;
- billingAddress.RunDirection = GetDirection(lang);
-
- billingAddress.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.BillingInformation", lang.Id), titleFont));
-
- if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.BillingAddress.Company))
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Company", lang.Id), order.BillingAddress.Company), font));
-
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Name", lang.Id), order.BillingAddress.FirstName + " " + order.BillingAddress.LastName), font));
- if (_addressSettings.PhoneEnabled)
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Phone", lang.Id), order.BillingAddress.PhoneNumber), font));
- if (_addressSettings.FaxEnabled && !String.IsNullOrEmpty(order.BillingAddress.FaxNumber))
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Fax", lang.Id), order.BillingAddress.FaxNumber), font));
- if (_addressSettings.StreetAddressEnabled)
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.BillingAddress.Address1), font));
- if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.BillingAddress.Address2))
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address2", lang.Id), order.BillingAddress.Address2), font));
- if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
- billingAddress.AddCell(new Paragraph(" " + String.Format("{0}, {1} {2}", order.BillingAddress.City, order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id) : "", order.BillingAddress.ZipPostalCode), font));
- if (_addressSettings.CountryEnabled && order.BillingAddress.Country != null)
- billingAddress.AddCell(new Paragraph(" " + order.BillingAddress.Country.GetLocalized(x => x.Name, lang.Id), font));
-
- //VAT number
- if (!String.IsNullOrEmpty(order.VatNumber))
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.VATNumber", lang.Id), order.VatNumber), font));
-
- //custom attributes
- var customBillingAddressAttributes = _addressAttributeFormatter.FormatAttributes( order.BillingAddress.CustomAttributes);
- if (!String.IsNullOrEmpty(customBillingAddressAttributes))
- {
- //TODO: we should add padding to each line (in case if we have sevaral custom address attributes)
- billingAddress.AddCell(new Paragraph(" " + HtmlHelper.ConvertHtmlToPlainText(customBillingAddressAttributes, true, true), font));
- }
-
-
-
- //vendors payment details
- if (vendorId == 0)
- {
- //payment method
- var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
- string paymentMethodStr = paymentMethod != null ? paymentMethod.GetLocalizedFriendlyName(_localizationService, lang.Id) : order.PaymentMethodSystemName;
- if (!String.IsNullOrEmpty(paymentMethodStr))
- {
- billingAddress.AddCell(new Paragraph(" "));
- billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.PaymentMethod", lang.Id), paymentMethodStr), font));
- billingAddress.AddCell(new Paragraph());
- }
-
- //custom values
- var customValues = order.DeserializeCustomValues();
- if (customValues != null)
- {
- foreach (var item in customValues)
- {
- billingAddress.AddCell(new Paragraph(" "));
- billingAddress.AddCell(new Paragraph(" " + item.Key + ": " + item.Value, font));
- billingAddress.AddCell(new Paragraph());
- }
- }
- }
- addressTable.AddCell(billingAddress);
-
- //shipping info
- var shippingAddress = new PdfPTable(1);
- shippingAddress.DefaultCell.Border = Rectangle.NO_BORDER;
- shippingAddress.RunDirection = GetDirection(lang);
-
- if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
- {
- //cell = new PdfPCell();
- //cell.Border = Rectangle.NO_BORDER;
-
- if (!order.PickUpInStore)
- {
- if (order.ShippingAddress == null)
- throw new NopException(string.Format("Shipping is required, but address is not available. Order ID = {0}", order.Id));
-
- shippingAddress.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.ShippingInformation", lang.Id), titleFont));
- if (!String.IsNullOrEmpty(order.ShippingAddress.Company))
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Company", lang.Id), order.ShippingAddress.Company), font));
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Name", lang.Id), order.ShippingAddress.FirstName + " " + order.ShippingAddress.LastName), font));
- if (_addressSettings.PhoneEnabled)
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Phone", lang.Id), order.ShippingAddress.PhoneNumber), font));
- if (_addressSettings.FaxEnabled && !String.IsNullOrEmpty(order.ShippingAddress.FaxNumber))
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Fax", lang.Id), order.ShippingAddress.FaxNumber), font));
- if (_addressSettings.StreetAddressEnabled)
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.ShippingAddress.Address1), font));
- if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.ShippingAddress.Address2))
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address2", lang.Id), order.ShippingAddress.Address2), font));
- if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
- shippingAddress.AddCell(new Paragraph(" " + String.Format("{0}, {1} {2}", order.ShippingAddress.City, order.ShippingAddress.StateProvince != null ? order.ShippingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id) : "", order.ShippingAddress.ZipPostalCode), font));
- if (_addressSettings.CountryEnabled && order.ShippingAddress.Country != null)
- shippingAddress.AddCell(new Paragraph(" " + order.ShippingAddress.Country.GetLocalized(x => x.Name, lang.Id), font));
- //custom attributes
- var customShippingAddressAttributes = _addressAttributeFormatter.FormatAttributes(order.ShippingAddress.CustomAttributes);
- if (!String.IsNullOrEmpty(customShippingAddressAttributes))
- {
- //TODO: we should add padding to each line (in case if we have sevaral custom address attributes)
- shippingAddress.AddCell(new Paragraph(" " + HtmlHelper.ConvertHtmlToPlainText(customShippingAddressAttributes, true, true), font));
- }
- shippingAddress.AddCell(new Paragraph(" "));
- }
- else
- if (order.PickupAddress != null)
- {
- shippingAddress.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.Pickup", lang.Id), titleFont));
- if (!string.IsNullOrEmpty(order.PickupAddress.Address1))
- shippingAddress.AddCell(new Paragraph(string.Format(" {0}", string.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.PickupAddress.Address1)), font));
- if (!string.IsNullOrEmpty(order.PickupAddress.City))
- shippingAddress.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.City), font));
- if (order.PickupAddress.Country != null)
- shippingAddress.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.Country.GetLocalized(x => x.Name, lang.Id)), font));
- if (!string.IsNullOrEmpty(order.PickupAddress.ZipPostalCode))
- shippingAddress.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.ZipPostalCode), font));
- shippingAddress.AddCell(new Paragraph(" "));
- }
- shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.ShippingMethod", lang.Id), order.ShippingMethod), font));
- shippingAddress.AddCell(new Paragraph());
-
- addressTable.AddCell(shippingAddress);
- }
- else
- {
- shippingAddress.AddCell(new Paragraph());
- addressTable.AddCell(shippingAddress);
- }
-
- doc.Add(addressTable);
- doc.Add(new Paragraph(" "));
-
- #endregion
-
- #region Products
-
- //products
- var productsHeader = new PdfPTable(1);
- productsHeader.RunDirection = GetDirection(lang);
- productsHeader.WidthPercentage = 100f;
- var cellProducts = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.Product(s)", lang.Id), titleFont));
- cellProducts.Border = Rectangle.NO_BORDER;
- productsHeader.AddCell(cellProducts);
- doc.Add(productsHeader);
- doc.Add(new Paragraph(" "));
-
-
- var orderItems = order.OrderItems;
-
- var productsTable = new PdfPTable(_catalogSettings.ShowSkuOnProductDetailsPage ? 5 : 4);
- productsTable.RunDirection = GetDirection(lang);
- productsTable.WidthPercentage = 100f;
- if (lang.Rtl)
- {
- productsTable.SetWidths(_catalogSettings.ShowSkuOnProductDetailsPage
- ? new[] {15, 10, 15, 15, 45}
- : new[] {20, 10, 20, 50});
- }
- else
- {
- productsTable.SetWidths(_catalogSettings.ShowSkuOnProductDetailsPage
- ? new[] {45, 15, 15, 10, 15}
- : new[] {50, 20, 10, 20});
- }
-
- //product name
- var cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductName", lang.Id), font));
- cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cellProductItem);
-
- //SKU
- if (_catalogSettings.ShowSkuOnProductDetailsPage)
- {
- cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.SKU", lang.Id), font));
- cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cellProductItem);
- }
-
- //price
- cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductPrice", lang.Id), font));
- cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cellProductItem);
-
- //qty
- cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductQuantity", lang.Id), font));
- cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cellProductItem);
-
- //total
- cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductTotal", lang.Id), font));
- cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cellProductItem);
-
- foreach (var orderItem in orderItems)
- {
- var p = orderItem.Product;
-
- //a vendor should have access only to his products
- if (vendorId > 0 && p.VendorId != vendorId)
- continue;
-
- var pAttribTable = new PdfPTable(1);
- pAttribTable.RunDirection = GetDirection(lang);
- pAttribTable.DefaultCell.Border = Rectangle.NO_BORDER;
-
- //product name
- string name = p.GetLocalized(x => x.Name, lang.Id);
- pAttribTable.AddCell(new Paragraph(name, font));
- cellProductItem.AddElement(new Paragraph(name, font));
- //attributes
- if (!String.IsNullOrEmpty(orderItem.AttributeDescription))
- {
- var attributesParagraph = new Paragraph(HtmlHelper.ConvertHtmlToPlainText(orderItem.AttributeDescription, true, true), attributesFont);
- pAttribTable.AddCell(attributesParagraph);
- }
- //rental info
- if (orderItem.Product.IsRental)
- {
- var rentalStartDate = orderItem.RentalStartDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalStartDateUtc.Value) : "";
- var rentalEndDate = orderItem.RentalEndDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalEndDateUtc.Value) : "";
- var rentalInfo = string.Format(_localizationService.GetResource("Order.Rental.FormattedDate"),
- rentalStartDate, rentalEndDate);
-
- var rentalInfoParagraph = new Paragraph(rentalInfo, attributesFont);
- pAttribTable.AddCell(rentalInfoParagraph);
- }
- productsTable.AddCell(pAttribTable);
-
- //SKU
- if (_catalogSettings.ShowSkuOnProductDetailsPage)
- {
- var sku = p.FormatSku(orderItem.AttributesXml, _productAttributeParser);
- cellProductItem = new PdfPCell(new Phrase(sku ?? String.Empty, font));
- cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cellProductItem);
- }
-
- //price
- string unitPrice;
- if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
- {
- //including tax
- var unitPriceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.UnitPriceInclTax, order.CurrencyRate);
- unitPrice = _priceFormatter.FormatPrice(unitPriceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
- }
- else
- {
- //excluding tax
- var unitPriceExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.UnitPriceExclTax, order.CurrencyRate);
- unitPrice = _priceFormatter.FormatPrice(unitPriceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
- }
- cellProductItem = new PdfPCell(new Phrase(unitPrice, font));
- cellProductItem.HorizontalAlignment = Element.ALIGN_LEFT;
- productsTable.AddCell(cellProductItem);
-
- //qty
- cellProductItem = new PdfPCell(new Phrase(orderItem.Quantity.ToString(), font));
- cellProductItem.HorizontalAlignment = Element.ALIGN_LEFT;
- productsTable.AddCell(cellProductItem);
-
- //total
- string subTotal;
- if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
- {
- //including tax
- var priceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.PriceInclTax, order.CurrencyRate);
- subTotal = _priceFormatter.FormatPrice(priceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
- }
- else
- {
- //excluding tax
- var priceExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.PriceExclTax, order.CurrencyRate);
- subTotal = _priceFormatter.FormatPrice(priceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
- }
- cellProductItem = new PdfPCell(new Phrase(subTotal, font));
- cellProductItem.HorizontalAlignment = Element.ALIGN_LEFT;
- productsTable.AddCell(cellProductItem);
- }
- doc.Add(productsTable);
-
- #endregion
-
- #region Checkout attributes
-
- //vendors cannot see checkout attributes
- if (vendorId == 0 && !String.IsNullOrEmpty(order.CheckoutAttributeDescription))
- {
- doc.Add(new Paragraph(" "));
- var attribTable = new PdfPTable(1);
- attribTable.RunDirection = GetDirection(lang);
- attribTable.WidthPercentage = 100f;
-
- string attributes = HtmlHelper.ConvertHtmlToPlainText(order.CheckoutAttributeDescription, true, true);
- var cCheckoutAttributes = new PdfPCell(new Phrase(attributes, font));
- cCheckoutAttributes.Border = Rectangle.NO_BORDER;
- cCheckoutAttributes.HorizontalAlignment = Element.ALIGN_RIGHT;
- attribTable.AddCell(cCheckoutAttributes);
- doc.Add(attribTable);
- }
-
- #endregion
-
- #region Totals
-
- //vendors cannot see totals
- if (vendorId == 0)
- {
- //subtotal
- var totalsTable = new PdfPTable(1);
- totalsTable.RunDirection = GetDirection(lang);
- totalsTable.DefaultCell.Border = Rectangle.NO_BORDER;
- totalsTable.WidthPercentage = 100f;
-
- //order subtotal
- if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax && !_taxSettings.ForceTaxExclusionFromOrderSubtotal)
- {
- //including tax
-
- var orderSubtotalInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubtotalInclTax, order.CurrencyRate);
- string orderSubtotalInclTaxStr = _priceFormatter.FormatPrice(orderSubtotalInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Sub-Total", lang.Id), orderSubtotalInclTaxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- else
- {
- //excluding tax
-
- var orderSubtotalExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubtotalExclTax, order.CurrencyRate);
- string orderSubtotalExclTaxStr = _priceFormatter.FormatPrice(orderSubtotalExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Sub-Total", lang.Id), orderSubtotalExclTaxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
-
- //discount (applied to order subtotal)
- if (order.OrderSubTotalDiscountExclTax > decimal.Zero)
- {
- //order subtotal
- if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax && !_taxSettings.ForceTaxExclusionFromOrderSubtotal)
- {
- //including tax
-
- var orderSubTotalDiscountInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubTotalDiscountInclTax, order.CurrencyRate);
- string orderSubTotalDiscountInCustomerCurrencyStr = _priceFormatter.FormatPrice(-orderSubTotalDiscountInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Discount", lang.Id), orderSubTotalDiscountInCustomerCurrencyStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- else
- {
- //excluding tax
-
- var orderSubTotalDiscountExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubTotalDiscountExclTax, order.CurrencyRate);
- string orderSubTotalDiscountInCustomerCurrencyStr = _priceFormatter.FormatPrice(-orderSubTotalDiscountExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Discount", lang.Id), orderSubTotalDiscountInCustomerCurrencyStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- }
-
- //shipping
- if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
- {
- if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
- {
- //including tax
- var orderShippingInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderShippingInclTax, order.CurrencyRate);
- string orderShippingInclTaxStr = _priceFormatter.FormatShippingPrice(orderShippingInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Shipping", lang.Id), orderShippingInclTaxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- else
- {
- //excluding tax
- var orderShippingExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderShippingExclTax, order.CurrencyRate);
- string orderShippingExclTaxStr = _priceFormatter.FormatShippingPrice(orderShippingExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Shipping", lang.Id), orderShippingExclTaxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- }
-
- //payment fee
- if (order.PaymentMethodAdditionalFeeExclTax > decimal.Zero)
- {
- if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
- {
- //including tax
- var paymentMethodAdditionalFeeInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeInclTax, order.CurrencyRate);
- string paymentMethodAdditionalFeeInclTaxStr = _priceFormatter.FormatPaymentMethodAdditionalFee(paymentMethodAdditionalFeeInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.PaymentMethodAdditionalFee", lang.Id), paymentMethodAdditionalFeeInclTaxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- else
- {
- //excluding tax
- var paymentMethodAdditionalFeeExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeExclTax, order.CurrencyRate);
- string paymentMethodAdditionalFeeExclTaxStr = _priceFormatter.FormatPaymentMethodAdditionalFee(paymentMethodAdditionalFeeExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.PaymentMethodAdditionalFee", lang.Id), paymentMethodAdditionalFeeExclTaxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- }
-
- //tax
- string taxStr = string.Empty;
- var taxRates = new SortedDictionary();
- bool displayTax = true;
- bool displayTaxRates = true;
- if (_taxSettings.HideTaxInOrderSummary && order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
- {
- displayTax = false;
- }
- else
- {
- if (order.OrderTax == 0 && _taxSettings.HideZeroTax)
- {
- displayTax = false;
- displayTaxRates = false;
- }
- else
- {
- taxRates = order.TaxRatesDictionary;
-
- displayTaxRates = _taxSettings.DisplayTaxRates && taxRates.Any();
- displayTax = !displayTaxRates;
-
- var orderTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTax, order.CurrencyRate);
- taxStr = _priceFormatter.FormatPrice(orderTaxInCustomerCurrency, true, order.CustomerCurrencyCode, false, lang);
- }
- }
- if (displayTax)
- {
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Tax", lang.Id), taxStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- if (displayTaxRates)
- {
- foreach (var item in taxRates)
- {
- string taxRate = String.Format(_localizationService.GetResource("PDFInvoice.TaxRate", lang.Id), _priceFormatter.FormatTaxRate(item.Key));
- string taxValue = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value, order.CurrencyRate), true, order.CustomerCurrencyCode, false, lang);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", taxRate, taxValue), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
- }
-
- //discount (applied to order total)
- if (order.OrderDiscount > decimal.Zero)
- {
- var orderDiscountInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderDiscount, order.CurrencyRate);
- string orderDiscountInCustomerCurrencyStr = _priceFormatter.FormatPrice(-orderDiscountInCustomerCurrency, true, order.CustomerCurrencyCode, false, lang);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Discount", lang.Id), orderDiscountInCustomerCurrencyStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
-
- //gift cards
- foreach (var gcuh in order.GiftCardUsageHistory)
- {
- string gcTitle = string.Format(_localizationService.GetResource("PDFInvoice.GiftCardInfo", lang.Id), gcuh.GiftCard.GiftCardCouponCode);
- string gcAmountStr = _priceFormatter.FormatPrice(-(_currencyService.ConvertCurrency(gcuh.UsedValue, order.CurrencyRate)), true, order.CustomerCurrencyCode, false, lang);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", gcTitle, gcAmountStr), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
-
- //reward points
- if (order.RedeemedRewardPointsEntry != null)
- {
- string rpTitle = string.Format(_localizationService.GetResource("PDFInvoice.RewardPoints", lang.Id), -order.RedeemedRewardPointsEntry.Points);
- string rpAmount = _priceFormatter.FormatPrice(-(_currencyService.ConvertCurrency(order.RedeemedRewardPointsEntry.UsedAmount, order.CurrencyRate)), true, order.CustomerCurrencyCode, false, lang);
-
- var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", rpTitle, rpAmount), font));
- p.HorizontalAlignment = Element.ALIGN_RIGHT;
- p.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(p);
- }
-
- //order total
- var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
- string orderTotalStr = _priceFormatter.FormatPrice(orderTotalInCustomerCurrency, true, order.CustomerCurrencyCode, false, lang);
-
-
- var pTotal = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.OrderTotal", lang.Id), orderTotalStr), titleFont));
- pTotal.HorizontalAlignment = Element.ALIGN_RIGHT;
- pTotal.Border = Rectangle.NO_BORDER;
- totalsTable.AddCell(pTotal);
-
- doc.Add(totalsTable);
- }
-
- #endregion
-
- #region Order notes
-
- if (pdfSettingsByStore.RenderOrderNotes)
- {
- var orderNotes = order.OrderNotes
- .Where(on => on.DisplayToCustomer)
- .OrderByDescending(on => on.CreatedOnUtc)
- .ToList();
- if (orderNotes.Any())
- {
- var notesHeader = new PdfPTable(1);
- notesHeader.RunDirection = GetDirection(lang);
- notesHeader.WidthPercentage = 100f;
- var cellOrderNote = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.OrderNotes", lang.Id), titleFont));
- cellOrderNote.Border = Rectangle.NO_BORDER;
- notesHeader.AddCell(cellOrderNote);
- doc.Add(notesHeader);
- doc.Add(new Paragraph(" "));
-
- var notesTable = new PdfPTable(2);
- notesTable.RunDirection = GetDirection(lang);
- if (lang.Rtl)
- {
- notesTable.SetWidths(new[] {70, 30});
- }
- else
- {
- notesTable.SetWidths(new[] {30, 70});
- }
- notesTable.WidthPercentage = 100f;
-
- //created on
- cellOrderNote = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.OrderNotes.CreatedOn", lang.Id), font));
- cellOrderNote.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellOrderNote.HorizontalAlignment = Element.ALIGN_CENTER;
- notesTable.AddCell(cellOrderNote);
-
- //note
- cellOrderNote = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.OrderNotes.Note", lang.Id), font));
- cellOrderNote.BackgroundColor = BaseColor.LIGHT_GRAY;
- cellOrderNote.HorizontalAlignment = Element.ALIGN_CENTER;
- notesTable.AddCell(cellOrderNote);
-
- foreach (var orderNote in orderNotes)
- {
- cellOrderNote = new PdfPCell(new Phrase(_dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc).ToString(), font));
- cellOrderNote.HorizontalAlignment = Element.ALIGN_LEFT;
- notesTable.AddCell(cellOrderNote);
-
- cellOrderNote = new PdfPCell(new Phrase(HtmlHelper.ConvertHtmlToPlainText(orderNote.FormatOrderNoteText(), true, true), font));
- cellOrderNote.HorizontalAlignment = Element.ALIGN_LEFT;
- notesTable.AddCell(cellOrderNote);
-
- //should we display a link to downloadable files here?
- //I think, no. Onyway, PDFs are printable documents and links (files) are useful here
- }
- doc.Add(notesTable);
- }
- }
-
- #endregion
-
- #region Footer
-
- if (!String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn1) || !String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn2))
- {
- var column1Lines = String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn1) ?
- new List() :
- pdfSettingsByStore.InvoiceFooterTextColumn1
- .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
- .ToList();
- var column2Lines = String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn2) ?
- new List() :
- pdfSettingsByStore.InvoiceFooterTextColumn2
- .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
- .ToList();
- if (column1Lines.Any() || column2Lines.Any())
- {
- var totalLines = Math.Max(column1Lines.Count, column2Lines.Count);
- const float margin = 43;
-
- //if you have really a lot of lines in the footer, then replace 9 with 10 or 11
- int footerHeight = totalLines * 9;
- var directContent = pdfWriter.DirectContent;
- directContent.MoveTo(pageSize.GetLeft(margin), pageSize.GetBottom(margin) + footerHeight);
- directContent.LineTo(pageSize.GetRight(margin), pageSize.GetBottom(margin) + footerHeight);
- directContent.Stroke();
-
-
- var footerTable = new PdfPTable(2);
- footerTable.WidthPercentage = 100f;
- footerTable.SetTotalWidth(new float[] { 250, 250 });
- footerTable.RunDirection = GetDirection(lang);
-
- //column 1
- if (column1Lines.Any())
- {
- var column1 = new PdfPCell(new Phrase());
- column1.Border = Rectangle.NO_BORDER;
- column1.HorizontalAlignment = Element.ALIGN_LEFT;
- foreach (var footerLine in column1Lines)
- {
- column1.Phrase.Add(new Phrase(footerLine, font));
- column1.Phrase.Add(new Phrase(Environment.NewLine));
- }
- footerTable.AddCell(column1);
- }
- else
- {
- var column = new PdfPCell(new Phrase(" "));
- column.Border = Rectangle.NO_BORDER;
- footerTable.AddCell(column);
- }
-
- //column 2
- if (column2Lines.Any())
- {
- var column2 = new PdfPCell(new Phrase());
- column2.Border = Rectangle.NO_BORDER;
- column2.HorizontalAlignment = Element.ALIGN_LEFT;
- foreach (var footerLine in column2Lines)
- {
- column2.Phrase.Add(new Phrase(footerLine, font));
- column2.Phrase.Add(new Phrase(Environment.NewLine));
- }
- footerTable.AddCell(column2);
- }
- else
- {
- var column = new PdfPCell(new Phrase(" "));
- column.Border = Rectangle.NO_BORDER;
- footerTable.AddCell(column);
- }
-
- footerTable.WriteSelectedRows(0, totalLines, pageSize.GetLeft(margin), pageSize.GetBottom(margin) + footerHeight, directContent);
- }
- }
-
- #endregion
-
- ordNum++;
- if (ordNum < ordCount)
- {
- doc.NewPage();
- }
- }
- doc.Close();
- }
-
- ///
- /// Print packaging slips to PDF
- ///
- /// Stream
- /// Shipments
- /// Language identifier; 0 to use a language used when placing an order
- public virtual void PrintPackagingSlipsToPdf(Stream stream, IList shipments, int languageId = 0)
- {
- if (stream == null)
- throw new ArgumentNullException("stream");
-
- if (shipments == null)
- throw new ArgumentNullException("shipments");
-
- var pageSize = PageSize.A4;
-
- if (_pdfSettings.LetterPageSizeEnabled)
- {
- pageSize = PageSize.LETTER;
- }
-
- var doc = new Document(pageSize);
- PdfWriter.GetInstance(doc, stream);
- doc.Open();
-
- //fonts
- var titleFont = GetFont();
- titleFont.SetStyle(Font.BOLD);
- titleFont.Color = BaseColor.BLACK;
- var font = GetFont();
- var attributesFont = GetFont();
- attributesFont.SetStyle(Font.ITALIC);
-
- int shipmentCount = shipments.Count;
- int shipmentNum = 0;
-
- foreach (var shipment in shipments)
- {
- var order = shipment.Order;
-
- var lang = _languageService.GetLanguageById(languageId == 0 ? order.CustomerLanguageId : languageId);
- if (lang == null || !lang.Published)
- lang = _workContext.WorkingLanguage;
-
- var addressTable = new PdfPTable(1);
- if (lang.Rtl)
- addressTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
- addressTable.DefaultCell.Border = Rectangle.NO_BORDER;
- addressTable.WidthPercentage = 100f;
-
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Shipment", lang.Id), shipment.Id), titleFont));
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Order", lang.Id), order.CustomOrderNumber), titleFont));
-
- if (!order.PickUpInStore)
- {
- if (order.ShippingAddress == null)
- throw new NopException(string.Format("Shipping is required, but address is not available. Order ID = {0}", order.Id));
-
- if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.ShippingAddress.Company))
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Company", lang.Id),
- order.ShippingAddress.Company), font));
-
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Name", lang.Id),
- order.ShippingAddress.FirstName + " " + order.ShippingAddress.LastName), font));
- if (_addressSettings.PhoneEnabled)
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Phone", lang.Id),
- order.ShippingAddress.PhoneNumber), font));
- if (_addressSettings.StreetAddressEnabled)
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Address", lang.Id),
- order.ShippingAddress.Address1), font));
-
- if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.ShippingAddress.Address2))
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Address2", lang.Id),
- order.ShippingAddress.Address2), font));
-
- if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
- addressTable.AddCell(new Paragraph(String.Format("{0}, {1} {2}", order.ShippingAddress.City, order.ShippingAddress.StateProvince != null
- ? order.ShippingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id)
- : "", order.ShippingAddress.ZipPostalCode), font));
-
- if (_addressSettings.CountryEnabled && order.ShippingAddress.Country != null)
- addressTable.AddCell(new Paragraph(order.ShippingAddress.Country.GetLocalized(x => x.Name, lang.Id), font));
-
- //custom attributes
- var customShippingAddressAttributes = _addressAttributeFormatter.FormatAttributes(order.ShippingAddress.CustomAttributes);
- if (!String.IsNullOrEmpty(customShippingAddressAttributes))
- {
- addressTable.AddCell(new Paragraph(HtmlHelper.ConvertHtmlToPlainText(customShippingAddressAttributes, true, true), font));
- }
- }
- else
- if (order.PickupAddress != null)
- {
- addressTable.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.Pickup", lang.Id), titleFont));
- if (!string.IsNullOrEmpty(order.PickupAddress.Address1))
- addressTable.AddCell(new Paragraph(string.Format(" {0}", string.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.PickupAddress.Address1)), font));
- if (!string.IsNullOrEmpty(order.PickupAddress.City))
- addressTable.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.City), font));
- if (order.PickupAddress.Country != null)
- addressTable.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.Country.GetLocalized(x => x.Name, lang.Id)), font));
- if (!string.IsNullOrEmpty(order.PickupAddress.ZipPostalCode))
- addressTable.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.ZipPostalCode), font));
- addressTable.AddCell(new Paragraph(" "));
- }
-
- addressTable.AddCell(new Paragraph(" "));
-
- addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.ShippingMethod", lang.Id), order.ShippingMethod), font));
- addressTable.AddCell(new Paragraph(" "));
- doc.Add(addressTable);
-
- var productsTable = new PdfPTable(3);
- productsTable.WidthPercentage = 100f;
- if (lang.Rtl)
- {
- productsTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
- productsTable.SetWidths(new[] {20, 20, 60});
- }
- else
- {
- productsTable.SetWidths(new[] {60, 20, 20});
- }
-
- //product name
- var cell = new PdfPCell(new Phrase(_localizationService.GetResource("PDFPackagingSlip.ProductName", lang.Id),font));
- cell.BackgroundColor = BaseColor.LIGHT_GRAY;
- cell.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cell);
-
- //SKU
- cell = new PdfPCell(new Phrase(_localizationService.GetResource("PDFPackagingSlip.SKU", lang.Id), font));
- cell.BackgroundColor = BaseColor.LIGHT_GRAY;
- cell.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cell);
-
- //qty
- cell = new PdfPCell(new Phrase(_localizationService.GetResource("PDFPackagingSlip.QTY", lang.Id), font));
- cell.BackgroundColor = BaseColor.LIGHT_GRAY;
- cell.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cell);
-
- foreach (var si in shipment.ShipmentItems)
- {
- var productAttribTable = new PdfPTable(1);
- if (lang.Rtl)
- productAttribTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
- productAttribTable.DefaultCell.Border = Rectangle.NO_BORDER;
-
- //product name
- var orderItem = _orderService.GetOrderItemById(si.OrderItemId);
- if (orderItem == null)
- continue;
-
- var p = orderItem.Product;
- string name = p.GetLocalized(x => x.Name, lang.Id);
- productAttribTable.AddCell(new Paragraph(name, font));
- //attributes
- if (!String.IsNullOrEmpty(orderItem.AttributeDescription))
- {
- var attributesParagraph = new Paragraph(HtmlHelper.ConvertHtmlToPlainText(orderItem.AttributeDescription, true, true), attributesFont);
- productAttribTable.AddCell(attributesParagraph);
- }
- //rental info
- if (orderItem.Product.IsRental)
- {
- var rentalStartDate = orderItem.RentalStartDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalStartDateUtc.Value) : "";
- var rentalEndDate = orderItem.RentalEndDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalEndDateUtc.Value) : "";
- var rentalInfo = string.Format(_localizationService.GetResource("Order.Rental.FormattedDate"),
- rentalStartDate, rentalEndDate);
-
- var rentalInfoParagraph = new Paragraph(rentalInfo, attributesFont);
- productAttribTable.AddCell(rentalInfoParagraph);
- }
- productsTable.AddCell(productAttribTable);
-
- //SKU
- var sku = p.FormatSku(orderItem.AttributesXml, _productAttributeParser);
- cell = new PdfPCell(new Phrase(sku ?? String.Empty, font));
- cell.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cell);
-
- //qty
- cell = new PdfPCell(new Phrase(si.Quantity.ToString(), font));
- cell.HorizontalAlignment = Element.ALIGN_CENTER;
- productsTable.AddCell(cell);
- }
- doc.Add(productsTable);
-
- shipmentNum++;
- if (shipmentNum < shipmentCount)
- {
- doc.NewPage();
- }
- }
-
-
- doc.Close();
- }
-
- ///
- /// Print products to PDF
- ///
- /// Stream
- /// Products
- public virtual void PrintProductsToPdf(Stream stream, IList products)
- {
- if (stream == null)
- throw new ArgumentNullException("stream");
-
- if (products == null)
- throw new ArgumentNullException("products");
-
- var lang = _workContext.WorkingLanguage;
-
- var pageSize = PageSize.A4;
-
- if (_pdfSettings.LetterPageSizeEnabled)
- {
- pageSize = PageSize.LETTER;
- }
-
- var doc = new Document(pageSize);
- PdfWriter.GetInstance(doc, stream);
- doc.Open();
-
- //fonts
- var titleFont = GetFont();
- titleFont.SetStyle(Font.BOLD);
- titleFont.Color = BaseColor.BLACK;
- var font = GetFont();
-
- int productNumber = 1;
- int prodCount = products.Count;
-
- foreach (var product in products)
- {
- string productName = product.GetLocalized(x => x.Name, lang.Id);
- string productDescription = product.GetLocalized(x => x.FullDescription, lang.Id);
-
- var productTable = new PdfPTable(1);
- productTable.WidthPercentage = 100f;
- productTable.DefaultCell.Border = Rectangle.NO_BORDER;
- if (lang.Rtl)
- {
- productTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
- }
-
- productTable.AddCell(new Paragraph(String.Format("{0}. {1}", productNumber, productName), titleFont));
- productTable.AddCell(new Paragraph(" "));
- productTable.AddCell(new Paragraph(HtmlHelper.StripTags(HtmlHelper.ConvertHtmlToPlainText(productDescription, decode: true)), font));
- productTable.AddCell(new Paragraph(" "));
-
- if (product.ProductType == ProductType.SimpleProduct)
- {
- //simple product
- //render its properties such as price, weight, etc
- var priceStr = string.Format("{0} {1}", product.Price.ToString("0.00"), _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode);
- if (product.IsRental)
- priceStr = _priceFormatter.FormatRentalProductPeriod(product, priceStr);
- productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.Price", lang.Id), priceStr), font));
- productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.SKU", lang.Id), product.Sku), font));
-
- if (product.IsShipEnabled && product.Weight > Decimal.Zero)
- productTable.AddCell(new Paragraph(String.Format("{0}: {1} {2}", _localizationService.GetResource("PDFProductCatalog.Weight", lang.Id), product.Weight.ToString("0.00"), _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name), font));
-
- if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock)
- productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.StockQuantity", lang.Id), product.GetTotalStockQuantity()), font));
-
- productTable.AddCell(new Paragraph(" "));
- }
- var pictures = _pictureService.GetPicturesByProductId(product.Id);
- if (pictures.Any())
- {
- var table = new PdfPTable(2);
- table.WidthPercentage = 100f;
- if (lang.Rtl)
- {
- table.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
- }
-
- foreach (var pic in pictures)
- {
- var picBinary = _pictureService.LoadPictureBinary(pic);
- if (picBinary != null && picBinary.Length > 0)
- {
- var pictureLocalPath = _pictureService.GetThumbLocalPath(pic, 200, false);
- var cell = new PdfPCell(Image.GetInstance(pictureLocalPath));
- cell.HorizontalAlignment = Element.ALIGN_LEFT;
- cell.Border = Rectangle.NO_BORDER;
- table.AddCell(cell);
- }
- }
-
- if (pictures.Count % 2 > 0)
- {
- var cell = new PdfPCell(new Phrase(" "));
- cell.Border = Rectangle.NO_BORDER;
- table.AddCell(cell);
- }
-
- productTable.AddCell(table);
- productTable.AddCell(new Paragraph(" "));
- }
-
-
- if (product.ProductType == ProductType.GroupedProduct)
- {
- //grouped product. render its associated products
- int pvNum = 1;
- foreach (var associatedProduct in _productService.GetAssociatedProducts(product.Id, showHidden: true))
- {
- productTable.AddCell(new Paragraph(String.Format("{0}-{1}. {2}", productNumber, pvNum, associatedProduct.GetLocalized(x => x.Name, lang.Id)), font));
- productTable.AddCell(new Paragraph(" "));
-
- //uncomment to render associated product description
- //string apDescription = associatedProduct.GetLocalized(x => x.ShortDescription, lang.Id);
- //if (!String.IsNullOrEmpty(apDescription))
- //{
- // productTable.AddCell(new Paragraph(HtmlHelper.StripTags(HtmlHelper.ConvertHtmlToPlainText(apDescription)), font));
- // productTable.AddCell(new Paragraph(" "));
- //}
-
- //uncomment to render associated product picture
- //var apPicture = _pictureService.GetPicturesByProductId(associatedProduct.Id).FirstOrDefault();
- //if (apPicture != null)
- //{
- // var picBinary = _pictureService.LoadPictureBinary(apPicture);
- // if (picBinary != null && picBinary.Length > 0)
- // {
- // var pictureLocalPath = _pictureService.GetThumbLocalPath(apPicture, 200, false);
- // productTable.AddCell(Image.GetInstance(pictureLocalPath));
- // }
- //}
-
- productTable.AddCell(new Paragraph(String.Format("{0}: {1} {2}", _localizationService.GetResource("PDFProductCatalog.Price", lang.Id), associatedProduct.Price.ToString("0.00"), _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode), font));
- productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.SKU", lang.Id), associatedProduct.Sku), font));
-
- if (associatedProduct.IsShipEnabled && associatedProduct.Weight > Decimal.Zero)
- productTable.AddCell(new Paragraph(String.Format("{0}: {1} {2}", _localizationService.GetResource("PDFProductCatalog.Weight", lang.Id), associatedProduct.Weight.ToString("0.00"), _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name), font));
-
- if (associatedProduct.ManageInventoryMethod == ManageInventoryMethod.ManageStock)
- productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.StockQuantity", lang.Id), associatedProduct.GetTotalStockQuantity()), font));
-
- productTable.AddCell(new Paragraph(" "));
-
- pvNum++;
- }
- }
-
- doc.Add(productTable);
-
- productNumber++;
-
- if (productNumber <= prodCount)
- {
- doc.NewPage();
- }
- }
-
- doc.Close();
- }
-
- #endregion
- }
+// RTL Support provided by Credo inc (www.credo.co.il || info@credo.co.il)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using iTextSharp.text;
+using iTextSharp.text.pdf;
+using Nop.Core;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Common;
+using Nop.Core.Domain.Directory;
+using Nop.Core.Domain.Localization;
+using Nop.Core.Domain.Orders;
+using Nop.Core.Domain.Shipping;
+using Nop.Core.Domain.Tax;
+using Nop.Core.Html;
+using Nop.Services.Catalog;
+using Nop.Services.Configuration;
+using Nop.Services.Directory;
+using Nop.Services.Helpers;
+using Nop.Services.Localization;
+using Nop.Services.Media;
+using Nop.Services.Orders;
+using Nop.Services.Payments;
+using Nop.Services.Stores;
+
+namespace Nop.Services.Common
+{
+ ///
+ /// PDF service
+ ///
+ public partial class PdfService : IPdfService
+ {
+ #region Fields
+
+ private readonly ILocalizationService _localizationService;
+ private readonly ILanguageService _languageService;
+ private readonly IWorkContext _workContext;
+ private readonly IOrderService _orderService;
+ private readonly IPaymentService _paymentService;
+ private readonly IDateTimeHelper _dateTimeHelper;
+ private readonly IPriceFormatter _priceFormatter;
+ private readonly ICurrencyService _currencyService;
+ private readonly IMeasureService _measureService;
+ private readonly IPictureService _pictureService;
+ private readonly IProductService _productService;
+ private readonly IProductAttributeParser _productAttributeParser;
+ private readonly IStoreService _storeService;
+ private readonly IStoreContext _storeContext;
+ private readonly ISettingService _settingContext;
+ private readonly IAddressAttributeFormatter _addressAttributeFormatter;
+
+ private readonly CatalogSettings _catalogSettings;
+ private readonly CurrencySettings _currencySettings;
+ private readonly MeasureSettings _measureSettings;
+ private readonly PdfSettings _pdfSettings;
+ private readonly TaxSettings _taxSettings;
+ private readonly AddressSettings _addressSettings;
+
+ #endregion
+
+ #region Ctor
+
+ public PdfService(ILocalizationService localizationService,
+ ILanguageService languageService,
+ IWorkContext workContext,
+ IOrderService orderService,
+ IPaymentService paymentService,
+ IDateTimeHelper dateTimeHelper,
+ IPriceFormatter priceFormatter,
+ ICurrencyService currencyService,
+ IMeasureService measureService,
+ IPictureService pictureService,
+ IProductService productService,
+ IProductAttributeParser productAttributeParser,
+ IStoreService storeService,
+ IStoreContext storeContext,
+ ISettingService settingContext,
+ IAddressAttributeFormatter addressAttributeFormatter,
+ CatalogSettings catalogSettings,
+ CurrencySettings currencySettings,
+ MeasureSettings measureSettings,
+ PdfSettings pdfSettings,
+ TaxSettings taxSettings,
+ AddressSettings addressSettings)
+ {
+ this._localizationService = localizationService;
+ this._languageService = languageService;
+ this._workContext = workContext;
+ this._orderService = orderService;
+ this._paymentService = paymentService;
+ this._dateTimeHelper = dateTimeHelper;
+ this._priceFormatter = priceFormatter;
+ this._currencyService = currencyService;
+ this._measureService = measureService;
+ this._pictureService = pictureService;
+ this._productService = productService;
+ this._productAttributeParser = productAttributeParser;
+ this._storeService = storeService;
+ this._storeContext = storeContext;
+ this._settingContext = settingContext;
+ this._addressAttributeFormatter = addressAttributeFormatter;
+ this._currencySettings = currencySettings;
+ this._catalogSettings = catalogSettings;
+ this._measureSettings = measureSettings;
+ this._pdfSettings = pdfSettings;
+ this._taxSettings = taxSettings;
+ this._addressSettings = addressSettings;
+ }
+
+ #endregion
+
+ #region Utilities
+
+ ///
+ /// Get font
+ ///
+ /// Font
+ protected virtual Font GetFont()
+ {
+ //nopCommerce supports unicode characters
+ //nopCommerce uses Free Serif font by default (~/App_Data/Pdf/FreeSerif.ttf file)
+ //It was downloaded from http://savannah.gnu.org/projects/freefont
+ return GetFont(_pdfSettings.FontFileName);
+ }
+ ///
+ /// Get font
+ ///
+ /// Font file name
+ /// Font
+ protected virtual Font GetFont(string fontFileName)
+ {
+ if (fontFileName == null)
+ throw new ArgumentNullException("fontFileName");
+
+ string fontPath = Path.Combine(CommonHelper.MapPath("~/App_Data/Pdf/"), fontFileName);
+ var baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
+ var font = new Font(baseFont, 10, Font.NORMAL);
+ return font;
+ }
+
+ ///
+ /// Get font direction
+ ///
+ /// Language
+ /// Font direction
+ protected virtual int GetDirection(Language lang)
+ {
+ return lang.Rtl ? PdfWriter.RUN_DIRECTION_RTL : PdfWriter.RUN_DIRECTION_LTR;
+ }
+
+ ///
+ /// Get element alignment
+ ///
+ /// Language
+ /// Is opposite?
+ /// Element alignment
+ protected virtual int GetAlignment(Language lang, bool isOpposite = false)
+ {
+ //if we need the element to be opposite, like logo etc`.
+ if (!isOpposite)
+ return lang.Rtl ? Element.ALIGN_RIGHT : Element.ALIGN_LEFT;
+
+ return lang.Rtl ? Element.ALIGN_LEFT : Element.ALIGN_RIGHT;
+ }
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Print an order to PDF
+ ///
+ /// Order
+ /// Language identifier; 0 to use a language used when placing an order
+ /// Vendor identifier to limit products; 0 to to print all products. If specified, then totals won't be printed
+ /// A path of generated file
+ public virtual string PrintOrderToPdf(Order order, int languageId = 0, int vendorId = 0)
+ {
+ if (order == null)
+ throw new ArgumentNullException("order");
+
+ string fileName = string.Format("order_{0}_{1}.pdf", order.OrderGuid, CommonHelper.GenerateRandomDigitCode(4));
+ string filePath = Path.Combine(CommonHelper.MapPath("~/content/files/ExportImport"), fileName);
+ using (var fileStream = new FileStream(filePath, FileMode.Create))
+ {
+ var orders = new List();
+ orders.Add(order);
+ PrintOrdersToPdf(fileStream, orders, languageId, vendorId);
+ }
+ return filePath;
+ }
+
+ ///
+ /// Print orders to PDF
+ ///
+ /// Stream
+ /// Orders
+ /// Language identifier; 0 to use a language used when placing an order
+ /// Vendor identifier to limit products; 0 to to print all products. If specified, then totals won't be printed
+ public virtual void PrintOrdersToPdf(Stream stream, IList orders, int languageId = 0, int vendorId = 0)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ if (orders == null)
+ throw new ArgumentNullException("orders");
+
+ var pageSize = PageSize.A4;
+
+ if (_pdfSettings.LetterPageSizeEnabled)
+ {
+ pageSize = PageSize.LETTER;
+ }
+
+
+ var doc = new Document(pageSize);
+ var pdfWriter = PdfWriter.GetInstance(doc, stream);
+ doc.Open();
+
+ //fonts
+ var titleFont = GetFont();
+ titleFont.SetStyle(Font.BOLD);
+ titleFont.Color = BaseColor.BLACK;
+ var font = GetFont();
+ var attributesFont = GetFont();
+ attributesFont.SetStyle(Font.ITALIC);
+
+ int ordCount = orders.Count;
+ int ordNum = 0;
+
+ foreach (var order in orders)
+ {
+ //by default _pdfSettings contains settings for the current active store
+ //and we need PdfSettings for the store which was used to place an order
+ //so let's load it based on a store of the current order
+ var pdfSettingsByStore = _settingContext.LoadSetting(order.StoreId);
+
+
+ var lang = _languageService.GetLanguageById(languageId == 0 ? order.CustomerLanguageId : languageId);
+ if (lang == null || !lang.Published)
+ lang = _workContext.WorkingLanguage;
+
+ #region Header
+
+ //logo
+ var logoPicture = _pictureService.GetPictureById(pdfSettingsByStore.LogoPictureId);
+ var logoExists = logoPicture != null;
+
+ //header
+ var headerTable = new PdfPTable(logoExists ? 2 : 1);
+ headerTable.RunDirection = GetDirection(lang);
+ headerTable.DefaultCell.Border = Rectangle.NO_BORDER;
+
+ //store info
+ var store = _storeService.GetStoreById(order.StoreId) ?? _storeContext.CurrentStore;
+ var anchor = new Anchor(store.Url.Trim(new [] { '/' }), font);
+ anchor.Reference = store.Url;
+
+ var cellHeader = new PdfPCell(new Phrase(String.Format(_localizationService.GetResource("PDFInvoice.Order#", lang.Id), order.CustomOrderNumber), titleFont));
+ cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
+ cellHeader.Phrase.Add(new Phrase(anchor));
+ cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
+ cellHeader.Phrase.Add(new Phrase(String.Format(_localizationService.GetResource("PDFInvoice.OrderDate", lang.Id), _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc).ToString("D", new CultureInfo(lang.LanguageCulture))), font));
+ cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
+ cellHeader.Phrase.Add(new Phrase(Environment.NewLine));
+ cellHeader.HorizontalAlignment = Element.ALIGN_LEFT;
+ cellHeader.Border = Rectangle.NO_BORDER;
+
+ headerTable.AddCell(cellHeader);
+
+ if (logoExists)
+ if (lang.Rtl)
+ headerTable.SetWidths(new[] { 0.2f, 0.8f });
+ else
+ headerTable.SetWidths(new[] { 0.8f, 0.2f });
+ headerTable.WidthPercentage = 100f;
+
+ //logo
+ if (logoExists)
+ {
+ var logoFilePath = _pictureService.GetThumbLocalPath(logoPicture, 0, false);
+ var logo = Image.GetInstance(logoFilePath);
+ logo.Alignment = GetAlignment(lang, true);
+ logo.ScaleToFit(65f, 65f);
+
+ var cellLogo = new PdfPCell();
+ cellLogo.Border = Rectangle.NO_BORDER;
+ cellLogo.AddElement(logo);
+ headerTable.AddCell(cellLogo);
+ }
+ doc.Add(headerTable);
+
+ #endregion
+
+ #region Addresses
+
+ var addressTable = new PdfPTable(2);
+ addressTable.RunDirection = GetDirection(lang);
+ addressTable.DefaultCell.Border = Rectangle.NO_BORDER;
+ addressTable.WidthPercentage = 100f;
+ addressTable.SetWidths(new[] { 50, 50 });
+
+ //billing info
+ var billingAddress = new PdfPTable(1);
+ billingAddress.DefaultCell.Border = Rectangle.NO_BORDER;
+ billingAddress.RunDirection = GetDirection(lang);
+
+ billingAddress.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.BillingInformation", lang.Id), titleFont));
+
+ if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.BillingAddress.Company))
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Company", lang.Id), order.BillingAddress.Company), font));
+
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Name", lang.Id), order.BillingAddress.FirstName + " " + order.BillingAddress.LastName), font));
+ if (_addressSettings.PhoneEnabled)
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Phone", lang.Id), order.BillingAddress.PhoneNumber), font));
+ if (_addressSettings.FaxEnabled && !String.IsNullOrEmpty(order.BillingAddress.FaxNumber))
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Fax", lang.Id), order.BillingAddress.FaxNumber), font));
+ if (_addressSettings.StreetAddressEnabled)
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.BillingAddress.Address1), font));
+ if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.BillingAddress.Address2))
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address2", lang.Id), order.BillingAddress.Address2), font));
+ if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
+ billingAddress.AddCell(new Paragraph(" " + String.Format("{0}, {1} {2}", order.BillingAddress.City, order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id) : "", order.BillingAddress.ZipPostalCode), font));
+ if (_addressSettings.CountryEnabled && order.BillingAddress.Country != null)
+ billingAddress.AddCell(new Paragraph(" " + order.BillingAddress.Country.GetLocalized(x => x.Name, lang.Id), font));
+
+ //VAT number
+ if (!String.IsNullOrEmpty(order.VatNumber))
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.VATNumber", lang.Id), order.VatNumber), font));
+
+ //custom attributes
+ var customBillingAddressAttributes = _addressAttributeFormatter.FormatAttributes( order.BillingAddress.CustomAttributes);
+ if (!String.IsNullOrEmpty(customBillingAddressAttributes))
+ {
+ //TODO: we should add padding to each line (in case if we have sevaral custom address attributes)
+ billingAddress.AddCell(new Paragraph(" " + HtmlHelper.ConvertHtmlToPlainText(customBillingAddressAttributes, true, true), font));
+ }
+
+
+
+ //vendors payment details
+ if (vendorId == 0)
+ {
+ //payment method
+ var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
+ string paymentMethodStr = paymentMethod != null ? paymentMethod.GetLocalizedFriendlyName(_localizationService, lang.Id) : order.PaymentMethodSystemName;
+ if (!String.IsNullOrEmpty(paymentMethodStr))
+ {
+ billingAddress.AddCell(new Paragraph(" "));
+ billingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.PaymentMethod", lang.Id), paymentMethodStr), font));
+ billingAddress.AddCell(new Paragraph());
+ }
+
+ //custom values
+ var customValues = order.DeserializeCustomValues();
+ if (customValues != null)
+ {
+ foreach (var item in customValues)
+ {
+ billingAddress.AddCell(new Paragraph(" "));
+ billingAddress.AddCell(new Paragraph(" " + item.Key + ": " + item.Value, font));
+ billingAddress.AddCell(new Paragraph());
+ }
+ }
+ }
+ addressTable.AddCell(billingAddress);
+
+ //shipping info
+ var shippingAddress = new PdfPTable(1);
+ shippingAddress.DefaultCell.Border = Rectangle.NO_BORDER;
+ shippingAddress.RunDirection = GetDirection(lang);
+
+ if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
+ {
+ //cell = new PdfPCell();
+ //cell.Border = Rectangle.NO_BORDER;
+
+ if (!order.PickUpInStore)
+ {
+ if (order.ShippingAddress == null)
+ throw new NopException(string.Format("Shipping is required, but address is not available. Order ID = {0}", order.Id));
+
+ shippingAddress.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.ShippingInformation", lang.Id), titleFont));
+ if (!String.IsNullOrEmpty(order.ShippingAddress.Company))
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Company", lang.Id), order.ShippingAddress.Company), font));
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Name", lang.Id), order.ShippingAddress.FirstName + " " + order.ShippingAddress.LastName), font));
+ if (_addressSettings.PhoneEnabled)
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Phone", lang.Id), order.ShippingAddress.PhoneNumber), font));
+ if (_addressSettings.FaxEnabled && !String.IsNullOrEmpty(order.ShippingAddress.FaxNumber))
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Fax", lang.Id), order.ShippingAddress.FaxNumber), font));
+ if (_addressSettings.StreetAddressEnabled)
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.ShippingAddress.Address1), font));
+ if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.ShippingAddress.Address2))
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.Address2", lang.Id), order.ShippingAddress.Address2), font));
+ if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
+ shippingAddress.AddCell(new Paragraph(" " + String.Format("{0}, {1} {2}", order.ShippingAddress.City, order.ShippingAddress.StateProvince != null ? order.ShippingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id) : "", order.ShippingAddress.ZipPostalCode), font));
+ if (_addressSettings.CountryEnabled && order.ShippingAddress.Country != null)
+ shippingAddress.AddCell(new Paragraph(" " + order.ShippingAddress.Country.GetLocalized(x => x.Name, lang.Id), font));
+ //custom attributes
+ var customShippingAddressAttributes = _addressAttributeFormatter.FormatAttributes(order.ShippingAddress.CustomAttributes);
+ if (!String.IsNullOrEmpty(customShippingAddressAttributes))
+ {
+ //TODO: we should add padding to each line (in case if we have sevaral custom address attributes)
+ shippingAddress.AddCell(new Paragraph(" " + HtmlHelper.ConvertHtmlToPlainText(customShippingAddressAttributes, true, true), font));
+ }
+ shippingAddress.AddCell(new Paragraph(" "));
+ }
+ else
+ if (order.PickupAddress != null)
+ {
+ shippingAddress.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.Pickup", lang.Id), titleFont));
+ if (!string.IsNullOrEmpty(order.PickupAddress.Address1))
+ shippingAddress.AddCell(new Paragraph(string.Format(" {0}", string.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.PickupAddress.Address1)), font));
+ if (!string.IsNullOrEmpty(order.PickupAddress.City))
+ shippingAddress.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.City), font));
+ if (order.PickupAddress.Country != null)
+ shippingAddress.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.Country.GetLocalized(x => x.Name, lang.Id)), font));
+ if (!string.IsNullOrEmpty(order.PickupAddress.ZipPostalCode))
+ shippingAddress.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.ZipPostalCode), font));
+ shippingAddress.AddCell(new Paragraph(" "));
+ }
+ shippingAddress.AddCell(new Paragraph(" " + String.Format(_localizationService.GetResource("PDFInvoice.ShippingMethod", lang.Id), order.ShippingMethod), font));
+ shippingAddress.AddCell(new Paragraph());
+
+ addressTable.AddCell(shippingAddress);
+ }
+ else
+ {
+ shippingAddress.AddCell(new Paragraph());
+ addressTable.AddCell(shippingAddress);
+ }
+
+ doc.Add(addressTable);
+ doc.Add(new Paragraph(" "));
+
+ #endregion
+
+ #region Products
+
+ //products
+ var productsHeader = new PdfPTable(1);
+ productsHeader.RunDirection = GetDirection(lang);
+ productsHeader.WidthPercentage = 100f;
+ var cellProducts = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.Product(s)", lang.Id), titleFont));
+ cellProducts.Border = Rectangle.NO_BORDER;
+ productsHeader.AddCell(cellProducts);
+ doc.Add(productsHeader);
+ doc.Add(new Paragraph(" "));
+
+
+ var orderItems = order.OrderItems;
+
+ var productsTable = new PdfPTable(_catalogSettings.ShowSkuOnProductDetailsPage ? 5 : 4);
+ productsTable.RunDirection = GetDirection(lang);
+ productsTable.WidthPercentage = 100f;
+ if (lang.Rtl)
+ {
+ productsTable.SetWidths(_catalogSettings.ShowSkuOnProductDetailsPage
+ ? new[] {15, 10, 15, 15, 45}
+ : new[] {20, 10, 20, 50});
+ }
+ else
+ {
+ productsTable.SetWidths(_catalogSettings.ShowSkuOnProductDetailsPage
+ ? new[] {45, 15, 15, 10, 15}
+ : new[] {50, 20, 10, 20});
+ }
+
+ //product name
+ var cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductName", lang.Id), font));
+ cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cellProductItem);
+
+ //SKU
+ if (_catalogSettings.ShowSkuOnProductDetailsPage)
+ {
+ cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.SKU", lang.Id), font));
+ cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cellProductItem);
+ }
+
+ //price
+ cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductPrice", lang.Id), font));
+ cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cellProductItem);
+
+ //qty
+ cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductQuantity", lang.Id), font));
+ cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cellProductItem);
+
+ //total
+ cellProductItem = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.ProductTotal", lang.Id), font));
+ cellProductItem.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cellProductItem);
+
+ foreach (var orderItem in orderItems)
+ {
+ var p = orderItem.Product;
+
+ //a vendor should have access only to his products
+ if (vendorId > 0 && p.VendorId != vendorId)
+ continue;
+
+ var pAttribTable = new PdfPTable(1);
+ pAttribTable.RunDirection = GetDirection(lang);
+ pAttribTable.DefaultCell.Border = Rectangle.NO_BORDER;
+
+ //product name
+ string name = p.GetLocalized(x => x.Name, lang.Id);
+ pAttribTable.AddCell(new Paragraph(name, font));
+ cellProductItem.AddElement(new Paragraph(name, font));
+ //attributes
+ if (!String.IsNullOrEmpty(orderItem.AttributeDescription))
+ {
+ var attributesParagraph = new Paragraph(HtmlHelper.ConvertHtmlToPlainText(orderItem.AttributeDescription, true, true), attributesFont);
+ pAttribTable.AddCell(attributesParagraph);
+ }
+ //rental info
+ if (orderItem.Product.IsRental)
+ {
+ var rentalStartDate = orderItem.RentalStartDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalStartDateUtc.Value) : "";
+ var rentalEndDate = orderItem.RentalEndDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalEndDateUtc.Value) : "";
+ var rentalInfo = string.Format(_localizationService.GetResource("Order.Rental.FormattedDate"),
+ rentalStartDate, rentalEndDate);
+
+ var rentalInfoParagraph = new Paragraph(rentalInfo, attributesFont);
+ pAttribTable.AddCell(rentalInfoParagraph);
+ }
+ productsTable.AddCell(pAttribTable);
+
+ //SKU
+ if (_catalogSettings.ShowSkuOnProductDetailsPage)
+ {
+ var sku = p.FormatSku(orderItem.AttributesXml, _productAttributeParser);
+ cellProductItem = new PdfPCell(new Phrase(sku ?? String.Empty, font));
+ cellProductItem.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cellProductItem);
+ }
+
+ //price
+ string unitPrice;
+ if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
+ {
+ //including tax
+ var unitPriceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.UnitPriceInclTax, order.CurrencyRate);
+ unitPrice = _priceFormatter.FormatPrice(unitPriceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+ }
+ else
+ {
+ //excluding tax
+ var unitPriceExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.UnitPriceExclTax, order.CurrencyRate);
+ unitPrice = _priceFormatter.FormatPrice(unitPriceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
+ }
+ cellProductItem = new PdfPCell(new Phrase(unitPrice, font));
+ cellProductItem.HorizontalAlignment = Element.ALIGN_LEFT;
+ productsTable.AddCell(cellProductItem);
+
+ //qty
+ cellProductItem = new PdfPCell(new Phrase(orderItem.Quantity.ToString(), font));
+ cellProductItem.HorizontalAlignment = Element.ALIGN_LEFT;
+ productsTable.AddCell(cellProductItem);
+
+ //total
+ string subTotal;
+ if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
+ {
+ //including tax
+ var priceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.PriceInclTax, order.CurrencyRate);
+ subTotal = _priceFormatter.FormatPrice(priceInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+ }
+ else
+ {
+ //excluding tax
+ var priceExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(orderItem.PriceExclTax, order.CurrencyRate);
+ subTotal = _priceFormatter.FormatPrice(priceExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
+ }
+ cellProductItem = new PdfPCell(new Phrase(subTotal, font));
+ cellProductItem.HorizontalAlignment = Element.ALIGN_LEFT;
+ productsTable.AddCell(cellProductItem);
+ }
+ doc.Add(productsTable);
+
+ #endregion
+
+ #region Checkout attributes
+
+ //vendors cannot see checkout attributes
+ if (vendorId == 0 && !String.IsNullOrEmpty(order.CheckoutAttributeDescription))
+ {
+ doc.Add(new Paragraph(" "));
+ var attribTable = new PdfPTable(1);
+ attribTable.RunDirection = GetDirection(lang);
+ attribTable.WidthPercentage = 100f;
+
+ string attributes = HtmlHelper.ConvertHtmlToPlainText(order.CheckoutAttributeDescription, true, true);
+ var cCheckoutAttributes = new PdfPCell(new Phrase(attributes, font));
+ cCheckoutAttributes.Border = Rectangle.NO_BORDER;
+ cCheckoutAttributes.HorizontalAlignment = Element.ALIGN_RIGHT;
+ attribTable.AddCell(cCheckoutAttributes);
+ doc.Add(attribTable);
+ }
+
+ #endregion
+
+ #region Totals
+
+ //vendors cannot see totals
+ if (vendorId == 0)
+ {
+ //subtotal
+ var totalsTable = new PdfPTable(1);
+ totalsTable.RunDirection = GetDirection(lang);
+ totalsTable.DefaultCell.Border = Rectangle.NO_BORDER;
+ totalsTable.WidthPercentage = 100f;
+
+ //order subtotal
+ if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax && !_taxSettings.ForceTaxExclusionFromOrderSubtotal)
+ {
+ //including tax
+
+ var orderSubtotalInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubtotalInclTax, order.CurrencyRate);
+ string orderSubtotalInclTaxStr = _priceFormatter.FormatPrice(orderSubtotalInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Sub-Total", lang.Id), orderSubtotalInclTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ else
+ {
+ //excluding tax
+
+ var orderSubtotalExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubtotalExclTax, order.CurrencyRate);
+ string orderSubtotalExclTaxStr = _priceFormatter.FormatPrice(orderSubtotalExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Sub-Total", lang.Id), orderSubtotalExclTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+
+ //discount (applied to order subtotal)
+ if (order.OrderSubTotalDiscountExclTax > decimal.Zero)
+ {
+ //order subtotal
+ if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax && !_taxSettings.ForceTaxExclusionFromOrderSubtotal)
+ {
+ //including tax
+
+ var orderSubTotalDiscountInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubTotalDiscountInclTax, order.CurrencyRate);
+ string orderSubTotalDiscountInCustomerCurrencyStr = _priceFormatter.FormatPrice(-orderSubTotalDiscountInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Discount", lang.Id), orderSubTotalDiscountInCustomerCurrencyStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ else
+ {
+ //excluding tax
+
+ var orderSubTotalDiscountExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderSubTotalDiscountExclTax, order.CurrencyRate);
+ string orderSubTotalDiscountInCustomerCurrencyStr = _priceFormatter.FormatPrice(-orderSubTotalDiscountExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Discount", lang.Id), orderSubTotalDiscountInCustomerCurrencyStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ }
+
+ //shipping
+ if (order.ShippingStatus != ShippingStatus.ShippingNotRequired && order.OrderShippingInclTax != decimal.Zero)
+ {
+ if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
+ {
+ //including tax
+ var orderShippingInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderShippingInclTax, order.CurrencyRate);
+ string orderShippingInclTaxStr = _priceFormatter.FormatShippingPrice(orderShippingInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Shipping", lang.Id), orderShippingInclTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ else
+ {
+ //excluding tax
+ var orderShippingExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderShippingExclTax, order.CurrencyRate);
+ string orderShippingExclTaxStr = _priceFormatter.FormatShippingPrice(orderShippingExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Shipping", lang.Id), orderShippingExclTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ }
+
+ //reward points applied to order amount
+ if (order.RedeemedRewardPointsEntry != null && order.RedeemedRewardPointsEntry.UsedAmount != decimal.Zero)
+ {
+ string rpTitle = string.Format(_localizationService.GetResource("PDFInvoice.RewardPoints", lang.Id), -order.RedeemedRewardPointsEntry.Points);
+ string rpAmount = _priceFormatter.FormatPrice(-(_currencyService.ConvertCurrency(order.RedeemedRewardPointsEntry.UsedAmount, order.CurrencyRate)), true, order.CustomerCurrencyCode, false, lang);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", rpTitle, rpAmount), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ //payment fee
+ if (order.PaymentMethodAdditionalFeeExclTax != decimal.Zero) //alow negative
+ {
+ if (order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
+ {
+ //including tax
+ var paymentMethodAdditionalFeeInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeInclTax, order.CurrencyRate);
+ string paymentMethodAdditionalFeeInclTaxStr = _priceFormatter.FormatPaymentMethodAdditionalFee(paymentMethodAdditionalFeeInclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.PaymentMethodAdditionalFee", lang.Id), paymentMethodAdditionalFeeInclTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ else
+ {
+ //excluding tax
+ var paymentMethodAdditionalFeeExclTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeExclTax, order.CurrencyRate);
+ string paymentMethodAdditionalFeeExclTaxStr = _priceFormatter.FormatPaymentMethodAdditionalFee(paymentMethodAdditionalFeeExclTaxInCustomerCurrency, true, order.CustomerCurrencyCode, lang, false);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.PaymentMethodAdditionalFee", lang.Id), paymentMethodAdditionalFeeExclTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ }
+
+ //discount (applied to order total)
+ if (order.OrderDiscount > decimal.Zero)
+ {
+ var orderDiscountInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderDiscount, order.CurrencyRate);
+ string orderDiscountInCustomerCurrencyStr = _priceFormatter.FormatPrice(-orderDiscountInCustomerCurrency, true, order.CustomerCurrencyCode, false, lang);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Discount", lang.Id), orderDiscountInCustomerCurrencyStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+
+ //tax
+ string taxStr = string.Empty;
+ var taxRates = new SortedDictionary();
+ bool displayTax = true;
+ bool displayTaxRates = true;
+ if (_taxSettings.HideTaxInOrderSummary && order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax)
+ {
+ displayTax = false;
+ }
+ else
+ {
+ if (order.OrderTax == 0 && _taxSettings.HideZeroTax)
+ {
+ displayTax = false;
+ displayTaxRates = false;
+ }
+ else
+ {
+ taxRates = order.TaxRatesDictionary;
+
+ displayTaxRates = _taxSettings.DisplayTaxRates && taxRates.Any();
+ displayTax = !displayTaxRates;
+
+ var orderTaxInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTax, order.CurrencyRate);
+ taxStr = _priceFormatter.FormatPrice(orderTaxInCustomerCurrency, true, order.CustomerCurrencyCode, false, lang);
+ }
+ }
+ if (displayTax)
+ {
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Tax", lang.Id), taxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ if (displayTaxRates)
+ {
+ foreach (var item in taxRates)
+ {
+ string taxRate = String.Format(_localizationService.GetResource("PDFInvoice.TaxRate", lang.Id), _priceFormatter.FormatTaxRate(item.Key));
+ string taxValue = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value.TaxAmount, order.CurrencyRate), true, order.CustomerCurrencyCode, false, lang);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", taxRate, taxValue), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+ }
+
+ //shipping non taxable
+ if (order.ShippingStatus != ShippingStatus.ShippingNotRequired && order.OrderShippingNonTaxable != decimal.Zero)
+ {
+ var orderShippingNonTaxableInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderShippingNonTaxable, order.CurrencyRate);
+ string orderShippingNonTaxableTaxStr = _priceFormatter.FormatShippingPrice(orderShippingNonTaxableInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.Shipping", lang.Id), orderShippingNonTaxableTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+
+ //payment fee non taxable
+ if (order.PaymentMethodAdditionalFeeNonTaxable != decimal.Zero) //alow negative
+ {
+ var paymentMethodAdditionalFeeNonTaxableInCustomerCurrency = _currencyService.ConvertCurrency(order.PaymentMethodAdditionalFeeNonTaxable, order.CurrencyRate);
+ string paymentMethodAdditionalFeeNonTaxStr = _priceFormatter.FormatPaymentMethodAdditionalFee(paymentMethodAdditionalFeeNonTaxableInCustomerCurrency, true, order.CustomerCurrencyCode, lang, true);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.PaymentMethodAdditionalFee", lang.Id), paymentMethodAdditionalFeeNonTaxStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+
+ //gift cards
+ foreach (var gcuh in order.GiftCardUsageHistory)
+ {
+ string gcTitle = string.Format(_localizationService.GetResource("PDFInvoice.GiftCardInfo", lang.Id), gcuh.GiftCard.GiftCardCouponCode);
+ string gcAmountStr = _priceFormatter.FormatPrice(-(_currencyService.ConvertCurrency(gcuh.UsedValue, order.CurrencyRate)), true, order.CustomerCurrencyCode, false, lang);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", gcTitle, gcAmountStr), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+
+ //reward points
+ if (order.RedeemedRewardPointsEntry != null && order.RedeemedRewardPointsEntry.UsedAmountPurchased != decimal.Zero)
+ {
+ string rpTitle = string.Format(_localizationService.GetResource("PDFInvoice.RewardPointsPurchased", lang.Id), -order.RedeemedRewardPointsEntry.PointsPurchased);
+ string rpAmount = _priceFormatter.FormatPrice(-(_currencyService.ConvertCurrency(order.RedeemedRewardPointsEntry.UsedAmountPurchased, order.CurrencyRate)), true, order.CustomerCurrencyCode, false, lang);
+
+ var p = new PdfPCell(new Paragraph(String.Format("{0} {1}", rpTitle, rpAmount), font));
+ p.HorizontalAlignment = Element.ALIGN_RIGHT;
+ p.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(p);
+ }
+
+ //order total
+ var orderTotalInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
+ string orderTotalStr = _priceFormatter.FormatPrice(orderTotalInCustomerCurrency, true, order.CustomerCurrencyCode, false, lang);
+
+
+ var pTotal = new PdfPCell(new Paragraph(String.Format("{0} {1}", _localizationService.GetResource("PDFInvoice.OrderTotal", lang.Id), orderTotalStr), titleFont));
+ pTotal.HorizontalAlignment = Element.ALIGN_RIGHT;
+ pTotal.Border = Rectangle.NO_BORDER;
+ totalsTable.AddCell(pTotal);
+
+ doc.Add(totalsTable);
+ }
+
+ #endregion
+
+ #region Order notes
+
+ if (pdfSettingsByStore.RenderOrderNotes)
+ {
+ var orderNotes = order.OrderNotes
+ .Where(on => on.DisplayToCustomer)
+ .OrderByDescending(on => on.CreatedOnUtc)
+ .ToList();
+ if (orderNotes.Any())
+ {
+ var notesHeader = new PdfPTable(1);
+ notesHeader.RunDirection = GetDirection(lang);
+ notesHeader.WidthPercentage = 100f;
+ var cellOrderNote = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.OrderNotes", lang.Id), titleFont));
+ cellOrderNote.Border = Rectangle.NO_BORDER;
+ notesHeader.AddCell(cellOrderNote);
+ doc.Add(notesHeader);
+ doc.Add(new Paragraph(" "));
+
+ var notesTable = new PdfPTable(2);
+ notesTable.RunDirection = GetDirection(lang);
+ if (lang.Rtl)
+ {
+ notesTable.SetWidths(new[] {70, 30});
+ }
+ else
+ {
+ notesTable.SetWidths(new[] {30, 70});
+ }
+ notesTable.WidthPercentage = 100f;
+
+ //created on
+ cellOrderNote = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.OrderNotes.CreatedOn", lang.Id), font));
+ cellOrderNote.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellOrderNote.HorizontalAlignment = Element.ALIGN_CENTER;
+ notesTable.AddCell(cellOrderNote);
+
+ //note
+ cellOrderNote = new PdfPCell(new Phrase(_localizationService.GetResource("PDFInvoice.OrderNotes.Note", lang.Id), font));
+ cellOrderNote.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cellOrderNote.HorizontalAlignment = Element.ALIGN_CENTER;
+ notesTable.AddCell(cellOrderNote);
+
+ foreach (var orderNote in orderNotes)
+ {
+ cellOrderNote = new PdfPCell(new Phrase(_dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc).ToString(), font));
+ cellOrderNote.HorizontalAlignment = Element.ALIGN_LEFT;
+ notesTable.AddCell(cellOrderNote);
+
+ cellOrderNote = new PdfPCell(new Phrase(HtmlHelper.ConvertHtmlToPlainText(orderNote.FormatOrderNoteText(), true, true), font));
+ cellOrderNote.HorizontalAlignment = Element.ALIGN_LEFT;
+ notesTable.AddCell(cellOrderNote);
+
+ //should we display a link to downloadable files here?
+ //I think, no. Onyway, PDFs are printable documents and links (files) are useful here
+ }
+ doc.Add(notesTable);
+ }
+ }
+
+ #endregion
+
+ #region Footer
+
+ if (!String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn1) || !String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn2))
+ {
+ var column1Lines = String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn1) ?
+ new List() :
+ pdfSettingsByStore.InvoiceFooterTextColumn1
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
+ .ToList();
+ var column2Lines = String.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn2) ?
+ new List() :
+ pdfSettingsByStore.InvoiceFooterTextColumn2
+ .Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries)
+ .ToList();
+ if (column1Lines.Any() || column2Lines.Any())
+ {
+ var totalLines = Math.Max(column1Lines.Count, column2Lines.Count);
+ const float margin = 43;
+
+ //if you have really a lot of lines in the footer, then replace 9 with 10 or 11
+ int footerHeight = totalLines * 9;
+ var directContent = pdfWriter.DirectContent;
+ directContent.MoveTo(pageSize.GetLeft(margin), pageSize.GetBottom(margin) + footerHeight);
+ directContent.LineTo(pageSize.GetRight(margin), pageSize.GetBottom(margin) + footerHeight);
+ directContent.Stroke();
+
+
+ var footerTable = new PdfPTable(2);
+ footerTable.WidthPercentage = 100f;
+ footerTable.SetTotalWidth(new float[] { 250, 250 });
+ footerTable.RunDirection = GetDirection(lang);
+
+ //column 1
+ if (column1Lines.Any())
+ {
+ var column1 = new PdfPCell(new Phrase());
+ column1.Border = Rectangle.NO_BORDER;
+ column1.HorizontalAlignment = Element.ALIGN_LEFT;
+ foreach (var footerLine in column1Lines)
+ {
+ column1.Phrase.Add(new Phrase(footerLine, font));
+ column1.Phrase.Add(new Phrase(Environment.NewLine));
+ }
+ footerTable.AddCell(column1);
+ }
+ else
+ {
+ var column = new PdfPCell(new Phrase(" "));
+ column.Border = Rectangle.NO_BORDER;
+ footerTable.AddCell(column);
+ }
+
+ //column 2
+ if (column2Lines.Any())
+ {
+ var column2 = new PdfPCell(new Phrase());
+ column2.Border = Rectangle.NO_BORDER;
+ column2.HorizontalAlignment = Element.ALIGN_LEFT;
+ foreach (var footerLine in column2Lines)
+ {
+ column2.Phrase.Add(new Phrase(footerLine, font));
+ column2.Phrase.Add(new Phrase(Environment.NewLine));
+ }
+ footerTable.AddCell(column2);
+ }
+ else
+ {
+ var column = new PdfPCell(new Phrase(" "));
+ column.Border = Rectangle.NO_BORDER;
+ footerTable.AddCell(column);
+ }
+
+ footerTable.WriteSelectedRows(0, totalLines, pageSize.GetLeft(margin), pageSize.GetBottom(margin) + footerHeight, directContent);
+ }
+ }
+
+ #endregion
+
+ ordNum++;
+ if (ordNum < ordCount)
+ {
+ doc.NewPage();
+ }
+ }
+ doc.Close();
+ }
+
+ ///
+ /// Print packaging slips to PDF
+ ///
+ /// Stream
+ /// Shipments
+ /// Language identifier; 0 to use a language used when placing an order
+ public virtual void PrintPackagingSlipsToPdf(Stream stream, IList shipments, int languageId = 0)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ if (shipments == null)
+ throw new ArgumentNullException("shipments");
+
+ var pageSize = PageSize.A4;
+
+ if (_pdfSettings.LetterPageSizeEnabled)
+ {
+ pageSize = PageSize.LETTER;
+ }
+
+ var doc = new Document(pageSize);
+ PdfWriter.GetInstance(doc, stream);
+ doc.Open();
+
+ //fonts
+ var titleFont = GetFont();
+ titleFont.SetStyle(Font.BOLD);
+ titleFont.Color = BaseColor.BLACK;
+ var font = GetFont();
+ var attributesFont = GetFont();
+ attributesFont.SetStyle(Font.ITALIC);
+
+ int shipmentCount = shipments.Count;
+ int shipmentNum = 0;
+
+ foreach (var shipment in shipments)
+ {
+ var order = shipment.Order;
+
+ var lang = _languageService.GetLanguageById(languageId == 0 ? order.CustomerLanguageId : languageId);
+ if (lang == null || !lang.Published)
+ lang = _workContext.WorkingLanguage;
+
+ var addressTable = new PdfPTable(1);
+ if (lang.Rtl)
+ addressTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
+ addressTable.DefaultCell.Border = Rectangle.NO_BORDER;
+ addressTable.WidthPercentage = 100f;
+
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Shipment", lang.Id), shipment.Id), titleFont));
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Order", lang.Id), order.CustomOrderNumber), titleFont));
+
+ if (!order.PickUpInStore)
+ {
+ if (order.ShippingAddress == null)
+ throw new NopException(string.Format("Shipping is required, but address is not available. Order ID = {0}", order.Id));
+
+ if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.ShippingAddress.Company))
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Company", lang.Id),
+ order.ShippingAddress.Company), font));
+
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Name", lang.Id),
+ order.ShippingAddress.FirstName + " " + order.ShippingAddress.LastName), font));
+ if (_addressSettings.PhoneEnabled)
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Phone", lang.Id),
+ order.ShippingAddress.PhoneNumber), font));
+ if (_addressSettings.StreetAddressEnabled)
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Address", lang.Id),
+ order.ShippingAddress.Address1), font));
+
+ if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.ShippingAddress.Address2))
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.Address2", lang.Id),
+ order.ShippingAddress.Address2), font));
+
+ if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
+ addressTable.AddCell(new Paragraph(String.Format("{0}, {1} {2}", order.ShippingAddress.City, order.ShippingAddress.StateProvince != null
+ ? order.ShippingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id)
+ : "", order.ShippingAddress.ZipPostalCode), font));
+
+ if (_addressSettings.CountryEnabled && order.ShippingAddress.Country != null)
+ addressTable.AddCell(new Paragraph(order.ShippingAddress.Country.GetLocalized(x => x.Name, lang.Id), font));
+
+ //custom attributes
+ var customShippingAddressAttributes = _addressAttributeFormatter.FormatAttributes(order.ShippingAddress.CustomAttributes);
+ if (!String.IsNullOrEmpty(customShippingAddressAttributes))
+ {
+ addressTable.AddCell(new Paragraph(HtmlHelper.ConvertHtmlToPlainText(customShippingAddressAttributes, true, true), font));
+ }
+ }
+ else
+ if (order.PickupAddress != null)
+ {
+ addressTable.AddCell(new Paragraph(_localizationService.GetResource("PDFInvoice.Pickup", lang.Id), titleFont));
+ if (!string.IsNullOrEmpty(order.PickupAddress.Address1))
+ addressTable.AddCell(new Paragraph(string.Format(" {0}", string.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.PickupAddress.Address1)), font));
+ if (!string.IsNullOrEmpty(order.PickupAddress.City))
+ addressTable.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.City), font));
+ if (order.PickupAddress.Country != null)
+ addressTable.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.Country.GetLocalized(x => x.Name, lang.Id)), font));
+ if (!string.IsNullOrEmpty(order.PickupAddress.ZipPostalCode))
+ addressTable.AddCell(new Paragraph(string.Format(" {0}", order.PickupAddress.ZipPostalCode), font));
+ addressTable.AddCell(new Paragraph(" "));
+ }
+
+ addressTable.AddCell(new Paragraph(" "));
+
+ addressTable.AddCell(new Paragraph(String.Format(_localizationService.GetResource("PDFPackagingSlip.ShippingMethod", lang.Id), order.ShippingMethod), font));
+ addressTable.AddCell(new Paragraph(" "));
+ doc.Add(addressTable);
+
+ var productsTable = new PdfPTable(3);
+ productsTable.WidthPercentage = 100f;
+ if (lang.Rtl)
+ {
+ productsTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
+ productsTable.SetWidths(new[] {20, 20, 60});
+ }
+ else
+ {
+ productsTable.SetWidths(new[] {60, 20, 20});
+ }
+
+ //product name
+ var cell = new PdfPCell(new Phrase(_localizationService.GetResource("PDFPackagingSlip.ProductName", lang.Id),font));
+ cell.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cell);
+
+ //SKU
+ cell = new PdfPCell(new Phrase(_localizationService.GetResource("PDFPackagingSlip.SKU", lang.Id), font));
+ cell.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cell);
+
+ //qty
+ cell = new PdfPCell(new Phrase(_localizationService.GetResource("PDFPackagingSlip.QTY", lang.Id), font));
+ cell.BackgroundColor = BaseColor.LIGHT_GRAY;
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cell);
+
+ foreach (var si in shipment.ShipmentItems)
+ {
+ var productAttribTable = new PdfPTable(1);
+ if (lang.Rtl)
+ productAttribTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
+ productAttribTable.DefaultCell.Border = Rectangle.NO_BORDER;
+
+ //product name
+ var orderItem = _orderService.GetOrderItemById(si.OrderItemId);
+ if (orderItem == null)
+ continue;
+
+ var p = orderItem.Product;
+ string name = p.GetLocalized(x => x.Name, lang.Id);
+ productAttribTable.AddCell(new Paragraph(name, font));
+ //attributes
+ if (!String.IsNullOrEmpty(orderItem.AttributeDescription))
+ {
+ var attributesParagraph = new Paragraph(HtmlHelper.ConvertHtmlToPlainText(orderItem.AttributeDescription, true, true), attributesFont);
+ productAttribTable.AddCell(attributesParagraph);
+ }
+ //rental info
+ if (orderItem.Product.IsRental)
+ {
+ var rentalStartDate = orderItem.RentalStartDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalStartDateUtc.Value) : "";
+ var rentalEndDate = orderItem.RentalEndDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalEndDateUtc.Value) : "";
+ var rentalInfo = string.Format(_localizationService.GetResource("Order.Rental.FormattedDate"),
+ rentalStartDate, rentalEndDate);
+
+ var rentalInfoParagraph = new Paragraph(rentalInfo, attributesFont);
+ productAttribTable.AddCell(rentalInfoParagraph);
+ }
+ productsTable.AddCell(productAttribTable);
+
+ //SKU
+ var sku = p.FormatSku(orderItem.AttributesXml, _productAttributeParser);
+ cell = new PdfPCell(new Phrase(sku ?? String.Empty, font));
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cell);
+
+ //qty
+ cell = new PdfPCell(new Phrase(si.Quantity.ToString(), font));
+ cell.HorizontalAlignment = Element.ALIGN_CENTER;
+ productsTable.AddCell(cell);
+ }
+ doc.Add(productsTable);
+
+ shipmentNum++;
+ if (shipmentNum < shipmentCount)
+ {
+ doc.NewPage();
+ }
+ }
+
+
+ doc.Close();
+ }
+
+ ///
+ /// Print products to PDF
+ ///
+ /// Stream
+ /// Products
+ public virtual void PrintProductsToPdf(Stream stream, IList products)
+ {
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ if (products == null)
+ throw new ArgumentNullException("products");
+
+ var lang = _workContext.WorkingLanguage;
+
+ var pageSize = PageSize.A4;
+
+ if (_pdfSettings.LetterPageSizeEnabled)
+ {
+ pageSize = PageSize.LETTER;
+ }
+
+ var doc = new Document(pageSize);
+ PdfWriter.GetInstance(doc, stream);
+ doc.Open();
+
+ //fonts
+ var titleFont = GetFont();
+ titleFont.SetStyle(Font.BOLD);
+ titleFont.Color = BaseColor.BLACK;
+ var font = GetFont();
+
+ int productNumber = 1;
+ int prodCount = products.Count;
+
+ foreach (var product in products)
+ {
+ string productName = product.GetLocalized(x => x.Name, lang.Id);
+ string productDescription = product.GetLocalized(x => x.FullDescription, lang.Id);
+
+ var productTable = new PdfPTable(1);
+ productTable.WidthPercentage = 100f;
+ productTable.DefaultCell.Border = Rectangle.NO_BORDER;
+ if (lang.Rtl)
+ {
+ productTable.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
+ }
+
+ productTable.AddCell(new Paragraph(String.Format("{0}. {1}", productNumber, productName), titleFont));
+ productTable.AddCell(new Paragraph(" "));
+ productTable.AddCell(new Paragraph(HtmlHelper.StripTags(HtmlHelper.ConvertHtmlToPlainText(productDescription, decode: true)), font));
+ productTable.AddCell(new Paragraph(" "));
+
+ if (product.ProductType == ProductType.SimpleProduct)
+ {
+ //simple product
+ //render its properties such as price, weight, etc
+ var priceStr = string.Format("{0} {1}", product.Price.ToString("0.00"), _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode);
+ if (product.IsRental)
+ priceStr = _priceFormatter.FormatRentalProductPeriod(product, priceStr);
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.Price", lang.Id), priceStr), font));
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.SKU", lang.Id), product.Sku), font));
+
+ if (product.IsShipEnabled && product.Weight > Decimal.Zero)
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1} {2}", _localizationService.GetResource("PDFProductCatalog.Weight", lang.Id), product.Weight.ToString("0.00"), _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name), font));
+
+ if (product.ManageInventoryMethod == ManageInventoryMethod.ManageStock)
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.StockQuantity", lang.Id), product.GetTotalStockQuantity()), font));
+
+ productTable.AddCell(new Paragraph(" "));
+ }
+ var pictures = _pictureService.GetPicturesByProductId(product.Id);
+ if (pictures.Any())
+ {
+ var table = new PdfPTable(2);
+ table.WidthPercentage = 100f;
+ if (lang.Rtl)
+ {
+ table.RunDirection = PdfWriter.RUN_DIRECTION_RTL;
+ }
+
+ foreach (var pic in pictures)
+ {
+ var picBinary = _pictureService.LoadPictureBinary(pic);
+ if (picBinary != null && picBinary.Length > 0)
+ {
+ var pictureLocalPath = _pictureService.GetThumbLocalPath(pic, 200, false);
+ var cell = new PdfPCell(Image.GetInstance(pictureLocalPath));
+ cell.HorizontalAlignment = Element.ALIGN_LEFT;
+ cell.Border = Rectangle.NO_BORDER;
+ table.AddCell(cell);
+ }
+ }
+
+ if (pictures.Count % 2 > 0)
+ {
+ var cell = new PdfPCell(new Phrase(" "));
+ cell.Border = Rectangle.NO_BORDER;
+ table.AddCell(cell);
+ }
+
+ productTable.AddCell(table);
+ productTable.AddCell(new Paragraph(" "));
+ }
+
+
+ if (product.ProductType == ProductType.GroupedProduct)
+ {
+ //grouped product. render its associated products
+ int pvNum = 1;
+ foreach (var associatedProduct in _productService.GetAssociatedProducts(product.Id, showHidden: true))
+ {
+ productTable.AddCell(new Paragraph(String.Format("{0}-{1}. {2}", productNumber, pvNum, associatedProduct.GetLocalized(x => x.Name, lang.Id)), font));
+ productTable.AddCell(new Paragraph(" "));
+
+ //uncomment to render associated product description
+ //string apDescription = associatedProduct.GetLocalized(x => x.ShortDescription, lang.Id);
+ //if (!String.IsNullOrEmpty(apDescription))
+ //{
+ // productTable.AddCell(new Paragraph(HtmlHelper.StripTags(HtmlHelper.ConvertHtmlToPlainText(apDescription)), font));
+ // productTable.AddCell(new Paragraph(" "));
+ //}
+
+ //uncomment to render associated product picture
+ //var apPicture = _pictureService.GetPicturesByProductId(associatedProduct.Id).FirstOrDefault();
+ //if (apPicture != null)
+ //{
+ // var picBinary = _pictureService.LoadPictureBinary(apPicture);
+ // if (picBinary != null && picBinary.Length > 0)
+ // {
+ // var pictureLocalPath = _pictureService.GetThumbLocalPath(apPicture, 200, false);
+ // productTable.AddCell(Image.GetInstance(pictureLocalPath));
+ // }
+ //}
+
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1} {2}", _localizationService.GetResource("PDFProductCatalog.Price", lang.Id), associatedProduct.Price.ToString("0.00"), _currencyService.GetCurrencyById(_currencySettings.PrimaryStoreCurrencyId).CurrencyCode), font));
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.SKU", lang.Id), associatedProduct.Sku), font));
+
+ if (associatedProduct.IsShipEnabled && associatedProduct.Weight > Decimal.Zero)
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1} {2}", _localizationService.GetResource("PDFProductCatalog.Weight", lang.Id), associatedProduct.Weight.ToString("0.00"), _measureService.GetMeasureWeightById(_measureSettings.BaseWeightId).Name), font));
+
+ if (associatedProduct.ManageInventoryMethod == ManageInventoryMethod.ManageStock)
+ productTable.AddCell(new Paragraph(String.Format("{0}: {1}", _localizationService.GetResource("PDFProductCatalog.StockQuantity", lang.Id), associatedProduct.GetTotalStockQuantity()), font));
+
+ productTable.AddCell(new Paragraph(" "));
+
+ pvNum++;
+ }
+ }
+
+ doc.Add(productTable);
+
+ productNumber++;
+
+ if (productNumber <= prodCount)
+ {
+ doc.NewPage();
+ }
+ }
+
+ doc.Close();
+ }
+
+ #endregion
+ }
}
\ No newline at end of file
diff --git a/src/Libraries/Nop.Services/Common/PdfService7.cs b/src/Libraries/Nop.Services/Common/PdfService7.cs
new file mode 100644
index 00000000000..8a0f5b5e482
--- /dev/null
+++ b/src/Libraries/Nop.Services/Common/PdfService7.cs
@@ -0,0 +1,1089 @@
+// RTL Support provided by Credo inc (www.credo.co.il || info@credo.co.il)
+
+using System;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using Nop.Core;
+using Nop.Core.Domain.Catalog;
+using Nop.Core.Domain.Common;
+using Nop.Core.Domain.Directory;
+using Nop.Core.Domain.Orders;
+using Nop.Core.Domain.Shipping;
+using Nop.Core.Domain.Tax;
+using Nop.Core.Html;
+using Nop.Services.Catalog;
+using Nop.Services.Configuration;
+using Nop.Services.Directory;
+using Nop.Services.Helpers;
+using Nop.Services.Localization;
+using Nop.Services.Media;
+using Nop.Services.Orders;
+using Nop.Services.Payments;
+using Nop.Services.Stores;
+using iText.Kernel.Font;
+using iText.IO.Font;
+using iText.Kernel.Geom;
+using iText.Kernel.Pdf;
+using iText.Kernel.Pdf.Canvas;
+using iText.Layout;
+using iText.Layout.Element;
+using iText.Layout.Borders;
+using iText.Layout.Properties;
+using iText.Kernel.Colors;
+using iText.Kernel.Pdf.Action;
+using iText.IO.Image;
+using iText.Kernel.Pdf.Xobject;
+using iText.Kernel.Events;
+using Nop.Services.Tax;
+using System.Text.RegularExpressions;
+
+namespace Nop.Services.Common
+{
+ ///
+ /// PDF service
+ ///
+ public partial class PdfService7 : IPdfService
+ {
+ #region Fields
+
+ private readonly ILocalizationService _localizationService;
+ private readonly ILanguageService _languageService;
+ private readonly IWorkContext _workContext;
+ private readonly IOrderService _orderService;
+ private readonly IPaymentService _paymentService;
+ private readonly IDateTimeHelper _dateTimeHelper;
+ private readonly IPriceFormatter _priceFormatter;
+ private readonly ICurrencyService _currencyService;
+ private readonly IMeasureService _measureService;
+ private readonly IPictureService _pictureService;
+ private readonly IProductService _productService;
+ private readonly IProductAttributeParser _productAttributeParser;
+ private readonly IStoreService _storeService;
+ private readonly IStoreContext _storeContext;
+ private readonly ISettingService _settingContext;
+ private readonly IAddressAttributeFormatter _addressAttributeFormatter;
+
+ private readonly CatalogSettings _catalogSettings;
+ private readonly CurrencySettings _currencySettings;
+ private readonly MeasureSettings _measureSettings;
+ private readonly PdfSettings _pdfSettings;
+ private readonly TaxSettings _taxSettings;
+ private readonly AddressSettings _addressSettings;
+ private readonly ICheckoutAttributeParser _checkoutAttributeParser;
+ private readonly ITaxService _taxService;
+
+ #endregion
+
+ #region Ctor
+
+ public PdfService7(ILocalizationService localizationService,
+ ILanguageService languageService,
+ IWorkContext workContext,
+ IOrderService orderService,
+ IPaymentService paymentService,
+ IDateTimeHelper dateTimeHelper,
+ IPriceFormatter priceFormatter,
+ ICurrencyService currencyService,
+ IMeasureService measureService,
+ IPictureService pictureService,
+ IProductService productService,
+ IProductAttributeParser productAttributeParser,
+ IStoreService storeService,
+ IStoreContext storeContext,
+ ISettingService settingContext,
+ IAddressAttributeFormatter addressAttributeFormatter,
+ CatalogSettings catalogSettings,
+ CurrencySettings currencySettings,
+ MeasureSettings measureSettings,
+ PdfSettings pdfSettings,
+ TaxSettings taxSettings,
+ AddressSettings addressSettings,
+ ICheckoutAttributeParser checkoutAttributeParser,
+ ITaxService taxService)
+ {
+ this._localizationService = localizationService;
+ this._languageService = languageService;
+ this._workContext = workContext;
+ this._orderService = orderService;
+ this._paymentService = paymentService;
+ this._dateTimeHelper = dateTimeHelper;
+ this._priceFormatter = priceFormatter;
+ this._currencyService = currencyService;
+ this._measureService = measureService;
+ this._pictureService = pictureService;
+ this._productService = productService;
+ this._productAttributeParser = productAttributeParser;
+ this._storeService = storeService;
+ this._storeContext = storeContext;
+ this._settingContext = settingContext;
+ this._addressAttributeFormatter = addressAttributeFormatter;
+ this._currencySettings = currencySettings;
+ this._catalogSettings = catalogSettings;
+ this._measureSettings = measureSettings;
+ this._pdfSettings = pdfSettings;
+ this._taxSettings = taxSettings;
+ this._addressSettings = addressSettings;
+ this._checkoutAttributeParser = checkoutAttributeParser;
+ this._taxService = taxService;
+ }
+
+ #endregion
+
+ #region Utilities
+
+ #region fonts
+ ///
+ /// Get font
+ ///
+ /// Font
+ protected virtual PdfFont GetFont()
+ {
+ //nopCommerce supports unicode characters
+ //nopCommerce uses Free Serif font by default (~/App_Data/Pdf/FreeSerif.ttf file)
+ //It was downloaded from http://savannah.gnu.org/projects/freefont
+ return GetFont(_pdfSettings.FontFileName);
+ }
+ ///
+ /// Get font
+ ///
+ /// Font file name
+ /// Font
+ protected virtual PdfFont GetFont(string fontFileName)
+ {
+ //if (fontFileName == null)
+ // throw new ArgumentNullException("fontFileName");
+
+ //string fontPath = Path.Combine(CommonHelper.MapPath("~/App_Data/Pdf/"), fontFileName);
+ //var baseFont = BaseFont.CreateFont(fontPath, BaseFont.IDENTITY_H, BaseFont.EMBEDDED);
+ var font = PdfFontFactory.CreateFont(FontConstants.HELVETICA, PdfEncodings.CP1252, true);
+ return font;
+ }
+ ///
+ /// Get font bold
+ ///
+ /// Font
+ protected virtual PdfFont GetFontBold()
+ {
+ var font = PdfFontFactory.CreateFont(FontConstants.HELVETICA_BOLD, PdfEncodings.CP1252, true);
+ return font;
+ }
+ ///
+ /// Get font italic
+ ///
+ /// Font
+ protected virtual PdfFont GetFontItalic()
+ {
+ var font = PdfFontFactory.CreateFont(FontConstants.HELVETICA_OBLIQUE, PdfEncodings.CP1252, true);
+ return font;
+ }
+ ///
+ /// Get font bold italic
+ ///
+ /// Font
+ protected virtual PdfFont GetFontBoldItalic()
+ {
+ var font = PdfFontFactory.CreateFont(FontConstants.HELVETICA_BOLDOBLIQUE, PdfEncodings.CP1252, true);
+ return font;
+ }
+ #endregion
+
+ #region generic
+ protected virtual float millimetersToPoints(float value)
+ {
+ return (value / 25.4f) * 72f;
+ }
+
+ protected virtual string TakeCountLines(string text, int count)
+ {
+ string lines = "";
+ int i = 0;
+ Match match = Regex.Match(text, "^.*$", RegexOptions.Multiline);
+
+ while (match.Success && i < count)
+ {
+ lines += match + "\n";
+ match = match.NextMatch();
+ i++;
+ }
+
+ return lines;
+ }
+ #endregion
+ #region pagehandler
+ private class PageNumSetter : IEventHandler
+ {
+ PdfFormXObject placeholder;
+ float side = 20;
+ float space = 3.5f;
+ float descent = 3;
+ float x; float y;
+ string txtPage = "Page {0}";
+ int pageOffset = 0;
+
+ //public string txtPage { get; set; } = "Page {0}";
+ //public int pageOffset { get; set; } = 0;
+
+ public PageNumSetter(float posX, float posY, string TxtPage, int PageOffset)
+ {
+ placeholder = new PdfFormXObject(new Rectangle(0, 0, side, side));
+ x = posX;
+ y = posY;
+ txtPage = TxtPage;
+ pageOffset = PageOffset;
+
+ }
+
+ public void HandleEvent(Event e)
+ {
+ PdfDocumentEvent docEvent = (PdfDocumentEvent)e;
+ PdfDocument pdf = docEvent.GetDocument();
+ PdfPage page = docEvent.GetPage();
+ int pageNumber = pdf.GetPageNumber(page) - pageOffset;
+ Rectangle pageSize = page.GetPageSize();
+ PdfCanvas pdfCanvas = new PdfCanvas(page.NewContentStreamBefore(), page.GetResources(), pdf);
+ Canvas canvas = new Canvas(pdfCanvas, pdf, pageSize);
+ Paragraph p = new Paragraph()
+ .Add(string.Format(txtPage, pageNumber)).Add(" / ").SetFontSize(8);
+ canvas.ShowTextAligned(p, x, y, TextAlignment.RIGHT);
+ pdfCanvas.AddXObject(placeholder, x + space, y - descent);
+ pdfCanvas.Release();
+
+
+ }
+ public void writeTotPageNum(PdfDocument pdf)
+ {
+ Canvas canvas = new Canvas(placeholder, pdf);
+ Paragraph p = new Paragraph()
+ .Add((pdf.GetNumberOfPages() - pageOffset).ToString()).SetFontSize(8);
+ canvas.ShowTextAligned(p, 0, descent, TextAlignment.LEFT);
+ }
+
+
+ }
+ #endregion
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Print an order to PDF
+ ///
+ /// Order
+ /// Language identifier; 0 to use a language used when placing an order
+ /// Vendor identifier to limit products; 0 to to print all products. If specified, then totals won't be printed
+ /// A path of generated file
+ public virtual string PrintOrderToPdf(Order order, int languageId = 0, int vendorId = 0)
+ {
+ if (order == null)
+ throw new ArgumentNullException("order");
+
+ string fileName = string.Format("order_{0}_{1}.pdf", order.OrderGuid, CommonHelper.GenerateRandomDigitCode(4));
+ string filePath = System.IO.Path.Combine(CommonHelper.MapPath("~/content/files/ExportImport"), fileName);
+ using (var fileStream = new FileStream(filePath, FileMode.Create))
+ {
+ var orders = new List();
+ orders.Add(order);
+ PrintOrdersToPdf(fileStream, orders, languageId, vendorId);
+ }
+ return filePath;
+ }
+
+ ///
+ /// Print orders to PDF
+ ///
+ /// Stream
+ /// Orders
+ /// Language identifier; 0 to use a language used when placing an order
+ /// Vendor identifier to limit products; 0 to to print all products. If specified, then totals won't be printed
+ public virtual void PrintOrdersToPdf(Stream stream, IList orders, int languageId = 0, int vendorId = 0)
+ {
+ #region doc settings
+ if (stream == null)
+ throw new ArgumentNullException("stream");
+
+ if (orders == null)
+ throw new ArgumentNullException("orders");
+
+ var pageSize = PageSize.A4; //595 x 842
+
+ if (_pdfSettings.LetterPageSizeEnabled)
+ {
+ pageSize = PageSize.LETTER;
+ }
+
+ var pdfWriter = new PdfWriter(stream);
+ var pdfDoc = new PdfDocument(pdfWriter);
+ var doc = new Document(pdfDoc, pageSize);//, false);
+
+ //store footer properties
+ var footerY = doc.GetLeftMargin();
+ var footerX = doc.GetBottomMargin();
+ var footerWidht = pageSize.GetWidth() - doc.GetLeftMargin() - doc.GetRightMargin();
+
+ //set margin for footer
+ var bottomMatgin = 100f;
+ doc.SetBottomMargin(bottomMatgin);
+
+ //generic vars
+ var cellPdf = new Cell();
+ var cellPdf2 = new Cell();
+ var paraPdf = new Paragraph();
+
+ pdfDoc.GetCatalog().SetPageLayout(PdfName.SinglePage);
+ //info
+ PdfDocumentInfo info = pdfDoc.GetDocumentInfo();
+ info.SetTitle("Rechnung");
+ info.SetAuthor("Förderverein The FoodCoop");
+ info.SetSubject("Invoice");
+ //info.SetCreator("The FoodCoop");
+
+ //styles
+ Style styleTitle = new Style().SetFont(GetFontBold()).SetFontSize(12).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style styleNormal = new Style().SetFont(GetFont()).SetFontSize(8).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style styleAttrib = new Style().SetFont(GetFontItalic()).SetFontSize(8).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style5 = new Style().SetFont(GetFont()).SetFontSize(5).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style5b = new Style().SetFont(GetFontBold()).SetFontSize(5).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style6 = new Style().SetFont(GetFont()).SetFontSize(6).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style6b = new Style().SetFont(GetFontBold()).SetFontSize(6).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style8 = new Style().SetFont(GetFont()).SetFontSize(8).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style8b = new Style().SetFont(GetFontBold()).SetFontSize(8).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style style9b = new Style().SetFont(GetFontBold()).SetFontSize(9).SetFontColor(Color.BLACK).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+ Style styleCell = new Style().SetBorder(Border.NO_BORDER).SetHorizontalAlignment(HorizontalAlignment.LEFT).SetTextAlignment(TextAlignment.LEFT);
+
+ #endregion
+
+
+ int ordCount = orders.Count;
+ int ordNum = 0;
+ int pagesSofar = 0;
+
+ foreach (var order in orders)
+ {
+ bool includingTax = order.CustomerTaxDisplayType == TaxDisplayType.IncludingTax;
+
+ //by default _pdfSettings contains settings for the current active store
+ //and we need PdfSettings for the store which was used to place an order
+ //so let's load it based on a store of the current order
+ var pdfSettingsByStore = _settingContext.LoadSetting(order.StoreId);
+
+
+ var lang = _languageService.GetLanguageById(languageId == 0 ? order.CustomerLanguageId : languageId);
+ if (lang == null || !lang.Published)
+ lang = _workContext.WorkingLanguage;
+
+ var currency = _currencyService.GetCurrencyByCode(order.CustomerCurrencyCode)
+ ?? new Currency
+ {
+ CurrencyCode = order.CustomerCurrencyCode
+ };
+ bool showCurrency = false;
+
+ var pageEvent = new PageNumSetter(pageSize.GetWidth() / 2, footerX - 20, _localizationService.GetResource("PDFInvoice.Page", lang.Id) + " {0}", pagesSofar);
+ pdfDoc.AddEventHandler(PdfDocumentEvent.INSERT_PAGE, pageEvent);
+
+ //there is no need for a new page as last element is a footer
+ //if (ordNum > 0)
+ // doc.Add(new AreaBreak(AreaBreakType.NEXT_PAGE));
+
+ //main layout is based on a single table
+ int col = 7;
+ int headerCol = 4; int logoCol = col - headerCol;
+ int shippAddrCol = 4; int ivAddrCol = col - shippAddrCol;
+ //main layout table. Will have only 1 column.
+ var tabPage = new Table(col).SetBorder(Border.NO_BORDER).SetWidthPercent(100f);
+ tabPage.SetProperty(Property.BORDER, null);
+ tabPage.SetProperty(Property.TEXT_ALIGNMENT, TextAlignment.LEFT);
+
+ #region Header&Logo
+
+ var logoPicture = _pictureService.GetPictureById(pdfSettingsByStore.LogoPictureId);
+ var logoExists = logoPicture != null;
+
+ //store info
+ var store = _storeService.GetStoreById(order.StoreId) ?? _storeContext.CurrentStore;
+ var anchor = new Text(store.Url.Trim(new[] { '/' })).SetAction(PdfAction.CreateURI(store.Url));
+
+ paraPdf = new Paragraph(new Text(store.CompanyName).AddStyle(styleTitle)).AddStyle(styleNormal).SetMultipliedLeading(1.2f).Add("\n");
+
+
+ //We use seetings for address and bank
+ if (!string.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn1))
+ {
+ paraPdf.Add(new Text(pdfSettingsByStore.InvoiceFooterTextColumn1).AddStyle(style5));
+ paraPdf.Add(new Text("\n").AddStyle(style6));
+ }
+ if (!string.IsNullOrEmpty(pdfSettingsByStore.InvoiceFooterTextColumn2))
+ {
+ paraPdf.Add(new Text(pdfSettingsByStore.InvoiceFooterTextColumn2).AddStyle(style5));
+ paraPdf.Add(new Text("\n").AddStyle(style6));
+ }
+
+ paraPdf.Add(anchor.AddStyle(style5)).Add("\n");
+ tabPage.AddHeaderCell(new Cell(1, logoExists ? headerCol : col).Add(paraPdf).AddStyle(styleCell));
+
+ //logo
+ if (logoExists)
+ {
+ var logoFilePath = _pictureService.GetThumbLocalPath(logoPicture, 0, false);
+ var logo = new Image(ImageDataFactory.Create(logoFilePath)).SetAutoScale(true);
+ tabPage.AddHeaderCell(new Cell(1, logoCol).Add(logo).AddStyle(styleCell));
+ }
+
+ //empty Line
+ tabPage.AddHeaderCell(new Cell(1, col).Add("\n").AddStyle(styleCell));
+
+ #endregion
+
+ #region Addresses
+
+ #region shippingaddr
+
+ //shipping info
+ if (order.ShippingStatus != ShippingStatus.ShippingNotRequired)
+ {
+ if (!order.PickUpInStore)
+ {
+ if (order.ShippingAddress == null)
+ throw new NopException(string.Format("Shipping is required, but address is not available. Order ID = {0}", order.Id));
+
+ paraPdf = new Paragraph(new Text(_localizationService.GetResource("PDFInvoice.ShippingInformation", lang.Id)).AddStyle(style8b)).AddStyle(styleNormal).SetMultipliedLeading(1.2f).SetFirstLineIndent(-5f).SetMarginLeft(5f);
+ paraPdf.Add("\n");
+ if (!String.IsNullOrEmpty(order.ShippingAddress.Company))
+ paraPdf.Add(new Text(order.ShippingAddress.Company)).Add("\n");
+ if (!String.IsNullOrEmpty(order.ShippingAddress.FirstName))
+ paraPdf.Add(new Text(order.ShippingAddress.FirstName)).Add(" ");
+ if (!String.IsNullOrEmpty(order.ShippingAddress.LastName))
+ paraPdf.Add(new Text(order.ShippingAddress.LastName)).Add("\n");
+ if (_addressSettings.PhoneEnabled && !String.IsNullOrEmpty(order.ShippingAddress.PhoneNumber))
+ paraPdf.Add(new Text(String.Format(_localizationService.GetResource("PDFInvoice.Phone", lang.Id), order.ShippingAddress.PhoneNumber))).Add("\n");
+ if (_addressSettings.FaxEnabled && !String.IsNullOrEmpty(order.ShippingAddress.FaxNumber))
+ paraPdf.Add(new Text(String.Format(_localizationService.GetResource("PDFInvoice.FaxNumber", lang.Id), order.ShippingAddress.FaxNumber))).Add("\n");
+ if (_addressSettings.StreetAddressEnabled && !String.IsNullOrEmpty(order.ShippingAddress.Address1))
+ paraPdf.Add(new Text(order.ShippingAddress.Address1)).Add("\n");
+ if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.ShippingAddress.Address2))
+ paraPdf.Add(new Text(order.ShippingAddress.Address2)).Add("\n");
+
+ if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
+ paraPdf.Add(new Text(String.Format("{2} {0}, {1}", order.ShippingAddress.City, order.ShippingAddress.StateProvince != null ? order.ShippingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id) : "", order.ShippingAddress.ZipPostalCode))).Add("\n");
+ if (_addressSettings.CountryEnabled && order.ShippingAddress.Country != null)
+ paraPdf.Add(new Text(String.Format("{0}", order.ShippingAddress.Country != null ? order.ShippingAddress.Country.GetLocalized(x => x.Name, lang.Id) : ""))).Add("\n");
+
+ //custom attributes
+ var customShippingAddressAttributes = _addressAttributeFormatter.FormatAttributes(order.ShippingAddress.CustomAttributes);
+ if (!String.IsNullOrEmpty(customShippingAddressAttributes))
+ {
+ //TODO: we should add padding to each line (in case if we have sevaral custom address attributes)
+ paraPdf.Add(new Text(HtmlHelper.ConvertHtmlToPlainText(customShippingAddressAttributes, true, true))).Add("\n");
+ }
+ }
+ else
+ if (order.PickupAddress != null)
+ {
+ paraPdf = new Paragraph(new Text(_localizationService.GetResource("PDFInvoice.Pickup", lang.Id)).AddStyle(style9b)).AddStyle(styleNormal).SetMultipliedLeading(1f).SetFirstLineIndent(5f);
+ paraPdf.Add("\n");
+ if (!string.IsNullOrEmpty(order.PickupAddress.Address1))
+ paraPdf.Add(new Text(String.Format(_localizationService.GetResource("PDFInvoice.Address", lang.Id), order.PickupAddress.Address1))).Add("\n");
+ if (!string.IsNullOrEmpty(order.PickupAddress.City))
+ paraPdf.Add(order.PickupAddress.City).Add("\n");
+ if (order.PickupAddress.Country != null)
+ paraPdf.Add(order.PickupAddress.Country.GetLocalized(x => x.Name, lang.Id)).Add("\n");
+ if (!string.IsNullOrEmpty(order.PickupAddress.ZipPostalCode))
+ paraPdf.Add(order.PickupAddress.ZipPostalCode).Add("\n");
+
+ }
+
+ tabPage.AddHeaderCell(new Cell(1, shippAddrCol).Add(paraPdf).AddStyle(styleCell));
+ }
+ else
+ {
+ paraPdf.Add("\n");
+ tabPage.AddHeaderCell(new Cell(1, shippAddrCol).Add(paraPdf).AddStyle(styleCell));
+ }
+
+ #endregion
+
+ #region billing
+ //billing info
+ paraPdf = new Paragraph(new Text(_localizationService.GetResource("PDFInvoice.BillingInformation", lang.Id)).AddStyle(style8b)).AddStyle(styleNormal).SetMultipliedLeading(1.2f).SetFirstLineIndent(-5f).SetMarginLeft(5f);
+ paraPdf.Add("\n");
+ //paraPdf = new Paragraph().AddStyle(styleNormal).SetMultipliedLeading(1.2f);
+ if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.BillingAddress.Company))
+ paraPdf.Add(new Text(order.BillingAddress.Company)).Add("\n");
+ if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.BillingAddress.FirstName))
+ paraPdf.Add(new Text(order.BillingAddress.FirstName)).Add(" ");
+ if (_addressSettings.CompanyEnabled && !String.IsNullOrEmpty(order.BillingAddress.LastName))
+ paraPdf.Add(new Text(order.BillingAddress.LastName)).Add("\n");
+ if (_addressSettings.PhoneEnabled && !String.IsNullOrEmpty(order.BillingAddress.PhoneNumber))
+ paraPdf.Add(new Text(String.Format(_localizationService.GetResource("PDFInvoice.Phone", lang.Id), order.BillingAddress.PhoneNumber))).Add("\n");
+ if (_addressSettings.FaxEnabled && !String.IsNullOrEmpty(order.BillingAddress.FaxNumber))
+ paraPdf.Add(new Text(String.Format(_localizationService.GetResource("PDFInvoice.FaxNumber", lang.Id), order.BillingAddress.FaxNumber))).Add("\n");
+ if (_addressSettings.StreetAddressEnabled && !String.IsNullOrEmpty(order.BillingAddress.Address1))
+ paraPdf.Add(new Text(order.BillingAddress.Address1)).Add("\n");
+ if (_addressSettings.StreetAddress2Enabled && !String.IsNullOrEmpty(order.BillingAddress.Address2))
+ paraPdf.Add(new Text(order.BillingAddress.Address2)).Add("\n");
+
+ if (_addressSettings.CityEnabled || _addressSettings.StateProvinceEnabled || _addressSettings.ZipPostalCodeEnabled)
+ paraPdf.Add(new Text(String.Format("{2} {0}, {1}", order.BillingAddress.City, order.BillingAddress.StateProvince != null ? order.BillingAddress.StateProvince.GetLocalized(x => x.Name, lang.Id) : "", order.BillingAddress.ZipPostalCode))).Add("\n");
+ if (_addressSettings.CountryEnabled && order.BillingAddress.Country != null)
+ paraPdf.Add(new Text(String.Format("{0}", order.BillingAddress.Country != null ? order.BillingAddress.Country.GetLocalized(x => x.Name, lang.Id) : ""))).Add("\n");
+
+ //VAT number
+ //if (!String.IsNullOrEmpty(order.VatNumber))
+ // paraPdf.Add(new Text(String.Format(_localizationService.GetResource("PDFInvoice.VATNumber", lang.Id), order.VatNumber))).Add("\n");
+
+ //custom attributes
+ var customBillingAddressAttributes = _addressAttributeFormatter.FormatAttributes(order.BillingAddress.CustomAttributes);
+ if (!String.IsNullOrEmpty(customBillingAddressAttributes))
+ {
+ //TODO: we should add padding to each line (in case if we have sevaral custom address attributes)
+ paraPdf.Add(new Text(HtmlHelper.ConvertHtmlToPlainText(customBillingAddressAttributes, true, true))).Add("\n");
+ }
+
+ //vendors payment details from payment provider
+ if (vendorId == 0)
+ {
+ //custom values
+ var customValues = order.DeserializeCustomValues();
+ if (customValues != null)
+ {
+ foreach (var item in customValues)
+ {
+ if (!String.IsNullOrEmpty(item.Value.ToString()))
+ paraPdf.Add(new Text(item.Key + ": " + item.Value)).Add("\n");
+ }
+ }
+ }
+
+ //add
+ tabPage.AddHeaderCell(new Cell(1, ivAddrCol).Add(paraPdf).AddStyle(styleCell));
+ #endregion
+
+ #endregion
+
+ #region invoice header
+ //empty Line
+ tabPage.AddHeaderCell(new Cell(1, col).Add("\n").AddStyle(styleCell));
+ //Invoice
+ var invoiceID = (order.InvoiceId != null && order.InvoiceId != "ToBeAssigned") ? order.InvoiceId : null;
+ paraPdf = new Paragraph(new Text(_localizationService.GetResource(invoiceID != null? "PDFInvoice.Invoice" :"PDFInvoice.Order", lang.Id)).AddStyle(styleTitle));
+ if (invoiceID != null)
+ paraPdf.Add(new Text(" " + invoiceID).AddStyle(styleTitle));
+ tabPage.AddHeaderCell(new Cell(1, col - 3).Add(paraPdf).AddStyle(styleCell));
+ paraPdf = new Paragraph();
+ if (order.InvoiceDateUtc.HasValue)
+ {
+ paraPdf.Add(new Text(_localizationService.GetResource("PDFInvoice.InvoiceDate", lang.Id)).AddStyle(style9b)).SetTextAlignment(TextAlignment.RIGHT);
+ tabPage.AddHeaderCell(new Cell(1, 2).Add(paraPdf).AddStyle(styleCell));
+ paraPdf = new Paragraph(new Text(_dateTimeHelper.ConvertToUserTime((System.DateTime)(order.InvoiceDateUtc), DateTimeKind.Utc).ToString("d", new CultureInfo(lang.LanguageCulture)) ?? "").AddStyle(style9b)).SetTextAlignment(TextAlignment.RIGHT);
+ tabPage.AddHeaderCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell));
+ }
+ else
+ tabPage.AddHeaderCell(new Cell(1, 3).Add(paraPdf).AddStyle(styleCell));
+
+ #region invoice titles
+ //compose titles
+
+ //payment method
+ var paymentMethod = _paymentService.LoadPaymentMethodBySystemName(order.PaymentMethodSystemName);
+ string paymentMethodStr = paymentMethod != null ? paymentMethod.GetLocalizedFriendlyName(_localizationService, lang.Id) : order.PaymentMethodSystemName;
+
+ //titles
+ string[,] tit = { { "PDFInvoice.Order#", order.Id.ToString() ?? ""}
+ , { "PDFInvoice.OrderDate", _dateTimeHelper.ConvertToUserTime(order.CreatedOnUtc, DateTimeKind.Utc).ToString("d", new CultureInfo(lang.LanguageCulture)) ?? ""}
+ , { "Order.ShippingMethod", order.ShippingMethod ?? ""}
+ , { "Order.PaymentMethod", paymentMethodStr ?? ""}
+ , { "Order.Payment.Status", order.PaymentStatus.GetLocalizedEnum(_localizationService, _workContext) ?? ""}
+ , { "Order.VatNumber", order.VatNumber ?? ""}
+ , { "PDFInvoice.Currency", order.CustomerCurrencyCode}
+ };
+
+ for (var i = 0; i <= tit.GetUpperBound(0); i++)
+ {
+ cellPdf = new Cell();
+ paraPdf = new Paragraph(new Text(String.Format(_localizationService.GetResource(tit[i, 0], lang.Id))).AddStyle(style8));
+ cellPdf.Add(paraPdf);
+ tabPage.AddHeaderCell(cellPdf.SetBorder(null)
+ .SetVerticalAlignment(VerticalAlignment.TOP)
+ .SetBackgroundColor(Color.LIGHT_GRAY)
+ .SetPadding(0)
+ .SetTextAlignment(TextAlignment.LEFT)
+ .SetBorderTop(new SolidBorder(1))
+ );
+ }
+
+ for (var i = 0; i <= tit.GetUpperBound(0); i++)
+ {
+ cellPdf = new Cell();
+ paraPdf = new Paragraph(new Text(tit[i, 1]).AddStyle(style9b));
+ cellPdf.Add(paraPdf);
+ tabPage.AddHeaderCell(cellPdf.SetBorder(null)
+ .SetVerticalAlignment(VerticalAlignment.BOTTOM)
+ .SetTextAlignment(TextAlignment.LEFT)
+ .SetBorderBottom(new SolidBorder(0.5f))
+ );
+ }
+
+ #endregion
+
+ #endregion
+
+ #region Products
+
+ //products
+ var orderItems = order.OrderItems;
+
+ var hasSku = _catalogSettings.ShowSkuOnProductDetailsPage;
+
+ tabPage.AddHeaderCell(new Cell(1, hasSku ? 2 : 3).Add(_localizationService.GetResource("PDFInvoice.ProductName", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetBackgroundColor(Color.LIGHT_GRAY));
+ if (_catalogSettings.ShowSkuOnProductDetailsPage)
+ {
+ tabPage.AddHeaderCell(new Cell(1, 1).Add(_localizationService.GetResource("PDFInvoice.SKU", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetBackgroundColor(Color.LIGHT_GRAY));
+ }
+ tabPage.AddHeaderCell(new Cell(1, 1).Add(_localizationService.GetResource("PDFInvoice.TaxRate", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.CENTER));
+ tabPage.AddHeaderCell(new Cell(1, 1).Add(_priceFormatter.FormatTaxString(_localizationService.GetResource("PDFInvoice.ProductPrice", lang.Id), lang, includingTax)).AddStyle(styleCell).AddStyle(style9b).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.RIGHT));
+ tabPage.AddHeaderCell(new Cell(1, 1).Add(_localizationService.GetResource("PDFInvoice.ProductQuantity", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.RIGHT));
+ tabPage.AddHeaderCell(new Cell(1, 1).Add(_priceFormatter.FormatTaxString(_localizationService.GetResource("PDFInvoice.ProductTotal", lang.Id), lang, includingTax)).AddStyle(styleCell).AddStyle(style9b).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.RIGHT));
+ int ic = 1; //init product numerator
+
+ foreach (var orderItem in orderItems)
+ {
+ var p = orderItem.Product;
+
+ //a vendor should have access only to his products
+ if (vendorId > 0 && p.VendorId != vendorId)
+ continue;
+
+ //product name
+ string name;
+ name = ic.ToString() + ") " + p.GetLocalized(x => x.Name, lang.Id);
+ paraPdf = new Paragraph(new Text(name).AddStyle(style9b)).AddStyle(styleNormal).SetMultipliedLeading(1.5f).Add("\n");
+
+ //attributes
+ if (!String.IsNullOrEmpty(orderItem.AttributeDescription))
+ {
+ paraPdf.Add(HtmlHelper.ConvertHtmlToPlainText(orderItem.AttributeDescription, true, true)).AddStyle(styleNormal).Add("\n");
+ }
+ //rental info
+ if (orderItem.Product != null && orderItem.Product.IsRental)
+ {
+ var rentalStartDate = orderItem.RentalStartDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalStartDateUtc.Value) : "";
+ var rentalEndDate = orderItem.RentalEndDateUtc.HasValue ? orderItem.Product.FormatRentalDate(orderItem.RentalEndDateUtc.Value) : "";
+ var rentalInfo = string.Format(_localizationService.GetResource("Order.Rental.FormattedDate"),
+ rentalStartDate, rentalEndDate);
+
+ paraPdf.Add(rentalInfo).AddStyle(styleNormal).Add("\n");
+ }
+
+ tabPage.AddCell(new Cell(1, hasSku ? 2 : 3).Add(paraPdf).AddStyle(styleCell).SetKeepTogether(true));
+
+ //SKU
+ if (_catalogSettings.ShowSkuOnProductDetailsPage)
+ {
+ var sku = p.FormatSku(orderItem.AttributesXml, _productAttributeParser);
+ paraPdf = new Paragraph(sku ?? String.Empty).AddStyle(styleNormal);
+ tabPage.AddCell(new Cell().Add(paraPdf).AddStyle(styleCell));
+ }
+ //taxrate
+ bool hasAttribVat = !String.IsNullOrEmpty(orderItem.AttributesXml) && orderItem.AttributesXml.Contains(" 0)
+ continue;
+
+ //product name
+ paraPdf = new Paragraph(HtmlHelper.ConvertHtmlToPlainText(attributeValue.Name, true, true)).AddStyle(styleNormal).Add("\n");
+ tabPage.AddCell(new Cell(1, hasSku ? 2 : 3).Add(paraPdf).AddStyle(styleCell));
+
+ //SKU
+ if (_catalogSettings.ShowSkuOnProductDetailsPage)
+ {
+ paraPdf = new Paragraph("").AddStyle(styleNormal);
+ tabPage.AddCell(new Cell().Add(paraPdf).AddStyle(styleCell));
+ }
+ //taxrate
+ paraPdf = new Paragraph(_priceFormatter.FormatTaxRate(taxRate)).AddStyle(styleNormal).SetTextAlignment(TextAlignment.CENTER);
+ tabPage.AddCell(new Cell().Add(paraPdf).AddStyle(styleCell));
+
+ //price
+ string unitPrice;
+ var unitPriceInclTaxInCustomerCurrency = _currencyService.ConvertCurrency(itemAmount, order.CurrencyRate);
+ unitPrice = _priceFormatter.FormatPrice(unitPriceInclTaxInCustomerCurrency, showCurrency, currency, lang, true, false);
+
+ paraPdf = new Paragraph(unitPrice).AddStyle(styleNormal).SetTextAlignment(TextAlignment.RIGHT);
+ tabPage.AddCell(new Cell().Add(paraPdf).AddStyle(styleCell));
+
+ //qty
+ paraPdf = new Paragraph("1").AddStyle(styleNormal).SetTextAlignment(TextAlignment.RIGHT);
+ tabPage.AddCell(new Cell().Add(paraPdf).AddStyle(styleCell));
+
+ //total
+ string subTotal = unitPrice;
+ paraPdf = new Paragraph(subTotal).AddStyle(styleNormal).SetTextAlignment(TextAlignment.RIGHT);
+ tabPage.AddCell(new Cell().Add(paraPdf).AddStyle(styleCell));
+
+ }
+ }
+ #endregion
+
+ //footer products, checkout table
+ paraPdf = new Paragraph(_localizationService.GetResource("PDFInvoice.Continue", lang.Id)).AddStyle(styleNormal).SetTextAlignment(TextAlignment.RIGHT);
+ tabPage.AddFooterCell(new Cell(1, col).Add(paraPdf).AddStyle(styleCell)).SetSkipLastFooter(true);
+
+ tabPage.AddCell(new Cell(1, col).Add(" ").AddStyle(styleCell).SetBorderTop(new SolidBorder(0.1f)));
+
+ #region Order notes in cell not used atm. They are printed on last page
+ //var notesCell = 4;
+ //int totNotes = 0;
+ //if (pdfSettingsByStore.RenderOrderNotes)
+ //{
+ // var orderNotes = order.OrderNotes
+ // .Where(on => on.DisplayToCustomer)
+ // .OrderByDescending(on => on.CreatedOnUtc)
+ // .ToList();
+
+ // if (orderNotes.Any())
+ // {
+ // totNotes = orderNotes.Count();
+ // int notesCol = 4;
+ // var tabNotes = new Table(notesCol).SetBorder(Border.NO_BORDER).SetWidthPercent(100f);
+ // paraPdf = new Paragraph(new Text(_localizationService.GetResource("PDFInvoice.OrderNotes", lang.Id)).AddStyle(styleTitle)).AddStyle(styleNormal).SetMultipliedLeading(1.5f);
+ // tabNotes.AddHeaderCell(new Cell(1, notesCol).Add(paraPdf).AddStyle(styleCell));
+
+ // //created on
+ // tabNotes.AddHeaderCell(new Cell(1, 1).Add(_localizationService.GetResource("PDFInvoice.OrderNotes.CreatedOn", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetTextAlignment(TextAlignment.LEFT));
+
+ // //note
+ // tabNotes.AddHeaderCell(new Cell(1, notesCol - 1).Add(_localizationService.GetResource("PDFInvoice.OrderNotes.Note", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetTextAlignment(TextAlignment.LEFT));
+ // var orderNote = orderNotes.FirstOrDefault();
+
+ // paraPdf = new Paragraph(_dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc).ToString("g", new CultureInfo(lang.LanguageCulture))).AddStyle(styleNormal).SetTextAlignment(TextAlignment.LEFT);
+ // tabNotes.AddCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell).SetKeepTogether(true));
+ // var strNotes = TakeCountLines(HtmlHelper.ConvertHtmlToPlainText(orderNote.FormatOrderNoteText(), true, true), 20);
+
+ // paraPdf = new Paragraph(strNotes).AddStyle(styleNormal).SetTextAlignment(TextAlignment.LEFT);
+ // tabNotes.AddCell(new Cell(1, notesCol - 1).Add(paraPdf).AddStyle(styleCell).SetKeepTogether(true));
+
+ // //should we display a link to downloadable files here?
+ // //I think, no. Onyway, PDFs are printable documents and links (files) are useful here
+
+ // tabPage.AddCell(new Cell(1, notesCell).Add(tabNotes).AddStyle(styleCell).SetPadding(0));
+ // }
+ //}
+
+ #endregion. see
+
+ #region Order totals
+ var taxRates = order.TaxRatesDictionary;
+
+ var sumCell = col; // - notesCell;
+ int subcol = col; // totNotes > 0 ? sumCell : col;
+ var tabTot = new Table(subcol).SetBorder(Border.NO_BORDER).SetWidthPercent(100f);
+
+ //vendors cannot see totals
+ if (vendorId == 0)
+ {
+ int redeemedPoints = 0;
+ decimal redeemedAmount = decimal.Zero;
+ if (order.RedeemedRewardPointsEntry != null)
+ {
+ redeemedPoints = -order.RedeemedRewardPointsEntry.Points;
+ redeemedAmount = -order.RedeemedRewardPointsEntry.UsedAmount;
+ }
+ var lstSummary = new List> //desc, amount, show when zero, doLocalize, borderTop, borderBottom
+ {
+ Tuple.Create("PDFInvoice.Sub-Total", includingTax ? order.OrderSubtotalInclTax : order.OrderSubtotalExclTax, true, true, false, false) //order subtotal
+ ,Tuple.Create("PDFInvoice.Discount", includingTax ? -order.OrderSubTotalDiscountInclTax : -order.OrderSubTotalDiscountExclTax, false, true, false, false) //discount (applied to order subtotal)
+ ,Tuple.Create("PDFInvoice.Shipping", includingTax ? order.OrderShippingInclTax : order.OrderShippingExclTax, false, order.OrderShippingInclTax == decimal.Zero && order.OrderShippingNonTaxable == decimal.Zero, order.ShippingStatus != ShippingStatus.ShippingNotRequired, false) //shipping
+ ,Tuple.Create(string.Format(_localizationService.GetResource("PDFInvoice.RewardPoints", lang.Id), redeemedPoints), redeemedAmount, false, true, false, false) //earned reward points
+ ,Tuple.Create("PDFInvoice.PaymentMethodAdditionalFee", includingTax ? order.PaymentMethodAdditionalFeeInclTax : order.PaymentMethodAdditionalFeeExclTax, false, true, false, false) //payment fee
+ ,Tuple.Create("PDFInvoice.InvoiceDiscount", -order.OrderDiscount, false, true, false, false) //discount (applied to order total)
+ };
+
+ if (!includingTax)
+ {
+ lstSummary.Add(new Tuple("PDFInvoice.OrderAmount", order.OrderAmount, true, true, false, false)); //tax base
+ lstSummary.Add(new Tuple("PDFInvoice.Tax", order.OrderTax, true, true, false, false)); //tax amount
+ }
+ //order total incl.
+ lstSummary.Add(new Tuple("PDFInvoice.OrderAmountIncl", order.OrderAmountIncl, true, true, false, false));
+
+ //shipping non taxable
+ var shippTuple = Tuple.Create("PDFInvoice.Shipping", order.OrderShippingNonTaxable, false, false, order.ShippingStatus != ShippingStatus.ShippingNotRequired, false);
+ lstSummary.Add(shippTuple);
+
+ //payment fee non taxable
+ lstSummary.Add(new Tuple("PDFInvoice.PaymentMethodAdditionalFee", order.PaymentMethodAdditionalFeeNonTaxable, false, true, false, false));
+
+ //gift cards
+ foreach (var gcuh in order.GiftCardUsageHistory)
+ {
+ lstSummary.Add(new Tuple(
+ string.Format(_localizationService.GetResource("PDFInvoice.GiftCardInfo", lang.Id), gcuh.GiftCard.GiftCardCouponCode), -gcuh.UsedValue, false, false, false, false
+ )
+ );
+ }
+
+ //purchased reward points
+ if (order.RedeemedRewardPointsEntry != null)
+ {
+ lstSummary.Add(new Tuple(
+ string.Format(_localizationService.GetResource("PDFInvoice.RewardPointsPurchased", lang.Id), -order.RedeemedRewardPointsEntry.PointsPurchased), -order.RedeemedRewardPointsEntry.UsedAmountPurchased, false, false, false, false
+ )
+ );
+ }
+
+ //order total to pay
+ lstSummary.Add(new Tuple("PDFInvoice.AmountToPay", order.OrderTotal, true, true, true, false));
+
+ foreach (var tupSummary in lstSummary)
+ {
+ var desc = tupSummary.Item1;
+ var amount = tupSummary.Item2;
+ var showZero = tupSummary.Item3;
+ var doLocalize = tupSummary.Item4;
+ var borderTop = tupSummary.Item5;
+ var borderBottom = tupSummary.Item6;
+
+ var amountInCustomerCurrency = _currencyService.ConvertCurrency(amount, order.CurrencyRate);
+ var amountInCustomerCurrencyStr = _priceFormatter.FormatPrice(amountInCustomerCurrency, showCurrency, currency, lang, includingTax, false);
+
+ if (amountInCustomerCurrency != decimal.Zero || showZero)
+ {
+ paraPdf = new Paragraph(doLocalize ? _localizationService.GetResource(desc, lang.Id) : desc).AddStyle(style9b).SetTextAlignment(TextAlignment.RIGHT);
+ cellPdf = new Cell(1, subcol - 1).Add(paraPdf).AddStyle(styleCell);
+ paraPdf = new Paragraph(amountInCustomerCurrencyStr).AddStyle(style9b).SetTextAlignment(TextAlignment.RIGHT);
+ cellPdf2 = new Cell(1, 1).Add(paraPdf).AddStyle(styleCell);
+ if (borderTop)
+ {
+ cellPdf.SetBorderTop(new SolidBorder(0.5f)); cellPdf2.SetBorderTop(new SolidBorder(0.5f));
+ }
+ if (borderBottom)
+ {
+ cellPdf.SetBorderBottom(new SolidBorder(0.5f)); cellPdf2.SetBorderBottom(new SolidBorder(0.5f));
+ }
+ tabTot.AddCell(cellPdf);
+ tabTot.AddCell(cellPdf2);
+ }
+ }
+
+
+ }
+ else
+ tabTot.AddCell("");
+
+ tabPage.AddCell(new Cell(1, col) //totNotes > 0 ? sumCell : col)
+ .Add(tabTot).AddStyle(styleCell).SetPadding(0).SetKeepTogether(true));
+ #endregion
+
+ #region Order notes
+
+ if (pdfSettingsByStore.RenderOrderNotes ) //&& totNotes > 1)
+ {
+ var orderNotes = order.OrderNotes
+ .Where(on => on.DisplayToCustomer)
+ .OrderByDescending(on => on.CreatedOnUtc)
+ .ToList();
+
+
+ if (orderNotes.Any())
+ {
+ paraPdf = new Paragraph(new Text(_localizationService.GetResource("PDFInvoice.OrderNotes", lang.Id)).AddStyle(styleTitle)).AddStyle(styleNormal).SetMultipliedLeading(1.5f);
+ tabPage.AddCell(new Cell(1, col).Add(paraPdf).AddStyle(styleCell));
+
+ //created on
+ tabPage.AddCell(new Cell(1, 1).Add(_localizationService.GetResource("PDFInvoice.OrderNotes.CreatedOn", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetTextAlignment(TextAlignment.LEFT));
+
+ //note
+ tabPage.AddCell(new Cell(1, col - 1).Add(_localizationService.GetResource("PDFInvoice.OrderNotes.Note", lang.Id)).AddStyle(styleCell).AddStyle(style9b).SetTextAlignment(TextAlignment.LEFT));
+
+ foreach (var orderNote in orderNotes)//.Skip(1))
+ {
+ paraPdf = new Paragraph(_dateTimeHelper.ConvertToUserTime(orderNote.CreatedOnUtc, DateTimeKind.Utc).ToString("g", new CultureInfo(lang.LanguageCulture))).AddStyle(styleNormal).SetTextAlignment(TextAlignment.LEFT);
+ tabPage.AddCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell));
+ var strNotes = TakeCountLines(HtmlHelper.ConvertHtmlToPlainText(orderNote.FormatOrderNoteText(), true, true), 20);
+
+ paraPdf = new Paragraph(strNotes).AddStyle(styleNormal).SetTextAlignment(TextAlignment.LEFT);
+ tabPage.AddCell(new Cell(1, col - 1).Add(paraPdf).AddStyle(styleCell));
+ }
+
+ }
+ }
+
+ #endregion
+
+ #region tax summary
+ var footerTable = new Table(col).SetBorder(Border.NO_BORDER).SetWidthPercent(100f);
+ var displayTaxRates = _taxSettings.DisplayTaxRates;
+ if (displayTaxRates)
+ {
+ var taxTable = new Table(new float[] { 20, 20, 20, 20, 20 }).SetBorder(Border.NO_BORDER).SetWidthPercent(100f).AddStyle(styleNormal).SetHorizontalAlignment(HorizontalAlignment.LEFT);
+
+ //header
+ taxTable.AddCell(new Cell().Add(_localizationService.GetResource("PDFInvoice.TaxRate", lang.Id)).AddStyle(styleCell).AddStyle(style8).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.CENTER));
+ taxTable.AddCell(new Cell().Add(_localizationService.GetResource(includingTax ? "PDFInvoice.OrderAmountIncl" : "PDFInvoice.OrderAmount", lang.Id)).AddStyle(styleCell).AddStyle(style8).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.CENTER));
+ taxTable.AddCell(new Cell().Add(_localizationService.GetResource(includingTax ? "PDFInvoice.DiscountAmountIncl" : "PDFInvoice.DiscountAmount", lang.Id)).AddStyle(styleCell).AddStyle(style8).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.CENTER));
+ taxTable.AddCell(new Cell().Add(_localizationService.GetResource("PDFInvoice.BaseAmount", lang.Id)).AddStyle(styleCell).AddStyle(style8).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.CENTER));
+ taxTable.AddCell(new Cell().Add(_localizationService.GetResource("PDFInvoice.TaxAmount", lang.Id)).AddStyle(styleCell).AddStyle(style8).SetBackgroundColor(Color.LIGHT_GRAY).SetTextAlignment(TextAlignment.CENTER));
+
+
+ foreach (var item in taxRates)
+ {
+ string taxRate = String.Format(_priceFormatter.FormatTaxRate(item.Key));
+ string Amount = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value.Amount, order.CurrencyRate), showCurrency, order.CustomerCurrencyCode, false, lang);
+ string DiscountAmount = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value.DiscountAmount, order.CurrencyRate), showCurrency, order.CustomerCurrencyCode, false, lang);
+ string BaseAmount = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value.BaseAmount, order.CurrencyRate), showCurrency, order.CustomerCurrencyCode, false, lang);
+ string TaxAmount = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value.TaxAmount, order.CurrencyRate), showCurrency, order.CustomerCurrencyCode, false, lang);
+ //string AmountIncludingVAT = _priceFormatter.FormatPrice(_currencyService.ConvertCurrency(item.Value.AmountIncludingVAT, order.CurrencyRate), true, order.CustomerCurrencyCode, false, lang);
+
+ taxTable.AddCell(new Cell().Add(taxRate).AddStyle(styleCell).AddStyle(style8b).SetTextAlignment(TextAlignment.CENTER).SetBackgroundColor(Color.WHITE));
+ taxTable.AddCell(new Cell().Add(Amount).AddStyle(styleCell).AddStyle(style8b).SetTextAlignment(TextAlignment.CENTER).SetBackgroundColor(Color.WHITE));
+ taxTable.AddCell(new Cell().Add(DiscountAmount).AddStyle(styleCell).AddStyle(style8b).SetTextAlignment(TextAlignment.CENTER).SetBackgroundColor(Color.WHITE));
+ taxTable.AddCell(new Cell().Add(BaseAmount).AddStyle(styleCell).AddStyle(style8b).SetTextAlignment(TextAlignment.CENTER).SetBackgroundColor(Color.WHITE));
+ taxTable.AddCell(new Cell().Add(TaxAmount).AddStyle(styleCell).AddStyle(style8b).SetTextAlignment(TextAlignment.CENTER).SetBackgroundColor(Color.WHITE));
+ }
+
+ footerTable.AddCell(new Cell(1, 5).Add(taxTable).AddStyle(styleCell).SetPadding(0).SetBorderTop(new SolidBorder(0.1f)).SetBorderBottom(new SolidBorder(0.1f)));
+
+ var taxAmountTable = new Table(2).SetBorder(Border.NO_BORDER).SetWidthPercent(100f).AddStyle(styleNormal).SetHorizontalAlignment(HorizontalAlignment.LEFT);
+
+ //base amount head
+ paraPdf = new Paragraph(_localizationService.GetResource("PDFInvoice.BaseAmountTotal", lang.Id)).AddStyle(style8).SetTextAlignment(TextAlignment.CENTER);
+ taxAmountTable.AddCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell).SetBackgroundColor(Color.LIGHT_GRAY));
+
+ //total pay header
+ paraPdf = new Paragraph(_localizationService.GetResource("PDFInvoice.AmountToPay", lang.Id)).AddStyle(style8).SetTextAlignment(TextAlignment.CENTER);
+ taxAmountTable.AddCell(new Cell(2, 1).Add(paraPdf).AddStyle(styleCell).SetBackgroundColor(Color.LIGHT_GRAY).SetVerticalAlignment(VerticalAlignment.TOP));
+
+ //base amount
+ var amountInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderAmount, order.CurrencyRate);
+ string amountInCustomerCurrencyStr = _priceFormatter.FormatPrice(amountInCustomerCurrency, showCurrency, currency, lang, false, false);
+ paraPdf = new Paragraph(amountInCustomerCurrencyStr).AddStyle(style9b).SetTextAlignment(TextAlignment.CENTER);
+ taxAmountTable.AddCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell).SetBackgroundColor(Color.WHITE));
+
+ //vat amount head
+ paraPdf = new Paragraph(_localizationService.GetResource("PDFInvoice.TaxAmount", lang.Id)).AddStyle(style8).SetTextAlignment(TextAlignment.CENTER);
+ taxAmountTable.AddCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell).SetBackgroundColor(Color.LIGHT_GRAY));
+
+ //total pay amount
+ amountInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTotal, order.CurrencyRate);
+ amountInCustomerCurrencyStr = _priceFormatter.FormatPrice(amountInCustomerCurrency, showCurrency, currency, lang, false, false);
+ paraPdf = new Paragraph(amountInCustomerCurrencyStr).AddStyle(style9b).SetTextAlignment(TextAlignment.CENTER);
+ taxAmountTable.AddCell(new Cell(2, 1).Add(paraPdf).AddStyle(styleCell).SetVerticalAlignment(VerticalAlignment.MIDDLE).SetBackgroundColor(Color.WHITE));
+
+ //vat amount
+ amountInCustomerCurrency = _currencyService.ConvertCurrency(order.OrderTax, order.CurrencyRate);
+ amountInCustomerCurrencyStr = _priceFormatter.FormatPrice(amountInCustomerCurrency, showCurrency, currency, lang, false, false);
+ paraPdf = new Paragraph(amountInCustomerCurrencyStr).AddStyle(style9b).SetTextAlignment(TextAlignment.CENTER);
+ taxAmountTable.AddCell(new Cell(1, 1).Add(paraPdf).AddStyle(styleCell).SetBackgroundColor(Color.WHITE));
+
+ footerTable.AddCell(new Cell(1, 2).Add(taxAmountTable).AddStyle(styleCell).SetPadding(0).SetBorderTop(new SolidBorder(0.1f)).SetBorderBottom(new SolidBorder(0.1f)).SetKeepTogether(true));
+
+ //place footer in tabPage cell and position it using setfixedpos
+ tabPage.AddCell(new Cell(1, col).Add(footerTable).AddStyle(styleCell).SetPadding(0).SetKeepTogether(true).SetFixedPosition(footerX, footerY, footerWidht));
+
+ }
+ #endregion
+
+
+ doc.Add(tabPage);
+
+ //tmp margin marker
+ //var pdfcan = new PdfCanvas(pdfDoc.GetLastPage());
+ //pdfcan.MoveTo(0, bottomMatgin).LineTo(100, bottomMatgin).Stroke();
+
+
+ //doc.Relayout();
+
+ //finalize page
+ pdfDoc.RemoveEventHandler(PdfDocumentEvent.INSERT_PAGE, pageEvent);
+ pageEvent.writeTotPageNum(pdfDoc);
+ pageEvent = null;
+ pagesSofar = pdfDoc.GetNumberOfPages();
+
+
+ ordNum++;
+
+ }
+ doc.Close();
+ }
+
+
+
+
+
+ ///
+ /// Print packaging slips to PDF
+ ///
+ /// Stream
+ /// Shipments
+ /// Language identifier; 0 to use a language used when placing an order
+ public virtual void PrintPackagingSlipsToPdf(Stream stream, IList shipments, int languageId = 0)
+ {
+ var _pdfService = new PdfService(_localizationService, _languageService, _workContext, _orderService, _paymentService, _dateTimeHelper, _priceFormatter, _currencyService, _measureService, _pictureService, _productService, _productAttributeParser, _storeService, _storeContext, _settingContext, _addressAttributeFormatter, _catalogSettings, _currencySettings, _measureSettings, _pdfSettings, _taxSettings, _addressSettings);
+ _pdfService.PrintPackagingSlipsToPdf(stream, shipments, languageId);
+
+ }
+
+ ///