erp_work_effort 4.0.0 → 4.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v1/projects_controller.rb +25 -0
  3. data/app/controllers/api/v1/time_entries_controller.rb +313 -0
  4. data/app/controllers/api/v1/transportation_routes_controller.rb +9 -0
  5. data/app/controllers/api/v1/work_effort_associations_controller.rb +102 -0
  6. data/app/controllers/api/v1/work_effort_party_assignments_controller.rb +136 -0
  7. data/app/controllers/api/v1/work_effort_types_controller.rb +120 -0
  8. data/app/controllers/api/v1/work_efforts_controller.rb +284 -0
  9. data/app/models/associated_transportation_route.rb +8 -5
  10. data/app/models/cal_evt_party_role.rb +9 -0
  11. data/app/models/calendar_event.rb +46 -0
  12. data/app/models/calendar_invite.rb +10 -0
  13. data/app/models/candidate_submission.rb +37 -0
  14. data/app/models/experience.rb +11 -0
  15. data/app/models/extensions/biz_txn_events.rb +35 -0
  16. data/app/models/extensions/finacial_txns.rb +27 -0
  17. data/app/models/extensions/order_line_item.rb +12 -0
  18. data/app/models/extensions/party.rb +204 -0
  19. data/app/models/party_skill.rb +2 -0
  20. data/app/models/pay_period.rb +59 -0
  21. data/app/models/position.rb +8 -0
  22. data/app/models/position_fulfillment.rb +8 -0
  23. data/app/models/position_type.rb +12 -0
  24. data/app/models/project.rb +59 -0
  25. data/app/models/requirement.rb +2 -0
  26. data/app/models/resume.rb +11 -0
  27. data/app/models/shift.rb +22 -0
  28. data/app/models/skill_type.rb +8 -0
  29. data/app/models/staffing_position.rb +9 -0
  30. data/app/models/time_entry.rb +260 -0
  31. data/app/models/timesheet.rb +194 -0
  32. data/app/models/timesheet_party_role.rb +21 -0
  33. data/app/models/transportation_route.rb +103 -18
  34. data/app/models/transportation_route_segment.rb +44 -0
  35. data/app/models/transportation_route_stop.rb +19 -0
  36. data/app/models/wc_code.rb +7 -0
  37. data/app/models/work_effort.rb +512 -30
  38. data/app/models/work_effort_association.rb +7 -0
  39. data/app/models/work_effort_association_type.rb +5 -0
  40. data/app/models/work_effort_biz_txn_event.rb +22 -0
  41. data/app/models/work_effort_party_assignment.rb +140 -3
  42. data/app/models/work_effort_type.rb +5 -0
  43. data/config/routes.rb +54 -11
  44. data/db/data_migrations/20150709053041_add_skill_types.rb +27 -0
  45. data/db/data_migrations/20150709055922_add_position_types.rb +27 -0
  46. data/db/data_migrations/20150711220000_add_task_tracked_statuses.rb +25 -0
  47. data/db/data_migrations/20150712081804_add_work_effort_types.rb +23 -0
  48. data/db/data_migrations/20150718052204_add_project_tracked_statuses.rb +24 -0
  49. data/db/data_migrations/20150718054404_add_project_party_role_types.rb +14 -0
  50. data/db/data_migrations/20150812150320_add_work_effort_dependency_types.rb +35 -0
  51. data/db/migrate/20100220000000_base_work_efforts.rb +310 -89
  52. data/db/migrate/20150227174108_create_staffing_positions.rb +11 -0
  53. data/db/migrate/20150305194158_create_wc_codes.rb +24 -0
  54. data/db/migrate/20150313134411_create_shifts.rb +28 -0
  55. data/db/migrate/20150325195749_create_candidate_submissions.rb +22 -0
  56. data/db/migrate/20150327115910_resumes.rb +15 -0
  57. data/db/migrate/20150401060938_add_resume_parser_statuses.rb +14 -0
  58. data/db/migrate/20150607181734_create_calendar_events.rb +24 -0
  59. data/db/migrate/20150616174228_create_cal_evt_party_roles.rb +19 -0
  60. data/db/migrate/20150707164520_create_calendar_invites.rb +21 -0
  61. data/db/migrate/20150718052404_create_projects.rb +20 -0
  62. data/db/migrate/20150812191812_update_work_efforts.rb +59 -0
  63. data/db/migrate/20150824152639_add_sequence_to_work_effort.rb +9 -0
  64. data/db/migrate/20150830133951_add_work_effort_biz_txn_events.rb +21 -0
  65. data/db/migrate/20150908200612_update_time_entries.rb +70 -0
  66. data/db/migrate/20151115180427_add_defaults_for_work_effort.rb +36 -0
  67. data/db/migrate/20151218195330_update_transportation_routes.rb +72 -0
  68. data/db/migrate/20160310163052_add_created_by_updated_by_to_erp_work_effort.rb +63 -0
  69. data/db/migrate/20160418115227_add_created_by_updated_by_to_skill_type.rb +34 -0
  70. data/lib/erp_work_effort.rb +3 -0
  71. data/lib/erp_work_effort/config.rb +33 -0
  72. data/lib/erp_work_effort/engine.rb +0 -4
  73. data/lib/erp_work_effort/services/unit_converter.rb +284 -0
  74. data/lib/erp_work_effort/version.rb +1 -1
  75. data/lib/tasks/populate_model_data.rake +19 -0
  76. data/lib/tasks/resume_parser.rake +18 -0
  77. metadata +80 -16
  78. data/app/controllers/erp_work_effort/erp_app/organizer/tasks/base_controller.rb +0 -13
  79. data/app/controllers/erp_work_effort/erp_app/organizer/tasks/work_efforts_controller.rb +0 -128
  80. data/db/data_migrations/20131213151309_create_tasks_organizer_application.rb +0 -14
  81. data/db/migrate/20130829153419_base_routes.rb +0 -93
  82. data/db/migrate/20131213140617_add_work_effort_role_assignments.rb +0 -16
  83. data/db/migrate/20131213144223_add_work_item_to_work_effort.rb +0 -8
  84. data/db/migrate/20140106184615_update_work_effort_item_id_to_int.rb +0 -27
