instant_quote 1.5.2 → 1.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 37edc1ac5cee7a85fb58c677061f8b6aab1fedaf6dbc472cfb92801b25f4fdbc
4
- data.tar.gz: 35cad5c9bcbb00750364ed51abb3367163b949a7bbe444c76404d37602868a6c
3
+ metadata.gz: c46963e390b87e65c4e598adb90a56dcbc9c8d4e9757816b0734af085cd098f6
4
+ data.tar.gz: abfa20a481247b1ddc9aa3abbda4de1c858bbf3d50584ef9fe7ac904c1f6a948
5
5
  SHA512:
6
- metadata.gz: 71b11b24f38de7282f55cf63820d9b1091bb0873781070dc2af58747055305cbb3029afb433adc2401e95a812040743649f9b398bc0c6fa338923377bacda8ef
7
- data.tar.gz: 520a2a85e693db1002e333db97e0b5549ef7632de054d6992e9d36ca0cfb72ed5f9fdab7a580ea6b6ce1b24097880825bd70d433f4cc946997c2187d5502833d
6
+ metadata.gz: 3e42e95e2474ad1e4b04838ace2371e409ce811b4c129fe7cc65375536febd85816338b4b943f5c3133ad19d3643817d4a466fe636c843a1fb36e6542c2c63b5
7
+ data.tar.gz: 26257fb6401faf86be51b53dc7c0ebacd787f8ebb187cdf330e646318cea2a113dd7131871eac972f28272b9f363dfeb209a6f2f3a330a080c526ac36c1c0c38
data/.rubocop.yml CHANGED
@@ -1,3 +1,14 @@
1
+ AllCops:
2
+ TargetRubyVersion: 2.6
3
+ NewCops: enable
4
+
5
+ require:
6
+ - rubocop-rake
7
+ - rubocop-rspec
8
+
9
+ Gemspec/RequiredRubyVersion:
10
+ Enabled: false
11
+
1
12
  Layout/LineLength:
2
13
  Max: 100
3
14
 
@@ -24,3 +35,6 @@ Style/HashTransformKeys:
24
35
 
25
36
  Style/HashTransformValues:
26
37
  Enabled: true
38
+
39
+ RSpec/MultipleExpectations:
40
+ Enabled: false
data/.travis.yml CHANGED
@@ -5,3 +5,6 @@ cache: bundler
5
5
  rvm:
6
6
  - 2.6.3
7
7
  before_install: gem install bundler -v 2.0.2
8
+ script:
9
+ - bundle exec rspec --fail-fast
10
+ - bundle exec rubocop
data/Gemfile.lock CHANGED
@@ -1,47 +1,50 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- instant_quote (1.5.2)
4
+ instant_quote (1.6.1)
5
5
  activesupport
6
6
  capital_on_tap
7
7
  iwoca
8
8
  money
9
9
  optimum
10
10
  phonelib
11
+ youlend
11
12
 
12
13
  GEM
13
14
  remote: https://rubygems.org/
14
15
  specs:
15
- activesupport (6.0.3.4)
16
+ activesupport (6.1.1)
16
17
  concurrent-ruby (~> 1.0, >= 1.0.2)
17
- i18n (>= 0.7, < 2)
18
- minitest (~> 5.1)
19
- tzinfo (~> 1.1)
20
- zeitwerk (~> 2.2, >= 2.2.2)
18
+ i18n (>= 1.6, < 2)
19
+ minitest (>= 5.1)
20
+ tzinfo (~> 2.0)
21
+ zeitwerk (~> 2.3)
21
22
  addressable (2.7.0)
22
23
  public_suffix (>= 2.0.2, < 5.0)
23
- ast (2.4.0)
24
- byebug (11.1.1)
24
+ ast (2.4.2)
25
+ byebug (11.1.3)
25
26
  capital_on_tap (1.0.0)
26
27
  addressable
27
28
  faraday
28
29
  faraday_middleware
29
30
  multi_json
30
31
  rainbow
31
- coderay (1.1.2)
32
- concurrent-ruby (1.1.7)
33
- crack (0.4.3)
34
- safe_yaml (~> 1.0.0)
35
- diff-lcs (1.3)
36
- docile (1.3.2)
37
- dotenv (2.7.5)
38
- faraday (1.1.0)
32
+ coderay (1.1.3)
33
+ concurrent-ruby (1.1.8)
34
+ crack (0.4.5)
35
+ rexml
36
+ diff-lcs (1.4.4)
37
+ docile (1.3.5)
38
+ dotenv (2.7.6)
39
+ faraday (1.3.0)
40
+ faraday-net_http (~> 1.0)
39
41
  multipart-post (>= 1.2, < 3)
40
42
  ruby2_keywords
43
+ faraday-net_http (1.0.1)
41
44
  faraday_middleware (1.0.0)
