FactoryPackGrid.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using System.Threading.Tasks;
  6. using System.Windows;
  7. using System.Windows.Controls;
  8. using System.Windows.Data;
  9. using System.Windows.Media;
  10. using Comal.Classes;
  11. using InABox.Clients;
  12. using InABox.Configuration;
  13. using InABox.Core;
  14. using InABox.DynamicGrid;
  15. using InABox.WPF;
  16. using Document = InABox.Core.Document;
  17. namespace PRSDesktop;
  18. public class FactoryPackGrid : DynamicDataGrid<StockHolding>
  19. {
  20. public StockArea? Area { get; set; }
  21. public ManufacturingPacket? CurrentPacket { get; set; }
  22. private readonly Dictionary<Guid, byte[]> _images = new();
  23. private readonly DynamicActionColumn _holding;
  24. public FactoryPackGrid()
  25. {
  26. HiddenColumns.Add(x=>x.ID);
  27. HiddenColumns.Add(x=>x.Product.ID);
  28. HiddenColumns.Add(x=>x.Product.Code);
  29. HiddenColumns.Add(x=>x.Product.Name);
  30. HiddenColumns.Add(x=>x.Product.Image.ID);
  31. HiddenColumns.Add(x => x.Dimensions.Unit.ID);
  32. HiddenColumns.Add(x => x.Dimensions.Unit.Formula);
  33. HiddenColumns.Add(x => x.Dimensions.Unit.Format);
  34. HiddenColumns.Add(x=>x.Dimensions.Height);
  35. HiddenColumns.Add(x=>x.Dimensions.Width);
  36. HiddenColumns.Add(x=>x.Dimensions.Length);
  37. HiddenColumns.Add(x=>x.Dimensions.Weight);
  38. HiddenColumns.Add(x=>x.Dimensions.Quantity);
  39. HiddenColumns.Add(x=>x.Dimensions.Value);
  40. HiddenColumns.Add(x=>x.Dimensions.UnitSize);
  41. HiddenColumns.Add(x=>x.Style.ID);
  42. HiddenColumns.Add(x=>x.Style.Code);
  43. HiddenColumns.Add(x=>x.Style.Description);
  44. HiddenColumns.Add(x => x.Job.ID);
  45. HiddenColumns.Add(x=>x.Job.JobNumber);
  46. HiddenColumns.Add(x=>x.Job.Name);
  47. HiddenColumns.Add(x=>x.Location.ID);
  48. HiddenColumns.Add(x=>x.Location.Code);
  49. HiddenColumns.Add(x=>x.Location.Description);
  50. HiddenColumns.Add(x=>x.Units);
  51. HiddenColumns.Add(x => x.Available);
  52. _holding = new DynamicTemplateColumn(PackTemplate) { Width = 0 };
  53. ActionColumns.Add(_holding);
  54. }
  55. protected override void DoReconfigure(FluentList<DynamicGridOption> options)
  56. {
  57. base.DoReconfigure(options);
  58. options.Clear();
  59. RowHeight = 85;
  60. }
  61. protected override DynamicGridColumns LoadColumns()
  62. {
  63. var columns = new DynamicGridColumns();
  64. return columns;
  65. }
  66. protected override void Reload(Filters<StockHolding> criteria, Columns<StockHolding> columns, ref SortOrder<StockHolding>? sort, Action<CoreTable?, Exception?> action)
  67. {
  68. Task<CoreTable> imagetask = Task.Run(() =>
  69. {
  70. if (Area == null)
  71. return new Client<Document>().Query(new Filter<Document>().None());
  72. return new Client<Document>().Query(
  73. new Filter<Document>(x => x.ID)
  74. .InQuery(new Filter<StockHolding>(x => x.Location.Area.ID).IsEqualTo(Area.ID),
  75. x => x.Product.Image.ID),
  76. new Columns<Document>(x => x.ID).Add(x => x.Data)
  77. );
  78. });
  79. criteria.Add(Area == null
  80. ? new Filter<StockHolding>().None()
  81. : new Filter<StockHolding>(x => x.Location.Area.ID).IsEqualTo(Area.ID));
  82. base.Reload(criteria, columns, ref sort, (o,e) =>
  83. {
  84. if (o != null)
  85. {
  86. Task.WaitAll(imagetask);
  87. _images.Clear();
  88. imagetask.Result.LoadDictionary<Document, Guid, byte[]>(
  89. _images,
  90. x => x.ID,
  91. x => x.Data
  92. );
  93. }
  94. action(o,e);
  95. });
  96. }
  97. private void CreateStackPanel(Grid grid, int row, int column, FontWeight weight,
  98. params Expression<Func<StockHolding, object>>[] bindings)
  99. {
  100. StackPanel result = new StackPanel()
  101. {
  102. Orientation = Orientation.Horizontal,
  103. HorizontalAlignment = HorizontalAlignment.Center,
  104. };
  105. result.SetValue(Grid.RowProperty,row);
  106. result.SetValue(Grid.ColumnProperty,column);
  107. foreach (var binding in bindings)
  108. {
  109. Label label = new Label()
  110. {
  111. Margin = new Thickness(5,0,0,0),
  112. VerticalContentAlignment = VerticalAlignment.Center,
  113. FontWeight = weight
  114. };
  115. label.Bind(Label.ContentProperty,binding);
  116. result.Children.Add(label);
  117. }
  118. grid.Children.Add(result);
  119. }
  120. private FrameworkElement PackTemplate()
  121. {
  122. var grid = new Grid();
  123. grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
  124. grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
  125. grid.RowDefinitions.Add(new RowDefinition() { Height = new GridLength(1, GridUnitType.Star) });
  126. grid.ColumnDefinitions.Add(new ColumnDefinition() { Width= new GridLength(28, GridUnitType.Pixel)});
  127. grid.ColumnDefinitions.Add(new ColumnDefinition() { Width= new GridLength(1, GridUnitType.Pixel)});
  128. grid.ColumnDefinitions.Add(new ColumnDefinition() { Width= new GridLength(90, GridUnitType.Pixel)});
  129. grid.ColumnDefinitions.Add(new ColumnDefinition() { Width= new GridLength(1, GridUnitType.Star)});
  130. grid.ColumnDefinitions.Add(new ColumnDefinition() { Width= new GridLength(70, GridUnitType.Pixel)});
  131. Label label = new Label()
  132. {
  133. HorizontalAlignment = HorizontalAlignment.Center,
  134. VerticalAlignment = VerticalAlignment.Center,
  135. LayoutTransform = new RotateTransform(-90)
  136. };
  137. label.Bind<StockHolding,String>(Label.ContentProperty, x=>x.Location.Code);
  138. label.SetValue(Grid.RowProperty,0);
  139. label.SetValue(Grid.RowSpanProperty,3);
  140. label.SetValue(Grid.ColumnProperty,0);
  141. grid.Children.Add(label);
  142. Border border = new Border()
  143. {
  144. BorderThickness = new Thickness(0.75),
  145. BorderBrush = new SolidColorBrush(Colors.DarkGray)
  146. };
  147. border.SetValue(Grid.RowProperty,0);
  148. border.SetValue(Grid.RowSpanProperty,3);
  149. border.SetValue(Grid.ColumnProperty,1);
  150. grid.Children.Add(border);
  151. Image image = new Image()
  152. {
  153. Stretch = Stretch.Uniform,
  154. Margin=new Thickness(5)
  155. };
  156. image.Bind<StockHolding,Guid>(Image.SourceProperty, x => x.Product.Image.ID, new GuidToImageSourceConverter() { Images = _images});
  157. image.SetValue(Grid.RowProperty,0);
  158. image.SetValue(Grid.RowSpanProperty,3);
  159. image.SetValue(Grid.ColumnProperty,2);
  160. grid.Children.Add(image);
  161. CreateStackPanel(grid, 0, 3, FontWeights.Bold, x => x.Product.Code, x => x.Product.Name);
  162. CreateStackPanel(grid, 1, 3, FontWeights.Normal, x => x.Dimensions.UnitSize, x => x.Style.Code, x=>x.Style.Description);
  163. CreateStackPanel(grid, 2, 3, FontWeights.Normal, x => x.Job.JobNumber, x => x.Job.Name);
  164. StackPanel buttonContent = new StackPanel()
  165. {
  166. Orientation = Orientation.Vertical,
  167. VerticalAlignment = VerticalAlignment.Center,
  168. HorizontalAlignment = HorizontalAlignment.Center
  169. };
  170. Label qtyLbl = new Label()
  171. {
  172. FontSize = 16,
  173. FontWeight = FontWeights.Bold,
  174. HorizontalAlignment = HorizontalAlignment.Center
  175. };
  176. qtyLbl.Bind<StockHolding, double>(Label.ContentProperty, x => x.Units, null, BindingMode.Default, "{0:F2}");
  177. buttonContent.Children.Add(qtyLbl);
  178. Label useLbl = new Label()
  179. {
  180. HorizontalAlignment = HorizontalAlignment.Center,
  181. Content = "Use"
  182. };
  183. buttonContent.Children.Add(useLbl);
  184. Button button = new Button()
  185. {
  186. Margin = new Thickness(5),
  187. Content = buttonContent
  188. };
  189. button.Bind<StockHolding,Guid>(Image.SourceProperty, x => x.ID);
  190. button.SetValue(Grid.RowProperty,0);
  191. button.SetValue(Grid.RowSpanProperty,4);
  192. button.SetValue(Grid.ColumnProperty,4);
  193. button.Click += UseStock;
  194. grid.Children.Add(button);
  195. return grid;
  196. }
  197. private class FactoryFloorReturn : BaseObject
  198. {
  199. [EditorSequence(1)]
  200. public ProductLink Product { get; set; }
  201. [EditorSequence(2)]
  202. public ProductStyleLink Style { get; set; }
  203. [EditorSequence(3)]
  204. [DimensionsEditor(typeof(StockDimensions), AllowEditingUnit = false)]
  205. public StockDimensions Dimensions { get; set;}
  206. [EditorSequence(4)]
  207. public double Qty { get; set; }
  208. [EditorSequence(5)]
  209. public JobLink Job { get; set; }
  210. }
  211. private class FactoryFloorIssue : BaseObject
  212. {
  213. public JobRequisitionItem[]? RequisitionItems { get; init; }
  214. [EditorSequence(1)]
  215. [ComboLookupEditor(typeof(RequisitionIDGenerator))]
  216. public Guid RequisitionID { get; set; }
  217. private class RequisitionIDGenerator : LookupGenerator<FactoryFloorIssue>
  218. {
  219. public RequisitionIDGenerator(FactoryFloorIssue[] items) : base(items)
  220. {
  221. }
  222. protected override void DoGenerateLookups()
  223. {
  224. var items = Items?.FirstOrDefault()?.RequisitionItems;
  225. if (items == null)
  226. return;
  227. foreach (var item in items)
  228. {
  229. AddValue(item.ID,
  230. item.ID == Guid.Empty
  231. ? $"{item.Requisition.Description}"
  232. : $"{item.Requisition.Number}: {item.Requisition.Description}");
  233. }
  234. }
  235. }
  236. [EditorSequence(2)]
  237. public double Qty { get; set; }
  238. }
  239. public void ReplaceStock()
  240. {
  241. var holdingrow = SelectedRows.FirstOrDefault();
  242. var receive = holdingrow == null
  243. ? new StockMovement()
  244. : holdingrow.ToObject<StockHolding>().CreateMovement();
  245. receive.Type = StockMovementType.Receive;
  246. receive.Date = DateTime.Now;
  247. receive.Employee.ID = App.EmployeeID;
  248. receive.Notes = "Returned from Manufacturing";
  249. receive.Received = 1.0F;
  250. var smg = new DynamicDataGrid<StockMovement>();
  251. smg.OnCustomiseEditor += StockMovementCustomiseEditor;
  252. smg.OnValidate += StockMovementValidate;
  253. smg.OnEditorValueChanged += StockMovementValueChanged;
  254. if (smg.EditItems(new StockMovement[] { receive }))
  255. {
  256. receive.Batch.ID = CheckStockMovementBatch();
  257. new Client<StockMovement>().Save(receive,"Issued Stock to Factory Floor");
  258. Refresh(false,true);
  259. }
  260. }
  261. private Dictionary<string, object?> StockMovementValueChanged(IDynamicEditorForm form, string name, object value)
  262. {
  263. var result = new Dictionary<string, object?>();
  264. if (name.Equals("Location.Job.ID"))
  265. {
  266. var editor = form.FindEditor("Job.ID");
  267. if (!value.Equals(Guid.Empty))
  268. result = DynamicGridUtils.UpdateEditorValue(form.Items, "Job.ID", value);
  269. else
  270. foreach (StockMovement item in form.Items)
  271. result = DynamicGridUtils.UpdateEditorValue(new[] { item }, "Job.ID",
  272. item.Job.HasOriginalValue("ID") ? item.Job.GetOriginalValue(x => x.ID) : item.Job.ID);
  273. editor.IsEnabled = value.Equals(Guid.Empty);
  274. }
  275. return result;
  276. }
  277. private void StockMovementValidate(object sender, StockMovement[] items, List<string> errors)
  278. {
  279. if (items.Any(x => x.Product.ID == Guid.Empty))
  280. errors.Add("Product may not be blank");
  281. if (items.Any(x => x.Location.ID == Guid.Empty))
  282. errors.Add("Location may not be blank");
  283. if (items.Any(x => x is { Received: 0, Issued: 0 }))
  284. errors.Add("Quantity may not be zero");
  285. }
  286. private void StockMovementCustomiseEditor(IDynamicEditorForm sender, StockMovement[]? items, DynamicGridColumn column, BaseEditor editor)
  287. {
  288. if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,double>(x=>x.Cost,".")))
  289. editor.Editable = Editable.Hidden;
  290. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,DateTime>(x=>x.Date,".")))
  291. editor.Editable = Editable.Hidden;
  292. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,double>(x=>x.Issued,".")))
  293. editor.Editable = Editable.Hidden;
  294. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,Guid>(x=>x.Employee.ID,".")))
  295. editor.Editable = Editable.Hidden;
  296. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,String>(x=>x.Notes,".")))
  297. editor.Editable = Editable.Hidden;
  298. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,Guid>(x=>x.Location.ID,".")))
  299. editor.EditorSequence = 1;
  300. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,Guid>(x=>x.Product.ID,".")))
  301. editor.EditorSequence = 2;
  302. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,Guid>(x=>x.Style.ID,".")))
  303. editor.EditorSequence = 3;
  304. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,Guid>(x=>x.Job.ID,".")))
  305. editor.EditorSequence = 4;
  306. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement,StockDimensions>(x=>x.Dimensions,".")))
  307. editor.EditorSequence = 5;
  308. else if (column.ColumnName.Equals(CoreUtils.GetFullPropertyName<StockMovement, double>(x => x.Received, ".")))
  309. {
  310. editor.EditorSequence = 6;
  311. editor.Caption = "Quantity";
  312. }
  313. }
  314. private static Guid CheckStockMovementBatch()
  315. {
  316. var _settings = new LocalConfiguration<FactoryFloorLocalSettings>().Load();
  317. if (_settings.CurrentBatchDate != DateTime.Today)
  318. {
  319. StockMovementBatch batch = new StockMovementBatch
  320. {
  321. Type = StockMovementBatchType.Issue,
  322. TimeStamp = DateTime.Now,
  323. Notes = "Manufacturing Consumption"
  324. };
  325. batch.Employee.ID = App.EmployeeID;
  326. new Client<StockMovementBatch>().Save(batch,"Created on Factory Floor Screen");
  327. _settings.CurrentBatchDate = DateTime.Today;
  328. _settings.CurrentBatchID = batch.ID;
  329. new LocalConfiguration<FactoryFloorLocalSettings>().Save(_settings);
  330. }
  331. return _settings.CurrentBatchID;
  332. }
  333. private void UseStock(object sender, RoutedEventArgs e)
  334. {
  335. if (CurrentPacket == null)
  336. {
  337. MessageBox.Show("Please select a Manufacturing Packet before proceeding");
  338. return;
  339. }
  340. var holdingrow = SelectedRows.FirstOrDefault();
  341. if (holdingrow == null)
  342. {
  343. MessageBox.Show("Please select a Stock Holding before proceeding");
  344. return;
  345. }
  346. var holding = holdingrow.ToObject<StockHolding>();
  347. var requiitems = holding.LoadRequisitionItems().ToArray();
  348. var ffi = new FactoryFloorIssue()
  349. {
  350. RequisitionItems = requiitems,
  351. Qty = 1,
  352. RequisitionID = requiitems.FirstOrDefault()?.ID ?? Guid.Empty
  353. };
  354. var ffg = new DynamicItemsListGrid<FactoryFloorIssue>();
  355. ffg.OnValidate += (_, _, errors) =>
  356. {
  357. var id = ffi.RequisitionID;
  358. var requi = ffi.RequisitionItems.FirstOrDefault(x => x.ID == id);
  359. if (requi == null)
  360. errors.Add("Please select an allocation!");
  361. else if (ffi.Qty > requi.Qty)
  362. errors.Add($"Qty must not exceed {requi.Qty}");
  363. };
  364. if (!ffg.EditItems(new[] { ffi }))
  365. return;
  366. var batchid = CheckStockMovementBatch();
  367. List<StockMovement> updates = new();
  368. Guid transactionid = Guid.NewGuid();
  369. if (CurrentPacket.SetoutLink.JobLink.ID != holding.Job.ID)
  370. {
  371. var transferout = holding.CreateMovement();
  372. transferout.JobRequisitionItem.ID = ffi.RequisitionID;
  373. transferout.Batch.ID = batchid;
  374. transferout.Type = StockMovementType.TransferOut;
  375. transferout.Issued = ffi.Qty;
  376. transferout.Transaction = transactionid;
  377. transferout.System = true;
  378. transferout.Date = DateTime.Now;
  379. transferout.Employee.ID = App.EmployeeID;
  380. updates.Add(transferout);
  381. var transferin = holding.CreateMovement();
  382. transferin.JobRequisitionItem.ID = ffi.RequisitionID;
  383. transferin.Batch.ID = batchid;
  384. transferin.Type = StockMovementType.TransferIn;
  385. transferin.Received = ffi.Qty;
  386. transferin.Job.ID = CurrentPacket.SetoutLink.JobLink.ID;
  387. transferin.Job.Synchronise(CurrentPacket.SetoutLink.JobLink);
  388. transferin.Transaction = transactionid;
  389. transferin.Date = DateTime.Now;
  390. transferin.System = true;
  391. transferin.Employee.ID = App.EmployeeID;
  392. updates.Add(transferin);
  393. }
  394. var issue = holding.CreateMovement();
  395. issue.JobRequisitionItem.ID = ffi.RequisitionID;
  396. issue.Batch.ID = batchid;
  397. issue.Type = StockMovementType.Issue;
  398. issue.Issued = ffi.Qty;
  399. issue.Job.ID = CurrentPacket.SetoutLink.JobLink.ID;
  400. issue.Job.Synchronise(CurrentPacket.SetoutLink.JobLink);
  401. issue.Transaction = transactionid;
  402. issue.Date = DateTime.Now;
  403. issue.Employee.ID = App.EmployeeID;
  404. issue.Notes = "Issued to Manufacturing";
  405. updates.Add(issue);
  406. new Client<StockMovement>().Save(updates,"Issued Stock to Factory Floor", (_,_) => { });
  407. if (issue.JobRequisitionItem.ID == Guid.Empty)
  408. UpdateRow<StockHolding, double>(holdingrow, x=>x.Available, holding.Available - ffi.Qty,false);
  409. UpdateRow<StockHolding,double>(holdingrow, x => x.Units, holding.Units - ffi.Qty);
  410. }
  411. }