208 lines
5.9 KiB
Ruby
208 lines
5.9 KiB
Ruby
class Captain::Reservations::CreateService
|
|
def initialize(account:, params:, created_by: nil)
|
|
@account = account
|
|
@params = params
|
|
@created_by = created_by
|
|
end
|
|
|
|
def perform
|
|
reservation = Captain::Reservation.create!(reservation_params)
|
|
|
|
# Generate Pix Charge natively
|
|
if reservation.unit&.inter_credentials_present?
|
|
charge_result = generate_pix_charge(reservation)
|
|
if charge_result[:success]
|
|
reservation.metadata['pix'] = charge_result[:payload]
|
|
reservation.save!
|
|
else
|
|
Rails.logger.error "Pix Generation Failed: #{charge_result[:error]}"
|
|
end
|
|
end
|
|
|
|
schedule_automations(reservation)
|
|
reservation
|
|
end
|
|
|
|
private
|
|
|
|
attr_reader :account, :params, :created_by
|
|
|
|
def reservation_params
|
|
conversation = find_conversation
|
|
inbox = conversation&.inbox || find_inbox
|
|
contact_inbox = conversation&.contact_inbox || find_or_create_contact_inbox(inbox)
|
|
|
|
raise ArgumentError, 'Inbox not found' if inbox.blank?
|
|
raise ArgumentError, 'Contact not found' if contact_inbox&.contact.blank?
|
|
|
|
check_in_at = parse_time(params[:check_in_at])
|
|
raise ArgumentError, 'Check-in time is required' unless check_in_at
|
|
|
|
check_out_at = parse_time(params[:check_out_at]) || calculate_check_out(check_in_at)
|
|
|
|
brand = find_brand(params[:brand_id])
|
|
unit = find_unit(params[:unit_id])
|
|
|
|
{
|
|
account: account,
|
|
inbox: inbox,
|
|
contact: contact_inbox.contact,
|
|
contact_inbox: contact_inbox,
|
|
conversation: conversation,
|
|
brand: brand,
|
|
unit: unit,
|
|
suite_identifier: params[:suite_identifier],
|
|
check_in_at: check_in_at,
|
|
check_out_at: check_out_at,
|
|
status: :pending_payment,
|
|
payment_status: :pending,
|
|
total_amount: params[:total_amount],
|
|
integracao_id: params[:integracao_id] || SecureRandom.uuid,
|
|
metadata: params[:metadata] || {},
|
|
created_by_id: created_by&.id,
|
|
created_by_type: created_by&.class&.name
|
|
}
|
|
end
|
|
|
|
def schedule_automations(reservation)
|
|
automations = Captain::InboxAutomation
|
|
.where(account: reservation.account, inbox: reservation.inbox, enabled: true)
|
|
.order(created_at: :asc)
|
|
|
|
automations.each do |automation|
|
|
scheduled_at = automation.scheduled_at(
|
|
check_in_at: reservation.check_in_at,
|
|
check_out_at: reservation.check_out_at
|
|
)
|
|
next if scheduled_at.blank?
|
|
|
|
Captain::Reminder.create!(
|
|
account: reservation.account,
|
|
inbox: reservation.inbox,
|
|
contact: reservation.contact,
|
|
contact_inbox: reservation.contact_inbox,
|
|
conversation: reservation.conversation,
|
|
reminder_type: :automation,
|
|
status: :scheduled,
|
|
message: automation.message,
|
|
scheduled_at: scheduled_at,
|
|
source_type: reservation.class.name,
|
|
source_id: reservation.id,
|
|
metadata: {
|
|
automation_id: automation.id,
|
|
automation_title: automation.title
|
|
}
|
|
)
|
|
end
|
|
end
|
|
|
|
def calculate_check_out(check_in_at)
|
|
duration_minutes = params[:duration_minutes].to_i
|
|
return check_in_at if duration_minutes <= 0
|
|
|
|
check_in_at + duration_minutes.minutes
|
|
end
|
|
|
|
def parse_time(value)
|
|
return if value.blank?
|
|
|
|
Time.zone.parse(value.to_s)
|
|
end
|
|
|
|
def find_conversation
|
|
return if params[:conversation_id].blank?
|
|
|
|
account.conversations.find_by(id: params[:conversation_id])
|
|
end
|
|
|
|
def find_inbox
|
|
return if params[:inbox_id].blank?
|
|
|
|
account.inboxes.find_by(id: params[:inbox_id])
|
|
end
|
|
|
|
def find_or_create_contact_inbox(inbox)
|
|
phone_number = params[:phone_number].to_s.strip
|
|
return if phone_number.blank? || inbox.blank?
|
|
|
|
source_id = normalized_source_id(inbox, phone_number)
|
|
contact_attributes = {
|
|
name: params[:contact_name],
|
|
phone_number: normalized_phone_number(phone_number)
|
|
}.compact
|
|
|
|
ContactInboxWithContactBuilder.new(
|
|
inbox: inbox,
|
|
source_id: source_id,
|
|
contact_attributes: contact_attributes
|
|
).perform
|
|
end
|
|
|
|
def normalized_source_id(inbox, phone_number)
|
|
return phone_number unless inbox.channel_type == 'Channel::Whatsapp'
|
|
|
|
Whatsapp::PhoneNumberNormalizationService.new(inbox).normalize_and_find_contact_by_provider(phone_number, :cloud)
|
|
end
|
|
|
|
def normalized_phone_number(phone_number)
|
|
phone_number.start_with?('+') ? phone_number : "+#{phone_number}"
|
|
end
|
|
|
|
def find_brand(brand_id)
|
|
return if brand_id.blank?
|
|
|
|
Captain::Brand.find_by(id: brand_id, account_id: account.id)
|
|
end
|
|
|
|
def find_unit(unit_id)
|
|
return if unit_id.blank?
|
|
|
|
Captain::Unit.find_by(id: unit_id, account_id: account.id)
|
|
end
|
|
|
|
def generate_pix_charge(reservation)
|
|
service = Captain::Inter::CobService.new(reservation.unit)
|
|
|
|
# 50% of total amount as down payment, default to full amount if not specified
|
|
amount = reservation.total_amount ? (reservation.total_amount / 2.0).round(2) : 50.00
|
|
|
|
response = service.create_immediate_charge(
|
|
amount: amount,
|
|
expiration_seconds: 600, # 10 minutes
|
|
payer: {
|
|
cpf_cnpj: params[:cpf] || params[:cnpj],
|
|
name: params[:contact_name] || 'Cliente'
|
|
}
|
|
)
|
|
|
|
if response[:success]
|
|
payload = response[:payload]
|
|
|
|
# Persist Charge
|
|
charge = Captain::PixCharge.create!(
|
|
reservation: reservation,
|
|
unit: reservation.unit,
|
|
txid: payload['txid'],
|
|
pix_copia_e_cola: payload['pixCopiaECola'],
|
|
status: payload['status'], # ATIVA
|
|
raw_webhook_payload: payload
|
|
)
|
|
|
|
reservation.update!(current_pix_charge_id: charge.id)
|
|
|
|
{
|
|
success: true,
|
|
payload: {
|
|
txid: payload['txid'],
|
|
pixCopiaECola: payload['pixCopiaECola'],
|
|
pixUrl: payload['loc']['location'] # Used for QR Code generation
|
|
}
|
|
}
|
|
else
|
|
{ success: false, error: response[:error] }
|
|
end
|
|
rescue StandardError => e
|
|
{ success: false, error: e.message }
|
|
end
|
|
end
|