Class: EmployeeReview

Inherits:
ApplicationRecord show all
Includes:
Models::Auditable
Defined in:
app/models/employee_review.rb

Overview

== Schema Information

Table name: employee_reviews
Database name: primary

id :integer not null, primary key
appraisal_and_development_of_people :integer
appraisal_and_development_of_people_emp_cmts :text
appraisal_and_development_of_people_sup_cmts :text
communication_skills :integer
communication_skills_emp_cmts :text
communication_skills_sup_cmts :text
employee_comments :text
end_period :date
leadership_ability_sup_cmts :text
maximum_score :integer
planning_and_organization :integer
planning_and_organization_emp_cmts :text
planning_and_organization_sup_cmts :text
pto_reviewed :boolean default(FALSE)
review :jsonb
review_date :date not null
review_name :string(255) not null
review_type :string not null
start_period :date
state :string(255) not null
supervisor_comments :text
total_score :integer
created_at :datetime not null
updated_at :datetime not null
creator_id :integer
employee_record_id :integer
reviewer_id :integer
updater_id :integer

Indexes

employee_reviews_employee_record_id_idx (employee_record_id)
employee_reviews_reviewer_id_idx (reviewer_id)
idx_review_name_er_id (review_name,employee_record_id)
idx_review_type_er_id (review_type,employee_record_id)

Foreign Keys

employee_reviews_employee_record_id_fk (employee_record_id => employee_records.id) ON DELETE => cascade
employee_reviews_reviewer_id_fk (reviewer_id => employee_records.id) ON DELETE => nullify

Defined Under Namespace

Classes: ReviewItem

Constant Summary collapse

REVIEW_ITEMS_BY_TYPE =
{
  kpi: %i[sales_goals_progression
          sales_channel_revenue
          customer_performance_review
          proactive_calls
          sales_goals_vs_promised_orders
          call_volumes_call_report
          call_volumes_call_block
          opportunity_conversion
          lead_conversion],
  quarterly: %i[growth
                initiative
                resourcefulness
                attendance_and_reliability
                improvements
                achievements
                strength_utilization]
}.freeze

Constants included from Models::Auditable

Models::Auditable::ALWAYS_IGNORED

Instance Attribute Summary collapse

Belongs to collapse

Methods included from Models::Auditable

#creator, #updater

Has many collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Models::Auditable

#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#employee_record_idObject (readonly)



81
# File 'app/models/employee_review.rb', line 81

validates :review_date, :review_name, :start_period, :end_period, :employee_record_id, presence: true

#end_periodObject (readonly)



81
# File 'app/models/employee_review.rb', line 81

validates :review_date, :review_name, :start_period, :end_period, :employee_record_id, presence: true

#review_dateObject (readonly)



81
# File 'app/models/employee_review.rb', line 81

validates :review_date, :review_name, :start_period, :end_period, :employee_record_id, presence: true

#review_nameObject (readonly)



80
# File 'app/models/employee_review.rb', line 80

validates :review_name, uniqueness: { scope: :employee_record_id }

#reviewer_idObject (readonly)



82
# File 'app/models/employee_review.rb', line 82

validates :reviewer_id, presence: { unless: :in_progress? }

#start_periodObject (readonly)



81
# File 'app/models/employee_review.rb', line 81

validates :review_date, :review_name, :start_period, :end_period, :employee_record_id, presence: true

#warningsObject

Returns the value of attribute warnings.



52
53
54
# File 'app/models/employee_review.rb', line 52

def warnings
  @warnings
end

Class Method Details

.activeActiveRecord::Relation<EmployeeReview>

A relation of EmployeeReviews that are active. Active Record Scope

Returns:

See Also:



95
# File 'app/models/employee_review.rb', line 95

scope :active, -> { where.not(state: %w[complete cancelled]) }

.all_states_for_selectObject



154
155
156
# File 'app/models/employee_review.rb', line 154

def self.all_states_for_select
  state_machines[:state].states.map(&:name)
end

.build_for_employee(employee:, review_type:, reviewer_employee_record:) ⇒ Object



158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'app/models/employee_review.rb', line 158

