JobBillOfMaterialsItem.cs 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Linq.Expressions;
  5. using InABox.Core;
  6. using PRSClasses;
  7. namespace Comal.Classes
  8. {
  9. public class JobBillOfMaterialsItem : StockEntity, IRemotable, IPersistent, IOneToMany<Job>,
  10. IOneToMany<JobBillOfMaterials>, ISequenceable, ILicense<ProjectManagementLicense>, IJobMaterial
  11. {
  12. [EntityRelationship(DeleteAction.Cascade)]
  13. [Editable(Editable.Hidden)]
  14. public JobLink Job { get; set; }
  15. [EntityRelationship(DeleteAction.Cascade)]
  16. [Editable(Editable.Hidden)]
  17. public JobBillOfMaterialsLink BillOfMaterials { get; set; }
  18. [EditorSequence(1)]
  19. [EntityRelationship(DeleteAction.SetNull)]
  20. [RequiredColumn]
  21. public override ProductLink Product { get; set; }
  22. [EditorSequence(2)]
  23. [EntityRelationship(DeleteAction.SetNull)]
  24. public ProductStyleLink Style { get; set; }
  25. [NullEditor]
  26. [Obsolete("Replaced by Dimensions",true)]
  27. public double UnitSize { get; set; }
  28. [EditorSequence(3)]
  29. [RequiredColumn]
  30. [DimensionsEditor(typeof(StockDimensions), AllowEditingUnit = false)]
  31. public override StockDimensions Dimensions { get; set; }
  32. [EditorSequence(4)]
  33. [DoubleEditor(Summary = Summary.Sum)]
  34. public double Quantity { get; set; }
  35. [EditorSequence(5)]
  36. public double UnitCost { get; set; }
  37. [EditorSequence(6)]
  38. [CurrencyEditor(Summary = Summary.Sum)]
  39. public double TotalCost { get; set; }
  40. [EditorSequence(7)]
  41. [TextBoxEditor]
  42. public string Section { get; set; }
  43. [EditorSequence(8)]
  44. public SupplierLink Supplier { get; set; }
  45. private class JobITPLookup : LookupDefinitionGenerator<JobITP, JobBillOfMaterialsItem>
  46. {
  47. public override Filter<JobITP> DefineFilter(JobBillOfMaterialsItem[] items)
  48. {
  49. if (items.Length == 1)
  50. return new Filter<JobITP>(x => x.Job.ID).IsEqualTo(items.First().Job.ID);
  51. return LookupFactory.DefineFilter<JobITP>();
  52. }
  53. public override Columns<JobBillOfMaterialsItem> DefineFilterColumns()
  54. => new Columns<JobBillOfMaterialsItem>(x => x.Job.ID);
  55. }
  56. [EditorSequence(9)]
  57. [EntityRelationship(DeleteAction.SetNull)]
  58. [LookupDefinition(typeof(JobITPLookup))]
  59. public JobITPLink ITP { get; set; }
  60. [NullEditor]
  61. public long Sequence { get; set; }
  62. public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
  63. public ManufacturingPacketLink Packet { get; set; }
  64. static JobBillOfMaterialsItem()
  65. {
  66. LinkedProperties.Register<JobBillOfMaterialsItem, ProductStyleLink, Guid>(x => x.Product.DefaultInstance.Style, x => x.ID, x => x.Style.ID);
  67. LinkedProperties.Register<JobBillOfMaterialsItem, ProductStyleLink, String>(x => x.Product.DefaultInstance.Style, x => x.Code, x => x.Style.Code);
  68. LinkedProperties.Register<JobBillOfMaterialsItem, ProductStyleLink, String>(x => x.Product.DefaultInstance.Style, x => x.Description, x => x.Style.Description);
  69. LinkedProperties.Register<JobBillOfMaterialsItem, ProductInstanceLink, double>(x => x.Product.DefaultInstance, x => x.NettCost, x => x.UnitCost);
  70. StockEntity.LinkStockDimensions<JobBillOfMaterialsItem>();
  71. }
  72. private bool bChanging;
  73. protected override void DoPropertyChanged(string name, object? before, object? after)
  74. {
  75. if (bChanging)
  76. return;
  77. try
  78. {
  79. bChanging = true;
  80. if (name.Equals(nameof(Quantity)) && after is double qty)
  81. TotalCost = UnitCost * qty;
  82. else if (name.Equals(nameof(UnitCost)) && after is double cost)
  83. TotalCost = cost * Quantity;
  84. else if (name.Equals(nameof(TotalCost)) && after is double total)
  85. {
  86. if (Quantity == 0)
  87. Quantity = 1;
  88. UnitCost = total / Quantity;
  89. }
  90. }
  91. finally
  92. {
  93. bChanging = false;
  94. }
  95. base.DoPropertyChanged(name, before, after);
  96. }
  97. public static void UpdateCosts(IEnumerable<JobBillOfMaterialsItem> items, Dictionary<String,object?> changes)
  98. {
  99. void UpdateValue<TType>(JobBillOfMaterialsItem item,Expression<Func<JobBillOfMaterialsItem, TType>> property, TType value)
  100. {
  101. CoreUtils.MonitorChanges(
  102. item,
  103. () => CoreUtils.SetPropertyValue(item, CoreUtils.GetFullPropertyName(property, "."), value),
  104. changes
  105. );
  106. }
  107. var productids = items.Where(x => x.Product.ID != Guid.Empty).Select(x => x.Product.ID).ToArray();
  108. MultiQuery query = new MultiQuery();
  109. query.Add(
  110. new Filter<SupplierProduct>(x=>x.Product.ID).InList(productids),
  111. new Columns<SupplierProduct>(x=>x.Product.ID)
  112. .Add(x=>x.SupplierLink.ID)
  113. .Add(x=>x.Style.ID)
  114. .Add(x=>x.Dimensions.UnitSize)
  115. .Add(x=>x.Job.ID)
  116. .Add(x=>x.SupplierCode)
  117. .Add(x=>x.SupplierDescription)
  118. .Add(x=>x.CostPrice)
  119. );
  120. query.Add(
  121. new Filter<ProductInstance>(x=>x.Product.ID).InList(productids),
  122. new Columns<ProductInstance>(x=>x.Product.ID)
  123. .Add(x => x.Style.ID)
  124. .Add(x => x.Dimensions.UnitSize)
  125. .Add(x => x.NettCost)
  126. );
  127. query.Query();
  128. foreach (var item in items)
  129. {
  130. //Check Supplier / Job Specific Pricing
  131. CoreRow? row = query.Get<SupplierProduct>()?.Rows.FirstOrDefault(r =>
  132. (r.Get<SupplierProduct, Guid>(c => c.Product.ID) == item.Product.ID)
  133. && (r.Get<SupplierProduct, Guid>(c => c.SupplierLink.ID) == item.Supplier.ID)
  134. && (r.Get<SupplierProduct, Guid>(c => c.Style.ID) == item.Style.ID)
  135. && (string.Equals(r.Get<SupplierProduct,String>(c=>c.Dimensions.UnitSize),item.Dimensions.UnitSize))
  136. && (r.Get<SupplierProduct, Guid>(c => c.Job.ID) == item.Job.ID)
  137. );
  138. if (row != null)
  139. {
  140. UpdateValue<double>(item, x => x.UnitCost, row.Get<SupplierProduct, double>(c => c.CostPrice));
  141. continue;
  142. }
  143. // Check Supplier Pricing
  144. row = query.Get<SupplierProduct>()?.Rows.FirstOrDefault(r =>
  145. (r.Get<SupplierProduct, Guid>(c => c.Product.ID) == item.Product.ID)
  146. && (r.Get<SupplierProduct, Guid>(c => c.SupplierLink.ID) == item.Supplier.ID)
  147. && (r.Get<SupplierProduct, Guid>(c => c.Style.ID) == item.Style.ID)
  148. && (string.Equals(r.Get<SupplierProduct,String>(c=>c.Dimensions.UnitSize),item.Dimensions.UnitSize))
  149. && (r.Get<SupplierProduct, Guid>(c => c.Job.ID) == Guid.Empty)
  150. );
  151. if (row != null)
  152. {
  153. UpdateValue<double>(item, x => x.UnitCost, row.Get<SupplierProduct, double>(c => c.CostPrice));
  154. continue;
  155. }
  156. // Check Specific Product Instance
  157. row = query.Get<ProductInstance>()?.Rows.FirstOrDefault(r =>
  158. (r.Get<ProductInstance, Guid>(c => c.Product.ID) == item.Product.ID)
  159. && (r.Get<ProductInstance, Guid>(c => c.Style.ID) == item.Style.ID)
  160. && (string.Equals(r.Get<ProductInstance,string>(c => c.Dimensions.UnitSize),item.Dimensions.UnitSize))
  161. );
  162. if (row != null)
  163. UpdateValue<double>(item, x => x.UnitCost, row.Get<ProductInstance, double>(c => c.NettCost));
  164. else
  165. UpdateValue<double>(item, x => x.UnitCost, 0.0F);
  166. }
  167. }
  168. }
  169. }