42
45
  faraday (~> 1.0)
43
46
  hashdiff (1.0.1)
44
- i18n (1.8.5)
47
+ i18n (1.8.8)
45
48
  concurrent-ruby (~> 1.0)
46
49
  iwoca (1.0.0)
47
50
  addressable
@@ -49,10 +52,9 @@ GEM
49
52
  faraday_middleware
50
53
  multi_json
51
54
  rainbow
52
- jaro_winkler (1.5.4)
53
55
  method_source (1.0.0)
54
- minitest (5.14.2)
55
- money (6.13.8)
56
+ minitest (5.14.3)
57
+ money (6.14.0)
56
58
  i18n (>= 0.6.4, <= 2)
57
59
  multi_json (1.15.0)
58
60
  multipart-post (2.1.1)
@@ -62,57 +64,72 @@ GEM
62
64
  faraday_middleware
63
65
  multi_json
64
66
  rainbow
65
- parallel (1.19.1)
66
- parser (2.7.0.5)
67
- ast (~> 2.4.0)
68
- phonelib (0.6.46)
69
- pry (0.13.0)
67
+ parallel (1.20.1)
68
+ parser (3.0.0.0)
69
+ ast (~> 2.4.1)
70
+ phonelib (0.6.48)
71
+ pry (0.13.1)
70
72
  coderay (~> 1.1)
71
73
  method_source (~> 1.0)
72
74
  pry-byebug (3.9.0)
73
75
  byebug (~> 11.0)
74
76
  pry (~> 0.13.0)
75
- public_suffix (4.0.3)
77
+ public_suffix (4.0.6)
76
78
  rainbow (3.0.0)
77
79
  rake (10.5.0)
80
+ regexp_parser (2.0.3)
78
81
  rexml (3.2.4)
79
- rspec (3.9.0)
80
- rspec-core (~> 3.9.0)
81
- rspec-expectations (~> 3.9.0)
82
- rspec-mocks (~> 3.9.0)
83
- rspec-core (3.9.1)
84
- rspec-support (~> 3.9.1)
85
- rspec-expectations (3.9.1)
82
+ rspec (3.10.0)
83
+ rspec-core (~> 3.10.0)
84
+ rspec-expectations (~> 3.10.0)
85
+ rspec-mocks (~> 3.10.0)
86
+ rspec-core (3.10.1)
87
+ rspec-support (~> 3.10.0)
88
+ rspec-expectations (3.10.1)
86
89
  diff-lcs (>= 1.2.0, < 2.0)
87
- rspec-support (~> 3.9.0)
88
- rspec-mocks (3.9.1)
90
+ rspec-support (~> 3.10.0)
91
+ rspec-mocks (3.10.2)
89
92
  diff-lcs (>= 1.2.0, < 2.0)
90
- rspec-support (~> 3.9.0)
91
- rspec-support (3.9.2)
92
- rubocop (0.80.1)
93
- jaro_winkler (~> 1.5.1)
93
+ rspec-support (~> 3.10.0)
94
+ rspec-support (3.10.2)
95
+ rubocop (1.9.1)
94
96
  parallel (~> 1.10)
95
- parser (>= 2.7.0.1)
97
+ parser (>= 3.0.0.0)
96
98
  rainbow (>= 2.2.2, < 4.0)
99
+ regexp_parser (>= 1.8, < 3.0)
97
100
  rexml
101
+ rubocop-ast (>= 1.2.0, < 2.0)
98
102
  ruby-progressbar (~> 1.7)
99
- unicode-display_width (>= 1.4.0, < 1.7)
100
- ruby-progressbar (1.10.1)
101
- ruby2_keywords (0.0.2)
102
- safe_yaml (1.0.5)
103
- simplecov (0.18.5)
103
+ unicode-display_width (>= 1.4.0, < 3.0)
104
+ rubocop-ast (1.4.1)
105
+ parser (>= 2.7.1.5)
106
+ rubocop-rake (0.5.1)
107
+ rubocop
108
+ rubocop-rspec (2.2.0)
109
+ rubocop (~> 1.0)
110
+ rubocop-ast (>= 1.1.0)
111
+ ruby-progressbar (1.11.0)
112
+ ruby2_keywords (0.0.4)
113
+ simplecov (0.21.2)
104
114
  docile (~> 1.1)
105
115
  simplecov-html (~> 0.11)
106
- simplecov-html (0.12.2)
107
- thread_safe (0.3.6)
108
- tzinfo (1.2.7)
109
- thread_safe (~> 0.1)
110
- unicode-display_width (1.6.1)
111
- webmock (3.8.3)
116
+ simplecov_json_formatter (~> 0.1)
117
+ simplecov-html (0.12.3)
118
+ simplecov_json_formatter (0.1.2)
119
+ tzinfo (2.0.4)
120
+ concurrent-ruby (~> 1.0)
121
+ unicode-display_width (2.0.0)
122
+ webmock (3.11.2)
112
123
  addressable (>= 2.3.6)
