private void Calculate( CalculationRange range, Order order, bool deliveryInDeminimisBase, IEnumerable <ChargeName> deminimisBaseCharges) { // If no delivery reversal is required, add to charges if (!deliveryInDeminimisBase) { var inputDeliveryPrice = order.GetChargeAmount(ChargeNames.InputDelivery, order.Currency); order.AddCharge(new OrderCharge(ChargeNames.Delivery, inputDeliveryPrice, ChargeNames.Delivery)); } // Create reverse calc using range var reverseCalculator = ReverseCalculatorFactory.Create(range, deminimisBaseCharges); // Run reverse calc reverseCalculator?.Invoke(order); // Create forward calc var forwardCalculator = ForwardCalculatorFactory.Create(range, includeFixedCalculators: false); // Run forward calc forwardCalculator?.Invoke(order); }
public Task <OrderResponse> Handle(ForwardCalculatorRequest request, CancellationToken cancellationToken) { // Add initial charges foreach (var item in request.Order.OrderItems) { var inputItemPrice = item.GetChargeAmount(ChargeNames.InputItem, request.Order.Currency); var inputDeliveryPrice = item.GetChargeAmount(ChargeNames.InputDelivery, request.Order.Currency); item.AddCharge(new OrderCharge(ChargeNames.Item, inputItemPrice, ChargeNames.Item)); item.AddCharge(new OrderCharge(ChargeNames.Delivery, inputDeliveryPrice, ChargeNames.Delivery)); } ; // Determine deminimis base var deminimisBase = request.Order.Charges.Where(chargeName => request.CalculatorConfiguration.DeminimisBaseCharges.Contains(chargeName.ChargeName)) .Select(x => x.ChargeAmount.Value).Sum(); // Get the correct range for the base price var range = request.CalculatorConfiguration.GetRangeForBasePrice(new Price(request.Order.Currency, deminimisBase)); // Create a forward calculator for the selected range var calculator = ForwardCalculatorFactory.Create(range); // Run calculator calculator?.Invoke(request.Order); foreach (var item in request.Order.OrderItems) { var totalItemPrice = item.Charges .Where(x => !x.InputCharge) .Where(x => x.ChargeName.Value.EndsWith("Item")) .Select(x => x.ChargeAmount.Value) .Sum(); var totalDeliveryPrice = item.Charges .Where(x => !x.InputCharge) .Where(x => x.ChargeName.Value.EndsWith("Delivery")) .Select(x => x.ChargeAmount.Value) .Sum(); item.AddCharge(new OrderCharge("TotalItem", new Price(request.Order.Currency, totalItemPrice), "TotalItem")); item.AddCharge(new OrderCharge("TotalDelivery", new Price(request.Order.Currency, totalDeliveryPrice), "TotalDelivery")); } return(Task.FromResult(new OrderResponse(request.Order))); }
// TODO: Create abstraction private void DetectGreyZones(CalculatorConfiguration configuration, Order order) { var boundaries = new List <Tuple <Price, CalculationRange> >(); foreach (var range in configuration.CalculationRanges.Where(x => x.DeminimisThreshold.Value != 0)) { boundaries.Add(new Tuple <Price, CalculationRange>(new Price(order.Currency, range.DeminimisThreshold.Value - .01m), range)); boundaries.Add(new Tuple <Price, CalculationRange>(new Price(order.Currency, range.DeminimisThreshold.Value), range)); } var greyZonePrices = new List <Tuple <Price, CalculationRange> >(); foreach (var boundary in boundaries) { // Create forward calc var range = configuration.GetRangeForBasePrice(boundary.Item1); var forwardCalculator = ForwardCalculatorFactory.Create(range); var orderItems = order.OrderItems.Select(oi => new OrderItem(oi.Quantity, oi.Weight, oi.VatRate, oi.DutyRate, new Price(order.Currency, order.RelativeOrderItemValue(oi) * boundary.Item1.Value))).ToList(); orderItems.ForEach(oi => { var c = oi.GetChargeAmount(ChargeNames.InputItem, order.Currency); oi.AddCharge(new OrderCharge(ChargeNames.Item, c, ChargeNames.Item)); }); var detectionOrder = new Order(order.Country, order.Currency, orderItems, new Price(order.Currency, 0)); forwardCalculator?.Invoke(detectionOrder); var greyZonePrice = detectionOrder.Charges .Where(x => !x.InputCharge) .Where(x => x.ChargeName.Value.EndsWith(ChargeNames.Item)) .Select(x => x.ChargeAmount.Value) .Sum(); greyZonePrices.Add(new Tuple <Price, CalculationRange>(new Price(order.Currency, greyZonePrice), boundary.Item2)); } if (greyZonePrices.Count % 2 != 0) { throw new ArgumentException("Invalid grey zones, should be in pairs!"); } for (int x = 0; x < greyZonePrices.Count; x++) { configuration.AddGreyZone(new GreyZone(greyZonePrices[x].Item1, greyZonePrices[x + 1].Item1, greyZonePrices[x].Item2)); x++; } }
public void Create_MultipleCalculators_ShouldCorrectlyCreateCalculator() { // Arrange var chargeConfiguration1 = ChargeConfigurationFactory.CreateFromOptions(new ChargeConfigurationOptions { CalculationType = CalculationType.Fixed, DeminimisThreshold = "EUR0", FixedChargeAmount = "EUR10", ChargeName = ChargeNames.Duty, Rate = 5 }); var chargeConfiguration2 = ChargeConfigurationFactory.CreateFromOptions(new ChargeConfigurationOptions { CalculationType = CalculationType.RateBased, DeminimisThreshold = "EUR0", BaseCharges = { "Item", ChargeNames.Duty }, ChargeName = ChargeNames.Vat, Rate = 5 }); var chargeConfiguration3 = ChargeConfigurationFactory.CreateFromOptions(new ChargeConfigurationOptions { CalculationType = CalculationType.WeightBased, DeminimisThreshold = "EUR0", ChargeName = "Fee", Rate = 5 }); var calculationRange = new CalculationRange(new Price("EUR100"), new List <ChargeConfiguration> { chargeConfiguration1, chargeConfiguration2, chargeConfiguration3 }); // Act var calculator = ForwardCalculatorFactory.Create(calculationRange).GetInvocationList(); // Assert calculator.Should().HaveCount(3); calculator[0].Method.DeclaringType.Should().Be(typeof(FixedChargeCalculator)); calculator[1].Method.DeclaringType.Should().Be(typeof(RateBasedChargeCalculator)); calculator[2].Method.DeclaringType.Should().Be(typeof(WeightBasedChargeCalculator)); }