protected bool ShouldNotifyInsuranceUsers(CostNotificationUsers costUsers)
        {
            var costOwner = costUsers.CostOwner;
            var agency    = costOwner.Agency;

            return(costUsers.InsuranceUsers?.Count > 0 && (agency.IsEuropeanAgency(RegionsService) || agency.IsNorthAmericanAgency(RegionsService)));
        }
示例#2
0
        private async Task BuildAutoApprovedNotification(CostNotificationUsers costUsers, Cost cost, CostStageRevision latestRevision, List <EmailNotificationMessage <CostNotificationObject> > notifications)
        {
            var approver = await GetLastApprover(cost);

            notifications.AddRange(await _emailNotificationBuilder.BuildCostApprovedNotification(costUsers,
                                                                                                 cost, latestRevision, approver, core.Constants.EmailApprovalType.Brand, DateTime.UtcNow));
        }
        internal async Task<IEnumerable<EmailNotificationMessage<CostNotificationObject>>> Build(Cost cost,
            CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List<EmailNotificationMessage<CostNotificationObject>>();

            //Cost Owner
            var costOwner = costUsers.CostOwner;
            var recipients = new List<string> { costOwner.GdamUserId };
            recipients.AddRange(costUsers.Watchers);

            var costOwnerNotification = new EmailNotificationMessage<CostNotificationObject>(Constants.EmailNotificationActionType.Cancelled, recipients);
            AddSharedTo(costOwnerNotification);
            MapEmailNotificationObject(costOwnerNotification.Object, cost, costOwner);
            PopulateOtherFields(costOwnerNotification, Constants.EmailNotificationParents.CostOwner, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(costOwnerNotification.Object, cost.Id);
            notifications.Add(costOwnerNotification);

            if (ShouldNotifyInsuranceUsers(costUsers))
            {
                var insuranceUserNotification = new EmailNotificationMessage<CostNotificationObject>(Constants.EmailNotificationActionType.Cancelled, costUsers.InsuranceUsers);
                AddSharedTo(insuranceUserNotification);
                MapEmailNotificationObject(insuranceUserNotification.Object, cost, costOwner);
                PopulateOtherFields(insuranceUserNotification, Constants.EmailNotificationParents.InsuranceUser, timestamp, cost.Id, costStageRevision.Id);
                await PopulateMetadata(insuranceUserNotification.Object, cost.Id);
                notifications.Add(insuranceUserNotification);
            }
            await AddFinanceManagerNotification(Constants.EmailNotificationActionType.Cancelled,
                cost, costUsers, costStageRevision, timestamp, notifications);

            return notifications;
        }
示例#4
0
        public async Task <bool> CostHasBeenRequestedToBeReopened(Guid costId, Guid userId, Guid userModuleId)
        {
            if (costId == Guid.Empty)
            {
                return(false);
            }

            if (userId == Guid.Empty)
            {
                return(false);
            }

            var cost = await _efContext.Cost
                       .Include(c => c.LatestCostStageRevision)
                       .ThenInclude(csr => csr.CostStage)
                       .Include(c => c.Project)
                       .ThenInclude(p => p.Brand)
                       .IncludeCostOwner()
                       .FirstOrDefaultAsync(c => c.Id == costId);

            var costStageRevision = cost.LatestCostStageRevision;

            var adminUsers = await _efContext.CostUser.Include(x => x.UserUserGroups).ThenInclude(x => x.UserGroup)
                             .Where(u => u.UserUserGroups.Any(x => x.UserGroup.Role.Name == Roles.ClientAdmin && x.UserGroup.ObjectId == userModuleId)).ToListAsync();

            var requestedBy = await _efContext.CostUser.FindAsync(userId);

            var costOwner = cost.Owner;
            var financeManagementUsers = await _costUserService.GetFinanceManagementUsers(cost.UserGroups, Constants.BudgetRegion.NorthAmerica);

            var insuranceUsers = await _costUserService.GetInsuranceUsers(costOwner.Agency);

            var costUsers = new CostNotificationUsers
            {
                CostOwner = costOwner,
                FinanceManagementUsers = financeManagementUsers,
                InsuranceUsers         = insuranceUsers,
                Watchers     = await GetWatchers(costId),
                ClientAdmins = adminUsers
            };

            var notificationMessages = await _emailNotificationBuilder.BuildCostReopenRequestedNotification(
                costUsers,
                cost,
                costStageRevision,
                DateTime.UtcNow,
                requestedBy);

            bool sent = false;

            foreach (var message in notificationMessages)
            {
                sent = await _paperPusherClient.SendMessage(message);
            }

            //For Brand Approval, cancel the reminder, if any
            await _reminderService.CancelReminder(cost.Id);

            return(sent);
        }
        protected void MapEmailNotificationObject(CostNotificationObject obj, Cost cost, CostUser costOwner, CostUser costApprover = null)
        {
            var users = new CostNotificationUsers
            {
                CostOwner = costOwner,
                Approver  = costApprover
            };

            MapEmailNotificationObject(obj, cost, users);
        }
示例#6
0
        public async Task <bool> CostHasBeenRecalled(Guid costId, Guid userId)
        {
            if (costId == Guid.Empty)
            {
                return(false);
            }

            if (userId == Guid.Empty)
            {
                return(false);
            }

            var cost = await _efContext.Cost
                       .Include(c => c.LatestCostStageRevision)
                       .ThenInclude(csr => csr.CostStage)
                       .Include(c => c.Project)
                       .ThenInclude(p => p.Brand)
                       .IncludeCostOwner()
                       .FirstOrDefaultAsync(c => c.Id == costId);

            var costStageRevision = cost.LatestCostStageRevision;
            var recaller          = await _efContext.CostUser.FirstOrDefaultAsync(u => u.Id == userId);

            var costOwner      = cost.Owner;
            var insuranceUsers = await _costUserService.GetInsuranceUsers(costOwner.Agency);

            var financeManagementUsers = await _costUserService.GetFinanceManagementUsers(cost.UserGroups, Constants.BudgetRegion.NorthAmerica);

            var costUsers = new CostNotificationUsers
            {
                CostOwner              = cost.Owner,
                InsuranceUsers         = insuranceUsers,
                FinanceManagementUsers = financeManagementUsers
            };
            var notificationMessages = await _emailNotificationBuilder.BuildCostRecalledNotification(
                costUsers,
                cost,
                costStageRevision,
                recaller,
                DateTime.UtcNow);

            bool sent = false;

            foreach (var message in notificationMessages)
            {
                sent = await _paperPusherClient.SendMessage(message);
            }

            //For Brand Approval, cancel the reminder, if any
            await _reminderService.CancelReminder(cost.Id);

            return(sent);
        }
示例#7
0
        public async Task <bool> CostHasBeenCancelled(Guid costId)
        {
            if (costId == Guid.Empty)
            {
                return(false);
            }

            var cost = await _efContext.Cost
                       .Include(c => c.LatestCostStageRevision)
                       .ThenInclude(csr => csr.CostStage)
                       .Include(c => c.Project)
                       .ThenInclude(p => p.Brand)
                       .IncludeCostOwner()
                       .FirstOrDefaultAsync(c => c.Id == costId);

            if (cost.Status != CostStageRevisionStatus.Cancelled)
            {
                // Cost.Status might be PendingCancellation so don't send an email until
                // the Cancellation message has been sent from Coupa to Costs via AMQ.
                return(false);
            }

            var costStageRevision = cost.LatestCostStageRevision;
            var costOwner         = cost.Owner;
            var insuranceUsers    = await _costUserService.GetInsuranceUsers(costOwner.Agency);

            var financeManagementUsers = await _costUserService.GetFinanceManagementUsers(cost.UserGroups, Constants.BudgetRegion.NorthAmerica);

            var costUsers = new CostNotificationUsers
            {
                CostOwner              = costOwner,
                InsuranceUsers         = insuranceUsers,
                Watchers               = await GetWatchers(costId),
                FinanceManagementUsers = financeManagementUsers
            };
            var notificationMessages = await _emailNotificationBuilder.BuildCostCancelledNotification(costUsers, cost, costStageRevision, DateTime.UtcNow);

            bool sent = false;

            foreach (var message in notificationMessages)
            {
                sent = await _paperPusherClient.SendMessage(message);
            }

            return(sent);
        }
示例#8
0
        public async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > BuildCostReopenRequestedNotification(
            CostNotificationUsers costUsers, Cost cost, CostStageRevision costStageRevision, DateTime timestamp, CostUser requestedBy
            )
        {
            if (cost == null)
            {
                Logger.Error("Failed to create an email notification because cost is null.");
                throw new ArgumentNullException(nameof(cost));
            }

            if (costStageRevision == null)
            {
                Logger.Error("Failed to create an email notification because costStageRevision is null.");
                throw new ArgumentNullException(nameof(costStageRevision));
            }

            if (costUsers.ClientAdmins == null || !costUsers.ClientAdmins.Any())
            {
                Logger.Error("Failed to create an email notification because costUsers is null or empty.");
                throw new ArgumentNullException(nameof(costUsers.ClientAdmins));
            }

            if (requestedBy == null)
            {
                Logger.Error("Failed to create an email notification because rejectedBy is null.");
                throw new ArgumentNullException(nameof(requestedBy));
            }

            if (timestamp == DateTime.MinValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MinValue");
                throw new ArgumentException("Param cannot be DateTime.MinValue", nameof(timestamp));
            }

            if (timestamp == DateTime.MaxValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MaxValue");
                throw new ArgumentException("Param cannot be DateTime.MaxValue", nameof(timestamp));
            }

            var notificationBuilder = new ReopenRequestedNotificationBuilder(_mapper,
                                                                             _uriHelper, _costStageRevisionService, _metadataProviderService, _appSettings, _efContext, _regionsService);

            return(await notificationBuilder.Build(cost, costUsers, requestedBy.FullName, costStageRevision, timestamp));
        }
示例#9
0
        public async Task Create_Notification_For_Coupa_For_Non_NA_Non_Cyclone_Agency_When_Cost_Total_Amount_Decreased()
        {
            //Arrange
            decimal?previousPaymentAmount = 10.00M;
            decimal?latestPaymentAmount   = 5.00M; //Decreased from 10
            var     timestamp             = DateTime.UtcNow;

            var cost           = new Cost();
            var costOwner      = new CostUser();
            var latestRevision = new CostStageRevision();

            var previousRevisionId = Guid.NewGuid();
            var latestRevisionId   = Guid.NewGuid();

            SetupPurchaseOrderCost(cost, latestRevision, costOwner, previousRevisionId, latestRevisionId, PoNumber);

            var costUsers = new CostNotificationUsers
            {
                CostOwner = costOwner
            };

            var previousPaymentResult = new PaymentAmountResult
            {
                TotalCostAmount = previousPaymentAmount
            };
            var latestPaymentResult = new PaymentAmountResult
            {
                TotalCostAmount = latestPaymentAmount
            };

            PgPaymentServiceMock.Setup(ppsm => ppsm.GetPaymentAmount(previousRevisionId, false)).Returns(Task.FromResult(previousPaymentResult));
            PgPaymentServiceMock.Setup(ppsm => ppsm.GetPaymentAmount(latestRevisionId, false)).Returns(Task.FromResult(latestPaymentResult));

            //Act
            IEnumerable <EmailNotificationMessage <CostNotificationObject> > result = await EmailNotificationBuilder.BuildPendingBrandApprovalNotification(costUsers, cost,
                                                                                                                                                           latestRevision, timestamp);

            //Assert
            result.Should().NotBeNull();
            var notifications = result.ToArray();

            notifications.Should().HaveCount(1);
        }
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(
            Cost cost,
            CostNotificationUsers costUsers,
            CostStageRevision costStageRevision,
            DateTime timestamp,
            CostUser changeApprover,
            CostUser previousOwner)
        {
            var    notifications = new List <EmailNotificationMessage <CostNotificationObject> >();
            string actionType    = core.Constants.EmailNotificationActionType.CostOwnerChanged;
            var    costOwner     = costUsers.CostOwner;

            //add recipients
            var recipients = new List <string> {
                costOwner.GdamUserId
            };

            if (costUsers.Watchers != null)
            {
                recipients.AddRange(costUsers.Watchers);
            }
            if (costUsers.Approvers != null)
            {
                recipients.AddRange(costUsers.Approvers);
            }
            recipients.Add(previousOwner.GdamUserId);

            var costOwnerNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, recipients.Distinct());

            AddSharedTo(costOwnerNotification);
            MapEmailNotificationObject(costOwnerNotification.Object, cost, costOwner);
            costOwnerNotification.Object.Cost.PreviousOwner = previousOwner.FullName;
            var approver = costOwnerNotification.Object.Approver;

            approver.Name = changeApprover.FullName;
            PopulateOtherFields(costOwnerNotification, core.Constants.EmailNotificationParents.CostOwner, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(costOwnerNotification.Object, cost.Id);

            notifications.Add(costOwnerNotification);

            return(notifications);
        }
        protected async Task <EmailNotificationMessage <CostNotificationObject> > AddFinanceManagerNotification(
            string actionType,
            Cost cost,
            CostNotificationUsers costUsers,
            CostStageRevision costStageRevision,
            DateTime timestamp,
            List <EmailNotificationMessage <CostNotificationObject> > notifications
            )
        {
            //Only send to North American P&G Finance Users when Budget Region is North America and Cyclone costs
            if (costUsers.FinanceManagementUsers == null ||
                !costUsers.FinanceManagementUsers.Any() ||
                Constants.BudgetRegion.NorthAmerica != GetBudgetRegion(costStageRevision) ||
                !costUsers.CostOwner.Agency.IsCyclone())
            {
                return(null);
            }

            //Send to FinanceManagement as well
            var financeManagementNotification =
                new EmailNotificationMessage <CostNotificationObject>(actionType, costUsers.FinanceManagementUsers);
            var notificationObject = new FinanceManagerCostNotificationObject();

            financeManagementNotification.Object = notificationObject;
            var parent = core.Constants.EmailNotificationParents.FinanceManagement;

            AddSharedTo(financeManagementNotification);
            MapEmailNotificationObject(financeManagementNotification.Object, cost, costUsers);
            PopulateOtherFields(financeManagementNotification, parent, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(financeManagementNotification.Object, cost.Id);

            notifications.Add(financeManagementNotification);

            if (cost.Status == CostStageRevisionStatus.Approved && cost.LatestCostStageRevision.CostStage.Key == Models.Stage.CostStages.OriginalEstimate.ToString())
            {
                notificationObject.CanAssignIONumber = true;
            }

            return(financeManagementNotification);
        }
        private async Task BuildPendingBrandApprovalNotification(List <EmailNotificationMessage <CostNotificationObject> > notifications,
                                                                 List <Approval> approvals, Cost cost, CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var costOwner                   = costUsers.CostOwner;
            var agency                      = costOwner.Agency;
            var isCyclone                   = agency.IsCyclone();
            var costStageRevisionId         = costStageRevision.Id;
            var isNorthAmericanBudgetRegion = IsNorthAmericanBudgetRegion(costStageRevisionId);

            var brandApprovals = approvals.Where(a => a.Type == ApprovalType.Brand).ToArray();

            if (isCyclone && isNorthAmericanBudgetRegion)
            {
                //Send notification to Brand Approver in the Platform for Cost in North American Budget Region and Cyclone agencies.
                const string actionType = core.Constants.EmailNotificationActionType.BrandApproverAssigned;
                var          parent     = core.Constants.EmailNotificationParents.BrandApprover;
                foreach (var brandApproval in brandApprovals)
                {
                    foreach (var approvalMember in brandApproval.ApprovalMembers)
                    {
                        //Send notifications to actual Brand Approvers only
                        if (approvalMember.IsSystemApprover())
                        {
                            continue;
                        }

                        var approvalCostUser          = approvalMember.CostUser;
                        var brandApproverNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, approvalCostUser.GdamUserId);
                        AddSharedTo(brandApproverNotification);

                        MapEmailNotificationObject(brandApproverNotification.Object, cost, costOwner, approvalCostUser);
                        PopulateOtherFields(brandApproverNotification, parent, timestamp, cost.Id, costStageRevisionId);
                        await PopulateMetadata(brandApproverNotification.Object, cost.Id);

                        notifications.Add(brandApproverNotification);

                        if (ShouldNotifyInsuranceUsers(costUsers))
                        {
                            var insuranceUserNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, costUsers.InsuranceUsers);
                            AddSharedTo(insuranceUserNotification);

                            MapEmailNotificationObject(insuranceUserNotification.Object, cost, costOwner, approvalCostUser);
                            PopulateOtherFields(insuranceUserNotification, core.Constants.EmailNotificationParents.InsuranceUser, timestamp, cost.Id, costStageRevisionId);
                            await PopulateMetadata(insuranceUserNotification.Object, cost.Id);

                            notifications.Add(insuranceUserNotification);
                        }
                    }
                }

                if (brandApprovals.Any())
                {
                    await AddFinanceManagerNotification(actionType, cost, costUsers, costStageRevision, timestamp, notifications);
                }
            }
            else
            {
                AddCoupaApprovalEmail(notifications, cost, costOwner, costStageRevisionId, timestamp);
            }
        }
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(Cost cost,
                                                                                                      CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();

            //Build PendingBrandApproval notifications
            var approvals = _approvalService.GetApprovalsByCostStageRevisionId(costStageRevision.Id).Result;

            if (approvals != null)
            {
                await BuildPendingBrandApprovalNotification(notifications, approvals, cost, costUsers, costStageRevision, timestamp);
            }

            return(notifications);
        }
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(dataAccess.Entity.Cost cost,
                                                                                                      CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var          notifications = new List <EmailNotificationMessage <CostNotificationObject> >();
            var          approvals     = _approvalService.GetApprovalsByCostStageRevisionId(costStageRevision.Id).Result;
            const string actionType    = core.Constants.EmailNotificationActionType.TechnicalApproverAssigned;

            if (approvals == null)
            {
                //No approvals set
                return(notifications);
            }

            var costOwner          = costUsers.CostOwner;
            var technicalApprovals = approvals.Where(a => a.Type == ApprovalType.IPM).ToArray();

            foreach (var technicalApproval in technicalApprovals)
            {
                foreach (var approvalMember in technicalApproval.ApprovalMembers)
                {
                    //Send notifications to Technical Approver
                    var approvalCostUser = approvalMember.CostUser;
                    var technicalApproverNotification = new EmailNotificationMessage <CostNotificationObject>(actionType,
                                                                                                              approvalCostUser.GdamUserId);
                    AddSharedTo(technicalApproverNotification);

                    MapEmailNotificationObject(technicalApproverNotification.Object, cost, costOwner, approvalCostUser);
                    PopulateOtherFields(technicalApproverNotification, core.Constants.EmailNotificationParents.TechnicalApprover, timestamp, cost.Id, costStageRevision.Id, approvalCostUser);
                    await PopulateMetadata(technicalApproverNotification.Object, cost.Id);

                    notifications.Add(technicalApproverNotification);

                    //Insurance User
                    if (ShouldNotifyInsuranceUsers(costUsers))
                    {
                        var insuranceUserNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, costUsers.InsuranceUsers);
                        AddSharedTo(insuranceUserNotification);

                        MapEmailNotificationObject(insuranceUserNotification.Object, cost, costOwner, approvalCostUser);
                        PopulateOtherFields(insuranceUserNotification, core.Constants.EmailNotificationParents.InsuranceUser, timestamp, cost.Id, costStageRevision.Id,
                                            approvalCostUser);
                        await PopulateMetadata(insuranceUserNotification.Object, cost.Id);

                        notifications.Add(insuranceUserNotification);
                    }
                }
            }
            await AddFinanceManagerNotification(actionType, cost, costUsers, costStageRevision, timestamp, notifications);

            return(notifications);
        }
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(IEnumerable <ApprovalMember> removedApprovers,
                                                                                                      dataAccess.Entity.Cost cost, CostNotificationUsers costUsers, CostStageRevision previousRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();

            foreach (var removedMember in removedApprovers)
            {
                if (removedMember.IsSystemApprover())
                {
                    continue;
                }

                var    approvalCostUser = removedMember.CostUser;
                var    costOwner        = costUsers.CostOwner;
                string actionType       = core.Constants.EmailNotificationActionType.ApproverUnassigned;
                string parent           = core.Constants.EmailNotificationParents.Approver;

                var previousApproverNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, approvalCostUser.GdamUserId);
                AddSharedTo(previousApproverNotification);
                MapEmailNotificationObject(previousApproverNotification.Object, cost, costOwner, approvalCostUser);
                PopulateOtherFields(previousApproverNotification, parent, timestamp, cost.Id, previousRevision.Id);
                await PopulateMetadata(previousApproverNotification.Object, cost.Id);

                var replacement = GetReplacement(cost.LatestCostStageRevision, removedMember);

                var approver = previousApproverNotification.Object.Approver;
                approver.Type = GetApprovalType(removedMember.Approval);
                if (replacement != null)
                {
                    approver.Name = replacement.CostUser.FullName;
                }
                notifications.Add(previousApproverNotification);
            }

            return(notifications);
        }
        protected void MapEmailNotificationObject(CostNotificationObject obj, Cost cost, CostNotificationUsers users)
        {
            dataAccess.Entity.Project project = cost.Project;

            obj.Agency = Mapper.Map <core.Models.Notifications.Agency>(users.CostOwner.Agency);
            Mapper.Map(users.CostOwner.Agency.Country, obj.Agency);

            obj.Brand = Mapper.Map <core.Models.Notifications.Brand>(project.Brand);

            obj.Cost = Mapper.Map <core.Models.Notifications.Cost>(cost);
            Mapper.Map(cost.LatestCostStageRevision.CostStage, obj.Cost);
            Mapper.Map(users.CostOwner, obj.Cost);

            obj.Project = Mapper.Map <core.Models.Notifications.Project>(project);

            if (users.Approver != null)
            {
                obj.Approver = Mapper.Map <Approver>(users.Approver);
            }
        }
