From 8e0a06246bae15e4da7338403983628fb64b1a66 Mon Sep 17 00:00:00 2001 From: Rodribm10 Date: Wed, 15 Apr 2026 01:37:23 -0300 Subject: [PATCH] feat(lifecycle): wire Captain::Reservation lifecycle hooks Add after_commit callbacks to call Captain::Lifecycle::Scheduler on create, status change (cancelled/no_show), and check_in_at change. Each handler wraps in rescue StandardError to preserve existing behavior. Co-Authored-By: Claude Sonnet 4.6 --- enterprise/app/models/captain/reservation.rb | 23 ++++++++++ .../reservation_lifecycle_hooks_spec.rb | 46 +++++++++++++++++++ 2 files changed, 69 insertions(+) create mode 100644 spec/enterprise/models/captain/reservation_lifecycle_hooks_spec.rb diff --git a/enterprise/app/models/captain/reservation.rb b/enterprise/app/models/captain/reservation.rb index f9c6f6bc6..943507499 100644 --- a/enterprise/app/models/captain/reservation.rb +++ b/enterprise/app/models/captain/reservation.rb @@ -90,6 +90,9 @@ class Captain::Reservation < ApplicationRecord after_commit :sync_conversation_marker_snapshot after_create_commit :update_contact_reservation_metadata after_create_commit :post_internal_reservation_note + after_create_commit :schedule_lifecycle_rules + after_update_commit :handle_lifecycle_status_change, if: :saved_change_to_status? + after_update_commit :handle_lifecycle_checkin_change, if: :saved_change_to_check_in_at? def ui_status Captain::Reservations::MarkerBuilder.ui_status(status) @@ -160,6 +163,26 @@ class Captain::Reservation < ApplicationRecord end # rubocop:enable Metrics/AbcSize,Metrics/MethodLength + def schedule_lifecycle_rules + Captain::Lifecycle::Scheduler.schedule_for(self) + rescue StandardError => e + Rails.logger.error("[Lifecycle] schedule_for failed for reservation #{id}: #{e.class} #{e.message}") + end + + def handle_lifecycle_status_change + return unless %w[cancelled no_show].include?(status.to_s) + + Captain::Lifecycle::Scheduler.cancel_pending(self) + rescue StandardError => e + Rails.logger.error("[Lifecycle] cancel_pending failed for reservation #{id}: #{e.class} #{e.message}") + end + + def handle_lifecycle_checkin_change + Captain::Lifecycle::Scheduler.reschedule_for_checkin_change(self) + rescue StandardError => e + Rails.logger.error("[Lifecycle] reschedule failed for reservation #{id}: #{e.class} #{e.message}") + end + # Atualiza campos visiveis no painel lateral do Chatwoot (custom_attributes) # pra que a recepcionista veja num relance: # ultima_suite, ultima_permanencia, ultima_reserva_em, total_reservas diff --git a/spec/enterprise/models/captain/reservation_lifecycle_hooks_spec.rb b/spec/enterprise/models/captain/reservation_lifecycle_hooks_spec.rb new file mode 100644 index 000000000..c0d2f13c4 --- /dev/null +++ b/spec/enterprise/models/captain/reservation_lifecycle_hooks_spec.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +require 'rails_helper' + +RSpec.describe Captain::Reservation, '#lifecycle_hooks' do + let(:account) { create(:account) } + let(:brand) { create(:captain_brand, account: account) } + let(:unit) { Captain::Unit.create!(account: account, name: 'Test', brand: brand) } + + describe 'after create' do + it 'calls Scheduler.schedule_for' do + expect(Captain::Lifecycle::Scheduler).to receive(:schedule_for).with(kind_of(described_class)) + create(:captain_reservation, + account: account, unit: unit, + check_in_at: 2.hours.from_now, check_out_at: 10.hours.from_now) + end + end + + describe 'after update (status → cancelled)' do + let(:reservation) do + allow(Captain::Lifecycle::Scheduler).to receive(:schedule_for) + create(:captain_reservation, + account: account, unit: unit, + check_in_at: 2.hours.from_now, check_out_at: 10.hours.from_now) + end + + it 'cancels pending deliveries' do + expect(Captain::Lifecycle::Scheduler).to receive(:cancel_pending).with(reservation) + reservation.update!(status: 'cancelled') + end + end + + describe 'after update (check_in_at changed)' do + let(:reservation) do + allow(Captain::Lifecycle::Scheduler).to receive(:schedule_for) + create(:captain_reservation, + account: account, unit: unit, + check_in_at: 2.hours.from_now, check_out_at: 10.hours.from_now) + end + + it 'reschedules checkin-based deliveries' do + expect(Captain::Lifecycle::Scheduler).to receive(:reschedule_for_checkin_change).with(reservation) + reservation.update!(check_in_at: 3.hours.from_now) + end + end +end