diff --git a/src/ec/ruc.spec.ts b/src/ec/ruc.spec.ts index c608fcd8..58c4a199 100644 --- a/src/ec/ruc.spec.ts +++ b/src/ec/ruc.spec.ts @@ -1,5 +1,9 @@ import { validate, format } from './ruc'; -import { InvalidLength, InvalidChecksum } from '../exceptions'; +import { + InvalidLength, + InvalidChecksum, + InvalidComponent, +} from '../exceptions'; describe('ec/ruc', () => { it('format:1792060346001', () => { @@ -29,9 +33,30 @@ describe('ec/ruc', () => { expect(result.error).toBeInstanceOf(InvalidLength); }); - it('validate:1792060347-001', () => { - const result = validate('1792060347-001'); + // Company RUCs are issued by SRI without a check-digit algorithm, so + // numbers that fail the historical mod-11 scheme are still valid. + test.each(['1792060347001', '0993381661001', '1760001550001'])( + 'validate:%s', + value => { + const result = validate(value); + + expect(result.isValid).toEqual(true); + }, + ); + + it('validate:0926687851-001', () => { + // Natural-person RUC with an invalid cedula check digit + const result = validate('0926687851-001'); expect(result.error).toBeInstanceOf(InvalidChecksum); }); + + test.each(['0993381661000', '1760001550000', '2593381661001'])( + 'validate-invalid-component:%s', + value => { + const result = validate(value); + + expect(result.error).toBeInstanceOf(InvalidComponent); + }, + ); }); diff --git a/src/ec/ruc.ts b/src/ec/ruc.ts index 76e571c8..971176d9 100644 --- a/src/ec/ruc.ts +++ b/src/ec/ruc.ts @@ -4,15 +4,21 @@ * The RUC is a tax identification number for legal entities. It has 13 digits * where the third digit is a number denoting the type of entity. * - * Source + * Company RUCs (third digit 6 = public, 9 = private/juridical) are issued by + * the SRI without any check-digit algorithm, so only structural checks apply + * to them; the historical mod-11 scheme rejects real registry-valid numbers. + * Cedula-based RUCs (third digit 0-5) do carry the cedula's check digit. * + * Source + * https://minka.gob.ec/mintel/ge/rutr/gobec_forms/-/issues/32 + * https://www.sri.gob.ec/en/web/intersri/consulta-al-ruc * * TAX/VAT */ import * as exceptions from '../exceptions'; import * as ci from './ci'; -import { strings, weightedSum } from '../util'; +import { strings } from '../util'; import { Validator, ValidateReturn } from '../types'; function clean(input: string): ReturnType { @@ -67,39 +73,19 @@ const impl: Validator = { } if (value[2] === '6') { - // Public RUC - const [front, end] = strings.splitAt(value, 9); + // Public RUC: SRI issues these without a check-digit algorithm, so + // only the establishment number is checked. + const [, end] = strings.splitAt(value, 9); if (end === '0000') { return { isValid: false, error: new exceptions.InvalidComponent() }; } - - if ( - weightedSum(front, { - weights: [3, 2, 7, 6, 5, 4, 3, 2, 1], - modulus: 11, - }) !== 0 - ) { - // If it's not a public, try natural - if (end.endsWith('000')) { - return { isValid: false, error: new exceptions.InvalidComponent() }; - } - - return ci.validate(value.substring(0, 10)); - } } else if (value[2] === '9') { - // Juridical RUC - const [front, end] = strings.splitAt(value, 10); + // Juridical RUC: SRI issues these without a check-digit algorithm, so + // only the establishment number is checked. + const [, end] = strings.splitAt(value, 10); if (end === '000') { return { isValid: false, error: new exceptions.InvalidComponent() }; } - if ( - weightedSum(front, { - weights: [4, 3, 2, 7, 6, 5, 4, 3, 2, 1], - modulus: 11, - }) !== 0 - ) { - return { isValid: false, error: new exceptions.InvalidChecksum() }; - } } else { return { isValid: false, error: new exceptions.InvalidComponent() }; }