Class: Crm::TimeOffRequestsController

Inherits:
CrmController show all
Includes:
Controllers::Workflowable
Defined in:
app/controllers/crm/time_off_requests_controller.rb

Overview

CRUD for TimeOffRequest records, plus the balance calculator,
conflict/blocked-date lookups, and Google Calendar sync used by the
request form and detail page.

Constant Summary

Constants included from Controllers::ReferenceFindable

Controllers::ReferenceFindable::ID_EMBEDDED_PATTERNS

Constants included from Controllers::AnalyticsEvents

Controllers::AnalyticsEvents::MAX_QUEUED_EVENTS, Controllers::AnalyticsEvents::SESSION_KEY

Constants included from Controllers::ErrorRendering

Controllers::ErrorRendering::NON_CONTENT_PATH_PREFIXES

Instance Method Summary collapse

Methods included from Controllers::Workflowable

#render_workflow_error_stream, #render_workflow_success_stream, #workflow_action, #workflow_action_complete

Methods inherited from CrmController

#access_denied, #context_id, #context_object, #crm_home_path, #current_ability, #default_url_options, #download_temp, #get_tempfile_path_for_download, #init_status_job_collector, #initialize_crm_lazy_chunks, #persist_enqueued_status_jobs, #record_not_found, #redirect_to_job_or_fallback, #render_edit_action, #set_context, #set_download_path, #stash_file_for_temp_download, #sync_admin_presence_cookie, #touch_employee_last_seen

Methods inherited from ApplicationController

#account_impersonated?, #add_to_flash, #after_sign_in_path_for, #bypass_forgery_protection?, #chat_enabled?, #cloudflare_cleared?, #default_catalog, #default_url_options, #enable_turbo_frames, #find_publication, #fix_invalid_accept_header, #init_js_utils, #is_globals_call?, #layout_by_resource, #locale_store, #redirect_to, #require_employee_for_crm, #set_base_host, #set_real_ip, #set_report_errors_for, #should_render_layout?, #skip_layout_for_turbo_frame?, #stamp_impersonation_context, #tab_frame_breakout_request?, #warmlyyours_canada_ip?, #warmlyyours_ip?, #y

Methods included from Controllers::ReturnPathHandling

#check_for_return_path, #redirect_to_return_path_or_default

Methods included from Controllers::AnalyticsEvents

#consume_queued_analytics_events, #registration_lead_type, #track_event

Methods included from Controllers::DeviceDetection

#device_detector, #is_ie?

Methods included from Controllers::SubdomainDetection

#is_crm_request?, #is_www_request?, #json_request?

Methods included from Controllers::TurboSafeRedirect

#redirect_to

Methods included from Controllers::TrackingDetection

#bot_request?, #gdpr_country?, #gdpr_country_data, #prevent_bots, #set_tracking_cookie, #track_visitor?

Methods included from Controllers::AcceleratedFileSending

#send_file_accelerated, #send_upload_accelerated

Methods included from Controllers::ErrorRendering

#excp_string, #mail_to_for_error_reporting, #render_400, #render_404, #render_406, #render_410, #render_500, #render_invalid_authenticity_token, #render_ip_spoof_error, #render_unpermitted_parameters, #safe_referer_or_fallback

Methods included from Controllers::TurnstileVerification

#load_turnstile_script_tag, #turnstile_lazy_widget, #turnstile_script_tag, #turnstile_widget, #validate_turnstile!

Methods included from Controllers::CloudflareCaching

edge_cached, #edge_cached_action?, #reset_cloudflare_cache, #set_cloudflare_cache, #skip_edge_cache!, #skip_session

Methods included from Controllers::Webpackable

#preload_webpack_fonts, #webpack_css_include, #webpack_css_url, #webpack_js_include, #wpd_is_running?

Methods included from Controllers::Localizable

#cloudflare_country_locale, #determine_request_locale, #geocoder_locale, #guest_user_locale_check, #locale_optional_www_auth_path?, #param_locale, #set_locale, #set_request_locale, #skip_localization?, #warmlyyours_ip_locale

Methods included from Controllers::Authenticable

