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); }
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); }