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