JobDocumentSetTree.xaml.cs 66 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections.ObjectModel;
  4. using System.ComponentModel;
  5. using System.Diagnostics;
  6. using System.IO;
  7. using System.Linq;
  8. using System.Windows;
  9. using System.Windows.Controls;
  10. using System.Windows.Documents;
  11. using System.Windows.Forms;
  12. using System.Windows.Input;
  13. using System.Windows.Media;
  14. using Comal.Classes;
  15. using InABox.Clients;
  16. using InABox.Core;
  17. using InABox.DynamicGrid;
  18. using InABox.WPF;
  19. using Syncfusion.Data.Extensions;
  20. using Syncfusion.UI.Xaml.Grid;
  21. using Syncfusion.UI.Xaml.TreeGrid;
  22. using Syncfusion.UI.Xaml.TreeGrid.Helpers;
  23. using JobDocumentSetFolder = Comal.Classes.JobDocumentSetFolder;
  24. using MessageBox = System.Windows.MessageBox;
  25. using UserControl = System.Windows.Controls.UserControl;
  26. namespace PRSDesktop
  27. {
  28. public class DocumentSetNode : INotifyPropertyChanged
  29. {
  30. private DocumentSetNodes _owner = null;
  31. public ObservableCollection<DocumentSetNode> Children => _owner.GetChilden(_id);
  32. private Guid _id;
  33. public Guid ID
  34. {
  35. get { return _id; }
  36. set
  37. {
  38. _id = value;
  39. RaisedOnPropertyChanged("ID");
  40. }
  41. }
  42. private Guid _parent;
  43. public Guid Parent
  44. {
  45. get { return _parent; }
  46. set
  47. {
  48. _parent = value;
  49. RaisedOnPropertyChanged("Parent");
  50. }
  51. }
  52. private string _description;
  53. public string Description
  54. {
  55. get { return _description; }
  56. set
  57. {
  58. _description = value;
  59. RaisedOnPropertyChanged("Description");
  60. }
  61. }
  62. private string _details;
  63. public string Details
  64. {
  65. get { return _details; }
  66. set
  67. {
  68. _details = value;
  69. RaisedOnPropertyChanged("Details");
  70. }
  71. }
  72. public Dictionary<String,String> Blocks { get; private set; }
  73. public event PropertyChangedEventHandler PropertyChanged;
  74. public void RaisedOnPropertyChanged(string propertyName)
  75. {
  76. PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
  77. }
  78. public DocumentSetNode(DocumentSetNodes owner)
  79. {
  80. _owner = owner;
  81. Blocks = new Dictionary<String,String>();
  82. foreach (var column in owner.Columns)
  83. Blocks[column] = "";
  84. }
  85. public DocumentSetNode(DocumentSetNodes owner, Guid id, Guid parent) : this(owner)
  86. {
  87. _id = id;
  88. _parent = parent;
  89. }
  90. }
  91. public class DocumentSetNodes
  92. {
  93. private List<DocumentSetNode> _nodes = null;
  94. public ObservableCollection<DocumentSetNode> Nodes => new ObservableCollection<DocumentSetNode>(_nodes.Where(x=>x.Parent == Guid.Empty));
  95. public IEnumerable<String> Columns { get; private set; }
  96. public DocumentSetNodes(IEnumerable<String> columns)
  97. {
  98. _nodes = new List<DocumentSetNode>();
  99. Columns = columns;
  100. }
  101. public DocumentSetNode Add(Guid id, Guid parent)
  102. {
  103. var node = new DocumentSetNode(this, id, parent);
  104. _nodes.Add(node);
  105. return node;
  106. }
  107. public ObservableCollection<DocumentSetNode> GetChilden(Guid id)
  108. {
  109. return new ObservableCollection<DocumentSetNode>(_nodes.Where(x => x.Parent.Equals(id) && (x.ID != id)));
  110. }
  111. public DocumentSetNode? GetNode(Guid id)
  112. {
  113. return _nodes.FirstOrDefault(x => x.ID == id);
  114. }
  115. }
  116. // public class GridColumnSizerExt : TreeGridColumnSizer
  117. // {
  118. // public GridColumnSizerExt(SfTreeGrid sfTreeGrid)
  119. // : base()
  120. // {
  121. //
  122. // }
  123. // public override double SetColumnWidth(TreeGridColumn column, double Width)
  124. // {
  125. // MethodInfo methodInfo = this.TreeGrid.ColumnResizingController.GetType().GetMethod("IsExpanderColumn", BindingFlags.NonPublic | BindingFlags.Instance);
  126. // if ((bool)methodInfo.Invoke(this.TreeGrid.ColumnResizingController, new object[] { column }))
  127. // {
  128. // var columnIndex = this.TreeGrid.Columns.IndexOf(column);
  129. // var scrollColumnIndex = this.TreeGrid.ResolveToScrollColumnIndex(columnIndex);
  130. // var treeGridPanel = this.TreeGrid.GetTreePanel();
  131. // //You can hide the expander column by setting width as 0 here.
  132. // treeGridPanel.ColumnWidths[scrollColumnIndex] = 100.0;
  133. // return 100.0;
  134. // }
  135. // else
  136. // return base.SetColumnWidth(column, Width);
  137. // }
  138. // }
  139. public delegate void JobDocumentSetMileStoneSelected(JobDocumentSetMileStoneBlock block);
  140. public partial class JobDocumentSetTree : UserControl
  141. {
  142. public event JobDocumentSetMileStoneSelected MileStoneSelected;
  143. private struct MileStone
  144. {
  145. public Guid TypeID { get; set; }
  146. public CoreRow Row { get; set; }
  147. }
  148. private struct MileStoneType
  149. {
  150. public String Code { get; set; }
  151. public String Description { get; set; }
  152. public Dictionary<Guid,List<CoreRow>> SetMileStones { get; set; }
  153. public List<String> Columns { get; set; }
  154. }
  155. public Guid JobID { get; set; }
  156. public Guid FolderID { get; set; }
  157. public bool DisciplineVisible { get; set; }
  158. public Guid DisciplineID { get; set; }
  159. public bool TypeVisible { get; set; }
  160. public Guid TypeID { get; set; }
  161. public bool CategoryVisible { get; set; }
  162. public Guid CategoryID { get; set; }
  163. public bool AreaVisible { get; set; }
  164. public Guid AreaID { get; set; }
  165. private Dictionary<Guid,MileStoneType> _types = null;
  166. private CoreTable _milestones = null;
  167. public CoreTable Data { get; private set; } = null;
  168. private CoreTable _files = null;
  169. private bool _hidesuperceded = false;
  170. private bool _flatlist = false;
  171. private bool _includeretired = false;
  172. private DocumentSetNodes _documentsets = null;
  173. public JobDocumentSetTree()
  174. {
  175. InitializeComponent();
  176. AddImage.Source = PRSDesktop.Resources.add.AsBitmapImage();
  177. EditImage.Source = PRSDesktop.Resources.pencil.AsBitmapImage();
  178. DeleteImage.Source = PRSDesktop.Resources.delete.AsBitmapImage();
  179. treeGrid.Loaded += (o, e) =>
  180. {
  181. treeGrid.GetTreePanel().RowHeights[1] = 0;
  182. treeGrid.UpdateDataRow(1);
  183. };
  184. }
  185. public void Refresh()
  186. {
  187. using (new WaitCursor())
  188. {
  189. var scrollviewer = WPFUtils.FindVisualChildren<ScrollViewer>(treeGrid).FirstOrDefault();
  190. var verticalOffset = scrollviewer != null ? scrollviewer.VerticalOffset : 0;
  191. var horizontalOffset = treeGrid.SelectedItem != null ? scrollviewer.HorizontalOffset : 0;
  192. treeGrid.ItemsSource = null;
  193. var setfilter = new Filter<JobDocumentSet>(x => x.Job.ID).IsEqualTo(JobID);
  194. if (FolderID != CoreUtils.FullGuid)
  195. setfilter = setfilter.And(x => x.Folder.ID).IsEqualTo(FolderID);
  196. if (DisciplineID != Guid.Empty)
  197. setfilter = setfilter.And(x => x.Discipline.ID).IsEqualTo(DisciplineID);
  198. if (TypeID != Guid.Empty)
  199. setfilter = setfilter.And(x => x.Type.ID).IsEqualTo(TypeID);
  200. if (CategoryID != Guid.Empty)
  201. setfilter = setfilter.And(x => x.Category.ID).IsEqualTo(CategoryID);
  202. if (AreaID != Guid.Empty)
  203. setfilter = setfilter.And(x => x.Area.ID).IsEqualTo(AreaID);
  204. if (!_includeretired)
  205. setfilter = setfilter.And(x => x.Retired).IsEqualTo(DateTime.MinValue);
  206. MultiQuery query = new MultiQuery();
  207. query.Add(
  208. setfilter,
  209. new Columns<JobDocumentSet>(x => x.ID)
  210. .Add(x => x.Parent.ID)
  211. .Add(x => x.Code)
  212. .Add(x => x.Description)
  213. .Add(x => x.Date)
  214. .Add(x => x.Size)
  215. .Add(x => x.Scale)
  216. .Add(x => x.Employee.Name),
  217. new SortOrder<JobDocumentSet>(x => x.Code)
  218. );
  219. var milestonefilter = new Filter<JobDocumentSetMileStone>(x => x.DocumentSet.Job.ID).IsEqualTo(JobID);
  220. if (FolderID != Guid.Empty && FolderID != CoreUtils.FullGuid)
  221. milestonefilter = milestonefilter.And(x => x.DocumentSet.Folder.ID).IsEqualTo(FolderID);
  222. query.Add(
  223. milestonefilter,
  224. new Columns<JobDocumentSetMileStone>(x => x.ID)
  225. .Add(x => x.DocumentSet.ID)
  226. .Add(x => x.Type.ID)
  227. .Add(x => x.Type.Code)
  228. .Add(x => x.Status)
  229. .Add(x => x.Notes)
  230. .Add(x => x.Revision)
  231. .Add(x => x.Due)
  232. .Add(x => x.Submitted)
  233. .Add(x => x.Closed)
  234. .Add(x => x.Attachments)
  235. .Add(x => x.Watermark)
  236. );
  237. if (_types == null)
  238. {
  239. query.Add<JobDocumentSetMileStoneType>(
  240. null,
  241. new Columns<JobDocumentSetMileStoneType>(x => x.ID)
  242. .Add(x => x.Code)
  243. .Add(x => x.Description),
  244. new SortOrder<JobDocumentSetMileStoneType>(x => x.Sequence)
  245. );
  246. }
  247. query.Query();
  248. Data = query.Get<JobDocumentSet>();
  249. _milestones = query.Get<JobDocumentSetMileStone>();
  250. if (_types == null)
  251. {
  252. _types = query.Get<JobDocumentSetMileStoneType>().ToDictionary<JobDocumentSetMileStoneType, Guid, MileStoneType>(
  253. x => x.ID,
  254. r => new MileStoneType()
  255. {
  256. Code = r.Get<JobDocumentSetMileStoneType, String>(c => c.Code),
  257. Description = r.Get<JobDocumentSetMileStoneType, String>(c => c.Description),
  258. SetMileStones = new Dictionary<Guid, List<CoreRow>>(),
  259. Columns = new List<string>()
  260. }
  261. );
  262. }
  263. else
  264. {
  265. foreach (var typeid in _types.Keys)
  266. {
  267. _types[typeid].Columns.Clear();
  268. _types[typeid].SetMileStones.Clear();
  269. }
  270. }
  271. var milestones = _milestones.ToLookup<JobDocumentSetMileStone, Guid, MileStone>(
  272. x => x.DocumentSet.ID,
  273. r => new MileStone()
  274. {
  275. TypeID = r.Get<JobDocumentSetMileStone, Guid>(c => c.Type.ID),
  276. Row = r
  277. }
  278. );
  279. foreach (var milestone in milestones)
  280. {
  281. foreach (var entry in milestone)
  282. {
  283. if (!_types[entry.TypeID].SetMileStones.ContainsKey(milestone.Key))
  284. _types[entry.TypeID].SetMileStones[milestone.Key] = new List<CoreRow>();
  285. if (_hidesuperceded)
  286. _types[entry.TypeID].SetMileStones[milestone.Key].Clear();
  287. _types[entry.TypeID].SetMileStones[milestone.Key].Add(entry.Row);
  288. }
  289. }
  290. List<String> columns = new List<string>();
  291. foreach (var typeid in _types.Keys)
  292. {
  293. int count = 1;
  294. foreach (var setkey in _types[typeid].SetMileStones.Keys)
  295. count = Math.Max(count, _types[typeid].SetMileStones[setkey].Count);
  296. for (int i = 1; i <= count; i++)
  297. {
  298. String column = String.Format("{0}_{1}", _types[typeid].Code, i);
  299. columns.Add(column);
  300. _types[typeid].Columns.Add(String.Format("Blocks[{0}]", column));
  301. }
  302. }
  303. _documentsets = new DocumentSetNodes(columns);
  304. foreach (var setrow in Data.Rows)
  305. {
  306. Guid setid = setrow.Get<JobDocumentSet, Guid>(x => x.ID);
  307. Guid parentid = _flatlist ? Guid.Empty : setrow.Get<JobDocumentSet, Guid>(x => x.Parent.ID);
  308. var node = _documentsets.Add(setid, parentid);
  309. JobDocumentSetDescriptionBlock desc = new JobDocumentSetDescriptionBlock()
  310. {
  311. ID = setid,
  312. Code = setrow.Get<JobDocumentSet, String>(c => c.Code),
  313. Description = setrow.Get<JobDocumentSet, String>(c => c.Description),
  314. };
  315. node.Description = Serialization.Serialize(desc);
  316. JobDocumentSetDetailsBlock dets = new JobDocumentSetDetailsBlock()
  317. {
  318. ID = setid,
  319. Date = setrow.Get<JobDocumentSet, DateTime>(c => c.Date),
  320. Size = setrow.Get<JobDocumentSet, PaperSize>(c => c.Size),
  321. Scale = setrow.Get<JobDocumentSet, String>(c => c.Scale),
  322. Employee = setrow.Get<JobDocumentSet, String>(c => c.Employee.Name)
  323. };
  324. node.Details = Serialization.Serialize(dets);
  325. foreach (var typeid in _types.Keys)
  326. {
  327. if (_types[typeid].SetMileStones.TryGetValue(setid, out var rows))
  328. {
  329. int i = 1;
  330. foreach (var row in rows)
  331. {
  332. JobDocumentSetMileStoneBlock block = new JobDocumentSetMileStoneBlock();
  333. block.ID = row.Get<JobDocumentSetMileStone, Guid>(c => c.ID);
  334. block.Revision = row.Get<JobDocumentSetMileStone, String>(c => c.Revision);
  335. block.Status = row.Get<JobDocumentSetMileStone, JobDocumentSetMileStoneStatus>(c => c.Status);
  336. block.Date = (block.Status == JobDocumentSetMileStoneStatus.Approved) ||
  337. (block.Status == JobDocumentSetMileStoneStatus.Cancelled) ||
  338. (block.Status == JobDocumentSetMileStoneStatus.Rejected)
  339. ? row.Get<JobDocumentSetMileStone, DateTime>(c => c.Closed)
  340. : block.Status == JobDocumentSetMileStoneStatus.Submitted
  341. ? block.Date = row.Get<JobDocumentSetMileStone, DateTime>(c => c.Submitted)
  342. : row.Get<JobDocumentSetMileStone, DateTime>(c => c.Due);
  343. String[] notes = row.Get<JobDocumentSetMileStone, String[]>(c => c.Notes);
  344. block.Notes = notes != null ? String.Join("\n", notes) : "";
  345. block.Attachments = row.Get<JobDocumentSetMileStone, int>(c => c.Attachments);
  346. block.Watermark = row.Get<JobDocumentSetMileStone, String>(c => c.Watermark);
  347. node.Blocks[String.Format("{0}_{1}", _types[typeid].Code, i)] = Serialization.Serialize(block);
  348. i++;
  349. }
  350. }
  351. }
  352. }
  353. ConfigureColumns(_documentsets);
  354. ConfigureStackedHeader();
  355. treeGrid.ItemsSource = _documentsets.Nodes;
  356. DocumentCount.Content = $"{_documentsets.Nodes.Count} {(_documentsets.Nodes.Count > 1 ? "Records" : "Record")}";
  357. if (scrollviewer != null)
  358. {
  359. scrollviewer.ScrollToVerticalOffset(verticalOffset);
  360. scrollviewer.ScrollToHorizontalOffset(horizontalOffset);
  361. }
  362. }
  363. }
  364. #region Grid Configuration
  365. private void ConfigureColumns(DocumentSetNodes documentsets)
  366. {
  367. treeGrid.Columns.Clear();
  368. treeGrid.Columns.Add(new TreeGridTemplateColumn()
  369. {
  370. CellTemplate = FindResource("descriptionTemplate") as DataTemplate,
  371. MappingName = "Description",
  372. SetCellBoundValue = true,
  373. MinimumWidth = 250,
  374. ColumnSizer = TreeColumnSizer.Star
  375. });
  376. treeGrid.Columns.Add(new TreeGridTemplateColumn()
  377. {
  378. CellTemplate = FindResource("detailsTemplate") as DataTemplate,
  379. MappingName = "Details",
  380. SetCellBoundValue = true,
  381. Width = 120
  382. });
  383. foreach (var column in documentsets.Columns)
  384. {
  385. var col = new TreeGridTemplateColumn()
  386. {
  387. CellTemplate = FindResource("milestoneTemplate") as DataTemplate,
  388. MappingName = String.Format("Blocks[{0}]",column),
  389. SetCellBoundValue = true,
  390. HeaderText = " ",
  391. Width = 80,
  392. ShowToolTip = true
  393. };
  394. treeGrid.Columns.Add(col);
  395. }
  396. }
  397. private void ConfigureStackedHeader()
  398. {
  399. stackedHeaderRow.StackedColumns.Clear();
  400. stackedHeaderRow.StackedColumns.Add(new StackedColumn()
  401. {
  402. ChildColumns = "Description,Details",
  403. HeaderText = "Document Register"
  404. });
  405. foreach (var typeid in _types.Keys)
  406. {
  407. stackedHeaderRow.StackedColumns.Add(new StackedColumn()
  408. {
  409. ChildColumns = String.Join(",", _types[typeid].Columns),
  410. HeaderText = _types[typeid].Code
  411. });
  412. }
  413. }
  414. private void TreeGrid_OnItemsSourceChanged(object? sender, TreeGridItemsSourceChangedEventArgs e)
  415. {
  416. var panel = treeGrid.GetTreePanel();
  417. panel.RowHeights[1] = 0;
  418. }
  419. private void TreeGrid_OnNodeCollapsing(object? sender, NodeCollapsingEventArgs e)
  420. {
  421. e.Cancel = true;
  422. }
  423. public MenuItem CreateCalendar(ContextMenu menu, string text, DateTime startDate, CoreRow[] milestones, Action<CoreRow[], DateTime?>? action)
  424. {
  425. var item = new MenuItem();
  426. var calendarItem = new MenuItem();
  427. var calendar = new Calendar { DisplayDate = startDate, SelectedDate = null};
  428. calendar.SelectedDatesChanged += (o, e) =>
  429. {
  430. action?.Invoke(milestones, calendar.SelectedDate);
  431. menu.IsOpen = false;
  432. };
  433. calendarItem.Header = calendar;
  434. calendarItem.Style = DynamicGridUtils.Resources["NonHighlightMenuItem"] as Style;
  435. item.Header = text;
  436. item.Items.Add(calendarItem);
  437. item.IsCheckable = false;
  438. return item;
  439. }
  440. private void TreeGrid_OnContextMenuOpening(object sender, ContextMenuEventArgs e)
  441. {
  442. MileStoneMenu.Items.Clear();
  443. var tag = (e.OriginalSource as FrameworkElement).Tag;
  444. Point pos = Mouse.GetPosition(treeGrid);
  445. var treeGridPanel = this.treeGrid.GetTreePanel();
  446. // get the row and column index based on the pointer position
  447. var rowColumnIndex = treeGridPanel.PointToCellRowColumnIndex(pos);
  448. if (rowColumnIndex.IsEmpty)
  449. return;
  450. var treeNodeAtRowIndex = treeGrid.GetNodeAtRowIndex(rowColumnIndex.RowIndex);
  451. if (rowColumnIndex.ColumnIndex < 2)
  452. {
  453. var document = treeGrid.SelectedItem as DocumentSetNode;
  454. MenuItem edit = new MenuItem();
  455. edit.Header = "Edit Document Set";
  456. edit.Click += (o, args) => { EditDocumentSets(new Guid[] { document.ID }); };
  457. MileStoneMenu.Items.Add(edit);
  458. MileStoneMenu.Items.Add(new Separator());
  459. MenuItem addchild = new MenuItem();
  460. addchild.Header = "Add Child";
  461. addchild.Click += (o,args) => { AddChildDocument(document); };
  462. MileStoneMenu.Items.Add(addchild);
  463. var documents = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode));
  464. MenuItem movetofolder = new MenuItem();
  465. movetofolder.Header = "Move To Folder";
  466. bool hasfolders = PopulateFolders(movetofolder, documents);
  467. if (hasfolders)
  468. {
  469. MileStoneMenu.Items.Add(new Separator());
  470. MileStoneMenu.Items.Add(movetofolder);
  471. }
  472. return;
  473. }
  474. var mappingname = treeGrid.Columns[rowColumnIndex.ColumnIndex].MappingName;
  475. var blockkey = mappingname.Replace("Blocks[", "").Replace("]", "");
  476. var typeid = _types.FirstOrDefault(x => x.Value.Columns.Contains(mappingname)).Key;
  477. //Guid setid = (treeGrid.SelectedItem as DocumentSetNode).ID;
  478. Guid[] setids = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).ID).ToArray();
  479. //Guid.TryParse(tag.ToString(), out Guid milestoneid);
  480. var blocks = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).Blocks[blockkey]).Where(x => !String.IsNullOrWhiteSpace(x))
  481. .ToArray();
  482. var milestoneids = blocks.Select(x => Serialization.Deserialize<JobDocumentSetMileStoneBlock>(x).ID).ToArray();
  483. //var milestone = _milestones.Rows.FirstOrDefault(r => r.Get<JobDocumentSetMileStone, Guid>(c => c.ID) == milestoneid);
  484. var milestones = _milestones.Rows.Where(r => milestoneids.Contains(r.Get<JobDocumentSetMileStone, Guid>(c => c.ID))).ToArray();
  485. bool canCreateNewMileStones = true;
  486. foreach (var setid in setids)
  487. {
  488. var openmilestones = _milestones.Rows.Any(r =>
  489. Guid.Equals(r.Get<JobDocumentSetMileStone, Guid>(c => c.DocumentSet.ID), setid)
  490. && Guid.Equals(r.Get<JobDocumentSetMileStone, Guid>(c => c.Type.ID), typeid)
  491. && (r.Get<JobDocumentSetMileStone, DateTime>(c => c.Closed).IsEmpty() ||
  492. (r.Get<JobDocumentSetMileStone, JobDocumentSetMileStoneStatus>(c => c.Status) == JobDocumentSetMileStoneStatus.Approved))
  493. );
  494. if (openmilestones)
  495. canCreateNewMileStones = false;
  496. }
  497. if (canCreateNewMileStones)
  498. {
  499. MenuItem newmilestone = new MenuItem()
  500. {
  501. Header = "New Milestone",
  502. Tag = typeid
  503. };
  504. newmilestone.Click += (o, args) => { CreateMileStone(setids, typeid, DateTime.Today); };
  505. MileStoneMenu.Items.Add(newmilestone);
  506. }
  507. if (milestones.Any())
  508. {
  509. MenuItem setstatus = new MenuItem() { Header = "Change Status" };
  510. foreach (JobDocumentSetMileStoneStatus newstatus in Enum.GetValues(typeof(JobDocumentSetMileStoneStatus)))
  511. {
  512. MenuItem setstatus2 = null;
  513. switch (newstatus)
  514. {
  515. case JobDocumentSetMileStoneStatus.Unknown:
  516. break;
  517. case JobDocumentSetMileStoneStatus.NotStarted:
  518. case JobDocumentSetMileStoneStatus.InProgress:
  519. case JobDocumentSetMileStoneStatus.OnHold:
  520. case JobDocumentSetMileStoneStatus.InfoRequired:
  521. setstatus2 = new MenuItem() { Header = newstatus.ToString().SplitCamelCase() };
  522. setstatus2.Click += (o, args) => { ChangeMileStoneStatus(milestones, newstatus, DateTime.MinValue, DateTime.MinValue); };
  523. break;
  524. case JobDocumentSetMileStoneStatus.Submitted:
  525. setstatus2 = CreateCalendar(
  526. MileStoneMenu,
  527. newstatus.ToString().SplitCamelCase(),
  528. DateTime.Today,
  529. milestones,
  530. (r, t) => { ChangeMileStoneStatus(milestones, newstatus, t, DateTime.MinValue); }
  531. );
  532. break;
  533. case JobDocumentSetMileStoneStatus.Approved:
  534. case JobDocumentSetMileStoneStatus.Cancelled:
  535. case JobDocumentSetMileStoneStatus.Rejected:
  536. setstatus2 = CreateCalendar(
  537. MileStoneMenu,
  538. newstatus.ToString().SplitCamelCase(),
  539. DateTime.Today,
  540. milestones,
  541. (r, t) => { ChangeMileStoneStatus(milestones, newstatus, null, t); }
  542. );
  543. break;
  544. }
  545. if (setstatus2 != null)
  546. setstatus.Items.Add(setstatus2);
  547. }
  548. MileStoneMenu.Items.Add(setstatus);
  549. //var closed = milestones.Any(r => !r.Get<JobDocumentSetMileStone, DateTime>(c => c.Closed).IsEmpty());
  550. if ((setids.Length == 1) && (milestones.Length == 1)) // && !closed)
  551. {
  552. MenuItem editmilestone = new MenuItem() { Header = "Edit MileStone" };
  553. editmilestone.Click += (o, args) => { EditMileStone(milestones[0]); };
  554. MileStoneMenu.Items.Add(editmilestone);
  555. var attachments = milestones[0].Get<JobDocumentSetMileStone, int>(x => x.Attachments);
  556. if (attachments > 1)
  557. {
  558. MenuItem splitmilestone = new MenuItem() { Header = "Split MileStone" };
  559. splitmilestone.Click += (o, args) => { SplitMileStone(setids[0], milestones[0]); };
  560. MileStoneMenu.Items.Add(splitmilestone);
  561. }
  562. MileStoneMenu.Items.Add(new Separator());
  563. MenuItem upload = new MenuItem() { Header = "Upload Files" };
  564. upload.Click += (o, args) => { UploadFiles(milestones[0]); };
  565. MileStoneMenu.Items.Add(upload);
  566. }
  567. if (milestones.Any())
  568. {
  569. MenuItem download = new MenuItem() { Header = "Download Files" };
  570. download.Items.Add(new MenuItem());
  571. download.SubmenuOpened += (o, e) =>
  572. {
  573. download.Items.Clear();
  574. var files = new Client<JobDocumentSetMileStoneFile>().Query(
  575. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).InList(milestoneids),
  576. new Columns<JobDocumentSetMileStoneFile>(x => x.ID)
  577. .Add(x => x.DocumentLink.FileName)
  578. .Add(x => x.DocumentLink.ID),
  579. new SortOrder<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName)
  580. );
  581. if (files.Rows.Any())
  582. {
  583. foreach (var row in files.Rows)
  584. {
  585. MenuItem downloadone = new MenuItem()
  586. {
  587. Header = row.Get<JobDocumentSetMileStoneFile, String>(x => x.DocumentLink.FileName),
  588. };
  589. downloadone.Click += (sender, args) =>
  590. {
  591. DownloadFiles(
  592. new CoreRow[] { milestones[0] },
  593. row.Get<JobDocumentSetMileStoneFile, Guid>(x => x.DocumentLink.ID)
  594. );
  595. };
  596. download.Items.Add(downloadone);
  597. }
  598. if (download.Items.Count > 1)
  599. {
  600. download.Items.Add(new Separator());
  601. MenuItem downloadall = new MenuItem()
  602. {
  603. Header = "Download All",
  604. };
  605. downloadall.Click += (sender, args) =>
  606. {
  607. DownloadFiles(
  608. milestones,
  609. Guid.Empty
  610. );
  611. };
  612. download.Items.Add(downloadall);
  613. }
  614. }
  615. else
  616. {
  617. download.Items.Add(
  618. new MenuItem()
  619. {
  620. Header = "No Files to download",
  621. IsEnabled = false
  622. }
  623. );
  624. }
  625. };
  626. MileStoneMenu.Items.Add(download);
  627. }
  628. // if ((milestoneids.Length == 1)) // && !closed)
  629. // {
  630. // MenuItem managefiles = new MenuItem()
  631. // {
  632. // Header = "Manage Files"
  633. // };
  634. // managefiles.Click += (sender, args) => { ManageFiles(milestones[0]); };
  635. // MileStoneMenu.Items.Add(managefiles);
  636. // }
  637. MileStoneMenu.Items.Add(new Separator());
  638. MenuItem delete = new MenuItem { Header = "Delete MileStone" };
  639. delete.Click += (o, args) => DeleteMileStone(milestones);
  640. MileStoneMenu.Items.Add(delete);
  641. }
  642. if (MileStoneMenu.Items.Count == 0)
  643. e.Handled = true;
  644. }
  645. private bool PopulateFolders(MenuItem menu, IEnumerable<DocumentSetNode> documents)
  646. {
  647. CoreTable data = new Client<JobDocumentSetFolder>().Query(
  648. new Filter<JobDocumentSetFolder>(x => x.Job.ID).IsEqualTo(JobID),
  649. new Columns<JobDocumentSetFolder>(x => x.ID)
  650. .Add(x => x.Parent.ID)
  651. .Add(x => x.Name)
  652. );
  653. if (!data.Rows.Any())
  654. return false;
  655. DynamicTreeNodes folders = new DynamicTreeNodes();
  656. folders.Load<JobDocumentSetFolder>(data, x => x.ID, x => x.Parent.ID, x => x.Name);
  657. foreach (var folder in folders.Nodes)
  658. DoPopulateFolder(menu, folder, documents);
  659. return true;
  660. }
  661. private void DoPopulateFolder(MenuItem header, DynamicTreeNode folder, IEnumerable<DocumentSetNode> documents)
  662. {
  663. MenuItem menu = new MenuItem();
  664. menu.Header = folder.Description;
  665. menu.Click += (sender, args) => MoveToFolder(documents, folder);
  666. header.Items.Add(menu);
  667. foreach (var childfolder in folder.Children)
  668. DoPopulateFolder(menu, childfolder, documents);
  669. }
  670. private void MoveToFolder(IEnumerable<DocumentSetNode> documents, DynamicTreeNode folder)
  671. {
  672. if (FolderID != folder.ID)
  673. {
  674. using (new WaitCursor())
  675. {
  676. List<JobDocumentSet> updates = new List<JobDocumentSet>();
  677. foreach (var document in documents)
  678. {
  679. var update = new JobDocumentSet();
  680. update.ID = document.ID;
  681. update.Folder.ID = folder.ID;
  682. update.Parent.ID = Guid.Empty;
  683. updates.Add(update);
  684. }
  685. new Client<JobDocumentSet>().Save(updates, "Moved to Folder: " + folder.Description);
  686. }
  687. Refresh();
  688. }
  689. else
  690. {
  691. MessageBox.Show("Target Folder is the same as Source Folder!");
  692. }
  693. }
  694. private void SplitMileStone(Guid setid, CoreRow milestone)
  695. {
  696. if (MessageBox.Show(
  697. "Are you sure you wish to split this Document Set?",
  698. "Confirm Delete",
  699. MessageBoxButton.YesNo
  700. ) != MessageBoxResult.Yes)
  701. return;
  702. Guid milestoneid = milestone.Get<JobDocumentSetMileStone, Guid>(c => c.ID);
  703. var dlg = new MultiSelectDialog<JobDocumentSetMileStoneFile>(
  704. new Filter<JobDocumentSetMileStoneFile>(c => c.EntityLink.ID).IsEqualTo(milestoneid),
  705. null,
  706. true
  707. );
  708. if (dlg.ShowDialog() == true)
  709. {
  710. var files = dlg.Items();
  711. Progress.ShowModal("Splitting Document Set", (progress) =>
  712. {
  713. JobDocumentSet newset = new Client<JobDocumentSet>().Query(
  714. new Filter<JobDocumentSet>(x => x.ID).IsEqualTo(setid)
  715. ).Rows.FirstOrDefault()?.ToObject<JobDocumentSet>();
  716. if (newset != null)
  717. {
  718. newset.ID = Guid.Empty;
  719. newset.CommitChanges();
  720. newset.Parent.ID = setid;
  721. newset.Code = String.Format("{0} (COPY)", newset.Code);
  722. //newset.Description = "New Child";
  723. new Client<JobDocumentSet>().Save(newset, "Created by Splitting MileStone");
  724. progress.Report("Creating Milestone");
  725. JobDocumentSetMileStone newms = new Client<JobDocumentSetMileStone>().Query(
  726. new Filter<JobDocumentSetMileStone>(c=>c.ID).IsEqualTo(milestoneid)
  727. ).Rows.FirstOrDefault()?.ToObject<JobDocumentSetMileStone>();
  728. if (newms != null)
  729. {
  730. newms.ID = Guid.Empty;
  731. newset.CommitChanges();
  732. newms.DocumentSet.ID = newset.ID;
  733. new Client<JobDocumentSetMileStone>().Save(newms, "Created By Splitting MileStone");
  734. progress.Report("Moving Files");
  735. foreach (var file in files)
  736. file.EntityLink.ID = newms.ID;
  737. new Client<JobDocumentSetMileStoneFile>().Save(files, "Moved when Splitting MileStone");
  738. }
  739. }
  740. });
  741. Refresh();
  742. }
  743. }
  744. private void AddChildDocument(DocumentSetNode node)
  745. {
  746. JobDocumentSet newset = new JobDocumentSet();
  747. newset.Parent.ID = node.ID;
  748. newset.Job.ID = JobID;
  749. newset.Folder.ID = FolderID;
  750. var grid = new DynamicDataGrid<JobDocumentSet>();
  751. if (grid.EditItems(new[] { newset }))
  752. Refresh();
  753. }
  754. // private void ManageFiles(CoreRow milestone)
  755. // {
  756. // var grid = new JobDocumentSetMileStoneFileGrid();
  757. // grid.OnGetWaterMark += (row) => milestone.Get<JobDocumentSetMileStone, String>(c => c.Watermark);
  758. // grid.ShowSupercededColumn = false;
  759. // Window window = new Window();
  760. // window.Padding = new Thickness(5);
  761. // window.Content = grid;
  762. // window.Width = 300;
  763. // window.Height = 500;
  764. // window.WindowStartupLocation = WindowStartupLocation.CenterScreen;
  765. // grid.Load(milestone.ToObject<JobDocumentSetMileStone>(), null);
  766. // grid.Margin = new Thickness(5);
  767. // grid.Refresh(true, true);
  768. // window.ShowDialog();
  769. // Refresh();
  770. // }
  771. private void DownloadFiles(CoreRow[] rows, Guid id)
  772. {
  773. FolderBrowserDialog dlg = new FolderBrowserDialog();
  774. if (dlg.ShowDialog() == DialogResult.OK)
  775. {
  776. Progress.ShowModal("Downloading Files", (progress) =>
  777. {
  778. foreach (var row in rows)
  779. {
  780. var status = row.Get<JobDocumentSetMileStone, JobDocumentSetMileStoneStatus>(c => c.Status);
  781. var stage = row.Get<JobDocumentSetMileStone, String>(c => c.Type.Code);
  782. var revision = row.Get<JobDocumentSetMileStone, String>(c => c.Revision);
  783. String tag = String.Format(" - {0}{1} ({2})", stage, String.IsNullOrWhiteSpace(revision) ? "" : " - Rev " + revision,
  784. status.ToString().SplitCamelCase());
  785. var filter = id == Guid.Empty
  786. ? new Filter<Document>(x => x.ID).InQuery(
  787. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).IsEqualTo(
  788. row.Get<JobDocumentSetMileStone, Guid>(c => c.ID)),
  789. x => x.DocumentLink.ID
  790. )
  791. : new Filter<Document>(x => x.ID).IsEqualTo(id);
  792. var files = new Client<Document>().Query(filter);
  793. foreach (var filerow in files.Rows)
  794. {
  795. string filename = filerow.Get<Document, String>(c => c.FileName);
  796. string extension = Path.GetExtension(filename);
  797. string basefilename = Path.GetFileNameWithoutExtension(filename);
  798. filename = String.Format("{0}{1}{2}", basefilename, tag, extension);
  799. filename = Path.Combine(dlg.SelectedPath, filename);
  800. File.WriteAllBytes(filename, filerow.Get<Document, byte[]>(c => c.Data));
  801. }
  802. }
  803. });
  804. Process.Start(new ProcessStartInfo(dlg.SelectedPath) { UseShellExecute = true });
  805. }
  806. }
  807. private bool SelectFiles(out String[] files)
  808. {
  809. Microsoft.Win32.OpenFileDialog dlg = new Microsoft.Win32.OpenFileDialog();
  810. dlg.Filter = "PDF Files (*.pdf)|*.pdf";
  811. dlg.Multiselect = true;
  812. if ((dlg.ShowDialog() == true) && (dlg.FileNames.Length > 0))
  813. {
  814. files = dlg.FileNames.ToArray();
  815. return true;
  816. }
  817. files = null;
  818. return false;
  819. }
  820. private void UploadFiles(CoreRow row)
  821. {
  822. Guid id = row.Get<JobDocumentSetMileStone, Guid>(c => c.ID);
  823. if (SelectFiles(out String[] filenames))
  824. {
  825. Progress.ShowModal("Uploading Files", (progress) =>
  826. {
  827. List<Document> documents = new List<Document>();
  828. foreach (var file in filenames)
  829. {
  830. var data = File.ReadAllBytes(file);
  831. documents.Add(
  832. new Document()
  833. {
  834. FileName = Path.GetFileName(file).ToLower(),
  835. Data = data,
  836. CRC = CoreUtils.CalculateCRC(data),
  837. TimeStamp = new FileInfo(file).LastWriteTime
  838. }
  839. );
  840. }
  841. new Client<Document>().Save(documents.ToArray(), "Uploaded by User");
  842. progress.Report("Updating Links");
  843. List<JobDocumentSetMileStoneFile> links = new List<JobDocumentSetMileStoneFile>();
  844. foreach (var document in documents)
  845. {
  846. var link = new JobDocumentSetMileStoneFile();
  847. link.EntityLink.ID = id;
  848. link.DocumentLink.ID = document.ID;
  849. links.Add(link);
  850. }
  851. new Client<JobDocumentSetMileStoneFile>().Save(links, "Uploaded By User");
  852. });
  853. MessageBox.Show(String.Format("{0} files uploaded", filenames.Length));
  854. Refresh();
  855. }
  856. }
  857. private Dictionary<Guid, JobDocumentSetMileStone> GetPreviousMileStones(Guid[] setids, Guid typeid)
  858. {
  859. var result = new Dictionary<Guid, JobDocumentSetMileStone>();
  860. foreach (var setid in setids)
  861. {
  862. var typeindex = _types.Keys.IndexOf(typeid);
  863. JobDocumentSetMileStone? last = null;
  864. while ((last == null) && (typeindex > 0))
  865. {
  866. last = _milestones.Rows.LastOrDefault(r =>
  867. (r.Get<JobDocumentSetMileStone, Guid>(c => c.DocumentSet.ID) == setid) &&
  868. (r.Get<JobDocumentSetMileStone, Guid>(c => c.Type.ID) == _types.Keys.ToArray()[typeindex]))
  869. ?.ToObject<JobDocumentSetMileStone>();
  870. typeindex--;
  871. }
  872. if (last != null)
  873. result[setid] = last;
  874. }
  875. return result;
  876. }
  877. private void CreateMileStone(Guid[] setids, Guid typeid, DateTime duedate)
  878. {
  879. bool bCopy = false;
  880. var lastmilestones = GetPreviousMileStones(setids, typeid);
  881. if (lastmilestones.Any(x => x.Value.Attachments > 0))
  882. {
  883. var confirm = MessageBox.Show("Do you wish to copy the files from the previous milestones?", "Copy Files",
  884. MessageBoxButton.YesNoCancel);
  885. if (confirm == MessageBoxResult.Cancel)
  886. return;
  887. bCopy = confirm == MessageBoxResult.Yes;
  888. }
  889. Dictionary<JobDocumentSetMileStone,JobDocumentSetMileStoneFile[]> updates = new Dictionary<JobDocumentSetMileStone, JobDocumentSetMileStoneFile[]>();
  890. foreach (var setid in setids)
  891. {
  892. JobDocumentSetMileStone milestone = new JobDocumentSetMileStone();
  893. milestone.DocumentSet.ID = setid;
  894. milestone.Type.ID = typeid;
  895. milestone.Status = JobDocumentSetMileStoneStatus.NotStarted;
  896. milestone.Due = duedate;
  897. JobDocumentSetMileStoneFile[] files = new JobDocumentSetMileStoneFile[] { };
  898. if (bCopy && lastmilestones.TryGetValue(setid, out var lastmilestone))
  899. {
  900. if (lastmilestone.Attachments > 0)
  901. {
  902. files = new Client<JobDocumentSetMileStoneFile>().Query(
  903. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).InList(lastmilestone.ID),
  904. new Columns<JobDocumentSetMileStoneFile>(x=>x.EntityLink.DocumentSet.ID)
  905. .Add(x => x.DocumentLink.FileName)
  906. .Add(x => x.DocumentLink.ID),
  907. new SortOrder<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName)
  908. ).Rows.Select(r=>r.ToObject<JobDocumentSetMileStoneFile>()).ToArray();
  909. }
  910. }
  911. updates[milestone] = files;
  912. }
  913. var grid = new JobDocumentSetMileStoneGrid();
  914. grid.OnAfterSave += (editor, items) =>
  915. {
  916. if (updates.Keys.Count == 1)
  917. return;
  918. List<JobDocumentSetMileStoneFile> fileupdates = new List<JobDocumentSetMileStoneFile>();
  919. foreach (var milestone in updates.Keys)
  920. {
  921. foreach (var file in updates[milestone])
  922. {
  923. file.EntityLink.ID = milestone.ID;
  924. fileupdates.Add(file);
  925. }
  926. }
  927. if (fileupdates.Any())
  928. new Client<JobDocumentSetMileStoneFile>().Save(fileupdates,"");
  929. };
  930. if (grid.EditItems(updates.Keys.ToArray(), (t) =>
  931. {
  932. if ((t == typeof(JobDocumentSetMileStoneFile)) && (updates.Keys.Count == 1))
  933. {
  934. CoreTable result = new CoreTable();
  935. result.LoadColumns(typeof(JobDocumentSetMileStoneFile));
  936. result.LoadRows(updates[updates.Keys.First()]);
  937. return result;
  938. }
  939. return null;
  940. }, true)
  941. )
  942. Refresh();
  943. }
  944. private void ChangeMileStoneStatus(CoreRow[] rows, JobDocumentSetMileStoneStatus newstatus, DateTime? issued, DateTime? closed)
  945. {
  946. var milestones = rows.Select(r=>r.ToObject<JobDocumentSetMileStone>()).ToArray();
  947. foreach (var milestone in milestones)
  948. {
  949. if (issued.HasValue)
  950. milestone.Submitted = issued.Value;
  951. if (closed.HasValue)
  952. milestone.Closed = closed.Value;
  953. milestone.Status = newstatus;
  954. }
  955. using (new WaitCursor())
  956. new Client<JobDocumentSetMileStone>().Save(milestones, "Changed Status to " + newstatus.ToString().SplitCamelCase());
  957. Refresh();
  958. }
  959. private void EditMileStone(CoreRow row)
  960. {
  961. var milestone = new Client<JobDocumentSetMileStone>().Query(
  962. new Filter<JobDocumentSetMileStone>(x => x.ID).IsEqualTo(row.Get<JobDocumentSetMileStone, Guid>(x => x.ID))
  963. ).Rows.FirstOrDefault()?.ToObject<JobDocumentSetMileStone>();
  964. var grid = new JobDocumentSetMileStoneGrid();
  965. if (grid.EditItems(new[] { milestone }))
  966. Refresh();
  967. }
  968. private void DeleteMileStone(CoreRow[] rows)
  969. {
  970. var milestones = rows.Select(r=>r.ToObject<JobDocumentSetMileStone>()).ToArray();
  971. using (new WaitCursor())
  972. new Client<JobDocumentSetMileStone>().Delete(milestones,"Deleted by User");
  973. Refresh();
  974. }
  975. private void TreeGrid_OnCellToolTipOpening(object? sender, TreeGridCellToolTipOpeningEventArgs e)
  976. {
  977. var column = e.Column.MappingName.Replace("Blocks[","").Replace("]","");
  978. var data = (e.Record as DocumentSetNode).Blocks[column];
  979. if (String.IsNullOrWhiteSpace(data))
  980. return;
  981. var block = Serialization.Deserialize<JobDocumentSetMileStoneBlock>(data.ToString());
  982. Guid id = block.ID;
  983. TextBlock text = new TextBlock();
  984. if (!String.IsNullOrWhiteSpace(block.Notes))
  985. {
  986. text.Inlines.Add(new Run("Milestone Notes\n") { FontWeight = FontWeights.Bold, TextDecorations = TextDecorations.Underline });
  987. text.Inlines.Add(new Run(block.Notes.Replace("=","").Replace("\n\n","\n")) { FontStyle = FontStyles.Italic });
  988. }
  989. if (block.Attachments > 0)
  990. {
  991. if (!String.IsNullOrWhiteSpace(block.Notes))
  992. text.Inlines.Add(new Run("\n\n"));
  993. text.Inlines.Add(new Run("Uploaded Files") { FontWeight = FontWeights.Bold, TextDecorations = TextDecorations.Underline });
  994. var files = new Client<JobDocumentSetMileStoneFile>().Query(
  995. new Filter<JobDocumentSetMileStoneFile>(x => x.EntityLink.ID).IsEqualTo(block.ID),
  996. new Columns<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName),
  997. new SortOrder<JobDocumentSetMileStoneFile>(x => x.DocumentLink.FileName)
  998. );
  999. foreach (var row in files.Rows)
  1000. text.Inlines.Add(new Run("\n"+row.Get<JobDocumentSetMileStoneFile,String>(c=>c.DocumentLink.FileName)) { FontStyle = FontStyles.Italic });
  1001. }
  1002. if (!text.Inlines.Any())
  1003. {
  1004. e.ToolTip.Template = null;
  1005. return;
  1006. }
  1007. e.ToolTip.Template = TemplateGenerator.CreateControlTemplate(
  1008. typeof(System.Windows.Controls.ToolTip),
  1009. () =>
  1010. {
  1011. var border = new Border
  1012. {
  1013. BorderBrush = new SolidColorBrush(Colors.Gray),
  1014. BorderThickness = new Thickness(0.75),
  1015. CornerRadius = new CornerRadius(5),
  1016. Background = new SolidColorBrush(Colors.LightYellow),
  1017. Padding = new Thickness(5),
  1018. Child = text
  1019. };
  1020. return border;
  1021. }
  1022. );
  1023. }
  1024. #endregion
  1025. #region Button Bar Actions
  1026. private void AddTypes(MenuItem parent, Action<Guid> addfunction)
  1027. {
  1028. if(_types.Count == 0)
  1029. {
  1030. MenuItem item = new MenuItem() { Header = "No Document Milestones", IsEnabled = false };
  1031. parent.Items.Add(item);
  1032. }
  1033. else
  1034. {
  1035. foreach (var type in _types.Keys)
  1036. {
  1037. MenuItem item = new MenuItem() { Header = _types[type].Description, Tag = type };
  1038. item.Click += (o, e) => addfunction(type);
  1039. parent.Items.Add(item);
  1040. }
  1041. }
  1042. }
  1043. private void Add_OnClick(object sender, RoutedEventArgs e)
  1044. {
  1045. if (FolderID == Guid.Empty)
  1046. {
  1047. MessageBox.Show("Please choose a Folder first!");
  1048. return;
  1049. }
  1050. ContextMenu menu = new ContextMenu();
  1051. var onetoone = new MenuItem() { Header = "Add Individual Files" };
  1052. AddTypes(onetoone, AddOneToOneFiles);
  1053. menu.Items.Add(onetoone);
  1054. var manytoone = new MenuItem() { Header = "Add Sets of Files" };
  1055. AddTypes(manytoone, AddManyToOneFiles);
  1056. menu.Items.Add(manytoone);
  1057. menu.Items.Add(new Separator());
  1058. var manual = new MenuItem() { Header = "Add Document Set Manually" };
  1059. manual.Click += (o, e) => { AddDocumentSet(); };
  1060. menu.Items.Add(manual);
  1061. menu.IsOpen = true;
  1062. }
  1063. private void AddOneToOneFiles(Guid type)
  1064. {
  1065. if (!SelectFiles(out String[] filenames))
  1066. return;
  1067. Progress.ShowModal("Preparing Upload", (progress) =>
  1068. {
  1069. Dictionary<String, Tuple<Document, JobDocumentSet, JobDocumentSetMileStone, JobDocumentSetMileStoneFile>> map =
  1070. new Dictionary<string, Tuple<Document, JobDocumentSet, JobDocumentSetMileStone, JobDocumentSetMileStoneFile>>();
  1071. foreach (var filename in filenames)
  1072. {
  1073. var data = File.ReadAllBytes(filename);
  1074. Document document = new Document()
  1075. {
  1076. FileName = Path.GetFileName(filename).ToLower(),
  1077. Data = data,
  1078. CRC = CoreUtils.CalculateCRC(data),
  1079. TimeStamp = new FileInfo(filename).LastWriteTime
  1080. };
  1081. JobDocumentSet set = new JobDocumentSet();
  1082. set.Job.ID = JobID;
  1083. set.Folder.ID = FolderID;
  1084. set.Code = Path.GetFileNameWithoutExtension(filename).ToUpper();
  1085. set.Description = Path.GetFileNameWithoutExtension(filename).ToUpper();
  1086. set.Discipline.ID = DisciplineID;
  1087. set.Type.ID = TypeID;
  1088. set.Category.ID = CategoryID;
  1089. set.Area.ID = AreaID;
  1090. JobDocumentSetMileStone milestone = new JobDocumentSetMileStone();
  1091. milestone.Type.ID = type;
  1092. milestone.Status = JobDocumentSetMileStoneStatus.InProgress;
  1093. milestone.Due = DateTime.Today;
  1094. JobDocumentSetMileStoneFile file = new JobDocumentSetMileStoneFile();
  1095. map[filename] = new Tuple<Document, JobDocumentSet, JobDocumentSetMileStone, JobDocumentSetMileStoneFile>(
  1096. document,
  1097. set,
  1098. milestone,
  1099. file
  1100. );
  1101. }
  1102. progress.Report("Uploading Files");
  1103. var docs = map.Select(x => x.Value.Item1);
  1104. new Client<Document>().Save(docs, "Uploaded By File Selection");
  1105. progress.Report("Creating Document Sets");
  1106. var sets = map.Select(x => x.Value.Item2);
  1107. new Client<JobDocumentSet>().Save(sets, "Uploaded by File Selection");
  1108. progress.Report("Creating MileStones");
  1109. foreach (var key in map.Keys)
  1110. map[key].Item3.DocumentSet.ID = map[key].Item2.ID;
  1111. var milestones = map.Select(x => x.Value.Item3);
  1112. new Client<JobDocumentSetMileStone>().Save(milestones, "Uploaded by File Selection");
  1113. progress.Report("Linking Documents");
  1114. foreach (var key in map.Keys)
  1115. {
  1116. map[key].Item4.EntityLink.ID = map[key].Item3.ID;
  1117. map[key].Item4.DocumentLink.ID = map[key].Item1.ID;
  1118. }
  1119. var files = map.Select(x => x.Value.Item4);
  1120. new Client<JobDocumentSetMileStoneFile>().Save(files, "Uploaded by File Selection");
  1121. });
  1122. MessageBox.Show(String.Format("{0} Document Sets Created", filenames.Length));
  1123. Refresh();
  1124. }
  1125. private void AddManyToOneFiles(Guid type)
  1126. {
  1127. if (!SelectFiles(out String[] filenames))
  1128. return;
  1129. JobDocumentSet set = new JobDocumentSet();
  1130. set.Job.ID = JobID;
  1131. set.Folder.ID = FolderID;
  1132. set.Discipline.ID = DisciplineID;
  1133. set.Type.ID = TypeID;
  1134. set.Category.ID = CategoryID;
  1135. set.Area.ID = AreaID;
  1136. var grid = new DynamicDataGrid<JobDocumentSet>();
  1137. grid.OnAfterSave += (form, items) =>
  1138. {
  1139. Progress.ShowModal("Creating MileStone", (progress) =>
  1140. {
  1141. JobDocumentSetMileStone milestone = new JobDocumentSetMileStone();
  1142. milestone.DocumentSet.ID = set.ID;
  1143. milestone.Type.ID = type;
  1144. milestone.Status = JobDocumentSetMileStoneStatus.InProgress;
  1145. milestone.Due = DateTime.Today;
  1146. new Client<JobDocumentSetMileStone>().Save(milestone, "Uploaded By File Selection");
  1147. progress.Report("Uploading Files");
  1148. List<Document> documents = new List<Document>();
  1149. foreach (var filename in filenames)
  1150. {
  1151. var data = File.ReadAllBytes(filename);
  1152. Document document = new Document()
  1153. {
  1154. FileName = Path.GetFileName(filename).ToLower(),
  1155. Data = data,
  1156. CRC = CoreUtils.CalculateCRC(data),
  1157. TimeStamp = new FileInfo(filename).LastWriteTime
  1158. };
  1159. documents.Add(document);
  1160. new Client<Document>().Save(documents, "Uploaded by File Selection");
  1161. }
  1162. progress.Report("Creating File Links");
  1163. List<JobDocumentSetMileStoneFile> files = new List<JobDocumentSetMileStoneFile>();
  1164. foreach (var document in documents)
  1165. {
  1166. JobDocumentSetMileStoneFile file = new JobDocumentSetMileStoneFile();
  1167. file.EntityLink.ID = milestone.ID;
  1168. file.DocumentLink.ID = document.ID;
  1169. files.Add(file);
  1170. }
  1171. new Client<JobDocumentSetMileStoneFile>().Save(files, "Uploaded by File Selection");
  1172. });
  1173. };
  1174. if (grid.EditItems(new[] { set }))
  1175. {
  1176. MessageBox.Show(String.Format("{0} files uploaded", filenames.Length));
  1177. Refresh();
  1178. }
  1179. }
  1180. private void AddDocumentSet()
  1181. {
  1182. JobDocumentSet set = new JobDocumentSet();
  1183. set.Job.ID = JobID;
  1184. set.Folder.ID = FolderID;
  1185. set.Discipline.ID = DisciplineID;
  1186. set.Type.ID = TypeID;
  1187. set.Category.ID = CategoryID;
  1188. set.Area.ID = AreaID;
  1189. var grid = new DynamicDataGrid<JobDocumentSet>();
  1190. if (grid.EditItems(new[] { set }))
  1191. Refresh();
  1192. }
  1193. private void Edit_OnClick(object sender, RoutedEventArgs e)
  1194. {
  1195. if (treeGrid.SelectedItem == null)
  1196. {
  1197. MessageBox.Show("Please choose a Document Set first");
  1198. return;
  1199. }
  1200. Guid[] setIDs = treeGrid.SelectedItems.Select(x => (x as DocumentSetNode).ID).ToArray();
  1201. EditDocumentSets(setIDs);
  1202. }
  1203. private void EditDocumentSets(Guid[] setIDs)
  1204. {
  1205. var sets = new Client<JobDocumentSet>().Query(
  1206. new Filter<JobDocumentSet>(x => x.ID).InList(setIDs)
  1207. ).Rows.Select(x => x.ToObject<JobDocumentSet>()).ToArray();
  1208. var grid = new DynamicDataGrid<JobDocumentSet>();
  1209. grid.OnCustomiseEditor += (form, items, column, editor) =>
  1210. {
  1211. if (String.Equals(column.ColumnName, "Discipline.ID"))
  1212. editor.Editable = DisciplineVisible ? Editable.Enabled : Editable.Hidden;
  1213. if (String.Equals(column.ColumnName, "Type.ID"))
  1214. editor.Editable = TypeVisible ? Editable.Enabled : Editable.Hidden;
  1215. if (String.Equals(column.ColumnName, "Category.ID"))
  1216. editor.Editable = CategoryVisible ? Editable.Enabled : Editable.Hidden;
  1217. if (String.Equals(column.ColumnName, "Area.ID"))
  1218. editor.Editable = AreaVisible ? Editable.Enabled : Editable.Hidden;
  1219. };
  1220. if (grid.EditItems(sets))
  1221. UpdateNodes(sets);
  1222. }
  1223. private void UpdateNodes(IEnumerable<JobDocumentSet> sets)
  1224. {
  1225. if (_documentsets == null)
  1226. return;
  1227. foreach (var set in sets)
  1228. {
  1229. var node = _documentsets.GetNode(set.ID);
  1230. if (node != null)
  1231. {
  1232. JobDocumentSetDescriptionBlock desc = new JobDocumentSetDescriptionBlock()
  1233. {
  1234. ID = set.ID,
  1235. Code = set.Code,
  1236. Description = set.Description,
  1237. };
  1238. node.Description = Serialization.Serialize(desc);
  1239. JobDocumentSetDetailsBlock dets = new JobDocumentSetDetailsBlock()
  1240. {
  1241. ID = set.ID,
  1242. Date = set.Date,
  1243. Size = set.Size,
  1244. Scale = set.Scale,
  1245. Employee = set.Employee.Name
  1246. };
  1247. node.Details = Serialization.Serialize(dets);
  1248. }
  1249. }
  1250. }
  1251. private void HideRejected_OnClick(object sender, RoutedEventArgs e)
  1252. {
  1253. _hidesuperceded = !_hidesuperceded;
  1254. HideSupercededLabel.Content = _hidesuperceded ? "Show All" : "Last Only";
  1255. Refresh();
  1256. }
  1257. private void Delete_OnClick(object sender, RoutedEventArgs e)
  1258. {
  1259. if ((treeGrid.SelectedItems == null) || !treeGrid.SelectedItems.Any())
  1260. {
  1261. MessageBox.Show("Please choose a Document Set first");
  1262. return;
  1263. }
  1264. if (MessageBox.Show(
  1265. "Are you sure you wish to delete the selected Document Sets?",
  1266. "Confirm Delete",
  1267. MessageBoxButton.YesNo
  1268. ) != MessageBoxResult.Yes)
  1269. return;
  1270. List<JobDocumentSet> updates = new List<JobDocumentSet>();
  1271. List<DocumentSetNode> orphans = new List<DocumentSetNode>();
  1272. var items = treeGrid.SelectedItems.Select(x => (DocumentSetNode)x).ToArray();
  1273. foreach (DocumentSetNode item in items)
  1274. {
  1275. var children = item.Children.Where(x => !items.Contains(x));
  1276. if (children.Any())
  1277. orphans.AddRange(children);
  1278. }
  1279. if (orphans.Any())
  1280. {
  1281. var confirm = MessageBox.Show(
  1282. "These Document Sets contain children!\nDo you wish to delete these as well?",
  1283. "Delete Children",
  1284. MessageBoxButton.YesNoCancel
  1285. );
  1286. if (confirm == MessageBoxResult.Cancel)
  1287. return;
  1288. if (confirm == MessageBoxResult.No)
  1289. {
  1290. foreach (var orphan in orphans)
  1291. {
  1292. var update = new JobDocumentSet();
  1293. update.ID = orphan.ID;
  1294. update.Parent.ID = Guid.Empty;
  1295. updates.Add(update);
  1296. }
  1297. return;
  1298. }
  1299. }
  1300. Progress.ShowModal("Deleting Document Set",(progress) =>
  1301. {
  1302. if (updates.Any())
  1303. new Client<JobDocumentSet>().Save(updates, "Parent Document Deleted");
  1304. var deletes = items.Select(x=>new JobDocumentSet() { ID = x.ID }).ToArray();
  1305. new Client<JobDocumentSet>().Delete(deletes, "Deleted By User");
  1306. });
  1307. Refresh();
  1308. }
  1309. #endregion
  1310. private void FlatList_OnClick(object sender, RoutedEventArgs e)
  1311. {
  1312. _flatlist = !_flatlist;
  1313. FlatListLabel.Content = _flatlist ? "Tree View" : "Flat List";
  1314. Refresh();
  1315. }
  1316. private void IncludeRetired_OnClick(object sender, RoutedEventArgs e)
  1317. {
  1318. _includeretired = !_includeretired;
  1319. FlatListLabel.Content = _includeretired ? "Active Only" : "Include Retired";
  1320. Refresh();
  1321. }
  1322. private void TreeGrid_OnSelectionChanged(object? sender, GridSelectionChangedEventArgs e)
  1323. {
  1324. //var treeColumn = treeGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex];
  1325. //var column = treeColumn.MappingName.Replace("Blocks[","").Replace("]","");
  1326. // var column = e.Column.MappingName.Replace("Blocks[","").Replace("]","");
  1327. // var data = (e.Record as DocumentSetNode).Blocks[column];
  1328. // if (String.IsNullOrWhiteSpace(data))
  1329. // return;
  1330. //
  1331. // var block = Serialization.Deserialize<JobDocumentSetMileStoneBlock>(data.ToString());
  1332. // Guid id = block.ID;
  1333. }
  1334. private void TreeGrid_OnCurrentCellActivated(object? sender, CurrentCellActivatedEventArgs e)
  1335. {
  1336. var node = treeGrid.CurrentItem as DocumentSetNode;
  1337. if (node == null)
  1338. return;
  1339. var treeColumn = treeGrid.Columns[e.CurrentRowColumnIndex.ColumnIndex];
  1340. var column = treeColumn.MappingName.Replace("Blocks[","").Replace("]","");
  1341. if (!node.Blocks.ContainsKey(column))
  1342. MileStoneSelected(null);
  1343. else
  1344. {
  1345. var block = Serialization.Deserialize<JobDocumentSetMileStoneBlock>(node.Blocks[column]);
  1346. MileStoneSelected?.Invoke(block);
  1347. }
  1348. }
  1349. private void TreeGrid_OnCellDoubleTapped(object? sender, TreeGridCellDoubleTappedEventArgs e)
  1350. {
  1351. var set = e.Record as DocumentSetNode;
  1352. if (set != null)
  1353. EditDocumentSets(new Guid[] { set.ID });
  1354. }
  1355. }
  1356. }