instant_quote 1.1.7 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 358724fb16cc0c89f578d0b59b1dc3b7f962af917986c46d8c77406cab783abf
4
- data.tar.gz: 3cdff1f454bfdb1ac5637912ac0bf421671fcbadf33c98bc0d3533c4f981c054
3
+ metadata.gz: eef07ddced31786ca9ec8a8d92d9d411a73d22346e9d5632ff59a7e672a1f788
4
+ data.tar.gz: a74a1aceb44867a2f6d3e00ceab66274767f62eb2985034f44b4d956b00efd76
5
5
  SHA512:
6
- metadata.gz: f3bb568804077b6526a0c39debbb36c39da156bbb158dd6e5f4ca32d59797aad9ab140aba3ee37955021196a833754e35281db9d26bf757ac080da991a077526
7
- data.tar.gz: 8301b9bd8ce4c50f502e86b0a62faca488a35150a7ad247af3b3148004552c04e70c3c6febf31895bdba94e0c96316e70cf4214be0dd4b68113c26d47b4ab463
6
+ metadata.gz: a857b026d315a8a510af1dc99e83956086c1cd8a5488405f94fec30a2076b6f1e433b1346c42d01e5f5fcfc39a7164bc6b6b00c81714a6a02f1dc700e67667ec
7
+ data.tar.gz: f204e1812ae0e798dd8730d4af0aded1f4efcfda51ba57dc2c3ab02841a24a8eba6a0eeeacf0a31af298475b7b662d8cb6340a2ba60f96dae9f5d070837c5111
@@ -1 +1 @@
1
- 2.6.3
1
+ 2.6.6
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source 'https://rubygems.org'
6
6
  #
7
7
  # gem 'capital_on_tap', path: '../capital-on-tap'
8
8
  # gem 'iwoca', path: '../iwoca'
9
+ # gem 'optimum', path: '../optimum'
9
10
 
10
11
  # Specify your gem's dependencies in instant-quotes.gemspec
11
12
  gemspec
@@ -1,22 +1,23 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- instant_quote (1.1.7)
4
+ instant_quote (1.5.0)
5
5
  activesupport
6
6
  capital_on_tap
7
7
  iwoca
8
8
  money
9
+ optimum
9
10
  phonelib
10
11
 
11
12
  GEM
12
13
  remote: https://rubygems.org/
13
14
  specs:
14
- activesupport (6.0.2.2)
15
+ activesupport (6.0.3.3)
15
16
  concurrent-ruby (~> 1.0, >= 1.0.2)
16
17
  i18n (>= 0.7, < 2)
17
18
  minitest (~> 5.1)
18
19
  tzinfo (~> 1.1)
19
- zeitwerk (~> 2.2)
20
+ zeitwerk (~> 2.2, >= 2.2.2)
20
21
  addressable (2.7.0)
21
22
  public_suffix (>= 2.0.2, < 5.0)
22
23
  ast (2.4.0)
@@ -28,7 +29,7 @@ GEM
28
29
  multi_json
29
30
  rainbow
30
31
  coderay (1.1.2)
31
- concurrent-ruby (1.1.6)
32
+ concurrent-ruby (1.1.7)
32
33
  crack (0.4.3)
33
34
  safe_yaml (~> 1.0.0)
34
35
  diff-lcs (1.3)
@@ -39,7 +40,7 @@ GEM
39
40
  faraday_middleware (1.0.0)
40
41
  faraday (~> 1.0)
41
42
  hashdiff (1.0.1)
42
- i18n (1.8.2)
43
+ i18n (1.8.5)
43
44
  concurrent-ruby (~> 1.0)
44
45
  iwoca (1.0.0)
45
46
  addressable
@@ -49,15 +50,21 @@ GEM
49
50
  rainbow
50
51
  jaro_winkler (1.5.4)
51
52
  method_source (1.0.0)
52
- minitest (5.14.0)
53
- money (6.13.7)
53
+ minitest (5.14.2)
54
+ money (6.13.8)
54
55
  i18n (>= 0.6.4, <= 2)
