JobRequisitionItemStore.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. using Comal.Classes;
  2. using Comal.Stores;
  3. using InABox.Core;
  4. using InABox.Database;
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. namespace PRSStores;
  9. public enum JobRequisitionItemAction
  10. {
  11. Created,
  12. Updated,
  13. Deleted,
  14. None
  15. }
  16. public class JobRequisitionItemStore : BaseStore<JobRequisitionItem>
  17. {
  18. protected override void BeforeSave(JobRequisitionItem item)
  19. {
  20. if (item.ID != Guid.Empty)
  21. {
  22. if (CalculateStatus(this, item))
  23. item.Notes += $"{(!String.IsNullOrWhiteSpace(item.Notes) ? "\n" : "")}Status updated to {item.Status.ToString().SplitCamelCase()} because the record was changed";
  24. }
  25. base.BeforeSave(item);
  26. }
  27. public static Columns<JobRequisitionItem> StatusRequiredColumns()
  28. {
  29. return new Columns<JobRequisitionItem>(
  30. x => x.ID,
  31. x => x.Archived,
  32. x => x.Cancelled,
  33. x => x.OrderRequired,
  34. x => x.Status,
  35. x => x.Style.ID,
  36. x => x.Product.ID,
  37. x => x.Qty,
  38. x => x.Notes);
  39. }
  40. /// <summary>
  41. /// Ensure that the columns of <paramref name="item"/> match <see cref="StatusRequiredColumns"/>.
  42. /// </summary>
  43. /// <param name="store"></param>
  44. /// <param name="item"></param>
  45. /// <returns></returns>
  46. public static bool CalculateStatus(IStore store, JobRequisitionItem item)
  47. {
  48. if (item.Archived != DateTime.MinValue)
  49. item.Status = JobRequisitionItemStatus.Archived;
  50. else if (item.Cancelled != DateTime.MinValue)
  51. item.Status = JobRequisitionItemStatus.Cancelled;
  52. else
  53. {
  54. var stockMovements = store.Provider
  55. .Query(
  56. new Filter<StockMovement>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID).And(x=>x.Type).IsNotEqualTo(StockMovementType.Issue),
  57. new Columns<StockMovement>(x => x.Units)
  58. .Add(x => x.Style.ID)
  59. .Add(x=>x.Type)
  60. )
  61. .ToObjects<StockMovement>();
  62. var styleTotal = 0.0;
  63. var total = 0.0;
  64. foreach (var mvt in stockMovements)
  65. {
  66. if (mvt.Style.ID == item.Style.ID)
  67. {
  68. styleTotal += mvt.Units;
  69. }
  70. total += mvt.Units;
  71. }
  72. var remStyle = item.Qty - styleTotal;
  73. var remTotal = item.Qty - total;
  74. if (remStyle <= 0)
  75. item.Status = JobRequisitionItemStatus.Allocated;
  76. else if (remTotal <= 0)
  77. {
  78. // Find all unreceived POItems for this guy that are treatments (i.e., wrong product ID).
  79. var jriPois = store.Provider.Query(
  80. new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID)
  81. .And(x => x.PurchaseOrderItem.ReceivedDate).IsEqualTo(DateTime.MinValue)
  82. .And(x => x.PurchaseOrderItem.Product.ID).IsNotEqualTo(item.Product.ID),
  83. new Columns<JobRequisitionItemPurchaseOrderItem>(x => x.ID));
  84. if (jriPois.Rows.Count > 0)
  85. item.Status = JobRequisitionItemStatus.TreatmentOnOrder;
  86. else
  87. item.Status = JobRequisitionItemStatus.TreatmentRequired;
  88. }
  89. else
  90. {
  91. // Find all unreceived POItems for this guy.
  92. var jriPois = store.Provider.Query(
  93. new Filter<JobRequisitionItemPurchaseOrderItem>(x => x.JobRequisitionItem.ID).IsEqualTo(item.ID)
  94. .And(x => x.PurchaseOrderItem.ReceivedDate).IsEqualTo(DateTime.MinValue),
  95. new Columns<JobRequisitionItemPurchaseOrderItem>(x => x.PurchaseOrderItem.Product.ID)
  96. .Add(x => x.PurchaseOrderItem.Qty))
  97. .ToObjects<JobRequisitionItemPurchaseOrderItem>()
  98. .ToList();
  99. var stockOrders = jriPois.Where(x => x.PurchaseOrderItem.Product.ID == item.Product.ID).ToList();
  100. var treatmentOrders = jriPois.Where(x => x.PurchaseOrderItem.Product.ID != item.Product.ID).ToList();
  101. remTotal -= stockOrders.Sum(x => x.PurchaseOrderItem.Qty);
  102. if (remTotal <= 0)
  103. {
  104. if (stockOrders.Count > 0)
  105. item.Status = JobRequisitionItemStatus.OnOrder;
  106. else
  107. {
  108. // This should be impossible to reach. We are at this point because remTotal <= 0, but stockOrders was an empty list. Therefore
  109. // remTotal is was <= 0 before checking PurchaseOrderItems, but then we should be TreatmentRequired, as above.
  110. Logger.Send(LogType.Error, store.UserID, $"Internal assertion failed: there is enough stock, but we didn't reach the correct clause.");
  111. if (treatmentOrders.Count > 0)
  112. item.Status = JobRequisitionItemStatus.TreatmentOnOrder;
  113. else
  114. item.Status = JobRequisitionItemStatus.TreatmentRequired;
  115. }
  116. }
  117. else if (item.OrderRequired != DateTime.MinValue)
  118. item.Status = JobRequisitionItemStatus.OrderRequired;
  119. else
  120. {
  121. // Even after all the orders have come through, we still don't have enough. We must order more.
  122. item.Status = JobRequisitionItemStatus.NotChecked;
  123. }
  124. }
  125. }
  126. return item.HasOriginalValue(x => x.Status);
  127. }
  128. public static bool CalculateStatus(IStore store, Guid jobRequiItemID)
  129. {
  130. var item = store.Provider.Query(
  131. new Filter<JobRequisitionItem>(x => x.ID).IsEqualTo(jobRequiItemID),
  132. StatusRequiredColumns())
  133. .ToObjects<JobRequisitionItem>()
  134. .FirstOrDefault();
  135. if(item is null)
  136. {
  137. Logger.Send(LogType.Error, store.UserID, $"No {nameof(JobRequisitionItem)} with ID {jobRequiItemID}");
  138. return false;
  139. }
  140. return CalculateStatus(store, item);
  141. }
  142. public static void UpdateStatus(IStore store, Guid jobRequiItemID, JobRequisitionItemAction action)
  143. {
  144. Logger.Send(LogType.Information, "", " ** Updating Requisition Item Status ({0}) -> {1}",
  145. store.GetType().EntityName(), jobRequiItemID);
  146. var item = store.Provider.Query(
  147. new Filter<JobRequisitionItem>(x => x.ID).IsEqualTo(jobRequiItemID),
  148. StatusRequiredColumns())
  149. .ToObjects<JobRequisitionItem>()
  150. .FirstOrDefault();
  151. if (item is null)
  152. {
  153. Logger.Send(LogType.Error, store.UserID, $"No {nameof(JobRequisitionItem)} with ID {jobRequiItemID}");
  154. }
  155. else
  156. {
  157. if (CalculateStatus(store, item))
  158. {
  159. if (action != JobRequisitionItemAction.None)
  160. item.Notes += $"{(!String.IsNullOrWhiteSpace(item.Notes) ? "\n" : "")}Status updated to {item.Status.ToString().SplitCamelCase()} because a {store.Type.EntityName().Split('.').Last().SplitCamelCase()} was {action.ToString().ToLower()}";
  161. store.Provider.Save(item);
  162. }
  163. }
  164. }
  165. }