JobBillOfMaterialsItem.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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, IIssues
  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))]
  31. public override StockDimensions Dimensions { get; set; }
  32. [EditorSequence(4)]
  33. [RequiredColumn]
  34. public JobScopeLink Scope { get; set; }
  35. [EditorSequence(4)]
  36. [DoubleEditor(Summary = Summary.Sum)]
  37. public double Quantity { get; set; }
  38. [EditorSequence(5)]
  39. public double UnitCost { get; set; }
  40. [EditorSequence(6)]
  41. [CurrencyEditor(Summary = Summary.Sum)]
  42. public double TotalCost { get; set; }
  43. [EditorSequence(7)]
  44. [TextBoxEditor]
  45. public string Section { get; set; }
  46. [EditorSequence(8)]
  47. public SupplierLink Supplier { get; set; }
  48. private class JobITPLookup : LookupDefinitionGenerator<JobITP, JobBillOfMaterialsItem>
  49. {
  50. public override Filter<JobITP> DefineFilter(JobBillOfMaterialsItem[] items)
  51. {
  52. if (items.Length == 1)
  53. return new Filter<JobITP>(x => x.Job.ID).IsEqualTo(items.First().Job.ID);
  54. return LookupFactory.DefineFilter<JobITP>();
  55. }
  56. public override Columns<JobBillOfMaterialsItem> DefineFilterColumns()
  57. => Columns.None<JobBillOfMaterialsItem>().Add(x => x.Job.ID);
  58. }
  59. [EditorSequence(9)]
  60. [EntityRelationship(DeleteAction.SetNull)]
  61. [LookupDefinition(typeof(JobITPLookup))]
  62. public JobITPLink ITP { get; set; }
  63. [NullEditor]
  64. public long Sequence { get; set; }
  65. public PurchaseOrderItemLink PurchaseOrderItem { get; set; }
  66. public ManufacturingPacketLink Packet { get; set; }
  67. [NullEditor]
  68. public string Issues { get; set; }
  69. static JobBillOfMaterialsItem()
  70. {
  71. LinkedProperties.Register<JobBillOfMaterialsItem, ProductStyleLink, Guid>(x => x.Product.DefaultInstance.Style, x => x.ID, x => x.Style.ID);
  72. LinkedProperties.Register<JobBillOfMaterialsItem, ProductStyleLink, String>(x => x.Product.DefaultInstance.Style, x => x.Code, x => x.Style.Code);
  73. LinkedProperties.Register<JobBillOfMaterialsItem, ProductStyleLink, String>(x => x.Product.DefaultInstance.Style, x => x.Description, x => x.Style.Description);
  74. LinkedProperties.Register<JobBillOfMaterialsItem, ProductInstanceLink, double>(x => x.Product.DefaultInstance, x => x.NettCost, x => x.UnitCost);
  75. LinkedProperties.Register<JobBillOfMaterialsItem, JobLink, Guid>(x => x.Job, x => x.DefaultScope.ID, x => x.Scope.ID);
  76. StockEntity.LinkStockDimensions<JobBillOfMaterialsItem>();
  77. }
  78. private bool bChanging;
  79. protected override void DoPropertyChanged(string name, object? before, object? after)
  80. {
  81. if (bChanging)
  82. return;
  83. try
  84. {
  85. bChanging = true;
  86. if (name.Equals(nameof(Quantity)) && after is double qty)
  87. TotalCost = UnitCost * qty;
  88. else if (name.Equals(nameof(UnitCost)) && after is double cost)
  89. TotalCost = cost * Quantity;
  90. else if (name.Equals(nameof(TotalCost)) && after is double total)
  91. {
  92. if (Quantity == 0)
  93. Quantity = 1;
  94. UnitCost = total / Quantity;
  95. }
  96. }
  97. finally
  98. {
  99. bChanging = false;
  100. }
  101. base.DoPropertyChanged(name, before, after);
  102. }
  103. public static void UpdateCosts(IEnumerable<JobBillOfMaterialsItem> items, Dictionary<String,object?> changes)
  104. {
  105. void UpdateValue<TType>(JobBillOfMaterialsItem item,Expression<Func<JobBillOfMaterialsItem, TType>> property, TType value)
  106. {
  107. CoreUtils.MonitorChanges(
  108. item,
  109. () => CoreUtils.SetPropertyValue(item, CoreUtils.GetFullPropertyName(property, "."), value),
  110. changes
  111. );
  112. }
  113. var productids = items.Where(x => x.Product.ID != Guid.Empty).Select(x => x.Product.ID).ToArray();
  114. MultiQuery query = new MultiQuery();
  115. query.Add(
  116. new Filter<SupplierProduct>(x=>x.Product.ID).InList(productids),
  117. Columns.None<SupplierProduct>().Add(x=>x.Product.ID)
  118. .Add(x=>x.SupplierLink.ID)
  119. .Add(x=>x.Style.ID)
  120. .AddDimensionsColumns(x => x.Dimensions, Classes.Dimensions.ColumnsType.Local)
  121. .Add(x=>x.Job.ID)
  122. .Add(x=>x.SupplierCode)
  123. .Add(x=>x.SupplierDescription)
  124. .Add(x=>x.CostPrice)
  125. );
  126. query.Add(
  127. new Filter<ProductInstance>(x=>x.Product.ID).InList(productids),
  128. Columns.None<ProductInstance>().Add(x=>x.Product.ID)
  129. .Add(x => x.Style.ID)
  130. .AddDimensionsColumns(x => x.Dimensions, Classes.Dimensions.ColumnsType.Local)
  131. .Add(x => x.NettCost)
  132. );
  133. query.Query();
  134. var supplierProducts = query.Get<SupplierProduct>().ToArray<SupplierProduct>();
  135. var productInstances = query.Get<ProductInstance>().ToArray<ProductInstance>();
  136. foreach (var item in items)
  137. {
  138. //Check Supplier / Job Specific Pricing
  139. var supplierProduct = supplierProducts.FirstOrDefault(x =>
  140. x.Product.ID == item.Product.ID
  141. && x.SupplierLink.ID == item.Supplier.ID
  142. && x.Style.ID == item.Style.ID
  143. && x.Dimensions.Equals(item.Dimensions)
  144. && x.Job.ID == item.Job.ID);
  145. if (supplierProduct != null)
  146. {
  147. UpdateValue<double>(item, x => x.UnitCost, supplierProduct.CostPrice);
  148. continue;
  149. }
  150. // Check Supplier Pricing
  151. supplierProduct = supplierProducts.FirstOrDefault(x =>
  152. x.Product.ID == item.Product.ID
  153. && x.SupplierLink.ID == item.Supplier.ID
  154. && x.Style.ID == item.Style.ID
  155. && x.Dimensions.Equals(item.Dimensions)
  156. && x.Job.ID == Guid.Empty);
  157. if (supplierProduct != null)
  158. {
  159. UpdateValue<double>(item, x => x.UnitCost, supplierProduct.CostPrice);
  160. continue;
  161. }
  162. // Check Specific Product Instance
  163. var productInstance = productInstances.FirstOrDefault(x =>
  164. x.Product.ID == item.Product.ID
  165. && x.Style.ID == item.Style.ID
  166. && x.Dimensions.Equals(item.Dimensions));
  167. if (productInstance != null)
  168. UpdateValue<double>(item, x => x.UnitCost, productInstance.NettCost);
  169. else
  170. UpdateValue<double>(item, x => x.UnitCost, 0.0F);
  171. }
  172. }
  173. }
  174. }