using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Input; using System.Windows.Media.Imaging; using System.Windows.Threading; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using Syncfusion.UI.Xaml.Kanban; namespace PRSDesktop { /// /// Interaction logic for AttendancePanel.xaml /// public partial class AttendancePanel : UserControl, IPanel { private string _search = ""; public CoreTable Activities; private bool bIncludeInactive; private readonly DispatcherTimer columnsizer = new(); public CoreTable Employees; private readonly List Kanbans = new(); public CoreTable LeaveRequests; public CoreTable TimeSheets; public AttendancePanel() { InitializeComponent(); columnsizer.Interval = new TimeSpan(0, 0, 0, 0, 500); columnsizer.Tick += Columnsizer_Tick; columnsizer.IsEnabled = true; } public bool IsReady { get; set; } public event DataModelUpdateEvent OnUpdateDataModel; public Dictionary Selected() { return new Dictionary { { typeof(Employee).EntityName(), Employees.Rows.ToArray() }, { typeof(TimeSheet).EntityName(), TimeSheets.Rows.ToArray() } }; } public void CreateToolbarButtons(IPanelHost host) { } public string SectionName => "Attendance"; public DataModel DataModel(Selection selection) { var ids = selection != Selection.None ? Employees.ExtractValues(x => x.ID).ToArray() : new Guid[] { }; var filter = new Filter(x => x.ID).InList(ids); if (!bIncludeInactive) { filter.And(x => x.ShowOnInOutBoard); } return new AttendanceDataModel(filter); } public void Refresh() { using (var cursor = new WaitCursor()) { var query = new MultiQuery(); query.Add( new Filter(x => x.Date).IsEqualTo(DateTime.Today), new Columns(x => x.EmployeeLink.ID) .Add(x => x.Start) .Add(x => x.Finish) .Add(x => x.Address) .Add(x => x.ActivityLink.ID) .Add(x => x.SoftwareVersion), new SortOrder(x => x.Start) ); query.Add( new Filter(x => x.From).IsLessThanOrEqualTo(DateTime.Today) .And(x => x.To).IsGreaterThanOrEqualTo(DateTime.Today) .And(x => x.Status).IsEqualTo(LeaveRequestStatus.InProgress), new Columns(x => x.EmployeeLink.ID) .Add(x => x.From) .Add(x => x.FromTime) .Add(x => x.To) .Add(x => x.ToTime) .Add(x => x.LeaveType.ID) ); query.Query(); TimeSheets = query.Get(); LeaveRequests = query.Get(); foreach (var emprow in Employees.Rows) { var empid = emprow.Get(c => c.ID); var kanban = Kanbans.FirstOrDefault(x => string.Equals(x.ID, empid.ToString())); if (kanban != null) { var bOK = CheckTimeSheet(empid, kanban); if (!bOK) bOK = CheckLeave(empid, kanban); if (!bOK) UpdateKanban(kanban, TimeSpan.MinValue, TimeSpan.MinValue, "", "White", "Gainsboro", "", "" ); } } FilterKanbans(); } } public void Setup() { Kanban.Columns.Clear(); var filter = LookupFactory.DefineFilter(); //.And(x=>x.Type).IsEqualTo(EmployeeType.Employee); var tables = Client.QueryMultiple( new KeyedQueryDef( filter, new Columns( x => x.ID, x => x.Name, x => x.Thumbnail.ID, x => x.Group.ID, x => x.Group.Description, x => x.Type, x => x.UsualStart, x => x.UsualFinish, x => x.ShowOnInOutBoard ), new SortOrder(x => x.Name)), new KeyedQueryDef()); Employees = tables[nameof(Employee)]; Activities = tables[nameof(Activity)]; CreateColumns(); CreateKanbans(); var imageids = Employees.Rows .Select(r => r.EntityLinkID(x => x.Thumbnail) ?? Guid.Empty) .Where(x => x != Guid.Empty).ToArray(); new Client().Query( new Filter(x => x.ID).InList(imageids), new Columns( x => x.ID, x => x.Data ), null, (data, error) => ProcessImages(data) ); } public void Shutdown() { } public void Heartbeat(TimeSpan time) { } private void Columnsizer_Tick(object sender, EventArgs e) { columnsizer.IsEnabled = false; ResizeColumns(); columnsizer.IsEnabled = true; } private void ResizeColumns() { using (var d = Dispatcher.DisableProcessing()) { var CollapsedWidth = 50; var CollapsedColumns = 0; Array.ForEach(Kanban.Columns.ToArray(), x => { CollapsedColumns += x.IsExpanded ? 0 : 1; }); if (Kanban.Columns.Count > 0 && CollapsedColumns != Kanban.Columns.Count) { var ColumnWidth = (Kanban.ActualWidth - CollapsedColumns * CollapsedWidth) / (Kanban.Columns.Count - CollapsedColumns) - 2; if (ColumnWidth != Kanban.ColumnWidth) Kanban.ColumnWidth = ColumnWidth; } } } private void CreateKanbans() { foreach (var row in Employees.Rows) { var empid = row.Get(x => x.ID); var empname = row.Get(x => x.Name); var groupid = row.Get(x => x.Group.ID); var imgid = row.Get(x => x.Thumbnail.ID); var img = PRSDesktop.Resources.anonymous.AsBitmapImage(); var active = row.Get(x => x.ShowOnInOutBoard); var color = "White"; var kanban = new AttendanceKanban { ID = empid.ToString(), Name = empname, Category = groupid.ToString(), Image = img, Clockin = "", Clockout = "", Address = "", ColorKey = "White", TextColor = "Gainsboro", Type = "", Version = "", Active = active }; Kanbans.Add(kanban); } } private void CreateColumns() { //Dictionary columns = Employees.ToDictionary(x => x.ID, new System.Linq.Expressions.Expression>[] { x => x.Group.Description }); Kanban.Columns.Clear(); var columns = new List>(); foreach (var row in Employees.Rows) { var active = row.Get(c => c.ShowOnInOutBoard); if (bIncludeInactive || active) { var id = row.Get(c => c.Group.ID); var desc = row.Get(c => c.Group.Description); if (!columns.Any(x => x.Item1.Equals(id))) columns.Add(new Tuple(id, desc)); } } columns = columns.OrderBy(x => x.Item2).ToList(); foreach (var column in columns) { var newcol = new KanbanColumn { Title = column.Item1 != Guid.Empty ? column.Item2 : "(No Group Assigned)", Categories = column.Item1.ToString() }; newcol.AllowDrag = false; newcol.AllowDrop = false; Kanban.Columns.Add(newcol); } } private void UpdateKanban(AttendanceKanban kanban, TimeSpan start, TimeSpan finish, string address, string background, string foreground, string description, string version) { kanban.Clockin = start.TotalMilliseconds > 0 ? string.Format("{0:hh\\:mm}", start) : ""; kanban.Clockout = finish.TotalMilliseconds > 0 ? string.Format("{0:hh\\:mm}", finish) : ""; kanban.Address = address; kanban.ColorKey = background; kanban.TextColor = foreground; kanban.Type = description; kanban.Version = version; } private bool CheckTimeSheet(Guid empid, AttendanceKanban kanban) { var firstrow = TimeSheets.Rows.FirstOrDefault(r => r.Get(c => c.EmployeeLink.ID).Equals(empid)); if (firstrow == null) return false; var lastrow = TimeSheets.Rows.LastOrDefault(r => r.Get(c => c.EmployeeLink.ID).Equals(empid)); var actid = lastrow.Get(c => c.ActivityLink.ID); var actrow = Equals(actid, Guid.Empty) ? null : Activities.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(actid)); var color = "White"; var finish = lastrow.Get(c => c.Finish); if (finish.Ticks > 0 && finish < DateTime.Now - DateTime.Today) { color = "Gainsboro"; } else { color = actrow != null ? actrow.Get(c => c.Color) : ""; if (string.IsNullOrWhiteSpace(color)) color = "LightGreen"; } UpdateKanban(kanban, firstrow.Get(c => c.Start), lastrow.Get(c => c.Finish), lastrow.Get(c => c.Address), color, kanban.TextColor = "Black", actrow != null ? actrow.Get(c => c.Description) : "", lastrow.Get(c => c.SoftwareVersion) ); //kanban.Clockin = firstrow != null ? String.Format("{0:hh\\:mm}", firstrow.Get(c => c.Start)) : ""; //kanban.Clockout = (lastrow != null) && (lastrow.Get(c => c.Finish).Ticks > 0) ? String.Format("{0:hh\\:mm}", lastrow.Get(c => c.Finish)) : ""; //kanban.Address = lastrow != null ? lastrow.Get(c => c.Address) : ""; //kanban.ColorKey = color; //kanban.TextColor = lastrow != null ? "Black" : "Gainsboro"; //kanban.Type = actrow != null ? actrow.Get(c => c.Description) : ""; //kanban.Version = lastrow != null ? lastrow.Get(c => c.SoftwareVersion) : ""; return true; } private bool CheckLeave(Guid empid, AttendanceKanban kanban) { var row = LeaveRequests.Rows.FirstOrDefault(r => r.Get(c => c.EmployeeLink.ID) == empid); if (row == null) return false; var actid = row.Get(c => c.LeaveType.ID); var actrow = Equals(actid, Guid.Empty) ? null : Activities.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(actid)); var color = actrow?.Get(c => c.Color); if (string.IsNullOrWhiteSpace(color)) color = "Gainsboro"; var description = actrow?.Get(c => c.Description); if (string.IsNullOrWhiteSpace(description)) description = "Leave"; UpdateKanban(kanban, row.Get(c => c.From) == DateTime.Today ? row.Get(c => c.FromTime) : TimeSpan.MinValue, row.Get(c => c.To) == DateTime.Today ? row.Get(c => c.ToTime) : TimeSpan.MinValue, "", color, kanban.TextColor = "Black", description, "" ); return true; } private void FilterKanbans() { var visible = Kanbans.Where(x => (x.Name?.ToUpper().Contains(_search.ToUpper()) == true || x.Address?.ToUpper().Contains(_search.ToUpper()) == true) && (bIncludeInactive || x.Active) ); Kanban.ItemsSource = visible; } private void ProcessImages(CoreTable data) { foreach (var row in data.Rows) { var imageid = row.Get(c => c.ID); BitmapImage img = null; var empids = Employees.Rows.Where(r => r.Get(c => c.Thumbnail.ID).Equals(imageid)) .Select(r => r.Get(c => c.ID)); foreach (var empid in empids) { var kanban = Kanbans.FirstOrDefault(x => string.Equals(x.ID, empid.ToString())); if (kanban != null) { if (img == null) { img = new BitmapImage(); img.LoadImage(row.Get(c => c.Data)); } kanban.Image = img; } } } Dispatcher.Invoke(() => { FilterKanbans(); }); } private void AttendanceMenu_Opened(object sender, RoutedEventArgs e) { var menu = sender as ContextMenu; var model = menu.Tag as AttendanceKanban; var sick = menu.Items[0] as MenuItem; var onoff = menu.Items[2] as MenuItem; if (string.IsNullOrWhiteSpace(model.Clockin) || !string.IsNullOrWhiteSpace(model.Clockout)) onoff.Header = "Clock Employee On to PRS"; else onoff.Header = "Clock Employee Out of PRS"; var show = menu.Items[4] as MenuItem; show.Visibility = !model.Active ? Visibility.Visible : Visibility.Collapsed; var hide = menu.Items[5] as MenuItem; hide.Visibility = model.Active ? Visibility.Visible : Visibility.Collapsed; } private void SickLeave_Click(object sender, RoutedEventArgs e) { var actrow = Activities.Rows.FirstOrDefault( r => r.Get(c => c.IsLeave) && r.Get(c => c.IsDefault) ); if (actrow == null) { MessageBox.Show("You must set up a default Sick Leave Activity before using this option!"); return; } var item = (MenuItem)sender; var model = (AttendanceKanban)item.Tag; var empid = Guid.Parse(model.ID); var row = Employees.Rows.FirstOrDefault(r => r.Get(c => c.ID).Equals(empid)); if (row == null) { MessageBox.Show("Cannot Find Employee: " + empid); return; } var emp = row.ToObject(); var request = new LeaveRequest(); request.EmployeeLink.ID = empid; request.From = DateTime.Today; request.FromTime = DateTime.Now.TimeOfDay; request.To = DateTime.Today; request.ToTime = emp.UsualFinish != TimeSpan.FromMilliseconds(0) ? emp.UsualFinish : new TimeSpan(23, 59, 59); request.Status = LeaveRequestStatus.InProgress; request.LeaveType.ID = actrow.Get(c => c.ID); request.Notes = string.Format("Marked As Sick at {0:hh\\:mm} by {1}", DateTime.Now, ClientFactory.UserID); if (new LeaveRequests().EditItems(new[] { request })) Refresh(); } private void ClockOnOff_Click(object sender, RoutedEventArgs e) { var item = (MenuItem)sender; var model = (AttendanceKanban)item.Tag; var empid = Guid.Parse(model.ID); var bOK = true; var time = new TimeSpan(DateTime.Now.Hour, DateTime.Now.Minute, 0); if (Security.IsAllowed()) bOK = TimeEdit.Execute("Enter Time", ref time); if (!bOK) return; if (string.IsNullOrWhiteSpace(model.Clockin) || !string.IsNullOrWhiteSpace(model.Clockout)) { var timesheet = new TimeSheet(); timesheet.EmployeeLink.ID = empid; timesheet.Date = DateTime.Today; timesheet.Start = time; timesheet.Notes = string.Format("Clocked in at {0:hh\\:mm} by {1}", DateTime.Now, ClientFactory.UserID); new Client().Save(timesheet, "Clocked on from In/Out Board"); Refresh(); } else { var timesheet = new Client().Load( new Filter(x => x.Date).IsEqualTo(DateTime.Today).And(x => x.Finish).IsEqualTo(new TimeSpan()) .And(x => x.EmployeeLink.ID) .IsEqualTo(empid), new SortOrder(x => x.Start) ).LastOrDefault(); if (timesheet != null) { if (!string.IsNullOrWhiteSpace(timesheet.Notes)) timesheet.Notes = timesheet.Notes + "\n"; else timesheet.Notes = ""; timesheet.Notes = string.Format("{0}Clocked out at {1:hh\\:mm} by {2}", timesheet.Notes, DateTime.Now, ClientFactory.UserID); timesheet.Finish = time; new Client().Save(timesheet, "Clocked off from In/Out Board"); Refresh(); } } } private void Search_KeyUp(object sender, KeyEventArgs e) { _search = Search.Text; if (string.IsNullOrWhiteSpace(Search.Text) || e.Key == Key.Return) FilterKanbans(); } private void Export_Click(object sender, RoutedEventArgs e) { var form = new DynamicExportForm(typeof(TimeSheet), TimeSheets.Columns.Select(x => x.ColumnName)); if (form.ShowDialog() != true) return; var export = new Client().Query( new Filter(x => x.Date).IsEqualTo(DateTime.Today), new Columns(form.Fields), LookupFactory.DefineSort() ); ExcelExporter.DoExport(export, string.Format("Attendance {0:dd-MMM-yy}", DateTime.Today)); } private void ShowAll_Click(object sender, RoutedEventArgs e) { if (string.Equals(ShowAll.Content as string, "Show All")) { ShowAll.Content = "Hide Inactive"; bIncludeInactive = true; } else { ShowAll.Content = "Show All"; bIncludeInactive = false; } FilterKanbans(); } private void ShowOnInOut_Click(object sender, RoutedEventArgs e) { var menu = sender as MenuItem; var model = menu.Tag as AttendanceKanban; UpdateInOutStatus(model, true); } private void UpdateInOutStatus(AttendanceKanban model, bool include) { var id = Guid.Parse(model.ID); var row = Employees.Rows.FirstOrDefault(r => r.Get(c => c.ID) == id); if (row != null) { var emp = new Employee { ID = id, ShowOnInOutBoard = include }; new Client().Save(emp, include ? "Added To" : "Removed From" + " In/Out Board", (o, e) => { }); row.Set(x => x.ShowOnInOutBoard, include); model.Active = include; } } private void RemoveFromInOut_Click(object sender, RoutedEventArgs e) { var menu = sender as MenuItem; var model = menu.Tag as AttendanceKanban; UpdateInOutStatus(model, false); FilterKanbans(); } } }