@@ -0,0 +1,8 @@
1
+ class Position < ActiveRecord::Base
2
+
3
+ tracks_created_by_updated_by
4
+
5
+ belongs_to :party
6
+ belongs_to :position_type
7
+
8
+ end
@@ -0,0 +1,8 @@
1
+ class PositionFulfillment < ActiveRecord::Base
2
+
3
+ tracks_created_by_updated_by
4
+
5
+ belongs_to :held_by_party, class_name: "Party"
6
+ belongs_to :position
7
+
8
+ end
@@ -0,0 +1,12 @@
1
+ class PositionType < ActiveRecord::Base
2
+
3
+ attr_protected :created_at, :updated_at
4
+
5
+ tracks_created_by_updated_by
6
+
7
+ class << self
8
+ def iid(internal_identifier)
9
+ find_by_internal_identifier(internal_identifier)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,59 @@
1
+ class Project < ActiveRecord::Base
2
+
3
+ attr_protected :created_at, :updated_at
4
+
5
+ has_many :work_efforts, :dependent => :destroy
6
+
7
+ tracks_created_by_updated_by
8
+ has_tracked_status
9
+ has_party_roles
10
+
11
+ class << self
12
+
13
+ #
14
+ # scoping helpers
15
+ #
16
+
17
+ # scope by dba organization
18
+ #
19
+ # @param dba_organization [Party] dba organization to scope by
20
+ #
21
+ # @return [ActiveRecord::Relation]
22
+ def scope_by_dba_organization(dba_organization)
23
+ scope_by_party(dba_organization, {role_types: [RoleType.iid('dba_org')]})
24
+ end
25
+
26
+ alias scope_by_dba scope_by_dba_organization
27
+
28
+ # scope by party
29
+ #
30
+ # @param party [Integer | Party | Array] either a id of Party record, a Party record, an array of Party records
31
+ # or an array of Party ids
32
+ # @param options [Hash] options to apply to this scope
33
+ # @option options [Array] :role_types role types to include in the scope
34
+ #
35
+ # @return [ActiveRecord::Relation]
36
+ def scope_by_party(party, options={})
37
+ table_alias = String.random
38
+
39
+ statement = joins("inner join entity_party_roles as \"#{table_alias}\" on \"#{table_alias}\".entity_record_id = projects.id
40
+ and \"#{table_alias}\".entity_record_type = 'Project'")
41
+ .where("#{table_alias}.party_id" => party).uniq
42
+
43
+ if options[:role_types]
44
+ statement = statement.where("#{table_alias}.role_type_id" => RoleType.find_child_role_types(options[:role_types]))
45
+ end
46
+
47
+ statement
48
+ end
49
+ end
50
+
51
+ def to_label
52
+ description
53
+ end
54
+
55
+ def to_data_hash
56
+ to_hash(only: [:id, :description, :created_at, :updated_at])
57
+ end
58
+
59
+ end
@@ -11,4 +11,6 @@ class Requirement < ActiveRecord::Base
11
11
  belongs_to :requirement_type
