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

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::EventPublishable

#publish_event

Instance Attribute Details

#holiday_dateObject (readonly)



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

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

#holiday_nameObject (readonly)



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

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:



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

scope :ca_past, -> { where(company_id: 2).where('holiday_date BETWEEN ? AND ?', 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:



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

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:



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

scope :in_past, -> { where(company_id: 3).where('holiday_date BETWEEN ? AND ?', 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:



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

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:



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

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

.sync_all_holidays_to_calendarsObject

Class method to sync all holidays to all employees



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

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:



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

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:



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

scope :usa_past, -> { where(company_id: 1).where('holiday_date BETWEEN ? AND ?', 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:



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

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

Instance Method Details

#companyCompany

Returns:

See Also:



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

belongs_to :company

#send_to_google_calendar(action) ⇒ Object

Legacy method for backwards compatibility



130
131
132
# File 'app/models/company_holiday.rb', line 130

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

Returns:

  • (Hash)

    with :synced_count and :failed_count



93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'app/models/company_holiday.rb', line 93

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



36
37
38
39
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
# File 'app/models/company_holiday.rb', line 36

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

  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