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,370 @@
1
+ require 'quick_travel/adapter'
2
+ require 'quick_travel/passenger'
3
+ require 'quick_travel/vehicle'
4
+ require 'quick_travel/payment'
5
+ require 'quick_travel/payment_type'
6
+ require 'quick_travel/discounts'
7
+
8
+ module QuickTravel
9
+ class Booking < Adapter
10
+ attr_accessor :reference, :state, :client_id, :first_travel_date, :created_at, :access_token
11
+ attr_accessor :gross_in_cents, :commission_in_cents, :cost_in_cents, :balance_in_cents, :paid_in_cents, :surcharge_in_cents
12
+ attr_accessor :deposit_in_cents, :deposit_due_on, :deposit_relevant
13
+
14
+ attr_accessor :id # booking_id. I am initializing this data member when we create booking.
15
+ attr_accessor :client_address, :client_party, :client_contact,
16
+ :customer_contact_name, :customer_contact_phone, :customer_contact_mobile, :customer_contact_email,
17
+ :post_code, :country_id, :referral_code_id,
18
+ :discardable_in, :inactivatable_in,
19
+ :promo_code, :web_site_name,
20
+ :insurance_offered, :public_comments
21
+
22
+ money :gross, :balance, :commission, :paid, :surcharge, :deposit
23
+
24
+ def self.api_base
25
+ '/api/bookings'
26
+ end
27
+
28
+ def self.front_office_base
29
+ '/front_office/bookings'
30
+ end
31
+
32
+ def self.find_by_reference(reference)
33
+ find_all!("#{api_base}/reference/#{reference}.json").first
34
+ end
35
+
36
+ def documents(regenerate = false)
37
+ Document.find_all!("#{Booking.api_base}/#{@id}/documents.json",
38
+ last_group: true, regenerate: regenerate)
39
+ end
40
+
41
+ def payments
42
+ Payment.find_all!("#{Booking.front_office_base}/#{@id}/payments.json")
43
+ end
44
+
45
+ def payment_types
46
+ PaymentType.find_all!("#{Booking.front_office_base}/#{@id}/payment_types.json")
47
+ end
48
+
49
+ def on_account_payment_type
50
+ payment_types_by_code = payment_types.group_by(&:code)
51
+
52
+ # Try on-account, or if not, on-account-with-reference
53
+ payment_types_by_code['on_account_without_reference'].try(:first) ||
54
+ payment_types_by_code['on_account_with_reference'].try(:first)
55
+ end
56
+
57
+ def country
58
+ Country.find(@country_id)
59
+ end
60
+
61
+ def deposit_due_on
62
+ @deposit_due_on_parsed ||= parse_date(@deposit_due_on)
63
+ end
64
+
65
+ def balance_due_on
66
+ @balance_due_on_parsed ||= parse_date(@balance_due_on)
67
+ end
68
+
69
+ # Create an empty booking
70
+ #
71
+ # Note, options pertain to initializing booking with some values:
72
+ #
73
+ # options = {
74
+ # :when => "28-04-2010" ,
75
+ # :passengers => { "1" => nil, "2" => nil, "3" => nil, "4" => nil , "5" => nil } ,
76
+ # :include_vehicle => nil , :vehicle => { :vehicle_type_id => nil , :length , :weight } ,
77
+ # :vehicle_return_weight => nil ,
78
+ # :include_trailer => nil ,
79
+ # :trailer => { :vehicle_type_id => nil , :length => nil }
80
+ # }
81
+ def self.create(options = {})
82
+ default_options = { referral_code_id: DEFAULT_REFERRAL_CODE_ID }
83
+ options = { booking: default_options.merge(options) }
84
+
85
+ response = post_and_validate("#{front_office_base}.json", options)
86
+ fail AdapterException.new(response) unless response['id']
87
+
88
+ return nil unless response['id']
89
+ Booking.new(response)
90
+ end
91
+
92
+ # Update an existing booking
93
+ def update(options = {})
94
+ response = put_and_validate("#{api_base}/#{@id}.json", booking: options)
95
+ # id is returned if other attributes change otherwise success: true
96
+ fail AdapterException.new(response) unless response['id'] || response['success']
97
+
98
+ Booking.find(@id)
99
+ end
100
+
101
+ # ###
102
+ # Updates:
103
+ # - Client information
104
+ # - Client contact
105
+ # - Client address
106
+ # - Pax details
107
+ # - Vehicle details
108
+ def update_with_nested_attributes!(booking_args = {})
109
+ response = put_and_validate("#{api_base}/#{@id}/update_with_nested_attributes.json",
110
+ booking: booking_args)
111
+ fail AdapterException.new(response) unless response['id']
112
+ Booking.find(response['id'])
113
+ end
114
+
115
+ def remove_unassigned_passengers
116
+ put_and_validate("/api/bookings/#{@id}/remove_unassigned_passengers")
117
+ end
118
+
119
+ # Create an accommodation reservation from the given options
120
+ #
121
+ # Returns current booking object after having added the reservation
122
+ #
123
+ # reservations_options = {
124
+ # :id => nil ,
125
+ # :first_travel_date => nil,
126
+ # :last_travel_date => nil,
127
+ # :resource_id => nil ,
128
+ # :passenger_ids => {} ,
129
+ # :vehicle_ids => {} ,
130
+ # :bed_configuration_id => nil ,
131
+ # :tariff_level_id => nil
132
+ # }
133
+ #
134
+ # Example 2:
135
+ # reservations_options = {
136
+ # :first_trave_date => "28-04-2010",
137
+ # :last_travel_date => "28-04-2010",
138
+ # :resource_id => 89,
139
+ # :bed_configuration_id => 581,
140
+ # :passenger_ids => {""=>nil},
141
+ # :vehicle_ids => {""=>nil}
142
+ # }
143
+ def accommodation_reserve(reservations_options = {})
144
+ if reservations_options[:last_travel_date].nil?
145
+ fail AdapterException.new('No checkout date specified')
146
+ end
147
+
148
+ last_travel_date = Date.strptime(reservations_options[:last_travel_date], '%d/%m/%Y')
149
+
150
+ # Because QT requires last-date to be the date of use, we have to subtract one
151
+ last_travel_date -= 1
152
+
153
+ options = { reservations: reservations_options }
154
+ options[:reservations][:last_travel_date] = last_travel_date.strftime(QT_DATE_FORMAT)
155
+
156
+ reserve('accommodations/create_or_update', options)
157
+ end
158
+
159
+ # Reserve a scheduled trips resource
160
+ # Returns current booking object after having added the reservation
161
+ #
162
+ # Note:
163
+ # Forward_options and return_options look similar, but each is a different trip
164
+ #
165
+ # Example:
166
+ # forward_options = {
167
+ # :passenger_ids => {} ,
168
+ # :vehicle_ids => {} ,
169
+ # :first_travel_date => nil,
170
+ # :tariff_level_id => nil ,
171
+ # :resource_id => nil ,
172
+ # :trip_id => nil
173
+ # }
174
+ def scheduled_trips_reserve(segments = {})
175
+ reserve(:scheduled_trips, segments.merge(segments: segments.keys))
176
+ end
177
+
178
+ def scheduled_trips_update(segments = {})
179
+ params = segments.merge(segments: segments.keys)
180
+ params[:booking_id] = @id
181
+ put_and_validate('/reservation_for/scheduled_trips/bulk_update.json', params)
182
+ Booking.find(@id)
183
+ end
184
+
185
+ def generics_reserve(options)
186
+ reserve(:generics, options)
187
+ end
188
+
189
+ def item_reserve(options)
190
+ reserve(:items, options)
191
+ end
192
+
193
+ def tour_reserve(options)
194
+ reserve(:packages, options)
195
+ end
196
+
197
+ # Delete a reservation
198
+ #
199
+ # Returns current booking object after deleting the reservation
200
+ def delete_reservation(reservation)
201
+ delete_reservation_by_id(reservation.id)
202
+ end
203
+
204
+ def delete_reservation_by_id(reservation_id)
205
+ if state != 'new'
206
+ fail AdapterException.new('Reservation cannot be deleted unless the booking is new')
207
+ end
208
+
209
+ delete_and_validate("#{api_base}/#{@id}/reservations/#{reservation_id}.json")
210
+ refresh!
211
+ end
212
+
213
+ def refresh!
214
+ Booking.find(@id) # refresh
215
+ end
216
+
217
+ # Generate passenger objects from passenger data (hash)
218
+ def passengers
219
+ @passengers ||=
220
+ @passengers_attributes.map { |passenger| Passenger.new(passenger) }
221
+ end
222
+
223
+ # Generate passenger objects from passenger data (hash)
224
+ def vehicles
225
+ @vehicles ||=
226
+ @vehicles_attributes.map { |vehicle| Vehicle.new(vehicle) }
227
+ end
228
+
229
+ def find_passenger_by_id(pid)
230
+ passengers.detect { |p| p.id.to_i == pid.to_i }
231
+ end
232
+
233
+ def find_vehicle_by_id(vid)
234
+ vehicles.detect { |v| v.id.to_i == vid.to_i }
235
+ end
236
+
237
+ def passenger_types_counts
238
+ passengers.each_with_object(Hash.new(0)) do |passenger, hash|
239
+ hash[passenger.passenger_type_id] += 1
240
+ end
241
+ end
242
+
243
+ def vehicles_hash
244
+ @vehicles
245
+ end
246
+
247
+ # It returns booking items for a booking.
248
+ # Array of BookingItem
249
+ def reservations
250
+ fail AdapterException, 'Reservations have not been set from API' if @reservations_attributes.nil?
251
+ @_reservations_object_array ||= @reservations_attributes.map { |item| Reservation.new(item) }
252
+ end
253
+
254
+ def legacy_reservations
255
+ @_reservations_object_array = @reservations.map { |item| Reservation.new(item) }
256
+ end
257
+
258
+ def client_address
259
+ @client_address_object ||= Address.new(@client_address)
260
+ end
261
+
262
+ def client_party
263
+ @client_party_object ||= Party.new(@client_party)
264
+ end
265
+
266
+ def client_contact
267
+ @client_contact_object ||= Contact.new(@client_contact)
268
+ end
269
+
270
+ def client
271
+ @client_object ||= Client.new(@client)
272
+ end
273
+
274
+ def calculate_existing_and_new_vehicles_for(required_vehicle_types)
275
+ return [[], []] if required_vehicle_types.blank?
276
+
277
+ vehicle_types_to_add = []
278
+ existing_vehicle_ids_to_assign = []
279
+
280
+ vehicles_yet_to_include = try(:vehicles)
281
+
282
+ required_vehicle_types.each do |searched_vehicle_type_hash|
283
+ matching_existing_vehicle = vehicles_yet_to_include.detect { |veh| veh.vehicle_type_id.to_i == searched_vehicle_type_hash[:vehicle_type_id].to_i }
284
+ if matching_existing_vehicle
285
+ existing_vehicle_ids_to_assign << matching_existing_vehicle.id.to_i
286
+ vehicles_yet_to_include -= [matching_existing_vehicle]
287
+
288
+ else
289
+ vehicle_types_to_add << searched_vehicle_type_hash
290
+ end
291
+ end
292
+
293
+ [existing_vehicle_ids_to_assign, vehicle_types_to_add]
294
+ end
295
+
296
+ def include_reservation_of?(product_type_id)
297
+ reservations.any? { |r| r.product_type_id.to_i == product_type_id }
298
+ end
299
+
300
+ def includes_resource_class?(resource_class_name)
301
+ reservations.any? { |r| r.resource_class_name == resource_class_name }
302
+ end
303
+
304
+ def clear_unfinished_reservations!
305
+ booking = self
306
+ reservations.reject(&:complete).each do |reservation|
307
+ # This will return updated booking..
308
+ booking = delete_reservation(reservation)
309
+ end
310
+ booking
311
+ end
312
+
313
+ def finalised?
314
+ (balance_in_cents == 0 && state != 'new' && !reservations.empty?) || state == 'quote'
315
+ end
316
+
317
+ def discount
318
+ @discount ||= fetch_discount
319
+ end
320
+
321
+ def discount_on(reservation)
322
+ discount.discount_on(reservation.id)
323
+ end
324
+
325
+ def total_discount_on(reservation)
326
+ discount.total_discount_on(reservation.id)
327
+ end
328
+
329
+ def calculate_price_quote(segments = {})
330
+ params = segments.merge(segments: segments.keys)
331
+ response = post_and_validate("#{api_base}/#{@id}/price_quotes/calculate", params)
332
+ Money.new(response['quoted_booking_gross_in_cents'])
333
+ end
334
+
335
+ def activate!
336
+ put_and_validate("#{api_base}/#{@id}/activate")
337
+ end
338
+
339
+ def subscribe_to_email_list(email = nil)
340
+ params = email.nil? ? {} : { email: email }
341
+ put_and_validate("#{api_base}/#{@id}/subscribe_to_email_list", params)
342
+ end
343
+
344
+ # secureAccessCodeString is simply a MAC algorithm (using SHA1) on the booking id string.
345
+ # it is using @id that is booking.id and configuration constant QUICK_TRAVEL_ACCESS_KEY
346
+ def secure_access_code
347
+ Encrypt.access_key(@id.to_s)
348
+ end
349
+
350
+ protected
351
+
352
+ def reserve(url, options)
353
+ options[:booking_id] = @id
354
+ post_and_validate("/reservation_for/#{url}.json", options)
355
+ Booking.find(@id)
356
+ end
357
+
358
+ private
359
+
360
+ def parse_date(hash)
361
+ return if hash.blank? || hash['_value'].blank?
362
+ hash['_value'].to_date
363
+ end
364
+
365
+ def fetch_discount
366
+ attributes = get_and_validate("#{api_base}/#{@id}/discount.json")
367
+ QuickTravel::Discounts::BookingDiscount.new(attributes)
368
+ end
369
+ end
370
+ end
@@ -0,0 +1,38 @@
1
+ module QuickTravel
2
+ module Cache
3
+ def cache(key, &block)
4
+ QuickTravel::Cache.cache(key) do
5
+ block.call
6
+ end
7
+ end
8
+
9
+ def self.cache(key)
10
+ cached_value = cache_store.read(key)
11
+ return cached_value unless cached_value.nil?
12
+ return nil unless block_given?
13
+ yield.tap { |value| cache_store.write(key, value) }
14
+ end
15
+
16
+ def self.cache_store
17
+ @@cache_store
18
+ end
19
+
20
+ def self.cache_store=(store)
21
+ @@cache_store = store
22
+ end
23
+ end
24
+
25
+ class DefaultCacheStore
26
+ def initialize
27
+ @store = {}
28
+ end
29
+
30
+ def write(key, value)
31
+ @store[key] = value
32
+ end
33
+
34
+ def read(key)
35
+ @store[key]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,8 @@
1
+ module QuickTravel
2
+ class Client < Adapter
3
+ attr_accessor :id, :party_id, :surchargeless, :client_type,
4
+ :contact_phone, :contact_mobile, :contact_email,
5
+ :name,
6
+ :first_name, :last_name
7
+ end
8
+ end
@@ -0,0 +1,6 @@
1
+ module QuickTravel
2
+ class ClientType < Adapter
3
+ attr_accessor :id, :name, :agent, :position, :right_id
4
+ self.api_base = '/client_types'
5
+ end
6
+ end
@@ -0,0 +1,26 @@
1
+ require 'quick_travel/adapter'
2
+
3
+ module QuickTravel
4
+ class Configuration
5
+ attr_accessor :url, :access_key, :version
6
+
7
+ def url=(url)
8
+ @url = url
9
+ QuickTravel::Adapter.base_uri url
10
+ end
11
+
12
+ attr_writer :access_key
13
+ end
14
+
15
+ def self.configure
16
+ yield QuickTravel.config
17
+ end
18
+
19
+ def self.config
20
+ @config ||= QuickTravel::Configuration.new
21
+ end
22
+
23
+ def self.url
24
+ config.url
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ module QuickTravel
2
+ class ConnectionError < Exception
3
+ attr_reader :original
4
+ def initialize(msg, original = $ERROR_INFO)
5
+ super(msg)
6
+ @original = original
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ ### DELETE ME! Left here only so you can pull out the constants for the few that we should be keeping.
2
+
3
+ module QuickTravel
4
+ LOGIN_URL = '/parties/login' # CMS_SSL_URL + '/parties/login'
5
+
6
+ DEFAULT_LOCATION_ID_FOR_ACCOMM_SEARCH = 3 # Kingscote
7
+ DEFAULT_REGION_ID_FOR_ACCOMM_SEARCH = 1 # Kangaroo Island
8
+ DEFAULT_PROPERTY_TYPE_ID_FOR_ACCOMM_SEARCH = 1
9
+
10
+ # System wide
11
+ PROPERTY_CACHE_TIMEOUT = 1440 # minutes
12
+ AUSTRALIA_COUNTRY_ID = 14
13
+ QT_DATE_FORMAT = '%d-%m-%Y'
14
+
15
+ DEFAULT_REFERRAL_CODE_ID = 33
16
+ end
@@ -0,0 +1,5 @@
1
+ module QuickTravel
2
+ class Contact < Adapter
3
+ attr_accessor :phone, :email, :id
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'quick_travel/adapter'
2
+ require 'quick_travel/cache'
3
+
4
+ module QuickTravel
5
+ class Country < Adapter
6
+ attr_accessor :id, :iso_3166_code, :name, :latitude, :longitude
7
+
8
+ self.api_base = '/api/countries'
9
+ self.lookup = true
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ module QuickTravel
2
+ class CreditCard
3
+ def self.type_from_number(number)
4
+ case number.to_s
5
+ when /^4.*$/ then 'visa'
6
+ when /^5[1-5].*$/ then 'master'
7
+ when /^3[4,7].*$/ then 'american_express'
8
+ when /^3[0,6,8].*$/ then 'diners_club'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,18 @@
1
+ module QuickTravel
2
+ module Discounts
3
+ class BookingDiscount < DiscountTree
4
+ def initialize(attrs = {})
5
+ super(
6
+ attrs.merge(
7
+ 'root' => attrs,
8
+ 'children' => attrs['reservation_discounts']
9
+ )
10
+ )
11
+ end
12
+
13
+ def reservation_discounts
14
+ @children
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,21 @@
1
+ module QuickTravel
2
+ module Discounts
3
+ class Discount
4
+ attr_reader :target
5
+ attr_reader :original_price, :discounted_price, :discount, :reason
6
+
7
+ def initialize(attrs = {})
8
+ @target = OpenStruct.new(attrs.fetch('target').slice('id', 'type'))
9
+
10
+ @original_price = Money.new(attrs.fetch('original_price_in_cents'))
11
+ @discounted_price = Money.new(attrs.fetch('discounted_price_in_cents'))
12
+ @discount = Money.new(attrs.fetch('discount_in_cents'))
13
+ @reason = attrs.fetch('reason')
14
+ end
15
+
16
+ def applied_on?(id, type = 'Reservation')
17
+ @target.type == type && @target.id == id
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,35 @@
1
+ module QuickTravel
2
+ module Discounts
3
+ class DiscountTree < Discount
4
+ attr_reader :root, :children
5
+
6
+ def initialize(attrs = {})
7
+ super(attrs)
8
+ @root = Discount.new(attrs['root'])
9
+ @children = attrs.fetch('children', []).map do |child_attrs|
10
+ DiscountTree.new(child_attrs)
11
+ end
12
+ end
13
+
14
+ def discount_on(id, type = 'Reservation')
15
+ return @root if applied_on?(id, type)
16
+ find_and_return_on_children { |child| child.discount_on(id, type) }
17
+ end
18
+
19
+ def total_discount_on(id, type = 'Reservation')
20
+ return self if applied_on?(id, type)
21
+ find_and_return_on_children { |child| child.total_discount_on(id, type) }
22
+ end
23
+
24
+ private
25
+
26
+ def find_and_return_on_children(&block)
27
+ @children.each do |child|
28
+ result = block.call(child)
29
+ return result unless result.nil?
30
+ end
31
+ nil
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,3 @@
1
+ require 'quick_travel/discounts/discount'
2
+ require 'quick_travel/discounts/discount_tree'
3
+ require 'quick_travel/discounts/booking_discount'
@@ -0,0 +1,20 @@
1
+ require 'quick_travel/adapter'
2
+
3
+ module QuickTravel
4
+ class Document < Adapter
5
+ attr_accessor :id, :booking_id, :name
6
+
7
+ self.api_base = "/front_office/bookings/#{@booking_id}/documents"
8
+
9
+ # We used to direct the browser to QT directly
10
+ # Now, we proxy over to it, and return the data
11
+ # def pdf_url
12
+ # #"#{QuickTravel.config.url}/api/bookings/#{@booking_id}/documents/#{@id}.pdf?key=#{access_key("document:#{@id}")}"
13
+ # end
14
+
15
+ # Do a second request fetching the PDF
16
+ def fetch_pdf
17
+ get_and_validate "/api/bookings/#{@booking_id}/documents/#{@id}.pdf"
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,14 @@
1
+ require 'quick_travel/adapter'
2
+ require 'quick_travel/encrypt'
3
+
4
+ module QuickTravel
5
+ class DocumentGroup < Adapter
6
+ attr_accessor :id, :booking_id
7
+
8
+ self.api_base = "/front_office/bookings/#{@booking_id}/document_groups"
9
+
10
+ def pdf_url
11
+ "#{QuickTravel.config.url}/api/bookings/#{@booking_id}/document_groups/#{@id}.pdf?key=#{Encrypt.access_key("document_group:#{@id}")}"
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,13 @@
1
+ require 'openssl'
2
+ require 'base64'
3
+
4
+ DIGEST = OpenSSL::Digest.new('sha256')
5
+
6
+ module QuickTravel
7
+ module Encrypt
8
+ def self.access_key(text)
9
+ digest = OpenSSL::HMAC.hexdigest(DIGEST, QuickTravel.config.access_key, text)
10
+ Base64.encode64s(digest).chomp
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,18 @@
1
+ module QuickTravel
2
+ class Graphic
3
+ attr_accessor :thumbnails, :image_uid
4
+
5
+ def initialize(g)
6
+ @image_uid = g['image_uid']
7
+ end
8
+
9
+ def path(thumbnail_name)
10
+ result = @thumbnails.find_all { |t| t['thumbnail'] == thumbnail_name.to_s }
11
+ '/media/cache/' + result.first['filename'] unless result.blank?
12
+ end
13
+
14
+ def url(thumbnail_name)
15
+ QuickTravel.url + path(thumbnail_name)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,16 @@
1
+ module QuickTravel
2
+ module InitFromHash
3
+ def initialize(hash = {})
4
+ return nil if hash.blank?
5
+
6
+ hash.each do |attr, val|
7
+ # set datamember of the object using hash key and value
8
+ if respond_to?("#{attr}=")
9
+ send("#{attr}=", val)
10
+ else
11
+ instance_variable_set("@#{attr}".to_sym, val)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end