#access_denied, #authenticate_account, #authenticate_account!, #authenticate_account_from_login_token!, #check_is_a_manager, #check_is_a_sales_manager, #check_is_an_admin, #check_is_an_employee, #check_party, #clear_mismatched_guest_user, #create_guest_user, #credentials?, #current_or_guest_user, #current_or_guest_user_id_read_only, #current_user, #devise_mapping, #fully_logged_in?, #generate_bot_id, #guest_user, #identifiable?, #init_current_user, #initialize_guest, #load_context_user, #logging_in, #resource, #resource_name, #restrict_access_for_non_employees, #scrubbed_request_path, #user_object, #warn_on_session_guest_id_leak

Methods included from UrlsHelper

#catalog_breadcrumb_links, #catalog_link, #catalog_link_for_product_line, #catalog_link_for_sku, #cms_link, #delocalized_path, #path_to_sales_product_sku, #path_to_sales_product_sku_for_product_line, #path_to_sales_product_sku_for_product_line_slug, #product_line_from_catalog_link, #protocol_neutral_url, #sanitize_external_url, #valid_external_url?

Instance Method Details

#createvoid

This method returns an undefined value.

Creates a time off request for the nested-route employee (never trusts
a form-submitted employee_id).



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

def create
  @time_off_request = TimeOffRequest.new(params[:time_off_request])
  # The employee is fixed by the nested route — never trust the form's
  # employee_id (a tampered POST could file a request under someone else).
  @time_off_request.employee_id = params[:employee_id]
  @employee = @time_off_request.employee
  @time_off_balances_view = TimeOffBalanceCalculator.new(employee: @employee, date: @time_off_request.start_date).formatted_balances
  @special_dates = fetch_special_dates(@employee)
  @work_schedule = @time_off_request.employee.work_schedules.effective_now.first

  if @time_off_request.save
    if @time_off_request.google_calendar_error.present?
      flash[:alert] = @time_off_request.google_calendar_error
    else
      flash[:notice] = 'Time-off request created successfully.'
    end
    redirect_to user_time_off_path(@employee, tab: "time_off_requests")
  else
    flash.now[:alert] = "Errors present: #{@time_off_request.errors.full_messages.to_sentence}"
    render :new, status: :unprocessable_content
  end
end

#editvoid

This method returns an undefined value.

Loads the request to edit via #prevent_past_edits and rebuilds the
balance calculator/special-dates data for the form.



82
83
84
85
86
87
88
89
90
# File 'app/controllers/crm/time_off_requests_controller.rb', line 82

def edit
  @time_off_request = TimeOffRequest.find(params.expect(:id))
  @employee = @time_off_request.employee
  @work_schedule = @employee.work_schedules.effective_now.first
  @special_dates = fetch_special_dates(@employee)
  # Call the balance calculator for all time off types as of today
  as_of_date = params[:calculator_date].present? ? Date.parse(params[:calculator_date]) : Date.current
  @time_off_balances_view = TimeOffBalanceCalculator.new(employee: @employee, date: as_of_date).formatted_balances
end

#indexvoid

This method returns an undefined value.

Lists an employee's time off requests.



14
15
16
17
# File 'app/controllers/crm/time_off_requests_controller.rb', line 14

def index
  @employee = Employee.find(params.expect(:employee_id))
  @time_off_requests = @employee.time_off_requests
end

#newvoid

This method returns an undefined value.

Builds a new time off request and the balance calculator/special-dates
data the form needs, redirecting if the employee has no active work
schedule.



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
# File 'app/controllers/crm/time_off_requests_controller.rb', line 52

def new
  redirect_to employees_path, error: 'Employee ID parameter not provided.' if params[:employee_id].blank?

  @employee = Employee.find(params.expect(:employee_id))
  @work_schedule = @employee.work_schedules.effective_now.first

  if @work_schedule.present?
    @time_off_request = @employee.time_off_requests.new
    @time_off_request.time_off_request_dates.build
    @special_dates = fetch_special_dates(@employee)

    # Call the balance calculator for all time off types as of today
    as_of_date = if params[:calculator_date].present?
                   Date.parse(params[:calculator_date])
                 elsif params[:start_date].present?
                   Date.parse(params[:start_date])
                 else
                   Date.current
                 end

    @time_off_balances_view = TimeOffBalanceCalculator.new(employee: @employee, date: as_of_date).formatted_balances
  else
    redirect_to user_time_off_path(@employee), error: "#{@employee.full_name} doesn't have an active work schedule."
  end
