Skip to content

Commit

Permalink
Merge pull request #117 from eclipxe13/version-2.26.0
Browse files Browse the repository at this point in the history
Add `Crp20277Fixer` to work with new rule `CRP20277`.
  • Loading branch information
eclipxe13 authored Jan 12, 2024
2 parents 9a2dbd6 + 158b280 commit cb1cf52
Show file tree
Hide file tree
Showing 6 changed files with 224 additions and 0 deletions.
4 changes: 4 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@
- Merge methods from `\CfdiUtils\Nodes\NodeHasValueInterface` into `\CfdiUtils\Nodes\NodeInterface`.
- Remove deprecated constant `CfdiUtils\Retenciones\Retenciones::RET_NAMESPACE`.

## Version 2.27.0 2024-01-12

Add `CfdiUtils\Utils\Crp20277Fixer` to work with new rule `CRP20277` (apply since 2024-01-15).

## Version 2.26.0 2024-01-10

Add `CfdiUtils\Elements\Cce30` *Elements* to work with "Complemento de Comercio Exterior 3.0".
Expand Down
1 change: 1 addition & 0 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ Solo hay métodos específicos para CFDI 3.3 y CFDI 4.0.

- [OpenSSL](utilerias/openssl.md)
- [Cálculo de CFDI con complemento de pagos 2.0](utilerias/calculo-pagos20.md)
- [Regla CRP20277 del complemento de pagos 2.0](utilerias/pagos20-crp20277.md)


## Contribuciones
Expand Down
85 changes: 85 additions & 0 deletions docs/utilerias/pagos20-crp20277.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Regla CRP20277 del complemento de pagos 2.0

El SAT creó la regla `CRP20277` en los Documentos técnicos del complemento de recepción de pagos 2.0, revisión B,
vigente a partir del 15 de enero de 2024, en la Matriz de errores. Donde dice:

- Validación:
*Cuando existan operaciones con más de un Documento relacionado en donde al menos uno de ellos contenga
la misma moneda que la del Pago, para la fórmula en el cálculo del margen de variación se deben
considerar 10 decimales en la EquivalenciaDR cuando el valor sea 1.*
- Código de error:
*El campo EquivalenciaDR debe contener el valor "1.0000000000".*

Esta regla cambia lo especificado en la regla `CRP20238`. Que establece que si el atributo `Pago@MonedaP` es igual
a `DoctoRelacionado@MonedaDR` entonces el valor de `DoctoRelacionado@EquivalenciaDR` debe ser `"1"`.

La regla `CRP20277` establece entonces que el valor debe ser `"1.0000000000"`, siempre que se cumplan las siguientes condiciones para el pago:

- *con más de un documento relacionado*:
Es decir, no aplica para cuando hay solo 1 `DoctoRelacionado`, debe haber al menos 2.
- *al menos uno de ellos contenga la misma moneda que la del Pago*:
Es decir, aplica con que exista 1 `DoctoRelacionado` donde el valor de `Pago@MonedaP` es el mismo que `DoctoRelacionado@MonedaDR`.
- *se deben considerar 10 decimales en la EquivalenciaDR cuando el valor sea 1*:
Es decir, `DoctoRelacionado@EquivalenciaDR` debe ser `"1"` (y va a cambiar a `"1.0000000000"`).

Y si las tres condiciones se cumplen:

- (a) *se deben considerar 10 decimales*, (b) *el campo EquivalenciaDR debe contener el valor "1.0000000000"*:
Es decir, el valor de `DoctoRelacionado@EquivalenciaDR` cambia de `"1"` a `"1.0000000000"`.

## Utilería `Crp20277Fixer`

Para facilitar la aplicación de la regla `CRP20277` y modificar `@EquivalenciaDR`, se ha creado
la clase `Crp20277Fixer`, que hace las revisiones necesarias para cuando es necesario cambiar
el valor de `DoctoRelacionado@EquivalenciaDR` de `"1"` a `"1.0000000000"`.

Recomendación de uso:

