PurchaseOrderItem.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  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. [UserTracking(typeof(Bill))]
  10. [Caption("Purchase Order Items")]
  11. public class PurchaseOrderItem : StockEntity, IRemotable, IPersistent, IOneToMany<PurchaseOrder>, ITaxable, IOneToMany<Consignment>, IOneToMany<Job>,
  12. ILicense<AccountsPayableLicense>, IPostableFragment<PurchaseOrder>, IJobMaterial
  13. {
  14. [RequiredColumn]
  15. [EntityRelationship(DeleteAction.Cascade)]
  16. public PurchaseOrderLink PurchaseOrder { get; set; }
  17. [Obsolete("Replaced by PurchaseOrder")]
  18. public PurchaseOrderLink PurchaseOrderLink
  19. {
  20. get => PurchaseOrder;
  21. set { }
  22. }
  23. [EntityRelationship(DeleteAction.SetNull)]
  24. [EditorSequence(1)]
  25. [RequiredColumn]
  26. public override ProductLink Product { get; set; }
  27. [EntityRelationship(DeleteAction.SetNull)]
  28. [EditorSequence(2)]
  29. public ProductStyleLink Style { get; set; }
  30. [EditorSequence(3)]
  31. [RequiredColumn]
  32. [DimensionsEditor(typeof(StockDimensions))]
  33. public override StockDimensions Dimensions { get; set; }
  34. private class AllocationsFormula : ComplexFormulaGenerator<PurchaseOrderItem, string>
  35. {
  36. public override IComplexFormulaNode<PurchaseOrderItem, string> GetFormula() =>
  37. Aggregate<PurchaseOrderItemAllocation>(
  38. AggregateCalculation.Concat,
  39. x => x.Property(x => x.Job.JobNumber))
  40. .WithLink(x => x.Item.ID, x => x.ID);
  41. }
  42. [ComplexFormula(typeof(AllocationsFormula))]
  43. [TextBoxEditor(Visible=Visible.Optional,Editable = Editable.Hidden)]
  44. public string Allocations { get; set; }
  45. [EntityRelationship(DeleteAction.SetNull)]
  46. [EditorSequence(4)]
  47. public JobLink Job { get; set; }
  48. [MemoEditor(Visible = Visible.Default)]
  49. [EditorSequence(6)]
  50. public string Description { get; set; }
  51. [DoubleEditor(Visible = Visible.Default)]
  52. [EditorSequence(7)]
  53. public double Qty { get; set; } = 1;
  54. private class AllocatedFormula : ComplexFormulaGenerator<PurchaseOrderItem, double>
  55. {
  56. public override IComplexFormulaNode<PurchaseOrderItem, double> GetFormula() =>
  57. If<PurchaseOrderItem,string?,double>(
  58. Property<PurchaseOrderItem,string?>(x=>x.Dimensions.Unit.Conversion),
  59. Condition.Equals,
  60. Constant<PurchaseOrderItem,string?>(""),
  61. ""
  62. )
  63. .Then(
  64. Aggregate<PurchaseOrderItem,PurchaseOrderItemAllocation, double>(
  65. AggregateCalculation.Sum,
  66. Property<PurchaseOrderItemAllocation,double>(a => a.Quantity)
  67. )
  68. .WithLink(x => x.Item.ID, x => x.ID)
  69. )
  70. .Else(
  71. Aggregate<PurchaseOrderItem,PurchaseOrderItemAllocation, double>(
  72. AggregateCalculation.Sum,
  73. Formula(
  74. FormulaOperator.Divide,
  75. Property<PurchaseOrderItemAllocation,double>(a => a.Quantity),
  76. Property<PurchaseOrderItemAllocation,double>(a => a.Item.Dimensions.Value)
  77. )
  78. ).WithLink(x => x.Item.ID, x => x.ID)
  79. );
  80. }
  81. [ComplexFormula(typeof(AllocatedFormula))]
  82. [DoubleEditor(Editable = Editable.Hidden)]
  83. public double Allocated { get; set; }
  84. private class UnallocatedFormula : ComplexFormulaGenerator<PurchaseOrderItem, double>
  85. {
  86. public override IComplexFormulaNode<PurchaseOrderItem, double> GetFormula() =>
  87. Formula(
  88. FormulaOperator.Subtract,
  89. Property(x=>x.Qty),
  90. Property(x=>x.Allocated)
  91. );
  92. }
  93. [ComplexFormula(typeof(UnallocatedFormula))]
  94. [DoubleEditor(Editable = Editable.Hidden)]
  95. public double Unallocated { get; set; }
  96. [CurrencyEditor(Visible = Visible.Optional)]
  97. [EditorSequence(8)]
  98. public double ForeignCurrencyCost { get; set; }
  99. [CurrencyEditor(Visible = Visible.Default)]
  100. [EditorSequence(9)]
  101. public double Cost { get; set; }
  102. [CurrencyEditor(Visible = Visible.Default, Summary = Summary.Sum)]
  103. [EditorSequence(10)]
  104. public double ExTax { get; set; }
  105. [EditorSequence(11)]
  106. public TaxCodeLink TaxCode { get; set; }
  107. [NullEditor]
  108. public double TaxRate { get; set; }
  109. [EditorSequence(12)]
  110. [CurrencyEditor(Visible = Visible.Default, Summary = Summary.Sum)]
  111. public double Tax { get; set; }
  112. [EditorSequence(13)]
  113. [CurrencyEditor(Visible = Visible.Default, Summary = Summary.Sum)]
  114. public double IncTax { get; set; }
  115. [EditorSequence("Additional",1)]
  116. [IntegerEditor(Visible = Visible.Optional)]
  117. public int PORevision { get; set; }
  118. [CodeEditor(Visible = Visible.Default, Editable = Editable.Enabled)]
  119. [EditorSequence("Additional",2)]
  120. public string SupplierCode { get; set; }
  121. [EditorSequence("Additional",3)]
  122. public PurchaseGLCodeLink PurchaseGL { get; set; }
  123. [EditorSequence("Additional",4)]
  124. public CostCentreLink CostCentre { get; set; }
  125. [DateTimeEditor(Visible = Visible.Default)]
  126. [EditorSequence("Additional",5)]
  127. public DateTime DueDate { get; set; }
  128. [EditorSequence("Additional",6)]
  129. [EntityRelationship(DeleteAction.SetNull)]
  130. public ConsignmentLink Consignment { get; set; }
  131. private class ConsignmentParcelLookup : LookupDefinitionGenerator<ConsignmentParcel, PurchaseOrderItem>
  132. {
  133. public override Filter<ConsignmentParcel> DefineFilter(PurchaseOrderItem[] items)
  134. {
  135. if (items.Select(x => x.Consignment.ID).Distinct().Count() != 1)
  136. return new Filter<ConsignmentParcel>().None();
  137. return Filter<ConsignmentParcel>.Where(x => x.Consignment.ID).IsEqualTo(items.FirstOrDefault()?.Consignment.ID ?? CoreUtils.FullGuid);
  138. }
  139. public override Columns<PurchaseOrderItem> DefineFilterColumns()
  140. => Columns.None<PurchaseOrderItem>().Add(x=>x.Consignment.ID);
  141. }
  142. [LookupDefinition(typeof(ConsignmentParcelLookup))]
  143. [EditorSequence("Additional",7)]
  144. [EntityRelationship(DeleteAction.SetNull)]
  145. [Caption("Parcel")]
  146. public ConsignmentParcelLink ConsignmentParcel { get; set; }
  147. [EditorSequence("Additional",8)]
  148. public StockLocationLink StockLocation { get; set; }
  149. [DateTimeEditor(Visible = Visible.Default)]
  150. [EditorSequence("Additional",9)]
  151. public DateTime ReceivedDate { get; set; }
  152. [TextBoxEditor(Visible = Visible.Optional)]
  153. [EditorSequence("Additional",10)]
  154. public string ReceivedReference { get; set; }
  155. [CurrencyEditor(Visible = Visible.Optional, Editable = Editable.Hidden, Summary = Summary.Sum)]
  156. public double Balance { get; set; }
  157. [Editable(Editable.Disabled)]
  158. [EntityRelationship(DeleteAction.SetNull)]
  159. public BillLineLink BillLine { get; set; }
  160. [NullEditor]
  161. public string PostedReference { get; set; }
  162. [EntityRelationship(DeleteAction.SetNull)]
  163. [NullEditor]
  164. [Obsolete("Replaced with Product", true)]
  165. public ProductLink ProductLink
  166. {
  167. get { return Product; }
  168. set { /* We cannot set the 'Product' to this value, because that would stuff the SubObject internal linking, so we do nothing instead. */ }
  169. }
  170. [EntityRelationship(DeleteAction.SetNull)]
  171. [NullEditor]
  172. [Obsolete("Replaced with Style", true)]
  173. public ProductStyleLink StyleLink
  174. {
  175. get { return Style; }
  176. set { /* Same as ProductLink */ }
  177. }
  178. [NullEditor]
  179. [Obsolete("Replaced with Dimensions", true)]
  180. public double UnitSize { get; set; }
  181. [NullEditor]
  182. [Obsolete]
  183. public StockMovementLink StockMovement { get; set; }
  184. [NullEditor]
  185. [EntityRelationship(DeleteAction.SetNull)]
  186. public ManufacturingPacketLink Packet { get; set; }
  187. private class FormCountAggregate : ComplexFormulaGenerator<PurchaseOrderItem, int>
  188. {
  189. public override IComplexFormulaNode<PurchaseOrderItem, int> GetFormula() =>
  190. Count<PurchaseOrderItemForm, Guid>(
  191. x => x.Property(x => x.ID))
  192. .WithLink(x => x.Parent.ID, x => x.ID);
  193. }
  194. [NullEditor]
  195. [ComplexFormula(typeof(FormCountAggregate))]
  196. public int FormCount { get; set; }
  197. private class OpenFormsAggregate : ComplexFormulaGenerator<PurchaseOrderItem, int>
  198. {
  199. public override IComplexFormulaNode<PurchaseOrderItem, int> GetFormula() =>
  200. Count<PurchaseOrderItemForm, Guid>(
  201. x => x.Property(x => x.ID),
  202. Filter<PurchaseOrderItemForm>.Where(x => x.FormCompleted).IsEqualTo(DateTime.MinValue)
  203. .Or(x => x.FormData).IsEqualTo(""))
  204. .WithLink(x => x.Parent.ID, x => x.ID);
  205. }
  206. [NullEditor]
  207. [ComplexFormula(typeof(OpenFormsAggregate))]
  208. public int OpenForms { get; set; }
  209. static PurchaseOrderItem()
  210. {
  211. LinkedProperties.Register<PurchaseOrderItem, ProductLink, String>(x => x.Product, x => x.Name, x => x.Description);
  212. LinkedProperties.Register<PurchaseOrderItem, TaxCodeLink, Guid>(x => x.Product.TaxCode, x => x.ID, x => x.TaxCode.ID);
  213. LinkedProperties.Register<PurchaseOrderItem, TaxCodeLink, String>(x => x.Product.TaxCode, x => x.Code,
  214. x => x.TaxCode.Code);
  215. LinkedProperties.Register<PurchaseOrderItem, TaxCodeLink, String>(x => x.Product.TaxCode, x => x.Description,
  216. x => x.TaxCode.Description);
  217. LinkedProperties.Register<PurchaseOrderItem, TaxCodeLink, double>(x => x.Product.TaxCode, x => x.Rate,
  218. x => x.TaxCode.Rate);
  219. LinkedProperties.Register<PurchaseOrderItem, TaxCodeLink, double>(x => x.TaxCode, x => x.Rate, x => x.TaxRate);
  220. LinkedProperties.Register<PurchaseOrderItem, PurchaseGLCodeLink, Guid>(x => x.Product.PurchaseGL, x => x.ID, x => x.PurchaseGL.ID);
  221. LinkedProperties.Register<PurchaseOrderItem, CostCentreLink, Guid>(x => x.Product.CostCentre, x => x.ID, x => x.CostCentre.ID);
  222. LinkedProperties.Register<PurchaseOrderItem, ProductStyleLink, Guid>(x => x.Product.DefaultInstance.Style, x => x.ID, x => x.Style.ID);
  223. LinkedProperties.Register<PurchaseOrderItem, ProductStyleLink, String>(x => x.Product.DefaultInstance.Style, x => x.Code, x => x.Style.Code);
  224. LinkedProperties.Register<PurchaseOrderItem, ProductStyleLink, String>(x => x.Product.DefaultInstance.Style, x => x.Description, x => x.Style.Description);
  225. LinkedProperties.Register<PurchaseOrderItem, ProductInstanceLink, double>(x => x.Product.DefaultInstance, x => x.NettCost,
  226. x => x.Cost);
  227. StockEntity.LinkStockDimensions<PurchaseOrderItem>();
  228. DefaultColumns.Add<PurchaseOrderItem>(x => x.PurchaseOrder.PONumber, caption: "PONumber");
  229. DefaultColumns.Add<PurchaseOrderItem>(x => x.Description);
  230. DefaultColumns.Add<PurchaseOrderItem>(x => x.Product.Code, caption: "Product", width: 100);
  231. DefaultColumns.Add<PurchaseOrderItem>(x => x.Style.Code, caption: "Style", width: 100);
  232. DefaultColumns.Add<PurchaseOrderItem>(x => x.Dimensions.UnitSize, width: 70, caption: "Size");
  233. DefaultColumns.Add<PurchaseOrderItem>(x => x.Job.JobNumber, caption: "Job", width: 70);
  234. DefaultColumns.Add<PurchaseOrderItem>(x => x.Qty, width: 50);
  235. DefaultColumns.Add<PurchaseOrderItem>(x => x.Cost);
  236. DefaultColumns.Add<PurchaseOrderItem>(x => x.ExTax);
  237. DefaultColumns.Add<PurchaseOrderItem>(x => x.TaxCode.Code, caption: "Tax", width: 50);
  238. DefaultColumns.Add<PurchaseOrderItem>(x => x.CostCentre.Code, width: 70);
  239. DefaultColumns.Add<PurchaseOrderItem>(x => x.PurchaseGL.Code, width: 70);
  240. DefaultColumns.Add<PurchaseOrderItem>(x => x.ReceivedDate, width: 100);
  241. }
  242. private bool bChanging;
  243. protected override void DoPropertyChanged(string name, object? before, object? after)
  244. {
  245. base.DoPropertyChanged(name, before, after);
  246. if (bChanging)
  247. return;
  248. try
  249. {
  250. bChanging = true;
  251. if (name.Equals(nameof(Qty)) && after is double qty)
  252. ExTax = qty * Cost;
  253. else if (name.Equals(nameof(ForeignCurrencyCost)) && (after is double foreigncost) &&
  254. PurchaseOrder.Supplier.Currency.ID != Guid.Empty)
  255. {
  256. Cost = foreigncost / (PurchaseOrder.Supplier.Currency.ExchangeRate.IsEffectivelyEqual(0.0) ? 1.0 : PurchaseOrder.Supplier.Currency.ExchangeRate);
  257. ExTax = Qty * Cost;
  258. }
  259. else if (name.Equals(nameof(Cost)) && (after is double cost))
  260. {
  261. ExTax = cost * Qty;
  262. if (PurchaseOrder.Supplier.Currency.ID != Guid.Empty)
  263. ForeignCurrencyCost = cost * (PurchaseOrder.Supplier.Currency.ExchangeRate.IsEffectivelyEqual(0.0) ? 1.0 : PurchaseOrder.Supplier.Currency.ExchangeRate);
  264. }
  265. else if (name.Equals(nameof(ExTax)) && after is double extax)
  266. {
  267. if (Qty == 0)
  268. Qty = 1;
  269. Cost = extax / Qty;
  270. if (PurchaseOrder.Supplier.Currency.ID != Guid.Empty)
  271. ForeignCurrencyCost = Cost * (PurchaseOrder.Supplier.Currency.ExchangeRate.IsEffectivelyEqual(0.0) ? 1.0 : PurchaseOrder.Supplier.Currency.ExchangeRate);
  272. }
  273. else if (name.Equals(nameof(IncTax)) && after is double inctax)
  274. Balance = ReceivedDate.IsEmpty() ? inctax : 0.00F;
  275. else if (name.Equals(nameof(ReceivedDate)) && after is DateTime received)
  276. Balance = received.IsEmpty() ? IncTax : 0.00F;
  277. }
  278. finally
  279. {
  280. bChanging = false;
  281. }
  282. }
  283. }
  284. }