113
124
  crack (>= 0.3.2)
114
125
  hashdiff (>= 0.4.0, < 2.0.0)
115
- zeitwerk (2.4.1)
126
+ youlend (1.1.3)
127
+ addressable
128
+ faraday
129
+ faraday_middleware
130
+ multi_json
131
+ rainbow
132
+ zeitwerk (2.4.2)
116
133
 
117
134
  PLATFORMS
118
135
  ruby
@@ -125,8 +142,10 @@ DEPENDENCIES
125
142
  rake (~> 10.0)
126
143
  rspec (~> 3.0)
127
144
  rubocop
145
+ rubocop-rake
146
+ rubocop-rspec
128
147
  simplecov
129
148
  webmock
130
149
 
131
150
  BUNDLED WITH
132
- 2.1.4
151
+ 2.2.5
data/bin/console CHANGED
@@ -24,6 +24,12 @@ Iwoca.configure do |config|
24
24
  config.webhook_signature = ENV['IWOCA_WEBHOOK_SIGNATURE']
25
25
  end
26
26
 
27
+ Youlend.configure do |config|
28
+ config.client_id = ENV['YOULEND_CLIENT_ID']
29
+ config.client_secret = ENV['YOULEND_CLIENT_SECRET']
30
+ config.debug = true
31
+ end
32
+
27
33
  send(:include, InstantQuote)
28
34
 
29
35
  require 'pry'
@@ -6,6 +6,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
6
  require 'instant_quote/version'
7
7
 
8
8
  Gem::Specification.new do |spec|
9
+ spec.required_ruby_version = '>= 2.5.0'
9
10
  spec.name = 'instant_quote'
10
11
  spec.version = InstantQuote::VERSION
11
12
  spec.authors = ['rikas']
@@ -32,13 +33,16 @@ Gem::Specification.new do |spec|
32
33
  spec.add_development_dependency 'rake', '~> 10.0'
33
34
  spec.add_development_dependency 'rspec', '~> 3.0'
34
35
  spec.add_development_dependency 'rubocop'
36
+ spec.add_development_dependency 'rubocop-rake'
37
+ spec.add_development_dependency 'rubocop-rspec'
35
38
  spec.add_development_dependency 'simplecov'
36
39
  spec.add_development_dependency 'webmock'
37
40
 
38
41
  spec.add_dependency 'activesupport'
39
42
  spec.add_dependency 'capital_on_tap'
40
43
  spec.add_dependency 'iwoca'
41
- spec.add_dependency 'optimum'
42
44
  spec.add_dependency 'money'
45
+ spec.add_dependency 'optimum'
43
46
  spec.add_dependency 'phonelib'
47
+ spec.add_dependency 'youlend'
44
48
  end
data/lib/instant_quote.rb CHANGED
@@ -17,6 +17,7 @@ module InstantQuote
17
17
  get_link
18
18
  get_quote
19
19
  get_status
20
+ get_preapproval
20
21
  get_approval
21
22
  additional_fields
22
23
  parse_decision
@@ -26,6 +27,7 @@ module InstantQuote
26
27
  # InstantQuote.get_link(connection)
27
28
  # InstantQuote.get_quote(connection)
28
29
  # InstantQuote.get_status(connection)
30
+ # InstantQuote.get_preapproval(connection)
29
31
  # InstantQuote.get_approval(connection)
30
32
  # InstantQuote.additional_fields(connection)
31
33
  # InstantQuote.parse_decision(connection)
@@ -2,42 +2,63 @@
2
2
 
3
3
  module InstantQuote
4
4
  class Adapter
5
+ # This is one additional field that we always want to ask, since we're using it for phone
6
+ # validating the applicant.
7
+ DEFAULT_ADDIONAL_FIELDS = [
8
+ {
9
+ name: 'applicant_phone_number',
10
+ label: 'Applicant mobile phone',
11
+ required: true
12
+ }
13
+ ].freeze
14
+
5
15
  def self.additional_fields(fields = [])
6
- return (@additional_fields || []) if fields.empty?
16
+ return (@additional_fields || DEFAULT_ADDIONAL_FIELDS) if fields.empty?
17
+
18
+ @additional_fields ||= DEFAULT_ADDIONAL_FIELDS
19
+ @additional_fields += fields
20
+ end
7
21
 
8
- @additional_fields = fields
22
+ def self.get_link(connection)
23
+ new.get_link(connection)
9
24
  end
10
25
 
11
- def self.get_link(application_id)
12
- new.get_link(application_id)
26
+ def self.get_quote(params, connection = nil)
27
+ new.get_quote(params, connection)
13
28
  end
