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));
        }
Beispiel #3
0
        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));
Beispiel #4
0
        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));
        }
Beispiel #5
0
 public Money RoundToPayableAmount()
 {
     if (_currencySymbol.Equals(Organisation.EnterpriseCurrency))
     {
         return(new Money(MoneyRounder.RoundToPayableAmount(_amount, 0.05m), _currencySymbol,
                          _currencySymbolDisplay, _currencyDecimalDigits));
     }
     else
     {
         return(this);
     }
 }
Beispiel #6
0
        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);
        }
Beispiel #7
0
        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);
        }
Beispiel #8
0
        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);
        }
Beispiel #9
0
        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);
        }
Beispiel #10
0
        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));
            }
        }
Beispiel #12
0
        /// <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
                        });
                    }
                }
            }
        }
Beispiel #13
0
        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));
            }
        }
Beispiel #15
0
 private MoneyAmount CalculatePercentPenaltyPrice(double percentToCharge, PaymentDetails paymentDetails)
 => new MoneyAmount(MoneyRounder.Ceil(paymentDetails.TotalAmount.Amount * (decimal)percentToCharge / 100, paymentDetails.TotalAmount.Currency), paymentDetails.TotalAmount.Currency);