| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855 | using System;using System.Collections.Generic;using System.Data;using System.Drawing;using System.Globalization;using System.Linq;using System.Windows;using System.Windows.Controls;using System.Windows.Data;using System.Windows.Input;using System.Windows.Media;using Comal.Classes;using InABox.Clients;using InABox.Configuration;using InABox.Core;using InABox.DynamicGrid;using InABox.WPF;using NPOI.SS.Formula.Functions;using PRSDesktop.WidgetGroups;using System.ComponentModel;using Syncfusion.UI.Xaml.Grid;using Syncfusion.Windows.Tools.Controls;using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;using PRS.Shared;namespace PRSDesktop;public class JobResourcePlannerProperties : IUserConfigurationSettings, IDashboardProperties{    public JobSelectorSettings JobSettings { get; set; }    public JobSelectorData JobSelection { get; set; }        public Guid ActivityType { get; set; }        public int MonthsToView { get; set; }    public TeamSelectorSettings TeamSettings { get; set; }    public TeamSelectorData TeamSelection { get; set; }    public double SplitterPosition { get; set; }    public double HoursPerDay { get; set; }    public double EmployeeSplitterPosition { get; set; }    public bool IncludeUnApprovedLeave { get; set; }    public JobResourcePlannerProperties()    {        JobSettings = new JobSelectorSettings();        JobSelection = new JobSelectorData();        TeamSettings = new TeamSelectorSettings();        TeamSelection = new TeamSelectorData();        MonthsToView = 1;        SplitterPosition = 0F;        EmployeeSplitterPosition = 0F;        HoursPerDay = 8.5;        ActivityType = Guid.Empty;        IncludeUnApprovedLeave = false;    }}public partial class JobResourcePlanner : UserControl{    private enum Suppress    {        This    }    private ActivityModel[] _activities = new ActivityModel[] { };    private LeaveRequestModel[] _leaverequests = new LeaveRequestModel[] { };    private StandardLeaveModel[] _standardleaves = new StandardLeaveModel[] { };    private JobModel[] _jobs = new JobModel[] { };    private EmployeeResourceModel[] _emps = new EmployeeResourceModel[] { };    private List<AssignmentModel> _assignments = new List<AssignmentModel>();        public JobResourcePlannerProperties Properties { get; set; }    public event LoadSettings<JobResourcePlannerProperties>? LoadSettings;    public event SaveSettings<JobResourcePlannerProperties>? SaveSettings;    private void DoLoadSettings()    {        Properties = LoadSettings?.Invoke(this);    }              private void DoSaveSettings()    {        SaveSettings?.Invoke(this, Properties);    }    public JobResourcePlanner()    {        using (new EventSuppressor(Suppress.This))            InitializeComponent();    }        public void Setup()    {        using (new EventSuppressor(Suppress.This))        {            DoLoadSettings();                        JobSelector.Setup();            JobSelector.Settings = Properties.JobSettings;            JobSelector.Selection = Properties.JobSelection;            _jobs = JobSelector.GetJobData<JobModel>(r => new JobModel(r));            TeamSelector.Setup();            TeamSelector.Settings = Properties.TeamSettings;            TeamSelector.Selection = Properties.TeamSelection;            _emps = TeamSelector.GetEmployeeData<EmployeeResourceModel>((row, rosters) => new EmployeeResourceModel(row, rosters));                        ViewWindow.ItemsSource = new Dictionary<int, String>()            {                { 1, "1 Month" },                { 3, "3 Months" },                { 6, "6 Months" },                { 12, "12 months" }            };            ViewWindow.SelectedValue = Properties.MonthsToView;                        if (Properties.SplitterPosition != 0F)                TeamSelectorRow.Height = new GridLength(Properties.SplitterPosition, GridUnitType.Pixel);            if (Properties.EmployeeSplitterPosition != 0F)                AvailableEmployeesRow.Height = new GridLength(Properties.SplitterPosition, GridUnitType.Pixel);            HoursSelector.Text = $"{Properties.HoursPerDay:F2}";                        LeaveType.SelectedIndex = Properties.IncludeUnApprovedLeave ? 1 : 0;                        AvailableEmployees.Refresh(true, false);            AssignedEmployees.Refresh(true, false);                        MultiQuery query = new MultiQuery();                        query.Add<StandardLeave>(                LookupFactory.DefineFilter<StandardLeave>(),                StandardLeaveModel.Columns            );                        query.Add<LeaveRequest>(                new Filter<LeaveRequest>(x => x.Status).IsNotEqualTo(LeaveRequestStatus.Rejected),                LeaveRequestModel.Columns            );            query.Add<Activity>(                LookupFactory.DefineFilter<Activity>(),                new Columns<Activity>(x => x.ID).Add(x => x.Code).Add(x => x.Description),                new SortOrder<Activity>(x => x.Code)            );            query.Query();            _standardleaves = query.Get<StandardLeave>().Rows.Select(r=>new StandardLeaveModel(r)).ToArray();            _leaverequests = query.Get<LeaveRequest>().Rows.Select(r => new LeaveRequestModel(r)).ToArray();                        _activities = query.Get<Activity>().Rows.Select(r => new ActivityModel(r)).ToArray();            ActivityType.ItemsSource = _activities;            ActivityType.SelectedValue = Properties.ActivityType;                    }    }    public void Shutdown(CancelEventArgs? cancel)    {    }        private bool GetStandardLeaveTimes(DateTime date, out TimeSpan start, out TimeSpan finish)    {        bool result = false;        start = TimeSpan.Zero;        finish = TimeSpan.FromDays(1);        var requests = _standardleaves.Where(x =>             (x.From <= date)             && (x.To.Add(x.ToTime) > date)        );        if (requests.Any())        {            result = true;            start = TimeSpan.FromDays(1);            finish = TimeSpan.Zero;            foreach (var leave in requests)            {                var curstart = leave.From == date ? leave.FromTime : TimeSpan.Zero;                start = start > curstart ? curstart : start;                var curfinish = leave.To == date ? leave.ToTime : TimeSpan.FromDays(1);                finish = finish < curfinish ? curfinish : finish;            }        }        return result;                }        private void AdjustStandardLeave(DateTime date, ref TimeSpan time)    {        TimeSpan result = TimeSpan.Zero;        var leaves = _standardleaves.Where(x =>             (x.From <= date)            && (x.To.Add(x.ToTime) > date)        );        foreach (var leave in leaves)        {            result += (leave.To == date ? leave.ToTime : TimeSpan.FromDays(1)) -                         (leave.From == date ? leave.FromTime : TimeSpan.Zero);        }        time = time >= result ? time - result : TimeSpan.Zero;    }    private bool GetLeaveRequestTimes(DateTime date, EmployeeResourceModel emp, out TimeSpan start, out TimeSpan finish)    {        bool result = false;        start = TimeSpan.Zero;        finish = TimeSpan.FromDays(1);        var requests = _leaverequests.Where(x =>             (x.From <= date)             && (x.To.Add(x.ToTime) > date)            && (x.EmployeeID == emp.ID)            && (Properties.IncludeUnApprovedLeave ? true : x.Status == LeaveRequestStatus.Approved)        );        if (requests.Any())        {            result = true;            start = TimeSpan.FromDays(1);            finish = TimeSpan.Zero;            foreach (var leave in requests)            {                var curstart = leave.From == date ? leave.FromTime : TimeSpan.Zero;                start = start > curstart ? curstart : start;                var curfinish = leave.To == date ? leave.ToTime : TimeSpan.FromDays(1);                finish = finish < curfinish ? curfinish : finish;            }        }        return result;                }        private void AdjustLeaveRequests(DateTime date, EmployeeResourceModel emp, ref TimeSpan time)    {        TimeSpan result = TimeSpan.Zero;        var requests = _leaverequests.Where(x =>             (x.From <= date)             && (x.To.Add(x.ToTime) > date)            && (x.EmployeeID == emp.ID)            && (Properties.IncludeUnApprovedLeave ? true : x.Status == LeaveRequestStatus.Approved)        );        foreach (var leave in requests)        {            result += (leave.To == date ? leave.ToTime : TimeSpan.FromDays(1)) -                         (leave.From == date ? leave.FromTime : TimeSpan.Zero);        }        time = time >= result ? time - result : TimeSpan.Zero;    }    public void Refresh()    {        using (new WaitCursor())        {                        var jobids = _jobs.Select(x => x.ID).ToArray();            var empids = _emps.Select(x => x.ID).ToArray();            var actids = _activities.Select(x => x.ID).ToArray();            DateTime todate = DateTime.Today.AddMonths(Properties.MonthsToView);                        MultiQuery query = new MultiQuery();            query.Add<Assignment>(                new Filter<Assignment>(x=>x.EmployeeLink.ID).InList(empids)                    .And(x=>x.Date).IsGreaterThanOrEqualTo(DateTime.Today)                    .And(x=>x.Date).IsLessThanOrEqualTo(DateTime.Today.AddMonths(Properties.MonthsToView)),                AssignmentModel.Columns            );            query.Query();            _assignments = query.Get<Assignment>().Rows.Select(r => new AssignmentModel(r)).ToList();            var data = new DataTable();            data.Columns.Add("Date", typeof(DateTime));            data.PrimaryKey = new System.Data.DataColumn[] { data.Columns[0] };                        data.Columns.Add("Total", typeof(object));            data.Columns.Add("Available", typeof(object));                        foreach (var job in _jobs)                data.Columns.Add(job.ID.ToString(), typeof(object));                        DateTime date = DateTime.Today;            while (date <= DateTime.Today.AddMonths(Properties.MonthsToView))            {                double avail = 0.0F;                foreach (var emp in _emps)                {                    var roster = RosterUtils.GetRoster(emp.Roster, emp.RosterStart, date);                    var hours = roster.GetBlocks(date, TimeSpan.MinValue, TimeSpan.MaxValue)                        .Aggregate<RosterBlock, TimeSpan>(TimeSpan.Zero, (value, block) => value + block.Duration);                                        AdjustStandardLeave(date, ref hours);                                        AdjustLeaveRequests(date, emp, ref hours);                                        avail += hours.TotalHours;                }                                double total = avail;                                var values = new List<object?> { date };                                var anyjobstoday = _assignments.Where(x => (x.Date.Date == date.Date));                 avail -= anyjobstoday.Aggregate<AssignmentModel, double>(0F, (value, model) => value + model.BookedDuration.TotalHours);                foreach (var job in _jobs)                {                    var thisjobtoday = _assignments.Where(x => (x.Date.Date == date.Date) && (x.JobID == job.ID));                    if (thisjobtoday.Any())                    {                        var assigned = thisjobtoday.Aggregate<AssignmentModel, double>(0F,                            (value, model) => value + model.BookedDuration.TotalHours);                        values.Add(assigned / Properties.HoursPerDay);                    }                    else                        values.Add(null);                }                values.Insert(1,avail / Properties.HoursPerDay);                values.Insert(1,total / Properties.HoursPerDay);                data.Rows.Add(values.ToArray());                date = date.AddDays(1);            }            dataGrid.ItemsSource = data;        }    }        private abstract class LeaveConverter : IMultiValueConverter    {        protected System.Windows.Media.Color GetColor(object[] value)        {            if ((value[0] != DBNull.Value) && (value[0] != DependencyProperty.UnsetValue))                return Colors.DarkSeaGreen;            if (value[1] is GridCell cell)            {                if (cell.DataContext is DataRowView rowview)                {                    var total = rowview.Row["Total"];                    if ((total == DBNull.Value) || ((double)total == 0.0F))                        return Colors.LightGray;                    var avail = rowview.Row["Available"];                    if ((avail == DBNull.Value) || ((double)avail == 0.0F))                        return Colors.LightSalmon;                    return Colors.LightYellow;                }            }            return Colors.WhiteSmoke;        }        public abstract object Convert(object[] value, Type targetType, object parameter, CultureInfo culture);        public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)        {            throw new NotImplementedException();        }    }        private class LeaveBackgroundConverter : LeaveConverter    {        public override object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)        {            return new SolidColorBrush(base.GetColor(value)) { Opacity = 0.8 };        }    }    private class LeaveForegroundConverter : LeaveConverter    {        public override object Convert(object[] value, Type targetType, object parameter, CultureInfo culture)        {            return new SolidColorBrush(ImageUtils.GetForegroundColor(base.GetColor(value)));        }    }       private void DataGrid_AutoGeneratingColumn(object sender, AutoGeneratingColumnArgs e)    {        MultiBinding CreateBinding<TConverter>(String path) where TConverter : IMultiValueConverter, new()        {            var binding = new MultiBinding();            binding.Bindings.Add(new Binding(path));            binding.Bindings.Add(new Binding() { RelativeSource = new RelativeSource(RelativeSourceMode.Self) });            binding.Converter = new TConverter();            return binding;        }        var value = (e.Column.ValueBinding as Binding)!;        if (value.Path.Path.Equals("Date"))        {            e.Column.Width = 80;            e.Column.HeaderStyle = Resources["DateHeaderStyle"] as Style;            e.Column.AllowFocus = false;        }        else if (value.Path.Path.Equals("Total"))        {            e.Cancel = true;        }        else if (value.Path.Path.Equals("Available"))        {            e.Column = new GridNumericColumn() { NumberDecimalDigits = 2, MappingName = e.Column.MappingName};            e.Column.Width = 50;            e.Column.HeaderStyle = e.Column.HeaderStyle = Resources["ContentHeaderStyle"] as Style;            e.Column.AllowFocus = false;            e.Column.HeaderText = "Available";        }        else        {            e.Column = new GridNumericColumn() { NumberDecimalDigits = 2, MappingName = e.Column.MappingName};            e.Column.Width = 40;            e.Column.HeaderStyle = Resources["ContentHeaderStyle"] as Style;            var jobid = Guid.Parse(value.Path.Path);            e.Column.HeaderText = _jobs.FirstOrDefault(x=>x.ID == jobid)?.Name ?? jobid.ToString();            e.Column.DisplayBinding = new Binding { Path = new PropertyPath(e.Column.MappingName) };            e.Column.AllowFocus = true;            var style = new Style(typeof(GridCell));            style.Setters.Add(new Setter(BackgroundProperty, CreateBinding<LeaveBackgroundConverter>(value.Path.Path)));            style.Setters.Add(new Setter(ForegroundProperty, CreateBinding<LeaveForegroundConverter>(value.Path.Path)));            e.Column.CellStyle = style;                    }        e.Column.TextAlignment = TextAlignment.Center;        e.Column.HorizontalHeaderContentAlignment = HorizontalAlignment.Center;        e.Column.ColumnSizer = GridLengthUnitType.None;        e.Column.ShowHeaderToolTip = false;        e.Column.ShowToolTip = false;    }       private void DataGrid_ContextMenuOpening(object sender, ContextMenuEventArgs e)    {        var emps = TeamSelector.GetEmployeeData<EmployeeResourceModel>((row, rosters) => new EmployeeResourceModel(row, rosters));    }        private void DataGrid_OnSelectionChanging(object? sender, GridSelectionChangingEventArgs e)    {             }        private bool bResettingSelection = false;        private void DataGrid_OnPreviewMouseDown(object sender, MouseButtonEventArgs e)    {        bResettingSelection = true;        dataGrid.SelectionController.ClearSelections(false);    }        private void DataGrid_OnCurrentCellActivating(object? sender, CurrentCellActivatingEventArgs e)    {        if (bResettingSelection)        {            bResettingSelection = false;            return;        }        var thiscol = dataGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex];        var selected = dataGrid.SelectionController.SelectedCells;        if (selected.Any(x => x.Column != thiscol))            e.Cancel = true;        else            e.Cancel = false;    }        private void DataGrid_OnPreviewMouseUp(object sender, MouseButtonEventArgs e)    {    }    private bool ExtractSelection(out Guid jobid, out DateTime[] dates)    {        var selected = dataGrid.SelectionController.SelectedCells;        var colname = selected.Select(x => x.Column.MappingName).Distinct().FirstOrDefault();        if (Guid.TryParse(colname, out Guid job))        {            jobid = job;            dates = selected.Select(x => ((DataRowView)x.RowData).Row["Date"]).Cast<DateTime>().ToArray();            return true;        }        else        {            jobid = Guid.Empty;            dates = new DateTime[] { };            return false;        }    }        private void DataGrid_OnMouseUp(object sender, MouseButtonEventArgs e)    {        if (ExtractSelection(out Guid jobid, out DateTime[] dates))        {            LoadAssignedEmployees(jobid, dates);            LoadAvailableEmployees(dates);        }                }        private JobPlannerEmployee GetEmployee(Guid id, List<JobPlannerEmployee> list)    {        var result = list.FirstOrDefault(x => x.ID == id);        if (result == null)        {            result = new JobPlannerEmployee()            {                ID = id,                Name = _emps.FirstOrDefault(x=>x.ID == id)?.Name ?? id.ToString(),                Time = TimeSpan.Zero            };            list.Add(result);        }        return result;    }    private void LoadAvailableEmployees(DateTime[] dates)    {        List<JobPlannerEmployee> availableemployees = new List<JobPlannerEmployee>();        foreach (var emp in _emps)        {            foreach (var date in dates)            {                var roster = RosterUtils.GetRoster(emp.Roster, emp.RosterStart, date);                var blocks = roster?.GetBlocks(date, TimeSpan.MinValue, TimeSpan.MaxValue) ?? new RosterBlock[] { };                var rostered = blocks.Aggregate(TimeSpan.Zero, (time, block) => time += block.Duration);                AdjustStandardLeave(date, ref rostered);                AdjustLeaveRequests(date, emp, ref rostered);                var assignments = _assignments.Where(x => (x.Date == date) && (x.EmployeeID == emp.ID));                var assigned = assignments.Aggregate(TimeSpan.Zero, (time, assign) => time += assign.BookedDuration);                if (rostered > assigned)                    GetEmployee(emp.ID, availableemployees).Time += rostered.Subtract(assigned);                AvailableEmployees.Items = availableemployees.OrderBy(x=>x.Name).ToList();                AvailableEmployees.Refresh(false, true);            }        }    }        private void LoadAssignedEmployees(Guid jobid, DateTime[] dates)    {        List<JobPlannerEmployee> assignedemployees = new List<JobPlannerEmployee>();        foreach (var assignment in _assignments.Where(x => dates.Contains(x.Date) && (x.JobID == jobid)))            GetEmployee(assignment.EmployeeID,assignedemployees).Time += assignment.BookedDuration;        AssignedEmployees.Items = assignedemployees.OrderBy(x=>x.Name).ToList();;        AssignedEmployees.Refresh(false, true);    }    private void JobSelector_OnSettingsChanged(object sender, JobSelectorSettingsChangedArgs args)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.JobSettings = args.Settings;        DoSaveSettings();    }    private void JobSelector_OnSelectionChanged(object sender, JobSelectorSelectionChangedArgs args)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.JobSelection = args.Selection;        _jobs = JobSelector.GetJobData<JobModel>(x => new JobModel(x));        DoSaveSettings();        Refresh();    }    private void TeamSelector_OnSettingsChanged(object sender, TeamSelectorSettingsChangedArgs args)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.TeamSettings = args.Settings;        DoSaveSettings();    }    private void TeamSelector_OnSelectionChanged(object sender, TeamSelectorSelectionChangedArgs args)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.TeamSelection = args.Selection;        _emps = TeamSelector.GetEmployeeData<EmployeeResourceModel>((row, rosters) => new EmployeeResourceModel(row, rosters));        DoSaveSettings();        Refresh();    }    private void JobSelector_OnSizeChanged(object sender, SizeChangedEventArgs e)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.SplitterPosition = TeamSelectorRow.Height.Value;        DoSaveSettings();    }    private void ViewWindow_OnSelectionChanged(object sender, SelectionChangedEventArgs e)    {        if (EventSuppressor.IsSet(Suppress.This))            return;         Properties.MonthsToView = (int)ViewWindow.SelectedValue;        DoSaveSettings();        Refresh();    }    private void HoursSelector_Down_Click(object sender, RoutedEventArgs e)    {        Properties.HoursPerDay = Math.Max(0.25F, Properties.HoursPerDay - 0.25);        HoursSelector.Text = $"{Properties.HoursPerDay:F2}";        DoSaveSettings();        Refresh();    }    private void HoursSelector_Up_Click(object sender, RoutedEventArgs e)    {        Properties.HoursPerDay = Math.Min(24F, Properties.HoursPerDay + 0.25);        HoursSelector.Text = $"{Properties.HoursPerDay:F2}";        DoSaveSettings();        Refresh();    }            private void ActivityType_OnSelectionChanged(object sender, SelectionChangedEventArgs e)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.ActivityType = (Guid)(ActivityType.SelectedValue ?? Guid.Empty);        DoSaveSettings();    }        private void AvailableEmployees_OnSizeChanged(object sender, SizeChangedEventArgs e)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.EmployeeSplitterPosition = AvailableEmployeesRow.Height.Value;        DoSaveSettings();    }    private void AvailableEmployees_OnOnAction(object sender, JobPlannerEmployee[] availables)    {        List<TimeSpan> edges = new List<TimeSpan>();        void CheckEdges(params TimeSpan[] times)        {            foreach (var time in times)            {                if (!edges.Contains(time))                    edges.Add(time);            }        }        bool IsRostered(RosterBlock[] blocks, TimeSpan start, TimeSpan finish)        {            foreach (var block in blocks)            {                if ((block.Start <= start) && (block.Finish >= finish))                    return true;            }            return false;        }        bool IsStandardLeave(DateTime date, TimeSpan start, TimeSpan finish)        {            return _standardleaves.Any(x =>                (x.From.Add(x.FromTime) <= date.Add(start))                && (x.To.Add(x.ToTime) >= date.Add(finish))            );        }                bool IsLeaveRequest(DateTime date, EmployeeResourceModel emp, TimeSpan start, TimeSpan finish)        {            return _leaverequests.Any(x =>                (x.EmployeeID == emp.ID)                && (x.From.Add(x.FromTime) <= date.Add(start))                && (x.To.Add(x.ToTime) >= date.Add(finish))            );        }        bool IsAssigned(AssignmentModel[] assignments, TimeSpan start, TimeSpan finish)        {            foreach (var assignment in assignments)            {                if ((assignment.BookedStart <= start) && (assignment.BookedFinish >= finish))                    return true;            }            return false;                        }                if (ExtractSelection(out Guid jobid, out DateTime[] dates))        {            if (dataGrid.ItemsSource is DataTable table)            {                List<Assignment> updates = new List<Assignment>();                foreach (var available in availables)                {                    foreach (var date in dates)                    {                        var emp = _emps.FirstOrDefault(x => x.ID == available.ID);                        if (emp != null)                        {                            var roster = RosterUtils.GetRoster(emp.Roster, emp.RosterStart, date);                            var blocks = roster?.GetBlocks(date, TimeSpan.MinValue, TimeSpan.MaxValue) ?? new RosterBlock[] { };                            foreach (var block in blocks)                                CheckEdges(block.Start, block.Finish);                            if (GetStandardLeaveTimes(date, out TimeSpan stdleavestart, out TimeSpan stdleavefinish))                                CheckEdges(stdleavestart, stdleavefinish);                            if (GetLeaveRequestTimes(date, emp, out TimeSpan leaverequeststart, out TimeSpan leaverequestfinish))                                CheckEdges(leaverequeststart, leaverequestfinish);                                                        var assignments = _assignments.Where(x => (x.Date == date) && (x.EmployeeID == emp.ID)).ToArray();                            foreach (var assignment in assignments)                                CheckEdges(assignment.BookedStart, assignment.BookedFinish);                            edges.Sort();                                                        double adjustment = 0.0F;                            for (int i = 0; i < edges.Count - 1; i++)                            {                                var start = edges[i];                                var finish = edges[i + 1];                                if (IsRostered(blocks, start, finish)                                     && (!IsStandardLeave(date,start,finish))                                    && (!IsLeaveRequest(date,emp,start,finish))                                    && !IsAssigned(assignments, start, finish))                                {                                    Assignment assignment = new Assignment();                                    assignment.ActivityLink.ID = Properties.ActivityType;                                    assignment.EmployeeLink.ID = emp.ID;                                    assignment.Date = date;                                    assignment.JobLink.ID = jobid;                                    assignment.Booked.Start = start;                                    assignment.Booked.Finish = finish;                                    assignment.Booked.Duration = finish - start;                                    updates.Add(assignment);                                    adjustment += assignment.Booked.Duration.TotalHours;                                }                            }                            System.Data.DataRow row = table.Rows.Find(date);                            row.BeginEdit();                            double jobvalue = (row[jobid.ToString()] == DBNull.Value)                                ? adjustment / Properties.HoursPerDay                                : (double)row[jobid.ToString()] + (adjustment / Properties.HoursPerDay);                            row[jobid.ToString()] = jobvalue <= 0F ? null : jobvalue;                            row["Available"] = Math.Max(0F, (double)row["Available"] - (adjustment / Properties.HoursPerDay));                            row.EndEdit();                        }                    }                    var entry = AvailableEmployees.Items.FirstOrDefault(x => x.ID == available.ID);                    if (entry != null)                    {                        AvailableEmployees.Items.Remove(entry);                        GetEmployee(entry.ID, AssignedEmployees.Items).Time += entry.Time;                    }                }                if (updates.Any())                {                    using (new WaitCursor())                    {                        new Client<Assignment>().Save(updates, "Assigned by Job Planner");                                                CoreTable temp = new CoreTable();                        temp.LoadColumns(typeof(Assignment));                        temp.LoadRows(updates);                        _assignments.AddRange(temp.Rows.Select(r => new AssignmentModel(r)));                        AssignedEmployees.Items = AssignedEmployees.Items.OrderBy(x => x.Name).ToList();                        AssignedEmployees.Refresh(false, true);                        AvailableEmployees.Refresh(false, true);                    }                }            }        }    }    private void AssignedEmployees_OnOnAction(object sender, JobPlannerEmployee[] employees)    {        if (ExtractSelection(out Guid jobid, out DateTime[] dates))        {            if (dataGrid.ItemsSource is DataTable table)            {                foreach (var date in dates)                {                    var emptimes = _assignments.Where(x =>                        (x.JobID == jobid)                        && (x.Date == date)                        && employees.Any(e => e.ID == x.EmployeeID)                    ).ToArray();                    var emptime = emptimes.Aggregate(TimeSpan.Zero, (time, ass) => time += ass.BookedDuration);                    System.Data.DataRow row = table.Rows.Find(date);                    row.BeginEdit();                    double value = (row[jobid.ToString()] == DBNull.Value)                        ? 0.0F                        : (double)row[jobid.ToString()] - (emptime.TotalHours / Properties.HoursPerDay);                    row[jobid.ToString()] = value <= 0F ? null : value;                    row["Available"] = (double)row["Available"] + (emptime.TotalHours/Properties.HoursPerDay);                    row.EndEdit();                }            }                        var assignments = _assignments.Where(x =>                (x.JobID == jobid)                && dates.Contains(x.Date)                && employees.Any(e => e.ID == x.EmployeeID)            ).ToArray();            if (assignments.Any())            {                using (new WaitCursor())                {                    var deletes = assignments.Select(x => new Assignment() { ID = x.ID }).ToArray();                    new Client<Assignment>().Delete(deletes, "Deleted from Job Planner");                    var removes = AssignedEmployees.Items.Where(x => employees.Any(e => e.ID == x.ID)).ToArray();                    foreach (var remove in removes)                    {                        AssignedEmployees.Items.Remove(remove);                        GetEmployee(remove.ID, AssignedEmployees.Items).Time += remove.Time;                    }                    AssignedEmployees.Refresh(false, true);                    AvailableEmployees.Items = AvailableEmployees.Items.OrderBy(x => x.Name).ToList();                    AvailableEmployees.Refresh(false, true);                    foreach (var assignment in assignments)                        _assignments.Remove(assignment);                }            }                    }    }    private void LeaveType_OnSelectionChanged(object sender, SelectionChangedEventArgs e)    {        if (EventSuppressor.IsSet(Suppress.This))            return;        Properties.IncludeUnApprovedLeave = LeaveType.SelectedIndex > 0;        DoSaveSettings();        Refresh();    }}
 |