示例#17
0
        public async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > BuildCostApprovedNotification(CostNotificationUsers costUsers,
                                                                                                                            Cost cost,
                                                                                                                            CostStageRevision costStageRevision,
                                                                                                                            string approverName,
                                                                                                                            string approvalType,
                                                                                                                            DateTime timestamp)
        {
            if (cost == null)
            {
                Logger.Error("Failed to create an email notification because cost is null.");
                throw new ArgumentNullException(nameof(cost));
            }

            if (costStageRevision == null)
            {
                Logger.Error("Failed to create an email notification because costStageRevision is null.");
                throw new ArgumentNullException(nameof(costStageRevision));
            }

            if (costUsers == null)
            {
                Logger.Error("Failed to create an email notification because costUsers is null.");
                throw new ArgumentNullException(nameof(costUsers));
            }

            if (approverName == null)
            {
                Logger.Error("ENB001: Failed to create an email notification because approver is null. This could be because IoNumberOwner is empty from Coupa. The IoNumberOwner is the approver for Costs approved in Coupa.");
                throw new ArgumentNullException(nameof(approverName));
            }

            if (timestamp == DateTime.MinValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MinValue");
                throw new ArgumentException("Param cannot be DateTime.MinValue", nameof(timestamp));
            }

            if (timestamp == DateTime.MaxValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MaxValue");
                throw new ArgumentException("Param cannot be DateTime.MaxValue", nameof(timestamp));
            }

            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();
            var approvedNotificationBuilder = new ApprovedNotificationBuilder(_mapper,
                                                                              _uriHelper, _costStageRevisionService, _metadataProviderService, _appSettings, _efContext, _regionsService);

            notifications.AddRange(await approvedNotificationBuilder.Build(cost, costUsers, approverName, approvalType, costStageRevision, timestamp));

            return(notifications);
        }
