public Task <Result> NotifyDeadlineApproaching(int bookingId, string email)
        {
            return(_bookingRecordsManager.Get(bookingId)
                   .Bind(booking =>
            {
                var roomDescriptions = booking.Rooms
                                       .Select(r => r.ContractDescription);

                var passengers = booking.Rooms
                                 .SelectMany(r => r.Passengers)
                                 .Select(p => $"{p.FirstName} {p.LastName}");

                var deadlineData = new BookingDeadlineData
                {
                    BookingId = booking.Id,
                    RoomDescriptions = string.Join(", ", roomDescriptions),
                    Passengers = string.Join(", ", passengers),
                    ReferenceCode = booking.ReferenceCode,
                    CheckInDate = DateTimeFormatters.ToDateString(booking.CheckInDate),
                    CheckOutDate = DateTimeFormatters.ToDateString(booking.CheckOutDate),
                    Deadline = DateTimeFormatters.ToDateString(booking.DeadlineDate)
                };

                return SendEmail(email, _options.DeadlineNotificationTemplateId, deadlineData);
            }));
        }
 static BookingSummaryNotificationData.BookingData CreateBookingData(Booking booking) =>
 new BookingSummaryNotificationData.BookingData
 {
     ReferenceCode    = booking.ReferenceCode,
     Accommodation    = booking.AccommodationName,
     Location         = $"{booking.Location.Country}, {booking.Location.Locality}",
     LeadingPassenger = GetLeadingPassengerFormattedName(booking),
     Amount           = MoneyFormatter.ToCurrencyString(booking.TotalPrice, booking.Currency),
     DeadlineDate     = DateTimeFormatters.ToDateString(booking.DeadlineDate),
     CheckInDate      = DateTimeFormatters.ToDateString(booking.CheckInDate),
     CheckOutDate     = DateTimeFormatters.ToDateString(booking.CheckOutDate),
     Status           = EnumFormatters.FromDescription(booking.Status)
 };
        public Task <Result> SendInvoice(int bookingId, string email, int agentId)
        {
            // TODO: hardcoded to be removed with UEDA-20
            var addresses = new List <string> {
                email
            };

            addresses.AddRange(_options.CcNotificationAddresses);

            return(_bookingDocumentsService.GetActualInvoice(bookingId, agentId)
                   .Bind(invoice =>
            {
                var(registrationInfo, data) = invoice;
                var invoiceData = new InvoiceData
                {
                    Number = registrationInfo.Number,
                    BuyerDetails = data.BuyerDetails,
                    InvoiceDate = DateTimeFormatters.ToDateString(registrationInfo.Date),
                    InvoiceItems = data.InvoiceItems
                                   .Select(i => new InvoiceData.InvoiceItem
                    {
                        Number = i.Number,
                        Price = FormatPrice(i.Price),
                        Total = FormatPrice(i.Total),
                        AccommodationName = i.AccommodationName,
                        RoomDescription = i.RoomDescription,
                        RoomType = EnumFormatters.FromDescription(i.RoomType),
                        DeadlineDate = DateTimeFormatters.ToDateString(i.DeadlineDate),
                        MainPassengerName = PersonNameFormatters.ToMaskedName(i.MainPassengerFirstName, i.MainPassengerLastName)
                    })
                                   .ToList(),
                    TotalPrice = FormatPrice(data.TotalPrice),
                    CurrencyCode = EnumFormatters.FromDescription(data.TotalPrice.Currency),
                    ReferenceCode = data.ReferenceCode,
                    SellerDetails = data.SellerDetails,
                    PayDueDate = DateTimeFormatters.ToDateString(data.PayDueDate),
                    CheckInDate = DateTimeFormatters.ToDateString(data.CheckInDate),
                    CheckOutDate = DateTimeFormatters.ToDateString(data.CheckOutDate),
                    PaymentStatus = EnumFormatters.FromDescription(data.PaymentStatus)
                };

                return _mailSender.Send(_options.InvoiceTemplateId, addresses, invoiceData);
            }));
        }
        public Task <Result> SendVoucher(int bookingId, string email, AgentContext agent, string languageCode)
        {
            return(_bookingDocumentsService.GenerateVoucher(bookingId, agent, languageCode)
                   .Bind(voucher =>
            {
                var voucherData = new VoucherData
                {
                    Accommodation = voucher.Accommodation,
                    AgentName = voucher.AgentName,
                    BookingId = voucher.BookingId,
                    DeadlineDate = DateTimeFormatters.ToDateString(voucher.DeadlineDate),
                    NightCount = voucher.NightCount,
                    ReferenceCode = voucher.ReferenceCode,
                    RoomDetails = voucher.RoomDetails,
                    CheckInDate = DateTimeFormatters.ToDateString(voucher.CheckInDate),
                    CheckOutDate = DateTimeFormatters.ToDateString(voucher.CheckOutDate),
                    MainPassengerName = voucher.MainPassengerName,
                    BannerUrl = voucher.BannerUrl,
                    LogoUrl = voucher.LogoUrl
                };

                return SendEmail(email, _options.VoucherTemplateId, voucherData);
            }));
        }
        public async Task <Result <string> > SendBookingReports(int agencyId)
        {
            DateTime reportBeginDate = _dateTimeProvider.UtcNow().Date;

            return(await GetEmailsAndSettings()
                   .Map(GetBookings)
                   .Bind(CreateMailData)
                   .Bind(SendMails));


            async Task <Result <List <EmailAndSetting> > > GetEmailsAndSettings()
            {
                var emailsAndSettings = await
                                            (from relation in _context.AgentAgencyRelations
                                            join agent in _context.Agents
                                            on relation.AgentId equals agent.Id
                                            where relation.AgencyId == agencyId &&
                                            relation.InAgencyPermissions.HasFlag(InAgencyPermissions.ReceiveBookingSummary)
                                            select new EmailAndSetting
                {
                    Email             = agent.Email,
                    ReportDaysSetting = _agentSettingsManager.GetUserSettings(agent).BookingReportDays
                }).ToListAsync();

                return(emailsAndSettings.Any()
                    ? Result.Success(emailsAndSettings)
                    : Result.Failure <List <EmailAndSetting> >($"Couldn't find any agents in agency with id {agencyId} to send summary to"));
            }

            async Task <(List <EmailAndSetting>, List <Booking>)> GetBookings(List <EmailAndSetting> emailsAndSettings)
            {
                var maxPeriod        = emailsAndSettings.Max(t => t.ReportDaysSetting);
                var reportMaxEndDate = reportBeginDate.AddDays(maxPeriod);

                var bookings = await _context.Bookings.Where(b => b.AgencyId == agencyId &&
                                                             b.PaymentMethod == PaymentMethods.BankTransfer &&
                                                             b.PaymentStatus != BookingPaymentStatuses.Captured &&
                                                             BookingStatusesForSummary.Contains(b.Status) &&
                                                             b.DeadlineDate < reportMaxEndDate).ToListAsync();

                return(emailsAndSettings, bookings);
            }

            async Task <Result <List <(BookingSummaryNotificationData, string)> > > CreateMailData((List <EmailAndSetting> emailsAndSettings, List <Booking> bookings) values)
            {
                var(_, isFailure, balanceInfo, error) = await _accountPaymentService.GetAccountBalance(Currencies.USD, agencyId);

                if (isFailure)
                {
                    return(Result.Failure <List <(BookingSummaryNotificationData, string)> >(
                               $"Couldn't retrieve account balance for agency with id {agencyId}. Error: {error}"));
                }

                var agencyBalance = balanceInfo.Balance;

                return(values.emailsAndSettings.Select(emailAndSetting =>
                {
                    var reportEndDate = reportBeginDate.AddDays(emailAndSetting.ReportDaysSetting);
                    var includedBookings = values.bookings.Where(b => b.DeadlineDate < reportEndDate).ToList();

                    var resultingBalance = agencyBalance - includedBookings.Sum(b => b.TotalPrice);

                    return (new BookingSummaryNotificationData
                    {
                        Bookings = includedBookings.OrderBy(b => b.DeadlineDate).Select(CreateBookingData).ToList(),
                        CurrentBalance = MoneyFormatter.ToCurrencyString(agencyBalance, Currencies.USD),
                        ResultingBalance = MoneyFormatter.ToCurrencyString(resultingBalance, Currencies.USD),
                        ShowAlert = resultingBalance < 0m,
                        ReportDate = DateTimeFormatters.ToDateString(reportEndDate)
                    },
                            emailAndSetting.Email);
                }).Where(t => t.Item1.Bookings.Any()).ToList());