public static RoomContractSet AlignPrices(RoomContractSet roomContractSet) { var totalFinalPrice = MoneyRounder.Ceil(roomContractSet.Rate.FinalPrice); var roomFinalPrices = roomContractSet.Rooms .Select(r => MoneyRounder.Ceil(r.Rate.FinalPrice)) .ToList(); var(alignedFinalPrice, alignedRoomFinalPrices) = PriceAligner.AlignAggregateValues(totalFinalPrice, roomFinalPrices); var totalGrossPrice = MoneyRounder.Ceil(roomContractSet.Rate.Gross); var roomGrossPrices = roomContractSet.Rooms .Select(r => MoneyRounder.Ceil(r.Rate.Gross)) .ToList(); var(alignedGrossPrice, alignedRoomGrossRates) = PriceAligner.AlignAggregateValues(totalGrossPrice, roomGrossPrices); var roomContracts = new List <RoomContract>(roomContractSet.Rooms.Count); for (var i = 0; i < roomContractSet.Rooms.Count; i++) { var room = roomContractSet.Rooms[i]; var totalPriceNet = alignedRoomFinalPrices[i]; var totalPriceGross = alignedRoomGrossRates[i]; var totalRate = new Rate(totalPriceNet, totalPriceGross); roomContracts.Add(BuildRoomContracts(room, room.DailyRoomRates, totalRate)); } var roomContractSetRate = new Rate(alignedFinalPrice, alignedGrossPrice); return(BuildRoomContractSet(roomContractSet, roomContractSetRate, roomContracts)); }
public static decimal GetRefundableAmount(this Booking booking, DateTime forDate) { var refundableAmount = booking.Rooms.Sum(room => room.Price.Amount * (decimal)room.DeadlineDetails.GetRefundableFraction(forDate)); return(MoneyRounder.Ceil(refundableAmount, booking.Currency)); }
public static MoneyAmount Calculate(Booking booking, DateTime cancellationDate) { var penaltyAmount = booking.Rooms .Sum(room => room.Price.Amount * (decimal)GetPenaltyFraction(room.DeadlineDetails, room.IsAdvancePurchaseRate, cancellationDate)); penaltyAmount = MoneyRounder.Ceil(penaltyAmount, booking.Currency); return(new MoneyAmount(penaltyAmount, booking.Currency));
public async Task <DataWithMarkup <TDetails> > ApplyMarkups <TDetails>(AgentContext agent, TDetails details, Func <TDetails, PriceProcessFunction, ValueTask <TDetails> > priceProcessFunc) { var markup = await _markupService.Get(agent, MarkupPolicyTarget.AccommodationAvailability); var responseWithMarkup = await priceProcessFunc(details, markup.Function); var ceiledResponse = await priceProcessFunc(responseWithMarkup, price => new ValueTask <MoneyAmount>(MoneyRounder.Ceil(price))); return(DataWithMarkup.Create(ceiledResponse, markup.Policies)); }
public Money RoundToPayableAmount() { if (_currencySymbol.Equals(Organisation.EnterpriseCurrency)) { return(new Money(MoneyRounder.RoundToPayableAmount(_amount, 0.05m), _currencySymbol, _currencySymbolDisplay, _currencyDecimalDigits)); } else { return(this); } }
public static (MoneyAmount amount, Discount discount) GetPrice(List <RateDetails> rateDetails) { var firstRateDetails = rateDetails.First(); var currency = firstRateDetails.PaymentDetails.TotalAmount.Currency; var totalPrice = rateDetails.Sum(rateDetailsItem => rateDetailsItem.PaymentDetails.TotalAmount.Amount); var moneyAmount = new MoneyAmount(totalPrice, currency); var totalPriceWithDiscount = rateDetails.Sum(rateDetailsItem => rateDetailsItem.PaymentDetails.TotalAmount.Amount - rateDetailsItem.PaymentDetails.TotalAmount.Amount * Convert.ToDecimal(rateDetailsItem.PaymentDetails.Discount.Percent) / 100); var totalDiscount = new Discount(Convert.ToDouble(MoneyRounder.Truncate(100 - totalPriceWithDiscount * 100 / totalPrice, currency))); return(moneyAmount, totalDiscount); }
public void KeepSum() { var input = new List <Tuple <string, decimal> >() { Tuple.Create("a", 1.5051m), Tuple.Create("b", 2.5051m), Tuple.Create("c", 1.5051m), Tuple.Create("d", 1.4047m), }; var res = MoneyRounder.round(input); res.Sum(a => a.Item2).Should().Be(6.92m); }
public void LeaveUnchanged() { var input = new List <Tuple <string, decimal> >() { Tuple.Create("a", 1.99m), Tuple.Create("b", 2.99m), Tuple.Create("c", 1.00m), Tuple.Create("d", 1.02m), }; var res = MoneyRounder.round(input); res.Sum(a => a.Item2).Should().Be(7m); }
public void CorrectResult() { var input = new List <Tuple <string, decimal> >() { Tuple.Create("a", 1.5051m), Tuple.Create("b", 2.5051m), Tuple.Create("c", 1.5051m), Tuple.Create("d", 1.4047m), }; var res = MoneyRounder.round(input); res.ElementAt(0).Item2.Should().Be(1.51m); res.ElementAt(1).Item2.Should().Be(2.51m); res.ElementAt(2).Item2.Should().Be(1.50m); res.ElementAt(3).Item2.Should().Be(1.40m); }
public void KeepOrder() { var input = new List <Tuple <string, decimal> >() { Tuple.Create("a", 1.5051m), Tuple.Create("b", 2.5051m), Tuple.Create("c", 1.5051m), Tuple.Create("d", 1.4047m), }; var res = MoneyRounder.round(input); res.ElementAt(0).Item1.ShouldBeEquivalentTo("a"); res.ElementAt(1).Item1.ShouldBeEquivalentTo("b"); res.ElementAt(2).Item1.ShouldBeEquivalentTo("c"); res.ElementAt(3).Item1.ShouldBeEquivalentTo("d"); }
public static async Task <RoomContractSet> ProcessPrices(RoomContractSet sourceRoomContractSet, PriceProcessFunction priceProcessFunction) { var roomContracts = new List <RoomContract>(sourceRoomContractSet.Rooms.Count); var sourceTotalPrice = sourceRoomContractSet.Rate.FinalPrice; if (sourceTotalPrice.Amount == 0) { throw new NotSupportedException("Room contract set price cannot be 0"); } var processedTotalPrice = await priceProcessFunction(sourceRoomContractSet.Rate.FinalPrice); var roomContractSetGross = ChangeProportionally(sourceRoomContractSet.Rate.Gross); var roomContractSetRate = new Rate(finalPrice: processedTotalPrice, gross: roomContractSetGross, discounts: sourceRoomContractSet.Rate.Discounts, type: sourceRoomContractSet.Rate.Type, description: sourceRoomContractSet.Rate.Description); foreach (var room in sourceRoomContractSet.Rooms) { var dailyRates = new List <DailyRate>(room.DailyRoomRates.Count); foreach (var dailyRate in room.DailyRoomRates) { var roomGross = MoneyRounder.Ceil(ChangeProportionally(dailyRate.Gross)); var roomFinalPrice = MoneyRounder.Ceil(ChangeProportionally(dailyRate.FinalPrice)); dailyRates.Add(BuildDailyPrice(dailyRate, roomFinalPrice, roomGross)); } var totalPriceNet = ChangeProportionally(room.Rate.FinalPrice); var totalPriceGross = ChangeProportionally(room.Rate.Gross); var totalRate = new Rate(totalPriceNet, totalPriceGross); roomContracts.Add(BuildRoomContracts(room, dailyRates, totalRate)); } return(BuildRoomContractSet(sourceRoomContractSet, roomContractSetRate, roomContracts)); MoneyAmount ChangeProportionally(MoneyAmount price) { var ratio = (price / sourceTotalPrice).Amount; return(new MoneyAmount(processedTotalPrice.Amount * ratio, processedTotalPrice.Currency)); } }
/// <summary> /// Rule implementation. /// </summary> /// <param name="context">Rule context.</param> protected override void Execute(RuleContext context) { object value = context.InputPropertyValues[PrimaryProperty]; if (value != null) { Money valueAsMoney = Money.TryConvert(value); if (valueAsMoney != null && valueAsMoney.Amount >= 0m && valueAsMoney.CurrencySymbol.Equals(Organisation.EnterpriseCurrency)) { if (MoneyRounder.RoundToPayableAmount(valueAsMoney.Amount, 0.05m) != valueAsMoney.Amount) { string message = string.Format(Localization.MoneyRules.MoneyNotPayable, PrimaryProperty.FriendlyName); context.Results.Add(new RuleResult(RuleName, PrimaryProperty, message) { Severity = Severity }); } } } }
public async Task <Result <TData> > ConvertPricesInData <TData>(TData data, Func <TData, PriceProcessFunction, ValueTask <TData> > changePricesFunc, Func <TData, Currencies?> getCurrencyFunc) { var currentCurrency = getCurrencyFunc(data); if (!currentCurrency.HasValue) { return(Result.Success(data)); } if (currentCurrency == TargetCurrency) { return(Result.Success(data)); } if (currentCurrency == Currencies.NotSpecified) { return(Result.Failure <TData>($"Cannot convert from '{Currencies.NotSpecified}' currency")); } var(_, isFailure, rate, error) = await _rateService.Get(currentCurrency.Value, TargetCurrency); if (isFailure) { return(Result.Failure <TData>(error)); } var converter = _converterFactory.Create(in rate, currentCurrency.Value, TargetCurrency); var convertedDetails = await changePricesFunc(data, price => { var convertedAmount = converter.Convert(price); var ceiledAmount = MoneyRounder.Ceil(convertedAmount); return(new ValueTask <MoneyAmount>(ceiledAmount)); }); return(Result.Success(convertedDetails)); }
public async Task Cancel(string referenceCode) { var orderToCancel = await _context.SupplierOrders .SingleOrDefaultAsync(o => o.ReferenceCode == referenceCode); if (orderToCancel == default) { return; } var applyingPolicy = orderToCancel.Deadline?.Policies .Where(p => p.FromDate <= _dateTimeProvider.UtcNow()) .OrderBy(p => p.FromDate) .LastOrDefault(); if (applyingPolicy is not null) { var rawRefundableAmount = (decimal)((100 - applyingPolicy.Percentage) / 100) * orderToCancel.Price; var roundedRefundableAmount = MoneyRounder.ToEven(rawRefundableAmount, orderToCancel.Currency); orderToCancel.RefundableAmount = roundedRefundableAmount; } else { orderToCancel.RefundableAmount = orderToCancel.Price; } orderToCancel.State = SupplierOrderState.Canceled; _context.SupplierOrders.Update(orderToCancel); await _context.SaveChangesAsync(); if (orderToCancel.PaymentType == SupplierPaymentType.CreditCard) { var moneyToCharge = orderToCancel.Price - orderToCancel.RefundableAmount; await _creditCardProvider.ProcessAmountChange(orderToCancel.ReferenceCode, new MoneyAmount(moneyToCharge, orderToCancel.Currency)); } }
private MoneyAmount CalculatePercentPenaltyPrice(double percentToCharge, PaymentDetails paymentDetails) => new MoneyAmount(MoneyRounder.Ceil(paymentDetails.TotalAmount.Amount * (decimal)percentToCharge / 100, paymentDetails.TotalAmount.Currency), paymentDetails.TotalAmount.Currency);