quicktravel_client 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.rspec +2 -0
- data/.rubocop.yml +13 -0
- data/.ruby-version +1 -0
- data/.travis.yml +18 -0
- data/Gemfile +5 -0
- data/LICENSE +22 -0
- data/README.md +17 -0
- data/Rakefile +18 -0
- data/bootstrap/boot.rb +4 -0
- data/examples/example_config.rb +4 -0
- data/examples/login_example.rb +25 -0
- data/lib/extensions.rb +12 -0
- data/lib/quick_travel/accommodation.rb +72 -0
- data/lib/quick_travel/adapter.rb +283 -0
- data/lib/quick_travel/adapter_exception.rb +20 -0
- data/lib/quick_travel/address.rb +27 -0
- data/lib/quick_travel/adjustment.rb +8 -0
- data/lib/quick_travel/background_check.rb +9 -0
- data/lib/quick_travel/bed_configuration.rb +7 -0
- data/lib/quick_travel/bed_requirement.rb +7 -0
- data/lib/quick_travel/booking.rb +370 -0
- data/lib/quick_travel/cache.rb +38 -0
- data/lib/quick_travel/client.rb +8 -0
- data/lib/quick_travel/client_type.rb +6 -0
- data/lib/quick_travel/config.rb +26 -0
- data/lib/quick_travel/connection_error.rb +9 -0
- data/lib/quick_travel/constants.rb +16 -0
- data/lib/quick_travel/contact.rb +5 -0
- data/lib/quick_travel/country.rb +11 -0
- data/lib/quick_travel/credit_card.rb +12 -0
- data/lib/quick_travel/discounts/booking_discount.rb +18 -0
- data/lib/quick_travel/discounts/discount.rb +21 -0
- data/lib/quick_travel/discounts/discount_tree.rb +35 -0
- data/lib/quick_travel/discounts.rb +3 -0
- data/lib/quick_travel/document.rb +20 -0
- data/lib/quick_travel/document_group.rb +14 -0
- data/lib/quick_travel/encrypt.rb +13 -0
- data/lib/quick_travel/graphic.rb +18 -0
- data/lib/quick_travel/init_from_hash.rb +16 -0
- data/lib/quick_travel/location.rb +14 -0
- data/lib/quick_travel/mock.rb +41 -0
- data/lib/quick_travel/party.rb +49 -0
- data/lib/quick_travel/passenger.rb +7 -0
- data/lib/quick_travel/passenger_price_break.rb +8 -0
- data/lib/quick_travel/passenger_type.rb +21 -0
- data/lib/quick_travel/payment.rb +41 -0
- data/lib/quick_travel/payment_type.rb +37 -0
- data/lib/quick_travel/price.rb +14 -0
- data/lib/quick_travel/product.rb +165 -0
- data/lib/quick_travel/product_configuration.rb +134 -0
- data/lib/quick_travel/product_passenger_search_criteria.rb +28 -0
- data/lib/quick_travel/product_type.rb +28 -0
- data/lib/quick_travel/property.rb +99 -0
- data/lib/quick_travel/property_facility.rb +7 -0
- data/lib/quick_travel/property_type.rb +20 -0
- data/lib/quick_travel/region.rb +21 -0
- data/lib/quick_travel/reservation.rb +110 -0
- data/lib/quick_travel/resource.rb +50 -0
- data/lib/quick_travel/room_facility.rb +7 -0
- data/lib/quick_travel/route.rb +81 -0
- data/lib/quick_travel/route_stop.rb +7 -0
- data/lib/quick_travel/search.rb +14 -0
- data/lib/quick_travel/service.rb +5 -0
- data/lib/quick_travel/trip.rb +8 -0
- data/lib/quick_travel/vehicle.rb +17 -0
- data/lib/quick_travel/vehicle_type.rb +11 -0
- data/lib/quick_travel/version.rb +3 -0
- data/lib/quick_travel.rb +59 -0
- data/lib/quicktravel_client.rb +1 -0
- data/quicktravel_client.gemspec +37 -0
- data/spec/booking_spec.rb +107 -0
- data/spec/country_spec.rb +18 -0
- data/spec/credit_card_spec.rb +11 -0
- data/spec/discounts_spec.rb +121 -0
- data/spec/passenger_type_spec.rb +32 -0
- data/spec/payment_type_spec.rb +18 -0
- data/spec/product_spec.rb +82 -0
- data/spec/region_spec.rb +29 -0
- data/spec/reservation_spec.rb +50 -0
- data/spec/resource_spec.rb +22 -0
- data/spec/spec_helper.rb +77 -0
- data/spec/support/cassettes/booking_create.yml +50 -0
- data/spec/support/cassettes/booking_create_legacy.yml +50 -0
- data/spec/support/cassettes/booking_discounts.yml +53 -0
- data/spec/support/cassettes/booking_documents.yml +48 -0
- data/spec/support/cassettes/booking_show.yml +49 -0
- data/spec/support/cassettes/booking_update.yml +94 -0
- data/spec/support/cassettes/booking_with_discounts.yml +72 -0
- data/spec/support/cassettes/booking_with_documents.yml +117 -0
- data/spec/support/cassettes/booking_with_nested_attributes.yml +187 -0
- data/spec/support/cassettes/country_all.yml +127 -0
- data/spec/support/cassettes/create_reservation_fail.yml +47 -0
- data/spec/support/cassettes/create_reservation_with_booking.yml +53 -0
- data/spec/support/cassettes/passenger_all.yml +48 -0
- data/spec/support/cassettes/product_date_range_bookability.yml +118 -0
- data/spec/support/cassettes/product_show.yml +52 -0
- data/spec/support/cassettes/region_index.yml +48 -0
- data/spec/support/cassettes/region_show.yml +48 -0
- data/spec/support/cassettes/reservation_with_extra_picks.yml +165 -0
- data/spec/support/cassettes/resource_fare_bases.yml +97 -0
- data/spec/support/cassettes/resource_show.yml +50 -0
- metadata +421 -0
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
|
3
|
+
module QuickTravel
|
4
|
+
class Location < Adapter
|
5
|
+
attr_accessor :id, :name, :region_ids
|
6
|
+
|
7
|
+
self.api_base = '/locations'
|
8
|
+
self.lookup = true
|
9
|
+
|
10
|
+
def regions
|
11
|
+
Region.all.select { |r| region_ids.include?(r.id) }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module QuickTravel
|
2
|
+
class Resource
|
3
|
+
def self.find(id)
|
4
|
+
new(id: id)
|
5
|
+
end
|
6
|
+
|
7
|
+
def product_type
|
8
|
+
QuickTravel::ProductType.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class ProductType
|
13
|
+
def needs_passengers
|
14
|
+
return true if @needs_passengers.nil?
|
15
|
+
@needs_passengers
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class PassengerType
|
20
|
+
def self.all
|
21
|
+
[
|
22
|
+
PassengerType.new(id: 1, name: 'Adult'),
|
23
|
+
PassengerType.new(id: 2, name: 'Child')
|
24
|
+
]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Adapter
|
29
|
+
def self.all
|
30
|
+
[new, new]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class PropertyType < Adapter
|
35
|
+
def self.all
|
36
|
+
[new, new]
|
37
|
+
end
|
38
|
+
end
|
39
|
+
class Location < Adapter; end
|
40
|
+
class Region < Adapter; end
|
41
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module QuickTravel
|
2
|
+
class Party < Adapter
|
3
|
+
LOGIN_URL = '/login.json'
|
4
|
+
|
5
|
+
attr_accessor :id, :type, :name, :login, :title, :first_name, :last_name, :gender, :active, :internal_staff, :display_name
|
6
|
+
|
7
|
+
attr_accessor :client_id, :vendor_id
|
8
|
+
attr_accessor :surchargeable
|
9
|
+
attr_accessor :client_type_id, :client_type_name, :client_type_is_agent
|
10
|
+
attr_accessor :default_web_user, :rights
|
11
|
+
|
12
|
+
attr_accessor :phone, :mobile, :email # if has a contact
|
13
|
+
attr_accessor :post_code, :country_id # if has an address
|
14
|
+
|
15
|
+
def initialize(hash = {})
|
16
|
+
super
|
17
|
+
if type.blank?
|
18
|
+
self.type = 'Person'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
self.api_base = '/parties'
|
23
|
+
|
24
|
+
def self.find_by_login(options)
|
25
|
+
get_and_validate('/parties/find_by_login.json', options)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Asks QuickTravel to check the credentials
|
29
|
+
#
|
30
|
+
# @returns: Party: Valid Credentials
|
31
|
+
# Nil: Invalid Credentialss
|
32
|
+
def self.login(options = { login: nil, password: nil })
|
33
|
+
fail 'You must specify :login and :password' unless options[:login] && options[:password]
|
34
|
+
response = post_and_validate(LOGIN_URL, options)
|
35
|
+
Party.new(response) unless response[:error]
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.request_password(login, url)
|
39
|
+
options = { login: login, url: url }
|
40
|
+
post_and_validate('/sessions/request_password_reset', options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.set_password_via_token(token, password)
|
44
|
+
post_and_validate('/sessions/set_password_via_token', token: token, password: password)
|
45
|
+
rescue QuickTravel::AdapterException => e
|
46
|
+
{ error: e.message }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
|
3
|
+
module QuickTravel
|
4
|
+
class PassengerType < Adapter
|
5
|
+
attr_accessor :age_range_required, :default_age, :id, :maximum_age, :minimum_age, :name, :position, :code
|
6
|
+
|
7
|
+
self.api_base = '/passenger_types'
|
8
|
+
self.lookup = true
|
9
|
+
|
10
|
+
def self.passenger_counts(count_hash)
|
11
|
+
applicable_types = count_hash.reject { |_t, c| c.zero? }
|
12
|
+
applicable_types.map do |type, count|
|
13
|
+
pluralize(count, find(type).try(:name))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.pluralize(count, singular, plural = nil)
|
18
|
+
"#{count || 0} " + ((count == 1 || count =~ /^1(\.0+)?$/) ? singular : (plural || singular.pluralize))
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
require 'quick_travel/credit_card'
|
3
|
+
|
4
|
+
module QuickTravel
|
5
|
+
class Payment < Adapter
|
6
|
+
attr_accessor :id, :payment_type_id, :amount_in_cents, :authorization, :success, :created_at
|
7
|
+
money :amount
|
8
|
+
|
9
|
+
def payment_type
|
10
|
+
QuickTravel::PaymentType.find(@payment_type_id)
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.create(options = {})
|
14
|
+
post_and_validate("/front_office/bookings/#{options[:booking_id]}/payments.json", options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.charge_account(booking, payment_options = {})
|
18
|
+
payment_type = booking.on_account_payment_type
|
19
|
+
|
20
|
+
if payment_type.nil?
|
21
|
+
fail ArgumentError, 'Booking not allowed to be paid on account'
|
22
|
+
end
|
23
|
+
|
24
|
+
agent_booking_reference = payment_options[:agent_booking_reference]
|
25
|
+
amount = payment_options[:amount].presence
|
26
|
+
|
27
|
+
options = {
|
28
|
+
payment: {
|
29
|
+
payment_type_id: payment_type.id,
|
30
|
+
uid: SecureRandom.hex(16),
|
31
|
+
comment: agent_booking_reference,
|
32
|
+
amount: amount,
|
33
|
+
currency_iso_code: 'AUD'
|
34
|
+
},
|
35
|
+
pay_balance: amount.nil?
|
36
|
+
}
|
37
|
+
|
38
|
+
post_and_validate("/api/bookings/#{booking.id}/payments.json", options)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
require 'quick_travel/cache'
|
3
|
+
|
4
|
+
module QuickTravel
|
5
|
+
class PaymentType < Adapter
|
6
|
+
attr_accessor :id,
|
7
|
+
:name,
|
8
|
+
:description,
|
9
|
+
:position,
|
10
|
+
:active,
|
11
|
+
:payment_method,
|
12
|
+
:for_frequent_traveller_redemption,
|
13
|
+
:notes,
|
14
|
+
:surchargeable,
|
15
|
+
:transaction_fee,
|
16
|
+
:credit_card_brand
|
17
|
+
|
18
|
+
self.api_base = '/payment_types'
|
19
|
+
self.lookup = true
|
20
|
+
|
21
|
+
def credit_card
|
22
|
+
payment_method == 'credit_card'
|
23
|
+
end
|
24
|
+
|
25
|
+
def code
|
26
|
+
if credit_card
|
27
|
+
credit_card_brand.underscore.gsub(/\s/, '_')
|
28
|
+
else
|
29
|
+
payment_method
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def as_json(options = nil)
|
34
|
+
super.merge(code: code)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
|
3
|
+
module QuickTravel
|
4
|
+
class Product < Adapter
|
5
|
+
attr_accessor :selection_name,
|
6
|
+
:description,
|
7
|
+
:capacity,
|
8
|
+
:available_capacity,
|
9
|
+
:available,
|
10
|
+
:error_message
|
11
|
+
|
12
|
+
attr_accessor :price # money_json(bookable_product.price)
|
13
|
+
|
14
|
+
attr_accessor :reservation_attributes
|
15
|
+
attr_accessor :image # is it url? dunno => json.image bookable_product.image
|
16
|
+
# {url: image_url, desc: image_desc}
|
17
|
+
|
18
|
+
attr_accessor :is_assigned # is it an assigned product -- i.e. reserved...
|
19
|
+
# useful only for extras when going through reservations api
|
20
|
+
|
21
|
+
attr_accessor :extras # more products
|
22
|
+
|
23
|
+
self.api_base = '/api/products'
|
24
|
+
|
25
|
+
# Find product details for a given product
|
26
|
+
#
|
27
|
+
# Options are::tabnew
|
28
|
+
# passenger_types: {<passenger_type_id> => <num_pax>, ...},
|
29
|
+
# date_range: {start_date: <date>, end_date: <date>}
|
30
|
+
def self.find(id, params = {})
|
31
|
+
fail 'Product#find requires passenger_type_numbers' if params[:passenger_type_numbers].blank?
|
32
|
+
fail 'Product#find requires date_range' if params[:date_range].blank?
|
33
|
+
super
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.bulk_availability(resource_ids, date_from, date_to, units)
|
37
|
+
request_params = {
|
38
|
+
resource_ids: resource_ids,
|
39
|
+
date_from: date_from,
|
40
|
+
date_to: date_to,
|
41
|
+
units: units
|
42
|
+
}
|
43
|
+
get_and_validate('/api/availability/bulk.json', request_params)
|
44
|
+
end
|
45
|
+
|
46
|
+
def self.unavailable_dates(resource_ids, date_from, date_to)
|
47
|
+
request_params = {
|
48
|
+
resource_ids: resource_ids,
|
49
|
+
date_from: date_from,
|
50
|
+
date_to: date_to
|
51
|
+
}
|
52
|
+
get_and_validate('/api/availability/unavailable_dates.json', request_params)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns minified mega structure:
|
56
|
+
#
|
57
|
+
# products_by_resource_id_and_date
|
58
|
+
# {
|
59
|
+
# <resource-id> => {<date> => <product-instance>, ...},
|
60
|
+
# ...
|
61
|
+
# }
|
62
|
+
def self.fetch_and_arrange_by_resource_id_and_date(resource_ids, options = {})
|
63
|
+
request_params = options.clone
|
64
|
+
request_params[:resource_ids] = resource_ids
|
65
|
+
|
66
|
+
# Returned call mega-structure is:
|
67
|
+
# [
|
68
|
+
# {
|
69
|
+
# "resource" => {"id" => <resource-id>, "name" => <resource-name>},
|
70
|
+
# "bookability" => {
|
71
|
+
# <date-as-string> => {<product-attrs}
|
72
|
+
# ...
|
73
|
+
# }
|
74
|
+
# },
|
75
|
+
# ...
|
76
|
+
# ]
|
77
|
+
complex_data = get_and_validate('/api/products/date_range_bookability.json', request_params)
|
78
|
+
complex_data ||= {}
|
79
|
+
|
80
|
+
products_by_resource_id_and_date = {}
|
81
|
+
|
82
|
+
# Go through shitty complex_grid_data
|
83
|
+
complex_data.each do |resource_and_bookability_hash|
|
84
|
+
resource_id = resource_and_bookability_hash.fetch('resource', {}).fetch('id', nil)
|
85
|
+
product_attrs_by_date = resource_and_bookability_hash.fetch('bookability', {})
|
86
|
+
|
87
|
+
products_by_resource_id_and_date[resource_id] = {}
|
88
|
+
product_attrs_by_date.each do |date, product_attrs|
|
89
|
+
products_by_resource_id_and_date[resource_id][date] = new(product_attrs)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
products_by_resource_id_and_date
|
94
|
+
end
|
95
|
+
|
96
|
+
attr_accessor :availability_details
|
97
|
+
attr_accessor :pricing_details,
|
98
|
+
:pricing_details_for_rack_rate,
|
99
|
+
:pricing_details_without_rules
|
100
|
+
|
101
|
+
def id
|
102
|
+
@reservation_attributes['resource_id']
|
103
|
+
end
|
104
|
+
|
105
|
+
def name
|
106
|
+
@selection_name
|
107
|
+
end
|
108
|
+
|
109
|
+
def available?
|
110
|
+
!!@available
|
111
|
+
end
|
112
|
+
|
113
|
+
def extras=(extra_attrs)
|
114
|
+
@extras = extra_attrs.map do |extra_attr|
|
115
|
+
QuickTravel::Product.new(extra_attr)
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def select_extras_by_id(selected_ids)
|
120
|
+
@extras.select { |extra| selected_ids.include? extra.id }
|
121
|
+
end
|
122
|
+
|
123
|
+
def resource=(resource_hash)
|
124
|
+
@resource = QuickTravel::Resource.new(resource_hash)
|
125
|
+
end
|
126
|
+
|
127
|
+
def availability_details=(availability_details)
|
128
|
+
@availability_details = AvailabilityDetails.new(availability_details)
|
129
|
+
end
|
130
|
+
|
131
|
+
def pricing_details=(pricing_details)
|
132
|
+
@pricing_details = PricingDetails.new(pricing_details)
|
133
|
+
end
|
134
|
+
|
135
|
+
def pricing_details_for_rack_rate=(pricing_details_for_rack_rate)
|
136
|
+
@pricing_details_for_rack_rate = PricingDetails.new(pricing_details_for_rack_rate)
|
137
|
+
end
|
138
|
+
|
139
|
+
def pricing_details_without_rules=(pricing_details_without_rules)
|
140
|
+
@pricing_details_without_rules = PricingDetails.new(pricing_details_without_rules)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
class PricingDetails < Adapter
|
145
|
+
attr_accessor :minimum_price_in_cents, :minimum_price_with_adjustments_in_cents, :total_adjustments_in_cents
|
146
|
+
attr_accessor :price_per_pax_type, :adjustments_to_apply, :applied_rules
|
147
|
+
|
148
|
+
money :minimum_price, :minimum_price_with_adjustments, :total_adjustments
|
149
|
+
|
150
|
+
def price_per_pax_type=(pax_type_hash)
|
151
|
+
@price_per_pax_type = convert_pax_type_hash(pax_type_hash)
|
152
|
+
end
|
153
|
+
|
154
|
+
def convert_pax_type_hash(pax_type_hash)
|
155
|
+
pax_type_hash.each.with_object({}) { |(type, cents), hash| hash[type.to_i] = Money.new(cents.to_i) }
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
class AvailabilityDetails < Adapter
|
160
|
+
end
|
161
|
+
|
162
|
+
class PricePerPaxType < Adapter
|
163
|
+
attr_accessor :amount_in_cents
|
164
|
+
end
|
165
|
+
end
|
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
require 'set'
|
3
|
+
|
4
|
+
module QuickTravel
|
5
|
+
class ProductConfiguration
|
6
|
+
attr_reader :product, :extra_pick_configurations
|
7
|
+
delegate :id, :name, to: :product
|
8
|
+
|
9
|
+
def initialize(product)
|
10
|
+
@product = product
|
11
|
+
@selected = false
|
12
|
+
@extra_pick_configurations =
|
13
|
+
product.extras.map { |extra_pick| self.class.new(extra_pick) }
|
14
|
+
end
|
15
|
+
|
16
|
+
def select!
|
17
|
+
@selected = true
|
18
|
+
end
|
19
|
+
|
20
|
+
def deselect!
|
21
|
+
@selected = false
|
22
|
+
end
|
23
|
+
|
24
|
+
def selected?
|
25
|
+
@selected
|
26
|
+
end
|
27
|
+
|
28
|
+
def available?
|
29
|
+
@product.available? &&
|
30
|
+
selected_extra_pick_configurations.all?(&:available?)
|
31
|
+
end
|
32
|
+
|
33
|
+
def priced?
|
34
|
+
@product.pricing_details.present?
|
35
|
+
end
|
36
|
+
|
37
|
+
def price
|
38
|
+
pricing_details.minimum_price_with_adjustments
|
39
|
+
end
|
40
|
+
|
41
|
+
def price_without_rules
|
42
|
+
pricing_details_without_rules.minimum_price_with_adjustments
|
43
|
+
end
|
44
|
+
|
45
|
+
def price_for_rack_rate
|
46
|
+
pricing_details_for_rack_rate.minimum_price_with_adjustments
|
47
|
+
end
|
48
|
+
|
49
|
+
def total_price
|
50
|
+
price + selected_extra_picks_price
|
51
|
+
end
|
52
|
+
|
53
|
+
def total_price_without_rules
|
54
|
+
price_without_rules + selected_extra_picks_price_without_rules
|
55
|
+
end
|
56
|
+
|
57
|
+
def total_price_for_rack_rate
|
58
|
+
price_for_rack_rate + selected_extra_picks_price_for_rack_rate
|
59
|
+
end
|
60
|
+
|
61
|
+
def applied_rules
|
62
|
+
pricing_details.applied_rules.uniq
|
63
|
+
end
|
64
|
+
|
65
|
+
def total_applied_rules
|
66
|
+
(applied_rules + selected_extra_picks_applied_rules).uniq
|
67
|
+
end
|
68
|
+
|
69
|
+
def price_per_passenger_type(passenger_type_id)
|
70
|
+
pricing_details.price_per_pax_type[passenger_type_id]
|
71
|
+
end
|
72
|
+
|
73
|
+
def selected_extra_pick_configurations
|
74
|
+
@extra_pick_configurations.select(&:selected?)
|
75
|
+
end
|
76
|
+
|
77
|
+
def available_extra_pick_configurations
|
78
|
+
@extra_pick_configurations.select(&:available?)
|
79
|
+
end
|
80
|
+
|
81
|
+
def select_extra_picks(extra_picks)
|
82
|
+
extra_picks.each do |extra_pick|
|
83
|
+
select_extra_pick(extra_pick)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def select_extra_pick(extra_pick)
|
88
|
+
extra_pick_configuration = configuration_for(extra_pick)
|
89
|
+
|
90
|
+
if extra_pick_configuration.nil?
|
91
|
+
fail ArgumentError, 'That extra pick does not belong to the product'
|
92
|
+
end
|
93
|
+
|
94
|
+
extra_pick_configuration.select!
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def configuration_for(extra_pick)
|
100
|
+
@extra_pick_configurations.find do |extra_pick_configuration|
|
101
|
+
extra_pick_configuration.product == extra_pick
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def selected_extra_picks_price
|
106
|
+
selected_extra_pick_configurations.map(&:price).total_money
|
107
|
+
end
|
108
|
+
|
109
|
+
def selected_extra_picks_price_without_rules
|
110
|
+
selected_extra_pick_configurations.map(&:price_without_rules).total_money
|
111
|
+
end
|
112
|
+
|
113
|
+
def selected_extra_picks_price_for_rack_rate
|
114
|
+
selected_extra_pick_configurations.map(&:price_for_rack_rate).total_money
|
115
|
+
end
|
116
|
+
|
117
|
+
def selected_extra_picks_applied_rules
|
118
|
+
selected_extra_pick_configurations.flat_map(&:applied_rules)
|
119
|
+
end
|
120
|
+
|
121
|
+
def pricing_details
|
122
|
+
fail 'Unable to calculate pricing details' unless priced?
|
123
|
+
@product.pricing_details
|
124
|
+
end
|
125
|
+
|
126
|
+
def pricing_details_without_rules
|
127
|
+
@product.pricing_details_without_rules || pricing_details
|
128
|
+
end
|
129
|
+
|
130
|
+
def pricing_details_for_rack_rate
|
131
|
+
@product.pricing_details_for_rack_rate || pricing_details
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module QuickTravel
|
2
|
+
class ProductPassengerSearchCriteria
|
3
|
+
def self.from_passengers(passengers)
|
4
|
+
new.tap do |product_passenger_search_criteria|
|
5
|
+
passengers.each do |passenger|
|
6
|
+
product_passenger_search_criteria.add_passenger_of_type(passenger.passenger_type_id)
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize
|
12
|
+
@passenger_count_by_type = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def add_passenger_of_type(passenger_type_id)
|
16
|
+
@passenger_count_by_type[passenger_type_id] ||= 0
|
17
|
+
@passenger_count_by_type[passenger_type_id] += 1
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_count_for_passenger_type(passenger_type_id, count)
|
21
|
+
@passenger_count_by_type[passenger_type_id] = count
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_hash
|
25
|
+
@passenger_count_by_type
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module QuickTravel
|
2
|
+
class ProductType < Adapter
|
3
|
+
attr_accessor :book_before_level, :book_before_units, :bookable_online, :can_find_by_location, :confirmation_request_fields, :default_passenger_ticket_format_id, :default_reservation_ticket_format_id
|
4
|
+
attr_accessor :default_vehicle_ticket_format_id, :detailed_template_fields, :disclaimer_id, :durational, :id, :individual_tickets
|
5
|
+
attr_accessor :individual_tickets, :mark_up_cents, :mark_up_definition_id, :mark_up_percent, :mark_up_rack_from_cost, :mark_up_rounding_cents
|
6
|
+
attr_accessor :mark_up_rounding_direction, :maximum_passengers_online, :measure_units_by_pax_count, :multi_leg, :name, :no_ticket
|
7
|
+
attr_accessor :only_bookable_with_accommodation, :overview_template_fields, :prompt_for_pick_up_drop_off, :resource_class_name, :rule_set_id,
|
8
|
+
:can_have_dates,
|
9
|
+
:can_have_quantity,
|
10
|
+
:needs_passengers
|
11
|
+
|
12
|
+
self.api_base = '/product_types'
|
13
|
+
|
14
|
+
def self.all
|
15
|
+
QuickTravel::Cache.cache 'all_product_types' do
|
16
|
+
super
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def route
|
21
|
+
Route.first(id)
|
22
|
+
end
|
23
|
+
|
24
|
+
def routes
|
25
|
+
Route.all(id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'quick_travel/adapter'
|
2
|
+
require 'quick_travel/property_facility'
|
3
|
+
|
4
|
+
module QuickTravel
|
5
|
+
class Property < Adapter
|
6
|
+
attr_accessor :id, :name, :check_in, :check_in_instructions, :check_out, :contact_id, :contact_person, :description
|
7
|
+
attr_accessor :location_id, :notes, :star_rating, :tourism_accredited, :availability, :on_request
|
8
|
+
attr_accessor :minimum_available_price_in_cents
|
9
|
+
attr_accessor :graphic # required in property search object
|
10
|
+
attr_accessor :boundary_start, :boundary_end, :season_id # added in API 3.8.*
|
11
|
+
attr_accessor :minimum_bookable_duration
|
12
|
+
attr_accessor :maximum_occupancy
|
13
|
+
attr_accessor :location_name, :region_names, :error
|
14
|
+
|
15
|
+
attr_reader :accommodations
|
16
|
+
|
17
|
+
money :minimum_available_price
|
18
|
+
|
19
|
+
def accommodations=(hash_array)
|
20
|
+
@accommodations = hash_array.map { |accommodation_hash| Accommodation.new(accommodation_hash) }
|
21
|
+
end
|
22
|
+
|
23
|
+
# This method returns first object of Property based on property id from QuickTravel
|
24
|
+
def self.first(id, options = {})
|
25
|
+
fail ArgumentError.new('Must Specify valid property id') if id.blank? || id.class != Fixnum
|
26
|
+
generic_first("/api/properties/#{id}.json", options)
|
27
|
+
end
|
28
|
+
|
29
|
+
# This method returns all objects of property from QuickTravel that match
|
30
|
+
#
|
31
|
+
# location_id is compulsory param
|
32
|
+
# :property_type_id=>1, :location_id=>5
|
33
|
+
#
|
34
|
+
# Example response:
|
35
|
+
# { :property_type_id=>1, :location_id=>5 , :number_of_rooms => 1 , :product => {:first_travel_date => "07-05-2010" , :duration => 1 } }
|
36
|
+
def self.find!(condition = {})
|
37
|
+
condition[:number_of_rooms] = 1 if condition[:number_of_rooms].blank? || condition[:number_of_rooms].to_i < 1
|
38
|
+
condition[:last_travel_date] = condition[:product][:last_travel_date].to_date - 1 if condition[:product].try(:fetch, :last_travel_date, nil)
|
39
|
+
self.find_all!('/api/properties.json', condition)
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.load_with_pricing(id, options)
|
43
|
+
# Find property 'standard' way -- finds price for whole duration
|
44
|
+
property = Property.first(id, options)
|
45
|
+
first_travel_date = options[:product][:first_travel_date]
|
46
|
+
property.accommodations.each do |accommodation|
|
47
|
+
accommodation.minimum_nightly_price = accommodation.nightly_price_on first_travel_date
|
48
|
+
accommodation.minimum_price_for_duration = accommodation.minimum_price_on first_travel_date
|
49
|
+
end
|
50
|
+
|
51
|
+
property
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_param
|
55
|
+
"#{id}-#{name.gsub(' ', '-')}"
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.all!
|
59
|
+
self.find_all!('/properties.json')
|
60
|
+
end
|
61
|
+
|
62
|
+
# this method is only used for properties that are searched through find method, because QT_API returns graphic string in search results
|
63
|
+
def graphic_url
|
64
|
+
QuickTravel.url + graphic if graphic.present?
|
65
|
+
end
|
66
|
+
|
67
|
+
def graphics=(graphics)
|
68
|
+
@graphic_attributes = graphics
|
69
|
+
end
|
70
|
+
|
71
|
+
def graphics
|
72
|
+
@graphics ||= @graphic_attributes.map { |g| QuickTravel::Graphic.new(g) }
|
73
|
+
end
|
74
|
+
|
75
|
+
def accommodations_by_availability(stay_dates)
|
76
|
+
accommodations.sort_by do |accommodation|
|
77
|
+
(accommodation.available_over_dates?(stay_dates) ? 0 : 1)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def check_availability(date)
|
82
|
+
availability && availability[date.to_s]
|
83
|
+
end
|
84
|
+
|
85
|
+
def available?
|
86
|
+
# self.availability.present? &&
|
87
|
+
# self.availability.any?{|availability_level| availability_level == 'on_allotment_and_available' }
|
88
|
+
availability.present? && availability.all? { |_date, is_avail| is_avail }
|
89
|
+
end
|
90
|
+
|
91
|
+
def property_facilities
|
92
|
+
@_property_facilities ||= @property_facilities.map { |item| PropertyFacility.new(item) }
|
93
|
+
end
|
94
|
+
|
95
|
+
def address
|
96
|
+
@_address ||= Address.new(@address)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|