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 Syncfusion.UI.Xaml.Grid; using Syncfusion.Windows.Tools.Controls; using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs; 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 _assignments = new List(); public JobResourcePlannerProperties Properties { get; set; } public event LoadSettings? LoadSettings; public event SaveSettings? 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(r => new JobModel(r)); TeamSelector.Setup(); TeamSelector.Settings = Properties.TeamSettings; TeamSelector.Selection = Properties.TeamSelection; _emps = TeamSelector.GetEmployeeData(r => new EmployeeResourceModel(r)); ViewWindow.ItemsSource = new Dictionary() { { 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( LookupFactory.DefineFilter(), StandardLeaveModel.Columns ); query.Add( new Filter(x => x.Status).IsNotEqualTo(LeaveRequestStatus.Rejected), LeaveRequestModel.Columns ); query.Add( LookupFactory.DefineFilter(), new Columns(x => x.ID).Add(x => x.Code).Add(x => x.Description), new SortOrder(x => x.Code) ); query.Query(); _standardleaves = query.Get().Rows.Select(r=>new StandardLeaveModel(r)).ToArray(); _leaverequests = query.Get().Rows.Select(r => new LeaveRequestModel(r)).ToArray(); _activities = query.Get().Rows.Select(r => new ActivityModel(r)).ToArray(); ActivityType.ItemsSource = _activities; ActivityType.SelectedValue = Properties.ActivityType; } } public void Shutdown() { } 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( new Filter(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().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.Start, date); var hours = roster.GetBlocks(date, TimeSpan.MinValue, TimeSpan.MaxValue) .Aggregate(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 { date }; var anyjobstoday = _assignments.Where(x => (x.Date.Date == date.Date)); avail -= anyjobstoday.Aggregate(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(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(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(value.Path.Path))); style.Setters.Add(new Setter(ForegroundProperty, CreateBinding(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(r => new EmployeeResourceModel(r)); } 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().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 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 availableemployees = new List(); foreach (var emp in _emps) { foreach (var date in dates) { var roster = RosterUtils.GetRoster(emp.Roster, emp.Start, 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 assignedemployees = new List(); 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(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(x => new EmployeeResourceModel(x)); 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 edges = new List(); 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 updates = new List(); 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.Start, 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().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().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(); } } }