JobDocumentSetTree.xaml.cs 82 KB

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