- Fabrica el *Pre-CFDI* considerando el valor `1` cuando `Pago@MonedaP` es igual a `DoctoRelacionado@MonedaDR`,
tal como do dice la regla `CRP20238`.
- Después de llenar el complemento y antes de firmar el *Pre-CFDI*, llama al método `Crp20277Fixer::staticFix()`.

Para el siguiente ejemplo se omite la lógica de cómo crear o llenar un CFDI 4.0 con Complemento de pagos 2.0,
la parte importante es ilustrar el llamado a `Crp20277Fixer::staticFix()`.

```php
<?php

use CfdiUtils\CfdiCreator40;
use CfdiUtils\Elements\Pagos20\Pagos;
use CfdiUtils\Utils\Crp20277Fixer;

// se fabrica el creador de CFDI 4.0
$creator = new CfdiCreator40(/* atributos del comprobante */);

// se crea el complemento de pagos
$complementoPagos = new Pagos();
// se agrega un pago
$pago = $complementoPagos->addPago([/* atributos del pago */]);
// se agrega uno o más documentos
$pago->addDoctoRelacionado([/* atributos del documento relacionado */]);


// se llama a la utilería para cambiar los valores DoctoRelacionado@MonedaDR de 1 a 1.0000000000 cuando sea necesario
Crp20277Fixer::staticFix($complementoPagos);


// se sigue con la lógica de firmado del Pre-CFDI, timbrado con el PAC, etc.
$creator->addSello($key, $password);
```

## Reflexión de la regla

Creo que el SAT ha cometido un error grave en esta regla, dado que la necesidad de 10 decimales
solamente aplica para el cálculo del margen de variación, dentro de las validaciones de un PAC.

Se trata de un valor que se puede *interpretar* de esta forma (con diez decimales), a pesar de que esté escrito sin decimales.

Sería suficiente con poner una nota en la validación dentro del estándar del complemento, y una nota en la guía de llenado.

Para la matriz de errores, se puede especificar (como en el caso de CFDI) que es una regla que solo aplica para los PAC.
Y donde dice *El campo EquivalenciaDR debe contener el valor "1.0000000000".*,
debería decir *El campo EquivalenciaDR se debe interpretar con el valor "1.0000000000" para el cálculo del margen de variación.*

Pero, al no hacerlo, se tiene que implementar una doble lógica, la primera es para seguir la especificación de `CRP20238`
y la segunda para cumplir con el requisito de la regla `CRP20277`.
1 change: 1 addition & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ nav:
- "Utilerías":
- utilerias/openssl.md
- utilerias/calculo-pagos20.md
- utilerias/pagos20-crp20277.md
- "Contribuir":
- contribuir/guia-desarrollador.md
- contribuir/guia-documentador.md
Expand Down
67 changes: 67 additions & 0 deletions src/CfdiUtils/Utils/Crp20277Fixer.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php

namespace CfdiUtils\Utils;

use CfdiUtils\Nodes\NodeInterface;

