Skip to content

Commit 64b8137

Browse files
committed
Version 2.14.0
2 parents 2f7544c + fccbe5e commit 64b8137

File tree

10 files changed

+454
-162
lines changed

10 files changed

+454
-162
lines changed

docs/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,16 @@
2828
- Change visibility of `CfdiUtils\Cleaner\Cleaner#removeIncompleteSchemaLocation()` to private.
2929

3030

31+
## Version 2.14.0 2020-10-01
32+
33+
- Add `Retenciones` reader to work with *CFDI de retenciones e información de pagos*.
34+
- Refactor `Cfdi` and `Retenciones` to use recently created `XmlReaderTrait`.
35+
36+
3137
## Version 2.13.1 2020-10-01
3238

3339
- Fix validation `FORMAPAGO01`, it only applies when `Complemento de Pago` exists.
40+
- Add `Retenciones` reader to work with *CFDI de retenciones e información de pagos*.
3441

3542

3643
## Version 2.13.0 2020-08-28

docs/crear/cfdi-de-retenciones-e-informacion-de-pagos.md

Lines changed: 6 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,13 @@
11
# CFDI de retenciones e información de pagos
22

3-
Existen otro tipo de CFDI definido en el Anexo 20 sección II adicionales los CFDI de tipo
4-
Comprobante `<cfdi:Comprobante/>` (ingresos, egresos, traslados y pagos).
3+
Revisa la documentación de la [Lectura de CFDI de retenciones e información de pagos](../leer/leer-cfdi-retenciones.md),
4+
ahí encontrarás más información de referencia.
55

6-
Este CFDI se llama *CFDI de retenciones e información de pagos* y tiene la misma esencia de
7-
los comprobantes "tradicionales" (definir en campos requeridos, condicionales y obligatorios)
8-
pero cuenta con una estructura totalmente diferente.
9-
10-
Existe poca información alrededor de este tipo de comprobantes porque su generación es mucho
11-
menor a los CFDI tradicionales.
12-
13-
Su obligatoriedad y la información que contienen está sujeta a LIVA, LISR y LIEPS.
14-
Si requieres saber si caes en algún supuesto en donde requieras emitir este tipo de comprobantes
15-
te recomiendo lo verifiques con tu contador de confianza.
16-
17-
Entre las diferencias fundamentales de *CFDI de retenciones* y *CFDI tradicionales* están:
18-
19-
- El espacio de nombres XML <http://www.sat.gob.mx/esquemas/retencionpago/1>
20-
- El nombre de los atributos principales, como `retencion:Retenciones@Cert` en lugar de `cfdi:Comprobante@Certificado`
21-
- Tiene sus propios catálogos de información
22-
- El sello se procesa usando el algoritmo de digestión `SHA1` en lugar de `SHA256`
23-
- La fecha se expresa en ISO-8601 (contiene al final `-06:00`)
24-
25-
Entre las semejanzas están:
26-
27-
- Se maneja con complementos
28-
- Puede contener addendas
29-
- También requiere firmarse por un PAC entregando un Timbre Fiscal Digital
30-
31-
32-
## Herramientas gratuitas
33-
34-
Es importante notar que **el SAT no cuenta con una herramienta para elaborar CFDI de retenciones e información de pagos**
35-
como la tiene para los CFDI tradicionales, en su explicación de esquema operativo explican que se requiere utilizar
36-
a un PAC para poderlos emitir.
37-
38-
Por otro lado, el SAT no les ha exigido a los PAC que cuenten con herramientas gratuitas para elaborarlos,
39-
por lo que generalmente se requerirá contratar el uso de una aplicación o bien contratar los servicios de timbrado.
40-
41-
Con `CfdiUtils` podrás generar el *precfdi* y mandarlo timbrar con tu PAC habitual, el PAC te devolverá el *cfdi*
42-
incluyendo el timbre fiscal digital y este es el comprobante legal.
43-
44-
45-
## Más información
46-
47-
- Esquema de factura de retenciones e información de pagos
48-
<https://www.sat.gob.mx/consulta/65554/conoce-el-esquema-de-factura-electronica-de-retenciones-e-informacion-de-pagos>
49-
- Video chat No.21: CFDI de retenciones e información de pagos con sus complementos
50-
<https://www.youtube.com/watch?v=Z0TadVAjrFc>
51-
- Anexo 20
52-
<http://omawww.sat.gob.mx/tramitesyservicios/Paginas/anexo_20_version3-3.htm>
53-
- Preguntas frecuentes de retenciones e información de pagos
54-
<https://www.sat.gob.mx/cs/Satellite?blobcol=urldata&blobkey=id&blobtable=MungoBlobs&blobwhere=1461173416174&ssbinary=true>
55-
- Esquema XML de estructura
56-
<http://www.sat.gob.mx/esquemas/retencionpago/1/retencionpagov1.xsd>
57-
- Esquema XML de catálogos
58-
<http://www.sat.gob.mx/esquemas/retencionpago/1/catalogos/catRetenciones.xsd>
59-
- Archivo PDF con los catálogos
60-
<https://www.sat.gob.mx/cs/Satellite?blobcol=urldata&blobkey=id&blobtable=MungoBlobs&blobwhere=1461172330889&ssbinary=true>
61-
- Herramienta de transformación XML para generar la cadena original
62-
<http://www.sat.gob.mx/esquemas/retencionpago/1/retenciones.xslt>
6+
En esta sección encontrarás el ejemplo para poder crear un CFDI de este tipo.
637

