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
@@ -1,9 +1,12 @@
1
- # EXAMPLE USAGE of has_many_polymorphic with AssociatedWorkEffort
2
- # TransportationRoute.class_eval do
3
- # has_many_polymorphic :associated_records,
4
- # :through => :associated_transportation_routes,
5
- # :models => [:shipment_items, :shipments]
1
+ # create_table :associated_transportation_routes do |t|
2
+ # t.integer :transportation_route_id
3
+ # t.integer :associated_record_id
4
+ # t.string :associated_record_type
6
5
  # end
6
+ #
7
+ # add_index :associated_transportation_routes, [:associated_record_id, :associated_record_type], :name => "associated_route_record_id_type_idx"
8
+ # add_index :associated_transportation_routes, :transportation_route_id, :name => "associated_route_transportation_route_id_idx"
9
+
7
10
  class AssociatedTransportationRoute < ActiveRecord::Base
8
11
  attr_protected :created_at, :updated_at
9
12
 
@@ -0,0 +1,9 @@
1
+ class CalEvtPartyRole < ActiveRecord::Base
2
+
3
+ attr_protected :created_at, :updated_at
4
+
5
+ belongs_to :calendar_event
6
+ belongs_to :party
7
+ belongs_to :role_type
8
+
9
+ end
@@ -0,0 +1,46 @@
1
+ class CalendarEvent < ActiveRecord::Base
2
+
3
+ # attr_accessible :title, :body
4
+ attr_accessor :invitee_status
5
+
6
+ has_many :cal_evt_party_roles, dependent: :destroy
7
+ has_many :parties, :through => :cal_evt_party_roles
8
+ has_many :calendar_invites, dependent: :destroy
9
+ has_file_assets
10
+
11
+ # serialize ExtJs attributes
12
+ is_json :custom_fields
13
+
14
+ def invitee_status_for_party( invitee_party_id )
15
+ invite = CalendarInvite.where( "invitee_id = ? and calendar_event_id = ?", invitee_party_id, self.id ).first
16
+ invite ? invite.current_status : nil
17
+ end
18
+
19
+ def get_invitees
20
+ self.calendar_invites.map { |i| i.invitee }.compact
21
+ end
22
+
23
+ def get_accepted_invitees
24
+ self.calendar_invites.select{ |i| i.current_status == 'accepted'}.map { |i| i.invitee }.compact
25
+ end
26
+
27
+ def primary_event_host
28
+ self.parties.where("role_type_id = ?", RoleType.iid('cal_evt_host')).first
29
+ end
30
+
31
+ def find_parties_by_role(role)
32
+ self.parties.where("role_type_id = ?", role.id).all
33
+ end
34
+
35
+ #This is a convenience method whose primary use is when a CalendarEvent starts out using specific invitations
36
+ #and later gets changed to being a public or network-type invite. You can clean up un-accepted invitations
37
+ #and invitees can still accept via the public or network invite.
38
+ def remove_unaccepted_invitations
39
+
40
+ end
41
+
42
+ def to_json
43
+ super methods: [:invitee_status]
44
+ end
45
+
46
+ end
@@ -0,0 +1,10 @@
1
+ class CalendarInvite < ActiveRecord::Base
2
+
3
+ belongs_to :calendar_event
4
+ belongs_to :inviter, :class_name => "Party", :foreign_key => "inviter_id"
5
+ belongs_to :invitee, :class_name => "Party", :foreign_key => "invitee_id"
6
+
7
+ has_tracked_status
8
+ # attr_accessible :title, :body
9
+
10
+ end
@@ -0,0 +1,37 @@
1
+ class CandidateSubmission < ActiveRecord::Base
2
+
3
+ belongs_to :order_line_item
4
+
5
+ attr_protected :created_at, :updated_at
6
+
7
+ has_tracked_status
8
+ tracks_created_by_updated_by
9
+
10
+ is_json :custom_fields
11
+
12
+ #must be after is_json
13
+
14
+ def order_txn
15
+ self.order_line_item.order_txn
16
+ end
17
+
18
+ # helper method to get dba_organization related to this candidate_submission's
19
+ # order line item
20
+ def dba_organization
21
+ order_line_item = OrderLineItem.find(order_line_item_id)
22
+ unless order_line_item.nil?
23
+ order_line_item.dba_organization
24
+ end
25
+ end
26
+
27
+ def clone
28
+ new_candidate_submission = CandidateSubmission.new
29
+ new_candidate_submission.description = self.description
30
+ new_candidate_submission.internal_identifier = self.internal_identifier
31
+ new_candidate_submission.custom_fields = self.custom_fields
32
+ new_candidate_submission.save!
33
+
34
+ new_candidate_submission
35
+ end
36
+ end
37
+
@@ -0,0 +1,11 @@
1
+ class Experience < ActiveRecord::Base
2
+
3
+ tracks_created_by_updated_by
4
+
5
+ belongs_to :party
6
+ belongs_to :experience_type
7
+
8
+ attr_protected :created_at, :updated_at
9
+ attr_accessible :description
10
+
11
+ end
@@ -0,0 +1,35 @@
1
+ module Api
2
+ module V1
3
+ module Extensions
4
+ module BizTxnEventFilters
5
+
6
+ # Filter records
7
+ #
8
+ # @param filters [Hash] a hash of filters to be applied,
9
+ # @param statement [ActiveRecord::Relation] the query being built
10
+ # @return [ActiveRecord::Relation] the query being built
11
+ def apply_filters(filters, statement=nil)
12
+ biz_txn_events = super(filters, statement)
13
+
14
+ if filters[:work_effort_id]
15
+ biz_txn_events = biz_txn_events.joins(:work_effort_biz_txn_events)
16
+ .where('work_effort_biz_txn_events.work_effort_id' => filters[:work_effort_id])
17
+ end
18
+
19
+ biz_txn_events
20
+ end
21
+
22
+ end # BizTxnEventFilters
23
+ end # Extensions
24
+ end # V1
25
+ end # Api
26
+
27
+ BizTxnEvent.singleton_class.prepend Api::V1::Extensions::BizTxnEventFilters
28
+
29
+ BizTxnEvent.class_eval do
30
+
31
+ ## What WorkEfforts have been related to this BizTxnEvent
32
+ has_many :work_effort_biz_txn_events, :dependent => :destroy
33
+ has_many :work_efforts, :through => :work_effort_biz_txn_events
34
+
35
+ end
@@ -0,0 +1,27 @@
1
+ module Api
2
+ module V1
3
+ module Extensions
4
+ module FinancialTxnFilters
5
+
6
+ # Filter records
7
+ #
8
+ # @param filters [Hash] a hash of filters to be applied,
9
+ # @param statement [ActiveRecord::Relation] the query being built
10
+ # @return [ActiveRecord::Relation] the query being built
11
+ def apply_filters(filters, statement=nil)
12
+ financial_txns = super(filters, statement)
13
+
14
+ if filters[:work_effort_id]
15
+ financial_txns = financial_txns.joins(biz_txn_event: :work_effort_biz_txn_events)
16
+ .where('work_effort_biz_txn_events.work_effort_id' => filters[:work_effort_id])
17
+ end
18
+
19
+ financial_txns
20
+ end
21
+
22
+ end # BizTxnEventFilters
23
+ end # Extensions
24
+ end # V1
25
+ end # Api
26
+
27
+ FinancialTxn.singleton_class.prepend Api::V1::Extensions::FinancialTxnFilters
@@ -0,0 +1,12 @@
1
+ OrderLineItem.class_eval do
2
+
3
+ has_many :candidate_submissions, :dependent => :destroy
4
+
5
+
6
+ before_destroy :destroy_candidate_submissions
7
+
8
+ def destroy_candidate_submissions
9
+ self.candidate_submissions.destroy_all
10
+ end
11
+
12
+ end
@@ -1,3 +1,207 @@
1
1
  Party.class_eval do