12
12
  belongs_to :requirement_record, :polymorphic => true
13
13
 
14
+ has_file_assets
15
+
14
16
  end
@@ -0,0 +1,11 @@
1
+ class Resume < ActiveRecord::Base
2
+
3
+ attr_protected :created_at,:updated_at
4
+
5
+ belongs_to :party
6
+
7
+ has_file_assets
8
+
9
+ has_tracked_status
10
+
11
+ end
@@ -0,0 +1,22 @@
1
+ class Shift < ActiveRecord::Base
2
+ attr_protected :created_at, :updated_at
3
+
4
+ tracks_created_by_updated_by
5
+
6
+ belongs_to :party
7
+
8
+ # override getter to format time
9
+ def shift_start
10
+ read_attribute(:shift_start).strftime('%I:%M%p')
11
+ end
12
+
13
+ # override getter to format time
14
+ def shift_end
15
+ read_attribute(:shift_end).strftime('%I:%M%p')
16
+ end
17
+
18
+ def to_label
19
+ read_attribute(:description) + ' ' + self.shift_start + '-' + self.shift_end
20
+ end
21
+
22
+ end
@@ -1,10 +1,18 @@
1
1
  class SkillType < ActiveRecord::Base
2
2
  attr_protected :created_at, :updated_at
3
3
 
4
+ tracks_created_by_updated_by
5
+
4
6
  acts_as_nested_set
5
7
  include ErpTechSvcs::Utils::DefaultNestedSetMethods
6
8
 
7
9
  has_many :party_skills
8
10
  has_many :parties, :through => :party_skills
9
11
 
12
+ class << self
13
+ def iid(internal_identifier)
14
+ find_by_internal_identifier(internal_identifier)
15
+ end
16
+ end
17
+
10
18
  end
