| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685 |
- using Comal.Classes;
- using InABox.Configuration;
- using InABox.Core;
- using InABox.Database;
- using InABox.DynamicGrid;
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Text;
- using System.Threading.Tasks;
- namespace PRS.Shared.Database_Update_Scripts;
- internal class Update_8_58 : DatabaseUpdateScript
- {
- public override VersionNumber Version => new(8, 58);
- private static void UpdateTimeSheets(IProvider provider)
- {
- Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed -> TimeSheet.Posted");
- var timeSheets = provider.Query(
- Filter<TimeSheet>.Where(x => x.Processed).IsNotEqualTo(DateTime.MinValue)
- .And(x => x.Posted).IsEqualTo(DateTime.MinValue),
- Columns.None<TimeSheet>()
- .Add(x => x.ID)
- .Add(x => x.Processed)
- .Add(x => x.Posted))
- .ToArray<TimeSheet>();
- if(timeSheets.Length == 0)
- {
- Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed -> TimeSheet.Posted: Done");
- return;
- }
- Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed -> TimeSheet.Posted: {timeSheets.Length} items");
- Utils.Utils.ProcessInChunks(
- timeSheets,
- chunk =>
- {
- foreach (var timeSheet in chunk)
- {
- timeSheet.Posted = timeSheet.Processed;
- }
- provider.Save(chunk);
- },
- 200,
- percentage => Logger.Send(LogType.Information, "", $"Migrating TimeSheet.Processed: {percentage:F2}%"));
- }
- private static void ConvertLink<T>(IProvider provider, string fromLink, string toLink)
- where T : Entity, new()
- {
- var fromColumn = new Column<T>(fromLink).SubColumn(new Column<IEntityLink>(x => x.ID));
- var toColumn = new Column<T>(toLink).SubColumn(new Column<IEntityLink>(x => x.ID));
- var items = provider.Query(
- Filter<T>.Where<Guid>(fromColumn).IsNotEqualTo(Guid.Empty)
- .And<Guid>(toColumn).IsEqualTo(Guid.Empty),
- Columns.None<T>()
- .Add(x => x.ID)
- .Add(toColumn) // The order here matters a lot; since I've made a lot of obsolete links just point directly to the new one, then we have to set the new before the old.
- .Add(fromColumn))
- .ToArray<T>();
- if(items.Length == 0)
- {
- return;
- }
- Logger.Send(LogType.Information, "", $"Migrating {typeof(T).Name}.{fromColumn.Property} -> {typeof(T).Name}.{toColumn.Property}: {items.Length} items");
- Utils.Utils.ProcessInChunks(
- items,
- chunk =>
- {
- foreach (var item in chunk)
- {
- item.SetObserving(false);
- var originalValue = toColumn.PropertyDefinition.Getter()(item);
- toColumn.PropertyDefinition.Setter()(
- item,
- fromColumn.PropertyDefinition.Getter()(item));
- item.SetOriginalValue(toColumn.Property, originalValue);
- item.SetObserving(true);
- }
- provider.Save(chunk);
- },
- 1000,
- percentage => Logger.Send(LogType.Information, "", $"Migrating {typeof(T).Name}.{fromColumn.Property}: {percentage:F2}%"));
- }
- private static void ConvertLink<T, TFromLink, TToLink>(IProvider provider, Expression<Func<T, TFromLink>> fromLink, Expression<Func<T, TToLink>> toLink)
- where T : Entity, new()
- where TFromLink : IEntityLink
- where TToLink : IEntityLink
- {
- ConvertLink<T>(provider, CoreUtils.GetFullPropertyName(fromLink, "."), CoreUtils.GetFullPropertyName(toLink, "."));
- }
- private static void ConvertLink<T, TLink>(IProvider provider, Expression<Func<T, TLink>> fromLink, Expression<Func<T, TLink>> toLink)
- where T : Entity, new()
- where TLink : IEntityLink
- {
- ConvertLink<T, TLink, TLink>(provider, fromLink, toLink);
- }
- private static string QuoteString(string str)
- {
- return $"\"{str.Replace("\\", "\\\\").Replace("\"", "\\\"")}\"";
- }
- private static void ConvertQAQuestions(IProvider provider)
- {
- var qaQuestions = provider.Query(
- Filter<QAQuestion>.Where(x => x.Converted).IsEqualTo(false),
- Columns.None<QAQuestion>()
- .Add(x => x.ID)
- .Add(x => x.Converted)
- .Add(x => x.Code)
- .Add(x => x.Question)
- .Add(x => x.Section)
- .Add(x => x.Description)
- .Add(x => x.Answer)
- .Add(x => x.Parameters)
- .Add(x => x.Sequence)
- .Add(x => x.Form.ID))
- .ToObjects<QAQuestion>()
- .GroupBy(x => x.Form.ID)
- .Select(x => new
- {
- FormID = x.Key,
- Questions = x.OrderBy(x => x.Sequence).ToList()
- })
- .ToList();
- Logger.Send(LogType.Information, "", $"Converting {qaQuestions.Count} QA forms into DigitalForms");
- var formSequences = provider.Query(
- Filter<DigitalFormVariable>.Where(x => x.Form.ID)
- .InList(qaQuestions.ToArray(x => x.FormID)),
- Columns.None<DigitalFormVariable>()
- .Add(x => x.Form.ID)
- .Add(x => x.Sequence))
- .ToObjects<DigitalFormVariable>()
- .GroupBy(x => x.Form.ID)
- .ToDictionary(x => x.Key, x => x.Max(x => x.Sequence));
- var variableMappings = new Dictionary<Guid, Dictionary<Guid, string>>();
- var variables = new List<DigitalFormVariable>();
- var layouts = new List<DigitalFormLayout>();
- foreach(var item in qaQuestions)
- {
- if(formSequences.TryGetValue(item.FormID, out var sequence))
- {
- sequence++;
- }
- else
- {
- sequence = 0;
- }
- var layout = new DFLayout();
- layout.ColumnWidths.Add("Auto");
- layout.ColumnWidths.Add("*");
- var codes = new HashSet<string>();
- string GenerateCode(string code)
- {
- var originalCode = code;
- var i = 1;
- while (codes.Contains(code))
- {
- code = $"{originalCode}{i}";
- ++i;
- }
- return code;
- }
- var mappings = variableMappings.GetValueOrAdd(item.FormID);
- var nButtons = 0;
- var i = 1;
- foreach(var question in item.Questions)
- {
- layout.RowHeights.Add("Auto");
- var row = layout.RowHeights.Count;
- if(question.Answer == QAAnswer.Comment)
- {
- var label = new DFLayoutLabel { Caption = question.Question, Row = row, Column = 1, ColumnSpan = 3 };
- label.Style.HorizontalTextAlignment = DFLayoutAlignment.Middle;
- layout.Elements.Add(label);
- }
- else
- {
- var rowNum = new DFLayoutLabel { Caption = i.ToString(), Row = row, Column = 1 };
- var label = new DFLayoutLabel { Caption = question.Question, Row = row, Column = 2 };
- layout.Elements.Add(rowNum);
- layout.Elements.Add(label);
- var variable = new DigitalFormVariable();
- variable.Form.CopyFrom(question.Form);
- variable.Sequence = sequence++;
- DFLayoutFieldProperties properties;
- Type fieldType;
- var code = GenerateCode(question.Code.NotWhiteSpaceOr(question.Answer.ToString()));
- var parameters = question.ParseParameters();
- switch (question.Answer)
- {
- case QAAnswer.Choice:
- {
- // ColourExpression
- var buttons = parameters["Options"].Split(',').ToArray(x => x.Trim());
- var colors = parameters["Colors"].Split(',').ToArray(x => x.Trim());
- var defValue = parameters["Default"].Trim();
- fieldType = typeof(DFLayoutOptionField);
- var optionProperties = new DFLayoutOptionFieldProperties();
- properties = optionProperties;
- optionProperties.Default = defValue;
- optionProperties.OptionType = DFLayoutOptionType.Buttons;
- optionProperties.Options = DFLayoutOptionFieldProperties.WriteOptions(buttons);
- var colourExpression = "null";
- foreach(var (option, colour) in buttons.Zip(colors))
- {
- colourExpression = $"If([{code}] == {QuoteString(option)}, {QuoteString(colour)}, {colourExpression})";
- }
- optionProperties.ColourExpression = colourExpression;
- nButtons = Math.Max(nButtons, buttons.Length);
- }
- break;
- case QAAnswer.Number:
- {
- var defValue = parameters["Default"];
- fieldType = typeof(DFLayoutDoubleField);
- var doubleProperties = new DFLayoutDoubleFieldProperties();
- properties = doubleProperties;
- doubleProperties.Default = double.TryParse(defValue, out var d) ? d : default;
- }
- break;
- case QAAnswer.Text:
- {
- var defValue = parameters["Default"];
- fieldType = typeof(DFLayoutStringField);
- var stringProperties = new DFLayoutStringFieldProperties();
- properties = stringProperties;
- stringProperties.Default = defValue;
- }
- break;
- case QAAnswer.Combo:
- {
- var buttons = parameters["Options"].Split(',');
- var defValue = parameters["Default"];
- fieldType = typeof(DFLayoutOptionField);
- var optionProperties = new DFLayoutOptionFieldProperties();
- properties = optionProperties;
- optionProperties.Default = defValue;
- optionProperties.OptionType = DFLayoutOptionType.Combo;
- optionProperties.Options = DFLayoutOptionFieldProperties.WriteOptions(buttons);
- }
- break;
- default:
- throw new Exception("Impossible");
- }
- properties.Code = code;
- properties.Description = question.Description.NotWhiteSpaceOr(question.Question);
- properties.Required = parameters.GetValueOrDefault("Default").IsNullOrWhiteSpace();
- variable.SaveProperties(fieldType, properties);
- mappings.Add(question.ID, variable.Code);
- codes.Add(variable.Code);
- var field = (Activator.CreateInstance(variable.FieldType()) as DFLayoutField)!;
- field.Name = variable.Code;
- field.Row = row;
- field.Column = 3;
- layout.Elements.Add(field);
- variables.Add(variable);
- ++i;
- }
- question.Converted = true;
- }
- layout.ColumnWidths.Add(Math.Max(150, nButtons * 80).ToString());
- var dfLayout = new DigitalFormLayout();
- dfLayout.Form.ID = item.FormID;
- dfLayout.Layout = layout.SaveLayout();
- dfLayout.Description = "Generated from QA form";
- dfLayout.Type = DFLayoutType.Desktop;
- dfLayout.Active = true;
- layouts.Add(dfLayout);
- }
- provider.Save(variables);
- provider.Save(layouts);
- provider.Save(qaQuestions.SelectMany(x => x.Questions));
- FormUpdater.UpdateAllForms(
- (form, variables) => false,
- (formType, instance, form, variables) =>
- {
- if (!variableMappings.TryGetValue(form.ID, out var mappings)) return false;
- var values = DigitalForm.DeserializeFormSaveData(instance) ?? new();
- var items = values.ToLoadStorage().Items().ToArray();
- foreach(var (key, value) in items)
- {
- if (!Guid.TryParse(key, out var id)) continue;
- if (!mappings.TryGetValue(id, out var code)) continue;
- values.AddValue(code, value?.ToString()?.Trim());
- }
- DigitalForm.SerializeFormData(instance, values);
- return true;
- },
- filter: Filter<DigitalForm>.Where(x => x.ID).InList(qaQuestions.ToArray(x => x.FormID)));
- }
- private static void RenameTable<TFrom, TTo>(IProvider provider,
- Dictionary<Expression<Func<TFrom, object?>>, Expression<Func<TTo, object?>>>? extraMaps = null)
- where TFrom : Entity, new()
- where TTo : Entity, new()
- {
- var currentMaps = new HashSet<string>();
- var maps = new List<(IProperty from, IProperty to)>();
- foreach(var (from, to) in extraMaps ?? [])
- {
- var fromProperty = DatabaseSchema.PropertyStrict(from);
- var toProperty = DatabaseSchema.PropertyStrict(to);
- currentMaps.Add(fromProperty.Name);
- maps.Add((fromProperty, toProperty));
- }
- foreach(var fromProperty in DatabaseSchema.LocalProperties(typeof(TFrom)))
- {
- if (currentMaps.Contains(fromProperty.Name)) continue;
- if(DatabaseSchema.Property(typeof(TTo), fromProperty.Name) is IProperty toProperty)
- {
- if(fromProperty.PropertyType != toProperty.PropertyType)
- {
- throw new Exception($"Cannot migrate {typeof(TFrom).Name}.{fromProperty.Name} -> {typeof(TTo).Name}.{toProperty.Name}: type mismatch");
- }
- maps.Add((fromProperty, toProperty));
- }
- else
- {
- }
- }
- var items = provider.Query<TFrom>(
- Filter<TFrom>.Where(x => x.ID).NotInQuery(Filter.All<TTo>(), x => x.ID),
- Columns.None<TFrom>()
- .Add(x => x.ID)
- .Add(maps.Select(x => new Column<TFrom>(x.from))))
- .ToArray<TFrom>();
- if (items.Length == 0) return;
- Logger.Send(LogType.Information, "", $"Migrating {typeof(TFrom).Name} -> {typeof(TTo).Name}: {items.Length} items");
- Utils.Utils.ProcessInChunks(
- items,
- chunk =>
- {
- var newItems = new List<TTo>();
- foreach (var item in chunk)
- {
- var newItem = new TTo();
- newItem.SetObserving(false);
- newItem.ID = item.ID;
- foreach(var (from, to) in maps)
- {
- to.Setter()(newItem, from.Getter()(item));
- }
- newItem.SetObserving(true);
- newItems.Add(newItem);
- }
- provider.Save(newItems);
- },
- 1000,
- percentage => Logger.Send(LogType.Information, "", $"Migrating {typeof(TFrom).Name}: {percentage:F2}%"));
- }
- private static void ConvertLinks(IProvider provider)
- {
- ConvertLink<Assignment, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<EquipmentAssignment, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<Kanban, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<Requisition, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<RequisitionItem, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<Setout, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<JobBillOfMaterialsActivity, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<ManufacturingSection, QAFormLink, DigitalFormLink>(provider, x => x.QAForm, x => x.DigitalForm);
- ConvertLink<ManufacturingTemplateStage, QAFormLink, DigitalFormLink>(provider, x => x.QAForm, x => x.DigitalForm);
- var method = typeof(Update_8_58).GetMethods(BindingFlags.Static | BindingFlags.NonPublic)
- .Where(x => x.Name == nameof(ConvertLink)
- && x.IsGenericMethod
- && x.GetGenericArguments().Length == 1)
- .First();
- foreach(var entity in DbFactory.ProviderFactory.Types.Where(x => x.HasInterface(typeof(IEntityDocument))))
- {
- var entityMethod = method.MakeGenericMethod(entity);
- var entityFromProp = DatabaseSchema.PropertyStrict<IEntityDocument>(entity, x => x.EntityLink);
- var entityToProp = DatabaseSchema.PropertyStrict<IEntityDocument>(entity, x => x.Entity);
- entityMethod.Invoke(null, [provider, entityFromProp.Name, entityToProp.Name]);
- var documentFromProp = DatabaseSchema.PropertyStrict<IEntityDocument>(entity, x => x.DocumentLink);
- var documentToProp = DatabaseSchema.PropertyStrict<IEntityDocument>(entity, x => x.Document);
- entityMethod.Invoke(null, [provider, documentFromProp.Name, documentToProp.Name]);
- }
- foreach(var entity in DbFactory.ProviderFactory.Types)
- {
- MethodInfo? entityMethod = null;
- foreach(var property in DatabaseSchema.LocalProperties(entity))
- {
- if (property.Parent is null
- || property.Parent.PropertyType != typeof(LocalityLink)
- || property.Parent.Parent is null
- || property.Parent.Parent.PropertyType != typeof(Address)) continue;
- var addressProp = property.Parent.Parent;
- entityMethod ??= method.MakeGenericMethod(entity);
- var fromProp = DatabaseSchema.PropertyStrict(entity, addressProp.Name + "." + nameof(Address.LocalityLink));
- var toProp = DatabaseSchema.PropertyStrict(entity, addressProp.Name + "." + nameof(Address.Locality));
- entityMethod.Invoke(null, [provider, fromProp.Name, toProp.Name]);
- }
- }
- ConvertLink<Schedule, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<Schedule, EmployeeLink>(provider, x => x.ManagerLink, x => x.Manager);
- ConvertLink<Assignment, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<Assignment, AssignmentActivityLink>(provider, x => x.ActivityLink, x => x.Activity);
- ConvertLink<Assignment, LeaveRequestLink>(provider, x => x.LeaveRequestLink, x => x.LeaveRequest);
- ConvertLink<Bill, SupplierLink>(provider, x => x.SupplierLink, x => x.Supplier);
- ConvertLink<BillLine, BillLink>(provider, x => x.BillLink, x => x.Bill);
- ConvertLink<DeliveryItem, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<DeliveryItem, ManufacturingPacketLink>(provider, x => x.ManufacturingPacketLink, x => x.ManufacturingPacket);
- ConvertLink<DeliveryItem, RequisitionLink, PickingListLink>(provider, x => x.RequisitionLink, x => x.PickingList);
- ConvertLink<DeliveryItem, SetoutLink>(provider, x => x.SetoutLink, x => x.Setout);
- ConvertLink<DeliveryItem, ShipmentLink>(provider, x => x.ShipmentLink, x => x.Shipment);
- ConvertLink<Employee, UserLink>(provider, x => x.UserLink, x => x.User);
- ConvertLink<Employee, OvertimeRuleLink>(provider, x => x.OvertimeRuleLink, x => x.OvertimeRule);
- ConvertLink<EmployeeRole, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<EmployeeRole, RoleLink>(provider, x => x.RoleLink, x => x.Role);
- ConvertLink<Equipment, EquipmentGroupLink>(provider, x => x.GroupLink, x => x.Group);
- ConvertLink<Equipment, GPSTrackerLink>(provider, x => x.TrackerLink, x => x.Tracker);
- ConvertLink<Invoice, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<Invoice, CustomerLink>(provider, x => x.CustomerLink, x => x.Customer);
- ConvertLink<InvoiceLine, InvoiceLink>(provider, x => x.InvoiceLink, x => x.Invoice);
- ConvertLink<Job, ScheduleLink>(provider, x => x.ScheduleLink, x => x.Schedule);
- ConvertLink<JobActivity, AssignmentActivityLink>(provider, x => x.ActivityLink, x => x.Activity);
- ConvertLink<JobBillOfMaterialsActivity, AssignmentActivityLink>(provider, x => x.ActivityLink, x => x.Activity);
- ConvertLink<JobEmployee, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<JobEmployee, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<JobEquipment, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<JobEquipment, EquipmentLink>(provider, x => x.EquipmentLink, x => x.Equipment);
- ConvertLink<JobTracker, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<JobTracker, GPSTrackerLink>(provider, x => x.TrackerLink, x => x.Tracker);
- ConvertLink<JobITP, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<Kanban, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<Kanban, EmployeeLink>(provider, x => x.ManagerLink, x => x.Manager);
- ConvertLink<Kanban, ScheduleLink>(provider, x => x.ScheduleLink, x => x.Schedule);
- ConvertLink<LeaveRequest, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<ManufacturingPacket, SetoutLink>(provider, x => x.SetoutLink, x => x.Setout);
- ConvertLink<ManufacturingPacket, ManufacturingTemplateLink>(provider, x => x.ManufacturingTemplateLink, x => x.ManufacturingTemplate);
- ConvertLink<ManufacturingPacket, ManufacturingPacketStageLink>(provider, x => x.StageLink, x => x.Stage);
- ConvertLink<ManufacturingPacketStage, ManufacturingSectionLink>(provider, x => x.ManufacturingSectionLink, x => x.ManufacturingSection);
- ConvertLink<ManufacturingPacketComponent, RequisitionLink, PickingListLink>(provider, x => x.Requisition, x => x.PickingList);
- ConvertLink<BillPayment, BillLink>(provider, x => x.BillLink, x => x.Bill);
- ConvertLink<BillPayment, PaymentLink>(provider, x => x.PaymentLink, x => x.Payment);
- ConvertLink<Payment, SupplierLink>(provider, x => x.SupplierLink, x => x.Supplier);
- ConvertLink<Payment, PaymentTypeLink>(provider, x => x.PaymentTypeLink, x => x.PaymentType);
- ConvertLink<PurchaseOrder, SupplierLink>(provider, x => x.SupplierLink, x => x.Supplier);
- ConvertLink<PurchaseOrderItem, PurchaseOrderLink>(provider, x => x.PurchaseOrderLink, x => x.PurchaseOrder);
- ConvertLink<InvoiceReceipt, InvoiceLink>(provider, x => x.InvoiceLink, x => x.Invoice);
- ConvertLink<InvoiceReceipt, ReceiptLink>(provider, x => x.ReceiptLink, x => x.Receipt);
- ConvertLink<Receipt, CustomerLink>(provider, x => x.CustomerLink, x => x.Customer);
- ConvertLink<Receipt, ReceiptTypeLink>(provider, x => x.ReceiptTypeLink, x => x.ReceiptType);
- ConvertLink<RequisitionItem, RequisitionLink>(provider, x => x.RequisitionLink, x => x.Requisition);
- ConvertLink<Shipment, GPSTrackerLink>(provider, x => x.TrackerLink, x => x.Tracker);
- ConvertLink<SupplierProduct, SupplierLink>(provider, x => x.SupplierLink, x => x.Supplier);
- ConvertLink<StockMovementBatch, RequisitionLink, PickingListLink>(provider, x => x.Requisition, x => x.PickingList);
- ConvertLink<EmployeeTeam, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<EmployeeTeam, TeamLink>(provider, x => x.TeamLink, x => x.Team);
- ConvertLink<TimeSheet, EmployeeLink>(provider, x => x.EmployeeLink, x => x.Employee);
- ConvertLink<TimeSheet, JobLink>(provider, x => x.JobLink, x => x.Job);
- ConvertLink<TimeSheet, TimeSheetActivityLink>(provider, x => x.ActivityLink, x => x.Activity);
- ConvertLink<TimeSheet, LeaveRequestLink>(provider, x => x.LeaveRequestLink, x => x.LeaveRequest);
- ConvertLink<TimeSheet, StandardLeaveLink>(provider, x => x.StandardLeaveLink, x => x.StandardLeave);
- RenameTable<Requisition, PickingList>(provider);
- RenameTable<RequisitionItem, PickingListItem>(provider,
- new()
- {
- { x => x.Requisition.ID, x => x.PickingList.ID }
- });
- RenameTable<RequisitionDestination, PickingListDestination>(provider);
- RenameTable<RequisitionDocument, PickingListDocument>(provider);
- RenameTable<RequisitionKanban, PickingListKanban>(provider);
- }
- private static void UpdateColumns<T>(IProvider provider)
- where T : Entity, IDatabaseStoredSettings, new()
- {
- var settings = provider.Query(
- Filter<T>.Where(x => x.Section).IsEqualTo(nameof(DynamicGridColumns)),
- Columns.None<T>()
- .Add(x => x.ID)
- .Add(x => x.Key)
- .Add(x => x.Contents))
- .ToArray<T>();
- var changedSettings = new List<T>();
- foreach(var setting in settings)
- {
- if (setting.Key.IsNullOrWhiteSpace()) continue;
- var entityName = setting.Key.Split('.')[^1];
- var entity = CoreUtils.Entities.Where(x => x.Name == entityName
- && x.IsSubclassOf(typeof(Entity)))
- .FirstOrDefault();
- if (entity is null) continue;
- var columns = Serialization.Deserialize<DynamicGridColumns>(setting.Contents);
- if(columns is not null)
- {
- var changed = false;
- foreach(var column in columns)
- {
- var i = 0;
- while(i < column.ColumnName.Length)
- {
- var index = column.ColumnName.IndexOf("Link.", i);
- if (index == -1) break;
- var columnName = column.ColumnName[0..index];
- if(DatabaseSchema.Property(entity, columnName) is IProperty property)
- {
- column.ColumnName = $"{property.Name}.{column.ColumnName[(index + 5)..]}";
- changed = true;
- }
- else
- {
- i = index + 5;
- }
- }
- }
- if (changed)
- {
- setting.Contents = Serialization.Serialize(columns);
- changedSettings.Add(setting);
- }
- }
- }
- provider.Save(changedSettings);
- }
- private static void UpdateColumns(IProvider provider)
- {
- UpdateColumns<GlobalSettings>(provider);
- UpdateColumns<UserSettings>(provider);
- }
- private static void UpdateSecurityToken<T>(IProvider provider, string oldDescriptor, string newDescriptor)
- where T : Entity, ISecurityToken, new()
- {
- var tokens = provider.Query(
- Filter<T>.Where(x => x.Descriptor).IsEqualTo(oldDescriptor),
- Columns.None<T>()
- .Add(x => x.ID)
- .Add(x => x.Descriptor))
- .ToArray<T>();
- foreach(var token in tokens)
- {
- token.Descriptor = newDescriptor;
- }
- provider.Save(tokens);
- }
- private static void UpdateSecurityToken(IProvider provider, string oldDescriptor, string newDescriptor)
- {
- UpdateSecurityToken<GlobalSecurityToken>(provider, oldDescriptor, newDescriptor);
- UpdateSecurityToken<UserSecurityToken>(provider, oldDescriptor, newDescriptor);
- UpdateSecurityToken<SecurityToken>(provider, oldDescriptor, newDescriptor);
- }
- private static void UpdateAutoSecurityToken<TFrom, TTo>(IProvider provider, Type token)
- {
- var toDescriptor = (Activator.CreateInstance(token.MakeGenericType(typeof(TTo))) as IAutoSecurityDescriptor)!;
- var overriden = Security.SecurityDescriptorOverride(toDescriptor);
- if (overriden != toDescriptor) return; // No point in updating these if the token has been overriden.
- var fromDescriptor = (Activator.CreateInstance(token.MakeGenericType(typeof(TFrom))) as IAutoSecurityDescriptor)!;
- UpdateSecurityToken(provider, Security.SecurityDescriptorOverride(fromDescriptor).Code, toDescriptor.Code);
- }
- private static void UpdateAutoSecurityTokens<TFrom, TTo>(IProvider provider)
- {
- var list = CoreUtils.Entities.Where(
- x => x.HasInterface(typeof(IAutoSecurityDescriptor)))
- .ToArray();
- foreach(var T in CoreUtils.Entities.Where(
- x => x.HasInterface(typeof(IAutoSecurityDescriptor))
- && x.IsGenericType && x.GetGenericArguments().Length == 1))
- {
- UpdateAutoSecurityToken<TFrom, TTo>(provider, T);
- }
- }
- private static void UpdateSecurityTokens(IProvider provider)
- {
- UpdateSecurityToken(provider, "CanViewRequisitionsDock", nameof(CanViewPickingListDock));
- UpdateSecurityToken(provider, "CanSkipRequisitionPhotos", nameof(CanSkipPickingListPhotos));
- UpdateSecurityToken(provider, "CanUpdateRequisitionStockMovements", nameof(CanUpdatePickingListStockMovements));
- UpdateSecurityToken(provider, "CanArchiveRequisitions", nameof(CanArchivePickingLists));
- UpdateAutoSecurityTokens<Requisition, PickingList>(provider);
- UpdateAutoSecurityTokens<RequisitionItem, PickingListItem>(provider);
- UpdateAutoSecurityTokens<RequisitionDestination, PickingListDestination>(provider);
- UpdateAutoSecurityTokens<RequisitionDocument, PickingListDocument>(provider);
- UpdateAutoSecurityTokens<RequisitionKanban, PickingListKanban>(provider);
- }
- private static void ConvertSettings<T>(IProvider provider, string fromSection, string toSection)
- where T : Entity, IDatabaseStoredSettings, new()
- {
- var settings = provider.Query(
- Filter<T>.Where(x => x.Section).IsEqualTo(fromSection),
- Columns.None<T>()
- .Add(x => x.ID)
- .Add(x => x.Section))
- .ToArray<T>();
- foreach(var setting in settings)
- {
- setting.Section = toSection;
- }
- provider.Save(settings);
- }
- public override bool Update()
- {
- var provider = DbFactory.NewProvider(Logger.Main);
- UpdateTimeSheets(provider);
- ConvertLinks(provider);
- UpdateColumns(provider);
- UpdateSecurityTokens(provider);
- ConvertSettings<UserSettings>(provider, "RequisitionSettings", "PickingListSettings");
- ConvertQAQuestions(provider);
- return true;
- }
- }
|