Module: Models::EmployeeDisplay

Extended by:
ActiveSupport::Concern
Included in:
Employee
Defined in:
app/concerns/models/employee_display.rb

Overview

Display helpers, formatting, and CRM utilities for Employee (party subtype).

Most instance methods return [String], numeric counts, or ActiveRecord relations for CRM UI.

See Also:

Instance Method Summary collapse

Instance Method Details

#assigned_or_requested_employee_topicsActiveRecord::Relation<EmployeeTopic>

Employee topics in the assigned or requested state.

Returns:



235
236
237
# File 'app/concerns/models/employee_display.rb', line 235

def assigned_or_requested_employee_topics
  employee_topics.where(state: %w[assigned requested])
end

#check_onlineTime, ...

Timestamp of the employee's latest Online status entry.

Returns:

  • (Time, ActiveSupport::TimeWithZone, nil)


158
159
160
# File 'app/concerns/models/employee_display.rb', line 158

def check_online
  OnlineOfflineUser.where(user_id: id, status: 'Online').last.created_at
end

#check_ptoActiveRecord::Relation<EmployeeEvent>

PTO events for the employee scheduled for today.

Returns:



165
166
167
# File 'app/concerns/models/employee_display.rb', line 165

def check_pto
  EmployeeEvent.where(date: Date.current).eager_load(:employee).where(employee_id: id)
end

#company_phoneString

WarmlyYours main company phone number, formatted for CRM display.

Returns:

  • (String)


131
132
133
# File 'app/concerns/models/employee_display.rb', line 131

def company_phone
  PhoneNumber.parse_and_format('+18008755285', display_format: :crm)
end

#countryString

ISO country of the employee's store, 'USA' on failure.

Returns:

  • (String)


212
213
214
215
216
# File 'app/concerns/models/employee_display.rb', line 212

def country
  store.country
rescue StandardError
  'USA'
end

#days_until_birthdayInteger?

Days until the employee's next birthday (leap-day safe).

Returns:

  • (Integer, nil)

    nil when no date of birth is on file



46
47
48
49
50
51
52
53
# File 'app/concerns/models/employee_display.rb', line 46

def days_until_birthday
  return nil if dob.blank?

  today = Date.current
  next_bday = safe_birthday_date(today.year)
  next_bday = safe_birthday_date(today.year + 1) if next_bday < today
  (next_bday - today).to_i
end

#default_printer_profile(scope_name) ⇒ PrinterProfile?

Resolves the employee's printer profile for an operational scope.

Parameters:

  • scope_name (String, Symbol)

    printer profile scope

Returns:

  • (PrinterProfile, nil)

    matching printer profile



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

def default_printer_profile(scope_name)
  store.printer_profile_for_scope(scope_name, employee_record&.id)
end

#direct_phoneString?

The employee's direct phone number (alias of phone).

Returns:

  • (String, nil)


195
196
197
# File 'app/concerns/models/employee_display.rb', line 195

def direct_phone
  phone
end

#employee_work_scheduleEmployeeWorkSchedule?

The employee's most recent work schedule record.

Returns:



285
286
287
# File 'app/concerns/models/employee_display.rb', line 285

def employee_work_schedule
  employee_work_schedules.last
end

#localeString, ...

Employee locale — the company's locale when set, else the party default.

Returns:

  • (String, Symbol, nil)


172
173
174
# File 'app/concerns/models/employee_display.rb', line 172

def locale
  company.try(:locale) || super
end

#name_and_emailString

Returns "Name - email".

Returns:

  • (String)

    "Name - email"



70
71
72
# File 'app/concerns/models/employee_display.rb', line 70

def name_and_email
  "#{name} - #{email}"
end

#name_and_extensionString

Returns "Name x".

Returns:

  • (String)

    "Name x"



80
81
82
# File 'app/concerns/models/employee_display.rb', line 80

def name_and_extension
  "#{name} x#{pbx_extension}"
end

#name_and_faxString

Returns "Name - fax".

Returns:

  • (String)

    "Name - fax"



