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 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));
        }
 public GreyZone(Price from, Price to, CalculationRange calculationRange)
 {
     From             = from;
     To               = to;
     CalculationRange = calculationRange;
 }
        /// <summary>
        /// Creates the specified range.
        /// </summary>
        /// <param name="range">The range.</param>
        /// <param name="includeFixedCalculators">if set to <c>true</c> [include fixed calculators]. Should not be included when forward calculator is running
        /// as part of the reverse calculator as the fixed charge will have already been added by the reverse calculator.</param>
        /// <returns></returns>
        /// <exception cref="InvalidChargeConfigurationException">Unknown charge configuration - can't create calculator delegate.</exception>
        public static Calculator?Create(CalculationRange range, bool includeFixedCalculators = true)
        {
            Calculator?calculatorDelegate = null;

            // Loop through each charge configuration (which are in order of execution) - and add the required
            // calculator to the delegate.
            foreach (var config in range.ChargeConfigurations)
            {
                IChargeCalculator?calculator = default;

                switch (config)
                {
                // FIXED RATE CONFIGURATIONS
                case FixedRateChargeConfiguration fixedChargeConfig when config is FixedRateChargeConfiguration:
                {
                    if (includeFixedCalculators)
                    {
                        calculator = new FixedChargeCalculator(fixedChargeConfig.ChargeName, fixedChargeConfig.FixedChargeAmount);
                    }

                    break;
                }

                // WEIGHT BASED CONFIGURATIONS
                case WeightBasedChargeConfiguration weightBasedChargeConfig when config is WeightBasedChargeConfiguration && weightBasedChargeConfig.Rate != null:
                {
                    calculator = new WeightBasedChargeCalculator(weightBasedChargeConfig.ChargeName, (oi) => weightBasedChargeConfig.Rate);
                    break;
                }

                case WeightBasedChargeConfiguration weightBasedChargeConfig when config is WeightBasedChargeConfiguration && config.ChargeName.Value is ChargeNames.Duty:
                {
                    calculator = new WeightBasedChargeCalculator(weightBasedChargeConfig.ChargeName, (oi) => oi.DutyRate);
                    break;
                }

                case WeightBasedChargeConfiguration weightBasedChargeConfig when config is WeightBasedChargeConfiguration && config.ChargeName.Value is ChargeNames.Vat:
                {
                    calculator = new WeightBasedChargeCalculator(weightBasedChargeConfig.ChargeName, (oi) => oi.VatRate);
                    break;
                }

                // RATE BASED CONFIGURATIONS
                case RateBasedChargeConfiguration rateBasedChargeConfig when config is RateBasedChargeConfiguration && rateBasedChargeConfig.Rate != null:
                {
                    calculator = new RateBasedChargeCalculator(rateBasedChargeConfig.ChargeName, (oi) => rateBasedChargeConfig.Rate, rateBasedChargeConfig.BaseCharges);
                    break;
                }

                case RateBasedChargeConfiguration rateBasedChargeConfig when config is RateBasedChargeConfiguration && rateBasedChargeConfig.ChargeName.Value is ChargeNames.Duty:
                {
                    calculator = new RateBasedChargeCalculator(rateBasedChargeConfig.ChargeName, (oi) => oi.DutyRate, rateBasedChargeConfig.BaseCharges);
                    break;
                }

                case RateBasedChargeConfiguration rateBasedChargeConfig when config is RateBasedChargeConfiguration && rateBasedChargeConfig.ChargeName.Value is ChargeNames.Vat:
                {
                    calculator = new RateBasedChargeCalculator(rateBasedChargeConfig.ChargeName, (oi) => oi.VatRate, rateBasedChargeConfig.BaseCharges);
                    break;
                }

                default:
                    throw new InvalidChargeConfigurationException("Unknown charge configuration - can't create calculator delegate.");
                }

                if (calculator != null)
                {
                    // Add constraints
                    if (config.MinimumPayable != null)
                    {
                        calculator = new MinimumPayableConstraint(calculator, config.ChargeName, config.MinimumPayable);
                    }
                    if (config.MinimumCollectible != null)
                    {
                        calculator = new MinimumCollectibleConstraint(calculator, config.ChargeName, config.MinimumCollectible);
                    }

                    // Add created calculator to delegate
                    calculatorDelegate += calculator.Calculate;
                }
            }

            return(calculatorDelegate);
        }
