Class: EmployeeEvent
- Inherits:
-
ApplicationRecord
- Object
- ActiveRecord::Base
- ApplicationRecord
- EmployeeEvent
- Includes:
- Models::Auditable
- Defined in:
- app/models/employee_event.rb
Overview
== Schema Information
Table name: employee_events
Database name: primary
id :integer not null, primary key
accumulated :integer
authorized :boolean default(FALSE)
avoid_lunch :boolean default(FALSE)
backup :string
broken_down :boolean default(FALSE)
broken_down_time :integer
date :date
day_block_departments :text
description :string(750)
event_time :integer
from_hour :time
from_hour_legacy :time
over_budget_alert :boolean default(FALSE)
status :enum default("working")
sub_status :integer
time_to_add :integer
to_hour :time
to_hour_legacy :time
created_at :datetime
updated_at :datetime
backup_employee_id :integer
employee_id :integer
Indexes
employee_events_backup_employee_id_idx (backup_employee_id)
index_employee_events_on_date (date)
index_employee_events_on_employee_id (employee_id)
Foreign Keys
fk_rails_... (backup_employee_id => parties.id) ON DELETE => nullify
fk_rails_... (employee_id => parties.id) ON DELETE => restrict
Constant Summary collapse
- STATES_FOR_GOOGLE_CALENDAR =
States for google calendar.
%w[working_no_calls not_working_timeoff partial_timeoff not_working_unpaid_timeoff short_term_disability jury_duty bereavement birthday company_holiday community_service working_on_service parental_leave].freeze
- MAX_GOOGLE_REFRESH_ATTEMPTS =
One refresh per sync, mirroring TimeOffRequests::GoogleCalendar::MAX_RETRIES.
A token that refreshes cleanly and still 401s means the grant is bad, not
stale — retrying that spins against Google and hangs the Sidekiq job. 1- STATUS_COLOR =
Status color.
{ working_no_calls: "#f0ad4e", not_working_closed: "#d7d7d7", not_working_timeoff: "#d9534f", partial_timeoff: "#94E994", not_working_unpaid_timeoff: "#d9534f", short_term_disability: "#337ab7", jury_duty: "#337ab7", bereavement: "#337ab7", birthday: "#5bc0de", community_service: "#5bc0de", parental_leave: "#5bc0de" }.freeze
Constants included from Models::Auditable
Models::Auditable::ALWAYS_IGNORED
Constants included from Models::Schedulable
Models::Schedulable::SIMPLE_FORM_OPTIONS
Instance Attribute Summary collapse
-
#date ⇒ Object
readonly
Pending: implement a validation that does not allow overlapping of time event of same status for a given time range/employee validates_uniqueness_of :employee_id, :scope => :date.
-
#employee_id ⇒ Object
readonly
Pending: implement a validation that does not allow overlapping of time event of same status for a given time range/employee validates_uniqueness_of :employee_id, :scope => :date.
-
#status ⇒ Object
readonly
Pending: implement a validation that does not allow overlapping of time event of same status for a given time range/employee validates_uniqueness_of :employee_id, :scope => :date.
Belongs to collapse
Methods included from Models::Auditable
Class Method Summary collapse
- .banked_time_select_options ⇒ void
-
.by_month ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are by month.
-
.dates_blocked ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are dates blocked.
-
.events_needing_attention ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are events needing attention.
-
.from_active_employees ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are from active employees.
-
.full_day_requests ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are full day requests.
-
.future_events ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are future events.
-
.partial_day_requests ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are partial day requests.
- .status_report_options ⇒ void
-
.status_select_options(options = {}) ⇒ Array
Returns employee event statuses grouped for a select list.
-
.time_off_events ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are time off events.
Instance Method Summary collapse
- #full_day? ⇒ Boolean
- #is_partial? ⇒ Boolean
- #pto_taken ⇒ void
- #send_to_google_calendar(action) ⇒ void
Methods included from Models::Auditable
#all_skipped_columns, #audit_reference_data, #should_not_save_version, #stamp_record
Methods inherited from ApplicationRecord
ransackable_associations, ransackable_attributes, ransackable_scopes, ransortable_attributes, #to_relation
Methods included from Models::Schedulable
Methods included from Models::AfterCommittable
Methods included from Models::EventPublishable
Instance Attribute Details
#date ⇒ Object (readonly)
Pending: implement a validation that does not allow overlapping of time event of same status for a given time range/employee
validates_uniqueness_of :employee_id, :scope => :date
Validations:
55 |
# File 'app/models/employee_event.rb', line 55 validates :employee_id, :status, :date, presence: true |
#employee_id ⇒ Object (readonly)
Pending: implement a validation that does not allow overlapping of time event of same status for a given time range/employee
validates_uniqueness_of :employee_id, :scope => :date
Validations:
55 |
# File 'app/models/employee_event.rb', line 55 validates :employee_id, :status, :date, presence: true |
#status ⇒ Object (readonly)
Pending: implement a validation that does not allow overlapping of time event of same status for a given time range/employee
validates_uniqueness_of :employee_id, :scope => :date
Validations:
55 |
# File 'app/models/employee_event.rb', line 55 validates :employee_id, :status, :date, presence: true |
Class Method Details
.banked_time_select_options ⇒ void
This method returns an undefined value.
142 143 144 145 |
# File 'app/models/employee_event.rb', line 142 def self. # (0..8).to_a.map{|i| [STATUS[i],i]} banked_times.map { |n, v| [n.to_s.humanize, v] } if CurrentScope.user&.is_manager? end |
.by_month ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are by month. Active Record Scope
92 |
# File 'app/models/employee_event.rb', line 92 scope :by_month, ->(d) { where(date: d.all_month) } |
.dates_blocked ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are dates blocked. Active Record Scope
104 |
# File 'app/models/employee_event.rb', line 104 scope :dates_blocked, -> { where(status: 'block_a_day') } |
.events_needing_attention ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are events needing attention. Active Record Scope
98 |
# File 'app/models/employee_event.rb', line 98 scope :events_needing_attention, ->(date = Date.current) { where('date = ? OR authorized = ?', date, false).where.not(status: %w[add_banked_time block_a_day]) } |
.from_active_employees ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are from active employees. Active Record Scope
101 |
# File 'app/models/employee_event.rb', line 101 scope :from_active_employees, -> { where(employee_id: Employee.active_employees.select(:id)) } |
.full_day_requests ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are full day requests. Active Record Scope
93 |
# File 'app/models/employee_event.rb', line 93 scope :full_day_requests, -> { where(from_hour: '00:00:00') } |
.future_events ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are future events. Active Record Scope
102 |
# File 'app/models/employee_event.rb', line 102 scope :future_events, -> { where("date > (?)", Date.current) } |
.partial_day_requests ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are partial day requests. Active Record Scope
94 |
# File 'app/models/employee_event.rb', line 94 scope :partial_day_requests, -> { where.not(from_hour: '00:00:00') } |
.status_report_options ⇒ void
This method returns an undefined value.
136 137 138 139 |
# File 'app/models/employee_event.rb', line 136 def self. [['Time Off', 'not_working_timeoff'], ['Unpaid Time Off', 'not_working_unpaid_timeoff'], ['Short Term Disability', 'short_term_disability'], ['Jury Duty', 'jury_duty'], ['Bereavement', 'bereavement'], ['Birthday', 'birthday'], ['Parental Leave', 'parental_leave']] end |
.status_select_options(options = {}) ⇒ Array
Returns employee event statuses grouped for a select list.
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'app/models/employee_event.rb', line 118 def self.( = {}) = statuses.reject { |n, _v| n.to_s != "add_banked_time" && n.to_s != "block_a_day" }.map { |n, v| [n.to_s.humanize, v] } = statuses.reject do |n, _v| %w[add_banked_time working not_working_closed partial_timeoff block_a_day company_holiday].include?(n.to_s) end.map { |n, v| [n.to_s.humanize, v] } if [:current_user]&.is_manager? || [:include_all_options] [ ['Time Off types', ], ['Admin only', ] ] else [ ['Time Off types', ] ] end end |
.time_off_events ⇒ ActiveRecord::Relation<EmployeeEvent>
A relation of EmployeeEvents that are time off events. Active Record Scope
103 |
# File 'app/models/employee_event.rb', line 103 scope :time_off_events, -> { where.not(status: 'block_a_day') } |
Instance Method Details
#backup_employee ⇒ Employee?
51 |
# File 'app/models/employee_event.rb', line 51 belongs_to :backup_employee, class_name: 'Employee', optional: true |
#employee ⇒ Employee?
49 |
# File 'app/models/employee_event.rb', line 49 belongs_to :employee, optional: true |
#full_day? ⇒ Boolean
148 149 150 |
# File 'app/models/employee_event.rb', line 148 def full_day? from_hour == to_hour end |
#is_partial? ⇒ Boolean
153 154 155 |
# File 'app/models/employee_event.rb', line 153 def is_partial? status == "not_working_timeoff" && from_hour != to_hour end |
#pto_taken ⇒ void
This method returns an undefined value.
158 159 160 161 162 |
# File 'app/models/employee_event.rb', line 158 def pto_taken taken = 0 taken = (event_time || 0) - (broken_down_time || 0) if status.in?(%w[not_working_timeoff partial_timeoff]) taken end |
#send_to_google_calendar(action) ⇒ void
168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'app/models/employee_event.rb', line 168 def send_to_google_calendar(action) # Through the shared authorizer, which refreshes ahead of the stored expiry. # Hand-building the client here skipped google_auth_expires_at entirely, so # this path always spent a failing API call first and recovered on the 401 — # the very thing recording the expiry was meant to stop. client = TimeOffRequests::GoogleAuthService.(employee) return if client.nil? service = Google::Apis::CalendarV3::CalendarService.new service. = client # Now lets use it case action when 'create' if status.in?(STATES_FOR_GOOGLE_CALENDAR) define_event_times event = Google::Apis::CalendarV3::Event.new( id: "ee#{id}", summary: status.titleize, description: description, start: @start_time, end: @end_time ) service.insert_event(employee.email, event) end when 'update' if status.in?(STATES_FOR_GOOGLE_CALENDAR) define_event_times event = service.get_event(employee.email, "ee#{id}") event.summary = status.titleize event.description = description event.start = @start_time event.end = @end_time service.update_event(employee.email, "ee#{id}", event) end when 'destroy' begin service.delete_event(employee.email, "ee#{id}") rescue StandardError nil end end rescue Google::Apis::AuthorizationError begin # Counted in an ivar, not a local: `retry` re-runs the method body from # the top, so a counter reset there would never terminate the loop. if (@google_refresh_attempts = @google_refresh_attempts.to_i + 1) > MAX_GOOGLE_REFRESH_ATTEMPTS return TimeOffRequests::GoogleAuthService.report_refresh_exhausted( employee, custom_data: { model: 'EmployeeEvent', employee_event_id: id, action: action } ) end # Through the shared refresher so google_auth_expires_at is recorded — # a local client.refresh! here would leave the stored expiry pointing at # the previous token and defeat proactive refresh everywhere else. raise 'Google token refresh failed' unless TimeOffRequests::GoogleAuthService.refresh_token(employee) retry rescue StandardError => e ErrorReporting.error e end rescue Google::Apis::ClientError => e if e.status_code == 409 # The event already exists, so we update it. service.update_event(employee.email, "ee#{id}", event) end ensure # Scoped to one invocation. `retry` restarts the body without unwinding, so # the counter survives the retry loop but not the next create/update/destroy # on this same record — which would otherwise start at 1 and report # exhaustion without ever attempting a refresh. @google_refresh_attempts = nil end |