yardi 4.0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/.circleci/config.yml +29 -0
  3. data/.gitignore +5 -0
  4. data/.rspec +3 -0
  5. data/.rubocop.yml +20 -0
  6. data/CODEOWNERS +1 -0
  7. data/CODE_OF_CONDUCT.md +13 -0
  8. data/Gemfile +4 -0
  9. data/README.md +212 -0
  10. data/Rakefile +7 -0
  11. data/bin/console +15 -0
  12. data/config/multi_xml.rb +4 -0
  13. data/docs/contributing.md +24 -0
  14. data/docs/getting_started.md +14 -0
  15. data/lib/yardi.rb +54 -0
  16. data/lib/yardi/document_parser.rb +6 -0
  17. data/lib/yardi/document_parser/base.rb +85 -0
  18. data/lib/yardi/document_parser/guest_card_import_response_object.rb +72 -0
  19. data/lib/yardi/document_parser/prospects.rb +79 -0
  20. data/lib/yardi/document_parser/residents.rb +59 -0
  21. data/lib/yardi/error/base.rb +7 -0
  22. data/lib/yardi/error/connection_error.rb +12 -0
  23. data/lib/yardi/error/empty_response.rb +11 -0
  24. data/lib/yardi/error/error_response.rb +11 -0
  25. data/lib/yardi/error/fault_response.rb +14 -0
  26. data/lib/yardi/error/guests_not_found.rb +10 -0
  27. data/lib/yardi/error/invalid_configuration.rb +11 -0
  28. data/lib/yardi/error/missing_property.rb +10 -0
  29. data/lib/yardi/error/no_results.rb +10 -0
  30. data/lib/yardi/error/resource_not_found.rb +9 -0
  31. data/lib/yardi/error/service_unavailable.rb +11 -0
  32. data/lib/yardi/error/unparsable_response.rb +11 -0
  33. data/lib/yardi/model/event.rb +18 -0
  34. data/lib/yardi/model/guest_card_response.rb +12 -0
  35. data/lib/yardi/model/prospect.rb +49 -0
  36. data/lib/yardi/model/resident.rb +36 -0
  37. data/lib/yardi/parameter/agent.rb +13 -0
  38. data/lib/yardi/parameter/contact_info.rb +13 -0
  39. data/lib/yardi/parameter/credential.rb +16 -0
  40. data/lib/yardi/parameter/property.rb +35 -0
  41. data/lib/yardi/parameter/prospect.rb +25 -0
  42. data/lib/yardi/parameter/user.rb +64 -0
  43. data/lib/yardi/request/base.rb +99 -0
  44. data/lib/yardi/request/get_residents.rb +39 -0
  45. data/lib/yardi/request/get_yardi_guest_activity.rb +85 -0
  46. data/lib/yardi/request/import_yardi_guest.rb +73 -0
  47. data/lib/yardi/request_section.rb +24 -0
  48. data/lib/yardi/request_section/authentication.rb +24 -0
  49. data/lib/yardi/request_section/lead_management.rb +148 -0
  50. data/lib/yardi/request_section/prospect.rb +27 -0
  51. data/lib/yardi/request_section/residents.rb +18 -0
  52. data/lib/yardi/utils.rb +6 -0
  53. data/lib/yardi/utils/configuration_validator.rb +17 -0
  54. data/lib/yardi/utils/phone_parser.rb +23 -0
  55. data/lib/yardi/utils/request_fetcher.rb +47 -0
  56. data/lib/yardi/utils/request_generator.rb +88 -0
  57. data/lib/yardi/validator.rb +6 -0
  58. data/lib/yardi/validator/empty_response.rb +43 -0
  59. data/lib/yardi/validator/error_response.rb +87 -0
  60. data/lib/yardi/validator/fault_response.rb +40 -0
  61. data/lib/yardi/validator/missing_property.rb +60 -0
  62. data/lib/yardi/version.rb +5 -0
  63. data/yardi.gemspec +31 -0
  64. 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