quicktravel_client 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|