JobDocumentSetTree.xaml.cs 87 KB

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