class ReservationsController < ApplicationController before_action :set_company before_action :set_reservation, only: %i[show edit update destroy] layout :determine_layout # GET /reservations or /reservations.json def index @reservations = Reservation.includes(:team, :customer).where(company: @company) @reservations = ActiveModelSerializers::SerializableResource.new( @reservations, each_serializer: ReservationSerializer ).as_json end # GET /reservations/1 or /reservations/1.json def show; end # GET /reservations/new def new # logger.debug "--- Reservations#new --- Params received: #{params.inspect}" # Use Time.zone.parse to interpret times within the application's configured timezone start_time_param = params[:start_time] end_time_param = params[:end_time] # logger.debug "--- Reservations#new --- Start Param: #{start_time_param}, End Param: #{end_time_param}" start_time = start_time_param ? Time.zone.parse(start_time_param) : Time.current end_time = end_time_param ? Time.zone.parse(end_time_param) : Time.current + 30.minutes # logger.debug "--- Reservations#new --- Parsed Start: #{start_time}, Parsed End: #{end_time}" @reservation = Reservation.new(start_time: start_time, end_time: end_time) # logger.debug "--- Reservations#new --- Reservation object initialized: #{@reservation.attributes.inspect}" @reservation.team = @company.teams.first # Assign default team @customers = @company.customers # Preload customers for potential dropdown end # GET /reservations/1/edit def edit; end # POST /reservations or /reservations.json def create @reservation = @company.reservations.new( reservation_params.except( :customer_id, :customer_first_name, :customer_surname, :customer_original_phone, :customer_birth_year, :customer_composite_key ) ) # Find or create customer based on submitted attributes find_or_create_customer # Associate the reservation with the found/created customer's primary key attributes assign_customer_to_reservation if @reservation.save redirect_to reservations_url, notice: t('.reservation_created') else @customers = @company.customers render :new, status: :unprocessable_entity end rescue ActiveRecord::RecordInvalid => e # If customer creation/validation fails @reservation.errors.add(:base, "Failed to save customer: #{e.message}") @customers = @company.customers render :new, status: :unprocessable_entity end # PATCH/PUT /reservations/1 or /reservations/1.json def update # Separate reservation attributes from customer attributes customer_attrs = build_customer_attributes # Use existing helper reservation_attrs = reservation_params.except( :customer_id, :customer_first_name, :customer_surname, :customer_original_phone, :customer_birth_year, :customer_composite_key ) # Find the customer identified by the submitted name/phone @customer = Customer.find_by( first_name: customer_attrs[:first_name], surname: customer_attrs[:surname], original_phone: customer_attrs[:original_phone], company_id: @company.id ) # Update customer phone/birthyear if found (identifiers assumed immutable here) customer_updated = @customer ? @customer.update(customer_attrs.slice(:phone, :birthyear)) : false # Assign the correct customer foreign keys to the reservation attributes if @customer reservation_attrs[:customer_first_name] = @customer.first_name reservation_attrs[:customer_surname] = @customer.surname reservation_attrs[:customer_original_phone] = @customer.original_phone else # Fall back to original keys if form customer wasn't found (e.g., identifiers changed) # Consider adding an error or different handling if customer *must* be found. reservation_attrs[:customer_first_name] = @reservation.customer_first_name reservation_attrs[:customer_surname] = @reservation.customer_surname reservation_attrs[:customer_original_phone] = @reservation.customer_original_phone end reservation_updated = @reservation.update(reservation_attrs) respond_to do |format| # Check if reservation update was successful # (We might ignore customer_updated status for now, or add more complex checks) if reservation_updated format.html { redirect_to reservations_url, notice: t('.reservation_updated') } format.json { render :show, status: :ok, location: @reservation } else @customers = @company.customers # Reload for form format.html { render :edit, status: :unprocessable_entity } format.json { render json: @reservation.errors, status: :unprocessable_entity } end end rescue ActiveRecord::RecordInvalid => e # Catch potential customer update errors @reservation.errors.add(:base, "Failed to save customer: #{e.message}") if @customer&.invalid? @customers = @company.customers render :edit, status: :unprocessable_entity end # DELETE /reservations/1 or /reservations/1.json def destroy @reservation.destroy! respond_to do |format| format.html { redirect_to reservations_url, notice: t('.reservation_destroyed') } format.json { head :no_content } end end private # Use callbacks to share common setup or constraints between actions. def set_reservation @reservation = Reservation.find(params[:id]) end # Only allow a list of trusted parameters through. def reservation_params # Permit composite key if form uses it, otherwise permit individual fields # params.require(:reservation).permit(:customer_composite_key, ...) params.require(:reservation).permit( :team_id, :start_time, :end_time, :customer_first_name, :customer_surname, :customer_original_phone, :customer_birth_year, :customer_composite_key, :customer_id # Allow this if select still sends it sometimes ) end def determine_layout action_name == 'index' ? 'calendar' : 'application' end # Finds or creates customer based on submitted fields def find_or_create_customer customer_params = build_customer_attributes @customer = if new_customer? Customer.create!(customer_params) else find_or_initialize_customer(customer_params) end end # Extracts customer attributes from reservation form parameters def build_customer_attributes { first_name: params[:reservation][:customer_first_name], surname: params[:reservation][:customer_surname], original_phone: params[:reservation][:customer_original_phone], phone: params[:reservation][:customer_original_phone], # Assuming phone is same as original_phone for now birthyear: params[:reservation][:customer_birth_year], company_id: @company.id } end # Checks if the submitted customer ID indicates a new customer def new_customer? # Check based on the specific value format used by TomSelect create function params[:reservation][:customer_composite_key]&.end_with?('__new') || params[:reservation][:customer_id]&.end_with?('__new') # Fallback if old key is used end # Finds customer by composite key or creates them def find_or_initialize_customer(attributes) # Find using the composite key fields Customer.find_or_create_by!( first_name: attributes[:first_name], surname: attributes[:surname], original_phone: attributes[:original_phone] # company_id: attributes[:company_id] # Scope to company if needed ) do |customer| # Assign other attributes only if creating customer.assign_attributes(attributes.slice(:phone, :birthyear, :company_id)) end end # Sets the foreign key fields on the reservation based on the found/created customer def assign_customer_to_reservation return unless @customer # Guard clause @reservation.customer_first_name = @customer.first_name @reservation.customer_surname = @customer.surname @reservation.customer_original_phone = @customer.original_phone end # Override the application controller method to include teams def set_company company_id = session[:company_id] unless company_id && Company.exists?(company_id) company_id = Company.first&.id session[:company_id] = company_id end @company = Company.includes(:teams).find(company_id) if company_id unless @company redirect_to companies_path, alert: 'No company found. Please create a company first.' return end end end