ProductDimensionUnitGrid.cs 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275
  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 = Filter<StockMovement>.Where(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. if (mvt.Received > 0)
  110. mvt.ConvertDimensions(
  111. x=>x.Dimensions,
  112. x=>x.Received,
  113. x=>x.Cost,
  114. Client<ProductDimensionUnit>.Provider
  115. );
  116. else if (mvt.Issued > 0)
  117. mvt.ConvertDimensions(
  118. x=>x.Dimensions,
  119. x=>x.Issued,
  120. x=>x.Cost,
  121. Client<ProductDimensionUnit>.Provider
  122. );
  123. if (mvt.IsChanged())
  124. {
  125. results.Add(mvt);
  126. nResult++;
  127. }
  128. nProcessed++;
  129. }
  130. Client.Save(results, "Updated conversion script.");
  131. }
  132. nMovements = nResult;
  133. }
  134. catch(Exception e)
  135. {
  136. exception = e;
  137. }
  138. });
  139. if(nMovements is not null)
  140. {
  141. MessageWindow.ShowMessage($"Update successful: {nMovements.Value} movements updated.", "Success");
  142. }
  143. else if(exception is not null)
  144. {
  145. MessageWindow.ShowError("Error while updating dimensions", exception);
  146. }
  147. }
  148. private static void UpdateExpressions(ProductDimensionUnit[] items)
  149. {
  150. Dictionary<Type, int>? results = null;
  151. Exception? exception = null;
  152. Progress.ShowModal("Updating Dimensions", progress =>
  153. {
  154. try
  155. {
  156. results = DimensionUnitUtils.UpdateExpressions<ProductDimensionUnit, ProductDimensionUnitLink>(items, progress);
  157. }
  158. catch(Exception e)
  159. {
  160. exception = e;
  161. }
  162. });
  163. if(results is not null)
  164. {
  165. MessageWindow.ShowMessage($"Update successful:\n{string.Join("\n", results.Select(x => $"- {x.Key.Name}: {x.Value} items updated"))}", "Success");
  166. }
  167. else if(exception is not null)
  168. {
  169. MessageWindow.ShowError("Error while updating dimensions", exception);
  170. }
  171. }
  172. private bool ShouldUpdateExpressions = false;
  173. protected override void DoBeforeSave(IDynamicEditorForm editor, ProductDimensionUnit[] items)
  174. {
  175. base.DoBeforeSave(editor, items);
  176. ShouldUpdateExpressions = false;
  177. if(items.Any(x => x.HasOriginalValue(x => x.Format) || x.HasOriginalValue(x => x.Formula)))
  178. {
  179. 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?"))
  180. {
  181. ShouldUpdateExpressions = true;
  182. }
  183. }
  184. }
  185. protected override void DoAfterSave(IDynamicEditorForm editor, ProductDimensionUnit[] items)
  186. {
  187. base.DoAfterSave(editor, items);
  188. if (ShouldUpdateExpressions)
  189. {
  190. UpdateExpressions(items);
  191. }
  192. }
  193. protected override bool DoMerge(CoreRow[] rows)
  194. {
  195. var columns = new Column<ProductDimensionUnit>[]
  196. {
  197. new(x => x.HasLength),
  198. new(x => x.HasQuantity),
  199. new(x => x.HasHeight),
  200. new(x => x.HasWeight),
  201. new(x => x.HasWidth),
  202. };
  203. var target = rows[^1].ToObject<ProductDimensionUnit>();
  204. if(columns.Any(c => rows.Select(r => r[c.Property]).Distinct().Skip(1).Any()))
  205. {
  206. 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);
  207. return false;
  208. }
  209. if (base.DoMerge(rows))
  210. {
  211. if(MessageWindow.ShowYesNo(
  212. $"Do you wish to update the UnitSize/Value for every item that uses {target.Code}? (This may take a while)",
  213. "Update Expressions?"))
  214. {
  215. UpdateExpressions([target]);
  216. }
  217. return true;
  218. }
  219. return false;
  220. }
  221. protected override void CustomiseEditor(IDynamicEditorForm form, ProductDimensionUnit[] items, DynamicGridColumn column, BaseEditor editor)
  222. {
  223. base.CustomiseEditor(form, items, column, editor);
  224. if (column.ColumnName == nameof(ProductDimensionUnit.Conversion) && editor is ScriptEditor scriptEditor)
  225. {
  226. scriptEditor.Type = ScriptEditorType.TemplateEditor;
  227. scriptEditor.OnEditorClicked += () =>
  228. {
  229. var script = items.FirstOrDefault()?.Conversion.NotWhiteSpaceOr()
  230. ?? DimensionUnit.DefaultConvertDimensionsScript();
  231. var editor = new ScriptEditorWindow(script, SyntaxLanguage.CSharp);
  232. if (editor.ShowDialog() == true)
  233. {
  234. foreach (var item in items)
  235. SetEditorValue(item, column.ColumnName, editor.Script);
  236. }
  237. };
  238. }
  239. }
  240. }