Skip to content

Commit

Permalink
Merge pull request #1 from bayasdev/add-is-ruc
Browse files Browse the repository at this point in the history
Add isRUC validator
  • Loading branch information
bayasdev committed Sep 14, 2024
2 parents 029de44 + 8bbd13d commit 846b26c
Show file tree
Hide file tree
Showing 8 changed files with 200 additions and 54 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# validator-ec

**validator-ec** es una colección de validadores de datos para Ecuador desarrollada en TypeScript. Actualmente, solo cuenta con un validador para cédulas de identidad ecuatorianas pero se espera agregar más validadores en el futuro.
**validator-ec** es una colección de validadores de datos para Ecuador desarrollada en TypeScript. Actualmente, cuenta con validadores para cédulas de identidad ecuatorianas y números de RUC (Registro Único de Contribuyentes).

## Características

Expand Down Expand Up @@ -32,9 +32,10 @@ npm install validator-ec

## Validadores

| Validador | Descripción | Ejemplo |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------ |
| `isCedula(cedula: string): boolean` | Valida una cédula ecuatoriana de identidad. Devuelve `true` si la cédula es válida, de lo contrario `false`. | `isCedula('1710034065')` |
| Validador | Descripción | Ejemplo |
| ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------------------------ |
| `isCedula(cedula: string): boolean` | Valida una cédula de identidad ecuatoriana. Devuelve `true` si la cédula es válida, de lo contrario `false`. | `isCedula('1710034065')` |
| `isRUC(ruc: string): boolean` | Valida un número de RUC (Registro Único de Contribuyentes). Devuelve `true` si el RUC es válido, de lo contrario `false`. | `isRUC('1790016919001')` |

## Uso