end

#retrieve_date_detailsvoid

This method returns an undefined value.

AJAX endpoint: computes the request-date breakdown (available dates,
overlaps, time available) for the given date range and type.



146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'app/controllers/crm/time_off_requests_controller.rb', line 146

def retrieve_date_details
  start_date = Date.parse(params[:start_date])
  end_date = Date.parse(params[:end_date])
  time_off_type_id = params[:time_off_type]
  employee_id = params[:employee_id]
  time_off_request_id = params[:time_off_request_id]

  # Validate that required parameters are present
  if start_date && end_date && time_off_type_id && employee_id
    result = TimeOffRequest.generate_request(start_date, end_date, time_off_type_id, employee_id, time_off_request_id)
    if result[:error].present?
      render json: { error: result[:error], dates: [] }, status: :unprocessable_content
    else
      render json: { dates: result[:dates], overlapping_dates: result[:overlapping_dates], time_available_for_request: result[:time_available_for_request] }
    end
  else
    render json: { error: 'Missing time off request parameters' }, status: :unprocessable_content
  end
end

#showvoid

This method returns an undefined value.

Shows a request with its blocked dates, holidays, conflicts, and
Google Calendar sync status.



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'app/controllers/crm/time_off_requests_controller.rb', line 23

def show
  @time_off_request = TimeOffRequest.includes(:employee, :time_off_request_dates).find(params.expect(:id))
  @employee = @time_off_request.employee
  @blocked_dates = fetch_blocked_dates(@time_off_request)
  @company_holidays = fetch_holidays(@time_off_request)
  @conflicting_requests = fetch_conflicting_requests(@time_off_request)
  @work_schedule = @employee.work_schedules.effective_now.first

  @google_calendar_event = TimeOffRequests::GoogleCalendar.new(@time_off_request).fetch_event if @time_off_request.google_event_id.present?

  google_auth_status = TimeOffRequests::GoogleAuthService.check_status(@employee)
  @is_own_request = current_user&.id == @employee.id
  @has_auth_error = google_auth_status[:needs_reconnect] ||
                    (@google_calendar_event.present? &&
                     @google_calendar_event[:error].present? &&
                     @google_calendar_event[:error].to_s.downcase.match?(/authoriz|reconnect|expired/))

  @time_off_types_by_name = if @time_off_request.balance_snapshot.present?
                              TimeOffType.where(name: @time_off_request.balance_snapshot.keys).index_by(&:name)
                            else
                              {}
                            end
end

#sync_google_calendarvoid

This method returns an undefined value.

Pushes/pulls the request's Google Calendar event and redirects with
the result.



170
171
172
173
174
175
176
177
178
179
180
181
# File 'app/controllers/crm/time_off_requests_controller.rb', line 170

def sync_google_calendar
  @time_off_request = TimeOffRequest.find(params.expect(:id))
  @employee = @time_off_request.employee

  result = TimeOffRequests::GoogleCalendar.new(@time_off_request).sync_event

  if result[:success]
    redirect_to time_off_request_path(@time_off_request), notice: result[:message]
  else
    redirect_to time_off_request_path(@time_off_request), alert: result[:error]
  end
end

#updatevoid

This method returns an undefined value.

Updates the request loaded by #prevent_past_edits (never lets the
employee change).



123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'app/controllers/crm/time_off_requests_controller.rb', line 123

def update
  @time_off_request = TimeOffRequest.find(params.expect(:id))
  @employee = @time_off_request.employee
  @work_schedule = @time_off_request.employee.work_schedules.effective_now.first

  # A request never moves between employees — ignore any employee_id sent.
  if @time_off_request.update(params[:time_off_request].except(:employee_id))
    if @time_off_request.google_calendar_error.present?
      flash[:alert] = @time_off_request.google_calendar_error
    else
      flash[:notice] = 'Request successfully updated.'
    end
    redirect_to user_time_off_path(@employee, tab: "time_off_requests")
  else
    flash.now[:alert] = "Errors present: #{@time_off_request.errors.full_messages.to_sentence}"
    render :edit, status: :unprocessable_content
  end
end