ProductDimensionUnitGrid.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Windows;
  5. using System.Windows.Controls;
  6. using System.Windows.Media.Imaging;
  7. using Comal.Classes;
  8. using InABox.Clients;
  9. using InABox.Core;
  10. using InABox.DynamicGrid;
  11. using InABox.Wpf;
  12. using InABox.WPF;
  13. using PRSDimensionUtils;
  14. namespace PRSDesktop.Grids;
  15. public class ProductDimensionUnitGrid : DynamicDataGrid<ProductDimensionUnit>
  16. {
  17. private static readonly BitmapImage CONVERT = PRSDesktop.Resources.specifications.AsBitmapImage();
  18. private Button ConvertButton = null!;
  19. protected override void Init()
  20. {
  21. base.Init();
  22. HiddenColumns.Add(x => x.Code);
  23. HiddenColumns.Add(x => x.HasQuantity);
  24. HiddenColumns.Add(x => x.HasLength);
  25. HiddenColumns.Add(x => x.HasWeight);
  26. HiddenColumns.Add(x => x.HasWidth);
  27. HiddenColumns.Add(x => x.HasHeight);
  28. HiddenColumns.Add(x =>x.Conversion);
  29. ActionColumns.Add(new DynamicImageColumn(ConvertImage) { Position = DynamicActionColumnPosition.End});
  30. AddButton("Update Expressions", null, (button, rows) =>
  31. {
  32. UpdateExpressions(rows.ToArray<ProductDimensionUnit>());
  33. return false;
  34. });
  35. ConvertButton = AddButton("Convert Movements", null, (button, rows) =>
  36. {
  37. UpdateConversion(rows.ToArray<ProductDimensionUnit>());
  38. return false;
  39. });
  40. }
  41. protected override void SelectItems(CoreRow[]? rows)
  42. {
  43. base.SelectItems(rows);
  44. if(!(rows?.FirstOrDefault()?.Get<ProductDimensionUnit, string>(x => x.Conversion)).IsNullOrWhiteSpace())
  45. {
  46. ConvertButton.Visibility = Visibility.Visible;
  47. }
  48. else
  49. {
  50. ConvertButton.Visibility = Visibility.Collapsed;
  51. }
  52. }
  53. private BitmapImage? ConvertImage(CoreRow? row)
  54. {
  55. return row == null
  56. ? CONVERT
  57. : String.IsNullOrWhiteSpace(row.Get<ProductDimensionUnit, string>(x => x.Conversion))
  58. ? null
  59. : CONVERT;
  60. }
  61. protected override void DoValidate(ProductDimensionUnit[] items, List<string> errors)
  62. {
  63. base.DoValidate(items, errors);
  64. foreach (var item in items)
  65. item.Validate(errors);
  66. }
  67. private static void UpdateConversion(ProductDimensionUnit[] items)
  68. {
  69. int? nMovements = null;
  70. Exception? exception = null;
  71. var filter = new Filter<StockMovement>(x => x.Dimensions.Unit.ID).InList(items.ToArray(x => x.ID));
  72. var columns = Columns.Required<StockMovement>()
  73. .Add(x => x.Units)
  74. .Add(x => x.Received)
  75. .Add(x => x.Issued)
  76. .AddDimensionsColumns(x => x.Dimensions, Dimensions.ColumnsType.Local);
  77. var nTotal = Client.Query(filter, Columns.None<StockMovement>().Add(x => x.ID)).Rows.Count;
  78. 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"))
  79. {
  80. return;
  81. }
  82. Progress.ShowModal("Updating Dimensions", progress =>
  83. {
  84. try
  85. {
  86. progress.Report($"Updating Stock Movements");
  87. var nProcessed = 0;
  88. var nResult = 0;
  89. var done = false;
  90. var percentStep = Math.Max(nTotal / 100, 1);
  91. var range = CoreRange.Database(1000);
  92. while(nProcessed < nTotal && !done)
  93. {
  94. var rows = Client.Query(filter, columns, range: range).Rows;
  95. if (rows.Count == 0) break;
  96. if(rows.Count < 1000)
  97. {
  98. done = true;
  99. }
  100. range.Next();
  101. var results = new List<StockMovement>(rows.Count);
  102. for(int i = 0; i < rows.Count; ++i)
  103. {
  104. if(nProcessed % percentStep == 0)
  105. {
  106. progress.Report($"Updating Stock Movements: {(double)nProcessed / (double)nTotal * 100:F0}%");
  107. }
  108. var mvt = rows[i].ToObject<StockMovement>();
  109. var qty = DimensionUtils.ConvertDimensions(mvt.Dimensions, mvt.Units, Client<ProductDimensionUnit>.Provider);
  110. if(qty >= 0)
  111. {
  112. mvt.Received = qty;
  113. mvt.Issued = 0;
  114. }
  115. else
  116. {
  117. mvt.Received = 0;
  118. mvt.Issued = -qty;
  119. }
  120. if (mvt.IsChanged())
  121. {
  122. results.Add(mvt);
  123. nResult++;
  124. }
  125. nProcessed++;
  126. }
  127. Client.Save(results, "Updated conversion script.");
  128. }
  129. nMovements = nResult;
  130. }
  131. catch(Exception e)
  132. {
  133. exception = e;
  134. }
  135. });
  136. if(nMovements is not null)
  137. {
  138. MessageWindow.ShowMessage($"Update successful: {nMovements.Value} movements updated.", "Success");
  139. }
  140. else if(exception is not null)
  141. {
  142. MessageWindow.ShowError("Error while updating dimensions", exception);
  143. }
  144. }
  145. private static void UpdateExpressions(ProductDimensionUnit[] items)
  146. {
  147. Dictionary<Type, int>? results = null;
  148. Exception? exception = null;
  149. Progress.ShowModal("Updating Dimensions", progress =>
  150. {
  151. try
  152. {
  153. results = DimensionUnitUtils.UpdateExpressions<ProductDimensionUnit, ProductDimensionUnitLink>(items, progress);
  154. }
  155. catch(Exception e)
  156. {
  157. exception = e;
  158. }
  159. });
  160. if(results is not null)
  161. {
  162. MessageWindow.ShowMessage($"Update successful:\n{string.Join("\n", results.Select(x => $"- {x.Key.Name}: {x.Value} items updated"))}", "Success");
  163. }
  164. else if(exception is not null)
  165. {
  166. MessageWindow.ShowError("Error while updating dimensions", exception);
  167. }
  168. }
  169. private bool ShouldUpdateExpressions = false;
  170. protected override void DoBeforeSave(IDynamicEditorForm editor, ProductDimensionUnit[] items)
  171. {
  172. base.DoBeforeSave(editor, items);
  173. ShouldUpdateExpressions = false;
  174. if(items.Any(x => x.HasOriginalValue(x => x.Format) || x.HasOriginalValue(x => x.Formula)))
  175. {
  176. 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?"))
  177. {
  178. ShouldUpdateExpressions = true;
  179. }
  180. }
  181. }
  182. protected override void DoAfterSave(IDynamicEditorForm editor, ProductDimensionUnit[] items)
  183. {
  184. base.DoAfterSave(editor, items);
  185. if (ShouldUpdateExpressions)
  186. {
  187. UpdateExpressions(items);
  188. }
  189. }
  190. protected override bool DoMerge(CoreRow[] rows)
  191. {
  192. var columns = new Column<ProductDimensionUnit>[]
  193. {
  194. new(x => x.HasLength),
  195. new(x => x.HasQuantity),
  196. new(x => x.HasHeight),
  197. new(x => x.HasWeight),
  198. new(x => x.HasWidth),
  199. };
  200. var target = rows[^1].ToObject<ProductDimensionUnit>();
  201. if(columns.Any(c => rows.Select(r => r[c.Property]).Distinct().Skip(1).Any()))
  202. {
  203. 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);
  204. return false;
  205. }
  206. if (base.DoMerge(rows))
  207. {
  208. if(MessageWindow.ShowYesNo(
  209. $"Do you wish to update the UnitSize/Value for every item that uses {target.Code}? (This may take a while)",
  210. "Update Expressions?"))
  211. {
  212. UpdateExpressions([target]);
  213. }
  214. return true;
  215. }
  216. return false;
  217. }
  218. protected override void CustomiseEditor(IDynamicEditorForm form, ProductDimensionUnit[] items, DynamicGridColumn column, BaseEditor editor)
  219. {
  220. base.CustomiseEditor(form, items, column, editor);
  221. if (column.ColumnName == nameof(ProductDimensionUnit.Conversion) && editor is ScriptEditor scriptEditor)
  222. {
  223. scriptEditor.Type = ScriptEditorType.TemplateEditor;
  224. scriptEditor.OnEditorClicked += () =>
  225. {
  226. var script = items.FirstOrDefault()?.Conversion.NotWhiteSpaceOr()
  227. ?? DimensionUnit.DefaultConvertDimensionsScript();
  228. var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp);
  229. if (editor.ShowDialog() == true)
  230. {
  231. foreach (var item in items)
  232. SetEditorValue(item, column.ColumnName, editor.Script);
  233. }
  234. };
  235. }
  236. }
  237. }