Class: TimeOffPartialDay

Inherits:
Object
  • Object
show all
Defined in:
app/services/time_off_partial_day.rb

Overview

Shared rules for hour-based partial-day PTO (aligned with Crm::ManagerScheduleGridSlot).
Used by Employee#day_status_employee_event / #capacity_ratio and Daily Focus prompts.

Constant Summary collapse

HOURS_EPSILON =
0.001

Class Method Summary collapse

Class Method Details

.hour_unit_partial_day?(time_off_request_date:, schedule_day_hours:, time_off_request:) ⇒ Boolean

Hour-unit request with some PTO hours taken but less than a full scheduled day (grid partial-day mode).

Returns:

  • (Boolean)


18
19
20
21
22
23
24
# File 'app/services/time_off_partial_day.rb', line 18

def self.hour_unit_partial_day?(time_off_request_date:, schedule_day_hours:, time_off_request:)
  return false if time_off_request_date.blank? || time_off_request.blank?
  return false unless time_off_request.time_off_type&.unit == 'hour'
  return false if schedule_day_hours.blank?

  time_off_request_date.amount.to_d < schedule_day_hours.to_d - HOURS_EPSILON
end

.net_work_hours_from_planned(planned) ⇒ Float?

Net working hours inside the planned window (subtracts lunch overlap unless skip_lunch).

Returns:

  • (Float, nil)

    nil if start/end missing or invalid



40
41
42
43
44
45
46
47
48
49
50
# File 'app/services/time_off_partial_day.rb', line 40

def self.net_work_hours_from_planned(planned)
  planned = planned.with_indifferent_access
  ws = parse_hhmm(planned[:start_time])
  we = parse_hhmm(planned[:end_time])
  return nil unless ws && we && we > ws

  dur = we - ws
  return dur.round(4) if ActiveModel::Type::Boolean.new.cast(planned[:skip_lunch])

  apply_planned_lunch_subtraction(dur, planned, ws, we).round(4)
end

.parse_hhmm(str) ⇒ Object



63
64
65
66
67
68
69
70
# File 'app/services/time_off_partial_day.rb', line 63

def self.parse_hhmm(str)
  return if str.blank?

  t = Time.zone.parse("2000-01-01 #{str}")
  t.hour + (t.min / 60.0)
rescue ArgumentError, TypeError
  nil
end

.partial_day_hour_pto?(time_off_request_date:, working_hours_on_date:, time_off_request:) ⇒ Boolean

Approved calendar row: positive hour PTO and not a full day off (matches manager schedule grid).

Returns:

  • (Boolean)


27
28
29
30
31
32
33
34
35
36
# File 'app/services/time_off_partial_day.rb', line 27

def self.partial_day_hour_pto?(time_off_request_date:, working_hours_on_date:, time_off_request:)
  return false unless working_hours_on_date.to_f.positive?
  return false unless time_off_request_date.amount.to_d.positive?

  hour_unit_partial_day?(
    time_off_request_date: time_off_request_date,
    schedule_day_hours: working_hours_on_date,
    time_off_request: time_off_request
  )
end

.planned_inner_hash(time_off_request_date) ⇒ Object

Inner "planned" hash from TimeOffRequestDate#planned_work_day JSON (start/end/lunch HH:MM).



9
10
11
12
13
14
15
# File 'app/services/time_off_partial_day.rb', line 9

def self.planned_inner_hash(time_off_request_date)
  raw = time_off_request_date&.planned_work_day
  return {} unless raw.is_a?(Hash)

  p = raw.with_indifferent_access[:planned]
  p.is_a?(Hash) ? p.with_indifferent_access : {}
end