8+
La estrategia para crear este un CFDI de retenciones es la misma que para [crear un CFDI 3.3](../crear/crear-cfdi.md).
9+
Consulta la información relacionada con el uso de [elementos](../componentes/elements.md),
10+
[nodos](../componentes/nodes.md) y [complementos no implementados](../crear/complementos-aun-no-implementados.md).
6411

6512
## Ejemplo de creación de un CFDI de retenciones e información de pagos
6613

docs/index.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ CFDI y herramientas del SAT. Y próximamente el lugar donde publicaremos la vers
4040
La librería ofrece métodos para leer CFDI versión 3.2 y 3.3.
4141

4242
- [Lectura formal de un CFDI](leer/leer-cfdi.md)
43+
- [Lectura formal de un CFDI de Retenciones](leer/leer-cfdi-retenciones.md)
4344
- [Lectura rápida de un CFDI](leer/quickreader.md)
4445
- [Limpieza de un CFDI](leer/limpieza-cfdi.md)
4546

docs/leer/leer-cfdi-retenciones.md

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
# Leer un CFDI de Retenciones
2+
3+
Existen otro tipo de CFDI definido en el Anexo 20 sección II adicionales los CFDI de tipo
4+
Comprobante `<cfdi:Comprobante/>` (ingresos, egresos, traslados y pagos).
5+
6+
Este CFDI se llama *CFDI de retenciones e información de pagos* y tiene la misma esencia de
7+
los comprobantes "tradicionales" (definir en campos requeridos, condicionales y obligatorios)
8+
pero cuenta con una estructura totalmente diferente.
9+
10+
## Acerca de los CFDI de Retenciones e información de pagos
11+
12+
Existe poca información alrededor de este tipo de comprobantes porque su generación es mucho
13+
menor a los CFDI tradicionales.
14+
15+
Su obligatoriedad y la información que contienen está sujeta a LIVA, LISR y LIEPS.
16+
Si requieres saber si caes en algún supuesto en donde requieras emitir este tipo de comprobantes
17+
te recomiendo lo verifiques con tu contador de confianza.
18+
19+
Entre las diferencias fundamentales de *CFDI de retenciones* y *CFDI tradicionales* están:
20+
21+
- El espacio de nombres XML <http://www.sat.gob.mx/esquemas/retencionpago/1>
22+
- El nombre de los atributos principales, como `retencion:Retenciones@Cert` en lugar de `cfdi:Comprobante@Certificado`
23+
- Tiene sus propios catálogos de información
24+
- El sello se procesa usando el algoritmo de digestión `SHA1` en lugar de `SHA256`
25+
- La fecha se expresa en ISO-8601 (contiene al final `-06:00`)
26+
27+
Entre las semejanzas están:
28+
29+
- Se maneja con complementos
30+
- Puede contener addendas
31+
- También requiere firmarse por un PAC entregando un Timbre Fiscal Digital
32+
33+
34+
### Herramientas gratuitas
35+
36+
Es importante notar que **el SAT no cuenta con una herramienta para elaborar CFDI de retenciones e información de pagos**
37+
como la tiene para los CFDI tradicionales, en su explicación de esquema operativo explican que se requiere utilizar
38+
a un PAC para poderlos emitir.
39+
40+
Por otro lado, el SAT no les ha exigido a los PAC que cuenten con herramientas gratuitas para elaborarlos,
41+
por lo que generalmente se requerirá contratar el uso de una aplicación o bien contratar los servicios de timbrado.
42+
43+
Con `CfdiUtils` podrás generar el *precfdi* y mandarlo timbrar con tu PAC habitual, el PAC te devolverá el *cfdi*
44+
incluyendo el timbre fiscal digital y este es el comprobante legal.
45+
46+
47+
### Más información
48+
49+
- Esquema de factura de retenciones e información de pagos
50+
<https://www.sat.gob.mx/consulta/65554/conoce-el-esquema-de-factura-electronica-de-retenciones-e-informacion-de-pagos>
51+
- Video chat No.21: CFDI de retenciones e información de pagos con sus complementos
52+
<https://www.youtube.com/watch?v=Z0TadVAjrFc>
53+
- Anexo 20
54+
<http://omawww.sat.gob.mx/tramitesyservicios/Paginas/anexo_20_version3-3.htm>
55+
- Preguntas frecuentes de retenciones e información de pagos
56+
<https://www.sat.gob.mx/cs/Satellite?blobcol=urldata&blobkey=id&blobtable=MungoBlobs&blobwhere=1461173416174&ssbinary=true>
57+
- Esquema XML de estructura
58+
<http://www.sat.gob.mx/esquemas/retencionpago/1/retencionpagov1.xsd>
59+
- Esquema XML de catálogos
60+
<http://www.sat.gob.mx/esquemas/retencionpago/1/catalogos/catRetenciones.xsd>
61+
- Archivo PDF con los catálogos
62+
<https://www.sat.gob.mx/cs/Satellite?blobcol=urldata&blobkey=id&blobtable=MungoBlobs&blobwhere=1461172330889&ssbinary=true>
63+
- Herramienta de transformación XML para generar la cadena original
64+
<http://www.sat.gob.mx/esquemas/retencionpago/1/retenciones.xslt>
65+
66+
67+
## Lectura de CFDI de retenciones e información de pagos
68+
69+
La estrategia para leer CFDI de retenciones e información de pagos es la misma que se utiliza para realizar la
70+
[lectura de CFDI](leer-cfdi.md). Consulta la información relacionada con el uso de
71+
[elementos](../componentes/elements.md), [nodos](../componentes/nodes.md) y [lectura rápida](quickreader.md).
72+
73+
### Clase `CfdiUtils\Retenciones\Retenciones`
74+
75+
El objeto que se utiliza para poder hacer la lectura es `CfdiUtils\Retenciones\Retenciones`. Y lo puedes inicializar
76+
por su constructor natural `$retenciones = new Retenciones(DOMDocument $document)` o con el constructor estático
77+
`$retenciones = Retenciones::newFromString(string $xmlContent)`.
78+
79+
### Inmutabilidad
80+
81+
El objeto `Retenciones` es inmutable, es creado con un objeto `DOMDocument` pero es clonado al momento de su
82+
construcción, por lo que si se hacen cambios en este objeto no se verán reflejados en el lector.
83+
A su vez, el objeto devuelto por los métodos `getDocument()` o `getNode()`, a pesar de ser en sí mismos mutables,
84+
no tendrán un impacto en el contenedor.
85+
86+
### Versión
87+
88+
El CFDI de retenciones solo tiene la versión 1.0, en caso de que se fabrique un objeto que contiene un número de versión
89+
diferente, entonces el método `Retenciones::getVersion()` devolverá una cadena vacía. Esto es por homogeneidad con la
90+
lectura de CFDI regulares.
91+
92+
### Lectura formal
93+
94+
La lectura formal utiliza [nodos](../componentes/nodes.md), que es una representación en memoria del contenido
95+
de la estructura XML. En esta forma, los elementos XML se deben acceder utilizando su prefijo y nombre exacto,
96+
y los atributos su nombre exacto. Para obtener el nodo raíz se usa el método `Retenciones::getNode()`.
97+
98+
### Lectura rápida
99+
100+
La lectura rápida utiliza [nodos](../componentes/nodes.md), que es una representación en memoria del contenido
101+
de la estructura XML. En esta forma, a diferencia del modo formal, los elementos se obtienen solo por su nombre
102+
simple y los nombres de elementos y atributos no son sensibles a mayúsculas y minúsculas.
103+
Para obtener el nodo raíz se usa el método `Retenciones::getQuickReader()`.
104+
105+
### Ejemplo de lectura de CFDI de retenciones e información de pagos
106+
107+
```php
108+
<?php
109+
110+
// construir el lector desde un contenido XML
111+
$xmlContent = file_get_contents('cfdi-retenciones-e-informacion-de-pagos.xml');
112+
$reader = \CfdiUtils\Retenciones\Retenciones::newFromString($xmlContent);
113+
114+
// obtener el nodo para lectura formal
115+
$nodeRetenciones = $reader->getNode();
116+
echo $nodeRetenciones->searchAttribute('retenciones:Emisor', 'RFCEmisor'); // AAA010101AAA
117+
118+
// obtener el QuickReader para lectura rápida
119+
$qrRetenciones = $reader->getQuickReader();
120+
echo $qrRetenciones->emisor['rfcemisor']; // AAA010101AAA
121+
```

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ nav:
1111
- instalar/instalacion.md
1212
- "Lectura":
1313
- leer/leer-cfdi.md
14+
- leer/leer-cfdi-retenciones.md
1415
- leer/quickreader.md
1516
- leer/limpieza-cfdi.md
1617
- "Validación":

