Skip to content

Commit

Permalink
v2.0.0: options, forbidden words, valid RFC.
Browse files Browse the repository at this point in the history
  • Loading branch information
manuelmhtr committed Jul 8, 2021
1 parent 59414aa commit 0936069
Show file tree
Hide file tree
Showing 17 changed files with 2,457 additions and 234 deletions.
12 changes: 2 additions & 10 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,4 @@
# These are supported funding model platforms

github: # manuelmhtr
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
custom: "https://www.paypal.me/manuelmhtr"
github: manuelmhtr
custom: # "https://www.paypal.me/manuelmhtr"
15 changes: 15 additions & 0 deletions .github/workflows/purge-cache.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: Purge CDN cache

on:
push:
branches:
- master

jobs:
purge-cache:
runs-on: ubuntu-latest
steps:
- name: curl
uses: wei/curl@v1
with:
args: https://purge.jsdelivr.net/gh/manuelmhtr/validate-rfc@latest/dist/index.js
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Changelog
All notable changes to this project will be documented in this file.


## [2.0.0] - 2021-06-12
### Added
- Accepts an optional second parameter `options`.
- Accepts the options `omitVerificationDigit` to skip the `INVALID_VERIFICATION_DIGIT` error validation.
- Adds the `FORBIDDEN_WORD` error that validates the RFC does not contain one of the "inconvenient" words.
- Add the `valid-rfc.json` list with the list of valid RFCs by the SAT that does not comply with the "verification digit" rule.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
The MIT License (MIT)

