Skip to content

Commit

Permalink
Version 2.6.5
Browse files Browse the repository at this point in the history
  • Loading branch information
eclipxe13 committed Oct 5, 2018
2 parents 352184d + ec9eba0 commit 4f72f00
Show file tree
Hide file tree
Showing 15 changed files with 288 additions and 159 deletions.
13 changes: 13 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,19 @@
- Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments.


## Version 2.6.5 2018-10-04

- Fix validation `PAGO09`:
- Before 2018-09-01 `pagos10:Pago@Monto` must be less or equal than sum of `pago10:DoctoRelacionado@ImpPagado`
in the same currency as `pagos10:Pago`.
- Since 2018-09-01 according to *Guía de llenado del comprobante al que se le incorpore el complemento para
recepción de pagos, page 22* it is required that `pagos10:Pago@Monto` must be in an interval.
- Fix samples from `tests/assets/pagos/` since new validation make it fail.
- Rename validation class `MontoGreaterOrEqualThanSumOfDocuments` to `MontoBetweenIntervalSumOfDocuments`
- Refactor `CfdiUtils\Certificado\Certificado` extracting obtain public key routine to an internal method.
- Create tests for trait `CalculateDocumentAmountTrait`.


## Version 2.6.4 2018-09-04

- Fix validation `TIPOCAMBIO02`:
Expand Down
38 changes: 22 additions & 16 deletions src/CfdiUtils/Certificado/Certificado.php
Original file line number Diff line number Diff line change
Expand Up @@ -53,21 +53,7 @@ public function __construct(string $filename)
}

// get the public key
$pubkey = false;
$pubData = false;
try {
$pubkey = openssl_get_publickey($contents);
if (is_resource($pubkey)) {
$pubData = openssl_pkey_get_details($pubkey);
}
} finally {
if (is_resource($pubkey)) {
openssl_free_key($pubkey);
}
}
if (false === $pubData) {
$pubData = ['key' => ''];
}
$pubKey = $this->obtainPubKeyFromContents($contents);

// set all the values
$this->rfc = (string) strstr($data['subject']['x500UniqueIdentifier'] . ' ', ' ', true);
Expand All @@ -84,7 +70,7 @@ public function __construct(string $filename)
$this->serial = $serial->asAscii();
$this->validFrom = $data['validFrom_time_t'];
$this->validTo = $data['validTo_time_t'];
$this->pubkey = $pubData['key'];
$this->pubkey = $pubKey;
$this->pemContents = $contents;
$this->filename = $filename;
}
Expand Down Expand Up @@ -206,4 +192,24 @@ protected function changeCerToPem(string $contents): string
. chunk_split(base64_encode($contents), 64, PHP_EOL)
. '-----END CERTIFICATE-----' . PHP_EOL;
}

protected function obtainPubKeyFromContents(string $contents): string
{
try {
$pubkey = openssl_get_publickey($contents);
if (! is_resource($pubkey)) {
return '';
}
$pubData = openssl_pkey_get_details($pubkey);
if (false === $pubData) {
return '';
}
return $pubData['key'] ?? '';
} finally {
// close public key even if the flow is throw an exception
if (isset($pubkey) && is_resource($pubkey)) {
openssl_free_key($pubkey);
}
}
}
}
2 changes: 1 addition & 1 deletion src/CfdiUtils/Validate/Cfdi33/RecepcionPagos/Pago.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function createValidators(): array
new Pagos\TipoCambioValue(), // PAGO6
new Pagos\MontoGreaterThanZero(), // PAGO07
new Pagos\MontoDecimals(), // PAGO08
new Pagos\MontoGreaterOrEqualThanSumOfDocuments(), // PAGO09
new Pagos\MontoBetweenIntervalSumOfDocuments(), // PAGO09
new Pagos\BancoOrdenanteRfcCorrecto(), // PAGO10
new Pagos\BancoOrdenanteNombreRequerido(), // PAGO11
new Pagos\BancoOrdenanteRfcProhibido(), // PAGO12
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<?php
namespace CfdiUtils\Validate\Cfdi33\RecepcionPagos\Pagos;

use CfdiUtils\Nodes\NodeInterface;
use CfdiUtils\Validate\Cfdi33\RecepcionPagos\Helpers\CalculateDocumentAmountTrait;

