mas-rad_core 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +3 -0
  4. data/Rakefile +14 -0
  5. data/app/models/accreditation.rb +5 -0
  6. data/app/models/adviser.rb +61 -0
  7. data/app/models/allowed_payment_method.rb +7 -0
  8. data/app/models/authentication.rb +19 -0
  9. data/app/models/firm.rb +143 -0
  10. data/app/models/in_person_advice_method.rb +7 -0
  11. data/app/models/initial_advice_fee_structure.rb +7 -0
  12. data/app/models/initial_meeting_duration.rb +7 -0
  13. data/app/models/investment_size.rb +7 -0
  14. data/app/models/lookup/adviser.rb +10 -0
  15. data/app/models/lookup/firm.rb +17 -0
  16. data/app/models/lookup/subsidiary.rb +11 -0
  17. data/app/models/ongoing_advice_fee_structure.rb +7 -0
  18. data/app/models/other_advice_method.rb +7 -0
  19. data/app/models/principal.rb +94 -0
  20. data/app/models/professional_body.rb +3 -0
  21. data/app/models/professional_standing.rb +3 -0
  22. data/app/models/qualification.rb +5 -0
  23. data/app/models/travel_distance.rb +15 -0
  24. data/config/routes.rb +2 -0
  25. data/db/migrate/20141211110031_create_lookup_firms.rb +11 -0
  26. data/db/migrate/20141221140208_create_principals.rb +23 -0
  27. data/db/migrate/20141222150416_add_last_sign_in_at_to_principal.rb +5 -0
  28. data/db/migrate/20141230112136_create_lookup_subsidiaries.rb +10 -0
  29. data/db/migrate/20150106115732_remove_last_sign_in_at_from_principals.rb +5 -0
  30. data/db/migrate/20150114144343_create_lookup_advisers.rb +10 -0
  31. data/db/migrate/20150114151447_create_firms.rb +13 -0
  32. data/db/migrate/20150115130949_add_address_to_firm.rb +9 -0
  33. data/db/migrate/20150119102735_create_in_person_advice_methods.rb +15 -0
  34. data/db/migrate/20150119111754_create_other_advice_methods.rb +15 -0
  35. data/db/migrate/20150119114218_add_free_initial_meeting_to_firm.rb +5 -0
  36. data/db/migrate/20150119125208_create_initial_meeting_durations.rb +11 -0
  37. data/db/migrate/20150119152325_create_initial_advice_fee_structures.rb +15 -0
  38. data/db/migrate/20150119152336_create_ongoing_advice_fee_structures.rb +15 -0
  39. data/db/migrate/20150120133717_create_allowed_payment_methods.rb +15 -0
  40. data/db/migrate/20150120141928_add_minimum_fixed_fee_to_firm.rb +5 -0
  41. data/db/migrate/20150120150738_create_investment_sizes.rb +15 -0
  42. data/db/migrate/20150121110757_create_advisers.rb +11 -0
  43. data/db/migrate/20150121123437_add_business_income_breakdown_fields_to_firm.rb +11 -0
  44. data/db/migrate/20150121134845_create_qualifications.rb +15 -0
  45. data/db/migrate/20150121154458_create_accreditations.rb +15 -0
  46. data/db/migrate/20150121173015_create_professional_standings.rb +15 -0
  47. data/db/migrate/20150121181341_create_professional_bodies.rb +15 -0
  48. data/db/migrate/20150121183728_add_confirmed_disclaimer_to_advisers.rb +5 -0
  49. data/db/migrate/20150124124350_add_geographical_fields_to_adviser.rb +7 -0
  50. data/db/migrate/20150125145457_add_parent_id_to_firms.rb +5 -0
  51. data/db/migrate/20150125164156_remove_unique_constraint_from_firms_fca_number.rb +5 -0
  52. data/db/migrate/20150127084858_add_ordering_on_reference_data.rb +31 -0
  53. data/db/migrate/20150209144836_remove_covers_whole_of_uk_from_advisers.rb +17 -0
  54. data/lib/mas/rad_core.rb +4 -0
  55. data/lib/mas/rad_core/engine.rb +21 -0
  56. data/lib/mas/rad_core/version.rb +5 -0
  57. data/lib/mas/stats.rb +19 -0
  58. data/lib/mas/tasks/rad_core_tasks.rake +4 -0
  59. data/spec/dummy/Rakefile +6 -0
  60. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  61. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  62. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  63. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  64. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  65. data/spec/dummy/bin/bundle +3 -0
  66. data/spec/dummy/bin/rails +4 -0
  67. data/spec/dummy/bin/rake +4 -0
  68. data/spec/dummy/bin/setup +29 -0
  69. data/spec/dummy/config.ru +4 -0
  70. data/spec/dummy/config/application.rb +26 -0
  71. data/spec/dummy/config/boot.rb +5 -0
  72. data/spec/dummy/config/database.example.yml +14 -0
  73. data/spec/dummy/config/database.yml +13 -0
  74. data/spec/dummy/config/environment.rb +5 -0
  75. data/spec/dummy/config/environments/development.rb +41 -0
  76. data/spec/dummy/config/environments/production.rb +79 -0
  77. data/spec/dummy/config/environments/test.rb +42 -0
  78. data/spec/dummy/config/initializers/assets.rb +11 -0
  79. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  80. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  81. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  82. data/spec/dummy/config/initializers/inflections.rb +16 -0
  83. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  84. data/spec/dummy/config/initializers/session_store.rb +3 -0
  85. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  86. data/spec/dummy/config/locales/en.yml +23 -0
  87. data/spec/dummy/config/routes.rb +3 -0
  88. data/spec/dummy/config/secrets.yml +22 -0
  89. data/spec/dummy/db/schema.rb +246 -0
  90. data/spec/dummy/log/test.log +13097 -0
  91. data/spec/dummy/public/404.html +67 -0
  92. data/spec/dummy/public/422.html +67 -0
  93. data/spec/dummy/public/500.html +66 -0
  94. data/spec/dummy/public/favicon.ico +0 -0
  95. data/spec/factories/accreditation.rb +5 -0
  96. data/spec/factories/adviser.rb +21 -0
  97. data/spec/factories/allowed_payment_method.rb +5 -0
  98. data/spec/factories/firm.rb +34 -0
  99. data/spec/factories/in_person_advice_method.rb +5 -0
  100. data/spec/factories/initial_advice_fee_structure.rb +5 -0
  101. data/spec/factories/initial_meeting_duration.rb +5 -0
  102. data/spec/factories/investment_size.rb +5 -0
  103. data/spec/factories/lookup_firm.rb +6 -0
  104. data/spec/factories/ongoing_advice_fee_structure.rb +5 -0
  105. data/spec/factories/other_advice_method.rb +5 -0
  106. data/spec/factories/principal.rb +15 -0
  107. data/spec/factories/professional_body.rb +5 -0
  108. data/spec/factories/professional_standing.rb +5 -0
  109. data/spec/factories/qualification.rb +5 -0
  110. data/spec/models/accreditation_spec.rb +3 -0
  111. data/spec/models/adviser_spec.rb +93 -0
  112. data/spec/models/allowed_payment_method_spec.rb +3 -0
  113. data/spec/models/firm_spec.rb +188 -0
  114. data/spec/models/in_person_advice_method_spec.rb +3 -0
  115. data/spec/models/initial_advice_fee_structure_spec.rb +3 -0
  116. data/spec/models/initial_meeting_duration_spec.rb +3 -0
  117. data/spec/models/investment_size_spec.rb +3 -0
  118. data/spec/models/lookup/adviser_spec.rb +21 -0
  119. data/spec/models/lookup/firm_spec.rb +19 -0
  120. data/spec/models/ongoing_advice_fee_structure_spec.rb +3 -0
  121. data/spec/models/other_advice_method_spec.rb +3 -0
  122. data/spec/models/principal_spec.rb +185 -0
  123. data/spec/models/qualification_spec.rb +3 -0
  124. data/spec/spec_helper.rb +24 -0
  125. data/spec/support/shared_examples/reference_data.rb +14 -0
  126. metadata +361 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8ddedc793fa1147bec67ccc827f16325a581689c
