Browse Source

Added scripts to timberline timesheet export

Kenric Nugteren 1 year ago
parent
commit
139952eead
1 changed files with 228 additions and 120 deletions
  1. 228 120
      prs.shared/Posters/Timberline/TimesheetTimberlinePoster.cs

+ 228 - 120
prs.shared/Posters/Timberline/TimesheetTimberlinePoster.cs

@@ -6,7 +6,8 @@ using InABox.Core.Postable;
 using InABox.Poster.Timberline;
 using InABox.Scripting;
 using Microsoft.Win32;
-using NPOI.SS.UserModel;
+using PRS.Shared.TimeSheetTimberline;
+using Syncfusion.Windows.Shared;
 using System;
 using System.Collections.Generic;
 using System.Globalization;
@@ -14,9 +15,132 @@ using System.IO;
 using System.Linq;
 using System.Text;
 using System.Threading.Tasks;
+using System.Windows.Input;
 
 namespace PRS.Shared
 {
+    namespace TimeSheetTimberline
+    {
+        public class ActivityBlock
+        {
+            public Guid Activity { get; set; }
+
+            public TimeSpan Start { get; set; }
+
+            public TimeSpan Finish { get; set; }
+
+            public TimeSheet TimeSheet { get; set; }
+
+            public TimeSpan Duration => Finish - Start;
+
+            public ActivityBlock(Assignment assignment, TimeSheet sheet)
+            {
+                Activity = assignment.ActivityLink.ID != Guid.Empty
+                    ? assignment.ActivityLink.ID
+                    : sheet.ActivityLink.ID;
+
+                Start = assignment.EffectiveStartTime();
+                Finish = assignment.EffectiveFinishTime();
+                TimeSheet = sheet;
+            }
+
+            public ActivityBlock(TimeSheet sheet)
+            {
+                Activity = sheet.ActivityLink.ID;
+                Start = sheet.ApprovedStart;
+                Finish = sheet.ApprovedFinish;
+                TimeSheet = sheet;
+            }
+
+            public ActivityBlock(TimeSheet sheet, TimeSpan start, TimeSpan finish)
+            {
+                Activity = sheet.ActivityLink.ID;
+                Start = start;
+                Finish = finish;
+                TimeSheet = sheet;
+            }
+
+            public ActivityBlock Chop(TimeSheet sheet)
+            {
+                if (Start < sheet.ApprovedStart)
+                {
+                    Start = sheet.ApprovedStart;
+                }
+                if (Finish > sheet.ApprovedFinish)
+                {
+                    Finish = sheet.ApprovedFinish;
+                }
+                return this;
+            }
+
+            public bool ContainedInTimeSheet(TimeSheet sheet) =>
+                Start < sheet.ApprovedFinish && Finish > sheet.ApprovedStart;
+
+            public bool IntersectsWith(ActivityBlock other)
+            {
+                return Start < other.Finish && Finish > other.Start;
+            }
+        }
+
+        public interface IBlock
+        {
+            string Job { get; set; }
+
+            string Extra { get; set; }
+
+            string TaskID { get; set; }
+
+            TimeSpan Duration { get; set; }
+
+            string PayrollID { get; set; }
+        }
+
+        public class PaidWorkBlock : IBlock
+        {
+            public string Job { get; set; }
+
+            public string Extra { get; set; }
+
+            public string TaskID { get; set; }
+
+            public TimeSpan Duration { get; set; }
+
+            public string PayrollID { get; set; }
+
+            public PaidWorkBlock(string taskID, TimeSpan duration, string payID, string job)
+            {
+                TaskID = taskID;
+                Duration = duration;
+                PayrollID = payID;
+                Job = job;
+                Extra = "";
+            }
+        }
+
+        public class LeaveBlock : IBlock
+        {
+            public string Job { get; set; }
+
+            public string Extra { get; set; }
+
+            public string TaskID { get; set; }
+
+            public TimeSpan Duration { get; set; }
+
+            public string PayrollID { get; set; }
+
+            public LeaveBlock(string payrollID, TimeSpan duration)
+            {
+                PayrollID = payrollID;
+                Duration = duration;
+                Job = "";
+                Extra = "";
+                TaskID = "";
+            }
+        }
+
+    }
+
     public class TimesheetTimberlineItem
     {
         [Index(0)]
@@ -56,8 +180,9 @@ namespace PRS.Shared
 
         protected override string DefaultScript()
         {
-            return @"
-using PRS.Shared;
+            return
+@"using PRS.Shared;
+using PRS.Shared.TimeSheetTimberline;
 using InABox.Core;
 using System.Collections.Generic;
 
@@ -67,6 +192,39 @@ public class Module
     {
         // Perform pre-processing
     }
+    
+    public bool ProcessDailyTimeSheets(IDataModel<TimeSheet> model, Guid employee, DateTime date, List<TimeSheet> timesheets, List<Assignment> assignments)
+    {
+        // Before PRS calculates anything, you can edit the list of timesheets and assignments it is working with here.
+
+        // Return false unless you wish PRS to skip these entries.
+        return false;
+    }
+
+    public bool ProcessActivityBlocks(IDataModel<TimeSheet> model, Guid employee, DateTime date, List<ActivityBlock> blocks)
+    {
+        // Once PRS has aggregated the list of timesheets and assignments into a list of time blocks with given activities, you can edit these time blocks here.
+
+        // Always return false, unless you wish PRS to skip all these entries.
+        return false;
+    }
+
+    public bool ProcessBlocks(IDataModel<TimeSheet> model, Guid employee, DateTime date, List<PaidWorkBlock> work, List<LeaveBlock> leave)
+    {
+        // This function is called after PRS has determined the length, duration and overtime rules for all the blocks of time. Here, you can edit
+        // this data before it is collated into the export.
+        
+        // Always return false, unless you wish PRS to skip these entries.
+        return false;
+    }
+
+    public bool ProcessItem(IDataModel<TimeSheet> model, Guid employee, DateTime date, TimesheetTimberlineItem line)
+    {
+        // This is the final function before PRS exports each item. You can edit the data as you wish.
+
+        // Returning true here means that PRS adds this item to the export. If you wish this item to be skipped, return false.
+        return true;
+    }
 
     public void AfterPost(IDataModel<TimeSheet> model)
     {
@@ -144,65 +302,52 @@ public class Module
             return true;
         }
 
-        private class ActivityBlock
+        /// <summary>
+        /// Preprocess the timesheets and assignments for a given day and employee, and optionally skip them.
+        /// </summary>
+        /// <param name="employee"></param>
+        /// <param name="date"></param>
+        /// <param name="timesheets"></param>
+        /// <param name="assignments"></param>
+        /// <returns><see langword="true"/> if this set of timesheets has now been processed, and so they should be skipped.</returns>
+        private bool ProcessDailyTimeSheets(IDataModel<TimeSheet> model, Guid employee, DateTime date, List<TimeSheet> timesheets, List<Assignment> assignments)
         {
-            public Guid Activity { get; set; }
-
-            public TimeSpan Start { get; set; }
-
-            public TimeSpan Finish { get; set; }
-
-            public TimeSheet TimeSheet { get; set; }
-
-            public TimeSpan Duration => Finish - Start;
-
-            public ActivityBlock(Assignment assignment, TimeSheet sheet)
-            {
-                Activity = assignment.ActivityLink.ID != Guid.Empty
-                    ? assignment.ActivityLink.ID
-                    : sheet.ActivityLink.ID;
-
-                Start = assignment.EffectiveStartTime();
-                Finish = assignment.EffectiveFinishTime();
-                TimeSheet = sheet;
-            }
-
-            public ActivityBlock(TimeSheet sheet)
-            {
-                Activity = sheet.ActivityLink.ID;
-                Start = sheet.ApprovedStart;
-                Finish = sheet.ApprovedFinish;
-                TimeSheet = sheet;
-            }
-
-            public ActivityBlock(TimeSheet sheet, TimeSpan start, TimeSpan finish)
-            {
-                Activity = sheet.ActivityLink.ID;
-                Start = start;
-                Finish = finish;
-                TimeSheet = sheet;
-            }
+            return Script?.Execute(methodname: "ProcessDailyTimeSheets", parameters: new object[] { model, employee, date, timesheets, assignments })
+                ?? false;
+        }
 
-            public ActivityBlock Chop(TimeSheet sheet)
-            {
-                if(Start < sheet.ApprovedStart)
-                {
-                    Start = sheet.ApprovedStart;
-                }
-                if (Finish > sheet.ApprovedFinish)
-                {
-                    Finish = sheet.ApprovedFinish;
-                }
-                return this;
-            }
+        /// <summary>
+        /// Process the activity blocks for a given day and employee, and optionally skip them.
+        /// </summary>
+        /// <param name="employee"></param>
+        /// <param name="date"></param>
+        /// <param name="blocks"></param>
+        /// <returns><see langword="true"/> if this set of timesheets should be skipped.</returns>
+        private bool ProcessActivityBlocks(IDataModel<TimeSheet> model, Guid employee, DateTime date, List<ActivityBlock> blocks)
+        {
+            return Script?.Execute(methodname: "ProcessActivityBlocks", parameters: new object[] { model, employee, date, blocks })
+                ?? false;
+        }
 
-            public bool ContainedInTimeSheet(TimeSheet sheet) =>
-                Start < sheet.ApprovedFinish && Finish > sheet.ApprovedStart;
+        /// <summary>
+        /// Process the blocks for a given day and employee after the roster has been calculated, and optionally skip them.
+        /// </summary>
+        /// <returns><see langword="true"/> if this set of timesheets should be skipped.</returns>
+        private bool ProcessBlocks(IDataModel<TimeSheet> model, Guid employee, DateTime date, List<PaidWorkBlock> work, List<LeaveBlock> leave)
+        {
+            return Script?.Execute(methodname: "ProcessBlocks", parameters: new object[] { model, employee, date, work, leave })
+                ?? false;
+        }
 
-            public bool IntersectsWith(ActivityBlock other)
-            {
-                return Start < other.Finish && Finish > other.Start;
-            }
+        /// <summary>
+        /// Process the item before it gets added to the export.
+        /// </summary>
+        /// <param name="line"></param>
+        /// <returns><see langword="false"/> if this item shouldn't be exported.</returns>
+        private bool ProcessItem(IDataModel<TimeSheet> model, Guid employee, DateTime date, TimesheetTimberlineItem line)
+        {
+            return Script?.Execute(methodname: "ProcessItem", parameters: new object[] { model, employee, date, line }, defaultResult: true)
+                ?? true;
         }
 
         private IEnumerable<ActivityBlock> GetMaskedActivityBlocks(IEnumerable<Assignment> assignments, TimeSheet sheet)
@@ -285,63 +430,6 @@ public class Module
             }
         }
 
-        private interface IBlock
-        {
-            string Job { get; set; }
-
-            string Extra { get; set; }
-
-            string TaskID { get; set; }
-
-            TimeSpan Duration { get; set; }
-
-            string PayrollID { get; set; }
-        }
-
-        private class PaidWorkBlock : IBlock
-        {
-            public string Job { get; set; }
-
-            public string Extra { get; set; }
-
-            public string TaskID { get; set; }
-
-            public TimeSpan Duration { get; set; }
-
-            public string PayrollID { get; set; }
-
-            public PaidWorkBlock(string taskID, TimeSpan duration, string payID, string job)
-            {
-                TaskID = taskID;
-                Duration = duration;
-                PayrollID = payID;
-                Job = job;
-                Extra = "";
-            }
-        }
-
-        private class LeaveBlock : IBlock
-        {
-            public string Job { get; set; }
-
-            public string Extra { get; set; }
-
-            public string TaskID { get; set; }
-
-            public TimeSpan Duration { get; set; }
-
-            public string PayrollID { get; set; }
-
-            public LeaveBlock(string payrollID, TimeSpan duration)
-            {
-                PayrollID = payrollID;
-                Duration = duration;
-                Job = "";
-                Extra = "";
-                TaskID = "";
-            }
-        }
-
         private List<PaidWorkBlock> EvaluateOvertime(IEnumerable<PaidWorkBlock> workTime, Guid overtimeID)
         {
             var overtimeIntervals = _overtimeIntervals.GetValueOrDefault(overtimeID)?.ToList() ?? new List<OvertimeInterval>();
@@ -423,15 +511,26 @@ public class Module
                 .ToDictionary(x => x.ID, x => x);
 
             var assignments = model.GetTable<Assignment>().ToObjects<Assignment>()
-                .GroupBy(x => new { x.Date, Employee = x.EmployeeLink.ID }).ToDictionary(x => x.Key, x => x.ToArray());
+                .GroupBy(x => new { x.Date, Employee = x.EmployeeLink.ID }).ToDictionary(x => x.Key, x => x.ToList());
 
-            var daily = timesheets.GroupBy(x => new { x.Date, Employee = x.EmployeeLink.ID }).ToDictionary(x => x.Key, x => x.ToArray());
+            var daily = timesheets.GroupBy(x => new { x.Date, Employee = x.EmployeeLink.ID }).ToDictionary(x => x.Key, x => x.ToList());
 
             foreach(var (key, sheets) in daily)
             {
-                var dateAssignments = assignments.GetValueOrDefault(new { key.Date, Employee = key.Employee }, Array.Empty<Assignment>());
+                var dateAssignments = assignments.GetValueOrDefault(new { key.Date, key.Employee }, new List<Assignment>());
+
+                if(ProcessDailyTimeSheets(model, key.Employee, key.Date, sheets, dateAssignments))
+                {
+                    continue;
+                }
 
                 var activityBlocks = GetActivityBlocks(dateAssignments, sheets);
+
+                if (ProcessActivityBlocks(model, key.Employee, key.Date, activityBlocks))
+                {
+                    continue;
+                }
+
                 var approvedDuration = sheets.Aggregate(TimeSpan.Zero, (x, y) => x + y.ApprovedDuration);
 
                 var leave = new List<LeaveBlock>();
@@ -477,10 +576,15 @@ public class Module
 
                     var workItems = EvaluateOvertime(workTime, overtimeID);
 
+                    if (ProcessBlocks(model, key.Employee, key.Date, workItems, leave))
+                    {
+                        continue;
+                    }
+
                     var blocks = (workItems as IEnumerable<IBlock>).Concat(leave);
                     foreach(var block in blocks.GroupBy(x => new { x.Job, x.TaskID, x.PayrollID }, x => x))
                     {
-                        items.Add(new TimesheetTimberlineItem
+                        var item = new TimesheetTimberlineItem
                         {
                             Employee = employee?.PayrollID ?? "",
                             InDate = DateOnly.FromDateTime(key.Date),
@@ -489,7 +593,11 @@ public class Module
                             Task = block.Key.TaskID,
                             Hours = block.Sum(x => x.Duration.TotalHours),
                             PayID = block.Key.PayrollID
-                        });
+                        };
+                        if (ProcessItem(model, key.Employee, key.Date, item))
+                        {
+                            items.Add(item);
+                        }
                     }
                 }
             }