示例#18
0
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > BuildAsync(Cost cost, CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();

            //Cost Owner
            var costOwner             = costUsers.CostOwner;
            var actionType            = Constants.EmailNotificationActionType.Recalled;
            var costOwnerNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, costOwner.GdamUserId);

            AddSharedTo(costOwnerNotification);
            MapEmailNotificationObject(costOwnerNotification.Object, cost, costOwner);
            await PopulateOtherFieldsForRecall(costOwnerNotification, Constants.EmailNotificationParents.CostOwner, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(costOwnerNotification.Object, cost.Id);

            notifications.Add(costOwnerNotification);

            var approvals = await _approvalService.GetApprovalsByCostStageRevisionId(costStageRevision.Id);

            if (approvals == null)
            {
                //No approvals set
                return(notifications);
            }

            var technicalApprovals = approvals.Where(a => a.Type == ApprovalType.IPM).ToArray();

            foreach (var technicalApproval in technicalApprovals)
            {
                foreach (var approvalMember in technicalApproval.ApprovalMembers)
                {
                    //Send notifications to Technical Approver
                    await AddApproverNotification(notifications, cost, costOwner, costStageRevision, timestamp, actionType, approvalMember.CostUser);
                }
            }

            var brandApprovals = approvals.Where(a => a.Type == ApprovalType.Brand).ToArray();

            foreach (var brandApproval in brandApprovals)
            {
                foreach (var approvalMember in brandApproval.ApprovalMembers)
                {
                    //Send notifications to actual Brand Approvers only
                    if (approvalMember.IsSystemApprover())
                    {
                        continue;
                    }

                    await AddApproverNotification(notifications, cost, costOwner, costStageRevision, timestamp, actionType, approvalMember.CostUser);
                }
                if (brandApproval.Requisitioners == null)
                {
                    continue;
                }

                foreach (var requisitioner in brandApproval.Requisitioners)
                {
                    await AddApproverNotification(notifications, cost, costOwner, costStageRevision, timestamp, actionType, requisitioner.CostUser);
                }
            }
            await AddFinanceManagerNotification(actionType, cost, costUsers, costStageRevision, timestamp, notifications);

            return(notifications);
        }