55
- multi_json (1.14.1)
56
+ multi_json (1.15.0)
56
57
  multipart-post (2.1.1)
58
+ optimum (1.0.0)
59
+ addressable
60
+ faraday
61
+ faraday_middleware
62
+ multi_json
63
+ rainbow
57
64
  parallel (1.19.1)
58
65
  parser (2.7.0.5)
59
66
  ast (~> 2.4.0)
60
- phonelib (0.6.42)
67
+ phonelib (0.6.45)
61
68
  pry (0.13.0)
62
69
  coderay (~> 1.1)
63
70
  method_source (~> 1.0)
@@ -103,7 +110,7 @@ GEM
103
110
  addressable (>= 2.3.6)
104
111
  crack (>= 0.3.2)
105
112
  hashdiff (>= 0.4.0, < 2.0.0)
106
- zeitwerk (2.3.0)
113
+ zeitwerk (2.4.0)
107
114
 
108
115
  PLATFORMS
109
116
  ruby
@@ -120,4 +127,4 @@ DEPENDENCIES
120
127
  webmock
121
128
 
122
129
  BUNDLED WITH
123
- 2.0.2
130
+ 2.1.4
@@ -38,6 +38,7 @@ Gem::Specification.new do |spec|
38
38
  spec.add_dependency 'activesupport'
39
39
  spec.add_dependency 'capital_on_tap'
40
40
  spec.add_dependency 'iwoca'
41
+ spec.add_dependency 'optimum'
41
42
  spec.add_dependency 'money'
42
43
  spec.add_dependency 'phonelib'
43
44
  end
@@ -1,16 +1,19 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Require all the connection translators
4
- translators = Dir.children(File.expand_path('connection_translators', __dir__))
5
- translators.each { |translator| require "instant_quote/connection_translators/#{translator}" }
4
+ Dir.glob('*.rb', base: File.expand_path('connection_translators', __dir__)) do |translator|
5
+ require "instant_quote/connection_translators/#{translator}"
6
+ end
6
7
 
7
8
  # Require all the decision parsers
8
- parsers = Dir.children(File.expand_path('decision_parsers', __dir__))
9
- parsers.each { |parser| require "instant_quote/decision_parsers/#{parser}" }
9
+ Dir.glob('*.rb', base: File.expand_path('decision_parsers', __dir__)) do |parser|
10
+ require "instant_quote/decision_parsers/#{parser}"
11
+ end
10
12
 
11
13
  # Require all the adapters
12
- adapters = Dir.children(File.expand_path('adapters', __dir__))
13
- adapters.each { |adapter| require "instant_quote/adapters/#{adapter}" }
14
+ Dir.glob('*.rb', base: File.expand_path('adapters', __dir__)) do |adapter|
15
+ require "instant_quote/adapters/#{adapter}"
16
+ end
14
17
 
15
18
  module InstantQuote
16
19
  class AdapterFinder
@@ -28,8 +31,8 @@ module InstantQuote
28
31
  end
29
32
 
30
33
  def self.available_adapters
31
- adapters = Dir.children(File.expand_path('adapters', __dir__))
32
- adapters.map { |adapter| adapter.tr('.rb', '') }
34
+ Dir.glob('*.rb', base: File.expand_path('adapters', __dir__))
35
+ .map { |adapter| adapter.sub('.rb', '') }
33
36
  end
34
37
 
35
38
  def get_link
@@ -13,11 +13,6 @@ module InstantQuote
13
13
  type: 'date',
14
14
  required: true
15
15
  },
16
- {
17
- name: 'business_landline',
18
- label: 'Business landline',
19
- required: true
20
- },
21
16
  {
22
17
  name: 'applicant_phone_number',
23
18
  label: 'Applicant mobile phone',
@@ -25,7 +20,7 @@ module InstantQuote
25
20
  },
26
21
  {
27
22
  name: 'monthly_income',
28
- label: 'Applicat monthly income',
23
+ label: 'Applicant monthly income',
29
24
  type: 'number',
30
25
  required: true,
31
26
  if: ->(app) { app.borrower_organisation.sole_trader? }
@@ -37,6 +32,31 @@ module InstantQuote
37
32
  required: true,
38
33
  if: ->(app) { app.borrower_organisation.sole_trader? }
39
34
  },
