StockTakeViewModel.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. using System;
  2. using System.Linq;
  3. using System.Text;
  4. using System.Threading.Tasks;
  5. using Comal.Classes;
  6. using InABox.Core;
  7. using Xamarin.CommunityToolkit.ObjectModel;
  8. using Xamarin.Forms;
  9. using Xamarin.Forms.Xaml;
  10. namespace PRS.Mobile
  11. {
  12. public enum StockTakeStatus
  13. {
  14. Complete,
  15. InProgress,
  16. NotFound
  17. }
  18. public enum StockTakeResult
  19. {
  20. AllCorrect,
  21. AdjustmentsMade
  22. }
  23. public class StockTakeViewModel : BindableObject
  24. {
  25. public ObservableRangeCollection<StockTransactionImage> Images { get; private set; }
  26. public String Notes { get; set; }
  27. public StockTransactions Transactions { get; private set; }
  28. private StockLocationShell _location;
  29. public StockLocationShell Location
  30. {
  31. get => _location;
  32. set
  33. {
  34. if (Equals(value, _location)) return;
  35. _location = value;
  36. Holdings?.Refresh(true);
  37. CheckTransactions();
  38. OnPropertyChanged();
  39. }
  40. }
  41. public StockHoldingModel Holdings { get; private set; }
  42. public StockTakeViewModel()
  43. {
  44. Images = new();
  45. Notes = "";
  46. Transactions = new StockTransactions();
  47. Transactions.CollectionChanged += (sender, args) =>
  48. {
  49. CheckTransactions();
  50. };
  51. _location = new StockLocationShell();
  52. Holdings = new StockHoldingModel(App.Data,
  53. () => new Filter<StockHolding>(x => x.Location.ID).IsEqualTo(Location.ID));
  54. Holdings.Transactions = Transactions;
  55. }
  56. private StockTakeStatus CalculateStatus()
  57. {
  58. var progress = Holdings.Count(x => x.LastStocktake >= Location.CurrentStocktake) + Transactions.Count;
  59. if (progress == Holdings.Items.Count)
  60. return StockTakeStatus.Complete;
  61. if (progress > 0)
  62. return StockTakeStatus.InProgress;
  63. return StockTakeStatus.NotFound;
  64. }
  65. public StockTakeStatus StocktakeStatus => CalculateStatus();
  66. private StockTakeResult CalculateResult()
  67. {
  68. var adjustments = Transactions.Count(x => !CoreUtils.IsEffectivelyEqual(x.Source.Units, x.Quantity));
  69. return adjustments > 0
  70. ? StockTakeResult.AdjustmentsMade
  71. : StockTakeResult.AllCorrect;
  72. }
  73. public StockTakeResult StocktakeResult => CalculateResult();
  74. public void CheckTransactions()
  75. {
  76. foreach (var transaction in Transactions.Where(x => x != null))
  77. {
  78. if (Location.ID == transaction.Source?.LocationID || Location.ID == transaction.Target?.LocationID)
  79. {
  80. if (!Holdings.Any(
  81. x => x.ProductID == transaction.ProductID
  82. && String.Equals(x.DimensionsUnitSize ?? string.Empty, transaction.DimensionsUnitSize ?? string.Empty)
  83. && (x.LocationID == (transaction.Source?.LocationID ?? Guid.Empty) ||
  84. x.LocationID == (transaction.Target?.LocationID ?? Guid.Empty))
  85. && (x.StyleID == (transaction.Source?.StyleID ?? Guid.Empty) ||
  86. x.StyleID == (transaction.Target?.StyleID ?? Guid.Empty))
  87. && (x.JobID == (transaction.Source?.JobID ?? Guid.Empty) ||
  88. x.JobID == (transaction.Target?.JobID ?? Guid.Empty))
  89. ))
  90. {
  91. var holding = Holdings.CreateItem();
  92. holding.ProductID = transaction.ProductID;
  93. holding.ProductCode = transaction.ProductCode;
  94. holding.ProductName = transaction.ProductName;
  95. Guid imageid = transaction.ImageID;
  96. if (imageid != Guid.Empty && transaction.Image !=null)
  97. Holdings.Images[imageid] = transaction.Image;
  98. holding.ImageID = imageid;
  99. holding.DimensionsUnitID = transaction.DimensionsUnitID;
  100. holding.DimensionsQuantity = transaction.DimensionsQuantity;
  101. holding.DimensionsLength = transaction.DimensionsLength;
  102. holding.DimensionsWidth = transaction.DimensionsWidth;
  103. holding.DimensionsHeight = transaction.DimensionsHeight;
  104. holding.DimensionsWeight = transaction.DimensionsWeight;
  105. holding.DimensionsUnitSize = transaction.DimensionsUnitSize;
  106. holding.DimensionsValue = transaction.DimensionsValue;
  107. holding.LocationID = transaction.Target.LocationID;
  108. holding.LocationCode = transaction.Target.LocationCode ?? string.Empty;
  109. holding.LocationDescription = transaction.Target.LocationDescription ?? string.Empty;
  110. holding.StyleID = transaction.Target.StyleID;
  111. holding.StyleCode = transaction.Target.StyleCode ?? string.Empty;
  112. holding.StyleDescription = transaction.Target.StyleDescription ?? string.Empty;
  113. holding.JobID = transaction.Target.JobID;
  114. holding.JobNumber = transaction.Target.JobNumber ?? string.Empty;
  115. holding.JobName = transaction.Target.JobName ?? string.Empty;
  116. holding.Transactions.Add(transaction);
  117. transaction.Source.Shell = holding;
  118. transaction.Target.Shell = holding;
  119. Holdings.CommitItem(holding);
  120. }
  121. }
  122. }
  123. }
  124. public Task Save(IProgress<String> progress)
  125. {
  126. return Task.Run(() =>
  127. {
  128. if (Location.Entity.IsChanged())
  129. {
  130. progress.Report("Updating Location ");
  131. Location.Save("Updated from Mobile Device");
  132. }
  133. progress.Report("Creating Stock Movement Batch");
  134. var batchModel = new StockMovementBatchModel(App.Data,
  135. () => new Filter<StockMovementBatch>().None());
  136. batchModel.Refresh(false);
  137. var batch = batchModel.AddItem();
  138. batch.Type = StockMovementBatchType.Stocktake;
  139. batch.EmployeeID = App.Data.Me.ID;
  140. StringBuilder sb = new StringBuilder();
  141. if (StocktakeStatus == StockTakeStatus.NotFound)
  142. sb.AppendLine("Location Not Found");
  143. else
  144. {
  145. if (StocktakeStatus == StockTakeStatus.InProgress)
  146. sb.Append("Partially Counted");
  147. else
  148. sb.Append("Stocktake Complete");
  149. if (StocktakeResult == StockTakeResult.AllCorrect)
  150. sb.AppendLine(", All Correct");
  151. else
  152. sb.AppendLine(", Adjustments made");
  153. if (!String.IsNullOrWhiteSpace(Notes.Trim()))
  154. sb.AppendLine(Notes.Trim());
  155. }
  156. // foreach (var holding in Holdings.Items)
  157. // {
  158. // if (!holding.Transactions.Any())
  159. // {
  160. // if (sb.Length != 0)
  161. // sb.AppendLine().AppendLine("The following Products were not checked:");
  162. //
  163. // var comps = new String[]
  164. // {
  165. // holding.ProductDisplay,
  166. // holding.DimensionsUnitSize,
  167. // holding.StyleID != Guid.Empty ? $"/ {holding.StyleDisplay}" : string.Empty,
  168. // holding.JobID != Guid.Empty ? $"(Job: {holding.JobNumber})" : string.Empty,
  169. // $": Qty {holding.Units:F2}"
  170. // };
  171. // sb.AppendLine(String.Join(" ", comps.Where(x => !String.IsNullOrWhiteSpace(x))));
  172. // }
  173. // }
  174. batch.Notes = sb.ToString();
  175. batch.Save("Created on Mobile Device");
  176. progress.Report($"Saving Transactions");
  177. var movementModel = new StockMovementModel(App.Data,
  178. () => new Filter<StockMovement>().None());
  179. movementModel.Refresh(false);
  180. Transactions.ProcessTransactions(movementModel, batch.ID);
  181. progress.Report($"Creating {movementModel.Count()} Movements");
  182. movementModel.Save("Created on Mobile Device");
  183. progress.Report($"Saving {Images.Count} Images");
  184. var batchimageModel = new StockMovementBatchDocumentModel(App.Data,
  185. () => new Filter<StockMovementBatchDocument>().None());
  186. batchimageModel.Refresh(false);
  187. var documentModel = new DocumentModel(App.Data,
  188. () => new Filter<Document>().None());
  189. documentModel.Refresh(false);
  190. int i = 1;
  191. foreach (var image in Images)
  192. {
  193. progress.Report($"Saving {i}/{Images.Count} Images");
  194. var document = documentModel.AddItem();
  195. document.Data = image.Document.Data;
  196. document.FileName = image.Document.FileName;
  197. document.CRC = CoreUtils.CalculateCRC(image.Document.Data);
  198. document.TimeStamp = DateTime.Now;
  199. document.Save("Created on MobileDevice");
  200. var batchImage = batchimageModel.AddItem();
  201. {
  202. batchImage.BatchID = batch.ID;
  203. batchImage.DocumentID = document.ID;
  204. batchImage.Thumbnail = image.Thumbnail;
  205. }
  206. }
  207. progress.Report($"Updating Thumbnails");
  208. batchimageModel.Save("Created on Mobile Device");
  209. progress.Report($"Updating Stocktake Status");
  210. if (StocktakeStatus == StockTakeStatus.Complete && !Location.CurrentStocktake.IsEmpty())
  211. {
  212. Location.CurrentStocktake = DateTime.MinValue;
  213. Location.LastStocktake = DateTime.Now;
  214. Location.Save("StockTake Completed on mobile device");
  215. }
  216. });
  217. }
  218. }
  219. }