Copyright (c) 2015 Gustavo Ortiz
Copyright (c) 2021 Manuel de la Torre

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
46 changes: 41 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,34 @@ Una librería sencilla y ligera para validar [RFCs Mexicanos](https://es.wikiped

## Instalación

### NodeJS

Usa NPM o YARN:

```shell
$ npm install validate-rfc
$ npm install --save validate-rfc
```

### Navegador

## API
Añade el siguiente script a tu proyecto:

```html
<!-- última versión -->
<script src="https://cdn.jsdelivr.net/gh/manuelmhtr/validate-rfc@latest/dist/index.js" type="text/javascript"></script>

<!-- O especifica una versión -->
<script src="https://cdn.jsdelivr.net/gh/manuelmhtr/[email protected]/dist/index.js" type="text/javascript"></script>

<!-- Esto va a exportar una función "validateRfc": -->
<script type="text/javascript">
var data = validateRfc('mhtr93041179a');
console.log(data);
</script>
```


## API

La librería expone una sola función (`.validateRfc`).

Expand All @@ -33,8 +52,8 @@ Valida que un string sea un RFC válido y entrega detalles sobre la validación.
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
|`rfc`|String|El RFC a validar.|
|`options`|Object| Opciones de la función (Opcional).|
|`options.omitVerificationDigit`|Boolean| Si esta opción es `true`, el dígito verificatdor no será validado. (Default: `false`).|
|`options`|Object| Parámetros de configuración (Opcional).|
|`options.omitVerificationDigit`|Boolean|Si esta opción es `true`, se omite la validación del dígito verificador (Default: `false`).|


**Respuesta**
Expand All @@ -45,7 +64,7 @@ Regresa un objeto plano con los siguientes valores:
| Parámetro | Tipo | Descripción |
| --------- | ---- | ----------- |
|`isValid`|Boolean|Indica si el string ingresado es un RFC válido.|
|`rfc`|String|El RFC formateado (en mayúsculas, sin espacios ni simbolos). Regresa `null` en caso de que el RFC sea inválido.|
|`rfc`|String|El RFC formateado (en mayúsculas, sin espacios ni símbolos). Regresa `null` en caso de que el RFC sea inválido.|
|`type`|String|El tipo del RFC ingresado. Los valores pueden ser `person` para personas físicas, `company` para personas morales, `generic` para el RFC genérico "XAXX010101000" o `foreign` para el RFC "XEXX010101000" para residentes en el extranjero. Regresa `null` en caso de que el RFC sea inválido.|
|`errors`|Array[String]|En caso de que el RFC no sea válido, aquí se indican los motivos por los que no fue válido.|

Expand All @@ -57,6 +76,7 @@ Los posibles valores que puede contener `errors` y su descripción son:
|`INVALID_FORMAT`|El formato es inválido, es decir, no cuenta con la longitud o estructura de caracteres esperado. Ej: `XYZ` porque claramente no es un RFC. |
|`INVALID_DATE`|El string tiene el formato adecuado, pero los dígitos para la fecha generan una fecha inválida. Ej: `MHTR815511A70` porque refiere al mes `55`.|
|`INVALID_VERIFICATION_DIGIT`|El string tiene el formato adecuado, pero el último caracter (dígito verificador) es inválido. Ej: `MHTR810511A79` termina en `9` pero se espera que termine en `2`.|
|`FORBIDDEN_WORD`|El string contiene una de las [palabras inconvenientes](https://solucionfactible.com/sfic/resources/files/palabrasInconvenientes-rfc.pdf) que no pueden formar un RFC. Ej: `FETO930411792` las iniciales forman la palabra `FETO`.|


**Ejemplo**
Expand Down Expand Up @@ -103,6 +123,22 @@ Para correr las pruebas ejecuta el comando:
$ npm test
```

## Problemas conocidos

### Discrepancias en el dígito verificador

Algunos RFC registrados ante el SAT no pasan la validación del dígito verificador. Por ejemplo el RFC `LME060822IH5` es válido y susceptible a recibir facturas según el [validador oficial del SAT](https://agsc.siat.sat.gob.mx/PTSC/ValidaRFC/index.jsf), sin embargo al calcular su dígito verificador este debería terminar en `3` en lugar de `5`. Por lo tanto en estos casos la librería retorna `isValid` como `false` y el error `INVALID_VERIFICATION_DIGIT`.

Soluciones:

1. Si encuentras un caso en particular agrega el RFC a la [lista de RFCs válidos](/src/valid-rfcs.json). En **mayúsculas y en orden alfabético**. Al agregarlo a la lista ese RFC ayudas a documentar estos casos y evitar que regrese error.
2. Utiliza la opción `omitVerificationDigit` para ignorar la validación del dígito verificador. Tiene la ventaja de que es una solución más rápida y cubre todos lo casos; pero va a regresar como válidos muchos RFC que no están permitidos.

Referencias:
- [Issue #10](https://github.com/manuelmhtr/validate-rfc/issues/10)
- https://es.stackoverflow.com/questions/140420/validacion-de-rfc-con-errores


## Relacionado

Además de validar el formato, necesitas validar que un RFC esté registrado en el SAT o no esté en una lista negra? Prueba con [Verifier](https://rapidapi.com/manuelmhtr/api/verifier).
Expand Down
2 changes: 2 additions & 0 deletions dist/index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions dist/index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 21 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
{
"name": "validate-rfc",
"version": "1.0.4",
"version": "2.0.0",
"description": "A simple library to validate mexican RFCs",
"main": "src/index.js",
"main": "dist/index.js",
"scripts": {
"build": "./node_modules/rollup/dist/bin/rollup -c",
"test": "mocha -r test/index.js test/unit/"
},
"repository": {
Expand All @@ -27,9 +28,25 @@
"url": "https://github.com/manuelmhtr/validate-rfc/issues"
},
"homepage": "https://github.com/manuelmhtr/validate-rfc#readme",
"mocha": {
"ui": "bdd",
"timeout": 1000,
"fullTrace": true,
"recursive": true,
"exit": true,
"bail": true
},
"devDependencies": {
"chai": "^4.3.0",
"mocha": "^8.2.1"
"@babel/cli": "^7.14.5",
"@babel/core": "^7.14.5",
"@babel/preset-env": "^7.14.5",
"@rollup/plugin-babel": "^5.3.0",
"@rollup/plugin-commonjs": "^19.0.0",
"@rollup/plugin-json": "^4.1.0",
"chai": "^4.3.4",
"mocha": "^9.0.0",
"rollup": "^2.51.2",
"rollup-plugin-uglify": "^6.0.4"
},
"dependencies": {}
}
23 changes: 23 additions & 0 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import babel from '@rollup/plugin-babel';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import { uglify } from "rollup-plugin-uglify";

export default {
input: 'src/index.js',
output: {
file: 'dist/index.js',
format: 'umd',
name: 'validateRfc',
sourcemap: true
},
plugins: [
commonjs(),
json(),
babel({
babelHelpers: 'bundled',
presets: ['@babel/env']
}),
uglify()
],
};
43 changes: 43 additions & 0 deletions src/forbidden-words.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
[
"BUEI",
"BUEY",
"CACA",
"CACO",
"CAGA",
"CAGO",
"CAKA",
"CAKO",
"COGE",
"COJA",
"COJE",
"COJI",
"COJO",
"CULO",
"FETO",
"GUEY",
"JOTO",
"KACA",
"KACO",
"KAGA",
"KAGO",
"KOGE",
"KOJO",
"KAKA",
"KULO",
"MAME",
"MAMO",
"MEAR",
"MEAS",
"MEON",
"MION",
"MOCO",
"MULA",
"PEDA",
"PEDO",
"PENE",
"PUTA",
"PUTO",
"QULO",
"RATA",
"RUIN"
]
2 changes: 1 addition & 1 deletion src/get-verification-digit.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ module.exports = (input) => {
const rfc = input.length === 12 ? ` ${input}` : input;
const base = rfc.slice(0, -1);
const score = getScore(base);
const mod = 11 - score % 11;
const mod = (11000 - score) % 11;
if (mod === 11) return '0';
if (mod === 10) return 'A';
return String(mod);
Expand Down
4 changes: 2 additions & 2 deletions src/index.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export default function validateRfc(rfc: string, options?: { omitVerificationDigit?: boolean }): {
export default function validateRfc(rfc: string, options?: { strict?: boolean }): {
isValid: boolean,
type?: 'company' | 'person' | 'foreign' | 'generic',
rfc?: string,
errors?: ('INVALID_FORMAT' | 'INVALID_DATE' | 'INVALID_VERIFICATION_DIGIT')[]
errors?: ('INVALID_FORMAT' | 'INVALID_DATE' | 'INVALID_VERIFICATION_DIGIT' | 'FORBIDDEN_WORD')[]
};
26 changes: 18 additions & 8 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
const getVerificationDigit = require('./get-verification-digit');
const forbiddenWords = require('./forbidden-words.json');
const validRfcs = require('./valid-rfcs.json');

const RFC_REGEXP = /^([A-ZÑ\x26]{3,4})([0-9]{6})([A-Z0-9]{3})$/;
const INVALID_FORMAT_ERROR = 'INVALID_FORMAT';
const INVALID_DATE_ERROR = 'INVALID_DATE';
const INVALID_VERIFICATION_DIGIT_ERROR = 'INVALID_VERIFICATION_DIGIT';
const FORBIDDEN_WORD_ERROR = 'FORBIDDEN_WORD';
const RFC_TYPE_FOR_LENGTH = {
'12': 'company',
'13': 'person'
Expand All @@ -30,26 +33,33 @@ const validateDate = (rfc) => {
};

const validateVerificationDigit = (rfc) => {
const expected = rfc.slice(-1);
const digit = getVerificationDigit(rfc);
const digit = rfc.slice(-1);
const expected = getVerificationDigit(rfc);
return expected === digit;
};

const validate = (rfc, options = {}) => {
if (isSpecialCase(rfc)) return [];
const hasForbiddenWords = (rfc) => {
const prefix = (rfc || '').slice(0, 4);
return forbiddenWords.includes(prefix);
};

const validate = (rfc, { omitVerificationDigit } = {}) => {
if (isSpecialCase(rfc) || isValidCase(rfc)) return [];
const errors = [];
const skipDigit = options.omitVerificationDigit;
const hasValidFormat = RFC_REGEXP.test(rfc);
const hasValidDate = hasValidFormat ? validateDate(rfc) : true;
const hasValidDigit = hasValidFormat ? validateVerificationDigit(rfc) : true;
if (!hasValidFormat) errors.push(INVALID_FORMAT_ERROR);
if (!hasValidDate) errors.push(INVALID_DATE_ERROR);
if (!hasValidDigit && !skipDigit) errors.push(INVALID_VERIFICATION_DIGIT_ERROR);
if (!hasValidDigit && !omitVerificationDigit) errors.push(INVALID_VERIFICATION_DIGIT_ERROR);
if (hasForbiddenWords(rfc)) errors.push(FORBIDDEN_WORD_ERROR);
return errors;
};

const isSpecialCase = (rfc) => rfc in SPECIAL_CASES;

const isValidCase = (rfc) => validRfcs.includes(rfc);

const getType = (rfc) => SPECIAL_CASES[rfc] || RFC_TYPE_FOR_LENGTH[rfc.length] || null;

const getValidResponse = (rfc) => ({
Expand All @@ -65,9 +75,9 @@ const getInvalidResponse = (errors) => ({
errors
});

module.exports = (input, strict = false) => {
module.exports = (input, options) => {
const rfc = parseInput(input);
const errors = validate(rfc, strict);
const errors = validate(rfc, options);
const isValid = errors.length === 0;

return isValid ? getValidResponse(rfc) : getInvalidResponse(errors);
Expand Down
7 changes: 7 additions & 0 deletions src/valid-rfcs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
"DAA020218JY1",
"EDG811007RB3",
"LIM0011098G0",
"LME060822IH5",
"NFS0103297H5"
]
1 change: 0 additions & 1 deletion test/mocha.opts

This file was deleted.

Loading

0 comments on commit 0936069

Please sign in to comment.