DataEntryPanel.xaml.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.DynamicGrid;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.ComponentModel;
  8. using System.Linq;
  9. using System.Windows;
  10. using System.Windows.Controls;
  11. using System.Windows.Media;
  12. using InABox.Configuration;
  13. using PRSDesktop.Panels.DataEntry.Grids;
  14. namespace PRSDesktop;
  15. public class DataEntryPanelSettings : BaseObject, IUserConfigurationSettings
  16. {
  17. private static readonly double DefaultCacheAge = 7;
  18. [NullEditor]
  19. public double PreviewWidth { get; set; }
  20. [DoubleEditor(ToolTip = "Age of cached documents before they are deleted (days)")]
  21. public double CacheAge { get; set; } = DefaultCacheAge;
  22. }
  23. /// <summary>
  24. /// Interaction logic for DataEntryPanel.xaml
  25. /// </summary>
  26. public partial class DataEntryPanel : UserControl, IBasePanel, IDynamicEditorHost
  27. {
  28. private DataEntryPanelSettings _settings;
  29. //IPopupEditorControl? _popup;
  30. private IDynamicDataGrid? _grid;
  31. private Type? _selectedType;
  32. private Guid _entityID;
  33. private Guid _originalID;
  34. private bool _processenabled;
  35. private Entity? _entity;
  36. private Button? _process;
  37. private bool _isChanged;
  38. private bool IsChanged
  39. {
  40. get => _isChanged;
  41. set
  42. {
  43. if(_isChanged != value)
  44. {
  45. _isChanged = value;
  46. Editor.HideButtons = !value;// (_entity?.IsChanged() != true) && (_originalID == _entityID);
  47. if (_process != null)
  48. _process.IsEnabled = value;// ((_entity?.ID ?? Guid.Empty) != Guid.Empty) || (_entity?.IsChanged() != false);
  49. _documents._dataEntryGrid.IsEnabled = !value;// (_entity?.IsChanged() != true) && (_originalID == _entityID);
  50. }
  51. }
  52. }
  53. public void Select(Type? type, Guid id)
  54. {
  55. ClearEditor();
  56. _selectedType = type;
  57. _entityID = id;
  58. if (_selectedType != null)
  59. {
  60. ClearEditor();
  61. CreateEditor();
  62. PopulateEditor();
  63. //LoadPopup();
  64. }
  65. }
  66. private void ScanPanel_OnSelectScan(string appliesto, Guid entityid, bool processenabled)
  67. {
  68. _processenabled = processenabled;
  69. _originalID = entityid;
  70. Select(
  71. CoreUtils.GetEntityOrNull(appliesto),
  72. entityid
  73. );
  74. }
  75. public DataEntryPanel()
  76. {
  77. InitializeComponent();
  78. LoadSettings();
  79. }
  80. private void LoadSettings()
  81. {
  82. _settings = new UserConfiguration<DataEntryPanelSettings>().Load();
  83. _panel.AnchorWidth = _settings.PreviewWidth > 0.0F
  84. ? _settings.PreviewWidth
  85. : 500F;
  86. }
  87. public void Setup()
  88. {
  89. _documents.Setup();
  90. IsChanged = false;
  91. }
  92. public void Refresh()
  93. {
  94. if (CheckSaved())
  95. {
  96. _documents.Refresh();
  97. IsChanged = false;
  98. }
  99. }
  100. public bool IsReady { get; set; }
  101. public string SectionName => "Data Entry";
  102. public DataModel DataModel(Selection selection)
  103. {
  104. return new EmptyDataModel();
  105. }
  106. public event DataModelUpdateEvent? OnUpdateDataModel;
  107. public void CreateToolbarButtons(IPanelHost host)
  108. {
  109. if (Security.IsAllowed<CanSetupDataEntryTags>())
  110. {
  111. host.CreateSetupAction(new PanelAction
  112. {
  113. Caption = "Data Entry Tags",
  114. OnExecute = (action) =>
  115. {
  116. var list = new MasterList(typeof(DataEntryTag));
  117. list.ShowDialog();
  118. }
  119. });
  120. host.CreateSetupAction(new PanelAction
  121. {
  122. Caption = "Settings",
  123. OnExecute = (action) =>
  124. {
  125. var settings = new UserConfiguration<DataEntryPanelSettings>().Load();
  126. var grid = new DynamicItemsListGrid<DataEntryPanelSettings>();
  127. if (grid.EditItems(new DataEntryPanelSettings[] { settings }))
  128. {
  129. new UserConfiguration<DataEntryPanelSettings>().Save(settings);
  130. LoadSettings();
  131. }
  132. }
  133. });
  134. }
  135. }
  136. public void Heartbeat(TimeSpan time)
  137. {
  138. }
  139. public Dictionary<string, object[]> Selected()
  140. {
  141. return new Dictionary<string, object[]>();
  142. }
  143. private void CheckSaved(CancelEventArgs cancel)
  144. {
  145. if (!_isChanged)
  146. {
  147. return;
  148. }
  149. var result = MessageBox.Show("You have changes that have not been saved; do you wish to save these changes?", "Save Changes?", MessageBoxButton.YesNoCancel);
  150. if (result == MessageBoxResult.Yes)
  151. {
  152. DoSave(false);
  153. if (!cancel.Cancel)
  154. MessageBox.Show("Item saved.");
  155. }
  156. else if (result == MessageBoxResult.Cancel)
  157. {
  158. cancel.Cancel = true;
  159. }
  160. }
  161. private bool CheckSaved()
  162. {
  163. var cancel = new CancelEventArgs();
  164. CheckSaved(cancel);
  165. return !cancel.Cancel;
  166. }
  167. public void Shutdown(CancelEventArgs? cancel)
  168. {
  169. if (cancel != null)
  170. CheckSaved(cancel);
  171. if (cancel?.Cancel != true)
  172. _documents.Shutdown(cancel);
  173. }
  174. #region Host
  175. public DynamicGridColumns Columns { get; set; } = new();
  176. IEnumerable<DynamicGridColumn> IDynamicEditorHost.Columns => Columns;
  177. public void LoadColumns(string column, Dictionary<string, string> columns)
  178. {
  179. if (_selectedType is null)
  180. return;
  181. columns.Clear();
  182. foreach (var c in LookupFactory.DefineColumns(_selectedType).ColumnNames().Where(x => x != "ID"))
  183. columns.Add(c, c);
  184. //if (_popup?.EditorDefinition is CodePopupEditorControl codePopup && !columns.ContainsKey(codePopup.CodeColumn))
  185. // columns.Add(codePopup.CodeColumn, codePopup.CodeColumn);
  186. }
  187. public IFilter? DefineFilter(Type type) => LookupFactory.DefineFilter(type);
  188. public void LoadLookups(ILookupEditorControl sender)
  189. {
  190. var colname = sender.ColumnName;
  191. var values = sender.LookupEditorDefinition.Values(colname, Editor.Items);
  192. sender.LoadLookups(values);
  193. }
  194. object?[] IDynamicEditorHost.GetItems() => Editor.Items;
  195. public BaseEditor? GetEditor(DynamicGridColumn column) => column.Editor.CloneEditor();
  196. #endregion
  197. private void ClearEditor()
  198. {
  199. DetailBorder.Child = null;
  200. IsChanged = false;
  201. //if (_popup is UIElement element)
  202. // DetailHeader.Children.Remove(element);
  203. }
  204. private void CreateEditor()
  205. {
  206. if (_selectedType == null)
  207. return;
  208. Editor = new EmbeddedDynamicEditorForm();
  209. Editor.SetLayoutType<VerticalDynamicEditorGridLayout>();
  210. Editor.HighlightButtons = true;
  211. Editor.HideButtons = true;
  212. Editor.SetValue(Grid.RowProperty, 1);
  213. Editor.SetValue(Grid.ColumnProperty, 0);
  214. Editor.SetValue(Grid.ColumnSpanProperty, 4);
  215. Editor.OnAfterEditorValueChanged += (sender, args) =>
  216. {
  217. IsChanged = IsChanged || (_entity?.IsChanged() == true || _originalID != _entityID);
  218. return null;
  219. };
  220. Editor.OnOK += () => { DoSave(false); };
  221. Editor.OnCancel += () =>
  222. {
  223. _entityID = _originalID;
  224. Select(_selectedType,_entityID);
  225. };
  226. Editor.OnChanged += (sender, args) => IsChanged = true;
  227. Editor.OnFormCustomiseEditor += (sender, items, column, editor) =>
  228. {
  229. if ((editor is BaseCodeEditor be) && editor.Editable.EditorVisible())
  230. {
  231. be.Buttons = new[]
  232. {
  233. new EditorButton(null, "..", 30, DoLookup, false)
  234. };
  235. }
  236. };
  237. DetailBorder.Child = Editor;
  238. _grid = DynamicGridUtils.CreateDynamicGrid(typeof(DynamicDataGrid<>), _selectedType) as IDynamicDataGrid;
  239. if (_grid is DynamicDataGrid<JobDocumentSetMileStone> milestoneGrid)
  240. {
  241. milestoneGrid.OnValidate += (o, items, err) =>
  242. {
  243. if (items.Any(x => x.ID == Guid.Empty))
  244. {
  245. err.Add("Cannot create a new milestone from Data Entry screen. Please select a milestone to edit.");
  246. }
  247. };
  248. }
  249. }
  250. private void DoLookup(object editor, object? item)
  251. {
  252. if (_selectedType == null)
  253. return;
  254. if (editor is CodeEditorControl ce)
  255. {
  256. Dictionary<string, string>? filter = null;
  257. if (ce.Value != null)
  258. {
  259. filter = new Dictionary<string, string>
  260. {
  261. [ce.ColumnName] = ce.Value
  262. };
  263. }
  264. Type? gridType = null;
  265. if (_selectedType == typeof(JobDocumentSetMileStone))
  266. {
  267. gridType = typeof(JobDocumentSetMileStoneDataEntryPopupGrid);
  268. }
  269. var popup = new PopupList(_selectedType, _entityID, Array.Empty<string>(), filter, gridType: gridType);
  270. popup.OnDefineFilter += LookupFactory.DefineFilter;
  271. if (popup.ShowDialog() == true)
  272. {
  273. _entityID = popup.ID;
  274. Select(_selectedType, _entityID);
  275. }
  276. }
  277. }
  278. private void SaveDocument(DataEntryDocument dataEntryDocument)
  279. {
  280. var linktype = CoreUtils.TypeList(x =>
  281. x.GetInterfaces().Contains(typeof(IEntityLink))
  282. && x.BaseType != null
  283. && x.BaseType.IsGenericType
  284. && x.BaseType.GenericTypeArguments.FirstOrDefault() == _selectedType
  285. ).FirstOrDefault();
  286. if (linktype == null)
  287. return;
  288. var doctype = CoreUtils.TypeList(x =>
  289. x.GetInterfaces().Contains(typeof(IEntityDocument))
  290. && x.BaseType != null
  291. && x.BaseType.IsGenericType
  292. && x.BaseType.GenericTypeArguments.FirstOrDefault() == linktype
  293. ).FirstOrDefault();
  294. if (doctype == null)
  295. return;
  296. var doc = (IEntityDocument)Activator.CreateInstance(doctype)!;
  297. CoreUtils.SetPropertyValue(doc,"EntityLink.ID",_entityID);
  298. doc.DocumentLink.ID = dataEntryDocument.Document.ID;
  299. doc.Thumbnail = dataEntryDocument.Thumbnail;
  300. ClientFactory.CreateClient(doctype).Save(doc,"Added from Data Entry Screen");
  301. }
  302. private void DoSave(bool markasprocessed)
  303. {
  304. var cancel = new System.ComponentModel.CancelEventArgs();
  305. if (markasprocessed && (_entity is IDataEntryInstance scannable))
  306. scannable.DataEntered = DateTime.Now;
  307. Editor.SaveItem(cancel);
  308. if (!cancel.Cancel)
  309. {
  310. _originalID = _entityID;
  311. IsChanged = false;
  312. var row = _documents._dataEntryGrid.SelectedRows.FirstOrDefault();
  313. if (row != null)
  314. {
  315. var scan = row.ToObject<DataEntryDocument>();
  316. scan.EntityID = _entity.ID;
  317. if (markasprocessed)
  318. {
  319. SaveDocument(scan);
  320. scan.Archived = DateTime.Now;
  321. }
  322. if (scan.IsChanged())
  323. {
  324. new Client<DataEntryDocument>().Save(scan, "Updated from Data Entry Screen");
  325. _documents.Refresh();
  326. }
  327. }
  328. }
  329. }
  330. private void PopulateEditor()
  331. {
  332. if (_selectedType == null)
  333. return;
  334. _entity = null;
  335. if (_entityID != Guid.Empty)
  336. {
  337. _entity = Client.Create(_selectedType)
  338. .Query(
  339. Filter.Create<Entity>(_selectedType, x => x.ID).IsEqualTo(_entityID),
  340. _grid.LoadEditorColumns()
  341. )
  342. .ToObjects(_selectedType)
  343. .OfType<Entity>()
  344. .FirstOrDefault();
  345. }
  346. _entity ??= Activator.CreateInstance(_selectedType) as Entity;
  347. if (_entity == null)
  348. return;
  349. _grid?.InitialiseEditorForm(Editor, new object[] { _entity }, null, true);
  350. _process = new Button()
  351. {
  352. Content = "Mark as Processed",
  353. BorderBrush = new SolidColorBrush(Colors.DarkBlue),
  354. Background = new SolidColorBrush(Colors.DodgerBlue),
  355. Margin = new Thickness(5, 5, 0, 5),
  356. Padding = new Thickness(10, 0, 10, 0),
  357. IsEnabled = _processenabled
  358. };
  359. _process.Click += (sender, args) => DoSave(true);
  360. Editor.AddButton(_process);
  361. IsChanged = false;
  362. if (_selectedType == typeof(JobDocumentSetMileStone))
  363. {
  364. var disabled = _entityID == Guid.Empty;
  365. foreach (var page in Editor.Pages)
  366. {
  367. if (page is DynamicEditorGrid.DynamicEditPage editPage)
  368. {
  369. foreach (var editor in editPage.Editors)
  370. {
  371. if (editor.EditorDefinition is not BaseCodeEditor)
  372. {
  373. editor.IsEnabled = !disabled && editor.EditorDefinition.Editable.IsEditable();
  374. }
  375. }
  376. }
  377. else
  378. {
  379. page.ReadOnly = disabled;
  380. }
  381. }
  382. }
  383. }
  384. private void _panel_OnOnChanged(object sender, DynamicSplitPanelSettings e)
  385. {
  386. _settings.PreviewWidth = e.AnchorWidth;
  387. new UserConfiguration<DataEntryPanelSettings>().Save(_settings);
  388. }
  389. }