TaskPanel.xaml.cs 50 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294
  1. using System;
  2. using System.Collections.Generic;
  3. using System.IO;
  4. using System.Linq;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using Comal.Classes;
  9. using InABox.Clients;
  10. using InABox.Configuration;
  11. using InABox.Core;
  12. using InABox.DynamicGrid;
  13. using InABox.WPF;
  14. using Syncfusion.Pdf.Graphics;
  15. using Syncfusion.Pdf;
  16. using System.Drawing;
  17. using System.ComponentModel;
  18. using InABox.Wpf;
  19. namespace PRSDesktop;
  20. public class TaskPanelProperties : BaseObject, IGlobalConfigurationSettings
  21. {
  22. [Comment("Require that all tasks are given a task type.")]
  23. public bool RequireTaskTypes { get; set; } = false;
  24. }
  25. public class TaskPanelFilterButton : FilterButton<Kanban>
  26. {
  27. public TaskPanelFilterButton() : base(
  28. new GlobalConfiguration<CoreFilterDefinitions>(nameof(Kanban)),
  29. new UserConfiguration<CoreFilterDefinitions>(nameof(Kanban)))
  30. {
  31. }
  32. public static Filter<Kanban> ConvertFilterToKanbanFilter(Filter<Kanban>? kanbanFilter)
  33. {
  34. if (kanbanFilter is null)
  35. {
  36. return new Filter<Kanban>().All();
  37. }
  38. else if (!kanbanFilter.Property.IsNullOrWhiteSpace())
  39. {
  40. var filter = new Filter<Kanban>(kanbanFilter.Property)
  41. {
  42. Operator = kanbanFilter.Operator,
  43. Value = kanbanFilter.Value
  44. };
  45. filter.Ands.AddRange(kanbanFilter.Ands.Select(ConvertFilterToKanbanFilter));
  46. filter.Ors.AddRange(kanbanFilter.Ors.Select(ConvertFilterToKanbanFilter));
  47. return filter;
  48. }
  49. else
  50. {
  51. return new Filter<Kanban>().None();
  52. }
  53. }
  54. public static Filter<KanbanSubscriber> ConvertFilterToSubscriberFilter(Filter<IKanban>? kanbanFilter)
  55. {
  56. if (kanbanFilter is null)
  57. {
  58. return new Filter<KanbanSubscriber>().All();
  59. }
  60. else if (!kanbanFilter.Property.IsNullOrWhiteSpace())
  61. {
  62. var filter = new Filter<KanbanSubscriber>(nameof(KanbanSubscriber.Kanban) + "." + kanbanFilter.Property)
  63. {
  64. Operator = kanbanFilter.Operator,
  65. Value = kanbanFilter.Value
  66. };
  67. filter.Ands.AddRange(kanbanFilter.Ands.Select(ConvertFilterToSubscriberFilter));
  68. filter.Ors.AddRange(kanbanFilter.Ors.Select(ConvertFilterToSubscriberFilter));
  69. return filter;
  70. }
  71. else
  72. {
  73. return new Filter<KanbanSubscriber>().None();
  74. }
  75. }
  76. }
  77. /// <summary>
  78. /// Interaction logic for TaskPanel.xaml
  79. /// </summary>
  80. public partial class TaskPanel : UserControl, IPanel<Kanban>, ITaskHost, IMasterDetailControl<Job>, IPropertiesPanel<TaskPanelProperties, CanConfigureTasksPanel>
  81. {
  82. private bool _bTabChanging;
  83. private KanbanType[] kanbanTypes = null!; // Initialized in Setup()
  84. public IList<KanbanType> KanbanTypes => kanbanTypes;
  85. public Job? Master { get; set; }
  86. public TaskPanel()
  87. {
  88. InitializeComponent();
  89. foreach (TabItem tab in TaskPanels.Items)
  90. {
  91. var panel = (tab.Content as ITaskControl)!;
  92. _viewmap[panel.KanbanViewType] = tab;
  93. panel.Host = this;
  94. }
  95. }
  96. #region Menu
  97. private void CompleteTask(ITaskControl control, IEnumerable<TaskModel> tasks, DateTime completed)
  98. {
  99. if (!MessageWindow.ShowYesNo("Are you sure you want to complete the selected tasks?", "Confirm Completion"))
  100. return;
  101. Progress.ShowModal("Completing Tasks", progress =>
  102. {
  103. var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Completed, x => x.Status));
  104. foreach (var kanban in kanbans)
  105. {
  106. kanban.Completed = completed;
  107. kanban.Status = KanbanStatus.Complete;
  108. }
  109. Client.Save(kanbans, $"Kanban Marked as Complete");
  110. });
  111. control.Refresh();
  112. }
  113. private void AddChangeStatusButton(ITaskControl control, TaskModel[] models, MenuItem menu, string header, KanbanStatus status)
  114. {
  115. menu.AddItem(header, null, Tuple.Create(control, models, status), ChangeStatus_Click);
  116. }
  117. private void ChangeStatus_Click(Tuple<ITaskControl, TaskModel[], KanbanStatus> obj)
  118. {
  119. var (control, tasks, status) = obj;
  120. if (!MessageWindow.ShowYesNo($"Are you sure you want to mark the selected tasks as {status}?", "Confirm Change Status"))
  121. return;
  122. Progress.ShowModal("Changing Status", progress =>
  123. {
  124. var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Completed, x => x.Status));
  125. foreach (var kanban in kanbans)
  126. {
  127. if (status == KanbanStatus.Complete)
  128. {
  129. kanban.Completed = DateTime.Now;
  130. }
  131. kanban.Status = status;
  132. }
  133. new Client<Kanban>().Save(kanbans, $"Kanban Marked as {status}");
  134. });
  135. control.Refresh();
  136. }
  137. public bool CanChangeTasks(IEnumerable<TaskModel> models)
  138. {
  139. foreach (var task in models)
  140. {
  141. if (!App.EmployeeID.Equals(task.ManagerID) && !App.EmployeeID.Equals(task.EmployeeID))
  142. {
  143. // If you can change others tasks, IsFullControl is true - but we don't check at the beginning of the function
  144. // to save checking security tokens every time.
  145. return Security.IsAllowed<CanChangeOthersTasks>();
  146. }
  147. }
  148. return true;
  149. }
  150. public void PopulateMenu(ITaskControl control, TaskModel task, ContextMenu menu)
  151. {
  152. menu.Items.Clear();
  153. var models = control.SelectedModels(task).ToArray();
  154. var references = GetReferences(models);
  155. var bLinks = references.Any(x => x.ReferenceType() != null);
  156. var referencetypes = references.Select(x => x.ReferenceType()).Distinct().ToArray();
  157. var bSingle = models.Length == 1;
  158. var canChange = CanChangeTasks(models);
  159. menu.AddItem(referencetypes.SingleOrDefault() == typeof(Requisition)
  160. ? "Edit Requisition Details"
  161. : referencetypes.SingleOrDefault() == typeof(Setout)
  162. ? "Edit Setout Details"
  163. : referencetypes.SingleOrDefault() == typeof(Delivery)
  164. ? "Edit Delivery Details"
  165. : referencetypes.SingleOrDefault() == typeof(PurchaseOrder)
  166. ? "Edit Order Details"
  167. : "Edit Task" + (bSingle ? "" : "s"),
  168. null,
  169. () =>
  170. {
  171. if (EditReferences(models))
  172. {
  173. control.Refresh();
  174. }
  175. },
  176. enabled: referencetypes.Length == 1);
  177. if (!bLinks && models.Length == 1)
  178. {
  179. var digitalForms = menu.AddItem("Digital Forms", null, null);
  180. var model = models.First();
  181. DynamicGridUtils.PopulateFormMenu<KanbanForm, Kanban, KanbanLink>(
  182. digitalForms,
  183. model.ID,
  184. () => new Client<Kanban>().Load(new Filter<Kanban>(x => x.ID).IsEqualTo(model.ID)).First(),
  185. model.EmployeeID == App.EmployeeID);
  186. menu.AddItem("Manage Subscribers", null, model, Subscribers_Click);
  187. }
  188. if (!models.Any(x => !x.CompletedDate.IsEmpty()) && !bLinks)
  189. {
  190. menu.AddSeparatorIfNeeded();
  191. var job = menu.AddItem("Link to Job", null, null);
  192. job.SubmenuOpened += (o, e) => CreateJobSubMenu(control, job, models);
  193. if (bSingle)
  194. {
  195. menu.AddItem("Create Setout from Task", null, models.First(), task =>
  196. {
  197. if (!MessageWindow.ShowYesNo("This will convert this task into a Setout.\n\nDo you wish to continue?", "Confirmation"))
  198. return;
  199. ManufacturingTemplate? template = new Client<ManufacturingTemplate>()
  200. .Load(new Filter<ManufacturingTemplate>(x => x.Code).IsEqualTo("PRS")).FirstOrDefault();
  201. if (template == null)
  202. {
  203. MessageWindow.ShowMessage("[Pressing] Template does not exist!", "No template");
  204. return;
  205. }
  206. string? setoutNumber = null;
  207. Kanban? kanban = null;
  208. ManufacturingTemplateStage[] tstages = Array.Empty<ManufacturingTemplateStage>();
  209. Progress.ShowModal("Creating Setout", (progress) =>
  210. {
  211. var kanbanFilter = new Filter<Kanban>(x => x.ID).IsEqualTo(task.ID);
  212. var tables = Client.QueryMultiple(new Dictionary<string, IQueryDef>
  213. {
  214. { "ManufacturingTemplateStage", new QueryDef<ManufacturingTemplateStage>(
  215. new Filter<ManufacturingTemplateStage>(x => x.Template.ID).IsEqualTo(template.ID),
  216. null,
  217. new SortOrder<ManufacturingTemplateStage>(x => x.Sequence)) },
  218. { "Kanban", new QueryDef<Kanban>(
  219. kanbanFilter,
  220. null,
  221. null) },
  222. { "Setout", new QueryDef<Setout>(
  223. new Filter<Setout>(x => x.JobLink.ID)
  224. .InQuery(new SubQuery<Kanban>(kanbanFilter, new Column<Kanban>(x => x.JobLink.ID))),
  225. Columns.None<Setout>().Add(x => x.JobLink.JobNumber, x => x.Number),
  226. null) }
  227. });
  228. tstages = tables["ManufacturingTemplateStage"].Rows
  229. .Select(x => x.ToObject<ManufacturingTemplateStage>()).ToArray();
  230. kanban = tables["Kanban"].Rows.FirstOrDefault()?.ToObject<Kanban>();
  231. if (kanban == null)
  232. {
  233. MessageWindow.ShowMessage("Task does not exist!", "No task");
  234. return;
  235. }
  236. progress.Report("Creating Setouts");
  237. CoreTable setouts = tables["Setout"];
  238. int ireq = 0;
  239. string sreq = "";
  240. while (true)
  241. {
  242. ireq++;
  243. sreq = string.Format("{0}-{1:yyMMdd}-{2}", kanban.JobLink.JobNumber, DateTime.Now, ireq);
  244. if (!setouts.Rows.Any(r => sreq.Equals(r.Get<Setout, String>(c => c.Number))))
  245. break;
  246. }
  247. setoutNumber = sreq;
  248. });
  249. if (setoutNumber == null || kanban == null)
  250. {
  251. return;
  252. }
  253. var result = CreateSetout(
  254. task,
  255. s =>
  256. {
  257. s.Number = setoutNumber;
  258. s.JobLink.ID = task.JobID;
  259. var notes = kanban.Notes.ToList();
  260. var description = kanban.Summary;
  261. if (string.IsNullOrWhiteSpace(description))
  262. {
  263. description = CoreUtils.StripHTML(kanban.Description);
  264. }
  265. if (!string.IsNullOrWhiteSpace(description))
  266. {
  267. notes.Insert(0, description);
  268. }
  269. s.Description = string.Join("\n==========================================\n", notes);
  270. }
  271. );
  272. if (result != null)
  273. {
  274. Progress.ShowModal("Creating Manufacturing Packet", progress =>
  275. {
  276. ManufacturingPacket packet = new ManufacturingPacket()
  277. {
  278. Serial = template.Code,
  279. Title = kanban.Title,
  280. Quantity = 1,
  281. BarcodeQty = 1,
  282. DueDate = kanban.DueDate
  283. };
  284. packet.ManufacturingTemplateLink.ID = template.ID;
  285. packet.ManufacturingTemplateLink.Code = template.Code;
  286. packet.ManufacturingTemplateLink.Factory.ID = template.Factory.ID;
  287. packet.SetoutLink.ID = result.ID;
  288. new Client<ManufacturingPacket>().Save(packet, "Created from Task");
  289. DoLink<ManufacturingPacketKanban, ManufacturingPacket, ManufacturingPacketLink>(task, packet.ID);
  290. List<ManufacturingPacketStage> pstages = new List<ManufacturingPacketStage>();
  291. foreach (var tstage in tstages)
  292. {
  293. var pstage = new ManufacturingPacketStage()
  294. {
  295. Time = tstage.Time,
  296. Sequence = tstage.Sequence,
  297. SequenceType = tstage.SequenceType,
  298. Started = DateTime.MinValue,
  299. PercentageComplete = 0.0F,
  300. Completed = DateTime.MinValue,
  301. QualityChecks = tstage.QualityChecks,
  302. QualityStatus = QualityStatus.NotChecked,
  303. QualityNotes = "",
  304. };
  305. pstage.Parent.ID = packet.ID;
  306. pstage.ManufacturingSectionLink.ID = tstage.Section.ID;
  307. pstage.ManufacturingSectionLink.Name = tstage.Section.Name;
  308. pstages.Add(pstage);
  309. }
  310. new Client<ManufacturingPacketStage>().Save(pstages, "Created from Task", (_, __) => { });
  311. progress.Report("Processing Documents");
  312. List<SetoutDocument> _setoutdocuments = new List<SetoutDocument>();
  313. List<KanbanDocument> _kanbandocuments = new List<KanbanDocument>();
  314. KanbanDocument[] docrefs = new Client<KanbanDocument>()
  315. .Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
  316. foreach (var docref in docrefs)
  317. {
  318. // Convert the document to a PDF
  319. var docid = ProcessKanbanDocument(docref);
  320. var newdoc = new SetoutDocument();
  321. newdoc.EntityLink.ID = result.ID;
  322. newdoc.DocumentLink.ID = docid;
  323. _setoutdocuments.Add(newdoc);
  324. if (docid != docref.DocumentLink.ID)
  325. {
  326. docref.DocumentLink.ID = docid;
  327. _kanbandocuments.Add(docref);
  328. }
  329. }
  330. new Client<SetoutDocument>().Save(_setoutdocuments, "Converted from Task", (_, __) => { });
  331. new Client<KanbanDocument>().Save(_kanbandocuments, "Converted to PDF", (_, __) => { });
  332. progress.Report("Updating Task");
  333. kanban.Title = kanban.Title + " (" + result.Number + ")";
  334. new Client<Kanban>().Save(kanban, "Converting Kanban to Setout");
  335. });
  336. control.Refresh();
  337. }
  338. });
  339. menu.AddItem("Create Requisition from Task", null, models, tasks =>
  340. {
  341. var taskModel = tasks.First();
  342. var kanbanTable = new Client<Kanban>().Query(new Filter<Kanban>(x => x.ID).IsEqualTo(taskModel.ID));
  343. var kanban = kanbanTable.Rows.First().ToObject<Kanban>();
  344. var result = CreateRequisition(
  345. taskModel,
  346. r =>
  347. {
  348. r.RequestedBy.ID = kanban.ManagerLink.ID;
  349. r.Employee.ID = Guid.Empty;
  350. r.Title = kanban.Title;
  351. r.Request = string.IsNullOrWhiteSpace(kanban.Summary)
  352. ? String.IsNullOrWhiteSpace(kanban.Description)
  353. ? String.Join("\n", kanban.Notes)
  354. : CoreUtils.StripHTML(kanban.Description)
  355. : kanban.Summary;
  356. r.Notes = kanban.Notes;
  357. r.Due = kanban.DueDate;
  358. r.JobLink.ID = taskModel.JobID;
  359. }
  360. );
  361. if (result != null)
  362. {
  363. Progress.ShowModal("Updating Documents", progress =>
  364. {
  365. progress.Report("Updating Documents");
  366. List<RequisitionDocument> requiDocuments = new();
  367. KanbanDocument[] kanbanDocuments = new Client<KanbanDocument>()
  368. .Load(new Filter<KanbanDocument>(x => x.EntityLink.ID).IsEqualTo(kanban.ID));
  369. foreach (var document in kanbanDocuments)
  370. {
  371. var newdoc = new RequisitionDocument();
  372. newdoc.EntityLink.ID = result.ID;
  373. newdoc.DocumentLink.ID = document.DocumentLink.ID;
  374. requiDocuments.Add(newdoc);
  375. }
  376. new Client<RequisitionDocument>().Save(requiDocuments, "Converted from Task", (_, __) => { });
  377. /*RequisitionKanban link = new();
  378. link.Entity.ID = result.ID;
  379. link.Kanban.ID = kanban.ID;
  380. new Client<RequisitionKanban>().Save(link, "Converting Task -> Requisition", (_, __) => { });*/
  381. progress.Report("Updating Task");
  382. kanban.Status = KanbanStatus.Open;
  383. kanban.Completed = DateTime.MinValue;
  384. kanban.Title += $" (Requi #{result.Number})";
  385. new Client<Kanban>().Save(kanban, "Converted to Requisition", (_, __) => { });
  386. });
  387. MessageWindow.ShowMessage($"Created Requisition {result.Number}", "Success");
  388. control.Refresh();
  389. }
  390. });
  391. menu.AddItem("Create Delivery from Task", null, models, tasks =>
  392. {
  393. var result = CreateDelivery(
  394. tasks.First(),
  395. d =>
  396. {
  397. // Post-Process Requi Here
  398. }
  399. );
  400. if (result != null)
  401. control.Refresh();
  402. });
  403. menu.AddItem("Create Purchase Order from Task", null, models, tasks =>
  404. {
  405. var result = CreateOrder(
  406. tasks.First(),
  407. p =>
  408. {
  409. // Post-Process Requi Here
  410. }
  411. );
  412. if (result != null)
  413. control.Refresh();
  414. });
  415. }
  416. }
  417. if (!bLinks && canChange)
  418. {
  419. menu.AddSeparatorIfNeeded();
  420. #region Status
  421. var changeStatus = new MenuItem { Header = "Change Status" };
  422. AddChangeStatusButton(control, models, changeStatus, "Open", KanbanStatus.Open);
  423. AddChangeStatusButton(control, models, changeStatus, "In Progress", KanbanStatus.InProgress);
  424. AddChangeStatusButton(control, models, changeStatus, "Waiting", KanbanStatus.Waiting);
  425. if (models.Any(x => x.CompletedDate.IsEmpty()))
  426. {
  427. var complete = menu.AddItem(models.Length > 1 ? "Complete Tasks" : "Complete Task", null, () =>
  428. {
  429. CompleteTask(control, models, DateTime.Now);
  430. });
  431. if (Security.IsAllowed<CanSetKanbanCompletedDate>())
  432. {
  433. var completeDate = new MenuItem
  434. {
  435. Tag = models,
  436. Header = "Set Completed Date"
  437. };
  438. var dateItem = new MenuItem();
  439. var dateCalendar = new System.Windows.Controls.Calendar { SelectedDate = DateTime.MinValue };
  440. dateCalendar.Tag = models;
  441. dateCalendar.SelectedDatesChanged += (o, e) =>
  442. {
  443. if (e.Source is not System.Windows.Controls.Calendar calendar) return;
  444. menu.IsOpen = false;
  445. var selectedDate = calendar.SelectedDate ?? DateTime.Now;
  446. CompleteTask(control, models, selectedDate);
  447. };
  448. dateItem.Header = dateCalendar;
  449. dateItem.Style = Resources["calendarItem"] as Style;
  450. completeDate.Items.Add(dateItem);
  451. menu.Items.Add(completeDate);
  452. }
  453. }
  454. else
  455. {
  456. menu.AddItem(models.Length > 1 ? "Archive Tasks" : "Archive Task", null, models, tasks =>
  457. {
  458. if (!MessageWindow.ShowYesNo("Are you sure you want to remove the selected tasks from the list?", "Confirm removal"))
  459. return;
  460. Progress.ShowModal("Closing Kanbans", progress =>
  461. {
  462. var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.Closed));
  463. foreach (var kanban in kanbans)
  464. kanban.Closed = DateTime.Now;
  465. new Client<Kanban>().Save(kanbans, "Kanban Marked as Closed");
  466. });
  467. control.Refresh();
  468. });
  469. }
  470. menu.Items.Add(changeStatus);
  471. #endregion
  472. #region Task Type
  473. var changeType = menu.AddItem("Change Task Type", null, null);
  474. foreach(var type in KanbanTypes)
  475. {
  476. changeType.AddItem($"{type.Code}: {type.Description}", null, type, type =>
  477. {
  478. Progress.ShowModal("Changing Task Type", progress =>
  479. {
  480. var kanbans = LoadKanbans(models, Columns.Required<Kanban>().Add(x => x.ID, x => x.Type.ID));
  481. foreach (var kanban in kanbans)
  482. {
  483. kanban.Type.ID = type.ID;
  484. }
  485. new Client<Kanban>().Save(kanbans, $"Kanban Task Type changed to {type}");
  486. });
  487. control.Refresh();
  488. });
  489. }
  490. #endregion
  491. #region Due Date
  492. var changeDueDate = menu.AddItem("Change Due Date", null, null);
  493. var calendarItem = new MenuItem();
  494. var calendar = new System.Windows.Controls.Calendar { SelectedDate = models.Length == 1 ? models[0].DueDate : DateTime.Today };
  495. calendar.Tag = models;
  496. calendar.SelectedDatesChanged += (o, e) =>
  497. {
  498. if (e.Source is not System.Windows.Controls.Calendar calendar) return;
  499. var selectedDate = calendar.SelectedDate ?? DateTime.Now;
  500. var models = (calendar.Tag as IList<TaskModel>)!;
  501. Progress.ShowModal("Changing Due Date", progress =>
  502. {
  503. var kanbans = LoadKanbans(models, Columns.Required<Kanban>().Add(x => x.ID, x => x.DueDate));
  504. foreach (var kanban in kanbans)
  505. {
  506. kanban.DueDate = selectedDate;
  507. }
  508. new Client<Kanban>().Save(kanbans, $"Kanban Due Date changed to {selectedDate:dd MMM yyyy}");
  509. });
  510. control.Refresh();
  511. menu.IsOpen = false;
  512. };
  513. calendarItem.Header = calendar;
  514. calendarItem.Style = Resources["calendarItem"] as Style;
  515. changeDueDate.Items.Add(calendarItem);
  516. #endregion
  517. #region Employees
  518. menu.AddItem("Assign To...", null, (control, models), SetAssignee_Click);
  519. menu.AddItem("Set Manager...", null, (control, models), SetManager_Click);
  520. #endregion
  521. }
  522. }
  523. private void SetAssignee_Click((ITaskControl control, TaskModel[] models) tuple)
  524. {
  525. var tasks = LoadKanbans(tuple.models, Columns.Required<Kanban>().Add(x => x.ID).Add(x => x.EmployeeLink.ID))
  526. .ToArray();
  527. if (MultiSelectDialog.SelectItem<Employee>(out var employee,
  528. filter: LookupFactory.DefineLookupFilter<Kanban, Employee, EmployeeLink>(x => x.EmployeeLink, tasks),
  529. columns: LookupFactory.DefineLookupColumns<Kanban, Employee, EmployeeLink>(x => x.EmployeeLink),
  530. title: "Select Assignee for Task"))
  531. {
  532. foreach (var task in tasks)
  533. {
  534. task.EmployeeLink.CopyFrom(employee);
  535. }
  536. Client.Save(tasks, "Set Assignee");
  537. tuple.control.Refresh();
  538. }
  539. }
  540. private void SetManager_Click((ITaskControl control, TaskModel[] models) tuple)
  541. {
  542. var tasks = LoadKanbans(tuple.models, Columns.Required<Kanban>().Add(x => x.ID).Add(x => x.ManagerLink.ID))
  543. .ToArray();
  544. if (MultiSelectDialog.SelectItem<Employee>(out var employee,
  545. filter: LookupFactory.DefineLookupFilter<Kanban, Employee, EmployeeLink>(x => x.ManagerLink, tasks),
  546. columns: LookupFactory.DefineLookupColumns<Kanban, Employee, EmployeeLink>(x => x.ManagerLink),
  547. title: "Select Manager for Task"))
  548. {
  549. foreach (var task in tasks)
  550. {
  551. task.ManagerLink.CopyFrom(employee);
  552. }
  553. Client.Save(tasks, "Set Manager");
  554. tuple.control.Refresh();
  555. }
  556. }
  557. private void Subscribers_Click(TaskModel model)
  558. {
  559. var kanban = LoadKanbans([model], Columns.Required<Kanban>().Add(x => x.ID).Add(x => x.Number)).First();
  560. ManageSubscribers(kanban);
  561. }
  562. public static void ManageSubscribers(Kanban kanban)
  563. {
  564. var grid = new KanbanSubscriberGrid(kanban);
  565. var window = DynamicGridUtils.CreateGridWindow($"Manage subscribers for Task #{kanban.Number}", grid);
  566. window.Width = 400;
  567. window.Height = 400;
  568. window.ShowDialog();
  569. }
  570. /// <summary>
  571. /// Takes a <see cref="KanbanDocument"/>, and if it is a .txt or an image (".png", ".jpg", ".jpeg" or ".bmp"), converts to a PDF
  572. /// with the content of the document, saving a new document with extension changed to ".pdf".
  573. /// </summary>
  574. /// <param name="docref">The original document.</param>
  575. /// <returns>
  576. /// The ID of the new <see cref="Document"/> or,
  577. /// if not one of the given types, the original document ID.
  578. /// </returns>
  579. private static Guid ProcessKanbanDocument(KanbanDocument docref)
  580. {
  581. var result = docref.DocumentLink.ID;
  582. var ext = System.IO.Path.GetExtension(docref.DocumentLink.FileName).ToLower();
  583. if (ext.EndsWith("txt"))
  584. {
  585. var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
  586. if (doc is null)
  587. {
  588. Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!");
  589. return docref.DocumentLink.ID;
  590. }
  591. PdfDocument pdf = new PdfDocument();
  592. PdfPage page = pdf.Pages.Add();
  593. PdfGraphics graphics = page.Graphics;
  594. PdfFont font = new PdfStandardFont(PdfFontFamily.Courier, 12);
  595. String text = System.Text.Encoding.UTF8.GetString(doc.Data);
  596. graphics.DrawString(text, font, PdfBrushes.Black, new PointF(0, 0));
  597. MemoryStream ms = new MemoryStream();
  598. pdf.Save(ms);
  599. pdf.Close(true);
  600. byte[] data = ms.ToArray();
  601. var newdoc = new Document()
  602. {
  603. Data = data,
  604. FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
  605. CRC = CoreUtils.CalculateCRC(data),
  606. TimeStamp = DateTime.Now,
  607. };
  608. new Client<Document>().Save(newdoc, "Converted from Text");
  609. return newdoc.ID;
  610. }
  611. else if (ext.EndsWith("png") || ext.EndsWith("bmp") || ext.EndsWith("jpg") || ext.EndsWith("jpeg"))
  612. {
  613. var doc = new Client<Document>().Load(new Filter<Document>(x => x.ID).IsEqualTo(docref.DocumentLink.ID)).FirstOrDefault();
  614. if (doc is null)
  615. {
  616. Logger.Send(LogType.Error, "", $"Document {docref.DocumentLink.ID} does not exist!");
  617. return docref.DocumentLink.ID;
  618. }
  619. PdfBitmap image = new PdfBitmap(new MemoryStream(doc.Data));
  620. PdfDocument pdf = new PdfDocument();
  621. pdf.PageSettings.Orientation = image.Height > image.Width ? PdfPageOrientation.Portrait : PdfPageOrientation.Landscape;
  622. pdf.PageSettings.Size = new SizeF(image.Width, image.Height);
  623. PdfPage page = pdf.Pages.Add();
  624. PdfGraphics graphics = page.Graphics;
  625. graphics.DrawImage(image, 0.0F, 0.0F);
  626. MemoryStream ms = new MemoryStream();
  627. pdf.Save(ms);
  628. pdf.Close(true);
  629. byte[] data = ms.ToArray();
  630. var newdoc = new Document()
  631. {
  632. Data = data,
  633. FileName = System.IO.Path.ChangeExtension(docref.DocumentLink.FileName, "pdf"),
  634. CRC = CoreUtils.CalculateCRC(data),
  635. TimeStamp = DateTime.Now,
  636. };
  637. new Client<Document>().Save(newdoc, "Converted from Image");
  638. return newdoc.ID;
  639. }
  640. return result;
  641. }
  642. private void CreateJobSubMenu(ITaskControl control, MenuItem job, IEnumerable<TaskModel> tasks)
  643. {
  644. job.Items.Clear();
  645. job.Items.Add(new MenuItem { Header = "Loading...", IsEnabled = false });
  646. using (new WaitCursor())
  647. {
  648. job.Items.Clear();
  649. var jobs = new Client<Job>().Query(
  650. LookupFactory.DefineFilter<Job>(),
  651. LookupFactory.DefineColumns<Job>(),
  652. LookupFactory.DefineSort<Job>()
  653. );
  654. foreach (var row in jobs.Rows)
  655. {
  656. var jobNumber = row.Get<Job, string>(x => x.JobNumber);
  657. var jobName = row.Get<Job, string>(x => x.Name);
  658. job.AddItem($"{jobNumber}: {jobName}", null, tasks, tasks =>
  659. {
  660. using (new WaitCursor())
  661. {
  662. var kanbans = LoadKanbans(tasks, Columns.Required<Kanban>().Add(x => x.ID, x => x.JobLink.ID));
  663. foreach (var kanban in kanbans)
  664. kanban.JobLink.ID = row.Get<Job, Guid>(x => x.ID);
  665. new Client<Kanban>().Save(kanbans, "Updated Job Number");
  666. control.Refresh();
  667. }
  668. });
  669. }
  670. }
  671. }
  672. #endregion
  673. private void TaskPanels_SelectionChanged(object sender, SelectionChangedEventArgs e)
  674. {
  675. if (!IsReady)
  676. return;
  677. if (e.Source is not TabControl)
  678. return;
  679. if (_bTabChanging)
  680. return;
  681. try
  682. {
  683. _bTabChanging = true;
  684. var panel = GetCurrentPanel();
  685. if(panel is not null)
  686. {
  687. KanbanSettings.ViewType = panel.KanbanViewType;
  688. new UserConfiguration<KanbanSettings>().Save(KanbanSettings);
  689. panel.Refresh();
  690. OnUpdateDataModel?.Invoke(SectionName, DataModel(Selection.None));
  691. }
  692. }
  693. finally
  694. {
  695. _bTabChanging = false;
  696. }
  697. }
  698. #region Get/Save Settings
  699. private KanbanSettings? _settings;
  700. public KanbanSettings KanbanSettings
  701. {
  702. get
  703. {
  704. _settings ??= new UserConfiguration<KanbanSettings>().Load();
  705. return _settings;
  706. }
  707. }
  708. public void SaveSettings()
  709. {
  710. if(_settings != null)
  711. new UserConfiguration<KanbanSettings>().Save(_settings);
  712. }
  713. #endregion
  714. #region IPanel Stuff
  715. public event DataModelUpdateEvent? OnUpdateDataModel;
  716. public bool IsReady { get; set; }
  717. public void CreateToolbarButtons(IPanelHost host)
  718. {
  719. host.CreatePanelAction(
  720. new PanelAction
  721. {
  722. Caption = "New Task",
  723. OnExecute = a => {
  724. if(CreateKanban(k => { }) != null)
  725. {
  726. Refresh();
  727. }
  728. },
  729. Image = InABox.Wpf.Resources.add
  730. }
  731. );
  732. if (Security.CanView<KanbanType>())
  733. {
  734. host.CreateSetupAction(
  735. new PanelAction
  736. {
  737. Caption = "Task Types",
  738. Image = PRSDesktop.Resources.kanbantype,
  739. OnExecute = a =>
  740. {
  741. var list = new MasterList(typeof(KanbanType));
  742. list.ShowDialog();
  743. }
  744. });
  745. }
  746. }
  747. public Dictionary<string, object[]> Selected()
  748. {
  749. return new Dictionary<string, object[]>();
  750. }
  751. public void Heartbeat(TimeSpan time)
  752. {
  753. }
  754. private readonly Dictionary<KanbanViewType, TabItem> _viewmap = new();
  755. private readonly List<ITaskControl> _initialized = new();
  756. private ITaskControl GetCurrentPanel()
  757. {
  758. var result = (TaskPanels.SelectedContent as ITaskControl)!;
  759. if (result == null)
  760. result = (TaskPanels.Items[0] as DynamicTabItem)?.Content as ITaskControl;
  761. try
  762. {
  763. //if (result != null)
  764. if (!_initialized.Contains(result))
  765. {
  766. result.Setup();
  767. result.IsReady = true;
  768. _initialized.Add(result);
  769. }
  770. }
  771. catch (Exception e)
  772. {
  773. Logger.Send(LogType.Error, "", $"Error in TaskPanel.GetCurrentPanel: {CoreUtils.FormatException(e)}");
  774. }
  775. return result;
  776. }
  777. public void Setup()
  778. {
  779. _settings = new UserConfiguration<KanbanSettings>().Load();
  780. TaskPanels.SelectedItem = _viewmap[_settings.ViewType];
  781. kanbanTypes = new Client<KanbanType>()
  782. .Query(
  783. new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false),
  784. Columns.None<KanbanType>().Add(x => x.ID, x => x.Code, x => x.Description))
  785. .Rows.Select(x => x.ToObject<KanbanType>()).ToArray();
  786. }
  787. public void Shutdown(CancelEventArgs? cancel)
  788. {
  789. }
  790. public void Refresh()
  791. {
  792. if (Master == null)
  793. {
  794. if (Equals(TaskPanels.SelectedItem, TasksPlannerTabItem))
  795. TaskPanels.SelectedItem = _viewmap[KanbanViewType.Status];
  796. TasksPlannerTabItem.Visibility = Visibility.Collapsed;
  797. }
  798. else
  799. TasksPlannerTabItem.Visibility = Visibility.Visible;
  800. GetCurrentPanel()?.Refresh();
  801. }
  802. public string SectionName => GetCurrentPanel().SectionName;
  803. public TaskPanelProperties Properties { get; set; }
  804. public DataModel DataModel(Selection selection)
  805. {
  806. return GetCurrentPanel().DataModel(selection);
  807. //return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).IsEqualTo(Guid.Empty));
  808. }
  809. #endregion
  810. #region CRUD Functionality
  811. private TEntity? DoCreate<TEntity>(Action<TEntity> customise)
  812. where TEntity : Entity, IRemotable, IPersistent, new()
  813. {
  814. var result = new TEntity();
  815. customise?.Invoke(result);
  816. if (DoEdit(new[] { result }, null))
  817. return result;
  818. return null;
  819. }
  820. private readonly Dictionary<Type, IDynamicGrid> _grids = new();
  821. private readonly List<Tuple<Guid, Entity>> _entitycache = new();
  822. private DynamicDataGrid<TEntity> GetGrid<TEntity>() where TEntity : Entity, IRemotable, IPersistent, new()
  823. {
  824. if(!_grids.TryGetValue(typeof(TEntity), out var grid))
  825. {
  826. grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), typeof(TEntity)) as DynamicDataGrid<TEntity>)!;
  827. _grids[typeof(TEntity)] = grid;
  828. if(grid is ITaskControl ctl)
  829. {
  830. ctl.Host = this;
  831. }
  832. }
  833. return (grid as DynamicDataGrid<TEntity>)!;
  834. }
  835. private IEnumerable<TEntity> DoLoad<TEntity>(IEnumerable<TaskModel> models, Columns<TEntity> columns)
  836. where TEntity : Entity, IRemotable, IPersistent, new()
  837. {
  838. var result = new List<TEntity>();
  839. var load = new List<Guid>();
  840. foreach (var model in models)
  841. {
  842. var entity = _entitycache.FirstOrDefault(x => Equals(x.Item1, model.ID) && x.Item2 is TEntity) as TEntity;
  843. if (entity is not null)
  844. result.Add(entity);
  845. else
  846. load.Add(model.ID);
  847. }
  848. if (load.Any())
  849. {
  850. var entities = new Client<TEntity>()
  851. .Query(new Filter<TEntity>(x => x.ID).InList(load.ToArray()), columns)
  852. .Rows.Select(x => x.ToObject<TEntity>()).ToList();
  853. foreach (var entity in entities)
  854. _entitycache.Add(new Tuple<Guid, Entity>(entity.ID, entity));
  855. result.AddRange(entities);
  856. }
  857. return result;
  858. }
  859. private IEnumerable<TEntity> DoLoad<TEntityKanban, TEntity, TLink>(IEnumerable<TaskModel> models, Columns<TEntity> columns)
  860. where TEntityKanban : EntityKanban<TEntity, TLink>, new()
  861. where TEntity : Entity, IRemotable, IPersistent, new()
  862. where TLink : IEntityLink<TEntity>, new()
  863. {
  864. var result = DoLoad(models, columns);
  865. if (!result.Any())
  866. foreach (var model in models)
  867. {
  868. result = new Client<TEntity>().Load(
  869. new Filter<TEntity>(x => x.ID).InQuery(new Filter<TEntityKanban>(x => x.Kanban.ID).IsEqualTo(model.ID),
  870. x => x.Entity.ID));
  871. foreach (var r in result)
  872. _entitycache.Add(new Tuple<Guid, Entity>(model.ID, r));
  873. }
  874. return result;
  875. }
  876. private void DoCache<TEntity>(Guid kanbanid, TEntity entity) where TEntity : Entity
  877. {
  878. if (!_entitycache.Any(x => Equals(x.Item1, kanbanid) && x.Item2 is TEntity && Equals(x.Item2.ID, entity.ID)))
  879. _entitycache.Add(new Tuple<Guid, Entity>(kanbanid, entity));
  880. }
  881. private bool DoEdit<TEntity>(IEnumerable<TEntity> entities, Action<TEntity>? action = null)
  882. where TEntity : Entity, IRemotable, IPersistent, new()
  883. {
  884. if (entities == null || !entities.Any())
  885. return false;
  886. foreach (var entity in entities)
  887. action?.Invoke(entity);
  888. return GetGrid<TEntity>().EditItems(entities.ToArray());
  889. }
  890. private void DoLink<TEntityKanban, TEntity, TLink>(TaskModel model, Guid entityid)
  891. where TEntityKanban : EntityKanban<TEntity, TLink>, new()
  892. where TEntity : Entity, IRemotable, IPersistent, new()
  893. where TLink : IEntityLink<TEntity>, new()
  894. {
  895. var linktask = Task.Run(() =>
  896. {
  897. var link = new TEntityKanban();
  898. link.Kanban.ID = model.ID;
  899. link.Entity.ID = entityid;
  900. new Client<TEntityKanban>().Save(link, "");
  901. });
  902. var kanbantask = Task.Run(() =>
  903. {
  904. var kanban = LoadKanbans(
  905. new[] { model },
  906. Columns.Required<Kanban>().Add(x => x.ID, x => x.Locked)).FirstOrDefault();
  907. if (kanban is not null)
  908. {
  909. kanban.Locked = true;
  910. new Client<Kanban>().Save(kanban, "Locked because of linked " + typeof(TEntity).EntityName().Split('.').Last());
  911. }
  912. });
  913. Task.WaitAll(linktask, kanbantask);
  914. }
  915. private static void DoDelete<TEntity>(IList<TEntity> entities, string auditnote)
  916. where TEntity : Entity, IRemotable, IPersistent, new()
  917. {
  918. new Client<TEntity>().Delete(entities, auditnote);
  919. }
  920. public Kanban? CreateKanban(Action<Kanban> customise)
  921. {
  922. var result = DoCreate<Kanban>(
  923. kanban =>
  924. {
  925. kanban.Title = "New Task";
  926. kanban.Description = "";
  927. kanban.Status = KanbanStatus.Open;
  928. kanban.DueDate = DateTime.Today;
  929. kanban.Private = false;
  930. kanban.JobLink.ID = Master?.ID ?? Guid.Empty;
  931. kanban.JobLink.Synchronise(Master ?? new Job());
  932. kanban.EmployeeLink.ID = App.EmployeeID;
  933. kanban.ManagerLink.ID = App.EmployeeID;
  934. customise?.Invoke(kanban);
  935. });
  936. if (result != null)
  937. DoCache(result.ID, result);
  938. return result;
  939. }
  940. public IEnumerable<Kanban> LoadKanbans(IEnumerable<TaskModel> models, Columns<Kanban> columns)
  941. {
  942. columns.Add(x => x.ID);
  943. columns.Add(x => x.Number);
  944. columns.Add(x => x.Title);
  945. columns.Add(x => x.Notes);
  946. columns.Add(x => x.Summary);
  947. columns.Add(x => x.Completed);
  948. columns.Add(x => x.DueDate);
  949. columns.Add(x => x.ManagerLink.ID);
  950. columns.Add(x => x.EmployeeLink.ID);
  951. return DoLoad(models, columns);
  952. }
  953. public bool EditKanbans(IEnumerable<TaskModel> models, Action<Kanban>? customise = null)
  954. {
  955. var entities = LoadKanbans(models, GetGrid<Kanban>().LoadEditorColumns());
  956. return DoEdit(entities, customise);
  957. }
  958. public void DeleteKanbans(IEnumerable<TaskModel> models, string auditnote)
  959. {
  960. var kanbans = models.Select(x => new Kanban { ID = x.ID }).ToList();
  961. DoDelete(kanbans, auditnote);
  962. }
  963. public Requisition? CreateRequisition(TaskModel model, Action<Requisition>? customise)
  964. {
  965. var result = DoCreate<Requisition>(
  966. requi =>
  967. {
  968. requi.JobLink.ID = Master?.ID ?? Guid.Empty;
  969. requi.JobLink.Synchronise(Master ?? new Job());
  970. customise?.Invoke(requi);
  971. });
  972. if (result != null)
  973. {
  974. DoCache(model.ID, result);
  975. DoLink<RequisitionKanban, Requisition, RequisitionLink>(model, result.ID);
  976. }
  977. return result;
  978. }
  979. public bool EditRequisitions(IEnumerable<TaskModel> models, Action<Requisition>? customise = null)
  980. {
  981. var requis = DoLoad<RequisitionKanban, Requisition, RequisitionLink>(models, GetGrid<Requisition>().LoadEditorColumns());
  982. if (requis.Any())
  983. return DoEdit(requis, customise);
  984. return false;
  985. }
  986. public Setout? CreateSetout(TaskModel model, Action<Setout> customise)
  987. {
  988. var result = DoCreate<Setout>(
  989. setout =>
  990. {
  991. setout.JobLink.ID = Master?.ID ?? Guid.Empty;
  992. setout.JobLink.Synchronise(Master ?? new Job());
  993. customise?.Invoke(setout);
  994. });
  995. if (result != null)
  996. {
  997. DoCache(model.ID, result);
  998. //DoLink<SetoutKanban, Setout, SetoutLink>(model, result.ID);
  999. }
  1000. return result;
  1001. }
  1002. public bool EditSetouts(IEnumerable<TaskModel> models, Action<Setout>? customise = null)
  1003. {
  1004. var setouts = DoLoad<SetoutKanban, Setout, SetoutLink>(models, GetGrid<Setout>().LoadEditorColumns());
  1005. if (setouts.Any())
  1006. return DoEdit(setouts, customise);
  1007. return false;
  1008. }
  1009. public Delivery? CreateDelivery(TaskModel model, Action<Delivery> customise)
  1010. {
  1011. var result = DoCreate<Delivery>(
  1012. delivery =>
  1013. {
  1014. delivery.Job.ID = Master?.ID ?? Guid.Empty;
  1015. delivery.Job.Synchronise(Master ?? new Job());
  1016. customise?.Invoke(delivery);
  1017. });
  1018. if (result != null)
  1019. {
  1020. DoCache(model.ID, result);
  1021. DoLink<DeliveryKanban, Delivery, DeliveryLink>(model, result.ID);
  1022. }
  1023. return result;
  1024. }
  1025. public bool EditDeliveries(IEnumerable<TaskModel> models, Action<Delivery>? customise = null)
  1026. {
  1027. var deliveries = DoLoad<DeliveryKanban, Delivery, DeliveryLink>(models, GetGrid<Delivery>().LoadEditorColumns());
  1028. if (deliveries.Any())
  1029. return DoEdit(deliveries, customise);
  1030. return false;
  1031. }
  1032. public PurchaseOrder? CreateOrder(TaskModel model, Action<PurchaseOrder> customise)
  1033. {
  1034. var result = DoCreate<PurchaseOrder>(
  1035. order => { customise?.Invoke(order); });
  1036. if (result != null)
  1037. {
  1038. DoCache(model.ID, result);
  1039. DoLink<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(model, result.ID);
  1040. }
  1041. return result;
  1042. }
  1043. public bool EditPurchaseOrders(IEnumerable<TaskModel> models, Action<PurchaseOrder>? customise = null)
  1044. {
  1045. var orders = DoLoad<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(models, GetGrid<PurchaseOrder>().LoadEditorColumns());
  1046. if (orders.Any())
  1047. return DoEdit(orders, customise);
  1048. return false;
  1049. }
  1050. #endregion
  1051. #region EntityReferences
  1052. private static void AddQuery<TEntityKanban, TEntity, TLink>(MultiQuery query, Guid[] taskids)
  1053. where TEntityKanban : EntityKanban<TEntity, TLink>, new()
  1054. where TEntity : Entity
  1055. where TLink : IEntityLink<TEntity>, new()
  1056. {
  1057. query.Add(
  1058. new Filter<TEntityKanban>(x => x.Kanban.ID).InList(taskids),
  1059. Columns.None<TEntityKanban>().Add(x => x.Entity.ID).Add(x => x.Kanban.ID)
  1060. );
  1061. }
  1062. private static Guid[] ExtractIDs<TEntityKanban, TEntity, TLink>(MultiQuery query)
  1063. where TEntityKanban : EntityKanban<TEntity, TLink>, new()
  1064. where TEntity : Entity
  1065. where TLink : IEntityLink<TEntity>, new()
  1066. {
  1067. var lookup = query.Get<TEntityKanban>().ToLookup<TEntityKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
  1068. return query.Get<TEntityKanban>().ExtractValues<TEntityKanban, Guid>(x => x.Entity.ID).ToArray();
  1069. }
  1070. public KanbanReferences[] GetReferences(IEnumerable<TaskModel> models)
  1071. {
  1072. var result = new List<KanbanReferences>();
  1073. var ids = models.Select(x => x.ID).ToArray();
  1074. var query = new MultiQuery();
  1075. AddQuery<RequisitionKanban, Requisition, RequisitionLink>(query, ids);
  1076. AddQuery<SetoutKanban, Setout, SetoutLink>(query, ids);
  1077. AddQuery<DeliveryKanban, Delivery, DeliveryLink>(query, ids);
  1078. AddQuery<PurchaseOrderKanban, PurchaseOrder, PurchaseOrderLink>(query, ids);
  1079. query.Query();
  1080. var requis = query.Get<RequisitionKanban>().ToLookup<RequisitionKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
  1081. var setouts = query.Get<SetoutKanban>().ToLookup<SetoutKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
  1082. var deliveries = query.Get<DeliveryKanban>().ToLookup<DeliveryKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
  1083. var orders = query.Get<PurchaseOrderKanban>().ToLookup<PurchaseOrderKanban, Guid, Guid>(x => x.Kanban.ID, x => x.Entity.ID);
  1084. foreach (var id in ids)
  1085. {
  1086. var references = new KanbanReferences
  1087. {
  1088. Kanban = id,
  1089. Requisitions = requis.Contains(id) ? requis[id].ToArray() : Array.Empty<Guid>(),
  1090. Setouts = setouts.Contains(id) ? setouts[id].ToArray() : Array.Empty<Guid>(),
  1091. Deliveries = deliveries.Contains(id) ? deliveries[id].ToArray() : Array.Empty<Guid>(),
  1092. Orders = orders.Contains(id) ? orders[id].ToArray() : Array.Empty<Guid>()
  1093. };
  1094. result.Add(references);
  1095. }
  1096. return result.ToArray();
  1097. }
  1098. public bool EditReferences(IEnumerable<TaskModel> models)
  1099. {
  1100. var result = false;
  1101. var refs = GetReferences(models).First();
  1102. if (refs.ReferenceType() == typeof(Requisition))
  1103. result = EditRequisitions(
  1104. models,
  1105. requi =>
  1106. {
  1107. requi.Notes = Utility.ProcessNotes(requi.Notes, requi.Request);
  1108. requi.Request = "";
  1109. }
  1110. );
  1111. else if (refs.ReferenceType() == typeof(Setout))
  1112. result = EditSetouts(models);
  1113. else if (refs.ReferenceType() == typeof(Delivery))
  1114. result = EditDeliveries(models);
  1115. else if (refs.ReferenceType() == typeof(PurchaseOrder))
  1116. result = EditPurchaseOrders(models);
  1117. else
  1118. result = EditKanbans(models);
  1119. return result;
  1120. }
  1121. #endregion
  1122. }