TaskPanel.xaml.cs 50 KB

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