chatwoot-develop/enterprise/app/controllers/public/api/v1/captain/reservations_controller.rb

165 lines
5.7 KiB
Ruby

class Public::Api::V1::Captain::ReservationsController < ActionController::API
# Use basic rescue_from for generic error handling to capture 'Inbox not found' or similar if they bubble up
rescue_from StandardError, with: :handle_standard_error
def create
Rails.logger.info '[Captain::Booking] 🏁 Starting Reservation Creation'
Rails.logger.info "[Captain::Booking] Params: #{params.to_unsafe_h.except(:controller, :action)}"
# 1. Parse Params
# 3. Persistence Logic
ActiveRecord::Base.transaction do
# A. Find or Create Contact (Simple Logic for now)
contact = find_or_create_contact(reservation_params)
# A.1 Find or Create Conversation
# We need an inbox to create a conversation.
inbox_id = unit.account.inboxes.first&.id
raise 'Inbox not found' unless inbox_id
conversation = ::Conversation.create!(
account_id: unit.account_id,
inbox_id: inbox_id,
contact_id: contact.id,
contact_inbox_id: ::ContactInbox.find_by(contact_id: contact.id, inbox_id: inbox_id)&.id,
status: :open
)
# B. Create Reservation
@reservation = ::Captain::Reservation.create!(
account_id: unit.account_id,
inbox_id: conversation.inbox_id,
contact_id: contact.id,
contact_inbox_id: conversation.contact_inbox_id,
conversation_id: conversation.id,
captain_brand_id: unit.captain_brand_id,
captain_unit_id: unit.id,
suite_identifier: params[:selected_category] || 'Standard',
check_in_at: params[:check_in_at],
check_out_at: params[:check_out_at],
total_amount: params[:total_value] || params[:total_amount],
status: :pending_payment,
payment_status: :pending,
metadata: {
observacao: params[:observacao]
}
)
# C. Generate PIX (Real or Mock)
pix_result = generate_pix(unit, reservation_params)
raise "Pix Generation Failed: #{pix_result[:error]}" unless pix_result[:success]
# D. Save Pix Charge
::Captain::PixCharge.create!(
reservation_id: @reservation.id,
unit_id: unit.id,
txid: pix_result[:txid],
pix_copia_e_cola: pix_result[:pix_copy_paste],
status: 'active', # Means created/waiting
raw_webhook_payload: {}, # Will be filled by webhook later
paid_at: nil
)
Rails.logger.info "✅ Reservation ##{@reservation.id} Created & Persisted."
render_success(pix_result, @reservation.id)
end
rescue StandardError => e
Rails.logger.error "❌ Failed to create reservation: #{e.message}"
render json: { error: e.message }, status: :unprocessable_entity
end
def status
reservation = ::Captain::Reservation.find_by(id: params[:id])
if reservation
render json: {
id: reservation.id,
status: reservation.status,
payment_status: reservation.payment_status
}, status: :ok
else
render json: { error: 'Not Found' }, status: :not_found
end
end
private
def find_or_create_contact(params)
# Logic to reuse contact by email or phone
# For this context, we assume an Account Context is known (unit.account_id)
# Simplified for brevity. In a real scenario, use ContactBuilder.
account_id = ::Captain::Unit.find(params[:unit_id]).account_id
contact = ::Contact.find_by(email: params[:email], account_id: account_id)
contact ||= ::Contact.create!(
name: params[:contact_name] || params[:name],
email: params[:email],
phone_number: params[:phone],
account_id: account_id
)
# Ensure ContactInbox exists (required for Reservation model validation)
inbox = ::Inbox.where(account_id: account_id).first
if inbox && !::ContactInbox.exists?(contact_id: contact.id, inbox_id: inbox.id)
::ContactInbox.create!(contact: contact, inbox: inbox, source_id: contact.id)
end
contact
end
def generate_pix(unit, params)
if unit.inter_client_id.present?
service = ::Captain::InterService.new(
client_id: unit.inter_client_id,
client_secret: unit.inter_client_secret,
cert_path: unit.inter_cert_path,
key_path: unit.inter_key_path,
pix_key: unit.inter_pix_key,
account_number: unit.inter_account_number
)
service.create_pix_charge(params)
else
# Mock Return
{
success: true,
pix_copy_paste: 'MOCK_PIX_CODE_123',
qr_code_url: 'https://dummyimage.com/300x300/000/fff&text=MOCK+QR',
txid: "MOCK-#{SecureRandom.hex(10)}"
}
end
end
def unit
@unit ||= ::Captain::Unit.find(params[:unit_id])
end
def reservation_params
params.permit(
:brand_id, :unit_id, :contact_name, :phone_number, :email, :cpf,
:check_in_at, :check_out_at, :total_amount, :duration_minutes,
metadata: {}
)
end
def handle_standard_error(e)
Rails.logger.error '[Captain::Booking::Error] 🔥🔥 CRITICAL ERROR in ReservationsController 🔥🔥'
Rails.logger.error "[Captain::Booking::Error] Message: #{e.message}"
Rails.logger.error "[Captain::Booking::Error] Backtrace:\n#{e.backtrace.first(15).join("\n")}"
render json: { error: "Internal Server Error: #{e.message}", details: 'Check logs for Captain::Booking::Error' },
status: :internal_server_error
end
def render_success(data, reservation_id)
render json: {
success: true,
message: 'Reserva iniciada com sucesso.',
reservation_id: reservation_id,
metadata: {
pix: {
copyPasteCode: data[:pix_copy_paste],
qrCodeValue: data[:qr_code_url] || 'https://dummyimage.com/300x300/000/fff&text=QR+Code+Inter'
}
}
}, status: :ok
end
end