2
2
  has_many :resource_availabilities, :class_name => 'PartyResourceAvailability', :dependent => :destroy
3
+ has_many :party_skills
4
+ has_many :position_fulfillments, foreign_key: "held_by_party_id"
5
+ has_many :experiences
6
+ has_many :wc_codes, dependent: :destroy
7
+ has_many :shifts, dependent: :destroy
8
+ has_many :resumes
9
+ has_many :work_effort_party_assignments, :dependent => :destroy
10
+ has_many :timesheet_party_roles, dependent: :destroy
11
+ has_many :timesheets, through: :timesheet_party_roles do
12
+ def current!(role_type)
13
+ party = proxy_association.owner
14
+
15
+ Timesheet.current!(party, role_type)
16
+ end
17
+ end
18
+ has_many :time_entries, through: :timesheets do
19
+ # Get TimeEntries where thru_datetime is null
20
+ #
21
+ # @return [ActiveRecord::Relation]
22
+ def open
23
+ where(TimeEntry.arel_table[:from_datetime].not_eq(nil))
24
+ .where(thru_datetime: nil)
25
+ .where(TimeEntry.arel_table[:manual_entry].eq(nil).or(TimeEntry.arel_table[:manual_entry].eq(false)))
26
+ end
27
+
28
+ # Scope TimeEntries by a WorkEffort
29
+ #
30
+ # @param work_effort [WorkEffort] WorkEffort to scope by
31
+ # @return [ActiveRecord::Relation]
32
+ def scope_by_work_effort(work_effort)
33
+ where('work_effort_id' => work_effort.id)
34
+ end
35
+ end
36
+
37
+ #
38
+ # scoping helpers
39
+ #
40
+
41
+ class << self
42
+
43
+ # scope by project
44
+ #
45
+ # @param project [Integer | Project | Array] either a id of Project record, a Project record, an array of Project records
46
+ # or an array of Project ids
47
+ # @param options [Hash] options to apply to this scope
48
+ # @option options [Array] :role_types role types to include in the scope
49
+ #
50
+ # @return [ActiveRecord::Relation]
51
+ def scope_by_project(project, options={})
52
+ statement = joins("join entity_party_roles project_epr on project_epr.party_id = parties.id")
53
+ .where('project_epr.entity_record_type = ?', 'Project')
54
+ .where('project_epr.entity_record_id' => project)
55
+
56
+ if options[:role_types]
57
+ statement = statement.joins("join role_types project_rt on project_rt.id = project_epr.role_type_id")
58
+ .where('project_rt.id' => RoleType.find_child_role_types(options[:role_types]))
59
+ end
60
+
61
+ statement
62
+ end
63
+ end
64
+
65
+ #
66
+ # end scoping helpers
67
+ #
68
+
69
+ #
70
+ # relationship helpers
71
+ #
72
+
73
+ # create relationship between a party and a project
74
+ #
75
+ # @param project [Project] project to relate this party to
76
+ # @param options [Hash] options to apply to this scope
77
+ # @option options [Array] :role_types role types to use in the relationship
78
+ #
79
+ # @return [Party] self
80
+ def create_project_relationship(project, options)
81
+ # make sure role_types is passed
82
+ raise StandardError('Party to Project relationships require a role_types option') if options[:role_types].blank?
83
+
84
+ options[:role_types].each do |role_type|
85
+
86
+ current_relationship = EntityPartyRole.where("entity_record_type = 'Project' and entity_record_id = ?
87
+ and party_id = ? and role_type_id = ?",
88
+ project.id,
89
+ self.id,
90
+ role_type.id).first
91
+ unless current_relationship
92
+ EntityPartyRole.create(
93
+ entity_record: project,
94
+ party: self,
95
+ role_type: role_type
96
+ )
97
+ end
98
+ end
99
+
100
+ self
101
+ end
102
+
103
+ # destroy relationship between a party and a project
104
+ #
105
+ # @param project [Project] project to remove the relationship with
106
+ # @param options [Hash] options to apply to this scope
107
+ # @option options [Array] :role_types role types to remove in this relationship. Pass no role_types option
108
+ # to remove all relationships regardless of role_types
109
+ #
110
+ # @return [Party] self
111
+ def destroy_project_relationship(project, options)
112
+ if options[:role_types]
113
+ options[:role_types].each do |role_type|
114
+
115
+ relationship = EntityPartyRole.where("entity_record_type = 'Project' and entity_record_id = ?
116
+ and party_id = ? and role_type_id = ?",
117
+ project.id,
118
+ self.id,
119
+ role_type.id).first
120
+ if relationship
121
+ relationship.destroy
122
+ end
123
+ end
124
+
125
+ else
126
+ entity_party_roles.delete_all(entity_record_type: 'Project', entity_record_id: project.id)
127
+ end
128
+
129
+ self
130
+ end
131
+
132
+ # updates a relationship between a party and a project. By using this method you are implying there should be only
133
+ # one relationship with the given role type between a party and a project. Any other relationships will be
134
+ # destroyed. If no current relationship is found it will create a new one.
135
+ #
136
+ # @param project [Project] project to update the relationship with
137
+ # @param options [Hash] options to apply to this scope
138
+ # @option options [Array] :role_types role types to update in this relationship.
139
+ #
140
+ # @return [Party] self
141
+ def update_project_relationship(project, options)
142
+ # make sure role_types is passed
143
+ raise StandardError('Party to Project relationships require a role_types option') if options[:role_types].blank?
144
+
145
+ if options[:role_types]
146
+ options[:role_types].each do |role_type|
147
+
148
+ relationship = EntityPartyRole.where("entity_record_type = 'Project'
149
+ and entity_record_id = ? and role_type_id = ?",
150
+ project.id,
151
+ role_type.id).first
152
+ if relationship
153
+ relationship.party = self
154
+ relationship.save!
155
+ else
156
+ create_project_relationship(project, options)
157
+ end
158
+ end
159
+
160
+ else
161
+ entity_party_roles.delete_all(entity_record_type: 'Project', entity_record_id: project.id)
162
+ end
163
+
164
+ self
165
+ end
166
+
167
+ #
168
+ # end relationship helpers
169
+ #
170
+
171
+ def transportation_routes
172
+ TransportationRoute.scope_by_party(self)
173
+ end
174
+
175
+ # Get array of assigned WorkEfforts
176
+ #
177
+ # @param role_types [Array] (['work_resource']) Array of role types to scope by
178
+ # @return [Array] Assigned WorkEfforts
179
+ def assigned_work_efforts(role_types=['work_resource'])
180
+ role_types = RoleType.find_child_role_types(role_types)
181
+
182
+ WorkEffort.joins(work_effort_party_assignments: :role_type)
183
+ .where(role_types: {id: role_types})
184
+ .where(work_effort_party_assignments: {party_id: self.id})
185
+ end
186
+
187
+ # Returns an open TimeEntry for this party if there is one, nil if not
188
+ #
189
+ # @return [TimeEntry] TimeEntry if present nil if not
190
+ def open_time_entry
191
+ self.time_entries.open_entries.first
192
+ end
193
+
194
+ # Returns the current Task this Party is working on if this is on, nil if not
195
+ #
196
+ # @return [WorkEffort] WorkEffort if present nil if not
197
+ def current_work_effort
198
+ self.time_entries.open_entries.try(:first).try(:work_effort)
199
+ end
200
+
201
+ # Returns True if there is an open TimeEntry, false if there is not
202
+ #
203
+ # @return [Boolean] True if there is an open TimeEntry, false if there is not
204
+ def has_open_time_entry?
205
+ !open_time_entry.nil?
206
+ end
3
207
  end
