JobDocumentSetTree.xaml.cs 82 KB

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