Skip to content

Commit

Permalink
Merge pull request #15 from eclipxe13/version-1.1.2
Browse files Browse the repository at this point in the history
Version 1.1.2
  • Loading branch information
eclipxe13 authored Apr 29, 2022
2 parents 2cbe322 + 8519d6b commit 2c81277
Show file tree
Hide file tree
Showing 6 changed files with 61 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
env:
fail-fast: true
- name: Code style (phpcs)
run: phpcs -q --report=checkstyle src/ tests/ | cs2pr
run: phpcs -q --report=checkstyle | cs2pr

php-cs-fixer:
name: Code style (php-cs-fixer)
Expand Down
6 changes: 3 additions & 3 deletions .phive/phars.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<phive xmlns="https://phar.io/phive">
<phar name="php-cs-fixer" version="^3.6.0" installed="3.6.0" location="./tools/php-cs-fixer" copy="false"/>
<phar name="php-cs-fixer" version="^3.8.0" installed="3.8.0" location="./tools/php-cs-fixer" copy="false"/>
<phar name="phpcs" version="^3.6.2" installed="3.6.2" location="./tools/phpcs" copy="false"/>
<phar name="phpcbf" version="^3.6.2" installed="3.6.2" location="./tools/phpcbf" copy="false"/>
<phar name="phpstan" version="^1.4.6" installed="1.4.6" location="./tools/phpstan" copy="false"/>
<phar name="psalm" version="^4.21.0" installed="4.21.0" location="./tools/psalm" copy="false"/>
<phar name="phpstan" version="^1.6.3" installed="1.6.3" location="./tools/phpstan" copy="false"/>
<phar name="psalm" version="^4.22.0" installed="4.22.0" location="./tools/psalm" copy="false"/>
<phar name="infection" version="^0.23.0" installed="0.23.0" location="./tools/infection" copy="false"/>
</phive>
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,21 @@ Provee métodos para crear una cadena de caracteres que es una clave RFC:
- `RfcFaker::mexicanRfcMoral()` para persona moral (12 posiciones).
- `RfcFaker::mexicanRfc()` indistintamente una persona moral o física.

## Dígito verificador

Se puede obtener el dígito verificador calculado con el método `Rfc::calculateCheckSum()`,
así como conocer si el dígito verificador coincide con el método `Rfc::doesCheckSumMatch()`.

La además provee la clase `CheckSum` para realizar el cálculo del dígito verificador de un RFC.
Cabe mencionar que, si bien debería ser siempre coincidente, hay algunos casos donde esto
no se respeta (SAT, ¿todo bien?), por ejemplo, el caso de *Restaurantes TOKS* ha tenido
los RFC `RT0840921REA` (dígito `A`) y también `RT0840921RE4`.

Puede ver el procedimiento del dígito verificador en alguno de estos enlaces:

- <https://www.studocu.com/es-mx/document/universidad-del-valle-de-mexico/administracion/algoritmo-para-generar-el-rfc-con-homoclave-para-personas-fisicas-y-morales/12002840>
- <https://solucionfactible.com/sfic/capitulos/timbrado/rfc-digito-verificador.jsp>

## Desarrollo

Para entender esta librería en el ámbito de desarrollo (para extender o modificar), lee los siguientes documentos:
Expand Down
10 changes: 10 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ Utilizamos [Versionado Semántico 2.0.0](SEMVER.md).

Los cambios no liberados no requieren de una nueva versión y son incluidos en la rama principal.

## Versión 1.1.2

