| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527 | using System;using System.Collections.Generic;using System.Drawing;using System.Linq;using System.Windows;using System.Windows.Controls;using System.Windows.Media.Imaging;using Comal.Classes;using InABox.Clients;using InABox.Configuration;using InABox.Core;using InABox.DynamicGrid;using InABox.WPF;namespace PRSDesktop{    public class TimeSheetGridSettings : IUserConfigurationSettings    {        public CoreFilterDefinition CurrentFilter { get; set; }    }    public class TimesheetGrid : DynamicDataGrid<TimeSheet>    {        private readonly BitmapImage leave = PRSDesktop.Resources.leave.AsBitmapImage();        private LeaveRequestGrid? leavegrid;        private readonly BitmapImage post = PRSDesktop.Resources.post.AsBitmapImage();        private readonly BitmapImage tick = PRSDesktop.Resources.tick.AsBitmapImage();        private readonly BitmapImage warning = PRSDesktop.Resources.warning.AsBitmapImage();        private static readonly BitmapImage? refresh = PRSDesktop.Resources.refresh.AsBitmapImage();        private TimeSheetGridSettings _settings;        protected override void Init()        {            base.Init();            _settings = new UserConfiguration<TimeSheetGridSettings>().Load();            SelectFilter(_settings.CurrentFilter, false);            ActionColumns.Add(new DynamicMenuColumn(BuildMenu) { Position = DynamicActionColumnPosition.Start });            ActionColumns.Add(new DynamicMapColumn<TimeSheet>(this, x => x.StartLocation));            //HiddenColumns.Add(x => x.StartLocation.Timestamp);            //HiddenColumns.Add(x => x.StartLocation.Latitude);            //HiddenColumns.Add(x => x.StartLocation.Longitude);            ActionColumns.Add(new DynamicMapColumn<TimeSheet>(this, x => x.FinishLocation));            //HiddenColumns.Add(x => x.FinishLocation.Timestamp);            //HiddenColumns.Add(x => x.FinishLocation.Latitude);            //HiddenColumns.Add(x => x.FinishLocation.Longitude);            //ActionColumns.Add(new DynamicImageColumn() { Image = WarningImage });            ActionColumns.Add(new DynamicTickColumn<TimeSheet, DateTime>(x => x.Approved, tick, tick, null));            //ActionColumns.Add(new DynamicTickColumn<TimeSheet, DateTime>(x => x.Processed, post, tick, null));            ActionColumns.Add(new DynamicImageColumn(Posted_Image, null)            {                ToolTip = Posted_ToolTip            });            HiddenColumns.Add(x => x.Date);            HiddenColumns.Add(x => x.Start);            HiddenColumns.Add(x => x.Finish);            HiddenColumns.Add(x => x.Duration);            HiddenColumns.Add(x => x.ApprovedStart);            HiddenColumns.Add(x => x.ApprovedFinish);            //HiddenColumns.Add(x => x.ApprovedHours);            HiddenColumns.Add(x => x.ApprovedDuration);            HiddenColumns.Add(x => x.EmployeeLink.ID);            HiddenColumns.Add(x => x.LeaveRequestLink.ID);            HiddenColumns.Add(x => x.JobLink.ID);            HiddenColumns.Add(x => x.JobLink.Deleted);            HiddenColumns.Add(x => x.Approved);            HiddenColumns.Add(x => x.PostedNote);            HiddenColumns.Add(x => x.PostedStatus);            if (Security.CanView<Employee>())                AddButton("Employee", PRSDesktop.Resources.anonymous.AsBitmapImage(Color.White), EditEmployee);            if (Security.IsAllowed<CanApproveTimesheets>())            {                AddButton("Approve", tick, ApproveTimeSheet);                AddButton("UnApprove", warning, UnApproveTimeSheet);            }            if (Security.CanView<LeaveRequest>())                AddButton("Create Leave", leave, CreateLeaveRequest);        }        protected override void DoReconfigure(FluentList<DynamicGridOption> options)        {            base.DoReconfigure(options);            options.AddRange(                DynamicGridOption.SelectColumns,                DynamicGridOption.FilterRows,                DynamicGridOption.MultiSelect,                DynamicGridOption.RecordCount            );        }        public DateTime StartDate { get; set; } = DateTime.MinValue;        public DateTime EndDate { get; set; } = DateTime.MaxValue;        public string Search { get; set; } = "";        public bool ApprovedOnly { get; set; } = false;        private FrameworkElement? Posted_ToolTip(DynamicActionColumn column, CoreRow? row)        {            if (row is null)            {                return column.TextToolTip("Timesheet Processed Status");            }            return column.TextToolTip(row.Get<TimeSheet, PostedStatus>(x => x.PostedStatus) switch            {                PostedStatus.PostFailed => "Post failed: " + row.Get<TimeSheet, string>(x => x.PostedNote),                PostedStatus.RequiresRepost => "Repost required: " + row.Get<TimeSheet, string>(x => x.PostedNote),                PostedStatus.Posted => "Processed",                PostedStatus.NeverPosted or _ => "Not posted yet",            });        }        private BitmapImage? Posted_Image(CoreRow? row)        {            if (row is null)                return post;            return row.Get<TimeSheet, PostedStatus>(x => x.PostedStatus) switch            {                PostedStatus.PostFailed => warning,                PostedStatus.Posted => tick,                PostedStatus.RequiresRepost => refresh,                PostedStatus.NeverPosted or _ => null,            };        }        private void BuildMenu(DynamicMenuColumn column, CoreRow? row)        {            if (row == null) return;            var formsItem = column.AddItem("Digital Forms", PRSDesktop.Resources.kanban, null);            DynamicGridUtils.PopulateFormMenu<TimeSheetForm, TimeSheet, TimeSheetLink>(                formsItem,                row.Get<TimeSheet, Guid>(x => x.ID),                () => row.ToObject<TimeSheet>());        }        protected override TimeSheet CreateItem()        {            var result = base.CreateItem();            result.Date = DateTime.Today;            return result;        }                private Tuple<Guid, DateTime, string>[] GroupTimeSheets(CoreRow[] rows)        {            var results = new List<Tuple<Guid, DateTime, string>>();            foreach (var row in rows)            {                var date = row.Get<TimeSheet, DateTime>(x => x.Date);                var empid = row.Get<TimeSheet, Guid>(x => x.EmployeeLink.ID);                var name = row.Get<TimeSheet, string>(x => x.EmployeeLink.Name);                if (!results.Any(x => x.Item1.Equals(empid) && x.Item2.Equals(date)))                    results.Add(new Tuple<Guid, DateTime, string>(empid, date, name));            }            return results.OrderBy(x => x.Item3).ThenBy(x => x.Item2).ToArray();        }        private bool CheckApproval(TimeSheet time)        {            time.ApprovedStart = time.Start.Round(new TimeSpan(0, 15, 0));            time.ApprovedFinish = time.Finish.Round(new TimeSpan(0, 15, 0));            time.Approved = DateTime.Now;            return true;        }        private bool ApproveTimeSheet(Button sender, CoreRow[] rows)        {            if (!rows.Any())            {                MessageBox.Show("Please select at least one row to process!");                return false;            }            Progress.Show("");            Progress.SetMessage("Loading Time Sheets");            var employeedays = GroupTimeSheets(rows);            var updates = new List<TimeSheet>();            var lastemp = Guid.Empty;            Employee? emp = null;            foreach (var day in employeedays)            {                var date = day.Item2;                var empid = day.Item1;                var name = day.Item3;                if (empid != lastemp)                {                    Progress.SetMessage("Processing: " + name);                    emp = new Client<Employee>().Load(new Filter<Employee>(x => x.ID).IsEqualTo(empid)).FirstOrDefault();                    lastemp = empid;                }                if (emp != null)                {                                        var times = new Client<TimeSheet>().Load(new Filter<TimeSheet>(x => x.Date).IsEqualTo(date)                        .And(x => x.EmployeeLink.ID).IsEqualTo(empid)                        .And(x => x.Approved).IsEqualTo(DateTime.MinValue)                        .And(x => x.Finish).IsNotEqualTo(new TimeSpan(0))                    );                    if (times.Length > 0)                    {                        // Rules:                         // if the employee has a usual start / usual end set, it will be treated as salary,                         //    and the approved times will always reflect the usual, eather than the actual times                        // Then, it will go through each timesheet, and wind back the start / finish time IF                        //    a: timesheet is joblinked                        //    b: job has a usual start / end                        //    b: timesheet activity == job.usual activity                        var first = times.OrderBy(x => x.Start).First();                        var start = first.Start.Round(new TimeSpan(0, 15, 0));                                                var last = times.OrderBy(x => x.Finish).Last();                        var finish = last.Finish.Round(new TimeSpan(0, 15, 0));                        var jobfilter = new Filter<Job>(x => x.ID).IsEqualTo(Guid.Empty);                        Array.ForEach(times, t => jobfilter.Ors.Add(new Filter<Job>(j => j.ID).IsEqualTo(t.JobLink.ID)));                        var jobs = new Client<Job>().Query(jobfilter,                            new Columns<Job>(x => x.ID, x => x.UsualStart, x => x.UsualFinish, x => x.UsualActivity.ID));                        for (var i = 0; i < times.Length; i++)                        {                            //Progress.SetMessage(String.Format("Checking Time Sheet({0:F2}% complete)", (i * 100.0F) / (double)times.Length));                            var time = times[i];                            time.ApprovedStart = time == first || time.Start < start ? start : time.Start;                            time.ApprovedFinish = time == last || time.Finish > finish ? finish : time.Finish;                            var job = jobs.Rows.Where(r => r.Get<Job, Guid>(c => c.ID).Equals(time.JobLink.ID)                                                           && Entity.IsEntityLinkValid<Job, ActivityLink>(x => x.UsualActivity, r)                                                           && r.Get<Job, Guid>(c => c.UsualActivity.ID).Equals(time.ActivityLink.ID)                            ).FirstOrDefault();                            if (job != null)                            {                                var jobstart = job.Get<Job, TimeSpan>(x => x.UsualStart);                                if (jobstart > new TimeSpan(0) && time.Start < jobstart)                                    time.ApprovedStart = jobstart;                                var jobend = job.Get<Job, TimeSpan>(x => x.UsualFinish);                                if (jobend > new TimeSpan(0) && time.Finish > jobend)                                    time.ApprovedFinish = jobend;                            }                            if (time.ApprovedFinish < time.ApprovedStart)                                time.ApprovedFinish = time.ApprovedStart;                            time.Approved = DateTime.Now;                            updates.Add(time);                        }                    }                }            }            if (updates.Any())            {                Progress.SetMessage("Updating Database");                new Client<TimeSheet>().Save(updates, "Approving Time Sheets");            }            Progress.Close();            MessageBox.Show("All Done");            return true;        }        private bool UnApproveTimeSheet(Button sender, CoreRow[] rows)        {            if (!rows.Any())            {                MessageBox.Show("Please select at least one row to process!");                return false;            }            Progress.Show("");            Progress.SetMessage("Processing Time Sheets");            var employeedays = GroupTimeSheets(rows);            var updates = new List<TimeSheet>();            foreach (var day in employeedays)            {                var date = day.Item2;                var empid = day.Item1;                var name = day.Item3;                Progress.SetMessage("Clearing Approvals for " + name);                var times = new Client<TimeSheet>().Load(new Filter<TimeSheet>(x => x.Date).IsEqualTo(date)                    .And(x => x.EmployeeLink.ID).IsEqualTo(empid)                );                for (var i = 0; i < times.Length; i++)                {                    //Progress.SetMessage(String.Format("Clearing Approval ({0:F2}% complete)", (i * 100.0F) / (double)times.Length));                    var time = times[i];                    time.ApprovedStart = new TimeSpan(0);                    time.ApprovedFinish = new TimeSpan(0);                    time.Approved = DateTime.MinValue;                    updates.Add(time);                }            }            if (updates.Any())            {                Progress.SetMessage("Updating Database");                new Client<TimeSheet>().Save(updates, "Clearing Approvals");            }            Progress.Close();            MessageBox.Show("All Done");            return true;        }        private bool EditEmployee(Button btn, CoreRow[] rows)        {            if (rows.Length != 1)            {                MessageBox.Show("Please select a single employee!");                return false;            }            var row = rows.First();            var employee = new Client<Employee>().Load(new Filter<Employee>(x => x.ID).IsEqualTo(row.Get<TimeSheet, Guid>(c => c.EmployeeLink.ID)))                .FirstOrDefault();            if (employee == null)            {                MessageBox.Show("Cannot Locate Employee!");                return false;            }            var employeegrid = new EmployeeGrid();            return employeegrid.EditItems(new[] { employee });        }        private bool CreateLeaveRequest(Button arg1, CoreRow[] arg2)        {            var leave = new LeaveRequest();            leave.From = DateTime.Today;            leave.To = DateTime.Today;            leavegrid ??= new LeaveRequestGrid();            return leavegrid.EditItems(new[] { leave });        }        public override bool EditItems(TimeSheet[] items, Func<Type, CoreTable>? PageDataHandler = null, bool PreloadPages = false)        {            var leaveids = items.Select(x => x.LeaveRequestLink.ID).Distinct().ToArray();            if (leaveids.Length > 1 && leaveids.Contains(Guid.Empty))            {                MessageBox.Show(                    "At least one of these timesheets is linked to a leave request!\nPlease select either regular or leave timesheets and try again.");                return false;            }            if (!leaveids.Contains(Guid.Empty))            {                if (!Security.CanView<LeaveRequest>())                {                    MessageBox.Show("Editing leave requests is not permitted here");                    return false;                }                LeaveRequest[] leaves;                using (new WaitCursor())                {                    leaves = new Client<LeaveRequest>().Load(new Filter<LeaveRequest>(x => x.ID).InList(leaveids));                    leavegrid ??= new LeaveRequestGrid();                }                return leavegrid.EditItems(leaves);            }            return base.EditItems(items, PageDataHandler, PreloadPages);        }        protected override Dictionary<string, object?> EditorValueChanged(IDynamicEditorForm editor, TimeSheet[] items, string name, object value)        {            var result = base.EditorValueChanged(editor, items, name, value);            if (name == "ActivityLink.ID")            {                ReloadForms<TimeSheet, TimeSheetForm, ActivityForm>(editor, items.FirstOrDefault(), x => x.Activity.ID,                    value != null ? (Guid)value : Guid.Empty);            }            else if (name.Equals("EmployeeLink.ID"))            {                var activity = editor.FindEditor("ActivityLink.ID") as ILookupEditorControl;                if (activity != null)                    DefineLookups(activity, items);            }            return result;        }        protected override void AfterLoad(IDynamicEditorForm editor, TimeSheet[] items)        {            base.AfterLoad(editor, items);            if (items.FirstOrDefault().ID == Guid.Empty)                ReloadForms<TimeSheet, TimeSheetForm, ActivityForm>(editor, items.FirstOrDefault(), x => x.Activity.ID,                    items.FirstOrDefault().ActivityLink.ID);        }        //private BitmapImage WarningImage(DataRow row)        //{        //    if (row == null)        //        return warning;        //    if (row.Get<TimeSheet, DateTime>(x => x.Approved) != DateTime.MinValue)        //        return null;        //    TimeSpan start = row.Get<TimeSheet, TimeSpan>(x => x.Start);        //    if (start.Equals(new TimeSpan(0)))        //        return warning;        //    TimeSpan finish = row.Get<TimeSheet, TimeSpan>(x => x.Finish);        //    if (finish.Equals(new TimeSpan(0)))        //        return warning;        //    TimeSpan actual = row.Get<TimeSheet, TimeSpan>(x => x.Duration);        //    TimeSpan approved = row.Get<TimeSheet, TimeSpan>(x => x.ApprovedDuration);        //    if (!actual.Equals(approved))        //        return warning;        //    return null;        //}        //private bool ApproveClick(DataRow row)        //{        //    if (row == null)        //        return false;        //    Guid id = row.Get<TimeSheet, Guid>(x => x.ID);        //    TimeSheet timesheet = new Client<TimeSheet>().Load(new Filter<TimeSheet>(x => x.ID).IsEqualTo(id)).FirstOrDefault();        //    if (timesheet != null)        //    {        //        timesheet.Approved = timesheet.Approved.IsEmpty() ? DateTime.Now : DateTime.MinValue;        //        new Client<TimeSheet>().Save(timesheet, "Toggled Approval flag");        //        return true;        //    }        //    return false;        //}        protected override void Reload(Filters<TimeSheet> criteria, Columns<TimeSheet> columns, ref SortOrder<TimeSheet>? sort,            Action<CoreTable?, Exception?> action)        {            var filter = new Filter<TimeSheet>(x => x.Processed).IsEqualTo(DateTime.MinValue);            if (!string.IsNullOrWhiteSpace(Search))                filter = filter.TextSearch(Search, x => x.EmployeeLink.Name, x => x.Address, x => x.Notes);            criteria.Add(filter);            criteria.Add(new Filter<TimeSheet>(x => x.EmployeeLink.FinishDate).IsEqualTo(DateTime.MinValue).Or(x => x.EmployeeLink.FinishDate)                .IsGreaterThanOrEqualTo(DateTime.Today));            criteria.Add(new Filter<TimeSheet>(x => x.Date).IsGreaterThanOrEqualTo(StartDate.Date));            criteria.Add(new Filter<TimeSheet>(x => x.Date).IsLessThan(EndDate == DateTime.MaxValue ? DateTime.MaxValue : EndDate.AddDays(1)));            if (ApprovedOnly)                criteria.Add(new Filter<TimeSheet>(x => x.Approved).IsNotEqualTo(DateTime.MinValue));            base.Reload(criteria, columns, ref sort, action);        }        protected override bool CanDeleteItems(CoreRow[] rows)        {            var result = true;            foreach (var row in rows)            {                if (row.IsEntityLinkValid<TimeSheet, LeaveRequestLink>(x => x.LeaveRequestLink))                    result = false;            }            if (!result)                MessageBox.Show(                    "You cannot delete timesheets linked to Leave Requests!\n\nPlease un-approve or delete the related Leave Request instead.");            return result;        }        protected override BaseEditor? GetEditor(object item, DynamicGridColumn column)        {            var result = base.GetEditor(item, column)?.CloneEditor();            if (result == null)                return null;            if (!Security.IsAllowed<CanApproveTimesheets>() &&                (column.ColumnName.Equals("ApprovedStart") || column.ColumnName.Equals("ApprovedFinish") ||                 column.ColumnName.Equals("Approved")))                result.Editable = Editable.Disabled;            if (!Security.IsAllowed<CanChangeStartFinishTimes>() &&                (column.ColumnName.Equals("Date") || column.ColumnName.Equals("Start") || column.ColumnName.Equals("Finish")))                result.Editable = Editable.Disabled;            if (!Security.IsAllowed<CanArchiveTimesheets>() && column.ColumnName.Equals("Processed"))                result.Editable = Editable.Disabled;            return result;        }        protected override void FilterSelected(CoreFilterDefinition filter)        {            base.FilterSelected(filter);            _settings.CurrentFilter = filter;            new UserConfiguration<TimeSheetGridSettings>().Save(_settings);        }    }}
 |