def self.build_for_employee(employee:, review_type:, reviewer_employee_record:)
  employee_review = employee.employee_record.employee_reviews.build
  employee_review.reviewer = reviewer_employee_record
  employee_review.review_type = review_type

  case review_type.to_sym
  when :quarterly
    employee_review.start_period = Date.current.beginning_of_quarter
    employee_review.end_period = Date.current.end_of_quarter
    employee_review.review_name = "#{Date.current.quarter_index.ordinalize} QTR #{Date.current.year} Review & Goals"
  when :kpi
    employee_review.start_period = Date.current.beginning_of_week - 2.weeks
    employee_review.end_period = Date.current.end_of_week - 2.weeks
    employee_review.review_name = "KPI Review #{employee_review.start_period} to #{employee_review.end_period}"
  end
  employee_review.review_date = [Date.current, employee_review.end_period.next_business_day].max
  employee_review
end

.current_goalsActiveRecord::Relation<EmployeeReview>

A relation of EmployeeReviews that are current goals. Active Record Scope

Returns:

See Also:



94
# File 'app/models/employee_review.rb', line 94

scope :current_goals, -> { goals_on(Date.current) }

.goals_onActiveRecord::Relation<EmployeeReview>

A relation of EmployeeReviews that are goals on. Active Record Scope

Returns:

See Also:



93
# File 'app/models/employee_review.rb', line 93

scope :goals_on, ->(date) { where(EmployeeReview[:start_period].lteq(date)).where(EmployeeReview[:end_period].gteq(date)) }

.overlappingActiveRecord::Relation<EmployeeReview>

A relation of EmployeeReviews that are overlapping. Active Record Scope

Returns:

See Also:



92
# File 'app/models/employee_review.rb', line 92

scope :overlapping, ->(review) { where(review_type: review.review_type).where("(DATE_PART('day',?::timestamp - start_period::timestamp) * DATE_PART('day', end_period::timestamp - ?::timestamp)) >= 0", review.end_period, review.start_period) }

.ratings_collectionObject



150
151
152
# File 'app/models/employee_review.rb', line 150

def self.ratings_collection
  (1..5).to_a.map { |e| [I18n.t("employee_review.rank_summary.#{e.humanize}"), e] }
end

Instance Method Details

#all_goals_finalized?Boolean

Returns:

  • (Boolean)


230
231
232
233
234
# File 'app/models/employee_review.rb', line 230

def all_goals_finalized?
  (
    employee_goals.finalized.size == employee_goals.size
  )
end

#check_reward_allocationObject

Check if allocated reward exceeds earned reward



266
267
268
269
270
# File 'app/models/employee_review.rb', line 266

def check_reward_allocation
  return unless reward_allocation_amount > earned_reward

  errors.add(:base, 'Allocated reward exceeds earned reward')
end

#default_review_itemsObject



204
205
206
# File 'app/models/employee_review.rb', line 204

def default_review_items
  default_review_items_keys.index_with { |key| {} }
end

#default_review_items_keysObject



200
201
202
# File 'app/models/employee_review.rb', line 200

def default_review_items_keys
  REVIEW_ITEMS_BY_TYPE[review_type.to_sym]
end

#earned_rewardObject



240
241
242
# File 'app/models/employee_review.rb', line 240

def earned_reward
  employee_goals.sum(:earned_reward)
end

#employee_emailObject



181
182
183
# File 'app/models/employee_review.rb', line 181

def employee_email
  employee_record.party.email
end

#employee_goalsActiveRecord::Relation<EmployeeGoal>

Returns:

See Also:



77
# File 'app/models/employee_review.rb', line 77

has_many :employee_goals, inverse_of: :employee_review, dependent: :destroy

#employee_nameObject



252
253
254
# File 'app/models/employee_review.rb', line 252

def employee_name
  employee_record&.employee&.full_name
end

#employee_recordEmployeeRecord



75
# File 'app/models/employee_review.rb', line 75

belongs_to :employee_record, inverse_of: :employee_reviews, optional: true

#goals_defined?Boolean

Returns:

  • (Boolean)


236
237
238
# File 'app/models/employee_review.rb', line 236

def goals_defined?
  employee_goals.active.present?
end

#kpi?Boolean

Returns:

  • (Boolean)


212
213
214
# File 'app/models/employee_review.rb', line 212

def kpi?
  review_type.to_sym == :kpi
end

#notify_allocate_reward_to_completeObject



296
297
298
# File 'app/models/employee_review.rb', line 296

def notify_allocate_reward_to_complete
  InternalMailer.notify_allocate_reward_to_complete(self).deliver_later