@@ -1,6 +1,8 @@
1
1
  class PartySkill < ActiveRecord::Base
2
2
  attr_protected :created_at, :updated_at
3
3
 
4
+ tracks_created_by_updated_by
5
+
4
6
  belongs_to :party
5
7
  belongs_to :skill_type
6
8
  end
@@ -0,0 +1,59 @@
1
+ # create_table :pay_periods do |t|
2
+ # t.date :from_date
3
+ # t.date :thru_date
4
+ # t.integer :week_number
5
+ #
6
+ # t.timestamps
7
+ # end
8
+
9
+ class PayPeriod < ActiveRecord::Base
10
+
11
+ attr_protected :created_at, :updated_at
12
+
13
+ has_many :timesheets
14
+ has_many :timesheet_party_roles
15
+
16
+ class << self
17
+ def current!
18
+ today = Date.today
19
+ pay_periods_tbl = self.arel_table
20
+
21
+ pay_period = self.where(pay_periods_tbl[:from_date].lteq(today))
22
+ .where(pay_periods_tbl[:thru_date].gteq(today)).first
23
+
24
+ # create pay_period if it does not exist from Sunday to Saturday
25
+ # this will eventually be based on a Calender
26
+ unless pay_period
27
+ today = Time.now.strftime("%A")
28
+ if today == 'Saturday'
29
+ from_date = Chronic.parse('last Sunday')
30
+ thru_date = Time.now
31
+ elsif today == 'Sunday'
32
+ thru_date = Chronic.parse('next Saturday')
33
+ from_date = Time.now
34
+ else
35
+ thru_date = Chronic.parse('next Saturday')
36
+ from_date = Chronic.parse('last Sunday')
37
+ end
38
+
39
+ pay_period = PayPeriod.create!(
40
+ from_date: from_date,
41
+ thru_date: thru_date,
42
+ week_number: Time.now.strftime("%U").to_i
43
+ )
44
+ end
45
+
46
+ pay_period
47
+ end
48
+
49
+ def is_end_of_pay_period?(date)
50
+ pay_period = self.current
51
+ pay_period.nil? ? false : (pay_period.thru_date == date)
52
+ end
53
+ end
54
+
55
+ def to_s
56
+ "[#{self.week_number}] #{self.from_date.strftime('%m/%d/%Y')} - #{self.thru_date.strftime('%m/%d/%Y')}"
57
+ end
58
+
59
+ end