Se actualiza la clase `CheckSum` y se mejoran las pruebas unitarias sobre la misma.
Gracias a `@fitorec` por sus sugerencias en el [PR #14](https://github.com/phpcfdi/rfc/pull/14).

Se actualizan las versiones de las herramientas de desarrollo.

Al ejecutar el trabajo de integración continua en el trabajo `phpcs` se usan los directorios según
el archivo de configuración.

## Versión 1.1.1

Se actualiza la expresión regular para lectura de RFC con las recomendaciones de simplificación:
Expand Down
26 changes: 14 additions & 12 deletions src/CheckSum.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,25 @@ final class CheckSum
{
private const DICTIONARY = [0 => 0, 1 => 1, 2 => 2, 3 => 3, 4 => 4, 5 => 5, 6 => 6, 7 => 7, 8 => 8, 9 => 9, 'A' => 10, 'B' => 11, 'C' => 12, 'D' => 13, 'E' => 14, 'F' => 15, 'G' => 16, 'H' => 17, 'I' => 18, 'J' => 19, 'K' => 20, 'L' => 21, 'M' => 22, 'N' => 23, '&' => 24, 'O' => 25, 'P' => 26, 'Q' => 27, 'R' => 28, 'S' => 29, 'T' => 30, 'U' => 31, 'V' => 32, 'W' => 33, 'X' => 34, 'Y' => 35, 'Z' => 36, ' ' => 37, '#' => 38];

private const DIGIT_OVERRIDE = [10 => 'A', 11 => '0'];

public function calculate(string $rfc): string
{
// 'Ñ' translated to '#' because 'Ñ' is multibyte 0xC3 0xB1
// 'Ñ' cambia a '#' porque 'Ñ' es multi-byte 0xC3 0xB1
$chars = str_split(str_replace('Ñ', '#', $rfc), 1);
array_pop($chars); // remove predefined checksum
$length = count($chars);
$sum = (11 === $length) ? 481 : 0; // 481 para morales, 0 para físicas
$j = $length + 1;
array_pop($chars); // remover el dígito predefinido

// Valor inicial de la suma: 481 para morales, 0 para físicas
$sum = (12 === $length) ? 481 : 0;
// suma de valores: Σ(Vi * (Pi + 1))
foreach ($chars as $i => $char) {
$sum += self::DICTIONARY[$char] * ($j - $i);
$sum += (self::DICTIONARY[$char] ?? 0) * ($length - $i);
}
$digit = strval(11 - $sum % 11);
if ('11' === $digit) {
$digit = '0';
} elseif ('10' === $digit) {
$digit = 'A';
}
return $digit;

// posibles valores: [1, 2, ..., 10, 11] porque $sum % 11 => int<0, 10>
$digit = 11 - $sum % 11;
// se retorna 10 => 0, 11 => A o el valor obtenido
return self::DIGIT_OVERRIDE[$digit] ?? strval($digit);
}
}
27 changes: 18 additions & 9 deletions tests/Unit/CheckSumTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,29 @@

final class CheckSumTest extends TestCase
{
public function testCheckSum(): void
/** @return array<string, array{string, string}> */
public function providerCheckSum(): array
{
$expected = 'A';
$rfc = 'COSC8001137NA';
return [
'física 0' => ['CAMA911215CJ0', '0'],
'física A' => ['COSC8001137NA', 'A'],
'física [1-9]' => ['SORC591116FJ6', '6'],

$checksum = new CheckSum();
$this->assertSame($expected, $checksum->calculate($rfc));
'moral A' => ['DIM8701081LA', 'A'],
'moral 0' => ['A&A050908GT0', '0'],
'moral [1-9]' => ['SAT970701NN3', '3'],

'multibyte' => ['AÑÑ801231JK0', '0'],

'empty rfc' => ['', '0'],
'invalid rfc' => ['$', '0'],
'invalid chars' => ['AAA010101$$$', '7'], // $ is managed as 0
];
}

public function testCheckSumWithMultiByte(): void
/** @dataProvider providerCheckSum */
public function testCheckSum(string $rfc, string $expected): void
{
$expected = '0';
$rfc = 'AÑÑ801231JK0';

$checksum = new CheckSum();
$this->assertSame($expected, $checksum->calculate($rfc));
}
Expand Down

0 comments on commit 2c81277

Please sign in to comment.