Skip to content

Commit

Permalink
Merge branch 'dev-totals-calc' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
tatarincev committed Feb 15, 2018
2 parents bbbd827 + fc99177 commit 4ff2121
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 580 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public class CustomerOrderServiceImpl : ServiceBase, ICustomerOrderService, ICus

public CustomerOrderServiceImpl(Func<IOrderRepository> orderRepositoryFactory, IUniqueNumberGenerator uniqueNumberGenerator, IEventPublisher<OrderChangeEvent> orderChangingPublisher,
IDynamicPropertyService dynamicPropertyService, IShippingMethodsService shippingMethodsService, IPaymentMethodsService paymentMethodsService,
IStoreService storeService, IChangeLogService changeLogService, IEventPublisher<OrderChangedEvent> orderChangedPublisher)
IStoreService storeService, IChangeLogService changeLogService, IEventPublisher<OrderChangedEvent> orderChangedPublisher, ICustomerOrderTotalsCalculator totalsCalculator)
{
RepositoryFactory = orderRepositoryFactory;
UniqueNumberGenerator = uniqueNumberGenerator;
Expand All @@ -37,6 +37,7 @@ public CustomerOrderServiceImpl(Func<IOrderRepository> orderRepositoryFactory, I
PaymentMethodsService = paymentMethodsService;
StoreService = storeService;
ChangeLogService = changeLogService;
TotalsCalculator = totalsCalculator;
}

protected IUniqueNumberGenerator UniqueNumberGenerator { get; }
Expand All @@ -48,6 +49,7 @@ public CustomerOrderServiceImpl(Func<IOrderRepository> orderRepositoryFactory, I
protected IPaymentMethodsService PaymentMethodsService { get; }
protected IShippingMethodsService ShippingMethodsService { get; }
protected IChangeLogService ChangeLogService { get; }
protected ICustomerOrderTotalsCalculator TotalsCalculator;

#region ICustomerOrderService Members

Expand All @@ -67,9 +69,15 @@ public virtual void SaveChanges(CustomerOrder[] orders)
var originalEntity = dataExistOrders.FirstOrDefault(x => x.Id == order.Id);
var originalOrder = originalEntity != null ? (CustomerOrder)originalEntity.ToModel(AbstractTypeFactory<CustomerOrder>.TryCreateInstance()) : order;

//Calculate order totals before event raising
TotalsCalculator.CalculateTotals(order);

var changingEvent = new OrderChangeEvent(originalEntity == null ? EntryState.Added : EntryState.Modified, originalOrder, order);
OrderChangingPublisher.Publish(changingEvent);
changedEvents.Add(new OrderChangedEvent(changingEvent.ChangeState, changingEvent.OrigOrder, changingEvent.ModifiedOrder));

//Calculate order totals after event raising but before save
TotalsCalculator.CalculateTotals(order);

var modifiedEntity = AbstractTypeFactory<CustomerOrderEntity>.TryCreateInstance()
.FromModel(order, pkMap) as CustomerOrderEntity;
Expand Down Expand Up @@ -135,7 +143,6 @@ public virtual CustomerOrder[] GetByIds(string[] orderIds, string responseGroup
}
}
}

retVal.Add(customerOrder);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
using System;
using System.Linq;
using VirtoCommerce.Domain.Order.Model;
using VirtoCommerce.Domain.Order.Services;
using VirtoCommerce.Platform.Core.Common;

