using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Controls.Primitives; using Comal.Classes; using InABox.Clients; using InABox.Configuration; using InABox.Core; using InABox.DynamicGrid; using InABox.WPF; using Syncfusion.Pdf.Graphics; using Syncfusion.Pdf; using System.Windows.Data; using System.Windows.Media; using System.Drawing; using System.Globalization; namespace PRSDesktop { public class TaskPanelProperties : BaseObject, IGlobalConfigurationSettings { [CheckBoxEditor(ToolTip = "Require that all tasks are given a task type.")] public bool RequireTaskTypes { get; set; } = false; } /// /// Interaction logic for TaskPanel.xaml /// public partial class TaskPanel : UserControl, IPanel, ITaskHost, IJobControl, IPropertiesPanel { private bool _bTabChanging; public Guid MyID { get; set; } = CoreUtils.FullGuid; private KanbanType[] kanbanTypes = null!; // Initialized in Setup() public IList KanbanTypes => kanbanTypes; public TaskPanel() { InitializeComponent(); foreach (TabItem tab in TaskPanels.Items) { var panel = (tab.Content as ITaskControl)!; _viewmap[panel.KanbanViewType] = tab; panel.Host = this; } if (MyID == CoreUtils.FullGuid) { var row = new Client() .Query(new Filter(x => x.UserLink.ID).IsEqualTo(ClientFactory.UserGuid), new Columns(x => x.ID)).Rows .FirstOrDefault(); if (row != null) MyID = row.Get(x => x.ID); } } public Guid ParentID { get; set; } public JobPanelSettings Settings { get; set; } private void ChangeStatus(ITaskControl control, object o, RoutedEventArgs e, string status) { if (MessageBox.Show($"Are you sure you want to mark the selected tasks as {status}?", "Confirm Change Status", MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; var tasks = (((MenuItem)e.Source).Tag as IEnumerable)!; Progress.ShowModal("Changing Status", progress => { var kanbans = LoadKanbans(tasks, new Columns(x => x.ID, x => x.Completed, x => x.Category)); foreach (var kanban in kanbans) { if(status == "Complete") { kanban.Completed = DateTime.Now; } kanban.Category = status; } new Client().Save(kanbans, $"Kanban Marked as {status}"); }); control.Refresh(true); } private void CompleteTask(ITaskControl control, RoutedEventArgs e, DateTime completed) { if (MessageBox.Show($"Are you sure you want to complete the selected tasks?", "Confirm Completion", MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; var tasks = (((FrameworkElement)e.Source).Tag as IEnumerable)!; Progress.ShowModal("Completing Tasks", progress => { var kanbans = LoadKanbans(tasks, new Columns(x => x.ID, x => x.Completed, x => x.Category)); foreach (var kanban in kanbans) { kanban.Completed = completed; kanban.Category = "Complete"; } new Client().Save(kanbans, $"Kanban Marked as Complete"); }); control.Refresh(true); } private void AddChangeStatusButton(ITaskControl control, TaskModel[] models, MenuItem menu, string header, string status) { var item = new MenuItem { Tag = models, Header = header }; item.Click += (o, e) => ChangeStatus(control, o, e, status); menu.Items.Add(item); } public bool CanChangeTasks(IEnumerable models) { foreach (var task in models) { if (!MyID.Equals(task.ManagerID) && !MyID.Equals(task.EmployeeID)) { // If you can change others tasks, IsFullControl is true - but we don't check at the beginning of the function // to save checking security tokens every time. return Security.IsAllowed(); } } return true; } /// /// should have set to a . /// /// /// public void PopulateMenu(ITaskControl control, ContextMenu menu) { menu.Items.Clear(); var models = control.SelectedModels((menu.Tag as TaskModel)!).ToArray(); var references = GetReferences(models); var bLinks = references.Any(x => x.ReferenceType() != null); var referencetypes = references.Select(x => x.ReferenceType()).Distinct().ToArray(); var bSingle = models.Length == 1; var canChange = CanChangeTasks(models); var edit = new MenuItem { Tag = models, Header = referencetypes.SingleOrDefault() == typeof(Requisition) ? "Edit Requisition Details" : referencetypes.SingleOrDefault() == typeof(Setout) ? "Edit Setout Details" : referencetypes.SingleOrDefault() == typeof(Delivery) ? "Edit Delivery Details" : referencetypes.SingleOrDefault() == typeof(PurchaseOrder) ? "Edit Order Details" : "Edit Task" + (bSingle ? "" : "s") }; edit.Click += (o, e) => { var tasks = (((MenuItem)e.Source).Tag as IEnumerable)!; if (EditReferences(tasks)) control.Refresh(true); e.Handled = true; }; edit.IsEnabled = referencetypes.Length == 1; menu.Items.Add(edit); if (!bLinks && models.Length == 1) { var digitalForms = new MenuItem { Header = "Digital Forms" }; var model = models.First(); Guid kanbanID = Guid.Parse(model.ID); DynamicGridUtils.PopulateFormMenu( digitalForms, kanbanID, () => new Client().Load(new Filter(x => x.ID).IsEqualTo(kanbanID)).First(), model.EmployeeID == MyID); menu.Items.Add(digitalForms); } if (!models.Any(x => !x.CompletedDate.IsEmpty()) && !bLinks) { menu.Items.Add(new Separator()); var job = new MenuItem { Tag = models, Header = "Link to Job" }; job.SubmenuOpened += (o, e) => CreateJobSubMenu(control, job, models); menu.Items.Add(job); if (bSingle) { menu.AddItem("Create Setout from Task", null, models.First(), task => { var jobID = task.JobID; if (task.JobID.Equals(Guid.Empty)) { jobID = ParentID; } if (MessageBox.Show("This will convert this task into a Setout.\n\nDo you wish to continue?", "Confirmation", MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; ManufacturingTemplate? template = new Client() .Load(new Filter(x => x.Code).IsEqualTo("PRS")).FirstOrDefault(); if (template == null) { MessageBox.Show("[Pressing] Template does not exist!"); return; } string? setoutNumber = null; Kanban? kanban = null; ManufacturingTemplateStage[] tstages = Array.Empty(); Progress.ShowModal("Creating Setout", (progress) => { var kanbanFilter = new Filter(x => x.ID).IsEqualTo(task.ID); var tables = Client.QueryMultiple(new Dictionary { { "ManufacturingTemplateStage", new QueryDef( new Filter(x => x.Template.ID).IsEqualTo(template.ID), null, new SortOrder(x => x.Sequence)) }, { "Kanban", new QueryDef( kanbanFilter, null, null) }, { "Setout", new QueryDef( new Filter(x => x.JobLink.ID) .InQuery(new SubQuery(kanbanFilter, new Column(x => x.JobLink.ID))), new Columns(x => x.JobLink.JobNumber, x => x.Number), null) } }); tstages = tables["ManufacturingTemplateStage"].Rows .Select(x => x.ToObject()).ToArray(); kanban = tables["Kanban"].Rows.FirstOrDefault()?.ToObject(); if (kanban == null) { MessageBox.Show("Task does not exist!"); return; } progress.Report("Creating Setouts"); CoreTable setouts = tables["Setout"]; int ireq = 0; string sreq = ""; while (true) { ireq++; sreq = string.Format("{0}-{1:yyMMdd}-{2}", kanban.JobLink.JobNumber, DateTime.Now, ireq); if (!setouts.Rows.Any(r => sreq.Equals(r.Get(c => c.Number)))) break; } setoutNumber = sreq; }); if (setoutNumber == null || kanban == null) { return; } var result = CreateSetout( task, s => { s.Number = setoutNumber; s.JobLink.ID = jobID; var notes = kanban.Notes.ToList(); var description = kanban.Summary; if (string.IsNullOrWhiteSpace(description)) { description = CoreUtils.StripHTML(kanban.Description); } if (!string.IsNullOrWhiteSpace(description)) { notes.Insert(0, description); } s.Description = string.Join("\n==========================================\n", notes); } ); if (result != null) { Progress.ShowModal("Creating Manufacturing Packet", progress => { ManufacturingPacket packet = new ManufacturingPacket() { Serial = template.Code, Title = kanban.Title, Quantity = 1, BarcodeQty = 1, DueDate = kanban.DueDate }; packet.ManufacturingTemplateLink.ID = template.ID; packet.ManufacturingTemplateLink.Code = template.Code; packet.ManufacturingTemplateLink.Factory.ID = template.Factory.ID; packet.SetoutLink.ID = result.ID; new Client().Save(packet, "Created from Task"); DoLink(task, packet.ID); List pstages = new List(); foreach (var tstage in tstages) { var pstage = new ManufacturingPacketStage() { Time = tstage.Time, Sequence = tstage.Sequence, SequenceType = tstage.SequenceType, Started = DateTime.MinValue, PercentageComplete = 0.0F, Completed = DateTime.MinValue, QualityChecks = tstage.QualityChecks, QualityStatus = QualityStatus.NotChecked, QualityNotes = "", }; pstage.Parent.ID = packet.ID; pstage.ManufacturingSectionLink.ID = tstage.Section.ID; pstage.ManufacturingSectionLink.Name = tstage.Section.Name; pstages.Add(pstage); } new Client().Save(pstages, "Created from Task", (_, __) => { }); progress.Report("Processing Documents"); List _setoutdocuments = new List(); List _kanbandocuments = new List(); KanbanDocument[] docrefs = new Client() .Load(new Filter(x => x.EntityLink.ID).IsEqualTo(kanban.ID)); foreach (var docref in docrefs) { // Convert the document to a PDF var docid = ProcessKanbanDocument(docref); var newdoc = new SetoutDocument(); newdoc.EntityLink.ID = result.ID; newdoc.DocumentLink.ID = docid; _setoutdocuments.Add(newdoc); if (docid != docref.DocumentLink.ID) { docref.DocumentLink.ID = docid; _kanbandocuments.Add(docref); } } new Client().Save(_setoutdocuments, "Converted from Task", (_, __) => { }); new Client().Save(_kanbandocuments, "Converted to PDF", (_, __) => { }); progress.Report("Updating Task"); kanban.Title = kanban.Title + " (" + result.Number + ")"; new Client().Save(kanban, "Converting Kanban to Setout"); }); control.Refresh(true); } }); menu.AddItem("Create Requisition from Task", null, models, tasks => { var taskModel = tasks.First(); var jobID = taskModel.JobID; if (taskModel.JobID.Equals(Guid.Empty)) { jobID = ParentID; } var kanbanTable = new Client().Query(new Filter(x => x.ID).IsEqualTo(taskModel.ID)); var kanban = kanbanTable.Rows.First().ToObject(); var result = CreateRequisition( taskModel, r => { r.RequestedBy.ID = kanban.ManagerLink.ID; r.Employee.ID = Guid.Empty; r.Title = kanban.Title; r.Request = string.IsNullOrWhiteSpace(kanban.Summary) ? String.IsNullOrWhiteSpace(kanban.Description) ? String.Join("\n", kanban.Notes) : CoreUtils.StripHTML(kanban.Description) : kanban.Summary; r.Notes = kanban.Notes; r.Due = kanban.DueDate; r.JobLink.ID = jobID; } ); if (result != null) { Progress.ShowModal("Updating Documents", progress => { progress.Report("Updating Documents"); List requiDocuments = new(); KanbanDocument[] kanbanDocuments = new Client() .Load(new Filter(x => x.EntityLink.ID).IsEqualTo(kanban.ID)); foreach (var document in kanbanDocuments) { var newdoc = new RequisitionDocument(); newdoc.EntityLink.ID = result.ID; newdoc.DocumentLink.ID = document.DocumentLink.ID; requiDocuments.Add(newdoc); } new Client().Save(requiDocuments, "Converted from Task", (_, __) => { }); /*RequisitionKanban link = new(); link.Entity.ID = result.ID; link.Kanban.ID = kanban.ID; new Client().Save(link, "Converting Task -> Requisition", (_, __) => { });*/ progress.Report("Updating Task"); kanban.Category = "Open"; kanban.Completed = DateTime.MinValue; kanban.Title += $" (Requi #{result.Number})"; new Client().Save(kanban, "Converted to Requisition", (_, __) => { }); }); MessageBox.Show(String.Format("Created Requisition {0}", result.Number)); control.Refresh(true); } }); menu.AddItem("Create Delivery from Task", null, models, tasks => { var result = CreateDelivery( tasks.First(), d => { // Post-Process Requi Here } ); if (result != null) control.Refresh(true); }); menu.AddItem("Create Purchase Order from Task", null, models, tasks => { var result = CreateOrder( tasks.First(), p => { // Post-Process Requi Here } ); if (result != null) control.Refresh(true); }); } } if (!bLinks && canChange) { menu.Items.Add(new Separator()); var changeStatus = new MenuItem { Header = "Change Status" }; AddChangeStatusButton(control, models, changeStatus, "Open", "Open"); AddChangeStatusButton(control, models, changeStatus, "In Progress", "In Progress"); AddChangeStatusButton(control, models, changeStatus, "Waiting", "Waiting"); if (models.Any(x => x.CompletedDate.IsEmpty())) { var complete = new MenuItem { Tag = models, Header = models.Length > 1 ? "Complete Tasks" : "Complete Task" }; complete.Click += (o, e) => { CompleteTask(control, e, DateTime.Now); }; menu.Items.Add(complete); if (Security.IsAllowed()) { var completeDate = new MenuItem { Tag = models, Header = "Set Completed Date" }; var dateItem = new MenuItem(); var dateCalendar = new System.Windows.Controls.Calendar { SelectedDate = DateTime.MinValue }; dateCalendar.Tag = models; dateCalendar.SelectedDatesChanged += (o, e) => { if (e.Source is not System.Windows.Controls.Calendar calendar) return; menu.IsOpen = false; var selectedDate = calendar.SelectedDate ?? DateTime.Now; CompleteTask(control, e, selectedDate); }; dateItem.Header = dateCalendar; dateItem.Style = Resources["calendarItem"] as Style; completeDate.Items.Add(dateItem); menu.Items.Add(completeDate); } } else { menu.AddItem(models.Length > 1 ? "Archive Tasks" : "Archive Task", null, models, tasks => { if (MessageBox.Show("Are you sure you want to remove the selected tasks from the list?", "Confirm removal", MessageBoxButton.YesNo) != MessageBoxResult.Yes) return; Progress.ShowModal("Closing Kanbans", progress => { var kanbans = LoadKanbans(tasks, new Columns(x => x.ID, x => x.Closed)); foreach (var kanban in kanbans) kanban.Closed = DateTime.Now; new Client().Save(kanbans, "Kanban Marked as Closed"); }); control.Refresh(true); }); } menu.Items.Add(changeStatus); var changeType = new MenuItem { Header = "Change Task Type", Tag = models }; foreach(var type in KanbanTypes) { changeType.AddItem($"{type.Code}: {type.Description}", null, type, type => { Progress.ShowModal("Changing Task Type", progress => { var kanbans = LoadKanbans(models, new Columns(x => x.ID, x => x.Type.ID)); foreach (var kanban in kanbans) { kanban.Type.ID = type.ID; } new Client().Save(kanbans, $"Kanban Task Type changed to {type}"); }); control.Refresh(true); }); } menu.Items.Add(changeType); var changeDueDate = new MenuItem { Header = "Change Due Date" }; var calendarItem = new MenuItem(); var calendar = new System.Windows.Controls.Calendar { SelectedDate = models.Length == 1 ? models[0].DueDate : DateTime.Today }; calendar.Tag = models; calendar.SelectedDatesChanged += (o, e) => { if (e.Source is not System.Windows.Controls.Calendar calendar) return; var selectedDate = calendar.SelectedDate ?? DateTime.Now; var models = (calendar.Tag as IList)!; Progress.ShowModal("Changing Due Date", progress => { var kanbans = LoadKanbans(models, new Columns(x => x.ID, x => x.DueDate)); foreach (var kanban in kanbans) { kanban.DueDate = selectedDate; } new Client().Save(kanbans, $"Kanban Due Date changed to {selectedDate:dd MMM yyyy}"); }); control.Refresh(true); menu.IsOpen = false; }; calendarItem.Header = calendar; calendarItem.Style = Resources["calendarItem"] as Style; changeDueDate.Items.Add(calendarItem); menu.Items.Add(changeDueDate); } } /// /// Takes a , and if it is a .txt or an image (".png", ".jpg", ".jpeg" or ".bmp"), converts to a PDF /// with the content of the document, saving a new document with extension changed to ".pdf". /// /// The original document. /// /// The ID of the new or, /// if not one of the given types, the original document ID. /// private static Guid ProcessKanbanDocument(KanbanDocument docref) { var result = docref.DocumentLink.ID; var ext = System.IO.Path.GetExtension(docref.DocumentLink.FileName).ToLower(); if (ext.EndsWith("txt")) { var doc = new Client().Load(new Filter(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault(); if (doc is null) { Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!"); return docref.DocumentLink.ID; } PdfDocument pdf = new PdfDocument(); PdfPage page = pdf.Pages.Add(); PdfGraphics graphics = page.Graphics; PdfFont font = new PdfStandardFont(PdfFontFamily.Courier, 12); String text = System.Text.Encoding.UTF8.GetString(doc.Data); graphics.DrawString(text, font, PdfBrushes.Black, new PointF(0, 0)); MemoryStream ms = new MemoryStream(); pdf.Save(ms); pdf.Close(true); byte[] data = ms.ToArray(); var newdoc = new Document() { Data = data, FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"), CRC = CoreUtils.CalculateCRC(data), TimeStamp = DateTime.Now, }; new Client().Save(newdoc, "Converted from Text"); return newdoc.ID; } else if (ext.EndsWith("png") || ext.EndsWith("bmp") || ext.EndsWith("jpg") || ext.EndsWith("jpeg")) { var doc = new Client().Load(new Filter(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault(); if (doc is null) { Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!"); return docref.DocumentLink.ID; } PdfBitmap image = new PdfBitmap(new MemoryStream(doc.Data)); PdfDocument pdf = new PdfDocument(); pdf.PageSettings.Orientation = image.Height > image.Width ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape; pdf.PageSettings.Size = new SizeF(image.Width, image.Height); PdfPage page = pdf.Pages.Add(); PdfGraphics graphics = page.Graphics; graphics.DrawImage(image, 0.0F, 0.0F); MemoryStream ms = new MemoryStream(); pdf.Save(ms); pdf.Close(true); byte[] data = ms.ToArray(); var newdoc = new Document() { Data = data, FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"), CRC = CoreUtils.CalculateCRC(data), TimeStamp = DateTime.Now, }; new Client().Save(newdoc, "Converted from Image"); return newdoc.ID; } return result; } private void TaskPanels_SelectionChanged(object sender, SelectionChangedEventArgs e) { if (!IsReady) return; if (e.Source is not TabControl) return; if (_bTabChanging) return; try { _bTabChanging = true; var panel = GetCurrentPanel(); if(panel is not null) { KanbanSettings.ViewType = panel.KanbanViewType; new UserConfiguration().Save(KanbanSettings); panel.Refresh(false); } } finally { _bTabChanging = false; } } private void CreateJobSubMenu(ITaskControl control, MenuItem job, IEnumerable tasks) { job.Items.Clear(); job.Items.Add(new MenuItem { Header = "Loading...", IsEnabled = false }); using (new WaitCursor()) { job.Items.Clear(); var jobs = new Client().Query( LookupFactory.DefineFilter(), LookupFactory.DefineColumns(), LookupFactory.DefineSort() ); foreach (var row in jobs.Rows) { var jobNumber = row.Get(x => x.JobNumber); var jobName = row.Get(x => x.Name); job.AddItem($"{jobNumber}: {jobName}", null, tasks, tasks => { using (new WaitCursor()) { var kanbans = LoadKanbans(tasks, new Columns(x => x.ID, x => x.JobLink.ID)); foreach (var kanban in kanbans) kanban.JobLink.ID = row.Get(x => x.ID); new Client().Save(kanbans, "Updated Job Number"); control.Refresh(false); } }); } } } #region Get/Save Settings private KanbanSettings? _settings; public KanbanSettings KanbanSettings { get { _settings ??= new UserConfiguration().Load(); return _settings; } } public void SaveSettings() { if(_settings != null) new UserConfiguration().Save(_settings); } #endregion #region IPanel Stuff public event DataModelUpdateEvent? OnUpdateDataModel; public bool IsReady { get; set; } public void CreateToolbarButtons(IPanelHost host) { host.CreatePanelAction( new PanelAction { Caption = "New Task", OnExecute = a => { if(CreateKanban(k => { }) != null) { Refresh(); } }, Image = PRSDesktop.Resources.add } ); } public Dictionary Selected() { return new Dictionary(); } public void Heartbeat(TimeSpan time) { } private readonly Dictionary _viewmap = new(); private readonly List _initialized = new(); private ITaskControl GetCurrentPanel() { var result = (TaskPanels.SelectedContent as ITaskControl)!; if (result == null) result = (TaskPanels.Items[0] as DynamicTabItem)?.Content as ITaskControl; try { //if (result != null) if (!_initialized.Contains(result)) { result.Setup(); result.IsReady = true; _initialized.Add(result); } } catch (Exception e) { Logger.Send(LogType.Error, "", $"Error in TaskPanel.GetCurrentPanel: {CoreUtils.FormatException(e)}"); } return result; } public void Setup() { _settings = new UserConfiguration().Load(); TaskPanels.SelectedItem = _viewmap[_settings.ViewType]; kanbanTypes = new Client() .Query(new Filter(x => x.Hidden).IsEqualTo(false), new Columns(x => x.ID, x => x.Code, x => x.Description)) .Rows.Select(x => x.ToObject()).ToArray(); } public void Shutdown() { } public void Refresh() { if (ParentID == Guid.Empty) { if (TaskPanels.SelectedItem == TasksPlannerTabItem) TaskPanels.SelectedItem = _viewmap[KanbanViewType.Status]; if (TasksPlannerTabItem.Visibility == Visibility.Visible) TasksPlannerTabItem.Visibility = Visibility.Collapsed; } else { if (TasksPlannerTabItem.Visibility == Visibility.Collapsed) TasksPlannerTabItem.Visibility = Visibility.Visible; } TasksPlannerTabItem.Visibility = ParentID != Guid.Empty ? Visibility.Visible : Visibility.Collapsed; GetCurrentPanel()?.Refresh(false); } public string SectionName => GetCurrentPanel().SectionName; public TaskPanelProperties Properties { get; set; } public DataModel DataModel(Selection selection) { return GetCurrentPanel().DataModel(selection); //return new AutoDataModel(new Filter(x => x.ID).IsEqualTo(Guid.Empty)); } #endregion #region CRUD Functionality private TEntity? DoCreate(Action customise) where TEntity : Entity, IRemotable, IPersistent, new() { var result = new TEntity(); customise?.Invoke(result); if (DoEdit(new[] { result }, null)) return result; return null; } private readonly Dictionary _grids = new(); private readonly List> _entitycache = new(); private DynamicDataGrid GetGrid() where TEntity : Entity, IRemotable, IPersistent, new() { if(!_grids.TryGetValue(typeof(TEntity), out var grid)) { grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(TEntity)) as DynamicDataGrid)!; _grids[typeof(TEntity)] = grid; if (typeof(TEntity) == typeof(Kanban)) { CustomiseKanbanGrid((grid as DynamicDataGrid)!); } } return (grid as DynamicDataGrid)!; } private IEnumerable DoLoad(IEnumerable models, Columns columns) where TEntity : Entity, IRemotable, IPersistent, new() { var result = new List(); var load = new List(); foreach (var model in models) { var id = Guid.Parse(model.ID); var entity = _entitycache.FirstOrDefault(x => Equals(x.Item1, id) && x.Item2 is TEntity) as TEntity; if (entity is not null) result.Add(entity); else load.Add(id); } if (load.Any()) { var entities = new Client() .Query(new Filter(x => x.ID).InList(load.ToArray()), columns) .Rows.Select(x => x.ToObject()).ToList(); foreach (var entity in entities) _entitycache.Add(new Tuple(entity.ID, entity)); result.AddRange(entities); } return result; } private IEnumerable DoLoad(IEnumerable models, Columns columns) where TEntityKanban : EntityKanban, new() where TEntity : Entity, IRemotable, IPersistent, new() where TLink : IEntityLink, new() { var result = DoLoad(models, columns); if (!result.Any()) foreach (var model in models) { var id = Guid.Parse(model.ID); result = new Client().Load( new Filter(x => x.ID).InQuery(new Filter(x => x.Kanban.ID).IsEqualTo(id), x => x.Entity.ID)); foreach (var r in result) _entitycache.Add(new Tuple(id, r)); } return result; } private void DoCache(Guid kanbanid, TEntity entity) where TEntity : Entity { if (!_entitycache.Any(x => Equals(x.Item1, kanbanid) && x.Item2 is TEntity && Equals(x.Item2.ID, entity.ID))) _entitycache.Add(new Tuple(kanbanid, entity)); } private bool DoEdit(IEnumerable entities, Action? action = null) where TEntity : Entity, IRemotable, IPersistent, new() { if (entities == null || !entities.Any()) return false; foreach (var entity in entities) action?.Invoke(entity); return GetGrid().EditItems(entities.ToArray()); } private void DoLink(TaskModel model, Guid entityid) where TEntityKanban : EntityKanban, new() where TEntity : Entity, IRemotable, IPersistent, new() where TLink : IEntityLink, new() { var linktask = Task.Run(() => { var link = new TEntityKanban(); link.Kanban.ID = Guid.Parse(model.ID); link.Entity.ID = entityid; new Client().Save(link, ""); }); var kanbantask = Task.Run(() => { var kanban = LoadKanbans(new[] { model }, new Columns(x => x.ID, x => x.Locked)).FirstOrDefault(); if (kanban is not null) { kanban.Locked = true; new Client().Save(kanban, "Locked because of linked " + typeof(TEntity).EntityName().Split('.').Last()); } }); Task.WaitAll(linktask, kanbantask); } private static void DoDelete(IList entities, string auditnote) where TEntity : Entity, IRemotable, IPersistent, new() { new Client().Delete(entities, auditnote); } public Kanban? CreateKanban(Action customise) { var result = DoCreate( kanban => { kanban.Title = "New Task"; kanban.Description = ""; kanban.Category = "Open"; kanban.DueDate = DateTime.Today; kanban.Private = false; kanban.JobLink.ID = ParentID; kanban.EmployeeLink.ID = MyID; kanban.ManagerLink.ID = MyID; customise?.Invoke(kanban); }); if (result != null) DoCache(result.ID, result); return result; } public IEnumerable LoadKanbans(IEnumerable models, Columns columns) { columns.Add(x => x.ID); columns.Add(x => x.Number); columns.Add(x => x.Title); columns.Add(x => x.Notes); columns.Add(x => x.Summary); columns.Add(x => x.Completed); columns.Add(x => x.DueDate); columns.Add(x => x.ManagerLink.ID); columns.Add(x => x.EmployeeLink.ID); return DoLoad(models, columns); } public void OnValidateKanban(object sender, Kanban[] items, List errors) { if (Properties.RequireTaskTypes && items.Any(x => x.Type.ID == Guid.Empty)) { errors.Add("[Task Type] may not be blank!"); } } public void CustomiseKanbanGrid(DynamicDataGrid grid) { grid.OnValidate += OnValidateKanban; } public bool EditKanbans(IEnumerable models, Action? customise = null) { var entities = LoadKanbans(models, GetGrid().LoadEditorColumns()); return DoEdit(entities, customise); } public void DeleteKanbans(IEnumerable models, string auditnote) { var kanbans = models.Select(x => new Kanban { ID = Guid.Parse(x.ID) }).ToList(); DoDelete(kanbans, auditnote); } public Requisition? CreateRequisition(TaskModel model, Action? customise) { var result = DoCreate( requi => { requi.JobLink.ID = ParentID; customise?.Invoke(requi); }); if (result != null) { var id = Guid.Parse(model.ID); DoCache(id, result); DoLink(model, result.ID); } return result; } public bool EditRequisitions(IEnumerable models, Action? customise = null) { var requis = DoLoad(models, GetGrid().LoadEditorColumns()); if (requis.Any()) return DoEdit(requis, customise); return false; } public Setout? CreateSetout(TaskModel model, Action customise) { var result = DoCreate( setout => { setout.JobLink.ID = ParentID; customise?.Invoke(setout); }); if (result != null) { var id = Guid.Parse(model.ID); DoCache(id, result); //DoLink(model, result.ID); } return result; } public bool EditSetouts(IEnumerable models, Action? customise = null) { var setouts = DoLoad(models, GetGrid().LoadEditorColumns()); if (setouts.Any()) return DoEdit(setouts, customise); return false; } public Delivery? CreateDelivery(TaskModel model, Action customise) { var result = DoCreate( delivery => { delivery.Job.ID = ParentID; customise?.Invoke(delivery); }); if (result != null) { var id = Guid.Parse(model.ID); DoCache(id, result); DoLink(model, result.ID); } return result; } public bool EditDeliveries(IEnumerable models, Action? customise = null) { var deliveries = DoLoad(models, GetGrid().LoadEditorColumns()); if (deliveries.Any()) return DoEdit(deliveries, customise); return false; } public PurchaseOrder? CreateOrder(TaskModel model, Action customise) { var result = DoCreate( order => { customise?.Invoke(order); }); if (result != null) { var id = Guid.Parse(model.ID); DoCache(id, result); DoLink(model, result.ID); } return result; } public bool EditPurchaseOrders(IEnumerable models, Action? customise = null) { var orders = DoLoad(models, GetGrid().LoadEditorColumns()); if (orders.Any()) return DoEdit(orders, customise); return false; } #endregion #region EntityReferences private static void AddQuery(MultiQuery query, Guid[] taskids) where TEntityKanban : EntityKanban, new() where TEntity : Entity where TLink : IEntityLink, new() { query.Add( new Filter(x => x.Kanban.ID).InList(taskids), new Columns(x => x.Entity.ID).Add(x => x.Kanban.ID) ); } private static Guid[] ExtractIDs(MultiQuery query) where TEntityKanban : EntityKanban, new() where TEntity : Entity where TLink : IEntityLink, new() { var lookup = query.Get().ToLookup(x => x.Kanban.ID, x => x.Entity.ID); return query.Get().ExtractValues(x => x.Entity.ID).ToArray(); } public KanbanReferences[] GetReferences(IEnumerable models) { var result = new List(); var ids = models.Select(x => Guid.Parse(x.ID)).ToArray(); var query = new MultiQuery(); AddQuery(query, ids); AddQuery(query, ids); AddQuery(query, ids); AddQuery(query, ids); query.Query(); var requis = query.Get().ToLookup(x => x.Kanban.ID, x => x.Entity.ID); var setouts = query.Get().ToLookup(x => x.Kanban.ID, x => x.Entity.ID); var deliveries = query.Get().ToLookup(x => x.Kanban.ID, x => x.Entity.ID); var orders = query.Get().ToLookup(x => x.Kanban.ID, x => x.Entity.ID); foreach (var id in ids) { var references = new KanbanReferences { Kanban = id, Requisitions = requis.Contains(id) ? requis[id].ToArray() : Array.Empty(), Setouts = setouts.Contains(id) ? setouts[id].ToArray() : Array.Empty(), Deliveries = deliveries.Contains(id) ? deliveries[id].ToArray() : Array.Empty(), Orders = orders.Contains(id) ? orders[id].ToArray() : Array.Empty() }; result.Add(references); } return result.ToArray(); } public bool EditReferences(IEnumerable models) { var result = false; var refs = GetReferences(models).First(); if (refs.ReferenceType() == typeof(Requisition)) result = EditRequisitions( models, requi => { requi.Notes = Utility.ProcessNotes(requi.Notes, requi.Request); requi.Request = ""; } ); else if (refs.ReferenceType() == typeof(Setout)) result = EditSetouts(models); else if (refs.ReferenceType() == typeof(Delivery)) result = EditDeliveries(models); else if (refs.ReferenceType() == typeof(PurchaseOrder)) result = EditPurchaseOrders(models); else result = EditKanbans(models); return result; } #endregion } }