yardi 4.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +29 -0
- data/.gitignore +5 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/CODEOWNERS +1 -0
- data/CODE_OF_CONDUCT.md +13 -0
- data/Gemfile +4 -0
- data/README.md +212 -0
- data/Rakefile +7 -0
- data/bin/console +15 -0
- data/config/multi_xml.rb +4 -0
- data/docs/contributing.md +24 -0
- data/docs/getting_started.md +14 -0
- data/lib/yardi.rb +54 -0
- data/lib/yardi/document_parser.rb +6 -0
- data/lib/yardi/document_parser/base.rb +85 -0
- data/lib/yardi/document_parser/guest_card_import_response_object.rb +72 -0
- data/lib/yardi/document_parser/prospects.rb +79 -0
- data/lib/yardi/document_parser/residents.rb +59 -0
- data/lib/yardi/error/base.rb +7 -0
- data/lib/yardi/error/connection_error.rb +12 -0
- data/lib/yardi/error/empty_response.rb +11 -0
- data/lib/yardi/error/error_response.rb +11 -0
- data/lib/yardi/error/fault_response.rb +14 -0
- data/lib/yardi/error/guests_not_found.rb +10 -0
- data/lib/yardi/error/invalid_configuration.rb +11 -0
- data/lib/yardi/error/missing_property.rb +10 -0
- data/lib/yardi/error/no_results.rb +10 -0
- data/lib/yardi/error/resource_not_found.rb +9 -0
- data/lib/yardi/error/service_unavailable.rb +11 -0
- data/lib/yardi/error/unparsable_response.rb +11 -0
- data/lib/yardi/model/event.rb +18 -0
- data/lib/yardi/model/guest_card_response.rb +12 -0
- data/lib/yardi/model/prospect.rb +49 -0
- data/lib/yardi/model/resident.rb +36 -0
- data/lib/yardi/parameter/agent.rb +13 -0
- data/lib/yardi/parameter/contact_info.rb +13 -0
- data/lib/yardi/parameter/credential.rb +16 -0
- data/lib/yardi/parameter/property.rb +35 -0
- data/lib/yardi/parameter/prospect.rb +25 -0
- data/lib/yardi/parameter/user.rb +64 -0
- data/lib/yardi/request/base.rb +99 -0
- data/lib/yardi/request/get_residents.rb +39 -0
- data/lib/yardi/request/get_yardi_guest_activity.rb +85 -0
- data/lib/yardi/request/import_yardi_guest.rb +73 -0
- data/lib/yardi/request_section.rb +24 -0
- data/lib/yardi/request_section/authentication.rb +24 -0
- data/lib/yardi/request_section/lead_management.rb +148 -0
- data/lib/yardi/request_section/prospect.rb +27 -0
- data/lib/yardi/request_section/residents.rb +18 -0
- data/lib/yardi/utils.rb +6 -0
- data/lib/yardi/utils/configuration_validator.rb +17 -0
- data/lib/yardi/utils/phone_parser.rb +23 -0
- data/lib/yardi/utils/request_fetcher.rb +47 -0
- data/lib/yardi/utils/request_generator.rb +88 -0
- data/lib/yardi/validator.rb +6 -0
- data/lib/yardi/validator/empty_response.rb +43 -0
- data/lib/yardi/validator/error_response.rb +87 -0
- data/lib/yardi/validator/fault_response.rb +40 -0
- data/lib/yardi/validator/missing_property.rb +60 -0
- data/lib/yardi/version.rb +5 -0
- data/yardi.gemspec +31 -0
- metadata +246 -0
@@ -0,0 +1,13 @@
|
|
1
|
+
module Yardi
|
2
|
+
module Parameter
|
3
|
+
# Wrapper for a Yardi agent. An agent is required for guest card insertions.
|
4
|
+
class Agent
|
5
|
+
attr_reader :first_name, :last_name
|
6
|
+
|
7
|
+
def initialize(first_name:, last_name:)
|
8
|
+
@first_name = first_name
|
9
|
+
@last_name = last_name
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module Yardi
|
2
|
+
module Parameter
|
3
|
+
class ContactInfo
|
4
|
+
attr_reader :email, :cell_phone, :home_phone
|
5
|
+
|
6
|
+
def initialize(email:, cell_phone: nil, home_phone: nil)
|
7
|
+
@email = email
|
8
|
+
@cell_phone= cell_phone
|
9
|
+
@home_phone = home_phone
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Yardi
|
2
|
+
module Parameter
|
3
|
+
# Contains PMC-specific data for interfacing with Yardi's API.
|
4
|
+
class Credential
|
5
|
+
attr_reader :database, :password, :server, :username, :web_service_url
|
6
|
+
|
7
|
+
def initialize(database:, password:, server:, username:, web_service_url:)
|
8
|
+
@database = database
|
9
|
+
@password = password
|
10
|
+
@server = server
|
11
|
+
@username = username
|
12
|
+
@web_service_url = web_service_url
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Yardi
|
2
|
+
module Parameter
|
3
|
+
class Property
|
4
|
+
attr_reader :first_contact_time,
|
5
|
+
:remote_id,
|
6
|
+
:tour_notes,
|
7
|
+
:tour_remote_id,
|
8
|
+
:tour_time
|
9
|
+
|
10
|
+
# @param first_contact_time [Time] The time of the renter's first contact
|
11
|
+
# with the property, in the property's time zone
|
12
|
+
# @param remote_id [String] The ID associated with the property in Yardi's
|
13
|
+
# system. From Apartment List, property.remote_property_id
|
14
|
+
# @param tour_notes [String] The notes to be inserted to the Comments
|
15
|
+
# node in the Appointment event.
|
16
|
+
# @param tour_remote_id [String] The ID in Yardi's system of the renter's
|
17
|
+
# tour, or nil if there is no tour for the renter at the property.
|
18
|
+
# @param tour_time [Time] The time of the renter's tour, or nil if there
|
19
|
+
# is no tour for the renter at the property.
|
20
|
+
def initialize(
|
21
|
+
remote_id:,
|
22
|
+
first_contact_time:,
|
23
|
+
tour_notes: nil,
|
24
|
+
tour_remote_id: nil,
|
25
|
+
tour_time: nil
|
26
|
+
)
|
27
|
+
@first_contact_time = first_contact_time
|
28
|
+
@remote_id = remote_id
|
29
|
+
@tour_notes = tour_notes
|
30
|
+
@tour_remote_id = tour_remote_id
|
31
|
+
@tour_time = tour_time
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# This is the parameter used for prospect search in GetYardiGuestActivity. It
|
2
|
+
# should not be confused with the Parameter::User that is used for guestcard
|
3
|
+
# insertion in ImportYardiGuest.
|
4
|
+
|
5
|
+
module Yardi
|
6
|
+
module Parameter
|
7
|
+
class Prospect
|
8
|
+
attr_reader :first_name, :last_name, :email, :phone, :yardi_prospect_id
|
9
|
+
|
10
|
+
def initialize(
|
11
|
+
first_name: nil,
|
12
|
+
last_name: nil,
|
13
|
+
email: nil,
|
14
|
+
phone: nil,
|
15
|
+
yardi_prospect_id: nil
|
16
|
+
)
|
17
|
+
@first_name = first_name
|
18
|
+
@last_name = last_name
|
19
|
+
@email = email
|
20
|
+
@phone = phone
|
21
|
+
@yardi_prospect_id = yardi_prospect_id
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Yardi
|
2
|
+
module Parameter
|
3
|
+
# Wraps the user-related data that we pass to Yardi.
|
4
|
+
class User
|
5
|
+
attr_reader :beds,
|
6
|
+
:contact_info,
|
7
|
+
:first_name,
|
8
|
+
:id,
|
9
|
+
:last_name,
|
10
|
+
:message,
|
11
|
+
:move_in_date,
|
12
|
+
:preference_notes,
|
13
|
+
:preferred_floorplan_id,
|
14
|
+
:preferred_unit_id,
|
15
|
+
:price
|
16
|
+
|
17
|
+
# @param beds [Integer] Number of beds the renter is interested in
|
18
|
+
# @param contact_info [Parameter::ContactInfo] Renter contact information
|
19
|
+
# @param first_name [String] Renter first name
|
20
|
+
# @param id [Integer] ID of the renter in Apartment List database, user.id
|
21
|
+
# @param last_name [String] Renter last name
|
22
|
+
# @param message [String] Message from the renter to the property
|
23
|
+
# @param move_in_date [Date] Renter move in date
|
24
|
+
# @param preference_notes [String] Notes to be inserted into Comments node
|
25
|
+
# of CustomerPreferences section of guest card
|
26
|
+
# @param preferred_floorplan_id [String] ID of Yardi floorplan the renter
|
27
|
+
# is interested in. MUST be a real floorplan on the property in Yardi's
|
28
|
+
# system. The list of floorplans can be seen with a
|
29
|
+
# UnitAvailability_Login request.
|
30
|
+
# @param preferred_unit_id [String] ID of Yardi unit the renter is
|
31
|
+
# interested in. If specified, it MUST be a real unit on the property in
|
32
|
+
# Yardi's system. The list of units can be seen with a
|
33
|
+
# UnitAvailability_Login request. This param is only required when
|
34
|
+
# inserting a tour.
|
35
|
+
# @param price [Integer] Renter budget
|
36
|
+
def initialize(
|
37
|
+
beds:,
|
38
|
+
contact_info:,
|
39
|
+
first_name:,
|
40
|
+
id:,
|
41
|
+
last_name:,
|
42
|
+
message:,
|
43
|
+
move_in_date:,
|
44
|
+
preference_notes:,
|
45
|
+
preferred_floorplan_id:,
|
46
|
+
preferred_unit_id: nil,
|
47
|
+
price:
|
48
|
+
)
|
49
|
+
|
50
|
+
@beds = beds.nil? || beds == '' ? nil : beds.to_i
|
51
|
+
@contact_info = contact_info
|
52
|
+
@first_name = first_name
|
53
|
+
@id = id
|
54
|
+
@last_name = last_name
|
55
|
+
@message = message
|
56
|
+
@move_in_date = move_in_date
|
57
|
+
@preference_notes = preference_notes
|
58
|
+
@preferred_floorplan_id = preferred_floorplan_id
|
59
|
+
@preferred_unit_id = preferred_unit_id
|
60
|
+
@price = price.to_i > 0 ? price.to_i : nil
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require 'yardi/request_section/authentication'
|
2
|
+
require 'yardi/utils/request_fetcher'
|
3
|
+
require 'yardi/utils/request_generator'
|
4
|
+
|
5
|
+
module Yardi
|
6
|
+
module Request
|
7
|
+
# Base class for actions that fetch and parse specific Yardi SOAP actions.
|
8
|
+
#
|
9
|
+
# When subclassing this base class, you may override the following methods
|
10
|
+
# to extend functionality:
|
11
|
+
#
|
12
|
+
# * after_initialize(params) - a hook into the initializer to give
|
13
|
+
# subclasses access to the parameters passed in. If the subclass uses
|
14
|
+
# any parameters that the base class is agnostic to, this is the place
|
15
|
+
# the set these values to instance variables
|
16
|
+
# * sections (returns Array<RequestSection>) - add additional request
|
17
|
+
# sections to the request XML document. The necessary authentication
|
18
|
+
# data will always be inserted into the XML document by this class,
|
19
|
+
# so there is no need to add it to the response of this method.
|
20
|
+
#
|
21
|
+
# Additionally, this base class provides the #soap_action method, which
|
22
|
+
# returns the CamelCased name of the SOAP action being requested. This
|
23
|
+
# method assumes that the subclass will be named the same as the action.
|
24
|
+
class Base
|
25
|
+
attr_reader :connection, :credential, :response
|
26
|
+
# @param credential [Parameter::Credential] contains the PMC-specific
|
27
|
+
# configuration information needed to make a request. We want to fail
|
28
|
+
# noisily if it's missing, so it is separate from the main params hash.
|
29
|
+
# @param params [Hash<Symbol, Object>] the parameters needed to build the
|
30
|
+
# XML request.
|
31
|
+
def initialize(credential:, params:)
|
32
|
+
@credential = credential
|
33
|
+
@connection = params[:connection] || Faraday.new
|
34
|
+
after_initialize(params)
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [Yardi::Model|Array<Yardi::Model>] the parsed response
|
38
|
+
# from Yardi
|
39
|
+
# @raise [Yardi::Error] if an error was encountered when validating
|
40
|
+
# the response
|
41
|
+
def perform
|
42
|
+
@response = xml
|
43
|
+
parser.parse(@response)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [String] the XMl response from Yardi
|
47
|
+
def xml
|
48
|
+
Utils::RequestFetcher.new(
|
49
|
+
connection: connection,
|
50
|
+
endpoint: credential.web_service_url,
|
51
|
+
generator: generator
|
52
|
+
).fetch
|
53
|
+
end
|
54
|
+
|
55
|
+
# This makes it easy for us to see what XML we're about to send when
|
56
|
+
# debugging requests
|
57
|
+
def generator
|
58
|
+
Utils::RequestGenerator.new(soap_action, sections, interface)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# A hook into the initializer to give subclasses access to the parameters
|
64
|
+
# passed in. If the subclass uses any parameters that the base class is
|
65
|
+
# agnostic to, this is the place the set these values to instance
|
66
|
+
# variables
|
67
|
+
def after_initialize(_params)
|
68
|
+
# No-op, this method is a call back for subclasses
|
69
|
+
end
|
70
|
+
|
71
|
+
# A hook to add additional request sections to the request XML document.
|
72
|
+
# The 'auth' section will always be inserted into the XML document by this
|
73
|
+
# class, so there is no need to add it to the response of this method.
|
74
|
+
def sections
|
75
|
+
{ soap_body: soap_body_sections, xml_doc: xml_doc_sections }
|
76
|
+
end
|
77
|
+
|
78
|
+
def soap_body_sections
|
79
|
+
[]
|
80
|
+
end
|
81
|
+
|
82
|
+
def xml_doc_sections
|
83
|
+
[]
|
84
|
+
end
|
85
|
+
|
86
|
+
# Each request must specify its associated SOAP action.
|
87
|
+
def soap_action
|
88
|
+
raise NotImplementedError
|
89
|
+
end
|
90
|
+
|
91
|
+
# Each request must specify its associated interface.
|
92
|
+
def interface
|
93
|
+
raise NotImplementedError
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
private_constant :Base
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'yardi/document_parser/residents'
|
2
|
+
require 'yardi/request_section/residents'
|
3
|
+
|
4
|
+
require_relative 'base'
|
5
|
+
|
6
|
+
module Yardi
|
7
|
+
module Request
|
8
|
+
# Get all residents for a given property ID.
|
9
|
+
class GetResidents < Base
|
10
|
+
private
|
11
|
+
|
12
|
+
attr_reader :property_id
|
13
|
+
|
14
|
+
def after_initialize(params)
|
15
|
+
@property_id = params[:property_id]
|
16
|
+
raise ArgumentError, ':property_id is required' unless property_id
|
17
|
+
end
|
18
|
+
|
19
|
+
def parser
|
20
|
+
DocumentParser::Residents.new(property_id)
|
21
|
+
end
|
22
|
+
|
23
|
+
def soap_body_sections
|
24
|
+
[
|
25
|
+
RequestSection::Authentication.new(credential),
|
26
|
+
RequestSection::Residents.new(property_id: property_id)
|
27
|
+
]
|
28
|
+
end
|
29
|
+
|
30
|
+
def soap_action
|
31
|
+
'GetResidents'
|
32
|
+
end
|
33
|
+
|
34
|
+
def interface
|
35
|
+
'ItfResidentData'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'yardi/document_parser/prospects'
|
2
|
+
require 'yardi/request_section/prospect'
|
3
|
+
|
4
|
+
require_relative 'base'
|
5
|
+
|
6
|
+
module Yardi
|
7
|
+
module Request
|
8
|
+
##
|
9
|
+
# Search Yardi for Prospects
|
10
|
+
#
|
11
|
+
# prospect = Yardi::Parameter::Prospect.new(...)
|
12
|
+
#
|
13
|
+
# request = Yardi::Request::GetYardiGuestActivity.new(
|
14
|
+
# credential: credential,
|
15
|
+
# params: { property_id: 'p263656', prospect: prospect }
|
16
|
+
# )
|
17
|
+
#
|
18
|
+
# request.perform
|
19
|
+
# # => [#<Yardi::Model::Prospect>, #<Yardi::Model::Prospect>]
|
20
|
+
#
|
21
|
+
class GetYardiGuestActivity < Base
|
22
|
+
##
|
23
|
+
# :method: initialize
|
24
|
+
#
|
25
|
+
# Create a new instance of GetYardiGuestActivity
|
26
|
+
#
|
27
|
+
# Required parameters:
|
28
|
+
# * +property_id+ String
|
29
|
+
# * +prospect+ Yardi::Parameter::Prospect
|
30
|
+
#
|
31
|
+
# request = Yardi::Request::GetYardiGuestActivity.new(
|
32
|
+
# credential: credential,
|
33
|
+
# params: { property_id: 'p263656', prospect: prospect }
|
34
|
+
# )
|
35
|
+
|
36
|
+
##
|
37
|
+
# :method: perform
|
38
|
+
#
|
39
|
+
# Use #perform to send the actual HTTP request
|
40
|
+
#
|
41
|
+
# request.perform
|
42
|
+
# # => [#<Yardi::Model::Prospect>, #<Yardi::Model::Prospect>]
|
43
|
+
#
|
44
|
+
# Returns an Array of Yardi::Model::Prospect records
|
45
|
+
#
|
46
|
+
|
47
|
+
##
|
48
|
+
# Private methods
|
49
|
+
private
|
50
|
+
|
51
|
+
attr_reader :property_id, :prospect
|
52
|
+
|
53
|
+
def after_initialize(params)
|
54
|
+
@property_id = params[:property_id]
|
55
|
+
@prospect = params[:prospect]
|
56
|
+
|
57
|
+
unless property_id && prospect
|
58
|
+
raise ArgumentError, ':property_id and :prospect are required'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def parser
|
63
|
+
DocumentParser::Prospects.new
|
64
|
+
end
|
65
|
+
|
66
|
+
def soap_body_sections
|
67
|
+
[
|
68
|
+
RequestSection::Authentication.new(credential),
|
69
|
+
RequestSection::Prospect.new(
|
70
|
+
property_id: property_id,
|
71
|
+
prospect: prospect
|
72
|
+
)
|
73
|
+
]
|
74
|
+
end
|
75
|
+
|
76
|
+
def soap_action
|
77
|
+
'GetYardiGuestActivity_Search'
|
78
|
+
end
|
79
|
+
|
80
|
+
def interface
|
81
|
+
'ItfILSGuestCard'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'yardi/document_parser/guest_card_import_response_object'
|
2
|
+
require 'yardi/request_section/lead_management'
|
3
|
+
|
4
|
+
require_relative 'base'
|
5
|
+
|
6
|
+
module Yardi
|
7
|
+
module Request
|
8
|
+
# Post a GuestCard to Yardi's system
|
9
|
+
#
|
10
|
+
# Required initializer parameters:
|
11
|
+
#
|
12
|
+
# @param agent [Parameter::Agent] The agent to associate with the guestcard.
|
13
|
+
# @param credential [Parameter::Credential] PMC-specific information needed
|
14
|
+
# to post to Yardi
|
15
|
+
# @param lead_source [String] The originating source of the lead
|
16
|
+
# @param property [Parameter::Property] The property that the renter is
|
17
|
+
# expressing interest in
|
18
|
+
# @param reason [String] The reason the renter contacted the property, e.g.
|
19
|
+
# 'tour', 'price inquiry', etc.
|
20
|
+
# @param user [Parameter::User] The renter
|
21
|
+
class ImportYardiGuest < Base
|
22
|
+
private
|
23
|
+
|
24
|
+
attr_reader :agent, :lead_source, :property, :reason, :user
|
25
|
+
|
26
|
+
def after_initialize(params)
|
27
|
+
@agent = params[:agent]
|
28
|
+
@lead_source = params[:lead_source]
|
29
|
+
@property = params[:property]
|
30
|
+
@reason = params[:reason]
|
31
|
+
@user = params[:user]
|
32
|
+
|
33
|
+
check_required_params
|
34
|
+
end
|
35
|
+
|
36
|
+
def check_required_params
|
37
|
+
unless agent && lead_source && property && reason && user
|
38
|
+
message =
|
39
|
+
':agent, :lead_source, :property, :reason, :user are all required'
|
40
|
+
raise ArgumentError, message
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def parser
|
45
|
+
DocumentParser::GuestCardImportResponseObject.new
|
46
|
+
end
|
47
|
+
|
48
|
+
def soap_body_sections
|
49
|
+
[RequestSection::Authentication.new(credential)]
|
50
|
+
end
|
51
|
+
|
52
|
+
def xml_doc_sections
|
53
|
+
[
|
54
|
+
RequestSection::LeadManagement.new(
|
55
|
+
agent: agent,
|
56
|
+
lead_source: lead_source,
|
57
|
+
property: property,
|
58
|
+
reason: reason,
|
59
|
+
user: user
|
60
|
+
)
|
61
|
+
]
|
62
|
+
end
|
63
|
+
|
64
|
+
def soap_action
|
65
|
+
'ImportYardiGuest_Login'
|
66
|
+
end
|
67
|
+
|
68
|
+
def interface
|
69
|
+
'ItfILSGuestCard'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|