65
66
67
# File 'app/concerns/models/employee_display.rb', line 65

def name_and_fax
  "#{name} - #{fax}"
end

#name_and_phoneString

Returns "Name ".

Returns:

  • (String)

    "Name "



75
76
77
# File 'app/concerns/models/employee_display.rb', line 75

def name_and_phone
  "#{name} #{PhoneNumber.parse_and_format(direct_phone)}"
end

#name_with_pairedString

Name with the paired backup rep appended as (/Backup Name) when set.

Returns:

  • (String)


87
88
89
90
91
# File 'app/concerns/models/employee_display.rb', line 87

def name_with_paired
  str = name.to_s
  str += " (/#{employee_record.backup_rep.name})" if employee_record&.backup_rep
  str
end

#online?Boolean Also known as: is_online?

Whether the employee is currently marked Online in the phone system.

Returns:

  • (Boolean)


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

def online?
  OnlineOfflineUser.where(user_id: id).last.status == 'Online'
end

#open_employee_goalsActiveRecord::Relation<EmployeeGoal>

Employee goals still in the defined state, ordered by due date.

Returns:



221
222
223
# File 'app/concerns/models/employee_display.rb', line 221

def open_employee_goals
  employee_goals.where(state: 'defined').order(:due_date)
end

#open_employee_reviewsActiveRecord::Relation<EmployeeReview>

Employee reviews currently in progress.

Returns:



228
229
230
# File 'app/concerns/models/employee_display.rb', line 228

def open_employee_reviews
  employee_reviews.where(state: 'in_progress')
end

#reference_numberString

CRM reference number for the employee, e.g. "EM42".

Returns:

  • (String)


96
97
98
# File 'app/concerns/models/employee_display.rb', line 96

def reference_number
  "EM#{id}"
end

#sales_rep?Boolean Also known as: is_sales_rep?

Whether the employee is in the sales-reps scope.

Returns:

  • (Boolean)


179
180
181
# File 'app/concerns/models/employee_display.rb', line 179

def sales_rep?
  self.class.sales_reps.exists?(id:)
end

#short_nameString

Two-letter initials derived from the account email/login, '-' on failure.

Returns:

  • (String)


58
59
60
61
62
# File 'app/concerns/models/employee_display.rb', line 58

def short_name
  (.email || .).first(2).upcase
rescue StandardError
  '-'
end

#signatureString

Email signature from the employee record, falling back to the default
communication signature.

Returns:

  • (String)


104
105
106
# File 'app/concerns/models/employee_display.rb', line 104

def signature
  employee_record.signature.presence || Communication.default_signature(self)
end

#sms_enabled?Boolean

Whether the employee has any SMS-capable company numbers.

Returns:

  • (Boolean)


111
112
113
# File 'app/concerns/models/employee_display.rb', line 111

def sms_enabled?
  sms_enabled_numbers.present?
end

#sms_enabled_numbers(format_for_select: false) ⇒ Array<String>+

Lists SMS-capable company numbers for this employee.

Parameters:

  • format_for_select (Boolean) (defaults to: false)

    whether to return label/value pairs

Returns:

  • (Array<String>, Array<Array<String>>)

    formatted numbers or select options



119
120
121
122
123
124
125
126
# File 'app/concerns/models/employee_display.rb', line 119

def sms_enabled_numbers(format_for_select: false)
  numbers = contact_points.company_sms_numbers
  if format_for_select
    numbers.map { |n| ["#{n.formatted_for_sms} - #{full_name}", n.formatted_for_sms] }.uniq
  else
    numbers.map(&:formatted_for_sms).uniq
  end
end

#start_year_at_warmlyyoursInteger?

Year the employee was hired at WarmlyYours.

employee_record is nil on system/placeholder employees (the "WarmlyYours"
party, id 1). Both readers are reached through CommunicationBuilder's merge
options for the sender, where an unguarded call takes down the whole
communication build with undefined method 'hire_date' for nil.

Returns:

  • (Integer, nil)


27
28
29
# File 'app/concerns/models/employee_display.rb', line 27

def start_year_at_warmlyyours
  employee_record&.hire_date&.year
