Пример #1
0
        public async Task <Result <string, ProblemDetails> > Register(AccommodationBookingRequest bookingRequest, AgentContext agentContext, string languageCode)
        {
            string availabilityId = default;
            var    settings       = await _accommodationBookingSettingsService.Get(agentContext);

            return(await GetCachedAvailability(bookingRequest, agentContext)
                   .Ensure(AreAprSettingsSuitable, ProblemDetailsBuilder.Build("You can't book the restricted contract without explicit approval from a Happytravel.com officer."))
                   .Ensure(AreDeadlineSettingsSuitable, ProblemDetailsBuilder.Build("You can't book the contract within deadline without explicit approval from a Happytravel.com officer."))
                   .Tap(FillAvailabilityId)
                   .Map(ExtractBookingAvailabilityInfo)
                   .Map(Register)
                   .Finally(WriteLog));


            bool AreAprSettingsSuitable(
                (Suppliers, DataWithMarkup <RoomContractSetAvailability>) bookingData)
            => BookingRegistrationService.AreAprSettingsSuitable(bookingRequest, bookingData, settings);


            bool AreDeadlineSettingsSuitable(
                (Suppliers, DataWithMarkup <RoomContractSetAvailability>) bookingData)
            => this.AreDeadlineSettingsSuitable(bookingRequest, bookingData, settings);


            void FillAvailabilityId((Suppliers, DataWithMarkup <RoomContractSetAvailability> Result) responseWithMarkup)
            => availabilityId = responseWithMarkup.Result.Data.AvailabilityId;


            async Task <string> Register(BookingAvailabilityInfo bookingAvailability)
            {
                var bookingRequestWithAvailabilityId = new AccommodationBookingRequest(bookingRequest, availabilityId);

                return(await _bookingRecordsManager.Register(bookingRequestWithAvailabilityId, bookingAvailability, agentContext, languageCode));
            }

            Result <string, ProblemDetails> WriteLog(Result <string, ProblemDetails> result)
            => LoggerUtils.WriteLogByResult(result,
                                            () => _logger.LogBookingRegistrationSuccess($"Successfully registered a booking with reference code: '{result.Value}'"),
                                            () => _logger.LogBookingRegistrationFailure($"Failed to register a booking. AvailabilityId: '{availabilityId}'. " +
                                                                                        $"Itinerary number: {bookingRequest.ItineraryNumber}. Passenger name: {bookingRequest.MainPassengerName}. Error: {result.Error.Detail}"));
        }
