DataEntryPanel.xaml.cs 13 KB

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