end

#state_codeString?

State code of the employee's first address.

Returns:

  • (String, nil)


242
243
244
# File 'app/concerns/models/employee_display.rb', line 242

def state_code
  first_address.try(:state_code)
end

#storeStore

The store attached to the employee's catalog, falling back to the first
store when the association is missing/broken.

Returns:



203
204
205
206
207
# File 'app/concerns/models/employee_display.rb', line 203

def store
  catalog.store
rescue StandardError
  Store.first
end

#tech_rep?Boolean Also known as: is_tech_rep?

Whether the employee is in the technical-support-reps scope.

Returns:

  • (Boolean)


187
188
189
# File 'app/concerns/models/employee_display.rb', line 187

def tech_rep?
  self.class.technical_support_reps.exists?(id:)
end

#time_off?Boolean Also known as: has_time_off?

Whether the employee has any time-off record this year.

Returns:

  • (Boolean)


264
265
266
# File 'app/concerns/models/employee_display.rb', line 264

def time_off?
  EmployeeTimeOff.where(employee_id: id).exists?(created_at: Time.current.beginning_of_year..)
end

#time_off_budgetEmployeeTimeOff?

The employee's time-off budget record for the current year.

Returns:



249
250
251
# File 'app/concerns/models/employee_display.rb', line 249

def time_off_budget
  employee_time_offs.where(year: Time.current.year).last
end

#to_liquidLiquid::EmployeeDrop

Liquid drop for exposing this employee to Liquid templates.



15
16
17
# File 'app/concerns/models/employee_display.rb', line 15

def to_liquid
  Liquid::EmployeeDrop.new(self)
end

#unescheduled_on_day?(date, events_in = nil) ⇒ Boolean Also known as: uneschedule_on_day

Checks whether an employee was unscheduled on or shortly before a date.

Parameters:

  • date (Date, Time)

    date being evaluated

  • events_in (Enumerable<EmployeeEvent>, nil) (defaults to: nil)

    optional preloaded events

Returns:

  • (Boolean)

    true when an applicable unschedule event is within two days



274
275
276
277
278
279
# File 'app/concerns/models/employee_display.rb', line 274

def unescheduled_on_day?(date, events_in = nil)
  timestamp = uneschedule_timestamp(date, events_in)
  return false if timestamp.nil?

  (date.to_date - timestamp.to_date).to_i <= 2
end

#versions_for_audit_trail(_params = {}) ⇒ ActiveRecord::Relation<RecordVersion>

Finds audit versions belonging to the employee's party records.

Parameters:

  • _params (Hash) (defaults to: {})

    unused compatibility options

Returns:

  • (ActiveRecord::Relation<RecordVersion>)

    matching audit versions



139
140
141
142
143
144
145
# File 'app/concerns/models/employee_display.rb', line 139

def versions_for_audit_trail(_params = {})
  RecordVersion.where(
    "(item_type = 'Party' AND item_id = :id) OR (item_type = 'EmployeeRecord' AND reference_data @> :party_id_json)",
    id:,
    party_id_json: { party_id: id }.to_json
  )
end

#work_schedule?Boolean Also known as: has_work_schedule?

Whether any work schedule exists for the employee.

Returns:

  • (Boolean)


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

def work_schedule?
  EmployeeWorkSchedule.exists?(employee_id: id)
end

#work_timezoneString?

Timezone the employee works in, from their latest work schedule.

Returns:

  • (String, nil)


292
293
294
# File 'app/concerns/models/employee_display.rb', line 292

def work_timezone
  employee_work_schedule.try(:work_timezone)
end

#years_at_warmlyyoursInteger?

Whole years since hire (minimum 1), nil for system/placeholder employees
without an employee record.

Returns:

  • (Integer, nil)


35
36
37
38
39
40
41
# File 'app/concerns/models/employee_display.rb', line 35

def years_at_warmlyyours
  hire_date = employee_record&.hire_date
  return nil if hire_date.nil?

  years = ((Date.current - hire_date) / 365).to_i
  [years, 1].max
end