DatabaseUpdateScripts.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574
  1. using Comal.Classes;
  2. using InABox.Core;
  3. using InABox.Database;
  4. using System;
  5. using System.Collections.Generic;
  6. using System.Linq;
  7. using System.Linq.Expressions;
  8. using System.Text;
  9. using System.Threading.Tasks;
  10. using Syncfusion.Windows.Tools.Controls;
  11. using System.Diagnostics.CodeAnalysis;
  12. using System.Reflection;
  13. using FastReport.Utils;
  14. namespace PRS.Shared
  15. {
  16. public static class DatabaseUpdateScripts
  17. {
  18. public static void RegisterScripts()
  19. {
  20. DataUpdater.RegisterUpdateScript("6.31", Update_6_31);
  21. DataUpdater.RegisterUpdateScript("6.37", Update_6_37);
  22. DataUpdater.RegisterUpdateScript("6.38", Update_6_38);
  23. DataUpdater.RegisterUpdateScript("6.39", Update_6_39);
  24. DataUpdater.RegisterUpdateScript("6.43", Update_6_43);
  25. DataUpdater.RegisterUpdateScript("7.00", Update_7_00);
  26. DataUpdater.RegisterUpdateScript("7.06", Update_7_06);
  27. DataUpdater.RegisterUpdateScript("7.14", Update_7_14);
  28. }
  29. private static Dictionary<string, Tuple<string, string>> _6_31_module_map = new()
  30. {
  31. { "Assignments", new("Assignments", "Assignments") },
  32. { "Daily Report", new("Daily Report", "Assignments") },
  33. { "Delivered On Site", new("Delivered On Site", "Delivery Items") },
  34. { "Deliveries", new("Deliveries", "Deliveries") },
  35. { "Digital Forms", new("Digital Forms", "DigitalForm") },
  36. { "Employee List", new("Employees", "Employee") },
  37. { "Equipment List", new("Equipment", "Equipment") },
  38. { "Factory Floor", new("Factory", "Manufacturing Packets") },
  39. { "Incoming Consignments", new("Consignments", "Consignment") },
  40. { "Manufacturing Status", new("Manufacturing Packets", "Manufacturing Packets") },
  41. { "Product List", new("Products", "Products") },
  42. { "Projects", new("Job Details", "Job Details") },
  43. { "Purchase Orders", new("Purchase Orders", "PurchaseOrder") },
  44. { "Quotes", new("Quotes", "Quotes") },
  45. { "Rack List", new("Shipping", "Shipments") },
  46. { "Site Requisitions", new("Requisitions", "Requisition") },
  47. { "Staff TimeSheets", new("Timesheets", "TimeSheet") },
  48. { "Stock Locations", new("Stock Locations", "StockLocation") },
  49. { "Stock Movements", new("Stock Movements", "StockMovement") },
  50. { "Task List", new("Tasks By Status", "Kanban") },
  51. };
  52. private static bool Update_6_31()
  53. {
  54. var modules = DbFactory.Provider.Query(new Filter<CustomModule>().All())
  55. .Rows.Select(x => x.ToObject<CustomModule>()).ToList();
  56. foreach(var module in modules)
  57. {
  58. if (!string.IsNullOrWhiteSpace(module.Section))
  59. {
  60. if (_6_31_module_map.TryGetValue(module.Section, out var map))
  61. {
  62. module.Section = map.Item1;
  63. module.DataModel = map.Item2;
  64. module.AllRecords = true;
  65. }
  66. else
  67. {
  68. Logger.Send(LogType.Error, "", $"Custom Module '{module.Name}' has section name '{module.Section}' and will no longer be visible!");
  69. }
  70. }
  71. }
  72. DbFactory.Provider.Save(modules);
  73. return true;
  74. }
  75. private static bool Update_6_37()
  76. {
  77. Logger.Send(LogType.Information, "", "Recreating views");
  78. DbFactory.Provider.ForceRecreateViews();
  79. return true;
  80. }
  81. private static bool Update_6_38()
  82. {
  83. Logger.Send(LogType.Information, "", "Converting Job Requisition Dates to Due Dates");
  84. List<JobRequisition> updates = new List<JobRequisition>();
  85. var columns = new Columns<JobRequisition>(x => x.ID);
  86. columns.Add("Date");
  87. CoreTable requis = DbFactory.Provider.Query<JobRequisition>(null, columns);
  88. foreach (var row in requis.Rows)
  89. {
  90. var requi = row.ToObject<JobRequisition>();
  91. requi.Approved = row.Get<DateTime>("Date");
  92. updates.Add(requi);
  93. }
  94. DbFactory.Provider.Save(updates);
  95. return true;
  96. }
  97. private static bool Update_6_39()
  98. {
  99. void ConvertJobDocumentIssuedDates()
  100. {
  101. Logger.Send(LogType.Information, "", "Converting Job Document Issued Dates");
  102. List<JobDocumentSetMileStone> updates = new List<JobDocumentSetMileStone>();
  103. var columns = new Columns<JobDocumentSetMileStone>(x => x.ID).Add(x => x.Submitted).Add(x => x.Status);
  104. columns.Add("Issued");
  105. CoreTable milestones = DbFactory.Provider.Query<JobDocumentSetMileStone>(null, columns);
  106. foreach (var row in milestones.Rows)
  107. {
  108. var milestone = row.ToObject<JobDocumentSetMileStone>();
  109. if (milestone.Status == JobDocumentSetMileStoneStatus.Unknown)
  110. milestone.Status = JobDocumentSetMileStoneStatus.Submitted;
  111. milestone.Submitted = row.Get<DateTime>("Issued");
  112. updates.Add(milestone);
  113. }
  114. DbFactory.Provider.Save(updates);
  115. }
  116. void ConvertProductUnitsOfMeasure()
  117. {
  118. Logger.Send(LogType.Information, "", "Converting Product Units of Measure");
  119. List<ProductDimensionUnit> updates = new List<ProductDimensionUnit>();
  120. var columns = new Columns<ProductDimensionUnit>(x => x.ID).Add(x => x.Description);
  121. CoreTable units = DbFactory.Provider.Query<ProductDimensionUnit>(new Filter<ProductDimensionUnit>(x=>x.Code).IsEqualTo(""), columns);
  122. foreach (var row in units.Rows)
  123. {
  124. var unit = row.ToObject<ProductDimensionUnit>();
  125. unit.Code = unit.Description;
  126. updates.Add(unit);
  127. }
  128. DbFactory.Provider.Save(updates);
  129. }
  130. void ConvertQuoteUnitsOfMeasure()
  131. {
  132. Logger.Send(LogType.Information, "", "Converting Quote Units of Measure");
  133. List<QuoteTakeOffUnit> updates = new List<QuoteTakeOffUnit>();
  134. var columns = new Columns<QuoteTakeOffUnit>(x => x.ID).Add(x => x.Description);
  135. CoreTable units = DbFactory.Provider.Query<QuoteTakeOffUnit>(new Filter<QuoteTakeOffUnit>(x=>x.Code).IsEqualTo(""), columns);
  136. foreach (var row in units.Rows)
  137. {
  138. var unit = row.ToObject<QuoteTakeOffUnit>();
  139. unit.Code = unit.Description;
  140. updates.Add(unit);
  141. }
  142. DbFactory.Provider.Save(updates);
  143. }
  144. ConvertJobDocumentIssuedDates();
  145. ConvertProductUnitsOfMeasure();
  146. ConvertQuoteUnitsOfMeasure();
  147. return true;
  148. }
  149. private static bool Update_6_43()
  150. {
  151. void ConvertSupplierProductLinks()
  152. {
  153. Logger.Send(LogType.Information, "", "Converting Supplier/Product Links");
  154. List<SupplierProduct> updates = new List<SupplierProduct>();
  155. var columns = new Columns<SupplierProduct>(x => x.ID).Add(x=>x.Product.ID);
  156. columns.Add("ProductLink.ID");
  157. CoreTable products = DbFactory.Provider.Query<SupplierProduct>(null, columns);
  158. foreach (var row in products.Rows)
  159. {
  160. Guid id = row.Get<SupplierProduct,Guid>(x=>x.ID);
  161. Guid oldid = row.Get<Guid>("ProductLink.ID");
  162. Guid newid = row.Get<SupplierProduct,Guid>(x=>x.Product.ID);
  163. if ((oldid != Guid.Empty) && (newid == Guid.Empty))
  164. {
  165. var update = new SupplierProduct() { ID = id };
  166. update.CommitChanges();
  167. update.Product.ID = oldid;
  168. updates.Add(update);
  169. }
  170. }
  171. DbFactory.Provider.Save(updates);
  172. }
  173. ConvertSupplierProductLinks();
  174. return true;
  175. }
  176. private struct Map<T>
  177. {
  178. public String Old;
  179. public Expression<Func<T, object>> New;
  180. public Map(String oldcolumn, Expression<Func<T, object>> newcolumn)
  181. {
  182. Old = oldcolumn;
  183. New = newcolumn;
  184. }
  185. }
  186. private static bool Update_7_00()
  187. {
  188. static void Convert<T>(
  189. Filter<T> filter,
  190. params Map<T>[] maps
  191. ) where T : Entity, IPersistent, IRemotable, new()
  192. {
  193. Logger.Send(LogType.Information, "", $"Converting {typeof(T).EntityName().Split('.').Last()}...");
  194. List<T> updates = new List<T>();
  195. var columns = new Columns<T>(x => x.ID);
  196. foreach (var map in maps)
  197. {
  198. if (!columns.Items.Any(x=>String.Equals(x.Property,map.Old)))
  199. columns.Add(map.Old);
  200. if (!columns.Items.Any(x=>String.Equals(x.Property,CoreUtils.GetFullPropertyName<T, object>(map.New, "."))))
  201. columns.Add(map.New);
  202. }
  203. CoreTable table = DbFactory.Provider.Query<T>(filter,columns);
  204. int iCount = 0;
  205. foreach (var row in table.Rows)
  206. {
  207. var update = row.ToObject<T>();
  208. foreach (var map in maps)
  209. CoreUtils.SetPropertyValue(update, CoreUtils.GetFullPropertyName<T, object>(map.New, "."), CoreUtils.GetPropertyValue(update, map.Old));
  210. if (update.IsChanged())
  211. updates.Add(update);
  212. if (updates.Count == 100)
  213. {
  214. iCount += updates.Count;
  215. Logger.Send(LogType.Information, "", $"Converting {typeof(T).EntityName().Split('.').Last()} Times ({iCount}/{table.Rows.Count}");
  216. DbFactory.Provider.Save(updates);
  217. updates.Clear();
  218. }
  219. }
  220. if (updates.Count > 0)
  221. {
  222. iCount += updates.Count;
  223. Logger.Send(LogType.Information, "", $"Converting {typeof(T).EntityName().Split('.').Last()} Times ({iCount}/{table.Rows.Count})");
  224. DbFactory.Provider.Save(updates);
  225. updates.Clear();
  226. }
  227. }
  228. Convert<Assignment>(
  229. new Filter<Assignment>(x=>x.Booked.Start).IsEqualTo(DateTime.MinValue)
  230. .And(x=>x.Booked.Finish).IsEqualTo(DateTime.MinValue)
  231. .And(x=>x.Actual.Finish).IsEqualTo(DateTime.MinValue)
  232. .And(x=>x.Actual.Finish).IsEqualTo(DateTime.MinValue),
  233. new Map<Assignment>("Start",x => x.Booked.Start),
  234. new Map<Assignment>("Finish",x => x.Booked.Finish),
  235. new Map<Assignment>("Start",x => x.Actual.Start),
  236. new Map<Assignment>("Finish",x => x.Actual.Finish)
  237. );
  238. // ConvertTimes<TimeSheet>(
  239. // x => x.Actual.Duration,
  240. // new TimeExpressions<TimeSheet>(x => x.Actual.Start, x => x.Actual.Finish),
  241. // new TimeExpressions<TimeSheet>(x => x.Approved.Start, x => x.Approved.Finish)
  242. // );
  243. void Convert_StandardLeaves_and_LeaveRequests()
  244. {
  245. // Delete from TimeSheet where processed={} and leaverequestlink.id != empty
  246. var unprocessedtimesheets = DbFactory.Provider.Query<TimeSheet>(
  247. new Filter<TimeSheet>(x => x.Processed).IsEqualTo(DateTime.MinValue)
  248. .And(x => x.LeaveRequestLink.ID).IsNotEqualTo(Guid.Empty),
  249. new Columns<TimeSheet>(x=>x.ID)
  250. ).Rows.Select(x=>x.ToObject<TimeSheet>()).ToArray();
  251. int iTimes = 0;
  252. while (iTimes < unprocessedtimesheets.Length)
  253. {
  254. var deletions = unprocessedtimesheets.Skip(iTimes).Take(100).ToArray();
  255. DbFactory.Provider.Purge<TimeSheet>(deletions);
  256. iTimes += deletions.Length;
  257. }
  258. //DbFactory.Provider.Delete<TimeSheet>(unprocessedtimesheets,"");
  259. // Find all Leave Requests where public holiday != empty
  260. var standardleaverequests = DbFactory.Provider.Query<LeaveRequest>(
  261. new Filter<LeaveRequest>(x => x.PublicHoliday.ID).IsNotEqualTo(Guid.Empty),
  262. new Columns<LeaveRequest>(x=>x.ID)
  263. .Add(x=>x.PublicHoliday.ID)
  264. ).Rows.Select(x => x.ToObject<LeaveRequest>()).ToArray();
  265. foreach (var standardleaverequest in standardleaverequests)
  266. {
  267. // Find all timesheets for this leave request
  268. var standardleavetimesheets = DbFactory.Provider.Query<TimeSheet>(
  269. new Filter<TimeSheet>(x=>x.LeaveRequestLink.ID).IsEqualTo(standardleaverequest.ID),
  270. new Columns<TimeSheet>(x=>x.ID)
  271. .Add(x=>x.LeaveRequestLink.ID)
  272. ).Rows.Select(x=>x.ToObject<TimeSheet>()).ToArray();
  273. // Redirect timesheet from leaverequest to standardleave
  274. foreach (var standardleavetimesheet in standardleavetimesheets)
  275. {
  276. standardleavetimesheet.StandardLeaveLink.ID = standardleaverequest.PublicHoliday.ID;
  277. standardleavetimesheet.LeaveRequestLink.ID = Guid.Empty;
  278. }
  279. if (standardleavetimesheets.Any())
  280. DbFactory.Provider.Save(standardleavetimesheets);
  281. }
  282. // delete these leave requests
  283. int iRequests = 0;
  284. while (iRequests < standardleaverequests.Length)
  285. {
  286. var deletions = standardleaverequests.Skip(iRequests).Take(100).ToArray();
  287. DbFactory.Provider.Purge<LeaveRequest>(deletions);
  288. iRequests += deletions.Length;
  289. }
  290. // Delete from Assignment where leaverequestlink id != empty
  291. var leaveassignments = DbFactory.Provider.Query<Assignment>(
  292. new Filter<Assignment>(x => x.LeaveRequestLink.ID).IsNotEqualTo(Guid.Empty),
  293. new Columns<Assignment>(x=>x.ID)
  294. ).Rows.Select(x=>x.ToObject<Assignment>()).ToArray();
  295. int iAssignments = 0;
  296. while (iAssignments < leaveassignments.Length)
  297. {
  298. var deletions = leaveassignments.Skip(iAssignments).Take(100).ToArray();
  299. DbFactory.Provider.Purge<Assignment>(deletions);
  300. iAssignments += deletions.Length;
  301. }
  302. }
  303. Convert_StandardLeaves_and_LeaveRequests();
  304. return true;
  305. }
  306. private class Update_7_06_Class
  307. {
  308. private static Dictionary<Type, List<Tuple<Type, string>>> _cascades = new();
  309. private static Dictionary<Type, List<Tuple<Type, List<string>>>> _setNulls = new();
  310. private static void LoadDeletions(Type type)
  311. {
  312. if (_cascades.ContainsKey(type)) return;
  313. // Get the EntityLink that is associated with this class
  314. var linkclass = CoreUtils.TypeList(
  315. new[] { type.Assembly },
  316. x => typeof(IEntityLink).GetTypeInfo().IsAssignableFrom(x) && x.GetInheritedGenericTypeArguments().FirstOrDefault() == type
  317. ).FirstOrDefault();
  318. // if The entitylink does not exist, we don't need to do anything
  319. if (linkclass == null)
  320. return;
  321. var cascades = new List<Tuple<Type, string>>();
  322. var setNulls = new List<Tuple<Type, List<string>>>();
  323. var childtypes = DbFactory.Provider.Types.Where(x => x.IsSubclassOf(typeof(Entity)) && x.GetCustomAttribute<AutoEntity>() == null);
  324. foreach (var childtype in childtypes)
  325. {
  326. // Get all registered types for this entitylink
  327. var fields = new List<string>();
  328. var bDelete = false;
  329. // Find any IEntityLink<> properties that refer back to this class
  330. var childprops = CoreUtils.PropertyList(childtype, x => x.PropertyType == linkclass);
  331. foreach (var childprop in childprops)
  332. {
  333. var fieldname = string.Format("{0}.ID", childprop.Name);
  334. var attr = childprop.GetCustomAttributes(typeof(EntityRelationshipAttribute), true).FirstOrDefault();
  335. if (attr != null && ((EntityRelationshipAttribute)attr).Action.Equals(DeleteAction.Cascade))
  336. {
  337. cascades.Add(new(childtype, fieldname));
  338. bDelete = true;
  339. break;
  340. }
  341. fields.Add(fieldname);
  342. }
  343. if (!bDelete && fields.Any())
  344. {
  345. setNulls.Add(new(childtype, fields));
  346. }
  347. }
  348. _cascades[type] = cascades;
  349. _setNulls[type] = setNulls;
  350. }
  351. private static bool GetCascades(Type type, [NotNullWhen(true)] out List<Tuple<Type, string>>? cascades)
  352. {
  353. LoadDeletions(type);
  354. return _cascades.TryGetValue(type, out cascades);
  355. }
  356. private static bool GetSetNulls(Type type, [NotNullWhen(true)] out List<Tuple<Type, List<string>>>? setNulls)
  357. {
  358. LoadDeletions(type);
  359. return _setNulls.TryGetValue(type, out setNulls);
  360. }
  361. private static MethodInfo _deleteEntitiesMethod = typeof(Update_7_06_Class).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
  362. .Single(x => x.Name == nameof(DeleteEntity) && x.IsGenericMethod);
  363. private static void DeleteEntity<T>(Deletion deletion, Guid parentID, string parentField, DeletionData deletionData) where T : Entity, new()
  364. {
  365. var columns = DeletionData.DeletionColumns<T>();
  366. var delEntities = DbFactory.Provider.QueryDeleted(deletion, new Filter<T>(parentField).IsEqualTo(parentID), columns);
  367. var nDelntities = DbFactory.Provider.Query(new Filter<T>(parentField).IsEqualTo(parentID), columns);
  368. foreach (var row in delEntities.Rows.Concat(nDelntities.Rows))
  369. {
  370. deletionData.DeleteEntity<T>(row);
  371. CascadeDelete(typeof(T), deletion, row.Get<T, Guid>(x => x.ID), deletionData);
  372. }
  373. }
  374. private static void DeleteEntity(Type T, Deletion deletion, Guid parentID, string parentField, DeletionData deletionData)
  375. {
  376. _deleteEntitiesMethod.MakeGenericMethod(T).Invoke(null, new object?[] { deletion, parentID, parentField, deletionData });
  377. }
  378. private static MethodInfo _setNullEntityMethod = typeof(Update_7_06_Class).GetMethods(BindingFlags.NonPublic | BindingFlags.Static)
  379. .Single(x => x.Name == nameof(SetNullEntity) && x.IsGenericMethod);
  380. private static void SetNullEntity<T>(List<string> properties, Guid parentID, DeletionData deletionData) where T : Entity, new()
  381. {
  382. foreach (var property in properties)
  383. {
  384. var entities = DbFactory.Provider.Query(new Filter<T>(property).IsEqualTo(parentID), new Columns<T>(x => x.ID));
  385. foreach (var row in entities.Rows)
  386. {
  387. deletionData.SetNullEntity<T>(row.Get<T, Guid>(x => x.ID), property, parentID);
  388. }
  389. }
  390. }
  391. private static void SetNullEntity(Type T, List<string> properties, Guid parentID, DeletionData deletionData)
  392. {
  393. _setNullEntityMethod.MakeGenericMethod(T).Invoke(null, new object?[] { properties, parentID, deletionData });
  394. }
  395. private static void CascadeDelete(Type type, Deletion deletion, Guid parentID, DeletionData deletionData)
  396. {
  397. if (GetCascades(type, out var cascades))
  398. {
  399. foreach (var cascade in cascades)
  400. {
  401. DeleteEntity(cascade.Item1, deletion, parentID, cascade.Item2, deletionData);
  402. }
  403. }
  404. if (GetSetNulls(type, out var setNulls))
  405. {
  406. foreach (var setNull in setNulls)
  407. {
  408. SetNullEntity(setNull.Item1, setNull.Item2, parentID, deletionData);
  409. }
  410. }
  411. }
  412. // Referenced via reflection.
  413. private static void PurgeEntityType<T>(Deletion deletion) where T : Entity, new()
  414. {
  415. var entities = DbFactory.Provider.QueryDeleted(deletion, null, DeletionData.DeletionColumns<T>()).ToObjects<T>().ToList();
  416. var deletionData = new DeletionData();
  417. foreach (var entity in entities)
  418. {
  419. deletionData.DeleteEntity(entity);
  420. CascadeDelete(typeof(T), deletion, entity.ID, deletionData);
  421. }
  422. if (deletionData.Cascades.Count > 0 || deletionData.SetNulls.Count > 0)
  423. {
  424. var tableName = typeof(T).Name;
  425. var newDeletion = new Deletion()
  426. {
  427. DeletionDate = DateTime.Now,
  428. HeadTable = tableName,
  429. Description = $"Deleted {entities.Count} entries",
  430. DeletedBy = deletion.DeletedBy,
  431. Data = Serialization.Serialize(deletionData)
  432. };
  433. DbFactory.Provider.Save(newDeletion);
  434. }
  435. DbFactory.Provider.Purge(entities);
  436. }
  437. public static void Purge(Deletion deletion)
  438. {
  439. if (deletion.ID == Guid.Empty)
  440. {
  441. Logger.Send(LogType.Error, "", "Empty Deletion ID");
  442. return;
  443. }
  444. var entityType = CoreUtils.Entities.FirstOrDefault(x => x.Name == deletion.HeadTable);
  445. if (entityType is null)
  446. {
  447. Logger.Send(LogType.Error, "", $"Entity {deletion.HeadTable} does not exist");
  448. return;
  449. }
  450. var purgeMethod = typeof(Update_7_06_Class).GetMethod(nameof(PurgeEntityType), BindingFlags.NonPublic | BindingFlags.Static)!;
  451. purgeMethod.MakeGenericMethod(entityType).Invoke(null, new object[] { deletion });
  452. DbFactory.Provider.Purge<Deletion>(deletion);
  453. }
  454. }
  455. private static bool Update_7_06()
  456. {
  457. var deletions = DbFactory.Provider.Query<Deletion>(
  458. new Filter<Deletion>(x => x.Data).IsEqualTo(""));
  459. Logger.Send(LogType.Information, "", "Updating Deletions");
  460. foreach (var deletion in deletions.ToObjects<Deletion>())
  461. {
  462. Update_7_06_Class.Purge(deletion);
  463. }
  464. Logger.Send(LogType.Information, "", "Finished updating Deletions");
  465. return true;
  466. }
  467. /// <summary>
  468. /// Updating Wpf and Timebench fields to use Platform.DesktopVersion and Platform.MobileVersion
  469. /// </summary>
  470. /// <returns></returns>
  471. private static bool Update_7_14()
  472. {
  473. Logger.Send(LogType.Information, "", "Converting User.Wpf, User.Timebench -> User.Platform.DesktopVersion, User.Platform.MobileVersion");
  474. Logger.Send(LogType.Information, "", "Loading Wpf, Timebench properties");
  475. var props = DbFactory.Provider.Query<CustomProperty>(new Filter<CustomProperty>(x => x.Name).InList("Wpf", "TimeBench"))
  476. .Rows.Select(x => x.ToObject<CustomProperty>()).ToArray();
  477. DatabaseSchema.Load(props);
  478. var columns = new Columns<User>(x => x.ID);
  479. columns.Add("Wpf", "TimeBench");
  480. var users = DbFactory.Provider.Query<User>(
  481. new Filter<User>().All(),
  482. columns).ToObjects<User>().ToList();
  483. foreach(var user in users)
  484. {
  485. if(user.UserProperties.Dictionary.TryGetValue("Wpf", out var wpf))
  486. {
  487. user.Platform.DesktopVersion = wpf?.Value?.ToString() ?? "";
  488. }
  489. if (user.UserProperties.Dictionary.TryGetValue("TimeBench", out var timebench))
  490. {
  491. user.Platform.MobileVersion = timebench?.Value?.ToString() ?? "";
  492. }
  493. }
  494. DbFactory.Provider.Save<User>(users);
  495. Logger.Send(LogType.Information, "", "Finished updating user versions");
  496. return true;
  497. }
  498. }
  499. }