DigitalFormDockGrid.cs 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  1. using Comal.Classes;
  2. using InABox.Clients;
  3. using InABox.Core;
  4. using InABox.DynamicGrid;
  5. using InABox.Scripting;
  6. using InABox.Wpf.Reports;
  7. using InABox.WPF;
  8. using PRSDesktop.Configuration;
  9. using System;
  10. using System.Collections.Generic;
  11. using System.Linq;
  12. using System.Reflection;
  13. using System.Threading;
  14. using System.Threading.Tasks;
  15. using System.Windows;
  16. using System.Windows.Controls;
  17. using System.Windows.Media;
  18. using System.Windows.Media.Imaging;
  19. namespace PRSDesktop;
  20. public class DigitalFormDockGrid : DynamicGrid<DigitalFormDockModel>
  21. {
  22. private static readonly CoreFieldMap<IDigitalFormInstance, DigitalFormDockModel> _mappings =
  23. new CoreFieldMap<IDigitalFormInstance, DigitalFormDockModel>()
  24. .Add(x => x.ID, x => x.ID)
  25. .Add(x => x.Form.ID, x => x.FormID)
  26. .Add(x => x.Number, x => x.Number)
  27. .Add(x => x.Description, x => x.FormName)
  28. .Add(x => x.FormCompleted, x => x.Completed)
  29. .Add(x => x.FormCompletedBy.UserID, x => x.CompletedBy)
  30. .Add(x => x.FormProcessed, x => x.Processed)
  31. .Add(x => x.Parent.ID, x => x.ParentID);
  32. public static readonly Dictionary<Type, System.Drawing.Bitmap> Images = new()
  33. {
  34. { typeof(AssignmentForm), PRSDesktop.Resources.assignments },
  35. { typeof(KanbanForm), PRSDesktop.Resources.kanban },
  36. { typeof(JobForm), PRSDesktop.Resources.project },
  37. { typeof(JobITPForm), PRSDesktop.Resources.checklist },
  38. { typeof(EmployeeForm), PRSDesktop.Resources.employees },
  39. { typeof(LeaveRequestForm), PRSDesktop.Resources.leave },
  40. { typeof(ManufacturingPacketStage), PRSDesktop.Resources.factory },
  41. { typeof(TimeSheetForm), PRSDesktop.Resources.time },
  42. { typeof(PurchaseOrderItemForm), PRSDesktop.Resources.purchase },
  43. { typeof(DeliveryForm), PRSDesktop.Resources.truck },
  44. };
  45. public List<Type> ExcludedTypes { get; private set; }
  46. public DateTime StartDate { get; set; }
  47. public DigitalFormDockGrid()
  48. {
  49. StartDate = DateTime.Today;
  50. ExcludedTypes = new List<Type>();
  51. ActionColumns.Add(new DynamicImageColumn(TypeImage)
  52. {
  53. Position = DynamicActionColumnPosition.Start,
  54. Filters = Images.Keys.Select(x => x.EntityName().Split('.').Last().SplitCamelCase()).ToArray(),
  55. FilterRecord = TypeFilter
  56. });
  57. ActionColumns.Add(new DynamicMenuColumn(MenuBuild, MenuStatus) { Position = DynamicActionColumnPosition.End });
  58. }
  59. protected override void Init()
  60. {
  61. }
  62. protected override void DoReconfigure(DynamicGridOptions options)
  63. {
  64. options.Clear();
  65. options.FilterRows = true;
  66. }
  67. protected override DynamicGridStyle GetRowStyle(CoreRow row, DynamicGridStyle style)
  68. {
  69. var result = base.GetRowStyle(row, style);
  70. if (!row.Get<DigitalFormDockModel, DateTime>(x => x.Processed).IsEmpty())
  71. result = new DynamicGridRowStyle(result)
  72. {
  73. Background = new SolidColorBrush(Colors.LightGray),
  74. };
  75. return result;
  76. }
  77. private bool TypeFilter(CoreRow row, string[] filter)
  78. {
  79. string typename = row.Get<DigitalFormDockModel, Type>(x => x.FormType).EntityName().Split('.').Last().SplitCamelCase();
  80. return filter.Contains(typename);
  81. }
  82. private BitmapImage? TypeImage(CoreRow? arg)
  83. {
  84. if (arg is null)
  85. return null;
  86. var type = arg.Get<DigitalFormDockModel, Type>(x => x.FormType);
  87. return Images.GetValueOrDefault(type)?.AsBitmapImage();
  88. }
  89. private static readonly MethodInfo MenuBuildGenericMethod = typeof(DigitalFormDockGrid)
  90. .GetMethod(nameof(MenuBuildGeneric), BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)!;
  91. private void MenuBuildGeneric<TEntityForm, TEntity, TEntityLink>(ContextMenu menu, DigitalFormDockModel formModel)
  92. where TEntityForm : Entity, IDigitalFormInstance<TEntityLink>, IRemotable, IPersistent, new()
  93. where TEntity : Entity, IRemotable, IPersistent, new()
  94. where TEntityLink : IEntityLink<TEntity>, new()
  95. {
  96. var filter = new Filter<TEntityForm>(x => x.ID).IsEqualTo(formModel.ID);
  97. var model = new DigitalFormReportDataModel<TEntityForm>(filter, formModel.FormID);
  98. if (Security.CanView<TEntity>())
  99. {
  100. menu.AddItem($"View {typeof(TEntity).Name}", Images.GetValueOrDefault(typeof(TEntityForm)), formModel, ViewEntity_Click<TEntity>);
  101. }
  102. var moduleTask = Task.Run(() =>
  103. {
  104. return new Client<CustomModule>().Query(
  105. new Filter<CustomModule>(x => x.DataModel).IsEqualTo(model.Name)
  106. .And(x => x.Section).IsEqualTo(formModel.FormID.ToString())
  107. .And(x => x.Visible).IsEqualTo(true)
  108. ).ToObjects<CustomModule>();
  109. });
  110. var modulesSeparator = menu.AddSeparatorIfNeeded();
  111. var modulesItem = menu.AddItem("Loading...", null, null, enabled: false);
  112. moduleTask.ContinueWith((task) =>
  113. {
  114. try
  115. {
  116. var index = menu.Items.IndexOf(modulesItem);
  117. menu.Items.Remove(modulesItem);
  118. var any = false;
  119. foreach (var module in task.Result)
  120. {
  121. any = true;
  122. menu.AddItem(
  123. module.Name,
  124. PRSDesktop.Resources.edit,
  125. () =>
  126. {
  127. try
  128. {
  129. if (ScriptDocument.RunCustomModule(model, new Dictionary<string, object[]>(), module.Script))
  130. {
  131. Refresh(false, true);
  132. }
  133. }
  134. catch (CompileException c)
  135. {
  136. MessageBox.Show(c.Message);
  137. }
  138. catch (Exception e)
  139. {
  140. MessageBox.Show(CoreUtils.FormatException(e));
  141. }
  142. },
  143. enabled: formModel.Processed.IsEmpty(),
  144. index: index);
  145. ++index;
  146. }
  147. if (!any && modulesSeparator is not null)
  148. {
  149. menu.Items.Remove(modulesSeparator);
  150. }
  151. }
  152. catch (Exception ex)
  153. {
  154. Logger.Send(LogType.Error, ClientFactory.UserID, $"Error in digital form dock while loading custom modules: {CoreUtils.FormatException(ex)}");
  155. MessageBox.Show($"Error: {ex.Message}", "Error");
  156. }
  157. }, TaskScheduler.FromCurrentSynchronizationContext());
  158. menu.AddSeparator();
  159. if (formModel.Processed.IsEmpty())
  160. {
  161. menu.AddItem(
  162. "Mark as Processed",
  163. PRSDesktop.Resources.lock_sml,
  164. () =>
  165. {
  166. var form = new TEntityForm
  167. {
  168. ID = formModel.ID,
  169. FormProcessed = formModel.Processed
  170. };
  171. form.CommitChanges();
  172. form.FormProcessed = DateTime.Now;
  173. using (new WaitCursor())
  174. {
  175. Client.Save(form, "Marked As Processed");
  176. Refresh(false, true);
  177. }
  178. });
  179. }
  180. else
  181. {
  182. menu.AddItem(
  183. "Clear Processed Flag",
  184. PRSDesktop.Resources.lock_sml,
  185. () =>
  186. {
  187. var form = new TEntityForm
  188. {
  189. ID = formModel.ID,
  190. FormProcessed = formModel.Processed
  191. };
  192. form.CommitChanges();
  193. form.FormProcessed = DateTime.MinValue;
  194. using (new WaitCursor())
  195. {
  196. Client.Save(form, "Processed Flag Cleared");
  197. Refresh(false, true);
  198. }
  199. });
  200. }
  201. if (typeof(TEntity).HasInterface<IJobScopedItem>())
  202. {
  203. menu.AddSeparatorIfNeeded();
  204. menu.AddItem("Set Job", PRSDesktop.Resources.project, formModel,
  205. SetJob_ClickMethod.MakeGenericMethod(typeof(TEntity)).CreateDelegate<Action<DigitalFormDockModel>>());
  206. menu.AddItem("Set Job Scope", PRSDesktop.Resources.project, formModel,
  207. SetJobScope_ClickMethod.MakeGenericMethod(typeof(TEntity)).CreateDelegate<Action<DigitalFormDockModel>>());
  208. }
  209. else if (typeof(TEntity) == typeof(Job) && typeof(TEntityForm) == typeof(JobForm))
  210. {
  211. menu.AddSeparatorIfNeeded();
  212. var scopeheader = menu.AddItem("Set Job Scope", PRSDesktop.Resources.project, formModel, dockModel => { });
  213. var scopes = Client.Query<JobScope>(
  214. new Filter<JobScope>(x => x.Job.ID).IsEqualTo(formModel.ParentID),
  215. Columns.None<JobScope>()
  216. .Add(x => x.ID)
  217. .Add(x => x.Number)
  218. .Add(x => x.Description)
  219. ).ToObjects<JobScope>();
  220. foreach (var scope in scopes)
  221. {
  222. var existingscope = new MenuItem() { Header = $"{scope.Number}: {scope.Description}", DataContext = formModel, Tag = scope };
  223. existingscope.Click += SetJobScopeFromMenu_Click;
  224. scopeheader.Items.Add(existingscope);
  225. }
  226. if (scopes.Any())
  227. scopeheader.Items.Add(new Separator());
  228. var newscope = new MenuItem() { Header = $"Create New Scope", DataContext = formModel };
  229. newscope.Click += CreateJobScopeFromMenu_Click;
  230. scopeheader.Items.Add(newscope);
  231. }
  232. if (Security.IsAllowed<CanCustomiseModules>())
  233. {
  234. menu.AddSeparatorIfNeeded();
  235. menu.AddItem("Customise Modules", PRSDesktop.Resources.script, () =>
  236. {
  237. var manager = new CustomModuleManager
  238. {
  239. Section = formModel.FormID.ToString(),
  240. DataModel = model
  241. };
  242. manager.ShowDialog();
  243. });
  244. }
  245. if (Security.IsAllowed<CanPrintReports>())
  246. {
  247. menu.AddSeparatorIfNeeded();
  248. var printItem = menu.AddItem("Print", PRSDesktop.Resources.printer, null);
  249. ReportUtils.PopulateMenu(printItem, formModel.FormID.ToString(), model, Security.IsAllowed<CanDesignReports>(), true);
  250. }
  251. }
  252. private static void UpdateScopeID(DigitalFormDockModel formModel, Guid scopeid)
  253. {
  254. var instance = Client.Query(
  255. new Filter<JobForm>(x => x.ID).IsEqualTo(formModel.ID),
  256. Columns.Required<JobForm>()
  257. ).ToObjects<JobForm>().First();
  258. instance.JobScope.ID = scopeid;
  259. Client.Save(instance, "Linked scope set by user from Digital forms dock.");
  260. }
  261. private void CreateJobScopeFromMenu_Click(object sender, RoutedEventArgs e)
  262. {
  263. if ((sender as MenuItem)?.DataContext is DigitalFormDockModel model)
  264. {
  265. var scope = new JobScope();
  266. scope.Job.ID = model.ParentID;
  267. scope.Description = model.FormName;
  268. if (new DynamicDataGrid<JobScope>().EditItems(new[] { scope }))
  269. {
  270. UpdateScopeID(model, scope.ID);
  271. MessageBox.Show($"Form has been assigned to scope {scope.Number}.");
  272. // Update the form scope here (from the SetScope) stuff
  273. }
  274. }
  275. }
  276. private void SetJobScopeFromMenu_Click(object sender, RoutedEventArgs e)
  277. {
  278. if (sender is MenuItem mi && mi.DataContext is DigitalFormDockModel model && mi.Tag is JobScope scope)
  279. {
  280. UpdateScopeID(model, scope.ID);
  281. MessageBox.Show($"Form has been assigned to scope {scope.Number}.");
  282. // Update the form scope here (from the SetScope) stuff
  283. }
  284. }
  285. private static void ViewEntity_Click<TEntity>(DigitalFormDockModel model)
  286. where TEntity : Entity, IRemotable, IPersistent, new()
  287. {
  288. var entity = Client.Query(
  289. new Filter<TEntity>(x => x.ID).IsEqualTo(model.ParentID),
  290. DynamicGridUtils.LoadEditorColumns<TEntity>(Columns.None<TEntity>()))
  291. .ToObjects<TEntity>().First();
  292. var grid = (DynamicGridUtils.CreateDynamicGrid(typeof(DynamicGrid<>), typeof(TEntity)) as DynamicGrid<TEntity>)!;
  293. grid.EditItems(new TEntity[] { entity });
  294. }
  295. private static void SetJobScopeFromJob_Click(DigitalFormDockModel formModel)
  296. {
  297. var instance = Client.Query(
  298. new Filter<JobForm>(x => x.ID).IsEqualTo(formModel.ID),
  299. LookupFactory.DefineLookupFilterColumns<JobForm, JobScope, JobScopeLink>(x => x.JobScope)
  300. .Add(x => x.ID)
  301. .Add(x => x.Parent.ID))
  302. .ToObjects<JobForm>().First();
  303. var job = Client.Query(
  304. new Filter<Job>(x => x.ID).IsEqualTo(instance.Parent.ID),
  305. LookupFactory.DefineChildFilterColumns<Job, JobScope>()).ToObjects<Job>().First();
  306. var window = new MultiSelectDialog<JobScope>(
  307. new Filters<JobScope>()
  308. .Add(LookupFactory.DefineChildFilter<Job, JobScope>(new Job[] { job }))
  309. .Add(new Filter<JobScope>(x => x.Job.ID).IsEqualTo(instance.Parent.ID))
  310. .Combine(),
  311. Columns.None<JobScope>().Add(x => x.ID).Add(x => x.Number), multiselect: false);
  312. if (!window.ShowDialog())
  313. {
  314. return;
  315. }
  316. var scope = window.Data().ToObjects<JobScope>().First();
  317. instance.JobScope.ID = scope.ID;
  318. Client.Save(instance, "Linked scope set by user from Digital forms dock.");
  319. MessageBox.Show($"Form has been assigned to scope {scope.Number}.");
  320. }
  321. private static readonly MethodInfo SetJobScope_ClickMethod = typeof(DigitalFormDockGrid).GetMethod(nameof(SetJobScope_Click), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!;
  322. private static void SetJobScope_Click<TEntity>(DigitalFormDockModel formModel)
  323. where TEntity : Entity, IJobScopedItem, IRemotable, IPersistent, new()
  324. {
  325. var entity = Client.Query(
  326. new Filter<TEntity>(x => x.ID).IsEqualTo(formModel.ParentID),
  327. LookupFactory.DefineLookupFilterColumns<TEntity, JobScope, JobScopeLink>(x => x.JobScope)
  328. .Add(x => x.ID)
  329. .Add(x => x.JobLink.ID)
  330. .Add(x => x.JobScope.ID)
  331. .Add(x => x.JobScope.Number))
  332. .ToObjects<TEntity>().First();
  333. if (entity.JobLink.ID == Guid.Empty)
  334. {
  335. MessageBox.Show($"{typeof(TEntity).Name} is not linked to a job. Please select a job first.");
  336. return;
  337. }
  338. var window = new MultiSelectDialog<JobScope>(
  339. new Filters<JobScope>()
  340. .Add(LookupFactory.DefineLookupFilter<TEntity, JobScope, JobScopeLink>(x => x.JobScope, new TEntity[] { entity }))
  341. .Add(new Filter<JobScope>(x => x.Job.ID).IsEqualTo(entity.JobLink.ID))
  342. .Combine(),
  343. Columns.None<JobScope>().Add(x => x.ID).Add(x => x.Number),
  344. multiselect: false);
  345. if (!window.ShowDialog(nameof(JobScope.Number), entity.JobScope.Number, Syncfusion.Data.FilterType.Equals))
  346. {
  347. return;
  348. }
  349. var scope = window.Data().ToObjects<JobScope>().First();
  350. entity.JobScope.ID = scope.ID;
  351. Client.Save(entity, "Linked scope set by user from Digital forms dock.");
  352. MessageBox.Show($"{typeof(TEntity).Name} has been assigned to scope {scope.Number}.");
  353. }
  354. private static readonly MethodInfo SetJob_ClickMethod = typeof(DigitalFormDockGrid).GetMethod(nameof(SetJob_Click), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)!;
  355. private static void SetJob_Click<TEntity>(DigitalFormDockModel formModel)
  356. where TEntity: Entity, IJobScopedItem, IRemotable, IPersistent, new()
  357. {
  358. var entity = Client.Query(
  359. new Filter<TEntity>(x => x.ID).IsEqualTo(formModel.ParentID),
  360. LookupFactory.DefineLookupFilterColumns<TEntity, Job, JobLink>(x => x.JobLink)
  361. .Add(x => x.ID)
  362. .Add(x => x.JobLink.ID)
  363. .Add(x => x.JobLink.JobNumber))
  364. .ToObjects<TEntity>().First();
  365. var window = new MultiSelectDialog<Job>(
  366. LookupFactory.DefineLookupFilter<TEntity, Job, JobLink>(x => x.JobLink, new TEntity[] { entity }),
  367. Columns.None<Job>().Add(x => x.DefaultScope.ID).Add(x => x.JobNumber),
  368. multiselect: false);
  369. if (!window.ShowDialog(nameof(Job.JobNumber), entity.JobLink.JobNumber, Syncfusion.Data.FilterType.Equals))
  370. {
  371. return;
  372. }
  373. var job = window.Data().ToObjects<Job>().First();
  374. entity.JobLink.ID = job.ID;
  375. entity.JobLink.Synchronise(job);
  376. Client.Save(entity, "Linked job set by user from Digital forms dock.");
  377. MessageBox.Show($"{typeof(TEntity).Name} has been assigned to job {job.JobNumber}.");
  378. }
  379. private void MenuBuild(DynamicMenuColumn column, CoreRow? row)
  380. {
  381. if (row is null) return;
  382. var form = row.ToObject<DigitalFormDockModel>();
  383. var linkType = DFUtils.FormEntityLinkType(form.FormType);
  384. var entityType = DFUtils.FormEntityType(form.FormType);
  385. MenuBuildGenericMethod.MakeGenericMethod(form.FormType, entityType, linkType).Invoke(this, new object?[] { column.GetMenu(), form });
  386. }
  387. private DynamicMenuStatus MenuStatus(CoreRow row)
  388. {
  389. if (row == null) return DynamicMenuStatus.Hidden;
  390. return DynamicMenuStatus.Enabled;
  391. }
  392. protected override void Reload(
  393. Filters<DigitalFormDockModel> criteria, Columns<DigitalFormDockModel> columns, ref SortOrder<DigitalFormDockModel>? sort,
  394. CancellationToken token, Action<CoreTable?, Exception?> action)
  395. {
  396. var queryDefs = new Dictionary<string, IQueryDef>();
  397. var types = DFUtils.GetFormInstanceTypes().Except(ExcludedTypes).ToList();
  398. foreach (var type in types)
  399. {
  400. var filter = Filter.Create<IDigitalFormInstance>(type, x => x.FormCompleted).IsGreaterThanOrEqualTo(StartDate)
  401. .And<IDigitalFormInstance>(x => x.FormCancelled).IsEqualTo(DateTime.MinValue);
  402. var cols = Columns.Create<IDigitalFormInstance>(type, ColumnTypeFlags.None)
  403. .Add<IDigitalFormInstance>(c => c.ID)
  404. .Add<IDigitalFormInstance>(c => c.Parent.ID)
  405. .Add<IDigitalFormInstance>(c => c.Form.ID)
  406. .Add<IDigitalFormInstance>(c => c.Number)
  407. .Add<IDigitalFormInstance>(c => c.Description)
  408. .Add<IDigitalFormInstance>(c => c.FormCompleted)
  409. .Add<IDigitalFormInstance>(c => c.FormCompletedBy.UserID)
  410. .Add<IDigitalFormInstance>(c => c.FormProcessed);
  411. var sorts = SortOrder.Create<IDigitalFormInstance>(type, x => x.FormCompleted, SortDirection.Descending);
  412. queryDefs.Add(
  413. type.ToString(),
  414. new QueryDef(type)
  415. {
  416. Filter = filter,
  417. Columns = cols,
  418. SortOrder = sorts
  419. });
  420. }
  421. Client.QueryMultiple(
  422. (results, e) =>
  423. {
  424. if(results is not null)
  425. {
  426. var data = new CoreTable();
  427. data.LoadColumns(typeof(DigitalFormDockModel));
  428. foreach (var type in types)
  429. data.LoadFrom(
  430. results[type.ToString()],
  431. _mappings,
  432. (r) => r.Set<DigitalFormDockModel, Type>(x => x.FormType, type)
  433. );
  434. action.Invoke(data, null);
  435. }
  436. else if(e is not null)
  437. {
  438. Logger.Send(LogType.Error, ClientFactory.UserID, CoreUtils.FormatException(e));
  439. MessageBox.Show($"Error: {e.Message}");
  440. }
  441. },
  442. queryDefs);
  443. }
  444. public override DigitalFormDockModel LoadItem(CoreRow row)
  445. {
  446. return row.ToObject<DigitalFormDockModel>();
  447. }
  448. public override void SaveItem(DigitalFormDockModel item)
  449. {
  450. }
  451. public override void DeleteItems(params CoreRow[] rows)
  452. {
  453. }
  454. protected override void DoDoubleClick(object sender, DynamicGridCellClickEventArgs args)
  455. {
  456. base.DoDoubleClick(sender, args);
  457. var row = SelectedRows.FirstOrDefault();
  458. if (row is null)
  459. return;
  460. var instanceID = row.Get<DigitalFormDockModel, Guid>(x => x.ID);
  461. var formID = row.Get<DigitalFormDockModel, Guid>(x => x.FormID);
  462. var formType = row.Get<DigitalFormDockModel, Type>(x => x.FormType);
  463. var formInstance = Client.Create(formType)
  464. .Query(
  465. Filter.Create<IDigitalFormInstance>(formType, x => x.ID).IsEqualTo(instanceID),
  466. DynamicFormEditWindow.FormColumns(formType))
  467. .Rows.FirstOrDefault()
  468. ?.ToObject(formType) as IDigitalFormInstance;
  469. if (formInstance is null)
  470. {
  471. return;
  472. }
  473. if (formID == Guid.Empty)
  474. {
  475. var window = new DeletedFormWindow();
  476. window.FormData = formInstance?.FormData ?? "";
  477. window.ShowDialog();
  478. return;
  479. }
  480. if (DynamicFormEditWindow.EditDigitalForm(formInstance, out var model))
  481. {
  482. model.Update(null);
  483. }
  484. }
  485. }