Assignment.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234
  1. using System;
  2. using System.Linq;
  3. using System.Linq.Expressions;
  4. using InABox.Core;
  5. namespace Comal.Classes
  6. {
  7. /// <summary>
  8. /// An assignment represents an anticipated booking for an employee, much like a diary entry
  9. /// <exclude />
  10. /// </summary>
  11. [UserTracking("Assignments")]
  12. [Caption("Assignments")]
  13. public class Assignment : Entity, IPersistent, IRemotable, INumericAutoIncrement<Assignment>, IOneToMany<Employee>, IOneToMany<Job>,
  14. IOneToMany<Kanban>, ILicense<SchedulingControlLicense>, IJobActivity, IOneToMany<Invoice>, IJobScopedItem,
  15. IInvoiceable
  16. {
  17. [IntegerEditor(Editable = Editable.Hidden)]
  18. [EditorSequence(1)]
  19. public int Number { get; set; }
  20. [DateEditor]
  21. [EditorSequence(2)]
  22. [RequiredColumn]
  23. public DateTime Date { get; set; }
  24. [EntityRelationship(DeleteAction.Cascade)]
  25. [EditorSequence(3)]
  26. [RequiredColumn]
  27. public EmployeeLink Employee { get; set; }
  28. [Obsolete("Replaced by Employee")]
  29. public EmployeeLink EmployeeLink
  30. {
  31. get => Employee;
  32. set { }
  33. }
  34. // [EntityRelationship(DeleteAction.Cascade)]
  35. // [EditorSequence(4)]
  36. // public EquipmentLink Equipment { get; set; }
  37. [TextBoxEditor]
  38. [EditorSequence(5)]
  39. public string Title { get; set; }
  40. [MemoEditor]
  41. [EditorSequence(6)]
  42. public string Description { get; set; }
  43. [EditorSequence(7)]
  44. public AssignmentActivityLink Activity { get; set; }
  45. [Obsolete("Replaced by Activity")]
  46. public AssignmentActivityLink ActivityLink
  47. {
  48. get => Activity;
  49. set { }
  50. }
  51. private class KanbanLookup : LookupDefinitionGenerator<Kanban, Assignment>
  52. {
  53. public override Filter<Kanban> DefineFilter(Assignment[] items)
  54. {
  55. if (items == null || !items.Any())
  56. return LookupFactory.DefineFilter<Kanban>();
  57. var employeeID = items.First().Employee.ID;
  58. // Open tasks which the employee of this assignment is an observer of.
  59. return Filter<Kanban>.Where(x => x.Closed).IsEqualTo(DateTime.MinValue)
  60. .And(x => x.ID).InQuery(Filter<KanbanSubscriber>.Where(x => x.Employee.ID).IsEqualTo(employeeID), x => x.Kanban.ID);
  61. }
  62. public override Columns<Assignment> DefineFilterColumns()
  63. => Columns.None<Assignment>().Add(x => x.Employee.ID);
  64. }
  65. [LookupDefinition(typeof(KanbanLookup))]
  66. [EditorSequence(8)]
  67. [EntityRelationship(DeleteAction.SetNull)]
  68. public KanbanLink Task { get; set; }
  69. [EditorSequence(9)]
  70. public JobLink Job { get; set; }
  71. [NullEditor]
  72. [Obsolete("Replaced with Job")]
  73. public JobLink JobLink { get; set; }
  74. private class JobITPLookup : LookupDefinitionGenerator<JobITP, Assignment>
  75. {
  76. public override Filter<JobITP> DefineFilter(Assignment[] items)
  77. {
  78. if (items.Length == 1)
  79. return Filter<JobITP>.Where(x => x.Job.ID).IsEqualTo(items.First().Job.ID);
  80. return LookupFactory.DefineFilter<JobITP>();
  81. }
  82. public override Columns<Assignment> DefineFilterColumns()
  83. => Columns.None<Assignment>().Add(x => x.Job.ID);
  84. }
  85. [LookupDefinition(typeof(JobITPLookup))]
  86. [EditorSequence(10)]
  87. public JobITPLink ITP { get; set; }
  88. [NullEditor]
  89. [Obsolete("Replaced with Actual.Start", true)]
  90. public TimeSpan Start { get; set; }
  91. [NullEditor]
  92. [Obsolete("Replaced with Actual.Duration", true)]
  93. public TimeSpan Duration { get; set; }
  94. [NullEditor]
  95. [Obsolete("Replaced with Actual.Finish", true)]
  96. public TimeSpan Finish { get; set; }
  97. [EditorSequence(11)]
  98. [CoreTimeEditor]
  99. public TimeBlock Booked { get; set; }
  100. [EditorSequence(12)]
  101. [CoreTimeEditor]
  102. public TimeBlock Actual { get; set; }
  103. [TimestampEditor]
  104. [RequiredColumn]
  105. [EditorSequence(13)]
  106. public DateTime Completed { get; set; }
  107. [Editable(Editable.Disabled)]
  108. [Comment("The duration this Assignment was treated as having for costing purposes")]
  109. [EditorSequence("Processing", 1)]
  110. public double CostedHours { get; set; }
  111. [CurrencyEditor]
  112. [Editable(Editable.Disabled)]
  113. [Comment("Cost of the Assignment; calculated from duration, overtime rules and the employee's hourly rate.")]
  114. [EditorSequence("Processing", 2)]
  115. public double Cost { get; set; }
  116. [EditorSequence("Processing", 3)]
  117. public ActualCharge Charge { get; set; }
  118. [TimestampEditor]
  119. [EditorSequence("Processing", 4)]
  120. public DateTime Processed { get; set; }
  121. [NullEditor]
  122. [EntityRelationship(DeleteAction.Cascade)]
  123. public LeaveRequestLink LeaveRequest { get; set; }
  124. [Obsolete("Replaced by LeaveRequest")]
  125. public LeaveRequestLink LeaveRequestLink
  126. {
  127. get => LeaveRequest;
  128. set { }
  129. }
  130. [NullEditor]
  131. [EntityRelationship(DeleteAction.Cascade)]
  132. public DeliveryLink Delivery { get; set; }
  133. [NullEditor]
  134. [EntityRelationship(DeleteAction.SetNull)]
  135. public InvoiceLink Invoice { get; set; }
  136. [NullEditor]
  137. public MeetingDetails Meeting { get; set; }
  138. private class JobScopeLookup : LookupDefinitionGenerator<JobScope, Assignment>
  139. {
  140. public override Filter<JobScope> DefineFilter(Assignment[] items)
  141. {
  142. var item = items?.Length == 1 ? items[0] : null;
  143. if (item != null)
  144. return Filter<JobScope>.Where(x => x.Job.ID).IsEqualTo(item.Job.ID).And(x => x.Status.Approved).IsEqualTo(true);
  145. return Filter.None<JobScope>();
  146. }
  147. public override Columns<Assignment> DefineFilterColumns()
  148. => Columns.None<Assignment>().Add(x => x.Job.ID);
  149. }
  150. [LookupDefinition(typeof(JobScopeLookup))]
  151. public JobScopeLink JobScope { get; set; }
  152. public Expression<Func<Assignment, int>> AutoIncrementField()
  153. {
  154. return x => x.Number;
  155. }
  156. public Filter<Assignment> AutoIncrementFilter()
  157. {
  158. return null;
  159. }
  160. public int AutoIncrementDefault() => 1;
  161. public static TimeSpan EffectiveTime(TimeSpan actual, TimeSpan booked) => actual.Ticks != 0L ? actual : booked;
  162. public TimeSpan EffectiveStartTime()
  163. {
  164. return EffectiveTime(Actual.Start, Booked.Start);
  165. }
  166. public DateTime EffectiveStart()
  167. {
  168. return Date.Add(EffectiveStartTime());
  169. }
  170. public TimeSpan EffectiveFinishTime()
  171. {
  172. // If we have an actual finish, always use that
  173. // otherwise use EffectiveStart() + booked.duration
  174. return EffectiveTime(
  175. Actual.Finish,
  176. EffectiveTime(Actual.Start, Booked.Start)
  177. .Add(Booked.Duration)
  178. );
  179. }
  180. public DateTime EffectiveFinish()
  181. {
  182. return Date.Add(
  183. EffectiveFinishTime()
  184. );
  185. }
  186. static Assignment()
  187. {
  188. LinkedProperties.Register<Assignment, ActivityCharge, bool>(ass => ass.Activity.Charge, chg => chg.Chargeable, ass => ass.Charge.Chargeable);
  189. Classes.JobScope.LinkScopeProperties<Assignment>();
  190. }
  191. }
  192. }