namespace VirtoCommerce.OrderModule.Data.Services
{
/// <summary>
/// Respond for totals values calculation for Customer order and all nested objects
/// </summary>
public class DefaultCustomerOrderTotalsCalculator : ICustomerOrderTotalsCalculator
{
/// <summary>
/// Order subtotal discount
/// When a discount is applied to the cart subtotal, the tax calculation has already been applied, and is reflected in the tax subtotal.
/// Therefore, a discount applying to the cart subtotal will occur after tax.
/// For instance, if the cart subtotal is $100, and $15 is the tax subtotal, a cart - wide discount of 10 % will yield a total of $105($100 subtotal – $10 discount + $15 tax on the original $100).
/// </summary>
public virtual void CalculateTotals(CustomerOrder order)
{
if (order == null)
{
throw new ArgumentNullException(nameof(order));
}
//Calculate totals for line items
if (!order.Items.IsNullOrEmpty())
{
foreach (var item in order.Items)
{
CalculateLineItemTotals(item);
}
}
//Calculate totals for shipments
if (!order.Shipments.IsNullOrEmpty())
{
foreach (var shipment in order.Shipments)
{
CalculateShipmentTotals(shipment);
}
}
//Calculate totals for payments
if (!order.InPayments.IsNullOrEmpty())
{
foreach (var payment in order.InPayments)
{
CalculatePaymentTotals(payment);
}
}

order.DiscountTotal = 0m;
order.DiscountTotalWithTax = 0m;
order.FeeTotal = order.Fee;
order.TaxTotal = 0m;

if (!order.Items.IsNullOrEmpty())
{
order.SubTotal = order.Items.Sum(x => x.Price * x.Quantity);
order.SubTotalWithTax = order.Items.Sum(x => x.PriceWithTax * x.Quantity);
order.SubTotalTaxTotal += order.Items.Sum(x => x.TaxTotal);
order.SubTotalDiscount = order.Items.Sum(x => x.DiscountTotal);
order.SubTotalDiscountWithTax = order.Items.Sum(x => x.DiscountTotalWithTax);
order.DiscountTotal += order.Items.Sum(x => x.DiscountTotal);
order.DiscountTotalWithTax += order.Items.Sum(x => x.DiscountTotalWithTax);
order.FeeTotal += order.Items.Sum(x => x.Fee);
order.FeeTotalWithTax += order.Items.Sum(x => x.FeeWithTax);
order.TaxTotal += order.Items.Sum(x => x.TaxTotal);
}

if (!order.Shipments.IsNullOrEmpty())
{
order.ShippingTotal = order.Shipments.Sum(x => x.Total);
order.ShippingTotalWithTax = order.Shipments.Sum(x => x.TotalWithTax);
order.ShippingSubTotal = order.Shipments.Sum(x => x.Price);
order.ShippingSubTotalWithTax = order.Shipments.Sum(x => x.PriceWithTax);
order.ShippingDiscountTotal = order.Shipments.Sum(x => x.DiscountAmount);
order.ShippingDiscountTotalWithTax = order.Shipments.Sum(x => x.DiscountAmountWithTax);
order.DiscountTotal += order.Shipments.Sum(x => x.DiscountAmount);
order.DiscountTotalWithTax += order.Shipments.Sum(x => x.DiscountAmountWithTax);
order.FeeTotal += order.Shipments.Sum(x => x.Fee);
order.FeeTotalWithTax += order.Shipments.Sum(x => x.FeeWithTax);
order.TaxTotal += order.Shipments.Sum(x => x.TaxTotal);
}

if (!order.InPayments.IsNullOrEmpty())
{
order.PaymentTotal = order.InPayments.Sum(x => x.Total);
order.PaymentTotalWithTax = order.InPayments.Sum(x => x.TotalWithTax);
order.PaymentSubTotal = order.InPayments.Sum(x => x.Price);
order.PaymentSubTotalWithTax = order.InPayments.Sum(x => x.PriceWithTax);
order.PaymentDiscountTotal = order.InPayments.Sum(x => x.DiscountAmount);
order.PaymentDiscountTotalWithTax = order.InPayments.Sum(x => x.DiscountAmountWithTax);
order.DiscountTotal += order.InPayments.Sum(x => x.DiscountAmount);
order.DiscountTotalWithTax += order.InPayments.Sum(x => x.DiscountAmountWithTax);
order.TaxTotal += order.InPayments.Sum(x => x.TaxTotal);
}

var taxFactor = 1 + order.TaxPercentRate;
order.FeeWithTax = order.Fee * taxFactor;
order.FeeTotalWithTax = order.FeeTotal * taxFactor;
order.DiscountTotal += order.DiscountAmount;
order.DiscountTotalWithTax += order.DiscountAmount * taxFactor;
//Subtract from order tax total self discount tax amount
order.TaxTotal -= order.DiscountAmount * order.TaxPercentRate;
order.Total = order.SubTotal + order.ShippingSubTotal + order.TaxTotal + order.PaymentSubTotal + order.FeeTotal - order.DiscountTotal;
}

protected virtual void CalculatePaymentTotals(PaymentIn payment)
{
if (payment == null)
{
throw new ArgumentNullException(nameof(payment));
}
var taxFactor = 1 + payment.TaxPercentRate;
payment.Total = payment.Price - payment.DiscountAmount;
payment.TotalWithTax = payment.Total * taxFactor;
payment.PriceWithTax = payment.Price * taxFactor;
payment.DiscountAmountWithTax = payment.DiscountAmount * taxFactor;
payment.TaxTotal = payment.Total * payment.TaxPercentRate;
}

protected virtual void CalculateShipmentTotals(Shipment shipment)
{
if (shipment == null)
{
throw new ArgumentNullException(nameof(shipment));
}
var taxFactor = 1 + shipment.TaxPercentRate;
shipment.PriceWithTax = shipment.Price * taxFactor;
shipment.DiscountAmountWithTax = shipment.DiscountAmount * taxFactor;
shipment.FeeWithTax = shipment.Fee * taxFactor;
shipment.Total = shipment.Price + shipment.Fee - shipment.DiscountAmount;
shipment.TotalWithTax = shipment.PriceWithTax + shipment.FeeWithTax - shipment.DiscountAmountWithTax;
shipment.TaxTotal = shipment.Total * shipment.TaxPercentRate;
}

protected virtual void CalculateLineItemTotals(LineItem lineItem)
{
if (lineItem == null)
{
throw new ArgumentNullException(nameof(lineItem));
}
var taxFactor = 1 + lineItem.TaxPercentRate;
lineItem.PriceWithTax = lineItem.Price * taxFactor;
lineItem.PlacedPrice = lineItem.Price - lineItem.DiscountAmount;
lineItem.ExtendedPrice = lineItem.PlacedPrice * lineItem.Quantity;
lineItem.DiscountAmountWithTax = lineItem.DiscountAmount * taxFactor;
lineItem.DiscountTotal = lineItem.DiscountAmount * Math.Max(1, lineItem.Quantity);
lineItem.FeeWithTax = lineItem.Fee * taxFactor;
lineItem.PlacedPriceWithTax = lineItem.PlacedPrice * taxFactor;
lineItem.ExtendedPriceWithTax = lineItem.PlacedPriceWithTax * lineItem.Quantity;
lineItem.DiscountTotalWithTax = lineItem.DiscountAmountWithTax * Math.Max(1, lineItem.Quantity);
lineItem.TaxTotal = (lineItem.ExtendedPrice + lineItem.Fee) * lineItem.TaxPercentRate;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,11 @@
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
<Reference Include="VirtoCommerce.Domain, Version=2.24.11.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VirtoCommerce.Domain.2.24.11\lib\net461\VirtoCommerce.Domain.dll</HintPath>
<Reference Include="VirtoCommerce.Domain, Version=2.24.24.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VirtoCommerce.Domain.2.24.24\lib\net461\VirtoCommerce.Domain.dll</HintPath>
</Reference>
<Reference Include="VirtoCommerce.Platform.Core, Version=2.13.15.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VirtoCommerce.Platform.Core.2.13.15\lib\net461\VirtoCommerce.Platform.Core.dll</HintPath>
<Reference Include="VirtoCommerce.Platform.Core, Version=2.13.17.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VirtoCommerce.Platform.Core.2.13.17\lib\net461\VirtoCommerce.Platform.Core.dll</HintPath>
</Reference>
<Reference Include="VirtoCommerce.Platform.Data, Version=2.13.15.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\VirtoCommerce.Platform.Data.2.13.15\lib\net461\VirtoCommerce.Platform.Data.dll</HintPath>
Expand Down Expand Up @@ -191,6 +191,7 @@
<Compile Include="Services\CustomerOrderBuilderImpl.cs" />
<Compile Include="Services\CustomerOrderServiceImpl.cs" />
<Compile Include="Observers\AdjustInventoryObserver.cs" />
<Compile Include="Services\DefaultCustomerOrderTotalsCalculator.cs" />
<Compile Include="Services\ICustomerOrderBuilder.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
4 changes: 2 additions & 2 deletions VirtoCommerce.OrderModule.Data/packages.config
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<package id="EntityFramework" version="6.1.3" targetFramework="net461" />
<package id="Newtonsoft.Json" version="9.0.1" targetFramework="net461" />
<package id="valueinjecter" version="2.3.3" targetFramework="net461" />
<package id="VirtoCommerce.Domain" version="2.24.11" targetFramework="net461" />
<package id="VirtoCommerce.Platform.Core" version="2.13.15" targetFramework="net461" />
<package id="VirtoCommerce.Domain" version="2.24.24" targetFramework="net461" />
<package id="VirtoCommerce.Platform.Core" version="2.13.17" targetFramework="net461" />
<package id="VirtoCommerce.Platform.Data" version="2.13.15" targetFramework="net461" />
</packages>
2 changes: 1 addition & 1 deletion VirtoCommerce.OrderModule.Test/CRUDScenarios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ private static CustomerOrderServiceImpl GetCustomerOrderService()
var dynamicPropertyService = new Mock<IDynamicPropertyService>().Object;
var orderEventPublisher = new EventPublisher<OrderChangeEvent>(Enumerable.Empty<IObserver<OrderChangeEvent>>().ToArray());

var orderService = new CustomerOrderServiceImpl(GetOrderRepositoryFactory(), new TimeBasedNumberGeneratorImpl(), orderEventPublisher, dynamicPropertyService, GetShippingMethodsService(), GetPaymentMethodsService(), GetStoreService(), null, null);
var orderService = new CustomerOrderServiceImpl(GetOrderRepositoryFactory(), new TimeBasedNumberGeneratorImpl(), orderEventPublisher, dynamicPropertyService, GetShippingMethodsService(), GetPaymentMethodsService(), GetStoreService(), null, null, null);

return orderService;
}
Expand Down
Loading

0 comments on commit 4ff2121

Please sign in to comment.