Skip to content

Instantly share code, notes, and snippets.

@marcusleemitchell
Created May 22, 2012 13:11
Show Gist options
  • Save marcusleemitchell/2768949 to your computer and use it in GitHub Desktop.
Save marcusleemitchell/2768949 to your computer and use it in GitHub Desktop.
@ -208,46 +202,16 @@ private
...skipping...
diff --git a/app/models/booking.rb b/app/models/booking.rb
index 0c52530..4443485 100644
--- a/app/models/booking.rb
+++ b/app/models/booking.rb
@@ -11,7 +11,6 @@ class Booking < ActiveRecord::Base
does 'date_range', :check_in_date, :check_out_date
does 'flag', :old_style_commissions, :default => false
- does 'flag', :used_for_authorisation, :default => false
does 'flag', 'unread', :default => true
does 'flag', :external_terms_accepted, :default => false
does 'booking/payment'
@@ -20,15 +19,20 @@ class Booking < ActiveRecord::Base
does 'booking/payment_export'
does 'booking/messaging'
does 'booking/agency'
+ does 'booking/payment_methods'
+ does 'money', :exchange_rate => :payment_exchange_rate
does 'booking/marketing_events'
- does 'money', :exchange_rate => :exchange_rate, :validate => true
+ def payment_exchange_rate
+ initial_payment.exchange_rate
+ end
+
+ # does 'money', :exchange_rate => :exchange_rate, :validate => true
does 'act_like_property_search'
does 'booking/kigo'
has_paper_trail
-
attr_writer :calendar_interface
delegate :is_valid_for_calendar?, :minimum_days_of_stay_on_check_in_date, :to => :calendar_interface
@@ -37,18 +41,17 @@ class Booking < ActiveRecord::Base
belongs_to :booking_offer
belongs_to :host, :class_name => 'User'
has_one :tenant_info_sheet, :as => :owner
- has_one :payment, :as => :owner
+ has_many :payments
+
+ has_many :additional_payments
has_one :external_services_log
- belongs_to :exchange_rate
has_many :frozen_additional_fees
has_many :booking_histories, :dependent => :destroy
has_many :booking_events, :primary_key => :booking_key, :foreign_key => :booking_key, :dependent => :delete_all
has_many :booking_tests
- # This list contains legacy values which must not be removed to avoid old bookings becoming invalid
- ALLOWED_CC_CHARGED_VALUES = ['', 'authorised', 'charged', 'X', 'processing', 'rejected', 'Charged AD', 'Charged GC']
- validates_presence_of :property_id, :check_in_date, :check_out_date, :total_price_of_stay
+ validates_presence_of :property_id, :check_in_date, :check_out_date
validates_presence_of :tenant_info_sheet, :unless => :pending?
validates_presence_of :user_id, :unless => :skip_validation_of_user_id
validates_numericality_of :number_of_guests, :greater_than => 0, :message => N_('!Please select the number of guests.')
@@ -59,8 +62,7 @@ class Booking < ActiveRecord::Base
attr_accessor :skip_validation_of_user_id
validates_inclusion_of :cancellation_policy, :in => %w[relaxed intermediate moderate strict relaxed_100_50 mid_run long_run strict_50_0]
- validates_inclusion_of :payment_faked_option, :in => ['fake_1', 'fake_2', 'fake_3', 'fake_4', '', nil]
- validates_inclusion_of :cc_charged, :in => ALLOWED_CC_CHARGED_VALUES, :allow_nil => true
+
validate :is_valid_for_calendar!, :if => :validate_availability?
validates_before :check_in_date, :check_out_date
@@ -68,6 +70,8 @@ class Booking < ActiveRecord::Base
validates_does_not_overlap lambda{|booking| booking.property.try(:bookings)}, :message => N_('must not overlap with other Bookings.'), :if => lambda { |booking| booking.validate_does_not_overlap_with_other_bookings? }
+ accepts_nested_attributes_for :payments
+
AVAILABLE_STATES = %w[pending requested requested_from_agency accepted denied no_response
agency_booking_failed_because_of_api agency_booking_failed_because_of_validation
cancelled cancelled_by_host cancelled_by_guest cancelled_by_housetrip
@@ -79,26 +83,28 @@ class Booking < ActiveRecord::Base
validates_inclusion_of :status, :in => AVAILABLE_STATES, :unless => :pending?, :allow_blank => true
validates_inclusion_of :host_reminder_sent, :review_reminder_sent, :in => [true, false]
- validates_inclusion_of :used_for_authorisation, :in => [false, nil], :unless => :usable_for_authorisation?, :message => 'not allowed for this booking'
# after_update :deliver_confirmation_emails, :if => :being_confirmed?
before_validation :set_booking_key
before_validation_on_create :set_money_fields, :set_external_fields
before_validation_on_create :accept_external_terms, :if => :agency_booking?
+ before_validation_on_create :set_booking_key
before_create :set_attributes
+
before_save :build_status_change_event, :if => proc { |b| b.status_changed? }
before_save :set_no_payment_info, :unless => proc { |b| b.property && b.property.has_valid_payment_method? }
+ after_create :set_booking_reference
+
after_save :update_crm
after_save :update_host
- after_destroy :destroy_crm
after_save :update_host_count, :if => :being_confirmed?
after_save :update_guest, :if => :being_confirmed?
after_save :update_property
after_save :confirm_booking_offer, :if => :being_confirmed?
- after_create :set_booking_reference
after_save :trust_host, :if => lambda { |booking| booking.regular_payment_email_sent_at.present? }
- before_validation_on_create :set_booking_key
after_save :freeze_hosts_contact_info
+ after_destroy :destroy_crm
+ after_save :set_money_fields
merge_with_existing_availabilities :start_date_method => :check_in_date, :end_date_method => :check_out_date, :callback => :after_save, :if => :merge_with_existing_availabilities?
@@ -124,27 +130,30 @@ class Booking < ActiveRecord::Base
named_scope :require_assistance_offer, lambda { { :conditions => ['pending = ? AND assistance_offer_sent_at IS NULL AND created_at > ? AND created_at < ? AND send_assistance_email = ?', true, Time.current - 2.hours, Time.current
named_scope :require_assistance_offer_at_create_stage, lambda { { :include => [:booking_events], :conditions => ['pending = ? AND assistance_offer_sent_at IS NULL AND bookings.created_at > ? AND bookings.created_at < ? AND sen
named_scope :converted, :conditions => ['status = ? AND assistance_offer_sent_at IS NOT NULL', 'accepted']
- named_scope :with_invalid_payment_information, :conditions => { :payment_on_hold => INVALID_PAYMENT_INFO }
-
+ named_scope :with_invalid_payment_information, :conditions => ["payments.payment_on_hold = ?", INVALID_PAYMENT_INFO], :joins => :payments
named_scope :by_check_in_date, :order => 'bookings.check_in_date DESC'
-
named_scope :visible_to_host, :conditions => ['bookings.status NOT IN (?)', %w(denied no_response requested)]
-
- # def self.require_assistance_offer_at_create_stage
- # require_assistance_offer.delete_if{ |x| !x.booking_events.collect{ |y| y.type }.include?("BillingDetailsPageHitBookingEvent") }
- # end
+
+ def initialize(args = {})
+ super
+ self.payments << InitialPayment.new(:booking => self)
+ end
def self.ended_yesterday
accepted.scoped :conditions => [ 'bookings.check_out_date = ?', Date.yesterday ]
end
+ def initial_payment_creation
+ self.payments << InitialPayment.create(:psp_reference => self.id)
+ end
+
delegate :message, :to => :tenant_info_sheet
delegate :has_valid_payment_method?, :to => :property
has_defaults :host_reminder_sent => false, :review_reminder_sent => false, :host_sms_sent => false
def self.for_bulk_charge(options = {})
- scope = chain_safely.scoped(:conditions => { :cc_charged => 'authorised' }).chain_safely
+ scope = chain_safely.scoped(:conditions => "bookings.id IN (SELECT payments.booking_id FROM payments WHERE payments.cc_charged = 'authorised' AND payments.booking_id = bookings.id)").chain_safely
if options[:all]
scope
elsif options[:ids].present?
@@ -185,7 +194,7 @@ class Booking < ActiveRecord::Base
def confirm!
self.pending = false
self.created_at = Time.now.utc
- self.cc_charged = 'authorised'
+ self.initial_payment.authorise!
if agency_booking?
self.status = 'requested_from_agency'
# Although we don't care when the Agency booking fails at this point,
@@ -204,25 +213,24 @@ class Booking < ActiveRecord::Base
user.confirm_email!
end
- def payment_faked?
- payment_faked_option.present?
- end
-
def fake_payment!(payment_faked_option)
if pending?
confirm!
end
- payment_faked_attributes = {:cc_charged => 'X', :payment_faked_option => payment_faked_option}
- payment_faked_attributes.merge!(:credit_card_cost => 0, :money_kept_by_adyen =>0) if ['fake_2', 'fake_3', 'fake_4'].include?(payment_faked_option)
- payment_faked_attributes.merge!(:payment_in_advance_amount => 0, :payment_in_advance_at => nil,
- :regular_payment_at => nil) if ['fake_3', 'fake_4'].include?(payment_faked_option)
- payment_faked_attributes.merge!(:total_price_of_stay_for_host => 0) if ['fake_3'].include?(payment_faked_option)
- payment_faked_attributes.merge!(:credit_card_fee_amount => 0, :pre_reduction_commission_amount => 0, :total_price_of_stay => total_price_of_stay_for_host) if payment_faked_option == 'fake_4'
- update_attributes! payment_faked_attributes
+ booking_faked_attributes = {}
+ payment_attributes = { :cc_charged => 'X', :payment_faked_option => payment_faked_option }
+ payment_attributes.merge!(:credit_card_cost => 0, :money_kept_by_adyen =>0) if ['fake_2', 'fake_3', 'fake_4'].include?(payment_faked_option)
+ booking_faked_attributes.merge!(:payment_in_advance_amount => 0, :payment_in_advance_at => nil, :regular_payment_at => nil) if ['fake_3', 'fake_4'].include?(payment_faked_option)
+ payment_attributes.merge!(:total_price_of_stay_for_host => 0) if ['fake_3'].include?(payment_faked_option)
+ payment_attributes.merge!(:credit_card_fee_amount => 0, :pre_reduction_commission_amount => 0, :total_price_of_stay => total_price_of_stay_for_host,
+ :commission_amount => 0) if payment_faked_option == 'fake_4'
+ update_attributes! booking_faked_attributes
+ initial_payment.update_attributes! payment_attributes
if payment_faked_option == 'fake_3'
settlement_amount = 0 - credit_card_fee_amount.to_f - commission_amount.to_f
create_or_update_settlement_booking!(:total_price_of_stay_for_host => settlement_amount, :regular_payment_amount => settlement_amount)
end
+ reload
end
def accept(strict = false, notify_kigo = false)
@@ -457,15 +465,16 @@ class Booking < ActiveRecord::Base
def valid_except_for_user?
self.skip_validation_of_user_id = true
- result = valid?
+ set_external_fields
+ result = if agency_booking?
+ valid? && initial_payment.total_price_of_stay.present?
+ else
+ valid?
+ end
self.skip_validation_of_user_id = false
result
end
- def usable_for_authorisation?
- !%w[accepted chargeback requested].include?(status) && !payment_faked?
- end
-
def violates_minimum_days_of_stay?
if check_in_date.present? and check_out_date.present?
minimum_days_of_stay_on_check_in_date = calendar_interface.minimum_days_of_stay_on_check_in_date
@@ -474,12 +483,12 @@ class Booking < ActiveRecord::Base
end
def authorisation_received(params)
- if payment
+ if initial_payment.psp_reference.present?
# we sometimes receive multiple confirmations from adyen and they can contain updated info
set_authorisation_data(params)
save(false)
else
- create_payment(:psp_reference => params[:pspReference])
+ initial_payment.update_attributes(:psp_reference => params[:pspReference])
set_authorisation_data(params)
confirm!
end
@@ -490,23 +499,10 @@ class Booking < ActiveRecord::Base
self.merchant_account_code = params[:merchantAccountCode]
end
- def capture_received(params)
- if params[:success] == 'true'
- update_attribute :cc_charged, 'Charged AD'
- payment.captured_amount = params[:value].to_f / 100
- payment.captured_at = Time.zone.now
- payment.save(false)
- else
- update_attribute :cc_charged, 'rejected' unless cc_charged == 'Charged AD' || cc_charged == 'Charged GC'
- end
- end
-
def clear_payment_caches!
+ #reload # do not reload, as it will let a new booking offer fail.
# clear frozen caches (forces them to recalculate)
- self.update_attribute(:total_price_of_stay, nil)
- self.update_attribute(:commissionable_amount_for_guest, nil)
- self.update_attribute(:pre_reduction_commission_amount, nil)
- self.update_attribute(:credit_card_fee_amount, nil)
+ initial_payment.recalculate!
# clear other caches
update_cache!
end
@@ -534,31 +530,12 @@ class Booking < ActiveRecord::Base
self.booking_tests << booking_test
end
- def human_booking_status(custom_status = status)
- hours = Object.new.tap { |helper| helper.extend ActionView::Helpers::TextHelper }.pluralize(hours_left_for_host_response.to_f, _('hour'), _('hours'))
- case custom_status
- when 'requested' then _('Awaiting response from host (%{hours} left)') % { :hours => hours }
- when 'accepted' then _('Accepted by host')
- when 'cancelled' then _('Cancelled')
- when 'denied' then _('Denied by host')
- when 'no_response' then _('Cancelled (no response from host)')
- when 'cancelled_by_host' then _('Cancelled by the host')
- when 'cancelled_by_guest' then _('Cancelled by the guest')
- when 'cancelled_by_housetrip' then _('Cancelled by HouseTrip')
- when 'additional_payment' then _('Additional payment')
- when 'chargeback' then _('Chargeback')
- when 'to be settled' then _('To be settled')
- when 'settled' then _('Settled')
- when 'requested_from_agency' then _('Agency booking requested (not confirmed yet)')
- when 'agency_booking_failed_because_of_api' then _('Agency booking failed (API)')
- when 'agency_booking_failed_because_of_validation' then _('Agency booking failed (validation)')
- end
+ def set_booking_key
+ self.booking_key ||= EventKeyGenerator.generate(self.booking_message)
end
- private
-
def set_no_payment_info
- self.payment_on_hold = INVALID_PAYMENT_INFO if status_changed? && accepted?
+ initial_payment.payment_on_hold = INVALID_PAYMENT_INFO if status_changed? && accepted?
end
def calendar_interface
@@ -588,8 +565,8 @@ class Booking < ActiveRecord::Base
end
def set_money_fields
- self.currency_code ||= property.andand.currency_code
- freeze_exchange_rate!
+ self.initial_payment.currency_code = property.andand.currency_code
+ initial_payment.freeze_exchange_rate!
end
def set_attributes
@@ -697,8 +674,11 @@ class Booking < ActiveRecord::Base
end
def build_status_change_event
- prev_status_name = human_booking_status(status_was) || status_was.to_s.titleize
- curr_status_name = human_booking_status(status) || status.to_s.titleize
+ helper = Object.new
+ helper.extend BookingsHelper
+ helper.extend ActionView::Helpers::TextHelper
+ prev_status_name = helper.human_booking_status(self, status_was) || status_was.to_s.titleize
+ curr_status_name = helper.human_booking_status(self, status) || status.to_s.titleize
event_message = if status_was
"Status changed from `%s' to `%s'" % [prev_status_name, curr_status_name]
else
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment