using InABox.Clients; using InABox.Core; using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; namespace Comal.Classes { public abstract class DimensionUnit : Entity, IRemotable, IPersistent, ISequenceable, IDimensionUnit { [UniqueCodeEditor(Visible = Visible.Default, Editable = Editable.Enabled)] [EditorSequence(1)] public virtual String Code { get; set; } [TextBoxEditor] [EditorSequence(2)] public virtual String Description { get; set; } [CheckBoxEditor] [EditorSequence(3)] public virtual bool HasQuantity { get; set; } = false; [CheckBoxEditor] [EditorSequence(4)] public virtual bool HasLength { get; set; } = false; [CheckBoxEditor] [EditorSequence(5)] public virtual bool HasWidth { get; set; } = false; [CheckBoxEditor] [EditorSequence(6)] public virtual bool HasHeight { get; set; } = false; [CheckBoxEditor] [EditorSequence(7)] public virtual bool HasWeight { get; set; } = false; [ExpressionEditor(null, typeof(DimensionsExpressionModelGenerator))] [EditorSequence(8)] public virtual string Formula { get; set; } = "1"; [ExpressionEditor(null, typeof(DimensionsExpressionModelGenerator))] [EditorSequence(9)] public virtual string Format { get; set; } = "\"EACH\""; [ScriptEditor] [EditorSequence(10)] public virtual string Conversion { get; set; } = ""; [NullEditor] public long Sequence { get; set; } public bool HasDimensions() => HasHeight || HasWidth || HasLength || HasWeight || HasQuantity; public static string ConvertDimensionsMethodName() => "ConvertDimensions"; public static string DefaultConvertDimensionsScript() { return "using Comal.Classes;\n"+ "\n"+ "public class Module\n"+ "{\n"+ " public void " + ConvertDimensionsMethodName() + "(PurchaseOrderItem item)\n"+ " {\n"+ " }\n"+ "}\n"; } public bool Validate(List errors) { bool result = true; var variables = new Dictionary() { { "Quantity", 1.00F }, { "Length", 1.00F }, { "Width", 1.00F }, { "Height", 1.00F }, { "Weight", 1.00F } }; try { var expr = new CoreExpression(Formula); expr.Evaluate(variables); result = true; } catch (Exception e) { errors.Add($"{Code}: Formula [{Formula}] => {e.Message}"); result = false; } try { var expr = new CoreExpression(Format); expr.Evaluate(variables); result = true; } catch (Exception e) { errors.Add($"{Code}: Format [{Format}] => {e.Message}"); result = false; } return result; } private class DimensionsExpressionModelGenerator : IExpressionModelGenerator { public List GetVariables(object?[] items) { var dimensionUnits = items.Select(x => x as IDimensionUnit).Where(x => x != null).Cast(); var variables = new List(); if (dimensionUnits.All(x => x.HasQuantity)) variables.Add("Quantity"); if (dimensionUnits.All(x => x.HasLength)) variables.Add("Length"); if (dimensionUnits.All(x => x.HasWidth)) variables.Add("Width"); if (dimensionUnits.All(x => x.HasHeight)) variables.Add("Height"); if (dimensionUnits.All(x => x.HasWeight)) variables.Add("Weight"); return variables; } } public override string ToString() { return $"(ProductDimensionUnit: {Code})"; } } public static class DimensionUnitUtils { public static Dictionary UpdateExpressions(T[] items, IProgress progress) where T : DimensionUnit, new() where TLink : DimensionUnitLink { var dimensionTypes = new List<(Type dimType, Type linkType)>(); foreach(var entity in CoreUtils.Entities) { var def = entity.GetSuperclassDefinition(typeof(Dimensions<,>)); if(def != null && def.GenericTypeArguments[1] == typeof(T)) { dimensionTypes.Add((entity, def.GenericTypeArguments[0])); } } var updateTypes = new Dictionary>(); foreach(var entity in CoreUtils.Entities) { if(entity.IsSubclassOf(typeof(Entity)) && !entity.HasAttribute() && entity.HasInterface()) { foreach(var property in DatabaseSchema.Properties(entity)) { if (property.Parent is null || property.Parent.Parent is null || property.IsCalculated || property.Parent.HasParentEntityLink() || !typeof(TLink).IsAssignableFrom(property.Parent.PropertyType) || !property.Name.EndsWith(".ID")) continue; var dimType = dimensionTypes.FirstOrDefault(x => property.Parent.Parent.PropertyType == x.dimType); if(dimType.dimType != null) { var propList = updateTypes.GetValueOrAdd(entity); propList.Add(property.Parent.Parent); } } } } var nResults = new Dictionary(); var ids = items.ToArray(x => x.ID); foreach(var (type, properties) in updateTypes) { var columns = Columns.Create(type, ColumnTypeFlags.Required); foreach(var prop in properties) { columns.Add(prop.Name + "." + Dimensions.unitid.Property); columns.Add(prop.Name + "." + Dimensions.quantity.Property); columns.Add(prop.Name + "." + Dimensions.length.Property); columns.Add(prop.Name + "." + Dimensions.width.Property); columns.Add(prop.Name + "." + Dimensions.height.Property); columns.Add(prop.Name + "." + Dimensions.weight.Property); columns.Add(prop.Name + "." + Dimensions.unitSize.Property); columns.Add(prop.Name + "." + Dimensions.value.Property); } IFilter? filter = null; foreach(var prop in properties) { var newFilter = Filter.Create(type, prop.Name + "." + Dimensions.unitid.Property, Operator.InList, ids); if(filter is null) { filter = newFilter; } else { filter = filter.Or(newFilter); } } if(filter != null) { progress.Report($"Updating {CoreUtils.Neatify(type.GetCaption())}"); var nTotal = Client.Create(type).Query(filter, Columns.None(type).Add("ID")).Rows.Count; var nProcessed = 0; var nResult = 0; var done = false; var percentStep = Math.Max(nTotal / 100, 1); var range = CoreRange.Database(1000); while(nProcessed < nTotal && !done) { var rows = Client.Create(type).Query(filter, columns, range: range).Rows; if (rows.Count == 0) break; if(rows.Count < 1000) { done = true; } range.Next(); var results = new List(rows.Count); for(int i = 0; i < rows.Count; ++i) { if(nProcessed % percentStep == 0) { progress.Report($"Updating {CoreUtils.Neatify(type.GetCaption())}: {(double)nProcessed / (double)nTotal * 100:F0}%"); } var obj = (rows[i].ToObject(type) as Entity)!; foreach(var property in properties) { var id = CoreUtils.GetPropertyValue(obj, property.Name + "." + Dimensions.unitid.Property); if(id is Guid guid) { var unit = items.First(x => x.ID == guid); var dim = (property.Getter()(obj) as IDimensions)!; dim.Calculate(dim.Quantity, dim.Length, dim.Width, dim.Height, dim.Weight, unit.Formula, unit.Format); } } if (obj.IsChanged()) { results.Add(obj); nResult++; } nProcessed++; } Client.Create(type).Save(results, "Updated Value and UnitSize to match dimension unit."); } nResults[type] = nResult; } } return nResults; } } }