src/CfdiUtils/Cfdi.php

Lines changed: 4 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,7 @@
22

33
namespace CfdiUtils;
44

5-
use CfdiUtils\Nodes\NodeInterface;
6-
use CfdiUtils\Nodes\XmlNodeUtils;
7-
use CfdiUtils\QuickReader\QuickReader;
8-
use CfdiUtils\QuickReader\QuickReaderImporter;
9-
use CfdiUtils\Utils\Xml;
5+
use CfdiUtils\Internals\XmlReaderTrait;
106
use DOMDocument;
117

128
/**
@@ -26,107 +22,14 @@
2622
*/
2723
class Cfdi
2824
{
29-
/** @var DOMDocument */
30-
private $document;
31-
32-
/** @var string */
33-
private $version;
34-
35-
/** @var string|null */
36-
private $source;
37-
38-
/** @var NodeInterface|null */
39-
private $node;
40-
41-
/** @var QuickReader|null */
42-
private $quickReader;
25+
use XmlReaderTrait;
4326

4427
const CFDI_NAMESPACE = 'http://www.sat.gob.mx/cfd/3';
4528

4629
public function __construct(DOMDocument $document)
4730
{
48-
$rootElement = Xml::documentElement($document);
49-
// is not docummented: lookupPrefix returns NULL instead of string when not found
50-
// this is why we are casting the value to string
51-
$nsPrefix = (string) $document->lookupPrefix(static::CFDI_NAMESPACE);
52-
if ('' === $nsPrefix) {
53-
throw new \UnexpectedValueException('Document does not implement namespace ' . static::CFDI_NAMESPACE);
54-
}
55-
if ('cfdi' !== $nsPrefix) {
56-
throw new \UnexpectedValueException('Prefix for namespace ' . static::CFDI_NAMESPACE . ' is not "cfdi"');
57-
}
58-
if ($rootElement->tagName !== $nsPrefix . ':Comprobante') {
59-
throw new \UnexpectedValueException('Root element is not Comprobante');
60-
}
61-
62-
$this->version = (new CfdiVersion())->getFromDOMElement($rootElement);
31+
$rootElement = self::checkRootElement($document, static::CFDI_NAMESPACE, 'cfdi', 'Comprobante');
6332
$this->document = clone $document;
64-
}
65-
66-
/**
67-
* Create a Cfdi object from a xml string
68-
*
69-
* @param string $content
70-
*
71-
* @return static
72-
*/
73-
public static function newFromString(string $content): self
74-
{
75-
$document = Xml::newDocumentContent($content);
76-
// populate source since it is already available
77-
// in this way we avoid the conversion from document to string
78-
$cfdi = new self($document);
79-
$cfdi->source = $content;
80-
return $cfdi;
81-
}
82-
83-
/**
84-
* Obtain the version from the CFDI, it is compatible with 3.2 and 3.3
85-
*/
86-
public function getVersion(): string
87-
{
88-
return $this->version;
89-
}
90-
91-
/**
92-
* Get a clone of the local DOM document
93-
*
94-
* @return DOMDocument
95-
*/
96-
public function getDocument(): DOMDocument
97-
{
98-
return clone $this->document;
99-
}
100-
101-
/**
102-
* Get the xml string source
103-
*/
104-
public function getSource(): string
105-
{
106-
if (null === $this->source) {
107-
// pass the document element to avoid xml header
108-
$this->source = $this->document->saveXML(Xml::documentElement($this->document));
109-
}
110-
return $this->source;
111-
}
112-
113-
/**
114-
* Get the node object to iterate in the CFDI
115-
*/
116-
public function getNode(): NodeInterface
117-
{
118-
if (null === $this->node) {
119-
$this->node = XmlNodeUtils::nodeFromXmlElement(Xml::documentElement($this->document));
120-
}
121-
return $this->node;
122-
}
123-
124-
public function getQuickReader(): QuickReader
125-
{
126-
if (null === $this->quickReader) {
127-
$this->quickReader = (new QuickReaderImporter())->importDocument($this->document);
128-
}
129-
130-
return $this->quickReader;
33+
$this->version = (new CfdiVersion())->getFromDOMElement($rootElement);
13134
}
13235
}

0 commit comments

Comments
 (0)