using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media.Imaging; using Comal.Classes; using InABox.Clients; using InABox.Core; using InABox.DynamicGrid; using InABox.Wpf; using InABox.WPF; using PRSDimensionUtils; namespace PRSDesktop.Grids; public class ProductDimensionUnitGrid : DynamicDataGrid { private static readonly BitmapImage CONVERT = PRSDesktop.Resources.specifications.AsBitmapImage(); private Button ConvertButton = null!; protected override void Init() { base.Init(); HiddenColumns.Add(x => x.Code); HiddenColumns.Add(x => x.HasQuantity); HiddenColumns.Add(x => x.HasLength); HiddenColumns.Add(x => x.HasWeight); HiddenColumns.Add(x => x.HasWidth); HiddenColumns.Add(x => x.HasHeight); HiddenColumns.Add(x =>x.Conversion); ActionColumns.Add(new DynamicImageColumn(ConvertImage) { Position = DynamicActionColumnPosition.End}); AddButton("Update Expressions", null, (button, rows) => { UpdateExpressions(rows.ToArray()); return false; }); ConvertButton = AddButton("Convert Movements", null, (button, rows) => { UpdateConversion(rows.ToArray()); return false; }); } protected override void SelectItems(CoreRow[]? rows) { base.SelectItems(rows); if(!(rows?.FirstOrDefault()?.Get(x => x.Conversion)).IsNullOrWhiteSpace()) { ConvertButton.Visibility = Visibility.Visible; } else { ConvertButton.Visibility = Visibility.Collapsed; } } private BitmapImage? ConvertImage(CoreRow? row) { return row == null ? CONVERT : String.IsNullOrWhiteSpace(row.Get(x => x.Conversion)) ? null : CONVERT; } protected override void DoValidate(ProductDimensionUnit[] items, List errors) { base.DoValidate(items, errors); foreach (var item in items) item.Validate(errors); } private static void UpdateConversion(ProductDimensionUnit[] items) { int? nMovements = null; Exception? exception = null; var filter = new Filter(x => x.Dimensions.Unit.ID).InList(items.ToArray(x => x.ID)); var columns = Columns.Required() .Add(x => x.Units) .Add(x => x.Received) .Add(x => x.Issued) .AddDimensionsColumns(x => x.Dimensions, Dimensions.ColumnsType.Local); var nTotal = Client.Query(filter, Columns.None().Add(x => x.ID)).Rows.Count; if(!MessageWindow.ShowYesNo($"This will potentially update the dimensions of {nTotal} stock movements, and this cannot be reversed. Are you sure you wish to proceed?", "Confirm")) { return; } Progress.ShowModal("Updating Dimensions", progress => { try { progress.Report($"Updating Stock Movements"); 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.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 Stock Movements: {(double)nProcessed / (double)nTotal * 100:F0}%"); } var mvt = rows[i].ToObject(); var qty = DimensionUtils.ConvertDimensions(mvt.Dimensions, mvt.Units, Client.Provider); if(qty >= 0) { mvt.Received = qty; mvt.Issued = 0; } else { mvt.Received = 0; mvt.Issued = -qty; } if (mvt.IsChanged()) { results.Add(mvt); nResult++; } nProcessed++; } Client.Save(results, "Updated conversion script."); } nMovements = nResult; } catch(Exception e) { exception = e; } }); if(nMovements is not null) { MessageWindow.ShowMessage($"Update successful: {nMovements.Value} movements updated.", "Success"); } else if(exception is not null) { MessageWindow.ShowError("Error while updating dimensions", exception); } } private static void UpdateExpressions(ProductDimensionUnit[] items) { Dictionary? results = null; Exception? exception = null; Progress.ShowModal("Updating Dimensions", progress => { try { results = DimensionUnitUtils.UpdateExpressions(items, progress); } catch(Exception e) { exception = e; } }); if(results is not null) { MessageWindow.ShowMessage($"Update successful:\n{string.Join("\n", results.Select(x => $"- {x.Key.Name}: {x.Value} items updated"))}", "Success"); } else if(exception is not null) { MessageWindow.ShowError("Error while updating dimensions", exception); } } private bool ShouldUpdateExpressions = false; protected override void DoBeforeSave(IDynamicEditorForm editor, ProductDimensionUnit[] items) { base.DoBeforeSave(editor, items); ShouldUpdateExpressions = false; if(items.Any(x => x.HasOriginalValue(x => x.Format) || x.HasOriginalValue(x => x.Formula))) { if(MessageWindow.ShowYesNo("The format and/or formula has been changed; do you wish to update the UnitSize/Value of every item that uses dimension to match the new expression? (This may take a while)", "Update expressions?")) { ShouldUpdateExpressions = true; } } } protected override void DoAfterSave(IDynamicEditorForm editor, ProductDimensionUnit[] items) { base.DoAfterSave(editor, items); if (ShouldUpdateExpressions) { UpdateExpressions(items); } } protected override bool DoMerge(CoreRow[] rows) { var columns = new Column[] { new(x => x.HasLength), new(x => x.HasQuantity), new(x => x.HasHeight), new(x => x.HasWeight), new(x => x.HasWidth), }; var target = rows[^1].ToObject(); if(columns.Any(c => rows.Select(r => r[c.Property]).Distinct().Skip(1).Any())) { MessageWindow.ShowMessage("These dimension units are incompatible, and cannot be merged.\n\n(Dimension units can only be merged if they have the same [HasQuantity], [HasLength], [HasWidth], [HasHeight] and [HasWeight] values).", "Incompatible units", image: MessageWindow.WarningImage); return false; } if (base.DoMerge(rows)) { if(MessageWindow.ShowYesNo( $"Do you wish to update the UnitSize/Value for every item that uses {target.Code}? (This may take a while)", "Update Expressions?")) { UpdateExpressions([target]); } return true; } return false; } protected override void CustomiseEditor(IDynamicEditorForm form, ProductDimensionUnit[] items, DynamicGridColumn column, BaseEditor editor) { base.CustomiseEditor(form, items, column, editor); if (column.ColumnName == nameof(ProductDimensionUnit.Conversion) && editor is ScriptEditor scriptEditor) { scriptEditor.Type = ScriptEditorType.TemplateEditor; scriptEditor.OnEditorClicked += () => { var script = items.FirstOrDefault()?.Conversion.NotWhiteSpaceOr() ?? DimensionUnit.DefaultConvertDimensionsScript(); var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp); if (editor.ShowDialog() == true) { foreach (var item in items) SetEditorValue(item, column.ColumnName, editor.Script); } }; } } }