Skip to content

Commit

Permalink
Add ReceptorResidenciaFiscal validator with tests
Browse files Browse the repository at this point in the history
* Added ComprobanteRegimenFiscal Validator with tests

* Removed registerAsserts function, used put function directly instead

* The parameter $attributes must be array instead of string[]

* Added ComprobanteResidenciaFiscal and its Tests

* Fix comprobante must not have ResidenciaFiscal neither NumRegIdTrib

* Fix comprobante must not have ResidenciaFiscal neither NumRegIdTrib

* Renamed ComprobanteResidenciaFiscal to ReceptorResidenciaFiscal
  • Loading branch information
driftking301 authored and eclipxe13 committed Nov 2, 2017
1 parent 270a7a3 commit ecaea32
Show file tree
Hide file tree
Showing 4 changed files with 183 additions and 7 deletions.
6 changes: 1 addition & 5 deletions src/CfdiUtils/Nodes/Node.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class Node implements NodeInterface
/**
* Node constructor.
* @param string $name
* @param string[] $attributes
* @param array $attributes
* @param NodeInterface[] $children
*/
public function __construct(string $name, array $attributes = [], array $children = [])
Expand Down Expand Up @@ -62,10 +62,6 @@ public function clear()
$this->children()->removeAll();
}

/**
* @param string[] $attributes
* @return void
*/
public function addAttributes(array $attributes)
{
$this->attributes->importArray($attributes);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
<?php
namespace CfdiUtils\Validate\Cfdi33\Standard;

use CfdiUtils\Nodes\Node;
use CfdiUtils\Nodes\NodeInterface;
use CfdiUtils\Validate\Asserts;
use CfdiUtils\Validate\Cfdi33\Abstracts\AbstractDiscoverableVersion33;
use CfdiUtils\Validate\Status;

/**
* ComprobanteResidenciaFiscal
*
* Valida que:
* - RESFISC01: Si el RFC no es XEXX010101000 entonces la residencia fiscal no debe existir
* - RESFISC02: Si el RFC sí es XEXX010101000 y existe el complemento de comercio exterior
* entonces la residencia fiscal debe establecerse y no puede ser "MEX"
* - RESFISC03: Si el RFC sí es XEXX010101000 y se registró el número de registro de identificación fiscal
* entonces la residencia fiscal debe establecerse y no puede ser "MEX"
*/
class ReceptorResidenciaFiscal extends AbstractDiscoverableVersion33
{
private function registerAsserts(Asserts $asserts)
{
$assertDescriptions = [
'RESFISC01' => 'Si el RFC no es XEXX010101000 entonces la residencia fiscal no debe existir',
'RESFISC02' => 'Si el RFC sí es XEXX010101000 y existe el complemento de comercio exterior'
. ' entonces la residencia fiscal debe establecerse y no puede ser "MEX"',
'RESFISC03' => 'Si el RFC sí es XEXX010101000 y se registró el número de registro de identificación fiscal'
. ' entonces la residencia fiscal debe establecerse y no puede ser "MEX"',
];
foreach ($assertDescriptions as $code => $title) {
$asserts->put($code, $title);
}
}
public function validate(NodeInterface $comprobante, Asserts $asserts)
{
$this->registerAsserts($asserts);

$receptor = $comprobante->searchNode('cfdi:Receptor');
if ($receptor === null) {
$receptor = new Node('cfdi:Receptor');
}

if ('XEXX010101000' !== $receptor['Rfc']) {
$asserts->putStatus(
'RESFISC01',
Status::when(! isset($receptor['ResidenciaFiscal']))
);
return;
}

$existsComercioExterior = (null !== $comprobante->searchNode('cfdi:Complemento', 'cce11:ComercioExterior'));
$isValidResidenciaFiscal = '' !== $receptor['ResidenciaFiscal'] && $receptor['ResidenciaFiscal'] !== 'MEX';
if ($existsComercioExterior) {
$asserts->putStatus(
'RESFISC02',
Status::when($isValidResidenciaFiscal)
);
}
if (isset($receptor['NumRegIdTrib'])) {
$asserts->putStatus(
'RESFISC03',
Status::when($isValidResidenciaFiscal)
);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
<?php
namespace CfdiUtilsTests\Validate\Cfdi33\Standard;

use CfdiUtils\Nodes\Node;
use CfdiUtils\Validate\Cfdi33\Standard\ReceptorResidenciaFiscal;
use CfdiUtils\Validate\Status;
use CfdiUtilsTests\Validate\ValidateTestCase;

class ReceptorResidenciaFiscalTest extends ValidateTestCase
{
/** @var ReceptorResidenciaFiscal */
protected $validator;

protected function setUp()
{
parent::setUp();
$this->validator = new ReceptorResidenciaFiscal();
}

public function providerValidCases()
{
return [
// RESFISC01: Si el RFC no es XEXX010101000 entonces la residencia fiscal no debe existir
[null, null, null, false, 'RESFISC01'],
['', null, null, false, 'RESFISC01'],
['XXXXXXXXXXXX', null, null, false, 'RESFISC01'],
// RESFISC02: Si el RFC sí es XEXX010101000 y existe el complemento de comercio exterior
// entonces la residencia fiscal debe establecerse y no puede ser "MEX"
['XEXX010101000', 'XXX', null, true, 'RESFISC02'],
// RESFISC03: Si el RFC sí es XEXX010101000 y se registró el número de registro de identificación fiscal
// entonces la residencia fiscal debe establecerse y no puede ser "MEX"
['XEXX010101000', 'XXX', '1234', false, 'RESFISC03'],
];
}

/**
* @param $receptorRfc
* @param $residenciaFiscal
* @param $numRegIdTrib
* @param $putComercioExterior
* @param $ok
* @dataProvider providerValidCases
*/
public function testValidCase($receptorRfc, $residenciaFiscal, $numRegIdTrib, $putComercioExterior, $ok)
{
$this->comprobante->addChild(new Node('cfdi:Receptor', [
'Rfc' => $receptorRfc,
'ResidenciaFiscal' => $residenciaFiscal,
'NumRegIdTrib' => $numRegIdTrib,
]));
if ($putComercioExterior) {
$this->comprobante->addChild(new Node('cfdi:Complemento', [], [
new Node('cce11:ComercioExterior'),
]));
}
$this->runValidate();
$this->assertFalse($this->asserts->hasErrors());
$this->assertStatusEqualsCode(Status::ok(), $ok);
}

public function providerInvalidCases()
{
return [
// RESFISC01: Si el RFC no es XEXX010101000 entonces la residencia fiscal no debe existir
[null, '', null, false, 'RESFISC01'],
['', '', null, false, 'RESFISC01'],
['XXXXXXXXXXXX', '', null, false, 'RESFISC01'],
[null, 'GER', null, false, 'RESFISC01'],
['', 'GER', null, false, 'RESFISC01'],
['XXXXXXXXXXXX', 'GER', null, false, 'RESFISC01'],
// RESFISC02: Si el RFC sí es XEXX010101000 y existe el complemento de comercio exterior
// entonces la residencia fiscal debe establecerse y no puede ser "MEX"
['XEXX010101000', null, null, true, 'RESFISC02'],
['XEXX010101000', '', null, true, 'RESFISC02'],
['XEXX010101000', 'MEX', null, true, 'RESFISC02'],
// RESFISC03: Si el RFC sí es XEXX010101000 y se registró el número de registro de identificación fiscal
// entonces la residencia fiscal debe establecerse y no puede ser "MEX"
['XEXX010101000', null, '1234', false, 'RESFISC03'],
['XEXX010101000', '', '1234', false, 'RESFISC03'],
['XEXX010101000', 'MEX', '1234', false, 'RESFISC03'],
];
}

/**
* @param $receptorRfc
* @param $residenciaFiscal
* @param $numRegIdTrib
* @param $putComercioExterior
* @param $error
* @dataProvider providerinValidCases
*/
public function testInvalidCase($receptorRfc, $residenciaFiscal, $numRegIdTrib, $putComercioExterior, $error)
{
$this->comprobante->addChild(new Node('cfdi:Receptor', [
'Rfc' => $receptorRfc,
'ResidenciaFiscal' => $residenciaFiscal,
'NumRegIdTrib' => $numRegIdTrib,
]));
if ($putComercioExterior) {
$this->comprobante->addChild(new Node('cfdi:Complemento', [], [
new Node('cce11:ComercioExterior'),
]));
}
$this->runValidate();
$this->assertStatusEqualsCode(Status::error(), $error);
}

public function testValidCaseWithoutReceptorNode()
{
$this->runValidate();
$this->assertFalse($this->asserts->hasErrors());
}
}
4 changes: 2 additions & 2 deletions tests/assets/cfdi33-valid.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ El SelloCFD del complemento TimbreFiscalDigital no tiene datos válidos.
Serie="A"
Folio="167ABC"
Fecha="2017-07-05T09:09:23"
Sello="FykOf+qcXcygcge07SdIJ7zshVzqCrFqhUfAZbgbdzqpoz92Xh+l8bAfPFmFXQiHSL+mhyyk7XGFF0VvN2q+2INH/G42yHo44yJEsQr3wkw7uybJ/z0iZD12xvADxoVaoRfMm+nNkgdwrWsaRHJvUMLT5hAkj09e3SIPY3JHvI4xg280WxNQeqXdK1qWyZShHTqv1Yy+RHirkH4A9PJ8+VZGtIyYUwengUc8sWe7VIDwb8rRmzVF8jIyUgPOXA4vphO9w4vhgQvgqDyF8P2ygxb91eC0lHJURvfRLKgf/yKOjmYQj5wdaKWJ8gT1ZXb1a7fcuAlDjJl02ES3zAQQwA=="
Sello="PxWRLK14SjI0b3IaYOJsOeJmp5CRt2BEE8HhjqMozc6vDcEoeM9VBKszx5/7xz4AHf2pC1ZWkFkxQODLoISpNKkGbjzCLeMPivFwOibDJUklSA/RPu8NHnzeWoMx2XcrWrHeYl91ywQeLH/Z1tQmpPtamwLX6nmW5P7/87neyCwdm4Q7n1RdNL4fDU5tDwjgqHoKl9/xVtcU9hque0efyOFElsfCodgDT5zIBPfrOUp/CCvEpXRT7r9A6Yd0OTyX4q4xqL1QrDit4yPqbNy9t6DXHP7UzCB+CSpN3CJzz6a3f4530m5xRlFslK0AXbDZCTJrUEnSB0g9d+psosRJ2Q=="
NoCertificado="30001000000300023708"
Certificado="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"
SubTotal="2269400"
Expand All @@ -30,7 +30,7 @@ El SelloCFD del complemento TimbreFiscalDigital no tiene datos válidos.
<cfdi:CfdiRelacionado UUID="A39DA66B-52CA-49E3-879B-5C05185B0EF7"/>
</cfdi:CfdiRelacionados>
<cfdi:Emisor Rfc="AAA010101AAA" Nombre="ACCEM SERVICIOS EMPRESARIALES SC" RegimenFiscal="601"/>
<cfdi:Receptor Rfc="COSC8001137NA" Nombre="CARLOS CORTES SOTO" ResidenciaFiscal="MEX" NumRegIdTrib="0000000000000" UsoCFDI="G01"/>
<cfdi:Receptor Rfc="COSC8001137NA" Nombre="CARLOS CORTES SOTO" UsoCFDI="G01"/>
<cfdi:Conceptos>
<cfdi:Concepto ClaveProdServ="01010101" ClaveUnidad="F52" NoIdentificacion="00001" Cantidad="1.5" Unidad="TONELADA" Descripcion="ACERO" ValorUnitario="1500000" Importe="2250000">
<cfdi:Impuestos>
Expand Down

0 comments on commit ecaea32

Please sign in to comment.