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
}
}