TaskPlannerControl.xaml.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Media;
  9. using Comal.Classes;
  10. using InABox.Clients;
  11. using InABox.Core;
  12. using InABox.DynamicGrid;
  13. using InABox.WPF;
  14. using Syncfusion.UI.Xaml.TreeGrid;
  15. using Syncfusion.Windows.Controls.Gantt;
  16. using Syncfusion.Windows.Controls.Grid;
  17. using GridSelectionMode = Syncfusion.Windows.Controls.Grid.GridSelectionMode;
  18. using SelectionChangedEventArgs = System.Windows.Controls.SelectionChangedEventArgs;
  19. namespace PRSDesktop
  20. {
  21. /// <summary>
  22. /// Interaction logic for TaskPlannerControl.xaml
  23. /// </summary>
  24. public partial class TaskPlannerControl : UserControl, ITaskControl
  25. {
  26. private enum Suppress
  27. {
  28. This
  29. }
  30. private CoreTable _kanbans;
  31. private CoreTable _relationships;
  32. private readonly ObservableCollection<Resource> _resources = new();
  33. private CoreTable _stages;
  34. private readonly ObservableCollection<GanttTask> _tasks = new();
  35. private bool bLoading;
  36. public TaskPlannerControl()
  37. {
  38. using (new EventSuppressor(Suppress.This))
  39. {
  40. InitializeComponent();
  41. Gantt.ItemsSource = _tasks;
  42. Gantt.ResourceCollection = _resources;
  43. }
  44. }
  45. public void Setup()
  46. {
  47. LoadKanbanTypes();
  48. }
  49. private void LoadKanbanTypes()
  50. {
  51. using (new EventSuppressor(Suppress.This))
  52. {
  53. var types = new Dictionary<Guid, string>
  54. {
  55. { CoreUtils.FullGuid, "All Task Types" },
  56. { Guid.Empty, "Uncategorized Tasks" }
  57. };
  58. new Client<KanbanType>().Query(new Filter<KanbanType>(x => x.Hidden).IsEqualTo(false))
  59. .IntoDictionary<Kanban, Guid>(types, x => x.ID, x => x.Description);
  60. SelectedType.ItemsSource = types;
  61. }
  62. }
  63. public void Refresh(bool resetselection)
  64. {
  65. Refresh();
  66. }
  67. public void Shutdown()
  68. {
  69. }
  70. private void Gantt_TemplateApplied(object sender, TemplateAppliedEventArgs args)
  71. {
  72. if (Gantt.GanttGrid != null)
  73. {
  74. //Gantt.GanttGrid.Model.Options.ListBoxSelectionMode = GridSelectionMode.One;
  75. //Gantt.GanttGrid.Model.Sizer.AllowAutoCalculateSize = true;
  76. //Gantt.GanttGrid.Model.Sizer.ListenToSizeChanged = true;
  77. //Gantt.GanttGrid.Model.Options.ColumnSizer = GridControlLengthUnitType.Star;
  78. Gantt.GanttGrid.RowHeaderWidth = 0;
  79. Gantt.GanttGrid.ShowRowHeader = false;
  80. CreateGanttColumns();
  81. //Gantt.GanttGrid.ReadOnly = true;
  82. }
  83. }
  84. private void CreateGanttColumns()
  85. {
  86. if (Gantt.GanttGrid == null)
  87. return;
  88. Gantt.GanttGrid.Columns.Clear();
  89. //Gantt.GanttGrid.Columns.Add(new Syncfusion.Windows.Controls.Grid.GridTreeColumn("TaskId") { Width = 60F, HeaderText = "#", StyleInfo = new Syncfusion.Windows.Controls.Grid.GridStyleInfo() { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center } });
  90. Gantt.GanttGrid.Columns.Add(new TreeGridTextColumn()
  91. {
  92. MappingName = "TaskName",
  93. Width = 220F,
  94. HeaderText = "Description",
  95. VerticalAlignment = VerticalAlignment.Center
  96. });
  97. Gantt.GanttGrid.Columns.Add(new TreeGridDateTimeColumn()
  98. {
  99. MappingName = "StartDate",
  100. Width = 70F,
  101. HeaderText = "Start",
  102. VerticalAlignment = VerticalAlignment.Center
  103. //StyleInfo = new GridStyleInfo
  104. // { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
  105. });
  106. Gantt.GanttGrid.Columns.Add(new TreeGridNumericColumn()
  107. {
  108. MappingName = "Manpower",
  109. Width = 60F,
  110. HeaderText = "Hrs",
  111. //StyleInfo = new GridStyleInfo { VerticalAlignment = VerticalAlignment.Center, HorizontalAlignment = HorizontalAlignment.Center }
  112. VerticalAlignment = VerticalAlignment.Center
  113. });
  114. Gantt.GanttGrid.Columns.Add(new TreeGridNumericColumn()
  115. {
  116. MappingName = "Percentage",
  117. Width = 40F,
  118. HeaderText = "%",
  119. //StyleInfo = new GridStyleInfo
  120. // { VerticalAlignment = VerticalAlignment.Center, Format = "#0.##%", HorizontalAlignment = HorizontalAlignment.Center }
  121. VerticalAlignment = VerticalAlignment.Center
  122. });
  123. Gantt.GanttGrid.Columns.Add(new TreeGridDateTimeColumn()
  124. {
  125. MappingName = "FinishDate",
  126. Width = 70F,
  127. HeaderText = "Due",
  128. //StyleInfo = new GridStyleInfo
  129. // { VerticalAlignment = VerticalAlignment.Center, Format = "dd MMM yy", HorizontalAlignment = HorizontalAlignment.Center }
  130. VerticalAlignment = VerticalAlignment.Center
  131. });
  132. }
  133. private void LoadData()
  134. {
  135. var query = new MultiQuery();
  136. var kanbanfilter = new Filter<Kanban>(x => x.Closed).IsEqualTo(DateTime.MinValue);
  137. if (Host.Master != null)
  138. kanbanfilter = kanbanfilter.And(x => x.JobLink.ID).IsEqualTo(Host.Master.ID);
  139. query.Add(
  140. new QueryDef<Kanban>(
  141. kanbanfilter,
  142. Columns.None<Kanban>().Add(
  143. x => x.ID,
  144. x => x.Created,
  145. x => x.DueDate,
  146. x => x.Completed,
  147. x => x.Description,
  148. x => x.Summary,
  149. x => x.Status,
  150. x => x.EmployeeLink.ID,
  151. x => x.EmployeeLink.Name,
  152. x => x.ManagerLink.ID,
  153. x => x.Title,
  154. x => x.Notes,
  155. x => x.Number,
  156. x => x.Attachments,
  157. x => x.Type.Code,
  158. x => x.StartDate,
  159. x => x.EstimatedTime,
  160. x => x.ActualTime,
  161. x => x.Type.ID
  162. ),
  163. new SortOrder<Kanban>(x => x.DueDate).ThenBy(x => x.StartDate)
  164. ),
  165. typeof(Kanban)
  166. );
  167. var stagefilter = (Host.Master != null)
  168. ? new Filter<JobStage>(x => x.Job.ID).IsEqualTo(Host.Master.ID)
  169. : new Filter<JobStage>(x => x.Job.ID).All();
  170. query.Add(
  171. new QueryDef<JobStage>(
  172. stagefilter,
  173. null,
  174. null
  175. ),
  176. typeof(JobStage)
  177. );
  178. var relfilter = (Host.Master != null)
  179. ? new Filter<KanbanRelationship>(x => x.Parent.ID).IsEqualTo(Host.Master.ID)
  180. : new Filter<KanbanRelationship>(x => x.Parent.ID).All();
  181. query.Add(
  182. new QueryDef<KanbanRelationship>(
  183. relfilter,
  184. Columns.None<KanbanRelationship>().Add(
  185. x => x.ID,
  186. x => x.Predecessor.ID,
  187. x => x.Successor.ID,
  188. x => x.Type,
  189. x => x.Parent.ID
  190. ),
  191. null
  192. ),
  193. typeof(KanbanRelationship)
  194. );
  195. query.Query();
  196. _kanbans = query.Get(typeof(Kanban));
  197. _stages = query.Get(typeof(JobStage));
  198. _relationships = query.Get(typeof(KanbanRelationship));
  199. }
  200. public void Refresh()
  201. {
  202. bLoading = true;
  203. using (new WaitCursor())
  204. {
  205. try
  206. {
  207. LoadData();
  208. _tasks.Clear();
  209. var startmarker = new GanttTask
  210. {
  211. StartDate = DateTime.Today,
  212. FinishDate = DateTime.Today,
  213. TaskName = "Start of Job",
  214. IsMileStone = true
  215. };
  216. var endmarker = new GanttTask
  217. {
  218. StartDate = DateTime.Today,
  219. FinishDate = DateTime.Today,
  220. TaskName = "End of Job",
  221. IsMileStone = true
  222. };
  223. var curstart = DateTime.MaxValue;
  224. var curend = DateTime.MinValue;
  225. _tasks.Add(startmarker);
  226. foreach (var row in _kanbans.Rows)
  227. if ((Host.KanbanSettings.PlannerSettings.IncludeCompleted || row.Get<Kanban, DateTime>(c => c.Completed).IsEmpty()) &&
  228. (Host.KanbanSettings.PlannerSettings.SelectedType == CoreUtils.FullGuid ||
  229. row.Get<Kanban, Guid>(c => c.Type.ID) == Host.KanbanSettings.PlannerSettings.SelectedType))
  230. {
  231. var task = new GanttTask();
  232. LoadTask(row, task);
  233. if (task.StartDate < curstart)
  234. curstart = task.StartDate;
  235. if (task.FinishDate > curend)
  236. curend = task.FinishDate;
  237. _tasks.Add(task);
  238. }
  239. _tasks.Add(endmarker);
  240. foreach (var row in _relationships.Rows)
  241. {
  242. var predtask = _tasks.FirstOrDefault(x => x.ID == row.Get<KanbanRelationship, Guid>(c => c.Predecessor.ID));
  243. var succtask = _tasks.FirstOrDefault(x => x.ID == row.Get<KanbanRelationship, Guid>(c => c.Successor.ID));
  244. if (predtask != null && succtask != null)
  245. {
  246. var type = row.Get<KanbanRelationship, GanttRelationshipType>(x => x.Type);
  247. var relationship = type == GanttRelationshipType.FinishToFinish
  248. ? GanttTaskRelationship.FinishToFinish
  249. : type == GanttRelationshipType.FinishToStart
  250. ? GanttTaskRelationship.FinishToStart
  251. : type == GanttRelationshipType.StartToFinish
  252. ? GanttTaskRelationship.StartToFinish
  253. : GanttTaskRelationship.StartToStart;
  254. succtask.Predecessor.Add(new Predecessor { GanttTaskIndex = predtask.TaskId, GanttTaskRelationship = relationship });
  255. }
  256. }
  257. var _striplines = new List<StripLineInfo>();
  258. foreach (var row in _stages.Rows)
  259. {
  260. if (!_striplines.Any())
  261. {
  262. var startline = new StripLineInfo();
  263. startline.Content = "Start of Job";
  264. startline.StartDate = row.Get<JobStage, DateTime>(c => c.StartDate);
  265. startline.EndDate = startline.StartDate;
  266. startline.HorizontalContentAlignment = HorizontalAlignment.Center;
  267. startline.VerticalContentAlignment = VerticalAlignment.Center;
  268. startline.Background = Brushes.Gold;
  269. startline.RepeatBehavior = Repeat.None;
  270. _striplines.Add(startline);
  271. }
  272. var stripline = new StripLineInfo();
  273. stripline.Content = row.Get<JobStage, string>(c => c.Name);
  274. stripline.StartDate = row.Get<JobStage, DateTime>(c => c.EndDate);
  275. stripline.EndDate = stripline.StartDate;
  276. stripline.HorizontalContentAlignment = HorizontalAlignment.Center;
  277. stripline.VerticalContentAlignment = VerticalAlignment.Center;
  278. stripline.Background = Brushes.Gold;
  279. stripline.RepeatBehavior = Repeat.None;
  280. _striplines.Add(stripline);
  281. if (stripline.StartDate < curstart)
  282. curstart = stripline.StartDate;
  283. if (stripline.EndDate > curend)
  284. curend = stripline.EndDate;
  285. }
  286. Gantt.StripLines = _striplines;
  287. Gantt.ShowStripLines = true;
  288. Gantt.UseOnDemandSchedule = true;
  289. if (curstart != DateTime.MaxValue)
  290. {
  291. startmarker.StartDate = curstart.Date;
  292. startmarker.FinishDate = curstart.Date;
  293. }
  294. if (curend != DateTime.MinValue)
  295. {
  296. endmarker.StartDate = curend.Date.AddDays(1);
  297. endmarker.FinishDate = curend.AddDays(1);
  298. }
  299. }
  300. finally
  301. {
  302. bLoading = false;
  303. }
  304. foreach (var task in _tasks)
  305. task.PropertyChanged += Task_PropertyChanged;
  306. }
  307. }
  308. private void LoadTask(CoreRow row, GanttTask task)
  309. {
  310. var start = row.Get<Kanban, DateTime>(x => x.StartDate);
  311. if (start.IsEmpty())
  312. start = row.Get<Kanban, DateTime>(x => x.Created);
  313. var estimated = row.Get<Kanban, TimeSpan>(x => x.EstimatedTime);
  314. if (estimated.TotalMilliseconds == 0L)
  315. estimated = TimeSpan.FromHours(1);
  316. var actual = row.Get<Kanban, TimeSpan>(x => x.ActualTime);
  317. var progress = estimated.TotalMilliseconds != 0L ? actual.TotalMilliseconds * 100L / estimated.TotalMilliseconds : 0F;
  318. task.TaskId = row.Get<Kanban, int>(c => c.Number);
  319. task.ID = row.Get<Kanban, Guid>(x => x.ID);
  320. task.TaskName = string.Format("{0} - {1}", row.Get<Kanban, int>(c => c.Number), row.Get<Kanban, string>(x => x.Title));
  321. task.StartDate = start.Date;
  322. task.FinishDate = row.Get<Kanban, DateTime>(x => x.DueDate).Date.AddDays(1).AddMilliseconds(-1);
  323. task.Manpower = estimated;
  324. task.Percentage = progress;
  325. task.IsMileStone = false;
  326. var empid = row.EntityLinkID<Kanban, EmployeeLink>(x => x.EmployeeLink) ?? Guid.Empty;
  327. if (empid != Guid.Empty)
  328. {
  329. var resource = _resources.FirstOrDefault(x => (x as GanttResource).Guid == empid) as GanttResource;
  330. if (resource == null)
  331. {
  332. resource = new GanttResource
  333. {
  334. ID = _resources.Count,
  335. Guid = empid,
  336. Name = row.Get<Kanban, string>(c => c.EmployeeLink.Name)
  337. };
  338. _resources.Add(resource);
  339. }
  340. task.Resources = new ObservableCollection<Resource> { resource };
  341. }
  342. }
  343. private void Task_PropertyChanged(object? sender, PropertyChangedEventArgs args)
  344. {
  345. if (bLoading)
  346. return;
  347. if (!string.Equals(args.PropertyName, "StartDate") && !string.Equals(args.PropertyName, "FinishDate"))
  348. return;
  349. var row = _kanbans.Rows.FirstOrDefault(r => r.Get<Kanban, Guid>(x => x.ID) == ((GanttTask)sender).ID);
  350. var kanban = row?.ToObject<Kanban>();
  351. var task = sender as GanttTask;
  352. var bChanged = false;
  353. if (task != null && kanban != null)
  354. {
  355. if (string.Equals(args.PropertyName, "FinishDate"))
  356. {
  357. kanban.DueDate = task.FinishDate.Date;
  358. bChanged = true;
  359. }
  360. else if (string.Equals(args.PropertyName, "StartDate"))
  361. {
  362. kanban.StartDate = task.StartDate.Date;
  363. bChanged = true;
  364. }
  365. }
  366. if (bChanged)
  367. {
  368. row.Set<Kanban, DateTime>(x => x.DueDate, kanban.DueDate.Date);
  369. row.Set<Kanban, DateTime>(x => x.StartDate, kanban.StartDate.Date);
  370. new Client<Kanban>().Save(kanban, "Updated by Planner", (o, e) => { });
  371. }
  372. }
  373. private void Gantt_RelationshipEstablished(object sender, GanttRelationshipEstablishedEventArgs args)
  374. {
  375. var pred = args.StartTask as GanttTask;
  376. var succ = args.EndTask as GanttTask;
  377. if (pred == null || pred.IsMileStone)
  378. throw new Exception("Cannot make a connection here");
  379. if (succ == null || succ.IsMileStone)
  380. throw new Exception("Cannot make a connection here");
  381. var relationship = new KanbanRelationship();
  382. relationship.Parent.ID = Host.Master?.ID ?? Guid.Empty;
  383. relationship.Parent.Synchronise(Host.Master ?? new Job());
  384. relationship.Predecessor.ID = pred.ID;
  385. relationship.Successor.ID = succ.ID;
  386. relationship.Type = args.Relationship == GanttTaskRelationship.FinishToFinish
  387. ? GanttRelationshipType.FinishToFinish
  388. : args.Relationship == GanttTaskRelationship.FinishToStart
  389. ? GanttRelationshipType.FinishToStart
  390. : args.Relationship == GanttTaskRelationship.StartToFinish
  391. ? GanttRelationshipType.StartToFinish
  392. : GanttRelationshipType.StartToStart;
  393. new Client<KanbanRelationship>().Save(relationship, "", (o, e) =>
  394. {
  395. _relationships.LoadRow(relationship);
  396. });
  397. }
  398. private void GanttContextMenu_Opened(object sender, RoutedEventArgs e)
  399. {
  400. var task = Gantt.SelectedItems.FirstOrDefault() as GanttTask;
  401. UnlinkTaskMenu.IsEnabled = task != null && task.Predecessor.Any();
  402. UnlinkTaskMenu.Tag = task;
  403. }
  404. private void UnlinkTaskMenu_Click(object sender, RoutedEventArgs args)
  405. {
  406. var successor = (sender as MenuItem).Tag as GanttTask;
  407. if (successor == null)
  408. return;
  409. var deletes = new List<KanbanRelationship>();
  410. foreach (var link in successor.Predecessor)
  411. {
  412. var predecessor = _tasks.FirstOrDefault(x => x.TaskId == link.GanttTaskIndex);
  413. if (predecessor != null)
  414. {
  415. var rows = _relationships.Rows.Where(r =>
  416. r.Get<KanbanRelationship, Guid>(c => c.Successor.ID).Equals(successor.ID) &&
  417. r.Get<KanbanRelationship, Guid>(c => c.Predecessor.ID).Equals(predecessor.ID)).ToArray();
  418. foreach (var row in rows)
  419. {
  420. deletes.Add(row.ToObject<KanbanRelationship>());
  421. _relationships.Rows.Remove(row);
  422. }
  423. }
  424. }
  425. if (deletes.Any())
  426. new Client<KanbanRelationship>().Delete(deletes, "", (o, e) => { });
  427. successor.Predecessor.Clear();
  428. }
  429. private void EditTask_Click(object sender, RoutedEventArgs e)
  430. {
  431. DoEditTask();
  432. }
  433. private void DoEditTask()
  434. {
  435. if (Gantt.SelectedItems.FirstOrDefault() is not GanttTask task)
  436. return;
  437. var row = _kanbans.Rows.FirstOrDefault(r => r.Get<Kanban, Guid>(x => x.ID) == task.ID);
  438. if (row is null) return;
  439. var kanban = row.ToObject<Kanban>();
  440. if (DynamicGridUtils.EditEntity(kanban))
  441. {
  442. _kanbans.FillRow(row, kanban);
  443. LoadTask(row, task);
  444. }
  445. }
  446. private void AddTask_Click(object sender, RoutedEventArgs e)
  447. {
  448. var kanban = new Kanban();
  449. kanban.JobLink.ID = Host.Master?.ID ?? Guid.Empty;
  450. kanban.JobLink.Synchronise(Host.Master ?? new Job());
  451. if (DynamicGridUtils.EditEntity(kanban))
  452. {
  453. var row = _kanbans.NewRow();
  454. _kanbans.FillRow(row, kanban);
  455. var task = new GanttTask();
  456. LoadTask(row, task);
  457. _tasks.Add(task);
  458. task.PropertyChanged += Task_PropertyChanged;
  459. }
  460. }
  461. private void IncludeCompleted_Checked(object sender, RoutedEventArgs e)
  462. {
  463. Host.KanbanSettings.PlannerSettings.IncludeCompleted = IncludeCompleted.IsChecked == true;
  464. Host.SaveSettings();
  465. Refresh();
  466. }
  467. #region ITaskControl Support
  468. public KanbanViewType KanbanViewType => KanbanViewType.Planner;
  469. public ITaskHost Host { get; set; }
  470. public bool IsReady { get; set; }
  471. public string SectionName => "Task Planner";
  472. public DataModel DataModel(Selection selection)
  473. {
  474. return new AutoDataModel<Kanban>(new Filter<Kanban>(x => x.ID).IsEqualTo(Guid.Empty));
  475. }
  476. public IEnumerable<TaskModel> SelectedModels(TaskModel sender = null)
  477. {
  478. MessageBox.Show("TaskPlannerControl.SelectedModels() is not Implemented!");
  479. return new TaskModel[] { };
  480. }
  481. #endregion
  482. private void TaskTypeButton_OnClick(object sender, RoutedEventArgs e)
  483. {
  484. var list = new MasterList(typeof(KanbanType));
  485. list.ShowDialog();
  486. LoadKanbanTypes();
  487. }
  488. private void SelectedType_SelectionChanged(object sender, SelectionChangedEventArgs e)
  489. {
  490. if (EventSuppressor.IsSet(Suppress.This))
  491. {
  492. Host.KanbanSettings.PlannerSettings.SelectedType = SelectedType.SelectedValue == null ? Guid.Empty : (Guid)SelectedType.SelectedValue;
  493. Host.SaveSettings();
  494. Refresh();
  495. }
  496. }
  497. }
  498. }