@@ -0,0 +1,9 @@
1
+ class StaffingPosition < ActiveRecord::Base
2
+ attr_accessible :description, :internal_identifier, :shift
3
+
4
+ tracks_created_by_updated_by
5
+
6
+ #must be after is_json
7
+ acts_as_product_type
8
+
9
+ end
@@ -0,0 +1,260 @@
1
+ # create_table :time_entries do |t|
2
+ # t.datetime :from_datetime
3
+ # t.datetime :thru_datetime
4
+ # t.integer :regular_hours_in_seconds
5
+ # t.integer :overtime_hours_in_seconds
6
+ # t.text :comment
7
+ # t.references :timesheet
8
+ # t.references :work_effort
9
+ #
10
+ # t.timestamps
11
+ # end
12
+ #
13
+ # add_index :time_entries, :timesheet_id
14
+ # add_index :time_entries, :work_effort_id
15
+
16
+ class TimeEntry < ActiveRecord::Base
17
+ attr_protected :created_at, :updated_at
18
+
19
+ tracks_created_by_updated_by
20
+
21
+ belongs_to :timesheet
22
+ belongs_to :work_effort
23
+
24
+ class << self
25
+ def open_entries
26
+ where(TimeEntry.arel_table[:from_datetime].not_eq(nil))
27
+ .where(thru_datetime: nil)
28
+ .where(TimeEntry.arel_table[:manual_entry].eq(nil).or(TimeEntry.arel_table[:manual_entry].eq(false)))
29
+ end
30
+
31
+ # scope by work_effort
32
+ #
33
+ # @param work_effort [Integer | WorkEffort | Array] either a id of WorkEffort record, a WorkEffort record, an array of WorkEffort records
34
+ # or an array of WorkEffort ids
35
+ #
36
+ # @return [ActiveRecord::Relation]
37
+ def scope_by_work_effort(work_effort)
38
+ where(work_effort_id: work_effort)
39
+ end
40
+
41
+ # entries scoped by party
42
+ #
43
+ # @param party [Party] party to get time_entries by
44
+ def scope_by_party(party)
45
+ joins(:timesheet => :timesheet_party_roles).where('timesheet_party_roles.party_id' => party)
46
+ end
47
+
48
+ # total seconds by work_effort
49
+ #
50
+ # @param opts [Hash] opts to calculate the total with
51
+ # @option opts [WorkEffort] :work_effort WorkEffort to get total hours for
52
+ # @option opts [Party] :party Party to get total hours for
53
+ # @option opts [Date] :start start date range
54
+ # @option opts [Date] :end end date range
55
+ #
56
+ # @return [Integer] total seconds
57
+ def total_seconds(opts)
58
+ seconds = 0
59
+
60
+ statement = self
61
+
62
+ time_entry_arel_tbl = self.arel_table
63
+
64
+ if opts[:work_effort]
65
+ statement = statement.scope_by_work_effort(opts[:work_effort])
66
+ end
67
+
68
+ if opts[:party]
69
+ statement = statement.scope_by_party(opts[:party])
70
+ end
71
+
72
+ if opts[:start]
73
+ statement = statement.where(time_entry_arel_tbl[:from_datetime].gteq(opts[:start].utc).
74
+ or(time_entry_arel_tbl[:manual_entry_start_date].gteq(opts[:start].utc)))
75
+ end
76
+
77
+ if opts[:end]
78
+ statement = statement.where(time_entry_arel_tbl[:from_datetime].lteq(opts[:end].utc).
79
+ or(time_entry_arel_tbl[:manual_entry_start_date].lteq(opts[:end].utc)))
80
+ end
81
+
82
+ statement.each do |time_entry|
83
+ seconds += ((time_entry.regular_hours_in_seconds || 0) + (time_entry.overtime_hours_in_seconds || 0))
84
+ end
85
+
86
+ seconds
87
+ end
88
+
89
+ # total hours as decimal round to the nearest 15th
90
+ #
91
+ # @param opts [Hash] opts to calculate the total with
92
+ # @option opts [WorkEffort] :work_effort WorkEffort to get total hours for
93
+ # @option opts [Party] :party Party to get total hours for
94
+ # @option opts [Date] :start start date range
95
+ # @option opts [Date] :end end date range
96
+ #
97
+ # @return [BigDecimal] hours
98
+ def total_hours(opts)
99
+ _total_seconds = total_seconds(opts)
100
+
101
+ if _total_seconds.nil? or _total_seconds == 0
102
+ 0
103
+ else
104
+ # get hours by dividing seconds by 3600 then get fractional minutes
105
+ ((_total_seconds / 3600).floor + (((_total_seconds % 3600) / 60)) / 100.0)
106
+ end
107
+ end
108
+
109
+ # total seconds by work_effort formatted as HH:MM:SS
110
+ #
111
+ # @param opts [Hash] opts to calculate the total with
112
+ # @option opts [WorkEffort] :work_effort WorkEffort to get total hours for
113
+ # @option opts [Party] :party Party to get total hours for
114
+ # @option opts [Date] :start start date range
115
+ # @option opts [Date] :end end date range
116
+ #
117
+ # @return [String] HH:MM:SS
118
+ def total_formatted(opts)
119
+ _total_seconds = total_seconds(opts)
120
+
121
+ if _total_seconds.nil? or _total_seconds == 0
122
+ '00:00:00'
123
+ else
124
+ seconds =_total_seconds % 60
125
+ minutes = (_total_seconds / 60) % 60
126
+ hours = _total_seconds / (60 * 60)
127
+
128
+ format("%02d:%02d:%02d", hours, minutes, seconds)
129
+ end
130
+ end
131
+ end
132
+
133
+ def calculate_regular_hours_in_seconds!
134
+ self.regular_hours_in_seconds = ((thru_datetime - from_datetime)).to_i
135
+ self.save!
136
+ end
137
+
138
+ #
139
+ # relationship helpers
140
+ #
141
+
142
+ # create relationship between a TimeEntry and WorkEffort
143
+ #
144
+ # @param work_effort [WorkEffort] work_effort to relate this time_entry to
145
+ #
146
+ # @return [TimeEntry] self
147
+ def create_work_effort_relationship(work_effort)
148
+ self.work_effort_id = work_effort.id
149
+
150
+ self
151
+ end
152
+
153
+ # destroy relationship between a TimeEntry and WorkEffort
154
+ #
155
+ # @return [TimeEntry] self
156
+ def destroy_work_effort_relationship
157
+ self.work_effort_id = nil
158
+
159
+ self
160
+ end
161
+
162
+ #
163
+ # end relationship helpers
164
+ #
165
+
166
+ # regular hours formatted as HH:MM:SS
167
+ #
168
+ # @return [String] HH:MM:SS
169
+ def regular_hours_formatted
170
+ if self.regular_hours_in_seconds.nil? or self.regular_hours_in_seconds == 0
171
+ '00:00:00'
172
+ else
173
+ seconds = self.regular_hours_in_seconds % 60
174
+ minutes = (self.regular_hours_in_seconds / 60) % 60
175
+ hours = self.regular_hours_in_seconds / (60 * 60)
176
+
177
+ format("%02d:%02d:%02d", hours, minutes, seconds)
178
+ end
179
+ end
180
+
181
+ # overtime hours formatted as HH:MM:SS
182
+ #
183
+ # @return [String] HH:MM:SS
184
+ def overtime_hours_formatted
185
+ if self.overtime_hours_in_seconds.nil? or self.overtime_hours_in_seconds == 0
186
+ '00:00:00'
187
+ else
188
+ seconds = self.overtime_hours_in_seconds % 60
189
+ minutes = (self.overtime_hours_in_seconds / 60) % 60
190
+ hours = self.overtime_hours_in_seconds / (60 * 60)
191
+
192
+ format("%02d:%02d:%02d", hours, minutes, seconds)
193
+ end
194
+ end
195
+
196
+ # get fractional hours for this TimeEntry
197
+ #
198
+ # @return [BigDecimal] hours
199
+ def hours
200
+ _total_seconds = (self.regular_hours_in_seconds || 0) + (self.overtime_hours_in_seconds || 0)
201
+
202
+ if _total_seconds.nil? or _total_seconds == 0
203
+ 0
204
+ else
205
+ # get hours by dividing seconds by 3600 then get fractional minutes
206
+ ((_total_seconds / 3600).floor + (((_total_seconds % 3600) / 60)) / 100.0)
207
+ end
208
+ end
209
+
210
+ # finds party with passed role to this TimeEntry
211
+ #
212
+ # @param role_type [RoleType] role type to use in the association
213
+ # @return [Party] party
214
+ def find_party_by_role(role_type)
215
+ self.timesheet.find_party_by_role(role_type)
216
+ end
217
+
218
+ def to_data_hash
219
+ to_hash(only: [:id,
220
+ :regular_hours_in_seconds,
221
+ :overtime_hours_in_seconds,
222
+ :comment],
223
+ from_datetime: (from_datetime.nil? ? nil : from_datetime.utc.iso8601),
224
+ thru_datetime: (thru_datetime.nil? ? nil : thru_datetime.utc.iso8601),
225
+ updated_at: (updated_at.nil? ? nil : updated_at.utc.iso8601),
226
+ created_at: (created_at.nil? ? nil : created_at.utc.iso8601)
227
+ )
228
+ end
229
+
230
+ # Sets the current status of the WorkEffort to In Progress
231
+ #
232
+ # @param status [String] Internal Identifier of TrackedStatusType to set
233
+ def update_task_status(status)
234
+ # make sure this TimeEntry is related to a WorkEffort
235
+ if self.work_effort
236
+ work_effort.current_status = status
237
+ end
238
+ end
239
+
240
+ # Sets the current status of the WorkEffortAssignment to In Progress
241
+ #
242
+ # @param status [String] Internal Identifier of TrackedStatusType to set
243
+ def update_task_assignment_status(status)
244
+ # make sure this TimeEntry is related to a WorkEffort
245
+ if self.work_effort
246
+ work_resource_role_types = RoleType.find_child_role_types(['work_resource'])
247
+
248
+ # find the party with work_resource related to this TimeEntry
249
+ work_resource_party = self.find_party_by_role(work_resource_role_types)
250
+
251
+ assignment = work_effort.work_effort_party_assignments.where(party_id: work_resource_party)
252
+ .where(role_type_id: work_resource_role_types).first
253
+
254
+ if assignment
255
+ assignment.current_status = status
256
+ end
257
+ end
258
+ end
259
+
260
+ end
@@ -0,0 +1,194 @@
1
+ # create_table :timesheets do |t|
2
+ # t.text :comment
3
+ # t.references :pay_period
4
+ # t.string :status
5
+ #
6
+ # t.timestamps
7
+ # end
8
+ #
9
+ # add_index :timesheets, :party_id
10
+ # add_index :timesheets, :pay_period_id
11
+
12
+ class Timesheet < ActiveRecord::Base
13
+ attr_protected :created_at, :updated_at
14
+
15
+ include AASM
16
+
17
+ has_file_assets
18
+
19
+ has_many :timesheet_party_roles, dependent: :destroy
20
+ has_many :time_entries, dependent: :destroy
21
+ belongs_to :pay_period
22
+
23
+ scope :for_user, lambda { |user| where(["party_id = ?", user.party_id]) }
24
+
25
+ scope :submitted_between, lambda { |from_date, thru_date|
26
+ includes(:pay_period)
27
+ .where(:status => 'submitted', :pay_periods => {:from_date => from_date.to_date..thru_date.to_date})
28
+ }
29
+
30
+ scope :existing_timesheets, lambda { |from_date, party_id|
31
+ includes(:pay_period)
32
+ .where(:party_id => party_id)
33
+ .where(:pay_periods => {:from_date => from_date})
34
+ }
35
+
36
+ class << self
37
+ # get current timesheet for party with passed role
38
+ # if no timesheet could be found it will create one
39
+ #
40
+ # @param party [Party] party to add
41
+ # @param role_type [RoleType] role type to use in the association
42
+ # @return [Timesheet] current timesheet
43
+ def current!(party, role_type)
44
+ pay_period = PayPeriod.current!
45
+
46
+ statement = self.joins(:timesheet_party_roles)
47
+ .where('pay_period_id' => pay_period)
48
+ .where('timesheet_party_roles.party_id' => party)
49
+ .where('timesheet_party_roles.role_type_id' => role_type)
50
+
51
+ timesheet = statement.first
52
+
53
+ # create a new timesheet if one does not exist for the pay_period
54
+ unless timesheet
55
+ timesheet = Timesheet.create!(
56
+ pay_period: pay_period
57
+ )
58
+
59
+ timesheet.add_party_with_role(party, role_type)
60
+ end
61
+
62
+ timesheet
63
+ end
64
+ end
65
+
66
+ #aasm mixin
67
+ aasm_column :status
68
+ aasm_state :open, :initial => true
69
+ aasm_state :submitted
70
+ aasm_state :recalled
71
+ aasm_state :rejected
72
+ aasm_state :approved
73
+ aasm_state :resubmitted
74
+
75
+ aasm_event :submit do
76
+ transitions :to => :submitted, :from => [:open]
77
+ end
78
+
79
+ aasm_event :recall do
80
+ transitions :to => :recalled, :from => [:submitted, :resubmitted, :approved]
81
+ end
82
+
83
+ aasm_event :resubmit do
84
+ transitions :to => :resubmitted, :from => [:recalled, :rejected]
85
+ end
86
+
87
+ aasm_event :reject do
88
+ transitions :to => :rejected, :from => [:submitted, :resubmitted]
89
+ end
90
+
91
+ aasm_event :approve do
92
+ transitions :to => :approved, :from => [:submitted, :resubmitted]
93
+ end
94
+
95
+ def recalled?
96
+ self.status == 'recalled'
97
+ end
98
+
99
+ def rejeted?
100
+ self.status == 'rejected'
101
+ end
102
+
103
+ # pass through method to get week_number on pay period
104
+ #
105
+ def week_number
106
+ self.pay_period.week_number
107
+ end
108
+
109
+ # pass through method to get thru_date on pay period
110
+ #
111
+ def thru_date
112
+ self.pay_period.thru_date
113
+ end
114
+
115
+ # pass through method to get from_date on pay period
116
+ #
117
+ def from_date
118
+ self.pay_period.from_date
119
+ end
120
+
121
+ # add party with passed role to this timesheet
122
+ #
123
+ # @param party [Party] party to add
124
+ # @param role_type [RoleType] role type to use in the association
125
+ # @return [TimeSheetPartyRole] newly created relationship
126
+ def add_party_with_role(party, role_type)
127
+ self.timesheet_party_roles << TimesheetPartyRole.create(
128
+ party: party,
129
+ role_type: role_type
130
+ )
131
+ end
132
+
133
+ # finds party with passed role to this timesheet
134
+ #
135
+ # @param role_type [RoleType] role type to use in the association
136
+ # @return [Party] Party associated to this timesheet
137
+ def find_party_by_role(role_type)
138
+ timesheet_party_role = self.timesheet_party_roles.where(role_type_id: role_type).first
139
+
140
+ if timesheet_party_role
141
+ timesheet_party_role.party
142
+ else
143
+ nil
144
+ end
145
+ end
146
+
147
+ # get total seconds for given date
148
+ #
149
+ # @param opts [Hash] opts to calculate the total with
150
+ # @option opts [Date] :start start date range
151
+ # @option opts [Date] :end end date range
152
+ # @return [Decimal] total seconds
153
+ def total_seconds(opts)
154
+ seconds = 0
155
+ time_entry_arel_tbl = TimeEntry.arel_table
156
+
157
+ statement = self.time_entries
158
+
159
+ if opts[:start]
160
+ statement = statement.where(time_entry_arel_tbl[:from_datetime].gteq(opts[:start]))
161
+ end
162
+
163
+ if opts[:end]
164
+ statement = statement.where(time_entry_arel_tbl[:from_datetime].lteq(opts[:end]))
165
+ end
166
+
167
+ statement.each do |time_entry|
168
+ seconds += ((time_entry.regular_hours_in_seconds || 0) + (time_entry.overtime_hours_in_seconds || 0))
169
+ end
170
+
171
+ seconds
172
+ end
173
+
174
+ # get total seconds for a given day formatted as HH:MM:SS
175
+ #
176
+ # @param opts [Hash] opts to calculate the total with
177
+ # @option opts [Date] :start start date range
178
+ # @option opts [Date] :end end date range
179
+ # @return [String] HH:MM:SS
180
+ def total_formatted(opts)
181
+ _total_seconds = total_seconds(opts)
182
+
183
+ if _total_seconds.nil? or _total_seconds == 0
184
+ '00:00:00'
185
+ else
186
+ seconds =_total_seconds % 60
187
+ minutes = (_total_seconds / 60) % 60
188
+ hours = _total_seconds / (60 * 60)
189
+
190
+ format("%02d:%02d:%02d", hours, minutes, seconds)
191
+ end
192
+ end
193
+
194
+ end