14
29
 
15
- def self.get_quote(params)
16
- new.get_quote(params)
30
+ def self.get_status(application_id, connection = nil)
31
+ new.get_status(application_id, connection)
17
32
  end
18
33
 
19
- def self.get_status(application_id)
20
- new.get_status(application_id)
34
+ def self.get_preapproval(params, connection = nil)
35
+ new.get_preapproval(params, connection)
21
36
  end
22
37
 
23
- def self.get_approval(application_id)
24
- new.get_approval(application_id)
38
+ def self.get_approval(application_id, connection = nil)
39
+ new.get_approval(application_id, connection)
25
40
  end
26
41
 
27
- def get_link(_application_id)
42
+ def get_link(_application_id, _connection = nil)
28
43
  raise NotImplementedError, "You must implement '#{self.class.name}#get_link'"
29
44
  end
30
45
 
31
- def get_quote(_params)
46
+ def get_quote(_params, _connection = nil)
32
47
  raise NotImplementedError, "You must implement '#{self.class.name}#get_quote'"
33
48
  end
34
49
 
35
- def get_status(_application_id)
50
+ def get_status(_application_id, _connection = nil)
36
51
  raise NotImplementedError, "You must implement '#{self.class.name}#get_status'"
37
52
  end
38
53
 
39
- def get_approval(_application_id)
40
- raise NotImplementedError, "You must implement '#{self.class.name}#get_approval"
54
+ # Default for pre approval will be true, override it in the adapter class if needed.
55
+ def get_preapproval(_params, _connection = nil)
56
+ true
57
+ end
58
+
59
+ # Default for approval will be true, override it in the adapter class if needed.
60
+ def get_approval(_application_id, _connection = nil)
61
+ true
41
62
  end
42
63
  end
43
64
  end
@@ -36,17 +36,23 @@ module InstantQuote
36
36
  end
37
37
 
38
38
  def get_link
39
- adapter.get_link(connection.external_id)
39
+ adapter.get_link(connection)
40
40
  end
41
41
 
42
42
  def get_quote
43
43
  params = translator.translate(connection)
44
44
 
45
- adapter.get_quote(params)
45
+ adapter.get_quote(params, connection)
46
46
  end
47
47
 
48
48
  def get_status
49
- adapter.get_status(connection.external_id)
49
+ adapter.get_status(connection.external_id, connection)
50
+ end
51
+
52
+ def get_preapproval
53
+ params = translator.translate(connection)
54
+
55
+ adapter.get_preapproval(params, connection)
50
56
  end
51
57
 
52
58
  def get_approval
@@ -6,6 +6,7 @@ require 'instant_quote/decision_parsers/capital_on_tap'
6
6
  module InstantQuote
7
7
  module Adapters
8
8
  class CapitalOnTap < Adapter
9
+ # rubocop:disable Layout/LineLength
9
10
  additional_fields [
10
11
  {
11
12
  name: 'date_of_birth',
@@ -13,11 +14,6 @@ module InstantQuote
13
14
  type: 'date',
14
15
  required: true
15
16
  },
16
- {
17
- name: 'applicant_phone_number',
18
- label: 'Applicant mobile phone',
19
- required: true
20
- },
21
17
  {
22
18
  name: 'monthly_income',
23
19
  label: 'Applicant monthly income',
@@ -71,6 +67,7 @@ module InstantQuote
71
67
  type: 'checkbox'
72
68
  }
73
69
  ]
70
+ # rubocop:enable Layout/LineLength
74
71
 
75
72
  # Creates the quote with the information from instant connection
76
73
  def get_quote(params)
@@ -79,7 +76,7 @@ module InstantQuote
79
76
  # CoT responses can be 200 OK but still have an error!
80
77
  raise_error(response) unless response.success? && !response.error
81
78
 
82
- response.body.dig(:result)
79
+ response.body[:result]
83
80
  end
84
81
 
85
82
  # Get the status for a given application ID
@@ -106,15 +103,10 @@ module InstantQuote
106
103
  end
107
104
 
108
105
  # The link is always the same for Capital On Tap
109
- def get_link(_application_id)
106
+ def get_link(_connection)
110
107
  'https://account.capitalontap.com/apps/portal/login/'
111
108
  end
112
109
 
113
- # Capital On Tap doesn't need an approval request
114
- def get_approval(_application_id)
115
- true
116
- end
117
-
118
110
  private
119
111
 
120
112
  def raise_error(response)
@@ -44,11 +44,6 @@ module InstantQuote
44
44
  label: 'Country',
45
45
  type: 'country'
46
46
  },