end

#notify_process_reward_to_completeObject



300
301
302
# File 'app/models/employee_review.rb', line 300

def notify_process_reward_to_complete
  InternalMailer.notify_process_reward_to_complete(self).deliver_later
end

#overlaps?(other) ⇒ Boolean

Check if a given interval overlaps this interval

Returns:

  • (Boolean)


261
262
263
# File 'app/models/employee_review.rb', line 261

def overlaps?(other)
  (start_period - other.end_period) * (other.start_period - end_period) >= 0
end

#possible_eventsObject



216
217
218
# File 'app/models/employee_review.rb', line 216

def possible_events
  state_transitions.map(&:event).sort
end

#potential_rewardObject



256
257
258
# File 'app/models/employee_review.rb', line 256

def potential_reward
  employee_goals.sum(:potential_reward)
end

#previous_review_of_same_typeObject



193
194
195
196
197
198
# File 'app/models/employee_review.rb', line 193

def previous_review_of_same_type
  EmployeeReview.where(review_type: review_type)
                .where(employee_record_id: employee_record_id)
                .where(EmployeeReview[:end_period].lt(start_period))
                .order('employee_reviews.end_period DESC').first
end

#quarterly?Boolean

Returns:

  • (Boolean)


208
209
210
# File 'app/models/employee_review.rb', line 208

def quarterly?
  review_type.to_sym == :quarterly
end

#remaining_reward_for_employeeObject



248
249
250
# File 'app/models/employee_review.rb', line 248

def remaining_reward_for_employee
  [earned_reward - reward_allocation_amount, 0].max
end

#reset_review_itemsObject



220
221
222
223
# File 'app/models/employee_review.rb', line 220

def reset_review_items
  set_default_review_items
  save
end

#reviewObject



276
277
278
279
280
281
282
# File 'app/models/employee_review.rb', line 276

def review
  # Because jsonb keys are not stored in the same input order, we will resort based on the
  # default key order
  review_hash_raw = read_attribute(:review)
  review_hash_sorted = review_hash_raw.sort_by { |k, _v| default_review_items_keys.index(k.to_sym) || 999 }.to_h
  review_hash_sorted.map { |review_key, review_item_properties| ReviewItem.new(review_key, review_item_properties) }
end

#review_attributes=(attributes) ⇒ Object



284
285
286
287
288
289
290
291
292
293
294
# File 'app/models/employee_review.rb', line 284

def review_attributes=(attributes)
  review_items = {}
  attributes.each do |_index, attrs|
    next if attrs.delete('_destroy') == '1'

    factor = attrs.delete('factor')
    review_items[factor.to_sym] = attrs
  end
  self[:review] = review_items
  review_will_change!
end

#reviewable?Boolean

Returns:

  • (Boolean)


272
273
274
# File 'app/models/employee_review.rb', line 272

def reviewable?
  %i[in_progress process_reward complete].include?(state.to_sym)
end

#reviewerEmployeeRecord



76
# File 'app/models/employee_review.rb', line 76

belongs_to :reviewer, class_name: 'EmployeeRecord', inverse_of: :employee_reviewed, optional: true

#reviewer_emailObject



185
186
187
# File 'app/models/employee_review.rb', line 185

def reviewer_email
  reviewer&.party&.email
end

#reward_allocation_amountObject



244
245
246
# File 'app/models/employee_review.rb', line 244

def reward_allocation_amount
  reward_allocations.to_a.map { |a| a.amount || 0.0 }.sum
end

#reward_allocationsActiveRecord::Relation<RewardAllocation>

Returns:

See Also:



78
# File 'app/models/employee_review.rb', line 78

has_many :reward_allocations, inverse_of: :employee_review, dependent: :destroy

#set_default_review_itemsObject



225
226
227
228
# File 'app/models/employee_review.rb', line 225

def set_default_review_items
  self[:review] = default_review_items
  review_will_change!
end

#to_sObject



177
178
179
# File 'app/models/employee_review.rb', line 177

def to_s
  "#{review_name} (#{start_period.to_fs(:crm_date_only)} to #{end_period.to_fs(:crm_date_only)})"
end

#total_donationObject



189
190
191
# File 'app/models/employee_review.rb', line 189

def total_donation
  reward_allocations.map(&:total_donation).compact.sum
end