示例#19
0
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(Cost cost,
                                                                                                      CostNotificationUsers costUsers, string approverName, string approvalType, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();

            //Cost Owner
            string actionType;
            var    parent = core.Constants.EmailNotificationParents.CostOwner;

            if (approvalType == core.Constants.EmailApprovalType.Brand)
            {
                actionType   = core.Constants.EmailNotificationActionType.BrandApprovalApproved;
                approvalType = await GetBrandManagerRoleLabel();

                if (cost.IsExternalPurchases)
                {
                    parent = core.Constants.EmailNotificationParents.MyPurchases;
                }
            }
            else
            {
                actionType = core.Constants.EmailNotificationActionType.TechnicalApprovalApproved;
            }
            var costOwner  = costUsers.CostOwner;
            var recipients = new List <string> {
                costOwner.GdamUserId
            };

            if (costUsers.Watchers != null)
            {
                recipients.AddRange(costUsers.Watchers);
            }
            var costOwnerNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, recipients);

            AddSharedTo(costOwnerNotification);
            MapEmailNotificationObject(costOwnerNotification.Object, cost, costOwner);
            PopulateOtherFields(costOwnerNotification, parent, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(costOwnerNotification.Object, cost.Id);

            var approver = costOwnerNotification.Object.Approver;

            approver.Name = approverName;
            approver.Type = approvalType;
            notifications.Add(costOwnerNotification);

            if (cost.Status == CostStageRevisionStatus.Approved)
            {
                if (ShouldNotifyInsuranceUsers(costUsers))
                {
                    //Send to all InsuranceUsers as well
                    var insuranceUsers            = costUsers.InsuranceUsers;
                    var insuranceUserNotification =
                        new EmailNotificationMessage <CostNotificationObject>(core.Constants.EmailNotificationActionType.AllApprovalsApproved, insuranceUsers);
                    parent = core.Constants.EmailNotificationParents.InsuranceUser;
                    AddSharedTo(insuranceUserNotification);
                    MapEmailNotificationObject(insuranceUserNotification.Object, cost, costUsers);
                    PopulateOtherFields(insuranceUserNotification, parent, timestamp, cost.Id, costStageRevision.Id);
                    await PopulateMetadata(insuranceUserNotification.Object, cost.Id);

                    approver      = insuranceUserNotification.Object.Approver;
                    approver.Name = approverName;
                    approver.Type = approvalType;
                    notifications.Add(insuranceUserNotification);
                }

                var financeManagementNotification = await AddFinanceManagerNotification(core.Constants.EmailNotificationActionType.AllApprovalsApproved,
                                                                                        cost, costUsers, costStageRevision, timestamp, notifications);

                if (financeManagementNotification != null)
                {
                    approver      = financeManagementNotification.Object.Approver;
                    approver.Name = approverName;
                    approver.Type = approvalType;
                }
            }

            return(notifications);
        }
