diff --git a/app/validators/siret_format_validator.rb b/app/validators/siret_format_validator.rb index c82b01f20..9bb9c3c7e 100644 --- a/app/validators/siret_format_validator.rb +++ b/app/validators/siret_format_validator.rb @@ -1,22 +1,35 @@ class SiretFormatValidator < ActiveModel::EachValidator - def validate_each(record,attribute,value) - if !(value =~ /^\d{14}$/) + def validate_each(record, attribute, value) + if !format_is_valid(value) record.errors.add(attribute, :format) end - if value.nil? || (luhn_checksum(value) % 10 != 0) + + if !luhn_passed(value) record.errors.add(attribute, :checksum) end end private + LA_POSTE_SIREN = '356000000' + + def format_is_valid(value) + value.match?(/^\d{14}$/) + end + + def luhn_passed(value) + # Do not enforce Luhn for La Poste SIRET numbers, the only exception to this rule + siret_is_attached_to_la_poste(value) || (luhn_checksum(value) % 10 == 0) + end + + def siret_is_attached_to_la_poste(value) + value[0..8] == LA_POSTE_SIREN + end + def luhn_checksum(value) - accum = 0 - value.reverse.each_char.map(&:to_i).each_with_index do |digit, index| + value.reverse.each_char.map(&:to_i).map.with_index do |digit, index| t = index.even? ? digit : digit * 2 - t = t - 9 if t >= 10 - accum += t - end - accum + t < 10 ? t : t - 9 + end.sum end end diff --git a/spec/models/siret_spec.rb b/spec/models/siret_spec.rb index c5d197660..579c2b916 100644 --- a/spec/models/siret_spec.rb +++ b/spec/models/siret_spec.rb @@ -1,26 +1,47 @@ require 'spec_helper' describe Siret, type: :model do - let(:valid_siret) { '41816609600051' } - let(:invalid_siret) { '111111111' } + subject { Siret.new(siret: siret) } context 'with no siret provided' do - it { is_expected.to validate_presence_of(:siret) } + let(:siret) { '' } + + it { is_expected.to be_invalid } end - context 'init with valid siret' do - it { is_expected.to allow_value(valid_siret).for(:siret) } + context 'with a siret that contains letters' do + let(:siret) { 'A1B1C6D9E0F0G1' } + + it { is_expected.to be_invalid } end - context 'init with invalid siret' do - it { is_expected.not_to allow_value(invalid_siret).for(:siret) } + context 'with a siret that is too short' do + let(:siret) { '1234567890' } + + it { is_expected.to be_invalid } end - context 'init with bullshit siret' do - it { is_expected.not_to allow_value('bullshit').for(:siret) } + context 'with a siret that is too long' do + let(:siret) { '12345678901234567890' } + + it { is_expected.to be_invalid } end - context 'init with a siret that is too long' do - it { is_expected.not_to allow_value('9' * 15).for(:siret) } + context 'with a lunh-invalid siret' do + let(:siret) { '41816609600052' } + + it { is_expected.to be_invalid } + end + + context 'with a lunh-invalid La Poste siret' do + let(:siret) { '35600000018723' } + + it { is_expected.to be_valid } + end + + context 'with a valid siret' do + let(:siret) { '41816609600051' } + + it { is_expected.to be_valid } end end