Example #5
0
        public static Calculator?Create(CalculationRange range, IEnumerable <ChargeName> deminimisBaseCharges)
        {
            Calculator?calculatorDelegate = null;

            foreach (var config in range.ChargeConfigurations)
            {
                IChargeCalculator calculator;

                switch (config)
                {
                // FIXED RATE CONFIGURATIONS
                case FixedRateChargeConfiguration fixedChargeConfig when config is FixedRateChargeConfiguration:
                {
                    calculator = new FixedChargeCalculator(fixedChargeConfig.ChargeName, fixedChargeConfig.FixedChargeAmount);
                    break;
                }

                // WEIGHT BASED CONFIGURATIONS
                case WeightBasedChargeConfiguration weightBasedChargeConfig when config is WeightBasedChargeConfiguration && weightBasedChargeConfig.Rate != null:
                {
                    calculator = new WeightBasedChargeCalculator(weightBasedChargeConfig.ChargeName, (oi) => weightBasedChargeConfig.Rate);
                    break;
                }

                case WeightBasedChargeConfiguration weightBasedChargeConfig when config is WeightBasedChargeConfiguration && config.ChargeName.Value is ChargeNames.Duty:
                {
                    calculator = new WeightBasedChargeCalculator(weightBasedChargeConfig.ChargeName, (oi) => oi.DutyRate);
                    break;
                }

                case WeightBasedChargeConfiguration weightBasedChargeConfig when config is WeightBasedChargeConfiguration && config.ChargeName.Value is ChargeNames.Vat:
                {
                    calculator = new WeightBasedChargeCalculator(weightBasedChargeConfig.ChargeName, (oi) => oi.VatRate);
                    break;
                }

                // RATE BASED CONFIGURATIONS
                case RateBasedChargeConfiguration rateBasedChargeConfig when config is RateBasedChargeConfiguration && rateBasedChargeConfig.Rate != null:
                {
                    calculator = new ReverseRateCalculator(rateBasedChargeConfig.ChargeName, (oi) => rateBasedChargeConfig.Rate, rateBasedChargeConfig.BaseCharges);
                    break;
                }

                case RateBasedChargeConfiguration rateBasedChargeConfig when config is RateBasedChargeConfiguration && rateBasedChargeConfig.ChargeName.Value is ChargeNames.Duty:
                {
                    calculator = new ReverseRateCalculator(rateBasedChargeConfig.ChargeName, (oi) => oi.DutyRate, rateBasedChargeConfig.BaseCharges);
                    break;
                }

                case RateBasedChargeConfiguration rateBasedChargeConfig when config is RateBasedChargeConfiguration && rateBasedChargeConfig.ChargeName.Value is ChargeNames.Vat:
                {
                    calculator = new ReverseRateCalculator(rateBasedChargeConfig.ChargeName, (oi) => oi.VatRate, rateBasedChargeConfig.BaseCharges);
                    break;
                }

                default:
                    throw new InvalidChargeConfigurationException("Unknown charge configuration - can't create calculator delegate.");
                }

                // Add created calculator to delegate
                calculatorDelegate += calculator.Calculate;
            }

            foreach (var deminimisBaseCharge in deminimisBaseCharges)
            {
                // Add the item price calculator last
                calculatorDelegate += new ReversePriceCalculator(deminimisBaseCharge, $"Input{deminimisBaseCharge}").Calculate;
            }

            return(calculatorDelegate);
        }