예제 #1
0
파일: Jobs.cs 프로젝트: kjb7749/testImport
        ///<summary>Efficiently queries DB to fill all in memory lists for all jobs passed in.</summary>
        public static void FillInMemoryLists(List <Job> listJobsAll)
        {
            //No need for remoting call here.
            List <long> jobNums = listJobsAll.Select(x => x.JobNum).ToList();
            Dictionary <long, List <JobLink> >   listJobLinksAll   = JobLinks.GetJobLinksForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            Dictionary <long, List <JobNote> >   listJobNotesAll   = JobNotes.GetJobNotesForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            Dictionary <long, List <JobReview> > listJobReviewsAll = JobReviews.GetJobReviewsForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            Dictionary <long, List <JobQuote> >  listJobQuotesAll  = JobQuotes.GetJobQuotesForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            Dictionary <long, List <JobLog> >    listJobLogsAll    = JobLogs.GetJobLogsForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());

            for (int i = 0; i < listJobsAll.Count; i++)
            {
                Job job = listJobsAll[i];
                if (!listJobLinksAll.TryGetValue(job.JobNum, out job.ListJobLinks))
                {
                    job.ListJobLinks = new List <JobLink>();                 //empty list if not found
                }
                if (!listJobNotesAll.TryGetValue(job.JobNum, out job.ListJobNotes))
                {
                    job.ListJobNotes = new List <JobNote>();                 //empty list if not found
                }
                if (!listJobReviewsAll.TryGetValue(job.JobNum, out job.ListJobReviews))
                {
                    job.ListJobReviews = new List <JobReview>();                 //empty list if not found
                }
                if (!listJobQuotesAll.TryGetValue(job.JobNum, out job.ListJobQuotes))
                {
                    job.ListJobQuotes = new List <JobQuote>();                 //empty list if not found
                }
                if (!listJobLogsAll.TryGetValue(job.JobNum, out job.ListJobLogs))
                {
                    job.ListJobLogs = new List <JobLog>();                 //empty list if not found
                }
            }
        }
