diff --git a/.rubocop.yml b/.rubocop.yml index d913572..caf4cdf 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,8 +5,13 @@ inherit_gem: - default.yml plugins: + - rubocop-rake + - rubocop-rspec - rubocop-rspec_rails +AllCops: + TargetRubyVersion: 3.2 + Lint/EmptyClass: AllowComments: true diff --git a/README.md b/README.md index 4f1e0ab..32bb7ad 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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 diff --git a/config/locales/defra_ruby/validators/companies_house_number_validator/en.yml b/config/locales/defra_ruby/validators/companies_house_number_validator/en.yml index 13f8964..9b95a7d 100644 --- a/config/locales/defra_ruby/validators/companies_house_number_validator/en.yml +++ b/config/locales/defra_ruby/validators/companies_house_number_validator/en.yml @@ -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. diff --git a/lib/defra_ruby/validators/companies_house_number_validator.rb b/lib/defra_ruby/validators/companies_house_number_validator.rb index 6d88ab0..aabfb60 100644 --- a/lib/defra_ruby/validators/companies_house_number_validator.rb +++ b/lib/defra_ruby/validators/companies_house_number_validator.rb @@ -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 @@ -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 diff --git a/lib/defra_ruby/validators/companies_house_service.rb b/lib/defra_ruby/validators/companies_house_service.rb index 48e625c..7692d9b 100644 --- a/lib/defra_ruby/validators/companies_house_service.rb +++ b/lib/defra_ruby/validators/companies_house_service.rb @@ -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 @@ -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) @@ -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 diff --git a/spec/defra_ruby/validators/companies_house_number_validator_spec.rb b/spec/defra_ruby/validators/companies_house_number_validator_spec.rb index f24662f..4c95c04 100644 --- a/spec/defra_ruby/validators/companies_house_number_validator_spec.rb +++ b/spec/defra_ruby/validators/companies_house_number_validator_spec.rb @@ -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 @@ -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 @@ -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 diff --git a/spec/defra_ruby/validators/companies_house_service_spec.rb b/spec/defra_ruby/validators/companies_house_service_spec.rb index 7ad7a37..100e82b 100644 --- a/spec/defra_ruby/validators/companies_house_service_spec.rb +++ b/spec/defra_ruby/validators/companies_house_service_spec.rb @@ -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:) }