booker_api 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/lib/booker/booker.rb +5 -0
- data/lib/booker/business_client.rb +22 -0
- data/lib/booker/business_rest.rb +130 -0
- data/lib/booker/client.rb +212 -0
- data/lib/booker/common_rest.rb +49 -0
- data/lib/booker/config/booker_country_ids_to_iso_codes.yml +269 -0
- data/lib/booker/customer_client.rb +17 -0
- data/lib/booker/customer_rest.rb +169 -0
- data/lib/booker/errors.rb +24 -0
- data/lib/booker/generic_token_store.rb +25 -0
- data/lib/booker/helpers/active_support_helper.rb +102 -0
- data/lib/booker/helpers/logging_helper.rb +11 -0
- data/lib/booker/models/address.rb +12 -0
- data/lib/booker/models/appointment.rb +77 -0
- data/lib/booker/models/appointment_treatment.rb +51 -0
- data/lib/booker/models/available_time.rb +13 -0
- data/lib/booker/models/business_type.rb +5 -0
- data/lib/booker/models/category.rb +5 -0
- data/lib/booker/models/class_instance.rb +25 -0
- data/lib/booker/models/country.rb +19 -0
- data/lib/booker/models/current_price.rb +5 -0
- data/lib/booker/models/customer.rb +52 -0
- data/lib/booker/models/customer_2.rb +5 -0
- data/lib/booker/models/customer_record_type.rb +5 -0
- data/lib/booker/models/discount.rb +5 -0
- data/lib/booker/models/dynamic_price.rb +11 -0
- data/lib/booker/models/employee.rb +10 -0
- data/lib/booker/models/feature_settings.rb +7 -0
- data/lib/booker/models/final_total.rb +5 -0
- data/lib/booker/models/gender.rb +5 -0
- data/lib/booker/models/itinerary_time_slot.rb +7 -0
- data/lib/booker/models/itinerary_time_slots_list.rb +7 -0
- data/lib/booker/models/location.rb +23 -0
- data/lib/booker/models/location_day_schedule.rb +18 -0
- data/lib/booker/models/model.rb +150 -0
- data/lib/booker/models/multi_service_availability_result.rb +7 -0
- data/lib/booker/models/notification_settings.rb +12 -0
- data/lib/booker/models/online_booking_settings.rb +23 -0
- data/lib/booker/models/original_price.rb +5 -0
- data/lib/booker/models/payment_method.rb +5 -0
- data/lib/booker/models/preferred_staff_gender.rb +5 -0
- data/lib/booker/models/price.rb +8 -0
- data/lib/booker/models/receipt_display_price.rb +5 -0
- data/lib/booker/models/room.rb +12 -0
- data/lib/booker/models/shipping_address.rb +5 -0
- data/lib/booker/models/source.rb +5 -0
- data/lib/booker/models/spa.rb +5 -0
- data/lib/booker/models/spa_employee_availability_search_item.rb +11 -0
- data/lib/booker/models/status.rb +5 -0
- data/lib/booker/models/sub_category.rb +5 -0
- data/lib/booker/models/tag_price.rb +5 -0
- data/lib/booker/models/teacher.rb +5 -0
- data/lib/booker/models/teacher_2.rb +5 -0
- data/lib/booker/models/time_zone.rb +8 -0
- data/lib/booker/models/treatment.rb +19 -0
- data/lib/booker/models/treatment_time_slot.rb +5 -0
- data/lib/booker/models/type.rb +8 -0
- data/lib/booker/models/user.rb +73 -0
- data/lib/booker/version.rb +3 -0
- data/lib/booker_api.rb +97 -0
- metadata +216 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class ClassInstance < Model
|
4
|
+
attr_accessor 'ID',
|
5
|
+
'EndDateTime',
|
6
|
+
'HasClassFilled',
|
7
|
+
'IsForMembersOnly',
|
8
|
+
'IsRecurring',
|
9
|
+
'IsSpecialEvent',
|
10
|
+
'IsWorkshop',
|
11
|
+
'NumReserved',
|
12
|
+
'Price',
|
13
|
+
'RoomName',
|
14
|
+
'StartDateTime',
|
15
|
+
'Teacher',
|
16
|
+
'Teacher2',
|
17
|
+
'TotalCapacity',
|
18
|
+
'TreatmentDuration',
|
19
|
+
'ListAsSubstitute',
|
20
|
+
'IsEnrollable',
|
21
|
+
'SeriesID',
|
22
|
+
'Treatment'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class Country < Type
|
4
|
+
# ISO 3166-1 alpha-2 Codes
|
5
|
+
IDS_TO_ISO_CODES = YAML::load_file(File.join(__dir__, '..', 'config', 'booker_country_ids_to_iso_codes.yml')).freeze
|
6
|
+
|
7
|
+
def country_code; IDS_TO_ISO_CODES[self.ID]; end
|
8
|
+
|
9
|
+
def self.from_country_code(code)
|
10
|
+
country = self.new ID: IDS_TO_ISO_CODES.detect{|k, v| v == code}.try(:[], 0)
|
11
|
+
if country.ID.nil?
|
12
|
+
raise ArgumentError, 'Country code not recognized'
|
13
|
+
else
|
14
|
+
country
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class Customer < Model
|
4
|
+
attr_accessor 'CustomerID',
|
5
|
+
'GUID',
|
6
|
+
'Address',
|
7
|
+
'AllowReceiveEmails',
|
8
|
+
'AllowReceiveSMS',
|
9
|
+
'SendEmail',
|
10
|
+
'CellPhone', # Returned when using FindCustomers
|
11
|
+
'CreditCard',
|
12
|
+
'Email',
|
13
|
+
'FirstName',
|
14
|
+
'HasActiveMembership',
|
15
|
+
'HomePhone',
|
16
|
+
'MobilePhone', # Set this when making an appointment
|
17
|
+
'MobilePhoneCarrierID',
|
18
|
+
'LastName',
|
19
|
+
'WorkPhone',
|
20
|
+
'WorkPhoneExt',
|
21
|
+
'ShippingAddress',
|
22
|
+
'CustomerRecordType',
|
23
|
+
'DateOfBirth',
|
24
|
+
'Gender',
|
25
|
+
'GenderID',
|
26
|
+
'HasMembership',
|
27
|
+
'HasPastMembership',
|
28
|
+
'IsNewCustomer',
|
29
|
+
'LoyaltyPoints',
|
30
|
+
'LocationID',
|
31
|
+
'LocationName',
|
32
|
+
'NumberOfReferrals',
|
33
|
+
'PreferredStaffGender',
|
34
|
+
'EmergencyContactName',
|
35
|
+
'EmergencyContactPhone',
|
36
|
+
'EmergencyContactRelationship',
|
37
|
+
'IsActive',
|
38
|
+
'Occupation',
|
39
|
+
'PreferredStaffMemberID',
|
40
|
+
'ReferredByCustomerID'
|
41
|
+
|
42
|
+
def self.from_list(array)
|
43
|
+
if array.any? && array.first['Customer']
|
44
|
+
flattened = array.map{|a| a['Customer'].merge('CustomerID' => a['CustomerID'])}
|
45
|
+
super(flattened)
|
46
|
+
else
|
47
|
+
super
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class Location < Model
|
4
|
+
attr_accessor 'ID',
|
5
|
+
'AccountName',
|
6
|
+
'BusinessName',
|
7
|
+
'BusinessType',
|
8
|
+
'EmailAddress',
|
9
|
+
'Address',
|
10
|
+
'Phone',
|
11
|
+
'TimeZone',
|
12
|
+
'WebSite',
|
13
|
+
'IsDistributionPartner',
|
14
|
+
'EncryptedLocationID',
|
15
|
+
'BrandAccountName',
|
16
|
+
'LogoUrl',
|
17
|
+
'BusinessType',
|
18
|
+
'FirstName',
|
19
|
+
'LastName',
|
20
|
+
'CurrencyCode'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class LocationDaySchedule < Model
|
4
|
+
attr_accessor 'Weekday',
|
5
|
+
'StartTime',
|
6
|
+
'EndTime'
|
7
|
+
|
8
|
+
def self.from_hash(hash)
|
9
|
+
model = super
|
10
|
+
model.Weekday = to_wday(model.Weekday)
|
11
|
+
strftime_format = '%T'
|
12
|
+
model.StartTime = model.StartTime.try(:strftime, strftime_format)
|
13
|
+
model.EndTime = model.EndTime.try(:strftime, strftime_format)
|
14
|
+
model
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class Model
|
4
|
+
def initialize(options = {})
|
5
|
+
options.each { |key, value| send(:"#{key}=", value) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def to_hash
|
9
|
+
hash = {}
|
10
|
+
self.instance_variables.each do |var|
|
11
|
+
value = self.instance_variable_get var
|
12
|
+
if value.is_a? Array
|
13
|
+
new_value = hash_list(value)
|
14
|
+
elsif value.is_a? Booker::Models::Model
|
15
|
+
new_value = value.to_hash
|
16
|
+
elsif value.is_a? Time
|
17
|
+
new_value = self.class.time_to_booker_datetime(value)
|
18
|
+
elsif value.is_a? Date
|
19
|
+
time = value.in_time_zone
|
20
|
+
new_value = self.class.time_to_booker_datetime(time)
|
21
|
+
else
|
22
|
+
new_value = value
|
23
|
+
end
|
24
|
+
hash[var[1..-1]] = new_value
|
25
|
+
end
|
26
|
+
hash
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_json; Oj.dump(to_hash, mode: :compat); end
|
30
|
+
|
31
|
+
def self.from_hash(hash)
|
32
|
+
model = self.new
|
33
|
+
hash.each do |key, value|
|
34
|
+
if model.respond_to?(:"#{key}")
|
35
|
+
constantized = self.constantize(key)
|
36
|
+
if constantized
|
37
|
+
if value.is_a?(Array) && value.first.is_a?(Hash)
|
38
|
+
model.send(:"#{key}=", constantized.from_list(value))
|
39
|
+
next
|
40
|
+
elsif value.is_a? Hash
|
41
|
+
model.send(:"#{key}=", constantized.from_hash(value))
|
42
|
+
next
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
if value.is_a?(String) && value.start_with?('/Date(')
|
47
|
+
model.send(:"#{key}=", time_from_booker_datetime(value))
|
48
|
+
elsif !value.nil?
|
49
|
+
model.send(:"#{key}=", value)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
model
|
54
|
+
end
|
55
|
+
|
56
|
+
def self.from_list(array); array.map { |item| self.from_hash(item) }; end
|
57
|
+
|
58
|
+
def self.constantize(key)
|
59
|
+
begin
|
60
|
+
Booker::Models.const_get("Booker::Models::#{key.singularize}")
|
61
|
+
rescue NameError
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Booker's API hands you back all time as if the business is in server time.
|
67
|
+
# First load the time in server time, then return the same hours and minutes in current time zone.
|
68
|
+
# Booker will hopefully fix this in a future API version. Sorry.
|
69
|
+
def self.time_from_booker_datetime(booker_datetime)
|
70
|
+
timestamp = booker_datetime[/\/Date\((.\d+)[\-\+]/, 1].to_i / 1000.to_f
|
71
|
+
|
72
|
+
original_tz = Time.zone
|
73
|
+
begin
|
74
|
+
# Booker's server is always EST
|
75
|
+
Time.zone = Booker::Client::TimeZone
|
76
|
+
|
77
|
+
booker_time = Time.zone.at(timestamp)
|
78
|
+
ensure
|
79
|
+
Time.zone = original_tz
|
80
|
+
end
|
81
|
+
|
82
|
+
# Convert it back to location time without changing hours and minutes
|
83
|
+
Time.zone.parse(booker_time.strftime('%Y-%m-%d %H:%M:%S'))
|
84
|
+
end
|
85
|
+
|
86
|
+
# Booker's API requires times to be sent in as if the business is in Eastern Time!
|
87
|
+
def self.time_to_booker_datetime(time)
|
88
|
+
original_tz = Time.zone
|
89
|
+
|
90
|
+
begin
|
91
|
+
# Booker's server is always EST
|
92
|
+
Time.zone = Booker::Client::TimeZone
|
93
|
+
timestamp = (Time.zone.parse(time.strftime("%Y-%m-%dT%H:%M:%S")).to_f * 1000).to_i
|
94
|
+
ensure
|
95
|
+
Time.zone = original_tz
|
96
|
+
end
|
97
|
+
|
98
|
+
"/Date(#{timestamp})/"
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.timezone_from_booker_timezone(booker_timezone_name)
|
102
|
+
normalized_booker_timezone_name = Booker::Helpers::ActiveSupport.to_active_support(booker_timezone_name)
|
103
|
+
return normalized_booker_timezone_name if normalized_booker_timezone_name.present?
|
104
|
+
|
105
|
+
begin
|
106
|
+
Booker::Helpers::LoggingHelper.log_issue(
|
107
|
+
'Unable to find time zone name using Booker::Helpers::ActiveSupport.to_active_support',
|
108
|
+
booker_timezone_name: booker_timezone_name
|
109
|
+
)
|
110
|
+
rescue
|
111
|
+
end
|
112
|
+
|
113
|
+
timezone_from_booker_offset!(booker_timezone_name)
|
114
|
+
end
|
115
|
+
|
116
|
+
def self.timezone_from_booker_offset!(booker_timezone_name)
|
117
|
+
booker_offset_match = booker_timezone_name.scan(/(\A)(.*)(?=\))/).first
|
118
|
+
|
119
|
+
if booker_offset_match.present?
|
120
|
+
booker_offset = booker_offset_match.delete_if { |match| match.blank? }.first
|
121
|
+
|
122
|
+
if booker_offset
|
123
|
+
booker_timezone_map_key = Booker::Helpers::ActiveSupport.booker_timezone_names.find do |key|
|
124
|
+
key.start_with?(booker_offset)
|
125
|
+
end
|
126
|
+
|
127
|
+
return Booker::Helpers::ActiveSupport.to_active_support(booker_timezone_map_key) if booker_timezone_map_key
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
raise Booker::Error
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.to_wday(booker_wday); Date.parse(booker_wday).wday; end
|
135
|
+
|
136
|
+
private
|
137
|
+
def hash_list(array)
|
138
|
+
array.map do |item|
|
139
|
+
if item.is_a? Array
|
140
|
+
hash_list(item)
|
141
|
+
elsif item.is_a? Booker::Models::Model
|
142
|
+
item.to_hash
|
143
|
+
else
|
144
|
+
item
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class NotificationSettings < Model
|
4
|
+
attr_accessor 'NotificationsEmail',
|
5
|
+
'NotifyAdminOnAppointmentOtherEvent',
|
6
|
+
'SendAppointmentReminders',
|
7
|
+
'SendConfirmationAfterBooking',
|
8
|
+
'SendNoticeHoursBeforeAppointment',
|
9
|
+
'NotifyServiceProviderOnAppointmentOtherEvent'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Booker
|
2
|
+
module Models
|
3
|
+
class OnlineBookingSettings < Model
|
4
|
+
attr_accessor 'NewAppointmentCutOffInterval',
|
5
|
+
'PaymentMethods',
|
6
|
+
'PreAuthorizeCreditCard',
|
7
|
+
'PreferredAppointmentTimes',
|
8
|
+
'RequirePaymentInformation',
|
9
|
+
'AppointmentStartTimeInterval',
|
10
|
+
'SelectTechnician',
|
11
|
+
'SelectTechnicianGender',
|
12
|
+
'AllowCancelAppointments',
|
13
|
+
'MaximumResultsPerDay',
|
14
|
+
'UsePreferredAppointmentTimes',
|
15
|
+
'RequireCustomerGender',
|
16
|
+
'RequireStreetAddress',
|
17
|
+
'GoogleAnalyticsCode',
|
18
|
+
'BillingAddressRequired',
|
19
|
+
'BackgroundImageUrl',
|
20
|
+
'BusinessLogoUrl'
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|