Expand Down
15 changes: 12 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "validator-ec",
"version": "1.0.3",
"version": "1.1.0",
"type": "module",
"author": "Victor Bayas",
"license": "Apache-2.0",
Expand All @@ -11,11 +11,19 @@
"bugs": {
"url": "https://github.com/bayasdev/validator-ec/issues"
},
"keywords": [
"validator",
"validador",
"ecuador",
"cedula",
"ci",
"identidad",
"ruc"
],
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts",
"exports": {
"./package.json": "./package.json",
".": {
"import": {
"types": "./dist/index.d.ts",
Expand All @@ -25,7 +33,8 @@
"types": "./dist/index.d.cts",
"default": "./dist/index.cjs"
}
}
},
"./package.json": "./package.json"
},
"files": [
"dist"
Expand Down
4 changes: 4 additions & 0 deletions src/helpers/isDigits.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
// Helper para validar que una cadena contenga únicamente dígitos.
export default function isDigits(value: string): boolean {
return /^\d+$/.test(value);
}
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import isCedula from "./lib/isCedula";
import isRUC from "./lib/isRUC";

export { isCedula };
export { isCedula, isRUC };
93 changes: 50 additions & 43 deletions src/lib/isCedula.ts
Original file line number Diff line number Diff line change
@@ -1,55 +1,62 @@
export default function isCedula(cedula: string): boolean {
// Validamos que la cédula solo contenga 10 dígitos
if (cedula.length === 10) {
// Definimos el último dígito o tambien llamado dígito verificador
const lastDigit = parseInt(cedula[cedula.length - 1], 10);

// Definimos las variables a utilizar
let evens = 0;
let odds = 0;
let sum = 0;

// Iteramos cada item excluyendo el último digito, aplicando el Algoritmo de Luhn
for (let i = 1; i <= cedula.length - 1; i++) {
if (i % 2 === 0) {
evens += parseInt(cedula[i - 1]);
} else {
let x = parseInt(cedula[i - 1]) * 2;
x > 9 ? (odds += x - 9) : (odds += x);
}
}
import isDigits from "../helpers/isDigits";

sum += evens + odds;
export default function isCedula(cedula: string): boolean {
// Validamos que la cédula tenga 10 dígitos
if (cedula.length !== 10) {
return false;
}

// Extraemos el primer digito de la suma
const firstDigit = parseInt(sum.toString()[0], 10);
// Verificamos que todos los caracteres sean dígitos
if (!isDigits(cedula)) {
return false;
}

// Obtenemos la decena
const dozen = (firstDigit + 1) * 10;
// Definimos el último dígito o tambien llamado dígito verificador
const lastDigit = parseInt(cedula[cedula.length - 1], 10);

// Obtenemos el dígito validador
let validatorDigit = dozen - sum;
// Definimos las variables a utilizar
let evens = 0;
let odds = 0;
let sum = 0;

// Si el dígito verificador es mayor a 10 lo igualamos a 0
if (validatorDigit >= 10) {
validatorDigit = 0;
// Iteramos cada item excluyendo el último digito, aplicando el Algoritmo de Luhn
for (let i = 1; i <= cedula.length - 1; i++) {
if (i % 2 === 0) {
evens += parseInt(cedula[i - 1]);
} else {
let x = parseInt(cedula[i - 1]) * 2;
x > 9 ? (odds += x - 9) : (odds += x);
}
}

// Codigo de provincia
// Validamos si la cedula pertenece a alguna provincia
const provinceCode = parseInt(cedula[0] + cedula[1]);
sum += evens + odds;

// Valida cédulas locales y de Ecuatorianos en el exterior
if (provinceCode > 24 && provinceCode != 30) {
return false;
}
// Extraemos el primer digito de la suma
const firstDigit = parseInt(sum.toString()[0], 10);

if (validatorDigit == lastDigit) {
return true;
} else {
return false;
}
} else {
// Obtenemos la decena
const dozen = (firstDigit + 1) * 10;

// Obtenemos el dígito validador
let validatorDigit = dozen - sum;

// Si el dígito verificador es mayor a 10 lo igualamos a 0
if (validatorDigit >= 10) {
validatorDigit = 0;
}

// Codigo de provincia
// Validamos si la cedula pertenece a alguna provincia
const provinceCode = parseInt(cedula[0] + cedula[1]);

// Valida cédulas locales y de Ecuatorianos en el exterior
if (provinceCode > 24 && provinceCode != 30) {
return false;
}

if (validatorDigit == lastDigit) {
return true;
}

return false;
}
86 changes: 86 additions & 0 deletions src/lib/isRUC.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import isDigits from "../helpers/isDigits";
import isCedula from "./isCedula";

export default function isRUC(ruc: string): boolean {
// Verificamos que el RUC tenga 13 dígitos
if (ruc.length !== 13) {
return false;
}

// Verificamos que todos los caracteres sean dígitos
if (!isDigits(ruc)) {
return false;
}

// Extraemos los primeros 10 dígitos (cédula o código base)
const baseCode = ruc.substring(0, 10);

// Extraemos los últimos 3 dígitos (código de establecimiento)
const establishmentCode = ruc.substring(10, 13);

// Verificamos que el código de establecimiento sea 001
if (establishmentCode !== "001") {
return false;
}

// Obtenemos el tercer dígito para determinar el tipo de entidad
// Fuente: https://www.sri.gob.ec/ruc-personas-naturales
const thirdDigit = parseInt(ruc[2], 10);

// Validación para personas naturales (tercer dígito entre 0 y 5)
if (thirdDigit >= 0 && thirdDigit <= 5) {
// Validamos que los primeros 10 dígitos sean una cédula válida
return isCedula(baseCode);
}

// Validación para personas jurídicas (tercer dígito es 9)
if (thirdDigit === 9) {
return validateLegalEntityRUC(ruc);
}

// Validación para entidades públicas (tercer dígito es 6)
if (thirdDigit === 6) {
return validatePublicEntityRUC(ruc);
}

// Si el tercer dígito no es válido, el RUC es inválido
return false;
}

// Función para validar RUC de personas jurídicas
function validateLegalEntityRUC(ruc: string): boolean {
// Coeficientes para personas jurídicas
const coefficients = [4, 3, 2, 7, 6, 5, 4, 3, 2];
const baseCode = ruc.substring(0, 9);
const validatorDigit = parseInt(ruc[9], 10);

let sum = 0;
for (let i = 0; i < coefficients.length; i++) {
const value = parseInt(baseCode[i], 10) * coefficients[i];
sum += value;
}

const remainder = sum % 11;
const result = remainder === 0 ? 0 : 11 - remainder;

return result === validatorDigit;
}

// Función para validar RUC de entidades públicas
function validatePublicEntityRUC(ruc: string): boolean {
// Coeficientes para entidades públicas
const coefficients = [3, 2, 7, 6, 5, 4, 3, 2];
const baseCode = ruc.substring(0, 8);
const validatorDigit = parseInt(ruc[8], 10);

let sum = 0;
for (let i = 0; i < coefficients.length; i++) {
const value = parseInt(baseCode[i], 10) * coefficients[i];
sum += value;
}

const remainder = sum % 11;
const result = remainder === 0 ? 0 : 11 - remainder;

return result === validatorDigit;
}
41 changes: 39 additions & 2 deletions tests/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { describe, it, expect } from "vitest";
import { isCedula } from "../src";
import { isCedula, isRUC } from "../src";

describe("Función isCedula", () => {
describe("Validador isCedula", () => {
it("debería retornar true para una cédula válida", () => {
expect(isCedula("0151314416")).toBe(true); // Provincia 01 (Azuay)
expect(isCedula("0258192483")).toBe(true); // Provincia 02 (Bolívar)
Expand Down Expand Up @@ -62,3 +62,40 @@ describe("Función isCedula", () => {
expect(isCedula("1710034069")).toBe(false);
});
});

describe("Validador isRUC", () => {
it("debería retornar true para un RUC válido de persona natural", () => {
expect(isRUC("1710034065001")).toBe(true);
});

it("debería retornar true para un RUC válido de persona jurídica", () => {
expect(isRUC("1790016919001")).toBe(true);
expect(isRUC("0990004196001")).toBe(true);
expect(isRUC("0190072002001")).toBe(true);
expect(isRUC("1390012949001")).toBe(true);
});

it("debería retornar true para un RUC válido de entidad pública", () => {
expect(isRUC("1760001550001")).toBe(true);
});

it("debería retornar false para un RUC inválido", () => {
expect(isRUC("1710034065000")).toBe(false);
expect(isRUC("0990001099000")).toBe(false);
expect(isRUC("1760001550000")).toBe(false);
});

it("debería retornar false si el RUC no tiene 13 dígitos", () => {
expect(isRUC("17100340650")).toBe(false);
expect(isRUC("17100340650011")).toBe(false);
});

it("debería retornar false para RUCs con caracteres no numéricos", () => {
expect(isRUC("17A0034065001")).toBe(false);
expect(isRUC("17100X4065001")).toBe(false);
});

it("debería retornar false si el código de establecimiento no es 001", () => {
expect(isRUC("1710034065000")).toBe(false);
});
});
3 changes: 2 additions & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,5 +23,6 @@
"noUnusedLocals": false,
"noUnusedParameters": false,
"noPropertyAccessFromIndexSignature": false
}
},
"exclude": ["dist"]
}

0 comments on commit 846b26c

Please sign in to comment.