47
- {
48
- name: 'applicant_phone_number',
49
- label: 'Applicant mobile number',
50
- required: true
51
- },
52
47
  {
53
48
  name: 'applicant_consent',
54
49
  label: "I agree to iwoca running soft credit checks on my personal information with \
@@ -81,7 +76,9 @@ module InstantQuote
81
76
  end
82
77
 
83
78
  # Gets the URL for the given state key.
84
- def get_link(state_key)
79
+ def get_link(connection)
80
+ state_key = connection.external_id
81
+
85
82
  response = ::Iwoca::Quote.login_link(state_key)
86
83
 
87
84
  return response.data[:login_link] if response.success?
@@ -7,11 +7,6 @@ module InstantQuote
7
7
  module Adapters
8
8
  class Optimum < Adapter
9
9
  additional_fields [
10
- {
11
- name: 'applicant_phone_number',
12
- label: 'Applicant mobile phone',
13
- required: true
14
- },
15
10
  {
16
11
  name: 'expected_turnover_next_12_months',
17
12
  label: 'Expected turnover next 12 months',
@@ -118,14 +113,10 @@ module InstantQuote
118
113
  end
119
114
 
120
115
  # Gets the URL for the given state key.
121
- def get_link(_)
116
+ def get_link(_connection)
122
117
  ''
123
118
  end
124
119
 
125
- def get_approval(_)
126
- true
127
- end
128
-
129
120
  private
130
121
 
131
122
  def decision_parser
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'youlend'
4
+ require 'instant_quote/decision_parsers/youlend'
5
+
6
+ module InstantQuote
7
+ module Adapters
8
+ class Youlend < Adapter
9
+ additional_fields [
10
+ {
11
+ name: 'monthly_card_revenue',
12
+ label: 'Avg. monthly revenue from card payments',
13
+ required: true,
14
+ type: 'money'
15
+ },
16
+ {
17
+ name: 'monthly_revenue_from_invoices',
18
+ label: 'Avg. monthly revenue from invoices',
19
+ required: true,
20
+ type: 'money'
21
+ },
22
+ {
23
+ name: 'applicant_consent',
24
+ label: 'Yes, I give my consent for my data to be shared',
25
+ required: true,
26
+ type: 'checkbox'
27
+ }
28
+ ]
29
+
30
+ # For Youlend we first need to do the pre_qualification and then, if we get a positive
31
+ # response we can create the lead. We need to call both endpoints here, so it might take a
32
+ # while.
33
+ def get_quote(params, connection)
34
+ response = ::Youlend::Lead.create(params)
35
+
36
+ if response.success?
37
+ connection.update(link: response.data[:signUpURL])
38
+
39
+ return response.data[:leadId]
40
+ end
41
+
42
+ raise_error(response)
43
+ end
44
+
45
+ def get_status(_lead_id, connection)
46
+ DecisionParsers::Youlend.new(connection.approval_decision_info)
47
+ end
48
+
49
+ # Gets the URL for the given state key.
50
+ def get_link(connection)
51
+ connection.link
52
+ end
53
+
54
+ # Checks if the application is eligible for a loan. If it is we need to update the
55
+ # connection's approval_decision_info right away, since it's the only place where we get it.
56
+ def get_preapproval(params, connection)
57
+ # The preapproval endpoint doesn't work with all params, we need to remove some
58
+ params.delete(:companyName)
59
+ params.delete(:monthsTrading)
60
+ params.delete(:keyContactName)
61
+ params.delete(:registeredAddress)
62
+ params.delete(:thirdPartyLeadId)
63
+ params.delete(:contactPhoneNumber)
64
+
65
+ response = ::Youlend::Prequalification.verify(params)
66
+
67
+ connection.update(approval_decision_info: response.data) if response.success?
68
+
69
+ response
70
+ end
71
+
72
+ private
73
+
74
+ def raise_error(response)
75
+ raise ApiError.new(
76
+ adapter: 'youlend',
77
+ data: response.body,
78
+ error: response.error || '[Youlend] API error'
79
+ )
80
+ end
81
+ end
82
+ end
83
+ end
@@ -8,6 +8,8 @@ module InstantQuote
8
8
  alias to_s error
9
9
 
10
10
  def initialize(params = {})
11
+ super
12
+
11
13
  @adapter = params[:adapter]
12
14
  @error = params[:error]
13
15
  @data = params[:data] || {}
@@ -1,6 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_support/core_ext/hash/indifferent_access'
4
+
3
5
  module InstantQuote
6
+ # Inherit from this class to create the translation from Finpoint's app to the JSON that needs to
7
+ # be sent to the service you're integrating. You'll have access to `application` and `extra_info`
8
+ # from the application's record.
4
9
  class ConnectionTranslator
5
10
  attr_accessor :application, :extra_info
6
11
 
@@ -10,7 +15,7 @@ module InstantQuote
10
15
 
11
16
  def initialize(connection)
12
17
  @application = connection.finance_application
13
- @extra_info = connection.extra_info
18
+ @extra_info = ActiveSupport::HashWithIndifferentAccess.new(connection.extra_info)
14
19
  end
15
20
 
16
21
  def translate
@@ -117,10 +117,14 @@ module InstantQuote
117
117
 
118
118
  def question_from_extra_info(name, question_type)
119
119
  field = Adapters::CapitalOnTap.additional_fields.find do |f|
120
- f[:name] == name
120
+ f[:name].to_sym == name.to_sym
121
121
  end
122
122
 
123
- { question: field[:label], answer: extra_info[field[:name]], questionType: question_type }
123
+ {
124
+ question: field[:label],
125
+ answer: extra_info[field[:name]],
126
+ questionType: question_type
127
+ }
124
128
  end
125
129
  end
126
130
  end
@@ -78,7 +78,7 @@ module InstantQuote
78
78
  application.borrower_organisation
79
79
  end
80
80
 
81
- # rubocop:disable Metrics/AbcSize
81
+ # rubocop:disable Naming/VariableNumber
82
82
  def company_address
83
83
  {
84
84
  postcode: company.address.postcode,
@@ -88,7 +88,7 @@ module InstantQuote
88
88
  country: company.address.country
89
89
  }
90
90
  end
91
- # rubocop:enable Metrics/AbcSize
91
+ # rubocop:enable Naming/VariableNumber
92
92
 
93
93
  def person_information
94
94
  {
@@ -123,6 +123,7 @@ module InstantQuote
123
123
  }
124
124
  end
125
125
 
126
+ # rubocop:disable Naming/VariableNumber
126
127
  def person_residential_address
127
128
  {
128
129
  uid: SecureRandom.uuid,
@@ -135,6 +136,7 @@ module InstantQuote
135
136
  residential_status: home_owning_translated
136
137
  }
137
138
  end
139
+ # rubocop:enable Naming/VariableNumber
138
140
 
139
141
  def home_owning_translated
140
142
  case company.directors_home_owning.name
@@ -3,6 +3,7 @@
3
3
  module InstantQuote
4
4
  module ConnectionTranslators
5
5
  class Optimum < ConnectionTranslator
6
+ # rubocop:disable Metrics/AbcSize
6
7
  def translate
7
8
  {
8
9
  companyNumber: company&.company_number.to_s,
@@ -19,6 +20,7 @@ module InstantQuote
19
20
  quotationOnly: false
20
21
  }
21
22
  end
23
+ # rubocop:enable Metrics/AbcSize
22
24
 
23
25
  private
24
26
 
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module ConnectionTranslators
5
+ CURRENCY = 'GBP'
6
+
7
+ class Youlend < ConnectionTranslator
8
+ # rubocop:disable Metrics/AbcSize
9
+ def translate
10
+ {
11
+ companyName: company.name,
12
+ companyType: company_type_translated,
13
+ monthsTrading: company.total_months_in_business,
14
+ keyContactName: primary_user_name,
15
+ thirdPartyLeadId: application.code,
16
+ financialData: financial_data,
17
+ countryISOCode: 'GBR',
18
+ loanCurrencyISOCode: CURRENCY,
19
+ thirdPartyMerchantId: company.id,
20
+ thirdPartyCustomerId: application&.primary_user&.id,
21
+ registeredAddress: company_address,
22
+ contactPhoneNumber: extra_info[:applicant_phone_number],
23
+ monthlyCardRevenue: extra_info[:monthly_card_revenue].to_i,
24
+ contactEmailAddress: application&.primary_user&.email # optional
25
+ }
26
+ end
27
+ # rubocop:enable Metrics/AbcSize
28
+
29
+ private
30
+
31
+ def financial_data
32
+ {
33
+ monthlyCardRevenue: extra_info[:monthly_card_revenue].to_i,
34
+ monthlyRevenueFromInvoices: extra_info[:monthly_revenue_from_invoices].to_i,
35
+ paymentData: payment_data
36
+ }
37
+ end
38
+
39
+ def payment_data
40
+ (1..12).to_a.map do |month|
41
+ formatted_month = format('%02<month>d', month: month)
42
+
43
+ {
44
+ paymentDate: "#{Date.today.year - 1}-#{formatted_month}-01",
45
+ amount: total_amount,
46
+ currencyISOCode: CURRENCY
47
+ }
48
+ end
49
+ end
50
+
51
+ def total_amount
52
+ @total_amount ||= begin
53
+ card = extra_info[:monthly_card_revenue].to_i || 0
54
+ invoices = extra_info[:monthly_revenue_from_invoices].to_i || 0
55
+
56
+ card + invoices
57
+ end
58
+ end
59
+
60
+ def company_address
61
+ address = company.address
62
+
63
+ {
64
+ line1: address.line_1,
65
+ line2: address.line_2,
66
+ city: address.town,
67
+ region: address.town,
68
+ areaCode: address.postcode,
69
+ country: address.country
70
+ }
71
+ end
72
+
73
+ def company
74
+ @company ||= application.borrower_organisation
75
+ end
76
+
77
+ def primary_user_name
78
+ application.primary_user.name
79
+ end
80
+
81
+ def company_type_translated
82
+ case company.company_type.name
83
+ when /LTD/
84
+ 'ltd'
85
+ when /LLP/
86
+ 'llp'
87
+ when /PLC/
88
+ 'plc'
89
+ when 'Sole Trader'
90
+ 'soleTrader'
91
+ else
92
+ 'other'
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -42,7 +42,7 @@ module InstantQuote
42
42
  end
43
43
 
44
44
  def status
45
- credit_decision.dig(:status) || STATUSES[:could_not_submit]
45
+ credit_decision[:status] || STATUSES[:could_not_submit]
46
46
  end
47
47
 
48
48
  # The applicationStage can be one of the following:
@@ -52,13 +52,13 @@ module InstantQuote
52
52
  # ApprovedCredit, ApprovedPrepaid, DdSetup, AddOnSelection, DoneHavFailed,
53
53
  # DoneIdVerificationRequired, Done, Portal
54
54
  def stage
55
- data.dig(:applicationStage) || {}
55
+ data[:applicationStage] || {}
56
56
  end
57
57
 
58
58
  private
59
59
 
60
60
  def credit_decision
61
- data.dig(:creditDecision) || {}
61
+ data[:creditDecision] || {}
62
62
  end
63
63
  end
64
64
  end
@@ -91,21 +91,21 @@ module InstantQuote
91
91
 
92
92
  return 0 unless rates&.any?
93
93
 
94
- rate_info = rates.first.dig(:rates)&.first
94
+ rate_info = rates.first[:rates]&.first
95
95
 
96
96
  return 0 unless rate_info
97
97
 
98
- rate_info.dig(:rate)
98
+ rate_info[:rate]
99
99
  end
100
100
 
101
101
  def status
102
- status_hash.dig(:status) || STATUSES[:not_defined]
102
+ status_hash[:status] || STATUSES[:not_defined]
103
103
  end
104
104
 
105
105
  private
106
106
 
107
107
  def status_hash
108
- data.dig(:approval_status) || data.dig(:latest_approval_request) || {}
108
+ data[:approval_status] || data[:latest_approval_request] || {}
109
109
  end
110
110
  end
111
111
  end
@@ -6,26 +6,29 @@ module InstantQuote
6
6
  #
7
7
  # {
8
8
  # decision: {
9
- # decision: "approved",
10
- # trialPeriodMonths: 4, # trial period length
11
- # fundingLimit: 745000,
12
- # annualRenewalFee: 0,
13
- # badDebtProtection: 0.85,
14
- # cashAvailableWithinHours: 48,
15
- # discountFee: 3.35,
16
- # facility: "Factoring", # Factoring or Invoice Discounting or Confidential Invoice Discounting
17
- # prepayment: 80,
18
- # serviceFee: 1.14
9
+ # decision: "approved",
10
+ # trialPeriodMonths: 4, # trial period length
11
+ # fundingLimit: 745000,
12
+ # annualRenewalFee: 0,
13
+ # badDebtProtection: 0.85,
14
+ # cashAvailableWithinHours: 48,
15
+ # discountFee: 3.35,
16
+ # facility: "Factoring", # Factoring / Invoice Discounting / Confidential Invoice Discounting
17
+ # prepayment: 80,
18
+ # serviceFee: 1.14
19
19
  # },
20
- # opportunityId: "01234567ABCDEFGH", # the Optimum Finance opportunity ID (only supplied for successful applications that are not 'quotation only')
21
- # companyName: "COMPANY NAME LTD" # company name from the given company number - use this to verify correct company
20
+ # opportunityId: "01234567ABCDEFGH", # the Optimum Finance opportunity ID (only supplied for
21
+ # successful applications that are not 'quotation only')
22
+ #
23
+ # companyName: "COMPANY NAME LTD" # company name from the given company number - use this to
24
+ # verify correct company
22
25
  # }
23
26
 
24
27
  class Optimum < DecisionParser
25
28
  STATUSES = {
26
29
  approved: 'approved',
27
30
  declined: 'declined',
28
- referred: 'referred',
31
+ referred: 'referred'
29
32
  }.freeze
30
33
 
31
34
  LOAN_STATES = {
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module DecisionParsers
5
+ # Example response:
6
+ #
7
+ # {
8
+ # thirdPartyMerchantId: "b97b0834-74e5-4da6-a3df-c8dbe017fe09",
9
+ # mid: nil,
10
+ # companyName: "Test Company",
11
+ # overallCreditRiskScore: 3.5,
12
+ # overrideCreditRiskScore: 1.0,
13
+ # loanOptions: [
14
+ # { fundedAmount: 0.0, fee: 0.0, loanAmount: 0.0, sweep: 0.0, currencyISOCode: "GBP" },
15
+ # { fundedAmount: 0.0, fee: 0.0, loanAmount: 0.0, sweep: 0.0, currencyISOCode: "GBP" },
16
+ # { fundedAmount: 0.0, fee: 0.0, loanAmount: 0.0, sweep: 0.0, currencyISOCode: "GBP" }
17
+ # ]
18
+ # }
19
+ class Youlend < DecisionParser
20
+ def approved?
21
+ status_hash[:loanOptions].any?
22
+ end
23
+
24
+ def declined?
25
+ status_hash[:loanOptions].empty?
26
+ end
27
+
28
+ def amount
29
+ option = status_hash[:loanOptions].first
30
+
31
+ amount_approved = option[:fundedAmount].to_f + option[:fee].to_f
32
+
33
+ Money.new(amount_approved * 100).format
34
+ end
35
+
36
+ private
37
+
38
+ def status_hash
39
+ data.presence || { loanOptions: [] }
40
+ end
41
+ end
42
+ end
43
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InstantQuote
4
- VERSION = '1.5.2'
4
+ VERSION = '1.6.1'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instant_quote
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.5.2
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - rikas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-11-02 00:00:00.000000000 Z
11
+ date: 2021-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -94,6 +94,34 @@ dependencies:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
96
  version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rubocop-rake
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: rubocop-rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  - !ruby/object:Gem::Dependency
98
126
  name: simplecov
99
127
  requirement: !ruby/object:Gem::Requirement
@@ -165,7 +193,7 @@ dependencies:
165
193
  - !ruby/object:Gem::Version
166
194
  version: '0'
167
195
  - !ruby/object:Gem::Dependency
168
- name: optimum
196
+ name: money
169
197
  requirement: !ruby/object:Gem::Requirement
170
198
  requirements:
171
199
  - - ">="
@@ -179,7 +207,7 @@ dependencies:
179
207
  - !ruby/object:Gem::Version
180
208
  version: '0'
181
209
  - !ruby/object:Gem::Dependency
182
- name: money
210
+ name: optimum
183
211
  requirement: !ruby/object:Gem::Requirement
184
212
  requirements:
185
213
  - - ">="
@@ -206,6 +234,20 @@ dependencies:
206
234
  - - ">="
207
235
  - !ruby/object:Gem::Version
208
236
  version: '0'
237
+ - !ruby/object:Gem::Dependency
238
+ name: youlend
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - ">="
242
+ - !ruby/object:Gem::Version
243
+ version: '0'
244
+ type: :runtime
245
+ prerelease: false
246
+ version_requirements: !ruby/object:Gem::Requirement
247
+ requirements:
248
+ - - ">="
249
+ - !ruby/object:Gem::Version
250
+ version: '0'
209
251
  description: Instant quote providers gem.
210
252
  email:
211
253
  - oterosantos@gmail.com
@@ -232,17 +274,20 @@ files:
232
274
  - lib/instant_quote/adapters/iwoca.rb
233
275
  - lib/instant_quote/adapters/iwoca_cbils.rb
234
276
  - lib/instant_quote/adapters/optimum.rb
277
+ - lib/instant_quote/adapters/youlend.rb
235
278
  - lib/instant_quote/api_error.rb
236
279
  - lib/instant_quote/connection_translator.rb
237
280
  - lib/instant_quote/connection_translators/capital_on_tap.rb
238
281
  - lib/instant_quote/connection_translators/iwoca.rb
239
282
  - lib/instant_quote/connection_translators/iwoca_cbils.rb
240
283
  - lib/instant_quote/connection_translators/optimum.rb
284
+ - lib/instant_quote/connection_translators/youlend.rb
241
285
  - lib/instant_quote/decision_parser.rb
242
286
  - lib/instant_quote/decision_parsers/capital_on_tap.rb
243
287
  - lib/instant_quote/decision_parsers/iwoca.rb
244
288
  - lib/instant_quote/decision_parsers/iwoca_cbils.rb
245
289
  - lib/instant_quote/decision_parsers/optimum.rb
290
+ - lib/instant_quote/decision_parsers/youlend.rb
246
291
  - lib/instant_quote/version.rb
247
292
  - lib/instant_quote/webhooks/iwoca.rb
248
293
  homepage: https://finpoint.co.uk
@@ -257,7 +302,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
257
302
  requirements:
258
303
  - - ">="
259
304
  - !ruby/object:Gem::Version
260
- version: '0'
305
+ version: 2.5.0
261
306
  required_rubygems_version: !ruby/object:Gem::Requirement
262
307
  requirements:
263
308
  - - ">="