示例#1
0
        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;
 }
示例#9
0
 public ForwardCalculatorRequest(Order order, CalculatorConfiguration calculatorConfiguration)
 {
     Order = order;
     CalculatorConfiguration = calculatorConfiguration;
 }
示例#10
0
 public ICalculator Configure(CalculatorConfiguration config)
 {
     configuration = config;
     return(this);
 }