35
+ {
36
+ name: 'turnover_percentage_after_feb_2020',
37
+ label: 'When compared to before COVID-19, what is your current turnover level?',
38
+ required: true,
39
+ choices: {
40
+ 'Greater than before (>100%)' => 'GreaterThanHundredPercentage',
41
+ 'Marginally reduced (80-100%)' => 'LessThanOrEqualToHunderdPercentage',
42
+ 'Reduced (60-79%)' => 'LessThanSeventyNinePercentage',
43
+ 'Halved (40-59%)' => 'LessThanFiftyNinePercentage',
44
+ 'Less than half (20-39%)' => 'LessThanThirtyNinePercentage',
45
+ 'Severely reduced or no turnover (<20%)' => 'LessThanTwentyPercentage'
46
+ },
47
+ type: 'select'
48
+ },
49
+ {
50
+ name: 'preferred_monthly_repayment_option',
51
+ label: 'What is your preferred monthly repayment option?',
52
+ required: true,
53
+ choices: [
54
+ 'Minimum Amount Required',
55
+ 'Full Balance',
56
+ 'Fixed Amount'
57
+ ],
58
+ type: 'select'
59
+ },
40
60
  {
41
61
  name: 'applicant_consent',
42
62
  label: 'Yes, I give my consent for my data to be shared',
@@ -57,17 +77,17 @@ module InstantQuote
57
77
 
58
78
  # Get the status for a given application ID
59
79
  # {:applicationStatus=>
60
- # {:locatorId=>"B247T62",
61
- # :applicationStage=>"AppSubmitted",
80
+ # {:locatorId=>'B247T62',
81
+ # :applicationStage=>'AppSubmitted',
62
82
  # :creditDecision=>
63
- # {:status=>"Pending",
83
+ # {:status=>'Pending',
64
84
  # :starRating=>0.0,
65
85
  # :approvalAmount=>0.0,
66
86
  # :monthlyInterestRate=>0.0,
67
87
  # :monthlyCardInterestRate=>0.0,
68
- # :decisionDate=>"2020-01-28T01:44:29.241",
69
- # :creditDecisionReason=>"None"}},
70
- # :links=>{:self=>"/api/v1/Applications/B247T62", :parent=>nil},
88
+ # :decisionDate=>'2020-01-28T01:44:29.241',
89
+ # :creditDecisionReason=>'None'}},
90
+ # :links=>{:self=>'/api/v1/Applications/B247T62', :parent=>nil},
71
91
  # :metaData=>{}
72
92
  # }
73
93
  def get_status(application_id)
@@ -77,7 +77,7 @@ module InstantQuote
77
77
 
78
78
  raise_error(response) unless response.success?
79
79
 
80
- DecisionParsers::Iwoca.new(response.body)
80
+ decision_parser.new(response.body)
81
81
  end
82
82
 
83
83
  # Gets the URL for the given state key.
@@ -96,6 +96,10 @@ module InstantQuote
96
96
 
97
97
  private
98
98
 
99
+ def decision_parser
100
+ DecisionParsers::Iwoca
101
+ end
102
+
99
103
  def raise_error(response)
100
104
  raise ApiError.new(
101
105
  adapter: 'iwoca',
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'iwoca'
4
+ require 'instant_quote/adapters/iwoca'
5
+ require 'instant_quote/decision_parsers/iwoca_cbils'
6
+
7
+ module InstantQuote
8
+ module Adapters
9
+ class IwocaCbils < Iwoca
10
+ additional_fields Iwoca.additional_fields
11
+
12
+ private
13
+
14
+ def decision_parser
15
+ DecisionParsers::IwocaCbils
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,144 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optimum'
4
+ require 'instant_quote/decision_parsers/optimum'
5
+
6
+ module InstantQuote
7
+ module Adapters
8
+ class Optimum < Adapter
9
+ additional_fields [
10
+ {
11
+ name: 'applicant_phone_number',
12
+ label: 'Applicant mobile phone',
13
+ required: true
14
+ },
15
+ {
16
+ name: 'expected_turnover_next_12_months',
17
+ label: 'Expected turnover next 12 months',
18
+ required: true,
19
+ type: 'money'
20
+ },
21
+ {
22
+ name: 'expected_customers',
23
+ label: 'Number of expected customers with a balance in 6 months',
24
+ required: true,
25
+ type: 'number'
26
+ },
27
+ {
28
+ name: 'industry_sector',
29
+ label: 'Industry sector',
30
+ required: true,
31
+ type: 'select',
32
+ choices: [
33
+ 'Advertising / Marketing',
34
+ 'Beer / Wine Merchant',
35
+ 'Builders Merchant',
36
+ 'Clothing & Fashion',
37
+ 'Computer Hardware',
38
+ 'Computer Software',
39
+ 'Construction',
40
+ 'Consultancy',
41
+ 'Contract Cleaning',
42
+ 'Courier',
43
+ 'Distribution',
44
+ 'Driving Agency',
45
+ 'Electrical Contracting',
46
+ 'Electrical Wholesaler',
47
+ 'Engineering - Heavy',
48
+ 'Engineering - Light',
49
+ 'Engineering - Precision',
50
+ 'Fabrication',
51
+ 'Flooring Contractor',
52
+ 'Food Manufacture',
53
+ 'Franchise',
54
+ 'Free Newspaper',
55
+ 'Freight Forwarding',
56
+ 'Fruit Importer',
57
+ 'Furniture Manufacture',
58
+ 'Garden Equipment',
59
+ 'Haulage',
60
+ 'IT Contracting',
61
+ 'Jewellery',
62
+ 'Maintenance',
63
+ 'Manufacturing',
64
+ 'Meat Trade',
65
+ 'Other Services',
66
+ 'Permanent Recruitment',
67
+ 'Plant Hire',
68
+ 'Plastic Injection Moulding',
69
+ 'Plumbers Merchant',
70
+ 'Printing',
71
+ 'Publishing',
72
+ 'Recycling',
73
+ 'Security Guards',
74
+ 'Shopfitting',
75
+ 'Stationery',
76
+ 'Steel Stockholding',
77
+ 'Telecoms',
78
+ 'Temporary Recruitment',
79
+ 'Timber Merchant',
80
+ 'Tooling Manufacture',
81
+ 'Toys & Fancy Goods',
82
+ 'Warehousing & Storage',
83
+ 'Wholesale'
84
+ ]
85
+ },
86
+ {
87
+ name: 'credit_control',
88
+ label: 'Do you require credit control?',
89
+ required: false,
90
+ choices: {
91
+ 'Yes' => '1',
92
+ 'No' => '0'
93
+ },
94
+ type: 'radio'
95
+ },
96
+ {
97
+ name: 'applicant_consent',
98
+ label: 'Yes, I give my consent for my data to be shared',
99
+ required: true,
100
+ type: 'checkbox'
101
+ }
102
+ ]
103
+
104
+ def get_quote(params)
105
+ response = ::Optimum::Decision.create(params)
106
+
107
+ return response.data[:opportunityId] if response.success?
108
+
109
+ raise_error(response)
110
+ end
111
+
112
+ def get_status(application_id)
113
+ response = ::Optimum::Decision.update(application_id)
114
+
115
+ raise_error(response) unless response.success?
116
+
117
+ decision_parser.new(response.body)
118
+ end
119
+
120
+ # Gets the URL for the given state key.
121
+ def get_link(_)
122
+ ''
123
+ end
124
+
125
+ def get_approval(_)
126
+ true
127
+ end
128
+
129
+ private
130
+
131
+ def decision_parser
132
+ DecisionParsers::Optimum
133
+ end
134
+
135
+ def raise_error(response)
136
+ raise ApiError.new(
137
+ adapter: 'optimum',
138
+ data: response.body,
139
+ error: response.error || 'Unknown API error'
140
+ )
141
+ end
142
+ end
143
+ end
144
+ end
@@ -14,7 +14,7 @@ module InstantQuote
14
14
  @code = params[:code]
15
15
 
16
16
  # Delete the metaData key, because it's irrelevant and has long strings
17
- @data.delete(:metaData) if @data.key?(:metaData)
17
+ @data.delete(:metaData) if @data.respond_to?(:key?) && @data.key?(:metaData)
18
18
  end
19
19
  end
20
20
  end
@@ -6,6 +6,60 @@ module InstantQuote
6
6
  module ConnectionTranslators
7
7
  class CapitalOnTap < ConnectionTranslator
8
8
  DEFAULT_SALUTATION = 'Mr'
9
+ LOAN_PURPOSE_MAPPING = {
10
+ 'Apply for COVID-19 support/for other reasons' => 'General working capital requirements',
11
+ 'Apply for COVID-19 support/to pay existing loans' => 'Repay Finances with another lender',
12
+ 'Apply for COVID-19 support/to pay suppliers' => 'General working capital requirements',
13
+ 'Apply for COVID-19 support/to protect jobs' => 'Payment of wages or rent',
14
+ 'Apply for COVID-19 support/to refinance existing loans' => 'Repay Finances with another lender',
15
+ 'Buy or develop property/buy the property my business already occupies' => '',
16
+ 'Buy or develop property/buying a business that includes premises' => 'Acquisition of another business in the UK',
17
+ 'Buy or develop property/buying a property for my business to move into' => '',
18
+ 'Buy or develop property/develop residential or commercial premises' => 'Bridging loan',
19
+ 'Buy or develop property/invest in a foreign property to rent it out' => '',
20
+ 'Buy or develop property/invest in a holiday let' => '',
21
+ 'Buy or develop property/invest in UK property' => 'Bridging loan',
22
+ 'Buy or develop property/other property finance needs' => 'Bridging loan',
23
+ 'Buy or refinance vehicle(s)/agricultural vehicle(s)' => 'General working capital requirements',
24
+ 'Buy or refinance vehicle(s)/car(s)' => 'General working capital requirements',
25
+ 'Buy or refinance vehicle(s)/caravan(s) & mobile home(s)' => 'General working capital requirements',
26
+ 'Buy or refinance vehicle(s)/commercial vehicle(s)' => 'General working capital requirements',
27
+ 'Buy or refinance vehicle(s)/fork lift truck(s)' => 'General working capital requirements',
28
+ 'Buy or refinance vehicle(s)/other vehicle finance requirements' => 'General working capital requirements',
29
+ 'Help with grants, tax credits and pension finance/for a research & development project' => 'Research and development activities',
30
+ 'Help with grants, tax credits and pension finance/for green energy projects' => 'General working capital requirements',
31
+ 'Help with grants, tax credits and pension finance/for projects with social impact' => 'General working capital requirements',
32
+ 'Help with grants, tax credits and pension finance/other grant funding needs' => '',
33
+ 'Help with grants, tax credits and pension finance/starting a business, incl. accelerator and incubator programs' => 'Start up costs',
34
+ 'Help with grants, tax credits and pension finance/to fund skills and training initiatives' => 'General working capital requirements',
35
+ 'Help with grants, tax credits and pension finance/utilise pensions to lend to a business' => '',
36
+ 'Improve cashflow/borrow against monthly income from a credit card terminal' => '',
37
+ 'Improve cashflow/finance overseas trading and exporting' => 'General working capital requirements',
38
+ 'Improve cashflow/other cashflow requirements' => 'General working capital requirements',
39
+ 'Improve cashflow/raise credit against agreed purchase orders' => 'General working capital requirements',
40
+ 'Improve cashflow/release capital from pension fund(s)' => '',
41
+ 'Improve cashflow/release cash from one unpaid invoice to another business' => 'General working capital requirements',
42
+ 'Improve cashflow/release cash from several invoices to other businesses' => 'General working capital requirements',
43
+ 'Improve cashflow/spread the cost of a large bill' => 'General working capital requirements',
44
+ 'Invest in equipment or machinery/computers (hardware, software)' => 'Purchase of equipment/machinery/stock',
45
+ 'Invest in equipment or machinery/equipment (office, printing, medical, etc.)' => 'Purchase of equipment/machinery/stock',
46
+ 'Invest in equipment or machinery/furniture & furnishings' => 'Purchase of equipment/machinery/stock',
47
+ 'Invest in equipment or machinery/machinery & tools' => 'Purchase of equipment/machinery/stock',
48
+ 'Invest in equipment or machinery/other investment needs' => 'Purchase of licences,quotas or other entitlements to trade',
49
+ 'Invest in equipment or machinery/phone & telecom systems' => 'Purchase of equipment/machinery/stock',
50
+ 'Refinance an existing.../asset' => 'Repay Finances with another lender',
51
+ 'Refinance an existing.../business loan' => 'Repay Finances with another lender',
52
+ 'Refinance an existing.../commercial mortgage' => '',
53
+ 'Refinance an existing.../other refinancing needs' => 'Repay Finances with another lender',
54
+ 'Seeking investment/for a management buy-out' => 'Acquisition of another business in the UK',
55
+ 'Seeking investment/other funding requirements' => 'General working capital requirements',
56
+ 'Seeking investment/to launch a new B2B product or service' => 'Start up costs',
57
+ 'Seeking investment/to launch a new consumer product or service' => 'Start up costs',
58
+ 'Seeking investment/to purchase another business' => 'Acquisition of another business in the UK',
59
+ 'Seeking investment/to reach more consumers with a proven product or service' => 'Expansion Activities in the UK',
60
+ 'Seeking investment/to win more business clients with a proven product or service' => 'Expansion Activities in the UK',
61
+ 'Seeking investment/venture builder, accelerator and incubator programs' => ''
62
+ }.freeze
9
63
 
10
64
  # rubocop:disable Metrics/AbcSize
11
65
  def translate
@@ -19,12 +73,13 @@ module InstantQuote
19
73
  PersonalAddress: translate_address,
20
74
  TradingName: app_org.name,
21
75
  BusinessLegalName: app_org.name,
22
- BusinessLandline: format_phone(extra_info['business_landline']),
76
+ BusinessLandline: format_phone(extra_info['applicant_phone_number']),
23
77
  YearsTrading: app_org.years_in_business,
24
78
  MonthlyTurnOver: translate_turnover,
25
79
  BusinessType: translate_business_type,
26
80
  BusinessAddress: translate_address,
27
- RegistrationNumber: app_org.company_number
81
+ RegistrationNumber: app_org.company_number,
82
+ additionalQuestions: additional_questions
28
83
  }
29
84
 
30
85
  # If the borrower organisation is Sole Trader we need to merge two additional fields.
@@ -71,7 +126,7 @@ module InstantQuote
71
126
  }
