quicktravel_client 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +25 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +13 -0
  5. data/.ruby-version +1 -0
  6. data/.travis.yml +18 -0
  7. data/Gemfile +5 -0
  8. data/LICENSE +22 -0
  9. data/README.md +17 -0
  10. data/Rakefile +18 -0
  11. data/bootstrap/boot.rb +4 -0
  12. data/examples/example_config.rb +4 -0
  13. data/examples/login_example.rb +25 -0
  14. data/lib/extensions.rb +12 -0
  15. data/lib/quick_travel/accommodation.rb +72 -0
  16. data/lib/quick_travel/adapter.rb +283 -0
  17. data/lib/quick_travel/adapter_exception.rb +20 -0
  18. data/lib/quick_travel/address.rb +27 -0
  19. data/lib/quick_travel/adjustment.rb +8 -0
  20. data/lib/quick_travel/background_check.rb +9 -0
  21. data/lib/quick_travel/bed_configuration.rb +7 -0
  22. data/lib/quick_travel/bed_requirement.rb +7 -0
  23. data/lib/quick_travel/booking.rb +370 -0
  24. data/lib/quick_travel/cache.rb +38 -0
  25. data/lib/quick_travel/client.rb +8 -0
  26. data/lib/quick_travel/client_type.rb +6 -0
  27. data/lib/quick_travel/config.rb +26 -0
  28. data/lib/quick_travel/connection_error.rb +9 -0
  29. data/lib/quick_travel/constants.rb +16 -0
  30. data/lib/quick_travel/contact.rb +5 -0
  31. data/lib/quick_travel/country.rb +11 -0
  32. data/lib/quick_travel/credit_card.rb +12 -0
  33. data/lib/quick_travel/discounts/booking_discount.rb +18 -0
  34. data/lib/quick_travel/discounts/discount.rb +21 -0
  35. data/lib/quick_travel/discounts/discount_tree.rb +35 -0
  36. data/lib/quick_travel/discounts.rb +3 -0
  37. data/lib/quick_travel/document.rb +20 -0
  38. data/lib/quick_travel/document_group.rb +14 -0
  39. data/lib/quick_travel/encrypt.rb +13 -0
  40. data/lib/quick_travel/graphic.rb +18 -0
  41. data/lib/quick_travel/init_from_hash.rb +16 -0
  42. data/lib/quick_travel/location.rb +14 -0
  43. data/lib/quick_travel/mock.rb +41 -0
  44. data/lib/quick_travel/party.rb +49 -0
  45. data/lib/quick_travel/passenger.rb +7 -0
  46. data/lib/quick_travel/passenger_price_break.rb +8 -0
  47. data/lib/quick_travel/passenger_type.rb +21 -0
  48. data/lib/quick_travel/payment.rb +41 -0
  49. data/lib/quick_travel/payment_type.rb +37 -0
  50. data/lib/quick_travel/price.rb +14 -0
  51. data/lib/quick_travel/product.rb +165 -0
  52. data/lib/quick_travel/product_configuration.rb +134 -0
  53. data/lib/quick_travel/product_passenger_search_criteria.rb +28 -0
  54. data/lib/quick_travel/product_type.rb +28 -0
  55. data/lib/quick_travel/property.rb +99 -0
  56. data/lib/quick_travel/property_facility.rb +7 -0
  57. data/lib/quick_travel/property_type.rb +20 -0
  58. data/lib/quick_travel/region.rb +21 -0
  59. data/lib/quick_travel/reservation.rb +110 -0
  60. data/lib/quick_travel/resource.rb +50 -0
  61. data/lib/quick_travel/room_facility.rb +7 -0
  62. data/lib/quick_travel/route.rb +81 -0
  63. data/lib/quick_travel/route_stop.rb +7 -0
  64. data/lib/quick_travel/search.rb +14 -0
  65. data/lib/quick_travel/service.rb +5 -0
  66. data/lib/quick_travel/trip.rb +8 -0
  67. data/lib/quick_travel/vehicle.rb +17 -0
  68. data/lib/quick_travel/vehicle_type.rb +11 -0
  69. data/lib/quick_travel/version.rb +3 -0
  70. data/lib/quick_travel.rb +59 -0
  71. data/lib/quicktravel_client.rb +1 -0
  72. data/quicktravel_client.gemspec +37 -0
  73. data/spec/booking_spec.rb +107 -0
  74. data/spec/country_spec.rb +18 -0
  75. data/spec/credit_card_spec.rb +11 -0
  76. data/spec/discounts_spec.rb +121 -0
  77. data/spec/passenger_type_spec.rb +32 -0
  78. data/spec/payment_type_spec.rb +18 -0
  79. data/spec/product_spec.rb +82 -0
  80. data/spec/region_spec.rb +29 -0
  81. data/spec/reservation_spec.rb +50 -0
  82. data/spec/resource_spec.rb +22 -0
  83. data/spec/spec_helper.rb +77 -0
  84. data/spec/support/cassettes/booking_create.yml +50 -0
  85. data/spec/support/cassettes/booking_create_legacy.yml +50 -0
  86. data/spec/support/cassettes/booking_discounts.yml +53 -0
  87. data/spec/support/cassettes/booking_documents.yml +48 -0
  88. data/spec/support/cassettes/booking_show.yml +49 -0
  89. data/spec/support/cassettes/booking_update.yml +94 -0
  90. data/spec/support/cassettes/booking_with_discounts.yml +72 -0
  91. data/spec/support/cassettes/booking_with_documents.yml +117 -0
  92. data/spec/support/cassettes/booking_with_nested_attributes.yml +187 -0
  93. data/spec/support/cassettes/country_all.yml +127 -0
  94. data/spec/support/cassettes/create_reservation_fail.yml +47 -0
  95. data/spec/support/cassettes/create_reservation_with_booking.yml +53 -0
  96. data/spec/support/cassettes/passenger_all.yml +48 -0
  97. data/spec/support/cassettes/product_date_range_bookability.yml +118 -0
  98. data/spec/support/cassettes/product_show.yml +52 -0
  99. data/spec/support/cassettes/region_index.yml +48 -0
  100. data/spec/support/cassettes/region_show.yml +48 -0
  101. data/spec/support/cassettes/reservation_with_extra_picks.yml +165 -0
  102. data/spec/support/cassettes/resource_fare_bases.yml +97 -0
  103. data/spec/support/cassettes/resource_show.yml +50 -0
  104. 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,7 @@
1
+ require 'quick_travel/adapter'
2
+
3
+ module QuickTravel
4
+ class Passenger < Adapter
5
+ attr_accessor :id, :passenger_type_id, :age, :gender, :title, :first_name, :last_name, :details, :passenger_type_name
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ require 'quick_travel/adapter'
2
+
3
+ module QuickTravel
4
+ class PassengerPriceBreak < Adapter
5
+ attr_accessor :amount_in_cents
6
+ money :amount
7
+ end
8
+ 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,14 @@
1
+ class Price
2
+ attr_accessor :cents, :currency, :dollars
3
+
4
+ def initialize(hash)
5
+ cents = hash['cents'].to_i
6
+ @dollars = cents / 100
7
+ @cents = cents % 100
8
+ @currency = hash['currency']
9
+ end
10
+
11
+ def formatted_text
12
+ "#{@dollars}"
13
+ end
14
+ 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