4
+ data.tar.gz: 82db2d2dc83430c01d804c0402c399f72cd0a82a
5
+ SHA512:
6
+ metadata.gz: 7968241320d7a619acc7c7f8022a29bba5529f2a9132d5f3ca10cb1efb0c3f2288b40d1a8396af7b31a5864ccfc72d13df06058c0add6e0e347012ba91194663
7
+ data.tar.gz: dd469eea433328259512990f84119ce6d8e9c248e9c4d65b49800c19a83969f0190321f95dfcdc572c08313c3d1f6f6270ef9b28a421d18ff48fefc1d00f1c2e
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2015 Ben Lovell
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # RadCore
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ require 'bundler/gem_tasks'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+
8
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
9
+
10
+ load 'rails/tasks/engine.rake'
11
+
12
+ Bundler::GemHelper.install_tasks
13
+
14
+ task default: :spec
@@ -0,0 +1,5 @@
1
+ class Accreditation < ActiveRecord::Base
2
+ validates_presence_of :name
3
+
4
+ default_scope { order(:order) }
5
+ end
@@ -0,0 +1,61 @@
1
+ class Adviser < ActiveRecord::Base
2
+ belongs_to :firm
3
+
4
+ has_and_belongs_to_many :qualifications
5
+ has_and_belongs_to_many :accreditations
6
+ has_and_belongs_to_many :professional_standings
7
+ has_and_belongs_to_many :professional_bodies
8
+
9
+ before_validation :assign_name, if: :reference_number?
10
+
11
+ before_validation :upcase_postcode
12
+
13
+ validates_acceptance_of :confirmed_disclaimer, accept: true
14
+
15
+ validates :travel_distance,
16
+ presence: true,
17
+ inclusion: { in: TravelDistance.all.values }
18
+
19
+ validates :postcode,
20
+ presence: true,
21
+ format: { with: /\A[A-Z\d]{1,4} [A-Z\d]{1,3}\z/ }
22
+
23
+ validates :reference_number,
24
+ presence: true,
25
+ uniqueness: true,
26
+ format: {
27
+ with: /\A[A-Z]{3}[0-9]{5}\z/
28
+ }
29
+
30
+ validate :match_reference_number
31
+
32
+ def field_order
33
+ [
34
+ :reference_number,
35
+ :postcode,
36
+ :travel_distance,
37
+ :confirmed_disclaimer
38
+ ]
39
+ end
40
+
41
+ private
42
+
43
+ def upcase_postcode
44
+ postcode.upcase! if postcode.present?
45
+ end
46
+
47
+ def assign_name
48
+ self.name = Lookup::Adviser.find_by(
49
+ reference_number: reference_number
50
+ ).try(:name)
51
+ end
52
+
53
+ def match_reference_number
54
+ unless Lookup::Adviser.exists?(reference_number: reference_number)
55
+ errors.add(
56
+ :reference_number,
57
+ I18n.t('questionnaire.adviser.reference_number_un_matched')
58
+ )
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,7 @@
1
+ class AllowedPaymentMethod < ActiveRecord::Base
2
+ has_and_belongs_to_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,19 @@
1
+ module Authentication
2
+ class << self
3
+ def required?
4
+ Rails.env.production?
5
+ end
6
+
7
+ def username
8
+ ENV['AUTH_USERNAME']
9
+ end
10
+
11
+ def password
12
+ ENV['AUTH_PASSWORD']
13
+ end
14
+
15
+ def authenticate(username, password)
16
+ self.username == username && self.password == password
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,143 @@
1
+ class Firm < ActiveRecord::Base
2
+ scope :registered, -> { where.not(email_address: nil) }
3
+
4
+ has_and_belongs_to_many :in_person_advice_methods
5
+ has_and_belongs_to_many :other_advice_methods
6
+ has_and_belongs_to_many :initial_advice_fee_structures
7
+ has_and_belongs_to_many :ongoing_advice_fee_structures
8
+ has_and_belongs_to_many :allowed_payment_methods
9
+ has_and_belongs_to_many :investment_sizes
10
+
11
+ belongs_to :initial_meeting_duration
12
+
13
+ belongs_to :principal, primary_key: :fca_number, foreign_key: :fca_number
14
+
15
+ has_many :advisers
16
+
17
+ has_many :subsidiaries, class_name: 'Firm', foreign_key: :parent_id
18
+
19
+ belongs_to :parent, class_name: 'Firm'
20
+
21
+ attr_accessor :percent_total
22
+
23
+ before_validation :upcase_postcode
24
+
25
+ validates :email_address,
26
+ presence: true,
27
+ length: { maximum: 50 },
28
+ format: { with: /.+@.+\..+/ }
29
+
30
+ validates :telephone_number,
31
+ presence: true,
32
+ length: { maximum: 30 },
33
+ format: { with: /\A[0-9 ]+\z/ }
34
+
35
+ validates :address_line_one,
36
+ presence: true,
37
+ length: { maximum: 100 }
38
+
39
+ validates :address_line_two,
40
+ length: { maximum: 100 }
41
+
42
+ validates :address_postcode,
43
+ presence: true,
44
+ format: { with: /\A[A-Z\d]{1,4} [A-Z\d]{1,3}\z/ }
45
+
46
+ validates :address_town,
47
+ :address_county,
48
+ presence: true
49
+
50
+ validates :free_initial_meeting,
51
+ inclusion: { in: [true, false] }
52
+
53
+ validates :initial_meeting_duration,
54
+ presence: true,
55
+ if: ->{ free_initial_meeting? }
56
+
57
+ validates :initial_advice_fee_structures,
58
+ length: { minimum: 1 }
59
+
60
+ validates :ongoing_advice_fee_structures,
61
+ length: { minimum: 1 }
62
+
63
+ validates :allowed_payment_methods,
64
+ length: { minimum: 1 }
65
+
66
+ validates :minimum_fixed_fee,
67
+ allow_blank: true,
68
+ numericality: { only_integer: true }
69
+
70
+ validate :sum_of_percentages_equals_one_hundred
71
+
72
+ validates :retirement_income_products_percent,
73
+ :pension_transfer_percent,
74
+ :long_term_care_percent,
75
+ :equity_release_percent,
76
+ :inheritance_tax_and_estate_planning_percent,
77
+ :wills_and_probate_percent,
78
+ :other_percent,
79
+ presence: true,
80
+ numericality: { only_integer: true }
81
+
82
+ validates :investment_sizes,
83
+ length: { minimum: 1 }
84
+
85
+ def in_person_advice?
86
+ in_person_advice_methods.present?
87
+ end
88
+
89
+ def subsidiary?
90
+ parent.present?
91
+ end
92
+
93
+ def field_order
94
+ [
95
+ :email_address,
96
+ :telephone_number,
97
+ :address_line_one,
98
+ :address_line_two,
99
+ :address_town,
100
+ :address_county,
101
+ :address_postcode,
102
+ :in_person_advice_methods,
103
+ :free_initial_meeting,
104
+ :initial_meeting_duration,
105
+ :initial_advice_fee_structures,
106
+ :ongoing_advice_fee_structures,
107
+ :allowed_payment_methods,
108
+ :minimum_fixed_fee,
109
+ :percent_total,
110
+ :retirement_income_products_percent,
111
+ :pension_transfer_percent,
112
+ :long_term_care_percent,
113
+ :equity_release_percent,
114
+ :inheritance_tax_and_estate_planning_percent,
115
+ :wills_and_probate_percent,
116
+ :other_percent,
117
+ :investment_sizes
118
+ ]
119
+ end
120
+
121
+ private
122
+
123
+ def upcase_postcode
124
+ address_postcode.upcase! if address_postcode.present?
125
+ end
126
+
127
+ def sum_of_percentages_equals_one_hundred
128
+ total = retirement_income_products_percent.to_i \
129
+ + pension_transfer_percent.to_i \
130
+ + long_term_care_percent.to_i \
131
+ + equity_release_percent.to_i \
132
+ + inheritance_tax_and_estate_planning_percent.to_i \
133
+ + wills_and_probate_percent.to_i \
134
+ + other_percent.to_i
135
+
136
+ unless total == 100
137
+ errors.add(
138
+ :percent_total,
139
+ I18n.t('questionnaire.retirement_advice.percent_total.not_one_hundred')
140
+ )
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,7 @@
1
+ class InPersonAdviceMethod < ActiveRecord::Base
2
+ has_and_belongs_to_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,7 @@
1
+ class InitialAdviceFeeStructure < ActiveRecord::Base
2
+ has_and_belongs_to_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,7 @@
1
+ class InitialMeetingDuration < ActiveRecord::Base
2
+ has_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,7 @@
1
+ class InvestmentSize < ActiveRecord::Base
2
+ has_and_belongs_to_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,10 @@
1
+ module Lookup
2
+ class Adviser < ActiveRecord::Base
3
+ validates_length_of :reference_number, is: 8
4
+ validates_presence_of :name
5
+
6
+ def self.table_name
7
+ "lookup_#{super}"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ module Lookup
2
+ class Firm < ActiveRecord::Base
3
+ has_many :subsidiaries, primary_key: :fca_number, foreign_key: :fca_number
4
+
5
+ validates :fca_number,
6
+ length: { is: 6 },
7
+ numericality: { only_integer: true }
8
+
9
+ def subsidiaries?
10
+ subsidiaries.present?
11
+ end
12
+
13
+ def self.table_name
14
+ "lookup_#{super}"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Lookup
2
+ class Subsidiary < ActiveRecord::Base
3
+ validates :fca_number,
4
+ length: { is: 6 },
5
+ numericality: { only_integer: true }
6
+
7
+ def self.table_name
8
+ "lookup_#{super}"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ class OngoingAdviceFeeStructure < ActiveRecord::Base
2
+ has_and_belongs_to_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,7 @@
1
+ class OtherAdviceMethod < ActiveRecord::Base
2
+ has_and_belongs_to_many :firms
3
+
4
+ validates_presence_of :name
5
+
6
+ default_scope { order(:order) }
7
+ end
@@ -0,0 +1,94 @@
1
+ class Principal < ActiveRecord::Base
2
+ self.primary_key = 'token'
3
+
4
+ before_create :generate_token
5
+ after_create :associate_firm
6
+
7
+ has_one :firm, primary_key: :fca_number, foreign_key: :fca_number
8
+
9
+ validates :fca_number,
10
+ presence: true,
11
+ uniqueness: true,
12
+ length: { is: 6 },
13
+ numericality: { only_integer: true }
14
+
15
+ validates :email_address,
16
+ presence: true,
17
+ uniqueness: true,
18
+ length: { maximum: 50 },
19
+ format: { with: /.+@.+\..+/ }
20
+
21
+ validates :first_name, :last_name, :job_title, presence: true, length: 2..80
22
+
23
+ validates :telephone_number,
24
+ presence: true,
25
+ length: { maximum: 50 },
26
+ format: { with: /\A[0-9 ]+\z/ }
27
+
28
+ validates :website_address,
29
+ allow_blank: true,
30
+ length: { maximum: 100 }
31
+
32
+ validates_acceptance_of :confirmed_disclaimer, accept: true
33
+
34
+ validate :match_fca_number, if: :fca_number?
35
+
36
+ def to_param
37
+ token.parameterize
38
+ end
39
+
40
+ def lookup_firm
41
+ @lookup_firm ||= Lookup::Firm.find_by(fca_number: fca_number)
42
+ end
43
+
44
+ delegate :subsidiaries?, to: :lookup_firm
45
+
46
+ def field_order
47
+ [
48
+ :fca_number,
49
+ :website_address,
50
+ :first_name,
51
+ :last_name,
52
+ :job_title,
53
+ :email_address,
54
+ :telephone_number,
55
+ :confirmed_disclaimer
56
+ ]
57
+ end
58
+
59
+ def find_or_create_subsidiary(id)
60
+ subsidiary = lookup_firm.subsidiaries.find(id)
61
+
62
+ find_subsidiary(subsidiary).tap do |firm|
63
+ firm.save(validate: false) unless firm.persisted?
64
+ end
65
+ end
66
+
67
+ private
68
+
69
+ def find_subsidiary(subsidiary)
70
+ firm.subsidiaries.find_or_initialize_by(
71
+ registered_name: subsidiary.name,
72
+ fca_number: subsidiary.fca_number
73
+ )
74
+ end
75
+
76
+ def associate_firm
77
+ Firm.new(fca_number: lookup_firm.fca_number, registered_name: lookup_firm.registered_name).tap do |f|
78
+ f.save!(validate: false)
79
+ end
80
+ end
81
+
82
+ def match_fca_number
83
+ unless Lookup::Firm.exists?(fca_number: self.fca_number)
84
+ errors.add(
85
+ :fca_number,
86
+ I18n.t('registration.principal.fca_number_un_matched')
87
+ )
88
+ end
89
+ end
90
+
91
+ def generate_token
92
+ self.token = SecureRandom.hex(4)
93
+ end
94
+ end