StagingPanel.xaml.cs 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579
  1. using Comal.Classes;
  2. using InABox.Core;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using InABox.Clients;
  9. using InABox.Configuration;
  10. using InABox.DynamicGrid;
  11. using System.Diagnostics;
  12. using System.IO;
  13. using InABox.WPF;
  14. using System.ComponentModel;
  15. using InABox.Scripting;
  16. using System.Reflection;
  17. using System.Collections.Immutable;
  18. using com.sun.istack.@internal.localization;
  19. namespace PRSDesktop
  20. {
  21. [Caption("Staging Panel Settings")]
  22. public class StagingPanellSettings : BaseObject, IGlobalConfigurationSettings
  23. {
  24. [Caption("PDF Markup Program Pathway", IncludePath = false)]
  25. [FileNameEditor]
  26. public string MarkupPathway { get; set; }
  27. [FolderEditor(Environment.SpecialFolder.CommonDocuments)]
  28. public string SetoutsFolder { get; set; }
  29. [ScriptEditor]
  30. public string? Script { get; set; }
  31. public StagingPanellSettings()
  32. {
  33. MarkupPathway = "";
  34. SetoutsFolder = "";
  35. Script = null;
  36. }
  37. public string DefaultScript()
  38. {
  39. return @"
  40. using PRSDesktop;
  41. using InABox.Core;
  42. using System.Collections.Generic;
  43. public class Module
  44. {
  45. /*public void CustomiseSetouts(CustomiseSetoutsArgs args)
  46. {
  47. // Perform customisation on the setouts when they are added to the 'Staged Documents' grid.
  48. }*/
  49. }";
  50. }
  51. }
  52. public class CustomiseSetoutsArgs
  53. {
  54. public IReadOnlyList<Tuple<StagingSetout, Document>> Setouts;
  55. public CustomiseSetoutsArgs(IReadOnlyList<Tuple<StagingSetout, Document>> setouts)
  56. {
  57. Setouts = setouts;
  58. }
  59. }
  60. /// <summary>
  61. /// Interaction logic for StagingPanel.xaml
  62. /// </summary>
  63. public partial class StagingPanel : UserControl, IPanel<StagingSetout>
  64. {
  65. private StagingPanellSettings _settings = new StagingPanellSettings();
  66. /// <summary>
  67. /// The currently selected setout.
  68. /// </summary>
  69. private StagingSetout? selectedSetout;
  70. /// <summary>
  71. /// All currently selected setouts; <see cref="selectedSetout"/> will be a member of this list.
  72. /// </summary>
  73. private List<StagingSetout> selectedSetouts = new();
  74. private MethodInfo? _customiseSetoutsMethod;
  75. private MethodInfo? CustomiseSetoutsMethod
  76. {
  77. get
  78. {
  79. EnsureScript();
  80. return _customiseSetoutsMethod;
  81. }
  82. }
  83. private object? _scriptObject;
  84. private object? ScriptObject
  85. {
  86. get
  87. {
  88. EnsureScript();
  89. return _scriptObject;
  90. }
  91. }
  92. private ScriptDocument? _script;
  93. private ScriptDocument? Script
  94. {
  95. get
  96. {
  97. EnsureScript();
  98. return _script;
  99. }
  100. }
  101. private void EnsureScript()
  102. {
  103. if (_script is null && !_settings.Script.IsNullOrWhiteSpace())
  104. {
  105. _script = new ScriptDocument(_settings.Script);
  106. if (!_script.Compile())
  107. {
  108. throw new Exception("Script in Staging Panel Settings failed to compile!");
  109. }
  110. _scriptObject = _script?.GetObject();
  111. _customiseSetoutsMethod = _script?.GetMethod(methodName: "CustomiseSetouts");
  112. }
  113. }
  114. public StagingPanel()
  115. {
  116. InitializeComponent();
  117. SectionName = nameof(StagingPanel);
  118. }
  119. private void DocumentPreviewer_OnApproved(IEntityDocument? stagingsetoutdocument)
  120. {
  121. bool bulkApprove = false;
  122. if (selectedSetouts.Count > 1)
  123. {
  124. if (MessageBox.Show("Bulk approve? (Skip individual setout approval)", "Continue?", MessageBoxButton.OKCancel) == MessageBoxResult.OK)
  125. {
  126. bulkApprove = true;
  127. Progress.Show("Approving Setouts..");
  128. }
  129. }
  130. string message = "Result: " + Environment.NewLine;
  131. foreach (var item in selectedSetouts)
  132. {
  133. if (bulkApprove)
  134. Progress.Show("Working on " + item.Number);
  135. var returnstring = ApproveSetout(item, bulkApprove);
  136. if (!string.IsNullOrWhiteSpace(returnstring))
  137. message = message + returnstring + Environment.NewLine;
  138. }
  139. if(bulkApprove)
  140. Progress.Close();
  141. new Client<StagingSetout>().Save(selectedSetouts, "Updated from staging screen");
  142. selectedSetout = null;
  143. Refresh();
  144. MessageBox.Show(message);
  145. }
  146. private static string ApproveSetout(StagingSetout item, bool bulkapprove)
  147. {
  148. if (item.Group.ID == Guid.Empty)
  149. {
  150. var message = "Setout has no group assigned";
  151. if (Security.IsAllowed<CanApproveSetoutsWithoutGroup>())
  152. {
  153. if (MessageBox.Show(message + ", continue?", "Continue?", MessageBoxButton.OKCancel) != MessageBoxResult.OK)
  154. return "";
  155. }
  156. else
  157. {
  158. MessageBox.Show(message + ", please assign a group!");
  159. return "";
  160. }
  161. }
  162. var setoutDocument = new Client<StagingSetoutDocument>()
  163. .Query(
  164. new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).IsEqualTo(item.ID),
  165. new Columns<StagingSetoutDocument>(x => x.ID, x => x.DocumentLink.ID, x => x.DocumentLink.FileName))
  166. .ToObjects<StagingSetoutDocument>().FirstOrDefault();
  167. if (setoutDocument is null)
  168. return "";
  169. var setout = new Client<Setout>()
  170. .Query(
  171. new Filter<Setout>(x => x.Number).IsEqualTo(item.Number),
  172. new Columns<Setout>(x => x.ID))
  173. .ToObjects<Setout>().FirstOrDefault();
  174. //setout already exists - create new setoutdoc and supercede old ones
  175. if (setout is not null)
  176. {
  177. if (!bulkapprove)
  178. if (MessageBox.Show("Supercede existing documents?", "Proceed?", MessageBoxButton.YesNo) != MessageBoxResult.Yes)
  179. return "";
  180. setout.Group.ID = item.Group.ID;
  181. item.Setout.ID = setout.ID;
  182. var setoutdoc = new SetoutDocument();
  183. setoutdoc.EntityLink.ID = setout.ID;
  184. setoutdoc.DocumentLink.ID = setoutDocument.DocumentLink.ID;
  185. var setoutdocs = new List<SetoutDocument>
  186. {
  187. setoutdoc
  188. };
  189. CoreTable oldDocsTable = new Client<SetoutDocument>().Query(
  190. new Filter<SetoutDocument>(x => x.EntityLink.ID).IsEqualTo((Guid)setout.ID)
  191. .And(x => x.DocumentLink.ID).IsNotEqualTo(item.Group.OptimizationDocument.ID)
  192. );
  193. foreach (var row in oldDocsTable.Rows)
  194. {
  195. var oldDoc = row.ToObject<SetoutDocument>();
  196. if (oldDoc.Superceded == DateTime.MinValue)
  197. {
  198. oldDoc.Superceded = DateTime.Now;
  199. setoutdocs.Add(oldDoc);
  200. }
  201. }
  202. new Client<SetoutDocument>().Save(setoutdocs, "Updated from Staging Screen");
  203. new Client<Setout>().Save((Setout)setout, "Updated from Staging Screen");
  204. return item.Number + " Superceded";
  205. }
  206. //no setout for this pdf - create new
  207. else
  208. {
  209. setout = new Setout
  210. {
  211. Number = item.Number
  212. };
  213. setout.JobLink.ID = item.JobLink.ID;
  214. setout.Group.ID = item.Group.ID;
  215. var editor = new DynamicDataGrid<Setout>();
  216. editor.OnAfterSave += (editor, items) =>
  217. {
  218. CreateSetoutDocument(setout, item, setoutDocument);
  219. };
  220. if (!bulkapprove)
  221. {
  222. if (!editor.EditItems(new[] { setout }))
  223. {
  224. MessageBox.Show("Setout Creation Cancelled");
  225. return "";
  226. }
  227. else
  228. return item.Number + " Created";
  229. }
  230. else
  231. {
  232. new Client<Setout>().Save(setout, "Added from staging screen");
  233. CreateSetoutDocument(setout, item, setoutDocument);
  234. return item.Number + " Created";
  235. }
  236. }
  237. //currently not creating packets due to temporary change in requirements - to uncomment and check for validity when required
  238. //CreatePackets(setout);
  239. }
  240. private static void CreateSetoutDocument(Setout setout, StagingSetout item, StagingSetoutDocument stagingsetoutdocument)
  241. {
  242. item.Setout.ID = setout.ID;
  243. var setoutdoc = new SetoutDocument();
  244. setoutdoc.EntityLink.ID = setout.ID;
  245. setoutdoc.DocumentLink.ID = stagingsetoutdocument.DocumentLink.ID;
  246. new Client<SetoutDocument>().Save(setoutdoc, "Added from staging screen");
  247. }
  248. private void DocumentPreviewer_OnRejected(IEntityDocument? stagingsetoutdocument)
  249. {
  250. if(selectedSetout is null || stagingsetoutdocument is null)
  251. {
  252. MessageBox.Show("Please select a setout");
  253. return;
  254. }
  255. //dont create setout - setout.id remains blank
  256. //create kanban and populate task.id - this prevents it from appearing on the stagingsetout grid, and allows a new staging setout to be created when the file is saved to the folder again
  257. //attach the document to the task for reference
  258. var task = new Kanban
  259. {
  260. Title = "Setout Review Task (setout rejected)",
  261. Description = "Please review the attached document for setout " + selectedSetout.Number
  262. };
  263. task.ManagerLink.ID = App.EmployeeID;
  264. var page = new KanbanGrid();
  265. page.MyID = App.EmployeeID;
  266. if (page.EditItems(new[] { task }))
  267. {
  268. var doc = new KanbanDocument();
  269. doc.EntityLink.ID = task.ID;
  270. doc.DocumentLink.ID = stagingsetoutdocument.DocumentLink.ID;
  271. new Client<KanbanDocument>().Save(doc, "Created from staging screen");
  272. selectedSetout.Task.ID = task.ID;
  273. new Client<StagingSetout>().Save(selectedSetout, "Updated from staging screen");
  274. MessageBox.Show("Success - Task Created for Document " + selectedSetout.Number + " (Task no. " + task.Number + " assigned to " + task.EmployeeLink.Name + ")");
  275. selectedSetout = null;
  276. documentPreviewer.Document = null;
  277. stagingSetoutGrid.Refresh(false, true);
  278. }
  279. else
  280. {
  281. MessageBox.Show("Task creation cancelled - setout not rejected");
  282. }
  283. }
  284. private void nestedPanel_OnChanged(object sender, DynamicSplitPanelSettings e)
  285. {
  286. if(e.View != DynamicSplitPanelView.Master && ManufacturingPacketList.Setout != selectedSetout)
  287. {
  288. ManufacturingPacketList.Setout = selectedSetout;
  289. }
  290. }
  291. private void StagingSetoutGrid_OnSelectItem(object sender, InABox.DynamicGrid.DynamicGridSelectionEventArgs e)
  292. {
  293. selectedSetouts.Clear();
  294. foreach (var row in e.Rows ?? Enumerable.Empty<CoreRow>())
  295. selectedSetouts.Add(row.ToObject<StagingSetout>());
  296. selectedSetout = selectedSetouts.FirstOrDefault();
  297. AddPacketButton.IsEnabled = selectedSetout is not null;
  298. if(selectedSetout is null)
  299. {
  300. documentPreviewer.Document = null;
  301. ManufacturingPacketList.Setout = null;
  302. CollapsePacketsButton.IsEnabled = false;
  303. return;
  304. }
  305. var doc = new Client<StagingSetoutDocument>()
  306. .Query(
  307. new Filter<StagingSetoutDocument>(x => x.EntityLink.ID).IsEqualTo(selectedSetout.ID),
  308. new Columns<StagingSetoutDocument>(x => x.ID, x => x.DocumentLink.ID, x => x.DocumentLink.FileName))
  309. .ToObjects<StagingSetoutDocument>().FirstOrDefault();
  310. if(doc is null)
  311. {
  312. MessageBox.Show("No document found for this setout.");
  313. documentPreviewer.Document = null;
  314. return;
  315. }
  316. documentPreviewer.Document = doc;
  317. if(mainPanel.View != DynamicSplitPanelView.Master && nestedPanel.View != DynamicSplitPanelView.Master)
  318. {
  319. ManufacturingPacketList.Setout = selectedSetout;
  320. }
  321. CollapsePacketsButton.IsEnabled = true;
  322. //manufacturingControl.StagingSetout = _item;
  323. documentPreviewer.Mode =
  324. selectedSetout.Locked == DateTime.MinValue ? InABox.Wpf.DocumentApprovalControl.ControlMode.Markup :
  325. selectedSetout.Locked != DateTime.MinValue && selectedSetout.LockedBy.ID == App.EmployeeID ? InABox.Wpf.DocumentApprovalControl.ControlMode.Complete :
  326. selectedSetout.Locked != DateTime.MinValue && selectedSetout.LockedBy.ID != App.EmployeeID ? InABox.Wpf.DocumentApprovalControl.ControlMode.Locked :
  327. InABox.Wpf.DocumentApprovalControl.ControlMode.Markup;
  328. }
  329. public bool IsReady { get; set; }
  330. public string SectionName { get; }
  331. public event DataModelUpdateEvent? OnUpdateDataModel;
  332. public void CreateToolbarButtons(IPanelHost host)
  333. {
  334. host.CreateSetupAction(new PanelAction() { Caption = "Setouts Configuration", Image = PRSDesktop.Resources.specifications, OnExecute = ConfigSettingsClick });
  335. }
  336. private void ConfigSettingsClick(PanelAction obj)
  337. {
  338. var pages = new DynamicEditorPages();
  339. var propertyEditor = new DynamicEditorForm(typeof(StagingPanellSettings), pages);
  340. propertyEditor.OnDefineLookups += sender =>
  341. {
  342. var editor = sender.EditorDefinition as ILookupEditor;
  343. var colname = sender.ColumnName;
  344. var values = editor.Values(colname, new[] { _settings });
  345. sender.LoadLookups(values);
  346. };
  347. propertyEditor.OnEditorValueChanged += (sender, name, value) =>
  348. {
  349. CoreUtils.SetPropertyValue(_settings, name, value);
  350. return new Dictionary<string, object?>();
  351. };
  352. propertyEditor.OnFormCustomiseEditor += Settings_OnFormCustomiseEditor;
  353. propertyEditor.Items = new BaseObject[] { _settings };
  354. if (propertyEditor.ShowDialog() == true)
  355. {
  356. new GlobalConfiguration<StagingPanellSettings>().Save(_settings);
  357. _script = null;
  358. }
  359. }
  360. private void Settings_OnFormCustomiseEditor(IDynamicEditorForm sender, object[] items, DynamicGridColumn column, BaseEditor editor)
  361. {
  362. if (items?.FirstOrDefault() is not StagingPanellSettings settings) return;
  363. if (column.ColumnName == nameof(StagingPanellSettings.Script) && editor is ScriptEditor scriptEditor)
  364. {
  365. scriptEditor.Type = ScriptEditorType.TemplateEditor;
  366. scriptEditor.OnEditorClicked += () =>
  367. {
  368. var script = settings.Script.NotWhiteSpaceOr()
  369. ?? settings.DefaultScript();
  370. var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp);
  371. if (editor.ShowDialog() == true)
  372. {
  373. sender.SetEditorValue(column.ColumnName, editor.Script);
  374. settings.Script = editor.Script;
  375. }
  376. };
  377. }
  378. }
  379. public void Heartbeat(TimeSpan time)
  380. {
  381. }
  382. public void Refresh()
  383. {
  384. //stagingSetoutGrid.ScanFiles(_settings.SetoutsFolder);
  385. stagingSetoutGrid.Refresh(false, true);
  386. documentPreviewer.Document = null;
  387. ManufacturingPacketList.Setout = null;
  388. //manufacturingControl.StagingSetout = new StagingSetout();
  389. //manufacturingControl.Refresh();
  390. }
  391. public Dictionary<string, object[]> Selected()
  392. {
  393. return new();
  394. }
  395. public void Setup()
  396. {
  397. _settings = new GlobalConfiguration<StagingPanellSettings>().Load();
  398. documentPreviewer.SetupButtons(
  399. markupVisible: Security.IsAllowed<CanMarkUpSetouts>(),
  400. rejectVisible: Security.IsAllowed<CanApproveSetouts>(),
  401. approveVisible: Security.IsAllowed<CanApproveSetouts>()
  402. );
  403. documentPreviewer.OnMarkupSelected += DocumentPreviewer_OnMarkupSelected;
  404. documentPreviewer.OnMarkupComplete += DocumentPreviewer_OnMarkupComplete;
  405. documentPreviewer.OnRejected += DocumentPreviewer_OnRejected;
  406. documentPreviewer.OnApproved += DocumentPreviewer_OnApproved;
  407. //stagingSetoutGrid.ScanFiles(_settings.SetoutsFolder);
  408. stagingSetoutGrid.Refresh(true, true);
  409. stagingSetoutGrid.OnSelectItem += StagingSetoutGrid_OnSelectItem;
  410. }
  411. private void DocumentPreviewer_OnMarkupSelected(IEntityDocument? document)
  412. {
  413. if(document is null || selectedSetout is null)
  414. {
  415. MessageBox.Show("Please select a setout first.");
  416. return;
  417. }
  418. var doc = new Client<Document>()
  419. .Query(
  420. new Filter<Document>(x => x.ID).IsEqualTo(document.DocumentLink.ID))
  421. .ToObjects<Document>().FirstOrDefault();
  422. if (doc is null)
  423. {
  424. Logger.Send(LogType.Error, "", $"Document with ID {document.DocumentLink.ID} could not be found.");
  425. MessageBox.Show("Error: the selected document could not be found in the database.");
  426. return;
  427. }
  428. var tempdocpath = Path.GetTempPath() + @"\" + doc.FileName;
  429. selectedSetout.SavePath = tempdocpath;
  430. selectedSetout.LockedBy.ID = App.EmployeeID;
  431. new Client<StagingSetout>().Save(selectedSetout, "Locked from Staging Screen");
  432. File.WriteAllBytes(tempdocpath, doc.Data);
  433. using (var p = new Process())
  434. {
  435. p.StartInfo = new ProcessStartInfo()
  436. {
  437. UseShellExecute = true,
  438. FileName = tempdocpath
  439. };
  440. p.Start();
  441. }
  442. stagingSetoutGrid.Refresh(false, true);
  443. }
  444. private void DocumentPreviewer_OnMarkupComplete(IEntityDocument? document)
  445. {
  446. if (selectedSetout is null)
  447. {
  448. MessageBox.Show("Please select a setout first.");
  449. return;
  450. }
  451. StagingSetoutGrid.ReloadFile(selectedSetout);
  452. stagingSetoutGrid.Refresh(false, true);
  453. }
  454. public void Shutdown(CancelEventArgs? cancel)
  455. {
  456. }
  457. public DataModel DataModel(Selection selection)
  458. {
  459. return new AutoDataModel<StagingSetout>(new Filter<StagingSetout>().All());
  460. }
  461. private void AddPacketButton_Click(object sender, RoutedEventArgs e)
  462. {
  463. ManufacturingPacketList.Add();
  464. }
  465. private void CollapsePacketsButton_Click(object sender, RoutedEventArgs e)
  466. {
  467. if (ManufacturingPacketList.Collapsed())
  468. {
  469. ManufacturingPacketList.Uncollapse();
  470. }
  471. else
  472. {
  473. ManufacturingPacketList.Collapse();
  474. }
  475. }
  476. private void ManufacturingPacketList_OnCollapsed(bool collapsed)
  477. {
  478. if (collapsed)
  479. {
  480. CollapsePacketsButton.Content = "Expand";
  481. }
  482. else
  483. {
  484. CollapsePacketsButton.Content = "Collapse";
  485. }
  486. }
  487. private void stagingSetoutGrid_OnCustomiseSetouts(IReadOnlyList<StagingSetoutGrid.SetoutDocument> setouts)
  488. {
  489. if(CustomiseSetoutsMethod != null && ScriptObject != null)
  490. {
  491. CustomiseSetoutsMethod?.Invoke(ScriptObject, new object?[]
  492. {
  493. new CustomiseSetoutsArgs(setouts.Select(x => new Tuple<StagingSetout, Document>(x.Setout, x.Document)).ToImmutableList())
  494. });
  495. }
  496. }
  497. }
  498. }