示例#20
0
        public async Task <bool> CostHasBeenApproved(Guid costId, Guid approverUserId, string approvalType)
        {
            if (costId == Guid.Empty)
            {
                return(false);
            }

            if (approverUserId == Guid.Empty)
            {
                return(false);
            }

            if (string.IsNullOrWhiteSpace(approvalType))
            {
                return(false);
            }

            if (!IsValidApprovalType(approvalType))
            {
                return(false);
            }

            var cost = await _efContext.Cost
                       .Include(c => c.LatestCostStageRevision)
                       .ThenInclude(csr => csr.CostStage)
                       .Include(c => c.Project)
                       .ThenInclude(p => p.Brand)
                       .IncludeCostOwner()
                       .FirstOrDefaultAsync(c => c.Id == costId);

            var costStageRevision = cost.LatestCostStageRevision;
            var approver          = await _costUserService.GetApprover(costId, approverUserId, approvalType);

            var costOwner = cost.Owner;
            var financeManagementUsers = await _costUserService.GetFinanceManagementUsers(cost.UserGroups, Constants.BudgetRegion.NorthAmerica);

            var insuranceUsers = await _costUserService.GetInsuranceUsers(costOwner.Agency);

            var costUsers = new CostNotificationUsers
            {
                CostOwner = costOwner,
                FinanceManagementUsers = financeManagementUsers,
                InsuranceUsers         = insuranceUsers,
                Watchers = await GetWatchers(costId)
            };
            var notificationMessages = await _emailNotificationBuilder.BuildCostApprovedNotification(costUsers, cost, costStageRevision, approver, approvalType, DateTime.UtcNow);

            var notifications = notificationMessages.ToList();

            if (approvalType != core.Constants.EmailApprovalType.Brand)
            {
                //Technical Approval sent so now send Brand Approval email
                notifications.AddRange(await _emailNotificationBuilder.BuildPendingBrandApprovalNotification(costUsers, cost, costStageRevision, DateTime.UtcNow));

                //For Technical Approval, cancel the reminder, if any
                await _reminderService.CancelReminder(cost.Id);

                //Enqueue reminder for the brand approvers.
                await _reminderService.CreateNew(cost.Id, DateTime.UtcNow.AddDays(1));
            }
            if (approvalType == core.Constants.EmailApprovalType.Brand)
            {
                //For Brand Approval, cancel the reminder, if any
                await _reminderService.CancelReminder(cost.Id);
            }

            bool sent = false;

            foreach (var message in notifications)
            {
                sent = await _paperPusherClient.SendMessage(message);
            }

            return(sent);
        }
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(dataAccess.Entity.Cost cost,
                                                                                                      CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();

            //Cost Owner
            var costOwner             = costUsers.CostOwner;
            var costOwnerNotification = new EmailNotificationMessage <CostNotificationObject>(
                core.Constants.EmailNotificationActionType.Submitted, costOwner.GdamUserId);

            AddSharedTo(costOwnerNotification);

            MapEmailNotificationObject(costOwnerNotification.Object, cost, costOwner);
            PopulateOtherFields(costOwnerNotification, core.Constants.EmailNotificationParents.CostOwner, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(costOwnerNotification.Object, cost.Id);

            notifications.Add(costOwnerNotification);

            return(notifications);
        }
示例#22
0
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(dataAccess.Entity.Cost cost,
                                                                                                      CostNotificationUsers costUsers, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();
            var approvals     = await _approvalService.GetApprovalsByCostStageRevisionId(costStageRevision.Id);

            if (approvals == null)
            {
                //No approvals set
                return(notifications);
            }

            await BuildPendingTechnicalApprovalNotification(notifications, approvals, cost, costUsers, costUsers.CostOwner, costStageRevision, timestamp);

            return(notifications);
        }
示例#23
0
        private async Task BuildPendingTechnicalApprovalNotification(List <EmailNotificationMessage <CostNotificationObject> > notifications,
                                                                     List <Approval> approvals, dataAccess.Entity.Cost cost, CostNotificationUsers costUsers,
                                                                     CostUser costOwner, CostStageRevision costStageRevision, DateTime timestamp)
        {
            const string actionType         = core.Constants.EmailNotificationActionType.TechnicalApproverSendReminder;
            var          technicalApprovals = approvals.Where(a => a.Type == ApprovalType.IPM).ToArray();

            foreach (var technicalApproval in technicalApprovals)
            {
                foreach (var approvalMember in technicalApproval.ApprovalMembers)
                {
                    //Send notifications to Technical Approver
                    var approvalCostUser = approvalMember.CostUser;
                    var technicalApproverNotification = new EmailNotificationMessage <CostNotificationObject>(actionType,
                                                                                                              approvalCostUser.GdamUserId);
                    AddSharedTo(technicalApproverNotification);

                    MapEmailNotificationObject(technicalApproverNotification.Object, cost, costOwner, approvalCostUser);
                    PopulateOtherFields(technicalApproverNotification, core.Constants.EmailNotificationParents.TechnicalApprover, timestamp, cost.Id, costStageRevision.Id, approvalCostUser);
                    await PopulateMetadata(technicalApproverNotification.Object, cost.Id);

                    notifications.Add(technicalApproverNotification);
                }
            }
        }
示例#24
0
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(dataAccess.Entity.Cost cost,
                                                                                                      CostNotificationUsers costUsers, string approverName, string approvalType, string comments, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var          notifications = new List <EmailNotificationMessage <CostNotificationObject> >();
            const string actionType    = core.Constants.EmailNotificationActionType.Rejected;

            //Cost Owner
            var costOwner  = costUsers.CostOwner;
            var recipients = new List <string> {
                costOwner.GdamUserId
            };

            recipients.AddRange(costUsers.Watchers);

            var costOwnerNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, recipients);

            AddSharedTo(costOwnerNotification);
            MapEmailNotificationObject(costOwnerNotification.Object, cost, costOwner);
            PopulateOtherFields(costOwnerNotification, core.Constants.EmailNotificationParents.CostOwner, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(costOwnerNotification.Object, cost.Id);

            var obj      = costOwnerNotification.Object;
            var approver = obj.Approver;

            approver.Name = approverName;
            approver.Type = approvalType;
            obj.Comments  = comments;

            notifications.Add(costOwnerNotification);

            if (ShouldNotifyInsuranceUsers(costUsers))
            {
                var insuranceUserNotification = new EmailNotificationMessage <CostNotificationObject>(actionType, costUsers.InsuranceUsers);
                AddSharedTo(insuranceUserNotification);
                MapEmailNotificationObject(insuranceUserNotification.Object, cost, costOwner);
                PopulateOtherFields(insuranceUserNotification, core.Constants.EmailNotificationParents.InsuranceUser, timestamp, cost.Id, costStageRevision.Id);
                await PopulateMetadata(insuranceUserNotification.Object, cost.Id);

                obj           = insuranceUserNotification.Object;
                approver      = obj.Approver;
                approver.Name = approverName;
                approver.Type = approvalType;
                obj.Comments  = comments;

                notifications.Add(insuranceUserNotification);
            }
            await AddFinanceManagerNotification(actionType, cost, costUsers, costStageRevision, timestamp, notifications);

            return(notifications);
        }
示例#25
0
        internal async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > Build(dataAccess.Entity.Cost cost,
                                                                                                      CostNotificationUsers costUsers, string requestedByName, CostStageRevision costStageRevision, DateTime timestamp)
        {
            var notifications = new List <EmailNotificationMessage <CostNotificationObject> >();

            //Admin Notification - one notification object for all admins
            var adminIds = costUsers.ClientAdmins.Select(x => x.GdamUserId).ToList();

            var adminNotification = new EmailNotificationMessage <CostNotificationObject>(core.Constants.EmailNotificationActionType.ReopenRequested, adminIds);

            AddSharedTo(adminNotification);
            MapEmailNotificationObject(adminNotification.Object, cost, costUsers.CostOwner);
            PopulateOtherFields(adminNotification, string.Empty, timestamp, cost.Id, costStageRevision.Id);
            await PopulateMetadata(adminNotification.Object, cost.Id);

            adminNotification.Object.Approver.Name = requestedByName;
            notifications.Add(adminNotification);

            await AddFinanceManagerNotification(core.Constants.EmailNotificationActionType.CostStatus, cost, costUsers, costStageRevision, timestamp, notifications);

            return(notifications);
        }
示例#26
0
        public async Task <bool> CostHasBeenSubmitted(Guid costId, Guid userId)
        {
            if (costId == Guid.Empty)
            {
                return(false);
            }

            if (userId == Guid.Empty)
            {
                return(false);
            }

            var cost = await _efContext.Cost
                       .Include(c => c.LatestCostStageRevision)
                       .ThenInclude(csr => csr.CostStage)
                       .ThenInclude(cs => cs.CostStageRevisions)
                       .Include(c => c.LatestCostStageRevision)
                       .Include(c => c.CostStages)
                       .ThenInclude(cs => cs.CostStageRevisions)
                       .ThenInclude(csr => csr.Approvals)
                       .ThenInclude(a => a.ApprovalMembers)
                       .ThenInclude(am => am.CostUser)
                       .Include(c => c.Project)
                       .ThenInclude(p => p.Brand)
                       .IncludeCostOwner()
                       .FirstOrDefaultAsync(c => c.Id == costId);

            var latestRevision = cost.LatestCostStageRevision;
            var costOwner      = cost.Owner;
            var insuranceUsers = await _costUserService.GetInsuranceUsers(costOwner.Agency);

            var financeManagementUsers = await _costUserService.GetFinanceManagementUsers(cost.UserGroups, Constants.BudgetRegion.NorthAmerica);

            var costUsers = new CostNotificationUsers
            {
                CostOwner              = cost.Owner,
                InsuranceUsers         = insuranceUsers,
                FinanceManagementUsers = financeManagementUsers
            };
            var notificationMessages = await _emailNotificationBuilder.BuildCostSubmittedNotification(costUsers, cost, latestRevision, DateTime.UtcNow);

            var notifications = notificationMessages.ToList();

            //Upon Submit the next stage might be PendingTechnicalApproval or PendingBrandApproval or Approved for FA.
            var nextStatus = cost.Status;

            switch (nextStatus)
            {
            case CostStageRevisionStatus.Approved:
                // The Cost is auto approved when the budget does not change between cost stages.
                await BuildAutoApprovedNotification(costUsers, cost, latestRevision, notifications);

                break;

            case CostStageRevisionStatus.PendingBrandApproval:
                var pendingBrandNotifications = await _emailNotificationBuilder.BuildPendingBrandApprovalNotification(costUsers, cost, latestRevision, DateTime.UtcNow);

                notifications.AddRange(pendingBrandNotifications);

                //Enqueue reminder for the brand approvers.
                await _reminderService.CreateNew(cost.Id, DateTime.UtcNow.AddDays(1));

                break;

            case CostStageRevisionStatus.PendingTechnicalApproval:
                var pendingTechnicalNotifications = await _emailNotificationBuilder.BuildPendingTechnicalApprovalNotification(costUsers, cost, latestRevision, DateTime.UtcNow);

                notifications.AddRange(pendingTechnicalNotifications);
                break;

                //ADC-2698 comment this line of code to take off the feature from release 1.9.4
                //    //Enqueue reminder for the technical approvers.
                //    await _reminderService.CreateNew(cost.Id, DateTime.UtcNow.AddDays(2));
                //    break;
            }

            //Notify previous approvers if they've been removed
            if (cost.HasPreviousRevision())
            {
                var previousRevision   = cost.GetPreviousRevision();
                var previousRevisionId = previousRevision.Id;
                //Get the approvers for the previous revision
                previousRevision.Approvals = await _approvalService.GetApprovalsByCostStageRevisionId(previousRevisionId);

                var csrAnalyser = new CostStageRevisionAnalyser();
                IEnumerable <ApprovalMember> removedApprovers = csrAnalyser.GetRemovedApprovers(previousRevision, latestRevision);

                var removedApproverNotifications = await _emailNotificationBuilder.BuildPreviousApproverNotification(costUsers,
                                                                                                                     removedApprovers, cost, previousRevision, DateTime.UtcNow);

                notifications.AddRange(removedApproverNotifications);
            }

            bool sent = false;

            foreach (var message in notifications)
            {
                sent = await _paperPusherClient.SendMessage(message);
            }

            return(sent);
        }
示例#27
0
        public async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > BuildCostReminderNotification(CostNotificationUsers costUsers,
                                                                                                                            Cost cost, CostStageRevision costStageRevision, DateTime timestamp)
        {
            if (cost == null)
            {
                Logger.Error("Failed to create an email notification because cost is null.");
                throw new ArgumentNullException(nameof(cost));
            }

            if (costStageRevision == null)
            {
                Logger.Error("Failed to create an email notification because costStageRevision is null.");
                throw new ArgumentNullException(nameof(costStageRevision));
            }

            if (costUsers == null)
            {
                Logger.Error("Failed to create an email notification because costUsers is null.");
                throw new ArgumentNullException(nameof(costUsers));
            }

            if (timestamp == DateTime.MinValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MinValue");
                throw new ArgumentException("Param cannot be DateTime.MinValue", nameof(timestamp));
            }

            if (timestamp == DateTime.MaxValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MaxValue");
                throw new ArgumentException("Param cannot be DateTime.MaxValue", nameof(timestamp));
            }

            //ADC-2698 send notification to technical approver
            var notificationMessages = new List <EmailNotificationMessage <CostNotificationObject> >();

            if (costStageRevision.Status == CostStageRevisionStatus.PendingTechnicalApproval)
            {
                var pendingTechnicalNotificationBuilder = new ReminderPendingTechnicalNotificationBuilder(_mapper,
                                                                                                          _uriHelper, _costStageRevisionService, _metadataProviderService, _approvalService, _appSettings, _efContext, _regionsService);
                notificationMessages.AddRange(await pendingTechnicalNotificationBuilder.Build(cost, costUsers, costStageRevision, timestamp));
            }
            else if (costStageRevision.Status == CostStageRevisionStatus.PendingBrandApproval)
            {
                var pendingBrandNotificationBuilder = new ReminderPendingBrandNotificationBuilder(_mapper,
                                                                                                  _uriHelper, _costStageRevisionService, _approvalService, _metadataProviderService, _regionsService, _appSettings, _efContext);
                notificationMessages.AddRange(await pendingBrandNotificationBuilder.Build(cost, costUsers, costStageRevision, timestamp));
            }

            return(notificationMessages);
        }
示例#28
0
        public async Task <bool> CostOwnerHasChanged(Guid costId, Guid userId, Guid previousOwnerId)
        {
            if (costId == Guid.Empty)
            {
                return(false);
            }

            if (userId == Guid.Empty)
            {
                return(false);
            }

            if (previousOwnerId == Guid.Empty)
            {
                return(false);
            }

            var cost = await _efContext.Cost
                       .Include(c => c.LatestCostStageRevision)
                       .ThenInclude(csr => csr.CostStage)
                       .ThenInclude(cs => cs.CostStageRevisions)
                       .Include(c => c.Project)
                       .ThenInclude(p => p.Brand)
                       .IncludeCostOwner()
                       .FirstOrDefaultAsync(c => c.Id == costId);

            var latestRevision = cost.LatestCostStageRevision;
            var costOwner      = cost.Owner;
            var previousOwner  = await _efContext.CostUser.FirstOrDefaultAsync(x => x.Id == previousOwnerId);

            var changeApprover = await _efContext.CostUser.FirstOrDefaultAsync(x => x.Id == userId);

            var insuranceUsers = await _costUserService.GetInsuranceUsers(costOwner.Agency);

            var watchers = await GetWatchers(costId);

            var approvals = _approvalService.GetApprovalsByCostStageRevisionId(latestRevision.Id).Result;
            var approvers = approvals?.SelectMany(a => a.ApprovalMembers
                                                  .Where(m => m.CostUser.Email != core.Builders.Response.ApprovalMemberModel.BrandApprovalUserEmail)
                                                  .Select(m => m.CostUser.GdamUserId))
                            ?? new List <string>();

            var costUsers = new CostNotificationUsers
            {
                CostOwner      = cost.Owner,
                InsuranceUsers = insuranceUsers,
                Watchers       = watchers,
                Approvers      = approvers,
            };
            var notificationMessages = await _emailNotificationBuilder.BuildCostOwnerChangedNotification(costUsers, cost, latestRevision, DateTime.UtcNow, changeApprover, previousOwner);

            var notifications = notificationMessages.ToList();

            bool sent = false;

            foreach (var message in notifications)
            {
                sent = await _paperPusherClient.SendMessage(message);
            }

            return(sent);
        }
示例#29
0
        public async Task <IEnumerable <EmailNotificationMessage <CostNotificationObject> > > BuildCostSubmittedNotification(CostNotificationUsers costUsers,
                                                                                                                             Cost cost, CostStageRevision costStageRevision, DateTime timestamp)
        {
            if (cost == null)
            {
                Logger.Error("Failed to create an email notification because cost is null.");
                throw new ArgumentNullException(nameof(cost));
            }

            if (costStageRevision == null)
            {
                Logger.Error("Failed to create an email notification because costStageRevision is null.");
                throw new ArgumentNullException(nameof(costStageRevision));
            }

            if (costUsers == null)
            {
                Logger.Error("Failed to create an email notification because costUsers is null.");
                throw new ArgumentNullException(nameof(costUsers));
            }

            if (timestamp == DateTime.MinValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MinValue");
                throw new ArgumentException("Param cannot be DateTime.MinValue", nameof(timestamp));
            }

            if (timestamp == DateTime.MaxValue)
            {
                Logger.Error("Failed to create an email notification because system timestamp provided was DateTime.MaxValue");
                throw new ArgumentException("Param cannot be DateTime.MaxValue", nameof(timestamp));
            }

            var submittedNotificationBuilder = new SubmittedNotificationBuilder(_mapper,
                                                                                _uriHelper, _costStageRevisionService, _metadataProviderService, _appSettings, _efContext, _regionsService);

            return(await submittedNotificationBuilder.Build(cost, costUsers, costStageRevision, timestamp));
        }
        private async Task ProcessReminders(StringBuilder logMessages)
        {
            try
            {
                _emailNotificationReminderService.ReminderUpdateToSending();

                List <EmailNotificationReminder> sentReminders = new List <EmailNotificationReminder>();
                Dictionary <EmailNotificationReminder, EmailReminderStatus> updateReminders = new Dictionary <EmailNotificationReminder, EmailReminderStatus>();
                var remindingEmails = await _emailNotificationReminderService.GetEmailNotificationReminderByStatus(EmailReminderStatus.Reminding, 10);

                List <CostStageRevisionStatus> invalidStatuses = new List <CostStageRevisionStatus>();
                invalidStatuses.Add(CostStageRevisionStatus.PendingBrandApproval);
                invalidStatuses.Add(CostStageRevisionStatus.PendingTechnicalApproval);

                List <CostStageRevisionStatus> approvedStatuses = new List <CostStageRevisionStatus>();
                approvedStatuses.Add(CostStageRevisionStatus.Approved);

                foreach (var reminder in remindingEmails)
                {
                    logMessages.AppendLine($"START: sending reminder for cost: {reminder.CostId}");

                    //Get the cost
                    var cost = await _efContext.Cost
                               .AsNoTracking()
                               .Include(c => c.LatestCostStageRevision)
                               .ThenInclude(csr => csr.CostStage)
                               .Include(c => c.Project)
                               .ThenInclude(p => p.Brand)
                               .Include(c => c.Owner)
                               .ThenInclude(o => o.Agency)
                               .ThenInclude(a => a.Country)
                               .FirstAsync(c => c.Id == reminder.CostId);

                    var users = new CostNotificationUsers
                    {
                        CostOwner = cost.Owner
                    };

                    if (approvedStatuses.Contains(cost.LatestCostStageRevision.Status))
                    {
                        updateReminders.Add(reminder, EmailReminderStatus.Reminded);
                    }
                    else if (invalidStatuses.Contains(cost.LatestCostStageRevision.Status))
                    {
                        //Build
                        var notificationMessages = await _emailNotificationBuilder.BuildCostReminderNotification(users, cost, cost.LatestCostStageRevision, DateTime.UtcNow);

                        //Send
                        bool sent = false;
                        foreach (var notification in notificationMessages)
                        {
                            sent = await _paperpusherClient.SendMessage(notification);

                            if (!sent)
                            {
                                logMessages.AppendLine($"[Error] Failed to send notification for cost: {reminder.CostId}");
                            }
                        }

                        //Update
                        if (sent)
                        {
                            sentReminders.Add(reminder);
                            logMessages.AppendLine($"FINISHED: Sent reminder for cost: {reminder.CostId}");
                        }
                    }
                    else
                    {
                        updateReminders.Add(reminder, EmailReminderStatus.Cancelled);
                    }
                }
                _emailNotificationReminderService.ReminderUpdateToSent(sentReminders);
                _emailNotificationReminderService.ReminderUpdateToSent(updateReminders);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }
        }