DataEntryPanel.xaml.cs 13 KB

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