72
127
  end
73
128
 
74
- # Since in Finpoint the user selects an intervel (i.e. "500,000 - 999,999"). We get the avg
129
+ # Since in Finpoint the user selects an intervel (i.e. '500,000 - 999,999'). We get the avg
75
130
  # on this interval and use that number as the monthly turnover.
76
131
  def translate_turnover
77
132
  turnover = app_org.turnover_range
@@ -93,6 +148,47 @@ module InstantQuote
93
148
  'Partnership'
94
149
  end
95
150
  end
151
+
152
+ def additional_questions
153
+ [
154
+ loan_purpose_question,
155
+ preferred_monthly_repayment_option_question,
156
+ turnover_percentage_question
157
+ ]
158
+ end
159
+
160
+ def loan_purpose_question
161
+ {
162
+ question: 'What is the purpose of the loan?',
163
+ answer: translate_loan_purpose,
164
+ questionType: '16'
165
+ }
166
+ end
167
+
168
+ def preferred_monthly_repayment_option_question
169
+ question_from_extra_info('preferred_monthly_repayment_option', '17')
170
+ end
171
+
172
+ def turnover_percentage_question
173
+ question_from_extra_info('turnover_percentage_after_feb_2020', '20')
174
+ end
175
+
176
+ def question_from_extra_info(name, question_type)
177
+ field = Adapters::CapitalOnTap.additional_fields.find do |f|
178
+ f[:name] == name
179
+ end
180
+
181
+ { question: field[:label], answer: extra_info[field[:name]], questionType: question_type }
182
+ end
183
+
184
+ def translate_loan_purpose
185
+ proceeds_purpose = [
186
+ application.proceeds_purpose&.overview&.name,
187
+ application.proceeds_purpose&.name
188
+ ].join('/')
189
+
190
+ LOAN_PURPOSE_MAPPING[proceeds_purpose]
191
+ end
96
192
  end
