DataEntryPanel.xaml.cs 12 KB

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