예제 #2
0
        public static JobLog MakeLogEntryForNote(Job job, JobNote jobNoteNew, JobNote jobNoteOld)
        {
            string note = "";

            if (jobNoteNew == null)
            {
                note = "Discussion note by user: "******" on " + jobNoteOld.DateTimeNote.ToString() + " was deleted.\r\nMessage Text:\r\n"
                       + jobNoteOld.Note;
            }
            else
            {
                note = "Discussion note by user: "******" on " + jobNoteOld.DateTimeNote.ToString() + " was edited.";
            }
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = note,
                TimeEstimate    = TimeSpan.FromHours(job.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #3
0
        ///<summary>Will return null if no changes were made.</summary>
        public static bool MakeLogEntryForEstimateChange(Job jobNew, Job jobOld, string note)
        {
            JobLog jobLog = new JobLog()
            {
                JobNum          = jobNew.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = jobNew.UserNumExpert,
                UserNumEngineer = jobNew.UserNumEngineer,
                Title           = jobNew.Title,
                Description     = "",
                TimeEstimate    = jobNew.TimeEstimateConcept + jobNew.TimeEstimateWriteup + jobNew.TimeEstimateDevelopment + jobNew.TimeEstimateReview + jobNew.TimeEstimateReview
            };            //Add review twice

            if (jobOld.HoursEstimateConcept != jobNew.HoursEstimateConcept)
            {
                if (!string.IsNullOrEmpty(jobLog.Description))
                {
                    jobLog.Description += "\r\n";
                }
                jobLog.Description += "Concept Estimate Changed From " + jobOld.HoursEstimateConcept + " hours To " + jobNew.HoursEstimateConcept + " hours.";
            }
            if (jobOld.HoursEstimateWriteup != jobNew.HoursEstimateWriteup)
            {
                if (!string.IsNullOrEmpty(jobLog.Description))
                {
                    jobLog.Description += "\r\n";
                }
                jobLog.Description += "Writeup Estimate Changed From " + jobOld.HoursEstimateWriteup + " hours To " + jobNew.HoursEstimateWriteup + " hours.";
            }
            if (jobOld.HoursEstimateDevelopment != jobNew.HoursEstimateDevelopment)
            {
                if (!string.IsNullOrEmpty(jobLog.Description))
                {
                    jobLog.Description += "\r\n";
                }
                jobLog.Description += "Development Estimate Changed From " + jobOld.HoursEstimateDevelopment + " hours To " + jobNew.HoursEstimateDevelopment + " hours.";
            }
            if (jobOld.HoursEstimateReview != jobNew.HoursEstimateReview)
            {
                if (!string.IsNullOrEmpty(jobLog.Description))
                {
                    jobLog.Description += "\r\n";
                }
                jobLog.Description += "Review Estimate Changed From " + jobOld.HoursEstimateReview + " hours To " + jobNew.HoursEstimateReview + " hours.";
            }
            if (!String.IsNullOrEmpty(note))
            {
                jobLog.Description += "\r\nReason: " + note;
            }
            if (string.IsNullOrEmpty(jobLog.Description))
            {
                return(false);
            }
            JobLogs.Insert(jobLog);
            return(true);
        }
예제 #4
0
        public static JobLog MakeLogEntryForView(Job job)
        {
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = "Job Viewed"
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #5
0
        public static JobLog MakeLogEntryForTitleChange(Job job, string oldTitle, string newTitle)
        {
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = newTitle,
                Description     = "Job Title Changed From\r\n" + oldTitle + "\r\nTo\r\n" + newTitle + "."
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #6
0
        public static JobLog MakeLogEntryForEstimateChange(Job job, double oldHours, double newHours)
        {
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = "Job Estimate Changed From " + oldHours + " hours To " + newHours + " hours."
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #7
0
        public static JobLog MakeLogEntryForSaveCommit(Job job)
        {
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = "Job Save Committed",
                TimeEstimate    = TimeSpan.FromHours(job.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #8
0
        public static JobLog MakeLogEntryForActive(Job job, long userNum, bool isActive)
        {
            string note   = $@"Job was set to {(isActive?"active":"inactive")}";
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = note,
                TimeEstimate    = TimeSpan.FromHours(job.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #9
0
        public static JobLog MakeLogEntryForProject(Job jobNew, Job jobOld)
        {
            string note   = $@"Job Project was manually changed from {jobOld.PatternReviewProject.ToString()} to {jobNew.PatternReviewProject.ToString()}.";
            JobLog jobLog = new JobLog()
            {
                JobNum          = jobOld.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = jobOld.UserNumExpert,
                UserNumEngineer = jobOld.UserNumEngineer,
                Title           = jobOld.Title,
                Description     = note,
                TimeEstimate    = TimeSpan.FromHours(jobNew.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            JobNotifications.UpsertAllNotifications(jobNew, Security.CurUser.UserNum, JobNotificationChanges.PhaseChange);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #10
0
파일: Jobs.cs 프로젝트: kjb7749/testImport
 ///<summary>You must surround with a try-catch when calling this method.  Deletes one job from the database.
 ///Also deletes all JobLinks, Job Events, and Job Notes associated with the job.  Jobs that have reviews or quotes on them may not be deleted and will throw an exception.</summary>
 public static void Delete(long jobNum)
 {
     if (RemotingClient.RemotingRole == RemotingRole.ClientWeb)
     {
         Meth.GetVoid(MethodBase.GetCurrentMethod(), jobNum);
         return;
     }
     if (JobReviews.GetForJob(jobNum).Count > 0 || JobQuotes.GetForJob(jobNum).Count > 0)
     {
         throw new Exception(Lans.g("Jobs", "Not allowed to delete a job that has attached reviews or quotes.  Set the status to deleted instead."));               //The exception is caught in FormJobEdit.
     }
     //JobReviews.DeleteForJob(jobNum);//do not delete, blocked above
     //JobQuotes.DeleteForJob(jobNum);//do not delete, blocked above
     JobLinks.DeleteForJob(jobNum);
     JobLogs.DeleteForJob(jobNum);
     JobNotes.DeleteForJob(jobNum);
     Crud.JobCrud.Delete(jobNum);             //Finally, delete the job itself.
 }
예제 #11
0
        public static JobLog MakeLogEntryForCategory(Job jobNew, Job jobOld)
        {
            string note   = "Job Category was changed from " + jobOld.Category.ToString() + " to " + jobNew.Category.ToString() + ".";
            JobLog jobLog = new JobLog()
            {
                JobNum          = jobOld.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = jobOld.UserNumExpert,
                UserNumEngineer = jobOld.UserNumEngineer,
                Title           = jobOld.Title,
                Description     = note,
                TimeEstimate    = TimeSpan.FromHours(jobNew.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            JobNotifications.UpsertAllNotifications(jobNew, Security.CurUser.UserNum, JobNotificationChanges.CategoryChange);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #12
0
        ///<summary>Makes a joblog entry for the passed in job, queries the database for the joblog that was just created and then returns it.
        ///Does not make a joblog for Complete or Cancelled jobs.  Instead, simply returns null indicating that nothing happened.</summary>
        public static JobLog MakeLogEntryForView(Job job)
        {
            if (job.PhaseCur.In(JobPhase.Complete, JobPhase.Cancelled))
            {
                return(null);
            }
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = "Job Viewed",
                TimeEstimate    = TimeSpan.FromHours(job.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #13
0
        ///<summary>Returns the jobs new list of JobActiveLinks</summary>
        public static List <JobActiveLink> UpsertLink(Job job, Job jobOld, long userNum, bool isActive)
        {
            //No need for remoting call here.
            List <JobActiveLink> listJobActiveLinks = job.ListJobActiveLinks;

            if (!jobOld.PhaseCur.In(JobPhase.Cancelled, JobPhase.Complete, JobPhase.Documentation) &&
                job.PhaseCur.In(JobPhase.Cancelled, JobPhase.Complete, JobPhase.Documentation))
            {
                EndForJobNum(job.JobNum);
                return(new List <JobActiveLink>());
            }
            JobActiveLink activeLink = listJobActiveLinks.FirstOrDefault(x => x.JobNum == job.JobNum && x.UserNum == userNum && x.DateTimeEnd == DateTime.MinValue);

            if (activeLink == null)
            {
                if (!isActive)
                {
                    return(listJobActiveLinks);
                }
                activeLink         = new JobActiveLink();
                activeLink.JobNum  = job.JobNum;
                activeLink.UserNum = userNum;
                Insert(activeLink);
            }
            else
            {
                if (isActive)
                {
                    return(listJobActiveLinks);
                }
                activeLink.DateTimeEnd = DateTime.Now;
                Update(activeLink);
            }
            //Only occurs if an actual db change happened for a single user
            //Mass delete does not make a log entry
            JobLogs.MakeLogEntryForActive(job, userNum, isActive);
            listJobActiveLinks.Add(activeLink);
            return(listJobActiveLinks);
        }
예제 #14
0
        public static JobLog MakeLogEntryForRequirementApproval(Job job)
        {
            List <JobRequirement> listRequirements = JsonConvert.DeserializeObject <List <JobRequirement> >(job.RequirementsJSON);
            string logText = "Requirements Approved\r\n";

            foreach (JobRequirement jobReq in listRequirements)
            {
                logText += "Requirement: " + jobReq.Description + "\r\n";
            }
            JobLog jobLog = new JobLog()
            {
                JobNum          = job.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = job.UserNumExpert,
                UserNumEngineer = job.UserNumEngineer,
                Title           = job.Title,
                Description     = logText,
                TimeEstimate    = TimeSpan.FromHours(job.HoursEstimate)
            };

            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #15
0
        ///<summary>Removes invalid jobs from the list of jobs passed in and then queries the DB to fill corresponding in-memory lists for remaining jobs.
        ///Set isForSearch true in order to only fill in-memory lists that are required for the Job Search window.</summary>
        public static void FillInMemoryLists(List <Job> listJobsAll, bool isForSearch = false)
        {
            //No need for remoting call here.
            listJobsAll.RemoveAll(x => x == null || x.JobNum == 0);
            List <long> jobNums = listJobsAll.Select(x => x.JobNum).ToList();
            Dictionary <long, List <JobLink> >         dictJobLinksAll         = new Dictionary <long, List <JobLink> >();
            Dictionary <long, List <JobNote> >         dictJobNotesAll         = new Dictionary <long, List <JobNote> >();
            Dictionary <long, List <JobReview> >       dictJobReviewsAll       = new Dictionary <long, List <JobReview> >();
            Dictionary <long, List <JobReview> >       dictJobTimeLogsAll      = new Dictionary <long, List <JobReview> >();
            Dictionary <long, List <JobQuote> >        dictJobQuotesAll        = new Dictionary <long, List <JobQuote> >();
            Dictionary <long, List <JobLog> >          dictJobLogsAll          = new Dictionary <long, List <JobLog> >();
            Dictionary <long, List <JobNotification> > dictJobNotificationsAll = new Dictionary <long, List <JobNotification> >();
            Dictionary <long, List <JobActiveLink> >   dictJobActiveLinksAll   = new Dictionary <long, List <JobActiveLink> >();

            //The job search only needs ListJobLinks and ListJobReviews to be filled.
            dictJobLinksAll   = JobLinks.GetJobLinksForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            dictJobReviewsAll = JobReviews.GetReviewsForJobs(jobNums.ToArray()).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            //Fill all other dictionaries when not filling in-memory lists for job search results (saves db calls and memory usage).
            if (!isForSearch)
            {
                dictJobNotesAll         = JobNotes.GetJobNotesForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
                dictJobTimeLogsAll      = JobReviews.GetTimeLogsForJobs(jobNums.ToArray()).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
                dictJobQuotesAll        = JobQuotes.GetJobQuotesForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
                dictJobLogsAll          = JobLogs.GetJobLogsForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
                dictJobNotificationsAll = JobNotifications.GetNotificationsForJobs(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
                dictJobActiveLinksAll   = JobActiveLinks.GetJobActiveLinksForJobNums(jobNums).GroupBy(x => x.JobNum).ToDictionary(x => x.Key, x => x.ToList());
            }
            foreach (Job job in listJobsAll)
            {
                if (!dictJobLinksAll.TryGetValue(job.JobNum, out job.ListJobLinks))
                {
                    job.ListJobLinks = new List <JobLink>();                 //empty list if not found
                }
                if (!dictJobNotesAll.TryGetValue(job.JobNum, out job.ListJobNotes))
                {
                    job.ListJobNotes = new List <JobNote>();                 //empty list if not found
                }
                if (!dictJobReviewsAll.TryGetValue(job.JobNum, out job.ListJobReviews))
                {
                    job.ListJobReviews = new List <JobReview>();                 //empty list if not found
                }
                if (!dictJobTimeLogsAll.TryGetValue(job.JobNum, out job.ListJobTimeLogs))
                {
                    job.ListJobTimeLogs = new List <JobReview>();                 //empty list if not found
                }
                if (!dictJobQuotesAll.TryGetValue(job.JobNum, out job.ListJobQuotes))
                {
                    job.ListJobQuotes = new List <JobQuote>();                 //empty list if not found
                }
                if (!dictJobLogsAll.TryGetValue(job.JobNum, out job.ListJobLogs))
                {
                    job.ListJobLogs = new List <JobLog>();                 //empty list if not found
                }
                if (!dictJobNotificationsAll.TryGetValue(job.JobNum, out job.ListJobNotifications))
                {
                    job.ListJobNotifications = new List <JobNotification>();                 //empty list if not found
                }
                if (!dictJobActiveLinksAll.TryGetValue(job.JobNum, out job.ListJobActiveLinks))
                {
                    job.ListJobActiveLinks = new List <JobActiveLink>();                 //empty list if not found
                }
            }
        }
예제 #16
0
        /// <summary>Inserts log entry to DB and returns the resulting JobLog.</summary>
        public static JobLog MakeLogEntry(Job jobNew, Job jobOld, bool isManualLog = false)
        {
            if (jobNew == null)
            {
                return(null);               //should never happen
            }
            JobLog jobLog = new JobLog()
            {
                JobNum          = jobNew.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = jobNew.UserNumExpert,
                UserNumEngineer = jobNew.UserNumEngineer,
                Description     = ""
            };

            if (isManualLog)
            {
                jobLog.Description = "Manual \"last worked on\" update";
                JobLogs.Insert(jobLog);
                return(JobLogs.GetOne(jobLog.JobLogNum));               //to get new timestamp.
            }
            List <string> logDescriptions = new List <string>();

            if (jobOld.IsApprovalNeeded && !jobNew.IsApprovalNeeded)
            {
                if (jobOld.PhaseCur == JobPhase.Concept && (jobNew.PhaseCur == JobPhase.Definition || jobNew.PhaseCur == JobPhase.Development))
                {
                    logDescriptions.Add("Concept approved.");
                    jobLog.MainRTF          = jobNew.Implementation;
                    jobLog.RequirementsRTF += jobNew.Requirements;
                }
                if ((jobOld.PhaseCur == JobPhase.Concept || jobOld.PhaseCur == JobPhase.Definition) && jobNew.PhaseCur == JobPhase.Development)
                {
                    logDescriptions.Add("Job approved.");
                    jobLog.MainRTF          = jobNew.Implementation;
                    jobLog.RequirementsRTF += jobNew.Requirements;
                }
                if (jobOld.PhaseCur == JobPhase.Development && jobNew.PhaseCur == JobPhase.Development)
                {
                    logDescriptions.Add("Changes approved.");
                    jobLog.MainRTF          = jobNew.Implementation;
                    jobLog.RequirementsRTF += jobNew.Requirements;
                }
            }
            else if (jobNew.PhaseCur.In(JobPhase.Documentation, JobPhase.Complete) && !jobOld.PhaseCur.In(JobPhase.Documentation, JobPhase.Complete))
            {
                logDescriptions.Add("Job implemented.");
                jobLog.MainRTF         += jobNew.Implementation;
                jobLog.RequirementsRTF += jobNew.Requirements;
            }
            if (jobOld.PhaseCur > jobNew.PhaseCur && jobOld.PhaseCur != JobPhase.Cancelled)
            {
                logDescriptions.Add("Job Unapproved.");                //may be a chance for a false positive when using override permission.
            }
            if (jobOld.PhaseCur != JobPhase.Cancelled && jobNew.PhaseCur == JobPhase.Cancelled)
            {
                logDescriptions.Add("Job Cancelled.");                //may be a chance for a false positive when using override permission.
            }
            if (jobNew.UserNumExpert != jobOld.UserNumExpert)
            {
                logDescriptions.Add("Expert changed.");
            }
            if (jobNew.UserNumEngineer != jobOld.UserNumEngineer)
            {
                logDescriptions.Add("Engineer changed.");
            }
            if (jobOld.Requirements != jobNew.Requirements)
            {
                logDescriptions.Add("Job Requirements Changed.");
                jobLog.RequirementsRTF += jobNew.Requirements;
            }
            if (jobOld.Implementation != jobNew.Implementation)
            {
                logDescriptions.Add("Job Implementation Changed.");
                jobLog.MainRTF += jobNew.Implementation;
            }
            if (jobOld.Title != jobNew.Title)
            {
                logDescriptions.Add("Job Title Changed.");
            }
            jobLog.Title       = jobNew.Title;
            jobLog.Description = string.Join("\r\n", logDescriptions);
            JobLogs.Insert(jobLog);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }
예제 #17
0
        /// <summary>Inserts log entry to DB and returns the resulting JobLog.  Returns null if no log was needed.</summary>
        public static JobLog MakeLogEntry(Job jobNew, Job jobOld, bool isManualLog = false)
        {
            if (jobNew == null)
            {
                return(null);
            }
            JobNotificationChanges changes = new JobNotificationChanges();
            JobLog jobLog = new JobLog()
            {
                JobNum          = jobNew.JobNum,
                UserNumChanged  = Security.CurUser.UserNum,
                UserNumExpert   = jobNew.UserNumExpert,
                UserNumEngineer = jobNew.UserNumEngineer,
                Description     = "",
                TimeEstimate    = TimeSpan.FromHours(jobNew.HoursEstimate)
            };

            if (isManualLog)
            {
                jobLog.Description = "Manual \"last worked on\" update";
                JobLogs.Insert(jobLog);
                return(JobLogs.GetOne(jobLog.JobLogNum));               //to get new timestamp.
            }
            List <string> logDescriptions = new List <string>();

            if (jobOld.IsApprovalNeeded && !jobNew.IsApprovalNeeded)
            {
                if (jobOld.PhaseCur == JobPhase.Concept && (jobNew.PhaseCur == JobPhase.Definition || jobNew.PhaseCur == JobPhase.Development))
                {
                    logDescriptions.Add("Concept approved.");
                    jobLog.MainRTF          = jobNew.Implementation;
                    jobLog.RequirementsRTF += jobNew.Requirements;
                    changes = changes | JobNotificationChanges.ApprovalChange;
                }
                if ((jobOld.PhaseCur == JobPhase.Concept || jobOld.PhaseCur == JobPhase.Definition) && jobNew.PhaseCur == JobPhase.Development)
                {
                    logDescriptions.Add("Job approved.");
                    jobLog.MainRTF          = jobNew.Implementation;
                    jobLog.RequirementsRTF += jobNew.Requirements;
                    changes = changes | JobNotificationChanges.ApprovalChange;
                }
                if (jobOld.PhaseCur == JobPhase.Development && jobNew.PhaseCur == JobPhase.Development)
                {
                    logDescriptions.Add("Changes approved.");
                    jobLog.MainRTF          = jobNew.Implementation;
                    jobLog.RequirementsRTF += jobNew.Requirements;
                    changes = changes | JobNotificationChanges.ApprovalChange;
                }
            }
            else if (jobNew.PhaseCur.In(JobPhase.Documentation, JobPhase.Complete) && !jobOld.PhaseCur.In(JobPhase.Documentation, JobPhase.Complete))
            {
                logDescriptions.Add("Job implemented.");
                jobLog.MainRTF         += jobNew.Implementation;
                jobLog.RequirementsRTF += jobNew.Requirements;
            }
            if (jobOld.PhaseCur > jobNew.PhaseCur && jobOld.PhaseCur != JobPhase.Cancelled)
            {
                logDescriptions.Add("Job Unapproved.");                //may be a chance for a false positive when using override permission.
            }
            if (jobOld.PhaseCur != JobPhase.Cancelled && jobNew.PhaseCur == JobPhase.Cancelled)
            {
                logDescriptions.Add("Job Cancelled.");                //may be a chance for a false positive when using override permission.
            }
            if (jobNew.UserNumExpert != jobOld.UserNumExpert)
            {
                logDescriptions.Add("Expert changed.");
                changes = changes | JobNotificationChanges.ExpertChange;
            }
            if (jobNew.UserNumEngineer != jobOld.UserNumEngineer)
            {
                logDescriptions.Add("Engineer changed.");
                changes = changes | JobNotificationChanges.EngineerChange;
            }
            if (jobOld.Requirements != jobNew.Requirements)
            {
                logDescriptions.Add("Job Requirements Changed.");
                jobLog.RequirementsRTF += jobNew.Requirements;
                changes = changes | JobNotificationChanges.ConceptChange;
            }
            if (jobOld.Implementation != jobNew.Implementation)
            {
                logDescriptions.Add("Job Implementation Changed.");
                jobLog.MainRTF += jobNew.Implementation;
                changes         = changes | JobNotificationChanges.WriteupChange;
            }
            //Do not log RequirementsJSON changes here.
            //if(jobOld.RequirementsJSON!=jobNew.RequirementsJSON) {
            //	logDescriptions.Add("Job Requirements List Changed.");
            //	changes=changes|JobNotificationChanges.ConceptChange;
            //}
            if (jobOld.Title != jobNew.Title)
            {
                logDescriptions.Add("Job Title Changed.");
            }
            if (jobOld.HoursEstimate != jobNew.HoursEstimate)
            {
                logDescriptions.Add("Job Estimate Changed from " + jobOld.HoursEstimate.ToString() + " hour(s) to " + jobNew.HoursEstimate.ToString() + " hour(s).");
            }
            jobLog.Title       = jobNew.Title;
            jobLog.Description = string.Join("\r\n", logDescriptions);
            if (string.IsNullOrEmpty(jobLog.Description))
            {
                return(null);
            }
            JobLogs.Insert(jobLog);
            JobNotifications.UpsertAllNotifications(jobNew, Security.CurUser.UserNum, changes);
            return(JobLogs.GetOne(jobLog.JobLogNum));           //to get new timestamp.
        }