|
namespace EPiServer.Reference.Commerce.Site.Features.Shared.Services |
|
{ |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
|
|
using EPiServer.Commerce.Catalog.ContentTypes; |
|
using EPiServer.Commerce.Order; |
|
using EPiServer.Commerce.Order.Calculator; |
|
using EPiServer.Commerce.Order.Internal; |
|
|
|
using Mediachase.Commerce; |
|
using Mediachase.Commerce.Catalog; |
|
using Mediachase.Commerce.Orders; |
|
|
|
/// <summary> |
|
/// Class CustomTaxCalculator. |
|
/// </summary> |
|
/// <seealso cref="EPiServer.Commerce.Order.Calculator.DefaultTaxCalculator" /> |
|
public class CustomTaxCalculator : DefaultTaxCalculator |
|
{ |
|
/// <summary> |
|
/// The content repository |
|
/// </summary> |
|
private readonly IContentRepository contentRepository; |
|
|
|
/// <summary> |
|
/// The line item calculator |
|
/// </summary> |
|
private readonly ILineItemCalculator lineItemCalculator; |
|
|
|
/// <summary> |
|
/// The reference converter |
|
/// </summary> |
|
private readonly ReferenceConverter referenceConverter; |
|
|
|
/// <summary> |
|
/// The shipping calculator |
|
/// </summary> |
|
private readonly IShippingCalculator shippingCalculator; |
|
|
|
/// <summary> |
|
/// Initializes a new instance of the <see cref="CustomTaxCalculator"/> class. |
|
/// </summary> |
|
/// <param name="contentRepository">The content Repository.</param> |
|
/// <param name="referenceConverter">The reference converter.</param> |
|
/// <param name="shippingCalculator">The shipping calculator.</param> |
|
/// <param name="lineItemCalculator">The line item calculator.</param> |
|
public CustomTaxCalculator( |
|
IContentRepository contentRepository, |
|
ReferenceConverter referenceConverter, |
|
IShippingCalculator shippingCalculator, |
|
ILineItemCalculator lineItemCalculator) |
|
: base(contentRepository, referenceConverter, shippingCalculator, lineItemCalculator) |
|
{ |
|
this.contentRepository = contentRepository; |
|
this.referenceConverter = referenceConverter; |
|
this.shippingCalculator = shippingCalculator; |
|
this.lineItemCalculator = lineItemCalculator; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the tax total for the order form. |
|
/// </summary> |
|
/// <param name="orderForm">The order form.</param> |
|
/// <param name="market">The market to be used in the calculation.</param> |
|
/// <param name="currency">The currency to be used in the calculation.</param> |
|
/// <returns>The total tax amount for the order form.</returns> |
|
/// <example> |
|
/// <code source="../CodeSamples/EPiServer.Commerce/Orders/OrderCalculatorsSample.cs" region="GetTaxTotalForOrderForm" lang="cs" /> |
|
/// </example> |
|
protected override Money CalculateTaxTotal(IOrderForm orderForm, IMarket market, Currency currency) |
|
{ |
|
decimal amount1 = new decimal(); |
|
foreach (IShipment shipment in orderForm.Shipments.Where(x => x.ShippingAddress != null)) |
|
{ |
|
decimal amount2 = new decimal(); |
|
decimal amount3 = this.shippingCalculator.GetDiscountedShippingAmount(shipment, market, currency).Amount; |
|
decimal amount4 = this.shippingCalculator.GetShippingItemsTotal(shipment, currency).Amount; |
|
foreach (ILineItem lineItem in shipment.LineItems) |
|
{ |
|
int taxCategoryId; |
|
ITaxValue[] taxValues; |
|
|
|
if (this.TryGetTaxCategoryId(lineItem, out taxCategoryId) |
|
&& this.TryGetTaxValues(market, shipment, taxCategoryId, out taxValues)) |
|
{ |
|
decimal num2 = string.IsNullOrEmpty(orderForm.Name) |
|
|| !orderForm.Name.Equals(OrderForm.ReturnName) |
|
? lineItem.Quantity |
|
: lineItem.ReturnQuantity; |
|
decimal num3 = GetPriceExcludingTax(lineItem) * num2; |
|
decimal totalQuantity = shipment.LineItems.Sum(l => l.Quantity); |
|
|
|
amount2 += CalculateShippingTax( |
|
taxValues, |
|
amount4, |
|
amount3, |
|
num3, |
|
lineItem.Quantity, |
|
totalQuantity); |
|
|
|
amount1 += currency.Round(GetTaxes(taxValues, TaxType.SalesTax, num3)); |
|
} |
|
} |
|
|
|
amount1 += currency.Round(amount2); |
|
} |
|
|
|
return new Money(currency.Round(amount1), currency); |
|
} |
|
|
|
/// <summary> |
|
/// Calculates the shipping tax. |
|
/// </summary> |
|
/// <param name="taxes">The taxes.</param> |
|
/// <param name="shippingTotal">The shipping total.</param> |
|
/// <param name="shippingCost">The shipping cost.</param> |
|
/// <param name="itemExtendedPrice">The item extended price.</param> |
|
/// <param name="itemQuantity">The item quantity.</param> |
|
/// <param name="totalQuantity">The total quantity.</param> |
|
/// <returns>The shipping tax.</returns> |
|
private static decimal CalculateShippingTax( |
|
ITaxValue[] taxes, |
|
decimal shippingTotal, |
|
decimal shippingCost, |
|
decimal itemExtendedPrice, |
|
decimal itemQuantity, |
|
decimal totalQuantity) |
|
{ |
|
decimal basePrice = shippingTotal == decimal.Zero |
|
? itemQuantity / totalQuantity * shippingCost |
|
: itemExtendedPrice / shippingTotal * shippingCost; |
|
return GetTaxes(taxes, TaxType.ShippingTax, basePrice); |
|
} |
|
|
|
/// <summary> |
|
/// Gets the price excluding tax. |
|
/// </summary> |
|
/// <param name="item">The item.</param> |
|
/// <returns>The prices excluding tax.</returns> |
|
private static decimal GetPriceExcludingTax(ILineItem item) |
|
{ |
|
return item.PlacedPrice - (item.TryGetDiscountValue(x => x.OrderAmount) + item.TryGetDiscountValue(x => x.EntryAmount)) / item.Quantity; |
|
} |
|
|
|
/// <summary> |
|
/// Gets the taxes. |
|
/// </summary> |
|
/// <param name="taxes">The taxes.</param> |
|
/// <param name="taxtype">The taxtype.</param> |
|
/// <param name="basePrice">The base price.</param> |
|
/// <returns>System.Decimal.</returns> |
|
private static decimal GetTaxes(IEnumerable<ITaxValue> taxes, TaxType taxtype, decimal basePrice) |
|
{ |
|
return |
|
taxes.Where(x => x.TaxType == taxtype) |
|
.Sum(x => basePrice * (decimal)x.Percentage * new decimal(1, 0, 0, false, 2)); |
|
} |
|
|
|
/// <summary> |
|
/// Try to get the tax category identifier. |
|
/// </summary> |
|
/// <param name="item">The item.</param> |
|
/// <param name="taxCategoryId">The tax category identifier.</param> |
|
/// <returns><c>true</c> if the identifier was retrieved, <c>false</c> otherwise.</returns> |
|
private bool TryGetTaxCategoryId(ILineItem item, out int taxCategoryId) |
|
{ |
|
IPricing pricing = |
|
this.contentRepository.Get<EntryContentBase>(this.referenceConverter.GetContentLink(item.Code)) as |
|
IPricing; |
|
if (pricing == null || !pricing.TaxCategoryId.HasValue) |
|
{ |
|
taxCategoryId = 0; |
|
return false; |
|
} |
|
|
|
taxCategoryId = pricing.TaxCategoryId.Value; |
|
return true; |
|
} |
|
|
|
/// <summary> |
|
/// Try to get the tax values. |
|
/// </summary> |
|
/// <param name="market">The market.</param> |
|
/// <param name="shipment">The shipment.</param> |
|
/// <param name="taxCategoryId">The tax category identifier.</param> |
|
/// <param name="taxValues">The tax values.</param> |
|
/// <returns><c>true</c> if tax values coould be retrieved, <c>false</c> otherwise.</returns> |
|
private bool TryGetTaxValues(IMarket market, IShipment shipment, int taxCategoryId, out ITaxValue[] taxValues) |
|
{ |
|
string categoryNameById = this.GetTaxCategoryNameById(taxCategoryId); |
|
taxValues = |
|
this.GetTaxValues(categoryNameById, market.DefaultLanguage.Name, shipment.ShippingAddress).ToArray(); |
|
return taxValues.Any(); |
|
} |
|
} |
|
} |