/**
* This utility class change the /pago20:Pagos/pago20:Pago/pago20:DoctoRelacionado@EquivalenciaDR attribute
* from '1' to '1.0000000000' as defined by the rule CRP20277.
*
* La regla CRP20277 está definida en los Documentos técnicos del complemento de recepción de pagos 2.0, revisión B,
* vigente a partir del 15 de enero de 2024, en la Matriz de errores, y dice:
* Validación:
* Cuando existan operaciones con más de un Documento relacionado en donde al menos uno de ellos contenga
* la misma moneda que la del Pago, para la fórmula en el cálculo del margen de variación se deben
* considerar 10 decimales en la EquivalenciaDR cuando el valor sea 1.
* Código de error:
* El campo EquivalenciaDR debe contener el valor "1.0000000000".
* Esta regla cambia lo especificado en la regla CRP20238.
*
* @see http://omawww.sat.gob.mx/tramitesyservicios/Paginas/recepcion_de_pagos.htm
*/
final class Crp20277Fixer
{
public static function staticFix(NodeInterface $complemento): void
{
$fixer = new self();
$fixer->fixPagos($complemento);
}

public function fixPagos(NodeInterface $complemento): void
{
$pagos = $complemento->searchNodes('pago20:Pago');
foreach ($pagos as $pago) {
$this->fixPago($pago);
}
}

public function fixPago(NodeInterface $pago): void
{
$doctoRelacionados = $pago->searchNodes('pago20:DoctoRelacionado');

// más de un Documento relacionado
if ($doctoRelacionados->count() < 2) {
return;
}

// al menos uno de ellos contenga la misma moneda que la del Pago
$hasDocumentsWithSameCurrency = false;
foreach ($doctoRelacionados as $doctoRelacionado) {
if ($doctoRelacionado['MonedaDR'] === $pago['MonedaP']) {
$hasDocumentsWithSameCurrency = true;
break;
}
}
if (! $hasDocumentsWithSameCurrency) {
return;
}

// se deben considerar 10 decimales en la EquivalenciaDR cuando el valor sea 1
foreach ($doctoRelacionados as $doctoRelacionado) {
if ('1' === $doctoRelacionado['EquivalenciaDR']) {
$doctoRelacionado['EquivalenciaDR'] = '1.0000000000'; // CRP20277
}
}
}
}
66 changes: 66 additions & 0 deletions tests/CfdiUtilsTests/Utils/Crp20277FixerTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php

namespace CfdiUtilsTests\Utils;

use CfdiUtils\Elements\Pagos20\Pagos;
use CfdiUtils\Utils\Crp20277Fixer;
use CfdiUtilsTests\TestCase;

final class Crp20277FixerTest extends TestCase
{
public const EXPECTED_FORMAT = '1.0000000000';

public function testFixerChangesFormat(): void
{
$complemento = new Pagos();
$equivalenciaDrUsd = '19.87654321';
$equivalenciaDrMxn = '0.050310559';
$firstPago = $complemento
->addPago(['MonedaP' => 'MXN'])
->multiDoctoRelacionado(
['MonedaDR' => 'USD', 'EquivalenciaDR' => $equivalenciaDrUsd],
['MonedaDR' => 'MXN', 'EquivalenciaDR' => '1'],
);
$secondPago = $complemento
->addPago(['MonedaP' => 'USD'])
->multiDoctoRelacionado(
['MonedaDR' => 'MXN', 'EquivalenciaDR' => $equivalenciaDrMxn],
['MonedaDR' => 'USD', 'EquivalenciaDR' => '1'],
);
$thirdPago = $complemento
->addPago(['MonedaP' => 'USD'])
->multiDoctoRelacionado(
['MonedaDR' => 'USD', 'EquivalenciaDR' => '1'],
);

Crp20277Fixer::staticFix($complemento);

$this->assertSame(
self::EXPECTED_FORMAT,
$firstPago->children()->get(1)['EquivalenciaDR'],
'DoctoRelacionados: 2, MonedaP: MXN, MonedaDR: MXN, expected to change'
);
$this->assertSame(
$equivalenciaDrUsd,
$firstPago->children()->get(0)['EquivalenciaDR'],
'DoctoRelacionados: 2, MonedaP: MXN, MonedaDR: USD, expected not to change'
);

$this->assertSame(
self::EXPECTED_FORMAT,
$secondPago->children()->get(1)['EquivalenciaDR'],
'DoctoRelacionados: 2, MonedaP: USD, MonedaDR: USD, expected to change'
);
$this->assertSame(
$equivalenciaDrMxn,
$secondPago->children()->get(0)['EquivalenciaDR'],
'DoctoRelacionados: 2, MonedaP: USD, MonedaDR: MXN, expected not to change'
);

$this->assertSame(
'1',
$thirdPago->children()->get(0)['EquivalenciaDR'],
'DoctoRelacionados: 1, MonedaP: USD, MonedaDR: USD, expected not to change'
);
}
}

0 comments on commit cb1cf52

Please sign in to comment.