Class: Event

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

Overview

== Schema Information

Table name: events
Database name: primary

id :integer not null, primary key
all_day :boolean default(FALSE), not null
description :string
end_time :datetime
event_type :enum
name :string
start_time :datetime
time_zone :string default("America/Chicago"), not null
created_at :datetime not null
updated_at :datetime not null
creator_id :integer
resource_id :integer

CRM marketing/PR event tracked on the calendar.

Has a date + start/end times interpreted in time_zone (default
"America/Chicago"). One or more authors are assigned via the
event_authors join table; creator_id is the audit record of who
first added the event and is set automatically from the current user.

Constant Summary collapse

DEFAULT_TIME_ZONE =
'America/Chicago'
TIME_ZONE_OPTIONS =

US-focused dropdown for the form, ordered west→east.

[
  ['Hawaii (HST)',        'Pacific/Honolulu'],
  ['Alaska (AKST/AKDT)',  'America/Anchorage'],
  ['Pacific (PST/PDT)',   'America/Los_Angeles'],
  ['Mountain (MST/MDT)',  'America/Denver'],
  ['Arizona (MST)',       'America/Phoenix'],
  ['Central (CST/CDT)',   'America/Chicago'],
  ['Eastern (EST/EDT)',   'America/New_York']
].freeze

Constants included from Schedulable

Schedulable::SIMPLE_FORM_OPTIONS

Instance Attribute Summary collapse

Has many collapse

Instance Method Summary collapse

Methods inherited from ApplicationRecord

ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation

Methods included from Schedulable

config

Methods included from Models::AfterCommittable

#after_commit

Methods included from Models::EventPublishable

#publish_event

Instance Attribute Details

#confirm_conflictObject

Set to '1' (string) on form submit to bypass the double-booking guard for
this save. Used by the "Save anyway" button surfaced after a conflict.



63
64
65
# File 'app/models/event.rb', line 63

def confirm_conflict
  @confirm_conflict
end

#end_dateObject

End date for multi-day events. Defaults to the start date when blank, so a
single-day event only needs event_date.



95
96
97
# File 'app/models/event.rb', line 95

def end_date
  @end_date || end_time&.in_time_zone(effective_time_zone)&.to_date&.iso8601
end

#end_time_of_dayObject



105
106
107
# File 'app/models/event.rb', line 105

def end_time_of_day
  @end_time_of_day || end_time&.in_time_zone(effective_time_zone)&.strftime('%H:%M')
end

#event_dateObject

─── Virtual form inputs (start/end dates + wall-clock times) ──────────
The form lets the user pick a start date, an optional end date (for
multi-day events), and — unless the event is all-day — two wall-clock
times, all interpreted in time_zone. Combined into UTC start/end
timestamps in assemble_times_from_local_inputs so the DB is
timezone-agnostic. All-day events store start-of-day → end-of-day.



89
90
91
# File 'app/models/event.rb', line 89

def event_date
  @event_date || start_time&.in_time_zone(effective_time_zone)&.to_date&.iso8601
end

#event_typeObject (readonly)



72
# File 'app/models/event.rb', line 72

validates :event_type, presence: true

#excluded_conflict_event_idObject

Edit_mode in Crm::EventsController#create destroys the previous Event and
creates a fresh one. Without this hint, the new (unsaved) Event sees its
own predecessor as a conflict and 422s the Update. Set this to the soon-
to-be-destroyed Event's id so the double-booking check skips it.



69
70
71
# File 'app/models/event.rb', line 69

def excluded_conflict_event_id
  @excluded_conflict_event_id
end

#nameObject (readonly)



71
# File 'app/models/event.rb', line 71

validates :name, presence: true

#start_time_of_dayObject



101
102
103
# File 'app/models/event.rb', line 101

def start_time_of_day
  @start_time_of_day || start_time&.in_time_zone(effective_time_zone)&.strftime('%H:%M')
end

#time_zoneObject (readonly)



73
74
# File 'app/models/event.rb', line 73

validates :time_zone, presence: true,
inclusion: { in: ->(_) { TIME_ZONE_OPTIONS.map(&:last) + [DEFAULT_TIME_ZONE] } }

Instance Method Details

#author_conflictsObject

Conflicting events for the current author set, excluding self. Two events
conflict when their [start, end) intervals overlap for the same employee.



133
134
135
136
137
138
139
140
141
142
143
# File 'app/models/event.rb', line 133

def author_conflicts
  return Event.none if start_time.blank? || end_time.blank? || author_ids.compact_blank.blank?

  excluded_ids = [id, excluded_conflict_event_id].compact_blank
  scope = Event.all
  scope = scope.where.not(id: excluded_ids) if excluded_ids.any?
  scope.where('events.start_time < ? AND events.end_time > ?', end_time, start_time)
       .joins(:event_authors)
       .where(event_authors: { employee_id: author_ids })
       .distinct
end

#authorsActiveRecord::Relation<Author>

validate: false — Rails would otherwise run each Employee's own
validations whenever Event#valid? is called, which inherits any preexisting
data debt on legacy Employee/Party records (e.g. missing employee_record
rows) and surfaces as a phantom "Authors is invalid" error on the Event.

Returns:

  • (ActiveRecord::Relation<Author>)

See Also:



59
# File 'app/models/event.rb', line 59

has_many :authors, through: :event_authors, source: :employee, validate: false

#calendar_endObject



124
125
126
127
128
129
# File 'app/models/event.rb', line 124

def calendar_end
  return if end_time.blank?

  local = end_time.in_time_zone(effective_time_zone)
  all_day? ? (local.to_date + 1).iso8601 : local.strftime('%Y-%m-%dT%H:%M:%S')
end

#calendar_startObject

─── FullCalendar serialization ────────────────────────────────────────
All-day events use bare dates; FullCalendar treats the end date as
EXCLUSIVE, so we emit the day after the (inclusive) end date.



117
118
119
120
121
122
# File 'app/models/event.rb', line 117

def calendar_start
  return if start_time.blank?

  local = start_time.in_time_zone(effective_time_zone)
  all_day? ? local.to_date.iso8601 : local.strftime('%Y-%m-%dT%H:%M:%S')
end

#effective_time_zoneObject



109
110
111
# File 'app/models/event.rb', line 109

def effective_time_zone
  time_zone.presence || DEFAULT_TIME_ZONE
end

#event_authorsActiveRecord::Relation<EventAuthor>

Returns:

See Also:



54
# File 'app/models/event.rb', line 54

has_many :event_authors, dependent: :destroy, inverse_of: :event

#has_author_conflict?Boolean

Returns:

  • (Boolean)


145
146
147
# File 'app/models/event.rb', line 145

def has_author_conflict?
  @has_author_conflict == true
end