Module: Api::Vendor::V1::Concerns::CreateReservation

Extended by:
ActiveSupport::Concern
Includes:
Authentication, ReservationUtils
Included in:
ReservationsController, VendorReservationsController
Defined in:
app/controllers/api/vendor/v1/concerns/create_reservation.rb

Overview

typed: ignore frozen_string_literal: true

Instance Method Summary collapse

Instance Method Details

#create_vendor_reservation(vendor_payment_attributes, is_package_qty_adjustable = false) ⇒ Object



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
# File 'app/controllers/api/vendor/v1/concerns/create_reservation.rb', line 9

def create_vendor_reservation(vendor_payment_attributes, is_package_qty_adjustable = false)
  reservation_id = params[:tmp_reservation_id]
  BUSINESS_LOGGER.set_business_context(reservation_id: reservation_id)

  reservation = Reservation.find(reservation_id)
  is_group_booking = params.fetch(:big_group, false)
  restaurant_packages_params = if params[:packages].present?
                                 params.permit(packages: [:id, :quantity, :vendor_price, {
                                                 menu_sections: [:id, :quantity, { menus: [:id, :quantity, { subsections: [box: [:id, { menus: %i[id quantity] }]] }] }],
                                               }])[:packages]
                               else
                                 []
                               end

  reservation_param = params.require(:reservation).permit(:date,
                                                          :start_time,
                                                          :adult,
                                                          :kids,
                                                          :service_type,
                                                          :delivery_address,
                                                          :distance_to_restaurant,
                                                          :special_request,
                                                          :dining_occasion_id,
                                                          :restaurant_id,
                                                          :promo_code,
                                                          :is_order_now,
                                                          :click_id,
                                                          :adv_partner,
                                                          :redeemed_points,
                                                          :redeemed_amount,
                                                          :adaptive_points_ratio_id,
                                                          :accept_we_travel_together).tap do |param|
    param[:restaurant_id] = if restaurant_packages_params.present?
                              package = HhPackage::RestaurantPackage.fetch(restaurant_packages_params.first[:id])
                              package.restaurant_id
                            else
                              param[:restaurant_id]
                            end
    param[:date] = Date.parse(param[:date]).to_s if param[:date].present?
  end

  nested_package_params = restaurant_packages_params.map(&:to_h)

  if is_package_qty_adjustable
    nested_package_params.each do |package|
      rest_pack = HhPackage::RestaurantPackage.find(package[:id])
      package[:quantity] = calculate_package_qty(rest_pack, reservation_param[:adult], reservation_param[:kids])
    end
  end

  if ::Channel.adjust_pax_to_max_per_pack_qty?(vendor.name)
    rest_pack_id = nested_package_params[0]['id'].to_i
    restaurant_package = HhPackage::RestaurantPackage.find_by(id: rest_pack_id)

    ttl_pkg_qty = if restaurant_package&.package&.dynamic_price_pricing_model&.to_sym == :per_pack
                    nested_package_params[0]['quantity'].to_i
                  else
                    1 # for per_person packages, we set package qty as 1 in reservation params
                  end
    adjusted_adult_kids_qty = calculate_pax_qty(rest_pack_id,
                                                reservation_param[:adult],
                                                reservation_param[:kids])

    reservation_param[:adult], reservation_param[:kids] = adjusted_adult_kids_qty.map { |count| count * ttl_pkg_qty }
  end

  nested_package_params.each do |package|
    next if package[:menu_sections].blank?

    package[:menu_sections].each do |ms|
      temp_menus = ms.delete :menus
      ms[:menus] = []
      temp_menus.map do |menu|
        if menu[:subsections].present?
          temp_subsections = menu.delete :subsections

          temp_subsections.map do |subsection|
            # re create and duplicate box as menus
            menu[:quantity] = 1
            new_menus = menu.merge(subsections: subsection[:box])

            ms[:menus] << new_menus
          end
        else
          ms[:menus] << menu
        end
      end
    end
  end

  # Set custom email if vendor does not provide the email params
  # (list of such vendors is maintained in channel.rb)
  if params.dig(:guest_user, :email).blank?
    params[:guest_user][:email] = ::Channel.default_vendor_email_if_skippable?(vendor.name)
  end

  # Set phone to support phone if vendor not provide phone (list of such vendors in maintained in channel.rb)
  if params.dig(:guest_user, :phone).blank?
    params[:guest_user][:phone] = ::Channel.default_vendor_phone_if_skippable?(vendor.name)
  end

  create_reservation_params = {
    reservation: reservation_param,
    guest_user: params.key?(:guest_user) ? params.require(:guest_user).permit(:name, :email, :phone) : nil,
    channel: parse_channel_source,
    medium: parse_medium_source,
    created_by: :user,
    group_booking: is_group_booking,
    business_booking: false,
    restaurant_packages: nested_package_params,
    vendor_payment: if vendor_payment_attributes.present?
                      vendor_payment_attributes.slice(:transaction_id, :amount_cents, :currency, :status, :paid_at)
                    end,
    blockage_id: params[:blockage_id],
    reference_id: params.dig(:reservation, :reference_id),
    reseller_reference_id: params.dig(:reservation, :reseller, :reference_id),
  }.tap do |param|
    param.each do |key, value|
      param[key] = value.permit!.to_h if value.is_a?(ActionController::Parameters)
    end
  end
  service = ReservationService::Create.new(create_reservation_params, reservation)

  if service.execute
    # Store reservation data to vendor_reservations table
    # If failed to store, it will be handled by rescue block to cancel the reservation
    vendor_reservation = VendorReservation.find_or_initialize_by reservation_id: reservation_id
    vendor_reservation.vendor_id = vendor.id
    vendor_reservation.reference_id = params[:reservation][:reference_id]
    vendor_reservation.reseller_reference_id = params.dig(:reservation, :reseller, :reference_id)
    vendor_reservation.reseller_name = params.dig(:reservation, :reseller, :name)
    vendor_reservation.save!

    # Handle payment from vendor
    if vendor_payment_attributes.present?
      calculated_amount = calculate_reservation_amount(reservation)

      # temporary solution for Globaltix sending zero amount_cents in vendor_payment
      if vendor_payment_attributes&.key?(:amount_cents) && vendor_payment_attributes[:amount_cents].zero?
        calc_amount_cents = Money.from_amount(
          calculated_amount[:total_price], reservation.package[:price_currency]
        ).cents
        vendor_payment_attributes[:amount_cents] = calc_amount_cents

        error_message = "#{self.class}: vendor sent zero amount in vendor_payment,
          calculated amount_cents is #{calc_amount_cents}"

        BUSINESS_LOGGER.error(error_message, params: params, calc_amount_cents: calc_amount_cents,
                                             vendor: vendor&.name)
        APMErrorHandler.report(error_message, params: params, calc_amount_cents: calc_amount_cents,
                                              vendor: vendor&.name)
      end

      vp_service = VendorsService::PaymentService.new(service.outcome)
      vp_result = vp_service.create(vendor_payment_attributes)
      raise vp_service.error_message_simple unless vp_result
    end

    if reservation.charges.present?
      charge_amount = reservation.charges.first.amount_v2
      total_price_money = Money.from_amount(reservation.total_price, reservation.package[:price_currency])
      if charge_amount >= total_price_money
        calculated_amount ||= calculate_reservation_amount(reservation)
        # if payment_handled_by_vendor then they always pay 100% for prepaid bookings
        reservation.update!(
          charge_price_v2: Money.from_amount(calculated_amount[:total_price], reservation.package[:price_currency]),
          charge_percent: 100,
          charge_type: HhPackage::Package::CHARGE_TYPE_ON_CHARGE,
        )

      end
    end
    reservation.reload # to get the vendor_reservation data for reservation.by_tablecheck? etc.
    # get new reservations with vendor_reservation object
    reservation = Reservation.find(service.outcome.id)
    render_create_reservation_success_response(reservation)
  else
    # Cancel reservation on TableCheck or SevenRooms if failed to create reservation on HH
    if reservation.by_tablecheck?
      cancel_service = Tablecheck::Reservations::Cancel.new(reservation.id)
      cancel_result = cancel_service.execute
      unless cancel_result.success?
        return render json: { success: false, message: service.error_message_simple, data: nil }
      end
    elsif reservation.by_seven_rooms?
      cancel_service = SevenRooms::Reservations::Cancel.new(reservation.id)
      cancel_result = cancel_service.execute
      unless cancel_result.success?
        return render json: { success: false, message: service.error_message_simple, data: nil }
      end
    elsif reservation.by_weeloy?
      cancel_service = Weeloy::Reservations::Cancel.new(reservation.id)
      cancel_result = cancel_service.execute
      unless cancel_result.success?
        return render json: { success: false, message: service.error_message_simple, data: nil }
      end
    elsif reservation.by_bistrochat?
      cancel_service = Bistrochat::Reservations::Cancel.new(reservation.id)
      cancel_result = cancel_service.execute
      unless cancel_result.success?
        return render json: { success: false, message: service.error_message_simple, data: nil }
      end
    elsif reservation.by_mymenu?
      cancel_service = MyMenu::Reservations::Cancel.new(reservation.id)
      cancel_result = cancel_service.execute
      unless cancel_result.success?
        return render json: { success: false, message: service.error_message_simple, data: nil }
      end
    end

    render json: { success: false, message: service.error_message_simple, data: nil }
  end
rescue StandardError => e
  cancel_error_reservation(reservation_id)

  if Rails.env.production?
    APMErrorHandler.report("Error in creating vendor reservations #{reservation_id}", error: e)

    render json: { success: false, message: 'Sorry, something went wrong', data: nil }
  else
    raise e
  end
end