/**
* PAGO09: En un pago, el monto del pago debe encontrarse entre límites mínimo y máximo de la suma
* de los valores registrados en el importe pagado de los documentos relacionados (CRP206)
*/
class MontoBetweenIntervalSumOfDocuments extends AbstractPagoValidator
{
use CalculateDocumentAmountTrait;

protected $code = 'PAGO09';
protected $title = 'En un pago, el monto del pago debe encontrarse entre límites mínimo y máximo de la suma'
. ' de los valores registrados en el importe pagado de los documentos relacionados (CRP206)';

public function validatePago(NodeInterface $pago): bool
{
$pagoAmount = floatval($pago['Monto']);
$bounds = $this->calculateDocumentsAmountBounds($pago);
$lower = $bounds['lower'];
$upper = $bounds['upper'];
if ($pagoAmount < $lower || $pagoAmount > $upper) {
throw new ValidatePagoException(
sprintf('Monto del pago: "%s", Suma mínima: "%s", Suma máxima: "%s"', $pagoAmount, $lower, $upper)
);
}

return true;
}

public function calculateDocumentsAmountBounds(NodeInterface $pago): array
{
$documents = $pago->searchNodes('pago10:DoctoRelacionado');
$values = [];
foreach ($documents as $document) {
$values[] = $this->calculateDocumentAmountBounds($document, $pago);
}
$bounds = [
'lower' => array_sum(array_column($values, 'lower')),
'upper' => array_sum(array_column($values, 'upper')),
];
return $bounds;
}

public function calculateDocumentAmountBounds(NodeInterface $doctoRelacionado, NodeInterface $pago): array
{
$amount = $this->calculateDocumentAmount($doctoRelacionado, $pago);
$impPagado = $doctoRelacionado['ImpPagado'] ?? $amount;
$tipoCambioDr = $doctoRelacionado['TipoCambioDR'];
$exchangeRate = 1;
if ('' !== $tipoCambioDr && $pago['MonedaP'] !== $pago['MonedaDR']) {
$exchangeRate = floatval($tipoCambioDr);
}
$numDecimalsAmount = $this->getNumDecimals($impPagado);
$numDecimalsExchangeRate = $this->getNumDecimals($tipoCambioDr);

if (0 === $numDecimalsExchangeRate) {
return [
'lower' => $amount / $exchangeRate,
'upper' => $amount / $exchangeRate,
];
}

$almostTwo = 2 - (10 ** - 10);

$lowerAmount = $amount - 10 ** - $numDecimalsAmount / 2;
$lowerExchangeRate = $exchangeRate + (10 ** (- $numDecimalsExchangeRate) / $almostTwo);

$upperAmount = $amount + 10 ** - $numDecimalsAmount / $almostTwo;
$upperExchangeRate = $exchangeRate - (10 ** (- $numDecimalsExchangeRate) / 2);

return [
'lower' => $lowerAmount / $lowerExchangeRate,
'upper' => $upperAmount / $upperExchangeRate,
];
}

public function getNumDecimals(string $numeric): int
{
if (! is_numeric($numeric)) {
return 0;
}
$pointPosition = strpos($numeric, '.');
if (false === $pointPosition) {
return 0;
}
return strlen($numeric) - 1 - $pointPosition;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php
namespace CfdiUtilsTests\Validate\Cfdi33\RecepcionPagos\Helpers;

use CfdiUtils\Elements\Pagos10\DoctoRelacionado;
use CfdiUtils\Elements\Pagos10\Pago;
use PHPUnit\Framework\TestCase;

class CalculateDocumentAmountTraitTest extends TestCase
{
public function testCalculateDocumentAmountWhenIsSet()
{
$validator = new CalculateDocumentAmountUse();
$amount = $validator->calculateDocumentAmount(new DoctoRelacionado([
'ImpPagado' => '123.45',
]), new Pago());

$this->assertEquals(123.45, $amount, '', 0.001);
}

public function testCalculateDocumentAmountWhenIsUndefined()
{
$pago = new Pago(['Monto' => '123.45']);
$docto = $pago->addDoctoRelacionado();

$validator = new CalculateDocumentAmountUse();
$amount = $validator->calculateDocumentAmount($docto, $pago);

$this->assertEquals(123.45, $amount, '', 0.001);
}

public function testCalculateDocumentAmountWhenIsUndefinedWithExchangeRate()
{
$pago = new Pago(['Monto' => '123.45']);
$docto = $pago->addDoctoRelacionado(['TipoCambioDR' => 'EUR']);

$validator = new CalculateDocumentAmountUse();
$amount = $validator->calculateDocumentAmount($docto, $pago);

$this->assertEquals(0, $amount, '', 0.001);
}

public function testCalculateDocumentAmountWhenIsUndefinedWithMoreDocuments()
{
$pago = new Pago(['Monto' => '123.45']);
$pago->addDoctoRelacionado(); // first
$docto = $pago->addDoctoRelacionado(); // second

$validator = new CalculateDocumentAmountUse();
$amount = $validator->calculateDocumentAmount($docto, $pago);

$this->assertEquals(0, $amount, '', 0.001);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
<?php
namespace CfdiUtilsTests\Validate\Cfdi33\RecepcionPagos\Helpers;

use CfdiUtils\Validate\Cfdi33\RecepcionPagos\Helpers\CalculateDocumentAmountTrait;

class CalculateDocumentAmountUse
{
use CalculateDocumentAmountTrait;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<?php
namespace CfdiUtilsTests\Validate\Cfdi33\RecepcionPagos\Pagos;

use CfdiUtils\Elements\Pagos10\Pago;
use CfdiUtils\Validate\Cfdi33\RecepcionPagos\Pagos\MontoBetweenIntervalSumOfDocuments;
use CfdiUtils\Validate\Cfdi33\RecepcionPagos\Pagos\ValidatePagoException;
use PHPUnit\Framework\TestCase;

class MontoBetweenIntervalSumOfDocumentsTest extends TestCase
{
public function testValid()
{
$pago = new Pago([
'MonedaP' => 'USD',
'Monto' => '123.45',
]);
$pago->multiDoctoRelacionado(...[
['ImpPagado' => '50.00'], // 50.00
['MonedaDR' => 'EUR', 'TipoCambioDR' => '0.50', 'ImpPagado' => '25.00'], // 25.00 / 0.50 => 50
['MonedaDR' => 'MXN', 'TipoCambioDR' => '18.7894', 'ImpPagado' => '440.61'], // 440.61 / 18.7894 => 23.45
]);

$validator = new MontoBetweenIntervalSumOfDocuments();
$this->assertTrue($validator->validatePago($pago));
}

/**
* This is testing lower bound (122.94) and upper bound (123.97)
* @param string $monto
* @testWith ["122.94"]
* ["123.97"]
*/
public function testInvalids(string $monto)
{
$pago = new Pago([
'MonedaP' => 'USD',
'Monto' => $monto,
]);
$pago->multiDoctoRelacionado(...[
['ImpPagado' => '20.00'], // 20.00
['MonedaDR' => 'USD', 'ImpPagado' => '30.00'], // 30.00
['MonedaDR' => 'EUR', 'TipoCambioDR' => '0.50', 'ImpPagado' => '25.00'], // 25.00 / 0.50 => 50
['MonedaDR' => 'MXN', 'TipoCambioDR' => '18.7894', 'ImpPagado' => '440.61'], // 440.61 / 18.7894 => 23.45
]);

$validator = new MontoBetweenIntervalSumOfDocuments();

$this->expectException(ValidatePagoException::class);
$validator->validatePago($pago);
}

public function testValidWithSeveralDecimals()
{
// payment was made of 5,137.42 USD (ER: 18.7694) => 96,426.29 MXN
// to pay a document on USD
$pago = new Pago([
'MonedaP' => 'MXN',
'Monto' => '96426.29',
]);
$pago->addDoctoRelacionado([
'MonedaDR' => 'USD',
'TipoCambioDR' => number_format(1 / 18.7694, 4),
'ImpPagado' => '5137.42',
]);

$validator = new MontoBetweenIntervalSumOfDocuments();
$this->assertTrue($validator->validatePago($pago));
}
}
Loading

0 comments on commit 4f72f00

Please sign in to comment.