Module: EmployeeWorkStatus

Extended by:
ActiveSupport::Concern
Includes:
EmployeeActivityCapacity, Memery
Included in:
Employee
Defined in:
app/models/concerns/employee_work_status.rb

Overview

Day status, schedule preloading, and next-working-day logic.

Instance Method Summary collapse

Methods included from EmployeeActivityCapacity

#activity_load_on_day, #can_take_activities_on_day?, #maximum_activities_per_day, #maximum_priority_tier_activities_per_day, #maximum_standard_tier_activities_per_day, #priority_tier_activities_ratio

Instance Method Details

#active_work_schedule(date) ⇒ Object



91
92
93
# File 'app/models/concerns/employee_work_status.rb', line 91

def active_work_schedule(date)
  work_schedules.effective_on(date)
end

#capacity_ratio(date) ⇒ Object



80
81
82
83
84
85
86
87
88
# File 'app/models/concerns/employee_work_status.rb', line 80

def capacity_ratio(date)
  date = date.to_date unless date.is_a?(Date)
  day_status = day_status_employee_event(date)

  return 1.0 if day_status.nil? || day_status == :working
  return compute_partial_capacity(date) if day_status == :partial_timeoff

  0.0
end

#current_company_holidays_arrayObject



12
13
14
# File 'app/models/concerns/employee_work_status.rb', line 12

def current_company_holidays_array
  @current_company_holidays_array ||= company.company_holidays.where(CompanyHoliday[:holiday_date].gteq(Date.current)).pluck(:holiday_date)
end

#day_employee_events(date) ⇒ Object



24
25
26
27
28
29
# File 'app/models/concerns/employee_work_status.rb', line 24

def day_employee_events(date)
  date = date.to_date unless date.is_a?(Date)
  return @preloaded_time_off_events[date] if @preloaded_time_off_events&.key?(date)

  employee_events_loaded(date)
end

#day_status_employee_event(date) ⇒ Object



32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'app/models/concerns/employee_work_status.rb', line 32

def day_status_employee_event(date)
  evts = day_employee_events(date)
  return if evts.blank?

  wh = working_hours_on_date(date)

  if evts.any? { |ev| ev.time_off_request.time_off_type_id == TimeOffType::BANKED_TIME_ID }
    :working
  elsif partial_day_pto_present?(evts, wh)
    :partial_timeoff
  else
    evts.first.time_off_request.time_off_type.name.to_sym
  end
end

#day_status_holiday_check(date) ⇒ Object



16
17
18
19
20
21
# File 'app/models/concerns/employee_work_status.rb', line 16

def day_status_holiday_check(date)
  date = date.to_date unless date.is_a?(Date)
  return :not_working_closed unless date.on_weekday?

  :not_working_closed if current_company_holidays_array.include?(date)
end

#day_status_work_schedule(date) ⇒ Object



59
60
61
62
63
64
# File 'app/models/concerns/employee_work_status.rb', line 59

def day_status_work_schedule(date)
  has_effective = work_schedules.loaded? ? effective_schedule_in_memory?(date) : work_schedules.effective_on(date).exists?
  return unless has_effective && working_hours_on_date(date).zero?

  :not_working_closed
end

#employee_events_loaded(start_date = nil) ⇒ Object



67
68
69
70
71
# File 'app/models/concerns/employee_work_status.rb', line 67

def employee_events_loaded(start_date = nil)
  evts = TimeOffRequestDate.joins(:time_off_request).where(time_off_requests: { state: 'approved', employee_id: id })
  evts = evts.where(date: start_date) if start_date
  evts.distinct
end

#next_business_day(start_date = nil, offset = 1) ⇒ Object



116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'app/models/concerns/employee_work_status.rb', line 116

def next_business_day(start_date = nil, offset = 1)
  start_date ||= Date.current
  search_date = start_date.dup.to_date + offset.days
  counter = 1
  while work_status_on_day(search_date) == :not_working_closed
    search_date += 1.day
    if (counter += 1) > 30
      search_date = nil
      break
    end
  end
  search_date
end

#next_business_day_closing_time(start_date = nil, offset = 1) ⇒ Object



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

def next_business_day_closing_time(start_date = nil, offset = 1)
  dt = next_business_day(start_date, offset)
  WorkingHours.advance_to_closing_time(dt)
end

#next_working_day(start_date = nil, offset = 1) ⇒ Object



102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'app/models/concerns/employee_work_status.rb', line 102

def next_working_day(start_date = nil, offset = 1)
  start_date ||= Date.current
  search_date = start_date.dup.to_date + offset.days
  preload_time_off_events(search_date, search_date + 30.days)

  attempts = 0
  until working_on_day?(search_date)
    search_date += 1.day
    attempts += 1
    raise no_working_day_within_search_limit_error if attempts > 730
  end
  search_date
end

#preload_time_off_events(start_date, end_date) ⇒ Object



135
136
137
138
139
140
141
142
143
144
145
# File 'app/models/concerns/employee_work_status.rb', line 135

def preload_time_off_events(start_date, end_date)
  @preloaded_time_off_events ||= {}
  events = TimeOffRequestDate.joins(:time_off_request)
                             .includes(time_off_request: :time_off_type)
                             .where(time_off_requests: { state: 'approved', employee_id: id })
                             .where(date: start_date..end_date)
                             .distinct

  events.group_by(&:date).each { |date, evts| @preloaded_time_off_events[date] = evts }
  (start_date..end_date).each { |date| @preloaded_time_off_events[date] ||= [] }
end

#preload_working_hours(start_date, end_date) ⇒ Object



147
148
149
150
151
152
153
154
155
# File 'app/models/concerns/employee_work_status.rb', line 147

def preload_working_hours(start_date, end_date)
  @preloaded_working_hours ||= {}
  schedules = schedules_for_preload(start_date, end_date)
  hours_lookup = hours_by_schedule_and_wday(schedules)

  (start_date..end_date).each do |date|
    @preloaded_working_hours[date] = sum_working_hours_for_date(schedules, hours_lookup, date)
  end
end

#work_status_on_day(date) ⇒ Object



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

def work_status_on_day(date)
  date = date.to_date unless date.is_a?(Date)
  day_status_holiday_check(date) || day_status_employee_event(date) || day_status_work_schedule(date) || :working
end

#working_hours_on_date(date) ⇒ Object



48
49
50
51
52
53
54
55
56
# File 'app/models/concerns/employee_work_status.rb', line 48

def working_hours_on_date(date)
  date = date.to_date unless date.is_a?(Date)
  return @preloaded_working_hours[date] if @preloaded_working_hours&.key?(date)

  work_schedules.effective_on(date)
                .joins(:work_schedule_days)
                .where(work_schedule_days: { day_of_week: date.wday })
                .sum('work_schedule_days.hours')
end

#working_on_day?(date) ⇒ Boolean

Returns:

  • (Boolean)


96
97
98
99
100
# File 'app/models/concerns/employee_work_status.rb', line 96

def working_on_day?(date)
  date = date.to_date unless date.is_a?(Date)
  ws = work_status_on_day(date)
  ws == :working || (ws == :partial_timeoff && capacity_ratio(date).positive?)
end