JobDocumentSetTree.xaml.cs 87 KB

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