TaskPanel.xaml.cs 50 KB

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