97
193
  end
98
194
  end
@@ -10,7 +10,7 @@ module InstantQuote
10
10
  company: company_information,
11
11
  people: [person_information],
12
12
  requested_products: {
13
- credit_facility: {
13
+ product_name => {
14
14
  approval: {
15
15
  amount: (application.amount_pennies / 100),
16
16
  duration: 12,
@@ -25,6 +25,10 @@ module InstantQuote
25
25
 
26
26
  private
27
27
 
28
+ def product_name
29
+ :credit_facility
30
+ end
31
+
28
32
  # rubocop:disable Metrics/AbcSize
29
33
  def company_information
30
34
  information = {
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'instant_quote/connection_translators/iwoca'
4
+
5
+ module InstantQuote
6
+ module ConnectionTranslators
7
+ class IwocaCbils < Iwoca
8
+ private
9
+
10
+ def product_name
11
+ :cbils
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module ConnectionTranslators
5
+ class Optimum < ConnectionTranslator
6
+ def translate
7
+ {
8
+ companyNumber: company&.company_number.to_s,
9
+ sector: extra_info['industry_sector'],
10
+ turnover: extra_info['expected_turnover_next_12_months'].to_f,
11
+ homeowners: homeowners?,
12
+ yearsTrading: company&.years_in_business,
13
+ expectedCustomers: extra_info['expected_customers'].to_i,
14
+ contactName: application&.primary_user&.name,
15
+ contactNumber: extra_info['applicant_phone_number'],
16
+ contactEmail: application&.primary_user&.email,
17
+ creditControl: extra_info['credit_control'] == '1',
18
+ phoenix: false,
19
+ quotationOnly: false
20
+ }
21
+ end
22
+
23
+ private
24
+
25
+ def company
26
+ application.borrower_organisation
27
+ end
28
+
29
+ def homeowners?
30
+ case company&.directors_home_owning&.name
31
+ when 'Home owner without mortgage', 'Home owner with mortgage'
32
+ true
33
+ else
34
+ false
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'instant_quote/decision_parsers/iwoca'
4
+
5
+ module InstantQuote
6
+ module DecisionParsers
7
+ class IwocaCbils < Iwoca
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,88 @@
1
+ # frozen_string_literal: true
2
+
3
+ module InstantQuote
4
+ module DecisionParsers
5
+ # Example response:
6
+ #
7
+ # {
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
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
22
+ # }
23
+
24
+ class Optimum < DecisionParser
25
+ STATUSES = {
26
+ approved: 'approved',
27
+ declined: 'declined',
28
+ referred: 'referred',
29
+ }.freeze
30
+
31
+ LOAN_STATES = {
32
+ started: 'Closed Won'
33
+ }.freeze
34
+
35
+ def initialize(data = {})
36
+ super
37
+ end
38
+
39
+ def pending?
40
+ !STATUSES.value?(status)
41
+ end
42
+
43
+ def approved?
44
+ status == STATUSES[:approved]
45
+ end
46
+
47
+ def declined?
48
+ status == STATUSES[:declined]
49
+ end
50
+
51
+ def manual_review?
52
+ status == STATUSES[:referred]
53
+ end
54
+
55
+ def loan_started?
56
+ data[:status] == LOAN_STATES[:started]
57
+ end
58
+
59
+ def amount
60
+ amount_approved = decision[:fundingLimit] || 0
61
+
62
+ Money.new(amount_approved * 100).format
63
+ end
64
+
65
+ def credit_duration
66
+ 0
67
+ end
68
+
69
+ def credit_interval
70
+ decision[:trialPeriodMonths]
71
+ end
72
+
73
+ def monthly_interest_rate
74
+ 0
75
+ end
76
+
77
+ def status
78
+ decision[:decision]
79
+ end
80
+
81
+ private
82
+
83
+ def decision
84
+ data[:decision] || data[:originalDecision] || {}
85
+ end
86
+ end
87
+ end
88
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module InstantQuote
4
- VERSION = '1.1.7'
4
+ VERSION = '1.5.0'
5
5
  end
@@ -16,31 +16,15 @@ module InstantQuote
16
16
  class SignatureVerifyError < StandardError
17
17
  end
18
18
 
19
- def iwoca_event
20
- # Dynamically calls the following methods:
21
- # * handle_approval_status_changed
22
- # * handle_instruct_payments
23
- # * handle_customer_funded
24
- if iwoca_verify_signature
25
- return send("handle_#{iwoca_event_type}") if respond_to?("handle_#{iwoca_event_type}")
26
-
27
- render json: iwoca_payload, status: 200
28
- else
29
- iwoca_verify_signature_error
30
- end
31
- rescue SignatureVerifyError => e
32
- iwoca_verify_signature_error(e.message)
33
- end
34
-
35
19
  private
36
20
 
37
21
  def iwoca_verify_signature
38
- unless webhook_signature && iwoca_payload && iwoca_signature
22
+ unless webhook_signature && payload && iwoca_signature
39
23
  raise SignatureVerifyError, "#{SIGNATURE_HEADER} header missing"
40
24
  end
41
25
 
42
26
  digest = OpenSSL::Digest.new('sha1')
43
- hmac_digest = OpenSSL::HMAC.digest(digest, webhook_signature, iwoca_payload)
27
+ hmac_digest = OpenSSL::HMAC.digest(digest, webhook_signature, payload)
44
28
  signature = "sha1=#{Base64.strict_encode64(hmac_digest)}"
45
29
 
46
30
  Rack::Utils.secure_compare(signature, iwoca_signature)
@@ -50,8 +34,8 @@ module InstantQuote
50
34
  @webhook_signature ||= ::Iwoca.configuration.webhook_signature
51
35
  end
52
36
 
53
- def iwoca_payload
54
- @iwoca_payload ||= request.body.read
37
+ def payload
38
+ @payload ||= request.body.read
55
39
  end
56
40
 
57
41
  def iwoca_signature
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.1.7
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - rikas
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-07-03 00:00:00.000000000 Z
11
+ date: 2020-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -164,6 +164,20 @@ dependencies:
164
164
  - - ">="
165
165
  - !ruby/object:Gem::Version
166
166
  version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: optimum
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :runtime
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
167
181
  - !ruby/object:Gem::Dependency
168
182
  name: money
169
183
  requirement: !ruby/object:Gem::Requirement
@@ -216,13 +230,19 @@ files:
216
230
  - lib/instant_quote/adapter_finder.rb
217
231
  - lib/instant_quote/adapters/capital_on_tap.rb
218
232
  - lib/instant_quote/adapters/iwoca.rb
233
+ - lib/instant_quote/adapters/iwoca_cbils.rb
234
+ - lib/instant_quote/adapters/optimum.rb
219
235
  - lib/instant_quote/api_error.rb
220
236
  - lib/instant_quote/connection_translator.rb
221
237
  - lib/instant_quote/connection_translators/capital_on_tap.rb
222
238
  - lib/instant_quote/connection_translators/iwoca.rb
239
+ - lib/instant_quote/connection_translators/iwoca_cbils.rb
240
+ - lib/instant_quote/connection_translators/optimum.rb
223
241
  - lib/instant_quote/decision_parser.rb
224
242
  - lib/instant_quote/decision_parsers/capital_on_tap.rb
225
243
  - lib/instant_quote/decision_parsers/iwoca.rb
244
+ - lib/instant_quote/decision_parsers/iwoca_cbils.rb
245
+ - lib/instant_quote/decision_parsers/optimum.rb
226
246
  - lib/instant_quote/version.rb
227
247
  - lib/instant_quote/webhooks/iwoca.rb
228
248
  homepage: https://finpoint.co.uk