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