Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,13 @@ inherit_gem:
- default.yml

plugins:
- rubocop-rake
- rubocop-rspec
- rubocop-rspec_rails

AllCops:
TargetRubyVersion: 3.2

Lint/EmptyClass:
AllowComments: true

Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,10 @@ This validator checks the company registration number provided. Specifically it
If the format is valid, it then makes a call to Companies House to validate that the number

- is recognised
- belongs to an 'active' company
- belongs to a company with an allowed status

A company can be in various states for example liquidation, which means for the purposes of Defra its not a valid entity. So we check the status along with the number as part of the validation.
Allowed statuses default to `active` and `voluntary-arrangement`, preserving the existing behaviour.
Services can override this when they need to accept other Companies House statuses.

Add it to your model or form object using

Expand All @@ -79,6 +80,14 @@ The company number also accepts a `company_type` option. This checks the `type`
validates :company_no, "defra_ruby/validators/companies_house_number": { company_type: "ltd" }
```

The allowed Companies House statuses can be changed with the `permitted_statuses` option:

```ruby
validates :company_no, "defra_ruby/validators/companies_house_number": {
permitted_statuses: %i[active voluntary-arrangement liquidation]
}
```

Note: to mock the Companies House service with the `company_type` option, you will need to be running at least v2.2.0 of [defra-ruby-mocks](https://github.com/DEFRA/defra-ruby-mocks#companies-house).

### Email
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ en:
not_found: Companies House couldn't find a company with this number
inactive: Your company must be registered as an active company
unsupported_company_type: Your company must be registered as a limited company or a limited liability partnership
argument_error: Invalid permitted_types value. It must be nil, a string or an array of strings.
argument_error: Invalid permitted_types or permitted_statuses value. It must be nil, a string or an array.
error: There was an error connecting with Companies House. Hopefully this is a one off and will work if you try again.
7 changes: 4 additions & 3 deletions lib/defra_ruby/validators/companies_house_number_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ def validate_each(record, attribute, value)
return false unless format_is_valid?(record, attribute, value)

permitted_types = options[:permitted_types]
validate_with_companies_house(record, attribute, value, permitted_types)
permitted_statuses = options[:permitted_statuses]
validate_with_companies_house(record, attribute, value, permitted_types, permitted_statuses)
end

private
Expand All @@ -37,8 +38,8 @@ def format_is_valid?(record, attribute, value)
false
end

def validate_with_companies_house(record, attribute, value, permitted_types)
case CompaniesHouseService.new(company_number: value, permitted_types:).status
def validate_with_companies_house(record, attribute, value, permitted_types, permitted_statuses)
case CompaniesHouseService.new(company_number: value, permitted_types:, permitted_statuses:).status
when :active
true
when :inactive
Expand Down
32 changes: 26 additions & 6 deletions lib/defra_ruby/validators/companies_house_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,18 @@
module DefraRuby
module Validators
class CompaniesHouseService
def initialize(company_number:, permitted_types: nil)
DEFAULT_PERMITTED_STATUSES = %i[active voluntary-arrangement].freeze
ARGUMENT_ERROR_TRANSLATION_KEY = "defra_ruby.validators.CompaniesHouseNumberValidator.argument_error"

private_constant :ARGUMENT_ERROR_TRANSLATION_KEY

def initialize(company_number:, permitted_types: nil, permitted_statuses: nil)
@company_number = company_number
@permitted_types = permitted_types
@permitted_statuses = permitted_statuses

validate_permitted_types
validate_permitted_statuses
end

def status
Expand All @@ -27,15 +34,28 @@ def companies_house_response
end

def validate_permitted_types
return if @permitted_types.nil?
return if valid_permitted_option?(@permitted_types)

raise ArgumentError, I18n.t(ARGUMENT_ERROR_TRANSLATION_KEY)
end

return if @permitted_types.is_a?(String) || @permitted_types.is_a?(Array)
def validate_permitted_statuses
return if valid_permitted_option?(@permitted_statuses)

raise ArgumentError, I18n.t("defra_ruby.validators.CompaniesHouseNumberValidator.argument_error")
raise ArgumentError, I18n.t(ARGUMENT_ERROR_TRANSLATION_KEY)
end

def valid_permitted_option?(option)
option.nil? || option.is_a?(String) || option.is_a?(Array)
end

def status_is_allowed?(companies_house_response)
%i[active voluntary-arrangement].include?(companies_house_response[:company_status])
normalised_permitted_statuses.include?(companies_house_response[:company_status].to_s.to_sym)
end

def normalised_permitted_statuses
@permitted_statuses ||= DEFAULT_PERMITTED_STATUSES
Array(@permitted_statuses).map { |status| status.to_s.to_sym }
end

def company_type_is_allowed?(companies_house_response)
Expand All @@ -48,7 +68,7 @@ def company_type_is_allowed?(companies_house_response)
when Array
@permitted_types.include?(companies_house_response[:company_type].to_s)
else
raise ArgumentError, I18n.t("defra_ruby.validators.CompaniesHouseNumberValidator.argument_error")
raise ArgumentError, I18n.t(ARGUMENT_ERROR_TRANSLATION_KEY)
end
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ module Test
validates :company_no, "defra_ruby/validators/companies_house_number": { permitted_types: %w[llp ltd] }
end

CompaniesHousePermittedStatusesAndNumberValidatable = Struct.new(:company_no) do
include ActiveModel::Validations

validates :company_no, "defra_ruby/validators/companies_house_number": {
permitted_statuses: %i[active voluntary-arrangement liquidation]
}
end

CompaniesHouseInvalidPermittedTypesAndNumberValidatable = Struct.new(:company_no) do
include ActiveModel::Validations

Expand Down Expand Up @@ -137,7 +145,7 @@ module Validators
Test::CompaniesHouseSinglePermittedTypeAndNumberValidatable.new(valid_numbers.first).valid?

expect(CompaniesHouseService).to have_received(:new)
.with(company_number: valid_numbers.first, permitted_types: "ltd")
.with(company_number: valid_numbers.first, permitted_types: "ltd", permitted_statuses: nil)
end
end

Expand All @@ -148,7 +156,18 @@ module Validators
Test::CompaniesHouseMultiplePermittedTypesAndNumberValidatable.new(valid_numbers.first).valid?

expect(CompaniesHouseService).to have_received(:new)
.with(company_number: valid_numbers.first, permitted_types:)
.with(company_number: valid_numbers.first, permitted_types:, permitted_statuses: nil)
end
end

context "with multiple permitted_statuses options" do
let(:permitted_statuses) { %i[active voluntary-arrangement liquidation] }

it "calls the companies house service with all permitted statuses" do
Test::CompaniesHousePermittedStatusesAndNumberValidatable.new(valid_numbers.first).valid?

expect(CompaniesHouseService).to have_received(:new)
.with(company_number: valid_numbers.first, permitted_types: nil, permitted_statuses:)
end
end

Expand Down
41 changes: 41 additions & 0 deletions spec/defra_ruby/validators/companies_house_service_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,47 @@
end
end

context "when the company_number has a liquidation status" do
let(:company_status) { :liquidation }

it "returns :inactive by default" do
expect(companies_house_service.status).to eq(:inactive)
end
end

context "when checking the company_status" do
subject(:companies_house_service) { described_class.new(company_number: "09360070", permitted_statuses:) }

context "when an invalid permitted company statuses value is specified" do
let(:permitted_statuses) { 0 }

it { expect { companies_house_service.status }.to raise_error(ArgumentError) }
end

context "when a single permitted company status is specified" do
let(:permitted_statuses) { "liquidation" }
let(:company_status) { :liquidation }

it { expect(companies_house_service.status).to eq(:active) }
end

context "when multiple permitted company statuses are specified" do
let(:permitted_statuses) { %i[active voluntary-arrangement liquidation] }

context "when the actual status is liquidation" do
let(:company_status) { :liquidation }

it { expect(companies_house_service.status).to eq(:active) }
end

context "when the actual status is dissolved" do
let(:company_status) { :dissolved }

it { expect(companies_house_service.status).to eq(:inactive) }
end
end
end

context "when checking the company_type" do
subject(:companies_house_service) { described_class.new(company_number: "09360070", permitted_types:) }

Expand Down
Loading