/// <summary>
        /// Execute method to write transaction to the database.
        /// </summary>
        public void Execute()
        {
            using (var rockContext = new RockContext())
            {
                var documentService    = new SignatureDocumentService(rockContext);
                var personAliasService = new PersonAliasService(rockContext);
                var appliesPerson      = personAliasService.GetPerson(AppliesToPersonAliasId);
                var assignedPerson     = personAliasService.GetPerson(AssignedToPersonAliasId);

                if (!documentService.Queryable().Any(d =>
                                                     d.SignatureDocumentTemplateId == SignatureDocumentTemplateId &&
                                                     d.AppliesToPersonAliasId.HasValue &&
                                                     d.AppliesToPersonAliasId.Value == AppliesToPersonAliasId &&
                                                     d.Status == SignatureDocumentStatus.Signed))
                {
                    var documentTypeService       = new SignatureDocumentTemplateService(rockContext);
                    var SignatureDocumentTemplate = documentTypeService.Get(SignatureDocumentTemplateId);

                    var errorMessages = new List <string>();
                    if (documentTypeService.SendDocument(SignatureDocumentTemplate, appliesPerson, assignedPerson, DocumentName, Email, out errorMessages))
                    {
                        rockContext.SaveChanges();
                    }
                }
            }
        }
        /// <summary>
        /// Called by the <see cref="IScheduler" /> when a <see cref="ITrigger" />
        /// fires that is associated with the <see cref="IJob" />.
        /// </summary>
        /// <param name="context">The execution context.</param>
        /// <remarks>
        /// The implementation may wish to set a  result object on the
        /// JobExecutionContext before this method exits.  The result itself
        /// is meaningless to Quartz, but may be informative to
        /// <see cref="IJobListener" />s or
        /// <see cref="ITriggerListener" />s that are watching the job's
        /// execution.
        /// </remarks>
        public virtual void Execute(IJobExecutionContext context)
        {
            JobDataMap dataMap    = context.JobDetail.JobDataMap;
            int        resendDays = dataMap.GetString("ResendInviteAfterNumberDays").AsIntegerOrNull() ?? 5;
            int        maxInvites = dataMap.GetString("MaxInvites").AsIntegerOrNull() ?? 2;
            int        checkDays  = dataMap.GetString("CheckForSignatureDays").AsIntegerOrNull() ?? 30;
            string     folderPath = System.Web.Hosting.HostingEnvironment.MapPath("~/App_Data/Cache/SignNow");

            var errorMessages         = new List <string>();
            int signatureRequestsSent = 0;
            int documentsUpdated      = 0;

            // Send documents
            using (var rockContext = new RockContext())
            {
                var maxInviteDate  = RockDateTime.Today.AddDays(0 - resendDays);
                var maxCheckDays   = RockDateTime.Today.AddDays(0 - checkDays);
                var docTypeService = new SignatureDocumentTemplateService(rockContext);
                var docService     = new SignatureDocumentService(rockContext);

                // Check for status updates
                foreach (var document in new SignatureDocumentService(rockContext).Queryable()
                         .Where(d =>
                                (
                                    d.Status == SignatureDocumentStatus.Sent ||
                                    (d.Status == SignatureDocumentStatus.Signed && !d.BinaryFileId.HasValue)
                                ) &&
                                d.LastInviteDate.HasValue &&
                                d.LastInviteDate.Value > maxCheckDays)
                         .ToList())
                {
                    var updateErrorMessages = new List <string>();
                    var status       = document.Status;
                    int?binaryFileId = document.BinaryFileId;
                    if (docTypeService.UpdateDocumentStatus(document, folderPath, out updateErrorMessages))
                    {
                        if (status != document.Status || !binaryFileId.Equals(document.BinaryFileId))
                        {
                            rockContext.SaveChanges();
                            documentsUpdated++;
                        }
                    }
                    else
                    {
                        errorMessages.AddRange(updateErrorMessages);
                    }
                }

                // Send any needed signature requests
                var docsSent = new Dictionary <int, List <int> >();
                foreach (var gm in new GroupMemberService(rockContext).Queryable()
                         .Where(m =>
                                m.GroupMemberStatus == GroupMemberStatus.Active &&
                                m.Group.IsActive && !m.Group.IsArchived &&
                                m.Person.Email != null &&
                                m.Person.Email != "" &&
                                m.Group.RequiredSignatureDocumentTemplate != null &&
                                !m.Group.RequiredSignatureDocumentTemplate.Documents.Any(d =>
                                                                                         d.AppliesToPersonAlias.PersonId == m.PersonId &&
                                                                                         d.Status == SignatureDocumentStatus.Signed
                                                                                         )
                                )
                         .Select(m => new
                {
                    GroupName = m.Group.Name,
                    Person = m.Person,
                    DocumentType = m.Group.RequiredSignatureDocumentTemplate
                })
                         .ToList())
                {
                    if (docsSent.ContainsKey(gm.Person.Id))
                    {
                        if (docsSent[gm.Person.Id].Contains(gm.DocumentType.Id))
                        {
                            continue;
                        }
                        else
                        {
                            docsSent[gm.Person.Id].Add(gm.DocumentType.Id);
                        }
                    }
                    else
                    {
                        docsSent.Add(gm.Person.Id, new List <int> {
                            gm.DocumentType.Id
                        });
                    }

                    var document = docService.Queryable()
                                   .Where(d =>
                                          d.SignatureDocumentTemplateId == gm.DocumentType.Id &&
                                          d.AppliesToPersonAlias.PersonId == gm.Person.Id &&
                                          d.AssignedToPersonAlias.PersonId == gm.Person.Id &&
                                          d.Status != SignatureDocumentStatus.Signed
                                          )
                                   .OrderByDescending(d => d.CreatedDateTime)
                                   .FirstOrDefault();

                    if (document == null || (document.InviteCount < maxInvites && document.LastInviteDate < maxInviteDate))
                    {
                        string documentName = string.Format("{0}_{1}", gm.GroupName.RemoveSpecialCharacters(), gm.Person.FullName.RemoveSpecialCharacters());

                        var sendErrorMessages = new List <string>();
                        if (document != null)
                        {
                            docTypeService.SendDocument(document, gm.Person.Email, out sendErrorMessages);
                        }
                        else
                        {
                            docTypeService.SendDocument(gm.DocumentType, gm.Person, gm.Person, documentName, gm.Person.Email, out sendErrorMessages);
                        }

                        if (!errorMessages.Any())
                        {
                            rockContext.SaveChanges();
                            signatureRequestsSent++;
                        }
                        else
                        {
                            errorMessages.AddRange(sendErrorMessages);
                        }
                    }
                }
            }

            if (errorMessages.Any())
            {
                throw new Exception("One or more exceptions occurred processing signature documents..." + Environment.NewLine + errorMessages.AsDelimited(Environment.NewLine));
            }

            context.Result = string.Format("{0} signature requests sent; {1} existing document's status updated", signatureRequestsSent, documentsUpdated);
        }