Пример #2
0
        public async Task <Result <AccommodationBookingInfo, ProblemDetails> > BookByAccount(AccommodationBookingRequest bookingRequest,
                                                                                             AgentContext agentContext, string languageCode, string clientIp)
        {
            string   availabilityId       = default;
            DateTime?availabilityDeadline = default;
            DateTime availabilityCheckIn  = default;
            string   referenceCode        = default;
            var      wasPaymentMade       = false;
            var      settings             = await _accommodationBookingSettingsService.Get(agentContext);

            // TODO Remove lots of code duplication in account and card purchase booking
            var(_, isRegisterFailure, booking, registerError) = await GetCachedAvailability(bookingRequest, agentContext)
                                                                .Ensure(AreAprSettingsSuitable, ProblemDetailsBuilder.Build("You can't book the restricted contract without explicit approval from a Happytravel.com officer."))
                                                                .Ensure(AreDeadlineSettingsSuitable, ProblemDetailsBuilder.Build("You can't book the contract within deadline without explicit approval from a Happytravel.com officer."))
                                                                .Tap(FillAvailabilityLocalVariables)
                                                                .Map(ExtractBookingAvailabilityInfo)
                                                                .BindWithTransaction(_context, info => Result.Success <BookingAvailabilityInfo, ProblemDetails>(info)
                                                                                     .Map(RegisterBooking)
                                                                                     .Bind(GetBooking)
                                                                                     .Bind(PayUsingAccountIfDeadlinePassed))
                                                                .OnFailure(WriteLogFailure);

            if (isRegisterFailure)
            {
                return(Result.Failure <AccommodationBookingInfo, ProblemDetails>(registerError));
            }

            return(await BookOnProvider(booking, booking.ReferenceCode, languageCode)
                   .Tap(ProcessResponse)
                   .OnFailure(VoidMoneyAndCancelBooking)
                   .Bind(GenerateInvoice)
                   .Bind(SendReceiptIfPaymentMade)
                   .Bind(GetAccommodationBookingInfo)
                   .Finally(WriteLog));


            void FillAvailabilityLocalVariables((Suppliers, DataWithMarkup <RoomContractSetAvailability> Result) responseWithMarkup)
            {
                availabilityId       = responseWithMarkup.Result.Data.AvailabilityId;
                availabilityDeadline = responseWithMarkup.Result.Data.RoomContractSet.Deadline.Date;
                availabilityCheckIn  = responseWithMarkup.Result.Data.CheckInDate;
            }

            bool AreAprSettingsSuitable(
                (Suppliers, DataWithMarkup <RoomContractSetAvailability>) bookingData)
            => BookingRegistrationService.AreAprSettingsSuitable(bookingRequest, bookingData, settings);


            bool AreDeadlineSettingsSuitable(
                (Suppliers, DataWithMarkup <RoomContractSetAvailability>) bookingData)
            => this.AreDeadlineSettingsSuitable(bookingRequest, bookingData, settings);


            async Task <string> RegisterBooking(BookingAvailabilityInfo bookingAvailability)
            {
                var bookingRequestWithAvailabilityId = new AccommodationBookingRequest(bookingRequest, availabilityId);
                var registeredReferenceCode          =
                    await _bookingRecordsManager.Register(bookingRequestWithAvailabilityId, bookingAvailability, agentContext, languageCode);

                referenceCode = registeredReferenceCode;
                return(registeredReferenceCode);
            }

            async Task <Result <Data.Booking.Booking, ProblemDetails> > GetBooking(string referenceCode)
            => await _bookingRecordsManager.Get(referenceCode).ToResultWithProblemDetails();


            async Task <Result <Data.Booking.Booking, ProblemDetails> > PayUsingAccountIfDeadlinePassed(Data.Booking.Booking bookingInPipeline)
            {
                var daysBeforeDeadline = Infrastructure.Constants.Common.DaysBeforeDeadlineWhenPayForBooking;
                var now = _dateTimeProvider.UtcNow();

                var deadlinePassed = availabilityCheckIn <= now.AddDays(daysBeforeDeadline) ||
                                     (availabilityDeadline.HasValue && availabilityDeadline <= now.AddDays(daysBeforeDeadline));

                if (!deadlinePassed)
                {
                    return(bookingInPipeline);
                }

                var(_, isPaymentFailure, _, paymentError) = await _accountPaymentService.Charge(bookingInPipeline, agentContext, clientIp);

                if (isPaymentFailure)
                {
                    return(ProblemDetailsBuilder.Fail <Data.Booking.Booking>(paymentError));
                }

                wasPaymentMade = true;
                return(bookingInPipeline);
            }

            Task ProcessResponse(EdoContracts.Accommodations.Booking bookingResponse) => _bookingResponseProcessor.ProcessResponse(bookingResponse, booking);

            Task VoidMoneyAndCancelBooking(ProblemDetails problemDetails) => this.VoidMoneyAndCancelBooking(booking, agentContext);

            Task <Result <EdoContracts.Accommodations.Booking, ProblemDetails> > GenerateInvoice(EdoContracts.Accommodations.Booking details) => this.GenerateInvoice(details, booking.ReferenceCode, agentContext);


            async Task <Result <EdoContracts.Accommodations.Booking, ProblemDetails> > SendReceiptIfPaymentMade(EdoContracts.Accommodations.Booking details)
            => wasPaymentMade
                    ? await SendReceipt(details, booking, agentContext)
                    : details;


            Task <Result <AccommodationBookingInfo, ProblemDetails> > GetAccommodationBookingInfo(EdoContracts.Accommodations.Booking details)
            => _bookingRecordsManager.GetAccommodationBookingInfo(details.ReferenceCode, languageCode)
            .ToResultWithProblemDetails();


            void WriteLogFailure(ProblemDetails problemDetails)
            => _logger.LogBookingByAccountFailure($"Failed to book using account. Reference code: '{referenceCode}'. Error: {problemDetails.Detail}");


            Result <T, ProblemDetails> WriteLog <T>(Result <T, ProblemDetails> result)
            => LoggerUtils.WriteLogByResult(result,
                                            () => _logger.LogBookingFinalizationSuccess($"Successfully booked using account. Reference code: '{referenceCode}'"),
                                            () => _logger.LogBookingFinalizationFailure(
                                                $"Failed to book using account. Reference code: '{referenceCode}'. Error: {result.Error.Detail}"));
        }