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.
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