public async Task <ActionResult <IEnumerable <OrderChargeResponse> > > ReverseCalculate(OrderRequest requestDto) { var currency = new Currency(requestDto.Currency); var orderItems = requestDto.OrderItems.Select(oi => new OrderItem( quantity: new Quantity(oi.Quantity), weight: Weight.InKilograms(oi.Weight), vatRate: new Rate(oi.VatRate), dutyRate: new Rate(oi.DutyRate), inputPrice: oi.Price)) .ToList(); var order = new Order( country: new Country(requestDto.DeclarationCountry), currency: new Currency(requestDto.Currency), orderItems: orderItems, deliveryPrice: requestDto.DeliveryPrice); var config = _options.Single(x => x.Id == requestDto.DeclarationCountry); var baseCharges = config.DeminimisBaseCharges.Select(chargeName => new ChargeName(chargeName)); var chargeConfigurations = config.ChargeConfigurations.Select(config => ChargeConfigurationFactory.CreateFromOptions(config)); var calculatorConfiguration = new CalculatorConfiguration(chargeConfigurations, baseCharges); var request = new ReverseCalculatorRequest(order, calculatorConfiguration); var response = await _mediator.Send(request); return(new OkObjectResult(response)); }
// 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 GetCalculationRange_MultipleRanges_ShouldReturnCorrectRegions(string price, string expectedThreshold) { // Arrange var deminimisBaseCharges = new List <ChargeName> { "Item" }; var chargeConfigurations = new List <ChargeConfiguration> { new FixedRateChargeConfiguration("Duty", "EUR0", "EUR10"), new FixedRateChargeConfiguration("Duty", "EUR100", "EUR20"), new FixedRateChargeConfiguration("Duty", "EUR200", "EUR30") }; var calculationRanges = new CalculatorConfiguration(chargeConfigurations, deminimisBaseCharges); // Act var range = calculationRanges.GetRangeForBasePrice(new Price(price)); // Assert range.DeminimisThreshold.Should().Be(new Price(expectedThreshold)); }
public void FromChargeConfigurations_RegionChargeOverride_ShouldCorrectlyCreateRegions() { // Arrange var deminimisBaseCharges = new List <ChargeName> { "Item" }; var chargeConfigurations = new List <ChargeConfiguration> { new FixedRateChargeConfiguration("Duty", "EUR100", "EUR10"), new FixedRateChargeConfiguration("Duty", "EUR200", "EUR20"), new FixedRateChargeConfiguration("Duty", "EUR300", "EUR30") }; // Act var calculationRanges = new CalculatorConfiguration(chargeConfigurations, deminimisBaseCharges).CalculationRanges.ToList(); // Assert calculationRanges[0].ChargeConfigurations.Single().As <FixedRateChargeConfiguration>().FixedChargeAmount.ToString().Should().Be("EUR10"); calculationRanges[1].ChargeConfigurations.Single().As <FixedRateChargeConfiguration>().FixedChargeAmount.ToString().Should().Be("EUR20"); calculationRanges[2].ChargeConfigurations.Single().As <FixedRateChargeConfiguration>().FixedChargeAmount.ToString().Should().Be("EUR30"); }
public void FromChargeConfigurations_ComplexBaseCharges_ShouldCorrectlyOrderConfigurations() { // Arrange var deminimisBaseCharges = new List <ChargeName> { "Item" }; var chargeConfigurations = new List <ChargeConfiguration> { new RateBasedChargeConfiguration("FinalInterest", "EUR0", new List <ChargeName> { "Item", "Duty", "Vat", "Fee", "FixedDuty", "FixedInterest", "AnotherInterest" }), new RateBasedChargeConfiguration("Fee", "EUR0", new List <ChargeName> { "Item", "Duty", "Vat" }), new FixedRateChargeConfiguration("FixedInterest", "EUR0", "EUR100"), new RateBasedChargeConfiguration("Vat", "EUR0", new List <ChargeName> { "Item", "Duty" }), new RateBasedChargeConfiguration("AnotherInterest", "EUR0", new List <ChargeName> { "Item", "Fee" }), new FixedRateChargeConfiguration("FixedDuty", "EUR0", "EUR100"), new RateBasedChargeConfiguration("Duty", "EUR0", new List <ChargeName> { "Item" }) }; // Act var configuration = new CalculatorConfiguration(chargeConfigurations, deminimisBaseCharges); var calculationRange = configuration.CalculationRanges.Single().ChargeConfigurations.ToList(); // Assert calculationRange[0].ChargeName.Value.Should().Be("FixedDuty"); calculationRange[1].ChargeName.Value.Should().Be("FixedInterest"); calculationRange[2].ChargeName.Value.Should().Be("Duty"); calculationRange[3].ChargeName.Value.Should().Be("Vat"); calculationRange[4].ChargeName.Value.Should().Be("Fee"); calculationRange[5].ChargeName.Value.Should().Be("AnotherInterest"); calculationRange[6].ChargeName.Value.Should().Be("FinalInterest"); }
public void Create_MultipleThreshold_ShouldCorrectlyCreateRanges() { // Arrange var deminimisBaseCharges = new List <ChargeName> { "Item" }; var chargeConfigurations = new List <ChargeConfiguration> { new RateBasedChargeConfiguration("Duty", "EUR0", new List <ChargeName> { "Item" }), new RateBasedChargeConfiguration("Vat", "EUR100", new List <ChargeName> { "Item", "Duty" }), new RateBasedChargeConfiguration("Fee", "EUR200", new List <ChargeName> { "Item", "Duty", "Vat" }), new FixedRateChargeConfiguration("FixedFee", "EUR300", "EUR100") }; // Act var calculationRanges = new CalculatorConfiguration(chargeConfigurations, deminimisBaseCharges) .CalculationRanges .ToList(); // Assert calculationRanges.Should().HaveCount(4); calculationRanges[0].DeminimisThreshold.ToString().Should().Be("EUR0"); calculationRanges[0].ChargeConfigurations.Should().HaveCount(1); calculationRanges[1].DeminimisThreshold.ToString().Should().Be("EUR100"); calculationRanges[1].ChargeConfigurations.Should().HaveCount(2); calculationRanges[2].DeminimisThreshold.ToString().Should().Be("EUR200"); calculationRanges[2].ChargeConfigurations.Should().HaveCount(3); calculationRanges[3].DeminimisThreshold.ToString().Should().Be("EUR300"); calculationRanges[3].ChargeConfigurations.Should().HaveCount(4); }
public void FromChargeConfigurations_ConfigurationsWithBaseCharges_ShouldSetCorrectBaseCharges() { // Arrange var deminimisBaseCharges = new List <ChargeName> { "Item" }; var chargeConfigurations = new List <ChargeConfiguration> { new RateBasedChargeConfiguration("Duty", "EUR0", new List <ChargeName> { "Item" }), new RateBasedChargeConfiguration("Vat", "EUR0", new List <ChargeName> { "Item", "Duty" }), new RateBasedChargeConfiguration("Fee", "EUR0", new List <ChargeName> { "Item", "Duty", "Vat" }) }; // Act var configurations = new CalculatorConfiguration(chargeConfigurations, deminimisBaseCharges) .CalculationRanges .Single() .ChargeConfigurations; // Assert configurations.Single(x => x.ChargeName.Value == "Duty") .As <RateBasedChargeConfiguration>().BaseChargeConfigurations.Should() .BeEmpty(); configurations.Single(x => x.ChargeName.Value == "Vat") .As <RateBasedChargeConfiguration>().BaseChargeConfigurations.Should() .OnlyContain(x => x.ChargeName.Value == "Duty"); configurations.Single(x => x.ChargeName.Value == "Fee") .As <RateBasedChargeConfiguration>().BaseChargeConfigurations.Should() .ContainSingle(x => x.ChargeName.Value == "Duty").And .ContainSingle(x => x.ChargeName.Value == "Vat"); }
public ReverseCalculatorRequest(Order order, CalculatorConfiguration calculatorConfiguration) { Order = order; CalculatorConfiguration = calculatorConfiguration; }
public ForwardCalculatorRequest(Order order, CalculatorConfiguration calculatorConfiguration) { Order = order; CalculatorConfiguration = calculatorConfiguration; }
public ICalculator Configure(CalculatorConfiguration config) { configuration = config; return(this); }