Class: CompanyHoliday

Inherits:
ApplicationRecord show all
Defined in:
app/models/company_holiday.rb

Overview

== Schema Information

Table name: company_holidays
Database name: primary

id :integer not null, primary key
holiday_date :date
holiday_name :string
created_at :datetime not null
updated_at :datetime not null
company_id :integer

Constant Summary

Constants included from Models::Schedulable

Models::Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Belongs to collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Models::Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#holiday_dateDate (readonly)

Returns:

  • (Date)


19
# File 'app/models/company_holiday.rb', line 19

validates :holiday_date, presence: true, uniqueness: { scope: :company_id, message: 'already exists for this company' }

#holiday_nameString (readonly)

Returns:

  • (String)


21
# File 'app/models/company_holiday.rb', line 21

validates :holiday_name, presence: true

Class Method Details

.ca_pastActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are ca past. Active Record Scope

Returns:

See Also:



33
# File 'app/models/company_holiday.rb', line 33

scope :ca_past, -> { where(company_id: 2).where(holiday_date: Time.current.beginning_of_year..Time.current) }

.ca_upcomingActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are ca upcoming. Active Record Scope

Returns:

See Also:



30
# File 'app/models/company_holiday.rb', line 30

scope :ca_upcoming, -> { where(company_id: 2).where(holiday_date: Time.current..) }

.in_pastActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are in past. Active Record Scope

Returns:

See Also:



34
# File 'app/models/company_holiday.rb', line 34

scope :in_past, -> { where(company_id: 3).where(holiday_date: Time.current.beginning_of_year..Time.current) }

.in_upcomingActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are in upcoming. Active Record Scope

Returns:

See Also:



31
# File 'app/models/company_holiday.rb', line 31

scope :in_upcoming, -> { where(company_id: 3).where(holiday_date: Time.current..) }

.pastActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are past. Active Record Scope

Returns:

See Also:



28
# File 'app/models/company_holiday.rb', line 28

scope :past, -> { where('holiday_date BETWEEN ? AND ?', Time.current.beginning_of_year, Time.current) }

.sync_all_holidays_to_calendarsHash

Class method to sync all holidays to all employees

Returns:

  • (Hash)

    with :total_synced, :total_failed, and :total_skipped counts



121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'app/models/company_holiday.rb', line 121

def self.sync_all_holidays_to_calendars
  results = { total_synced: 0, total_failed: 0, total_skipped: 0 }

  # Only sync upcoming holidays (from today onwards)
  where(holiday_date: Date.current..).find_each do |holiday|
    holiday_results = holiday.sync_to_all_employees('create')
    results[:total_synced] += holiday_results[:synced]
    results[:total_failed] += holiday_results[:failed]
    results[:total_skipped] += holiday_results[:skipped]
  end

  results
end

.upcomingActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are upcoming. Active Record Scope

Returns:

See Also:



27
# File 'app/models/company_holiday.rb', line 27

scope :upcoming, -> { where(holiday_date: Time.current..) }

.usa_pastActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are usa past. Active Record Scope

Returns:

See Also:



32
# File 'app/models/company_holiday.rb', line 32

scope :usa_past, -> { where(company_id: 1).where(holiday_date: Time.current.beginning_of_year..Time.current) }

.usa_upcomingActiveRecord::Relation<CompanyHoliday>

A relation of CompanyHolidays that are usa upcoming. Active Record Scope

Returns:

See Also:



29
# File 'app/models/company_holiday.rb', line 29

scope :usa_upcoming, -> { where(company_id: 1).where(holiday_date: Time.current..) }

Instance Method Details

#companyCompany

Returns:



16
# File 'app/models/company_holiday.rb', line 16

belongs_to :company

#send_to_google_calendar(action) ⇒ Hash

Legacy method for backwards compatibility

Parameters:

  • action (String)

Returns:

  • (Hash)


138
139
140
# File 'app/models/company_holiday.rb', line 138

def send_to_google_calendar(action)
  sync_to_all_employees(action)
end

#sync_to_all_employees(action = 'create') ⇒ Hash

Sync this holiday to all employees from the same country

Parameters:

  • action (String) (defaults to: 'create')

    'create', 'update', or 'destroy'

Returns:

  • (Hash)

    with :synced, :failed, and :skipped counts



98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# File 'app/models/company_holiday.rb', line 98

def sync_to_all_employees(action = 'create')
  results = { synced: 0, failed: 0, skipped: 0 }

  Employee.active_employees.where(company_id: company_id).find_each do |employee|
    auth_status = TimeOffRequests::GoogleAuthService.check_status(employee)
    unless auth_status[:connected]
      results[:skipped] += 1
      next
    end

    result = sync_to_employee_calendar(employee, action)
    if result[:success]
      results[:synced] += 1
    else
      results[:failed] += 1
    end
  end

  results
end

#sync_to_employee_calendar(employee, action = 'create') ⇒ Hash

Sync this holiday to a specific employee's Google Calendar

Parameters:

  • employee (Employee)

    the employee to sync to

  • action (String) (defaults to: 'create')

    'create', 'update', or 'destroy'

Returns:

  • (Hash)

    result with :success and :message keys



40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'app/models/company_holiday.rb', line 40

def sync_to_employee_calendar(employee, action = 'create')
  auth = employee.&.authentications&.google_auth&.first
  return { success: false, message: 'No Google auth' } if auth&.google_auth_refresh_token.blank?

  begin
    client = TimeOffRequests::GoogleAuthService.authorize(employee)
    return { success: false, message: 'Authorization failed' } unless client

    service = Google::Apis::CalendarV3::CalendarService.new
    service.authorization = client

    start_time = Google::Apis::CalendarV3::EventDateTime.new(date: holiday_date.to_s, time_zone: 'America/Chicago')
    end_time = Google::Apis::CalendarV3::EventDateTime.new(date: (holiday_date + 1.day).to_s, time_zone: 'America/Chicago')

    case action
    when 'create'
      event = Google::Apis::CalendarV3::Event.new(
        id: "ch#{id}",
        summary: "#{company.name} Holiday: #{holiday_name.titleize}",
        description: "Company Holiday - #{holiday_name}",
        start: start_time,
        end: end_time,
        transparency: 'opaque' # Mark as busy
      )
      service.insert_event('primary', event)
      { success: true, message: 'Holiday created' }
    when 'update'
      event = service.get_event('primary', "ch#{id}")
      event.summary = "#{company.name} Holiday: #{holiday_name.titleize}"
      event.description = "Company Holiday - #{holiday_name}"
      event.start = start_time
      event.end = end_time
      event.transparency = 'opaque'
      service.update_event('primary', "ch#{id}", event)
      { success: true, message: 'Holiday updated' }
    when 'destroy'
      service.delete_event('primary', "ch#{id}")
      { success: true, message: 'Holiday deleted' }
    end
  rescue Google::Apis::ClientError => e
    if e.status_code == 409 && action == 'create'
      # Event already exists, update instead
      sync_to_employee_calendar(employee, 'update')
    elsif e.message.include?('notFound') && action == 'destroy'
      { success: true, message: 'Holiday already deleted' }
    else
      Rails.logger.error("Holiday sync failed for #{employee.email}: #{e.message}")
      { success: false, message: e.message }
    end
  rescue StandardError => e
    Rails.logger.error("Holiday sync error for #{employee.email}: #{e.message}")
    { success: false, message: e.message }
  end
end