Skip to content

Commit

Permalink
Version 2.7.1
Browse files Browse the repository at this point in the history
  • Loading branch information
eclipxe13 committed Dec 4, 2018
2 parents e708b2f + 42a030e commit e3b7c34
Show file tree
Hide file tree
Showing 22 changed files with 252 additions and 47 deletions.
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ By participating in this project and its community, you are expected to uphold t
## Team members

* [Carlos C Soto](https://github.com/eclipxe13) - original author and maintainer
* [GitHub constributors](https://github.com/eclipxe13/CfdiUtils/graphs/contributors)
* [GitHub contributors](https://github.com/eclipxe13/CfdiUtils/graphs/contributors)

## Communication Channels

Expand Down
19 changes: 19 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,30 @@
- `\CfdiUtils\CadenaOrigen\DefaultLocations`
- `\CfdiUtils\CadenaOrigen\CadenaOrigenLocations`
- Remove `\CfdiUtils\PemPrivateKey\PemPrivateKey::isOpened` to `\CfdiUtils\PemPrivateKey\PemPrivateKey::isOpen`
- Remove `CfdiUtils\ConsultaCfdiSat\Config::getWsdlUrl()`
- Remove `static` methods from `\CfdiUtils\CfdiVersion`, instead create an instance of the class
- Remove `static` methods from `\CfdiUtils\TimbreFiscalDigital\TfdVersion`, instead create an instance of the class
- Remove `trigger_error` on `\CfdiUtils\Elements\Cfdi33\Comprobante::getCfdiRelacionados` when called with arguments.


## Version 2.7.1 2018-12-04

- Fix wrong use of `escapeshellcmd` replacing with `escapeshellarg`
- Add argument `-c|--clean` to script `tests/validate.php` to perform clean before validate
- Fix `CfdiCreator33::newUsingNode` since not all attributes where correctly imported (`xsi:schemaLocation`)
- Fix calling `CfdiCreator33::putCertificado` on imported cfdi (Emisor child is a `NodeInterface` but not `Emisor`)
- Refactor `CfdiUtils\ConsultaCfdiSat\WebService` to be able to use a local copy of WSDL file since SAT does not
allow to download it anymore.
- Add `ConsultaCFDIServiceSAT.svc.xml` to namespace folder
- Add `Config::wsdlLocation` static method to get `ConsultaCFDIServiceSAT.svc.xml` file path
- Deprecate `Config::getWsdlUrl` in favor of `Config::getServiceUrl`
- Add `Config::getServiceUrl`
- Add `Config::wsdlLocation` property
- Add `--local-wsdl` parameter to `tests/estadosat.php` script
- Add a new step on `CfdiUtils\Cleaner\Cleaner` that removes from `xsi:schemaLocations` the namespaces that are
not followed by a string that ends on `.xsd`


## Version 2.7.0 2018-10-19

- Reintroduce `MontoGreaterOrEqualThanSumOfDocuments` as `PAGO30`.
Expand Down
19 changes: 18 additions & 1 deletion docs/componentes/estado-sat.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

El SAT cuenta con un webservice para consultar el estado de un CFDI.

- Servicio: <https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?singleWsdl>
- Servicio: <https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc>
- Documentación: <ftp://ftp2.sat.gob.mx/asistencia_servicio_ftp/publicaciones/cfdi/WS_ConsultaCFDI.pdf>

Para poderlo consumir se han implementado varias clases dentro del espacio de nonbres `\CfdiUtils\ConsultaCfdiSat`.
Expand Down Expand Up @@ -136,6 +136,23 @@ $response->getCode(); // S - ...
$response->getCfdi(); // Cancelado
```

## Problema con el webservice del SAT

El SAT a partir de octubre 2018 dejó de publicar el archivo de contrato WSL del servicio web ubicado en
<https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc?singleWsdl>, sin embargo el servicio
sigue funcionando por lo que ahora es necesario especificar una la copia local del archivo WSDL.

Si no cuenta con el archivo WSDL esta librería contiene una copia que se puede obtener llamando al método
estático `CfdiUtils\ConsultaCfdiSat\Config::getLocalWsdlLocation()`.

```php
<?php
$wsdlLocalCopy = \CfdiUtils\ConsultaCfdiSat\Config::getLocalWsdlLocation();
$config = new \CfdiUtils\ConsultaCfdiSat\Config(10, true, '', $wsdlLocalCopy);
$webservice = new \CfdiUtils\ConsultaCfdiSat\WebService($config);
```


## Posibles futuros cambios

Usar alguna librería como <https://github.com/phpro/soap-client> o <https://github.com/meng-tian/async-soap-guzzle>
Expand Down
5 changes: 5 additions & 0 deletions docs/leer/limpieza-cfdi.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ Para evitar estos errores se puede usar el objeto `CfdiUtils\Cleaner\Cleaner`.
Este objeto requiere una cadena de texto con XML válido. Y limpia el XML siguiendo estos pasos:

1. Remueve el nodo `cfdi:Addenda`.
1. Remueve dentro de las locaciones de espacios de nombre `xsi:schemaLocation` los namespaces que no tengan
a continuación una uri que termine en `.xsd`.
1. Remueve todos los nodos que no tengan relación con el SAT (los que no contengan `http://www.sat.gob.mx/`).
1. Remueve todos los pares de espacio de nombre y archivo xsd de los `xsi:schemaLocation` que no tengan relación con el SAT.
1. Remueve todos los espacios de nombres listados que no están en uso.
Expand All @@ -38,3 +40,6 @@ También se puede instanciar un objeto de la clase `CfdiUtils\Cleaner\Cleaner` y
- `load(string $content)`: Carga un contenido XML "sucio"
- `clean()`: Realiza la limpieza
- `retrieveXml()`: Obtiene el contenido XML "limpio"

Si deseas implementar tu propio orden, hacer o agregar nuevos limpiadores puedes extender la clase o sobrescribir
el método `clean` o bien llamar a cada uno de los pasos de limpieza por tu propia cuenta.
2 changes: 1 addition & 1 deletion src/CfdiUtils/CadenaOrigen/SaxonbCliBuilder.php
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public function createCommand(string $xmlFile, string $xsltLocation): string
// if is running on windows then use NUL instead of /dev/null
$devnull = (0 === stripos(PHP_OS, 'win')) ? 'NUL' : '/dev/null';
return implode(' ', [
escapeshellcmd($this->getExecutablePath()),
escapeshellarg($this->getExecutablePath()),
escapeshellarg('-s:' . $xmlFile),
escapeshellarg('-xsl:' . $xsltLocation),
escapeshellarg('-warnings:silent'), // default recover
Expand Down
15 changes: 10 additions & 5 deletions src/CfdiUtils/CfdiCreator33.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,18 @@ class CfdiCreator33 implements

/**
* CfdiCreator33 constructor.
* @param string[] $complementoAttributes
* @param string[] $comprobanteAttributes
* @param Certificado|null $certificado
* @param XmlResolver|null $xmlResolver
* @param XsltBuilderInterface|null $xsltBuilder
*/
public function __construct(
array $complementoAttributes = [],
array $comprobanteAttributes = [],
Certificado $certificado = null,
XmlResolver $xmlResolver = null,
XsltBuilderInterface $xsltBuilder = null
) {
$this->comprobante = new Comprobante($complementoAttributes);
$this->comprobante = new Comprobante($comprobanteAttributes);
$this->setXmlResolver($xmlResolver ? : new XmlResolver());
if (null !== $certificado) {
$this->putCertificado($certificado);
Expand All @@ -59,8 +59,9 @@ public static function newUsingNode(
Certificado $certificado = null,
XmlResolver $xmlResolver = null
): self {
$new = new self($node->attributes()->exportArray(), $certificado, $xmlResolver);
$new = new self([], $certificado, $xmlResolver);
$comprobante = $new->comprobante();
$comprobante->addAttributes($node->attributes()->exportArray());
foreach ($node as $child) {
$comprobante->addChild($child);
}
Expand All @@ -81,7 +82,11 @@ public function putCertificado(Certificado $certificado, bool $putEmisorRfcNombr
$this->comprobante['Certificado'] = base64_encode((string) file_get_contents($cerfile));
}
if ($putEmisorRfcNombre) {
$this->comprobante->addEmisor([
$emisor = $this->comprobante->searchNode('cfdi:Emisor');
if (null === $emisor) {
$emisor = $this->comprobante->getEmisor();
}
$emisor->addAttributes([
'Nombre' => $certificado->getName(),
'Rfc' => $certificado->getRfc(),
]);
Expand Down
49 changes: 43 additions & 6 deletions src/CfdiUtils/Cleaner/Cleaner.php
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ public static function isNameSpaceAllowed(string $namespace): bool
public function clean()
{
$this->removeAddenda();
$this->removeIncompleteSchemaLocations();
$this->removeNonSatNSNodes();
$this->removeNonSatNSschemaLocations();
$this->removeUnusedNamespaces();
Expand Down Expand Up @@ -157,6 +158,37 @@ public function removeAddenda()
}
}

/**
* Procedure to drop schemaLocations where second part does not ends with '.xsd'
*
* @return void
*/
public function removeIncompleteSchemaLocations()
{
$schemaLocations = $this->obtainXsiSchemaLocations();
for ($s = 0; $s < $schemaLocations->length; $s++) {
$element = $schemaLocations->item($s);
if (null !== $element) {
$element->nodeValue = $this->removeIncompleteSchemaLocation($element->nodeValue);
}
}
}

public function removeIncompleteSchemaLocation(string $source): string
{
$components = array_values(array_filter(array_map('trim', explode(' ', $source))));
$length = count($components);
for ($c = 0; $c < $length; $c = $c + 1) {
$xsd = $components[$c + 1] ?? '';
if ((0 === strcasecmp('.xsd', substr($xsd, -4, 4)))) {
$c = $c + 1;
continue;
}
$components[$c] = '';
}
return strval(implode(' ', array_filter($components)));
}

/**
* Procedure to drop schemaLocations that are not allowed
* If the schemaLocation is empty then remove the attribute
Expand All @@ -165,12 +197,7 @@ public function removeAddenda()
*/
public function removeNonSatNSschemaLocations()
{
// Do not assume that prefix for http://www.w3.org/2001/XMLSchema-instance is "xsi"
$xsi = $this->dom()->lookupPrefix('http://www.w3.org/2001/XMLSchema-instance');
if (! $xsi) {
return;
}
$schemaLocations = $this->xpathQuery("//@$xsi:schemaLocation");
$schemaLocations = $this->obtainXsiSchemaLocations();
for ($s = 0; $s < $schemaLocations->length; $s++) {
$element = $schemaLocations->item($s);
if (null !== $element) {
Expand Down Expand Up @@ -265,6 +292,16 @@ public function removeUnusedNamespaces()
}
}

private function obtainXsiSchemaLocations(): DOMNodeList
{
// Do not assume that prefix for http://www.w3.org/2001/XMLSchema-instance is "xsi"
$xsi = $this->dom()->lookupPrefix('http://www.w3.org/2001/XMLSchema-instance');
if (! $xsi) {
return new DOMNodeList();
}
return $this->xpathQuery("//@$xsi:schemaLocation");
}

/**
* Helper function to perform a XPath query using an element (or root element)
* @param string $query
Expand Down
38 changes: 33 additions & 5 deletions src/CfdiUtils/ConsultaCfdiSat/Config.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,21 @@ class Config
private $verifyPeer;

/** @var string */
private $wsdlUrl;
private $serviceUrl;

public function __construct(int $timeout = 10, bool $verifyPeer = true, string $wsdlUrl = '')
{
/** @var string */
private $wsdlLocation;

public function __construct(
int $timeout = 10,
bool $verifyPeer = true,
string $serviceUrl = '',
string $wsdlLocation = ''
) {
$this->timeout = $timeout;
$this->verifyPeer = $verifyPeer;
$this->wsdlUrl = $wsdlUrl ? : static::DEFAULT_WSDL_URL;
$this->serviceUrl = $serviceUrl ? : static::DEFAULT_WSDL_URL;
$this->wsdlLocation = $wsdlLocation ? : $this->serviceUrl;
}

public function getTimeout(): int
Expand All @@ -31,8 +39,28 @@ public function shouldVerifyPeer(): bool
return $this->verifyPeer;
}

/**
* @deprecated since Version 2.7.1 in favor of getServiceUrl
* @see getServiceUrl
* @return string
*/
public function getWsdlUrl(): string
{
return $this->wsdlUrl;
return $this->getServiceUrl();
}

public function getServiceUrl(): string
{
return $this->serviceUrl;
}

public function getWsdlLocation(): string
{
return $this->wsdlLocation;
}

public static function getLocalWsdlLocation(): string
{
return __DIR__ . DIRECTORY_SEPARATOR . 'ConsultaCFDIServiceSAT.svc.xml';
}
}
2 changes: 2 additions & 0 deletions src/CfdiUtils/ConsultaCfdiSat/ConsultaCFDIServiceSAT.svc.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions name="ConsultaCFDIService" targetNamespace="http://tempuri.org/" xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" xmlns:wsap="http://schemas.xmlsoap.org/ws/2004/08/addressing/policy" xmlns:wsa10="http://www.w3.org/2005/08/addressing" xmlns:tns="http://tempuri.org/" xmlns:msc="http://schemas.microsoft.com/ws/2005/12/wsdl/contract" xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/" xmlns:wsx="http://schemas.xmlsoap.org/ws/2004/09/mex" xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:wsam="http://www.w3.org/2007/05/addressing/metadata" xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:wsaw="http://www.w3.org/2006/05/addressing/wsdl" xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><wsp:Policy wsu:Id="BasicHttpsBinding_IConsultaCFDIService_policy"><wsp:ExactlyOne><wsp:All><sp:TransportBinding xmlns:sp="http://schemas.xmlsoap.org/ws/2005/07/securitypolicy"><wsp:Policy><sp:TransportToken><wsp:Policy><sp:HttpsToken RequireClientCertificate="false"/></wsp:Policy></sp:TransportToken><sp:AlgorithmSuite><wsp:Policy><sp:Basic256/></wsp:Policy></sp:AlgorithmSuite><sp:Layout><wsp:Policy><sp:Strict/></wsp:Policy></sp:Layout></wsp:Policy></sp:TransportBinding></wsp:All></wsp:ExactlyOne></wsp:Policy><wsdl:types><xs:schema elementFormDefault="qualified" targetNamespace="http://tempuri.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema"><xs:import namespace="http://schemas.datacontract.org/2004/07/Sat.Cfdi.Negocio.ConsultaCfdi.Servicio"/><xs:element name="Consulta"><xs:complexType><xs:sequence><xs:element minOccurs="0" name="expresionImpresa" nillable="true" type="xs:string"/></xs:sequence></xs:complexType></xs:element><xs:element name="ConsultaResponse"><xs:complexType><xs:sequence><xs:element minOccurs="0" name="ConsultaResult" nillable="true" type="q1:Acuse" xmlns:q1="http://schemas.datacontract.org/2004/07/Sat.Cfdi.Negocio.ConsultaCfdi.Servicio"/></xs:sequence></xs:complexType></xs:element></xs:schema><xs:schema attributeFormDefault="qualified" elementFormDefault="qualified" targetNamespace="http://schemas.microsoft.com/2003/10/Serialization/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.microsoft.com/2003/10/Serialization/"><xs:element name="anyType" nillable="true" type="xs:anyType"/><xs:element name="anyURI" nillable="true" type="xs:anyURI"/><xs:element name="base64Binary" nillable="true" type="xs:base64Binary"/><xs:element name="boolean" nillable="true" type="xs:boolean"/><xs:element name="byte" nillable="true" type="xs:byte"/><xs:element name="dateTime" nillable="true" type="xs:dateTime"/><xs:element name="decimal" nillable="true" type="xs:decimal"/><xs:element name="double" nillable="true" type="xs:double"/><xs:element name="float" nillable="true" type="xs:float"/><xs:element name="int" nillable="true" type="xs:int"/><xs:element name="long" nillable="true" type="xs:long"/><xs:element name="QName" nillable="true" type="xs:QName"/><xs:element name="short" nillable="true" type="xs:short"/><xs:element name="string" nillable="true" type="xs:string"/><xs:element name="unsignedByte" nillable="true" type="xs:unsignedByte"/><xs:element name="unsignedInt" nillable="true" type="xs:unsignedInt"/><xs:element name="unsignedLong" nillable="true" type="xs:unsignedLong"/><xs:element name="unsignedShort" nillable="true" type="xs:unsignedShort"/><xs:element name="char" nillable="true" type="tns:char"/><xs:simpleType name="char"><xs:restriction base="xs:int"/></xs:simpleType><xs:element name="duration" nillable="true" type="tns:duration"/><xs:simpleType name="duration"><xs:restriction base="xs:duration"><xs:pattern value="\-?P(\d*D)?(T(\d*H)?(\d*M)?(\d*(\.\d*)?S)?)?"/><xs:minInclusive value="-P10675199DT2H48M5.4775808S"/><xs:maxInclusive value="P10675199DT2H48M5.4775807S"/></xs:restriction></xs:simpleType><xs:element name="guid" nillable="true" type="tns:guid"/><xs:simpleType name="guid"><xs:restriction base="xs:string"><xs:pattern value="[\da-fA-F]{8}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{4}-[\da-fA-F]{12}"/></xs:restriction></xs:simpleType><xs:attribute name="FactoryType" type="xs:QName"/><xs:attribute name="Id" type="xs:ID"/><xs:attribute name="Ref" type="xs:IDREF"/></xs:schema><xs:schema elementFormDefault="qualified" targetNamespace="http://schemas.datacontract.org/2004/07/Sat.Cfdi.Negocio.ConsultaCfdi.Servicio" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://schemas.datacontract.org/2004/07/Sat.Cfdi.Negocio.ConsultaCfdi.Servicio"><xs:complexType name="Acuse"><xs:sequence><xs:element minOccurs="0" name="CodigoEstatus" nillable="true" type="xs:string"/><xs:element minOccurs="0" name="EsCancelable" nillable="true" type="xs:string"/><xs:element minOccurs="0" name="Estado" nillable="true" type="xs:string"/><xs:element minOccurs="0" name="EstatusCancelacion" nillable="true" type="xs:string"/></xs:sequence></xs:complexType><xs:element name="Acuse" nillable="true" type="tns:Acuse"/></xs:schema></wsdl:types><wsdl:message name="IConsultaCFDIService_Consulta_InputMessage"><wsdl:part name="parameters" element="tns:Consulta"/></wsdl:message><wsdl:message name="IConsultaCFDIService_Consulta_OutputMessage"><wsdl:part name="parameters" element="tns:ConsultaResponse"/></wsdl:message><wsdl:portType name="IConsultaCFDIService"><wsdl:operation name="Consulta"><wsdl:input wsaw:Action="http://tempuri.org/IConsultaCFDIService/Consulta" message="tns:IConsultaCFDIService_Consulta_InputMessage"/><wsdl:output wsaw:Action="http://tempuri.org/IConsultaCFDIService/ConsultaResponse" message="tns:IConsultaCFDIService_Consulta_OutputMessage"/></wsdl:operation></wsdl:portType><wsdl:binding name="BasicHttpBinding_IConsultaCFDIService" type="tns:IConsultaCFDIService"><soap:binding transport="http://schemas.xmlsoap.org/soap/http"/><wsdl:operation name="Consulta"><soap:operation soapAction="http://tempuri.org/IConsultaCFDIService/Consulta" style="document"/><wsdl:input><soap:body use="literal"/></wsdl:input><wsdl:output><soap:body use="literal"/></wsdl:output></wsdl:operation></wsdl:binding><wsdl:binding name="BasicHttpsBinding_IConsultaCFDIService" type="tns:IConsultaCFDIService"><wsp:PolicyReference URI="#BasicHttpsBinding_IConsultaCFDIService_policy"/><soap:binding transport="http://schemas.xmlsoap.org/soap/http"/><wsdl:operation name="Consulta"><soap:operation soapAction="http://tempuri.org/IConsultaCFDIService/Consulta" style="document"/><wsdl:input><soap:body use="literal"/></wsdl:input><wsdl:output><soap:body use="literal"/></wsdl:output></wsdl:operation></wsdl:binding><wsdl:service name="ConsultaCFDIService"><wsdl:port name="BasicHttpBinding_IConsultaCFDIService" binding="tns:BasicHttpBinding_IConsultaCFDIService"><soap:address location="https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc"/></wsdl:port><wsdl:port name="BasicHttpsBinding_IConsultaCFDIService" binding="tns:BasicHttpsBinding_IConsultaCFDIService"><soap:address location="https://consultaqr.facturaelectronica.sat.gob.mx/ConsultaCFDIService.svc"/></wsdl:port></wsdl:service></wsdl:definitions>
3 changes: 2 additions & 1 deletion src/CfdiUtils/ConsultaCfdiSat/WebService.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ protected function createSoapClient(): SoapClient
{
$config = $this->getConfig();
$soapOptions = [
'location' => $config->getServiceUrl(),
'soap_version' => SOAP_1_1,
'cache_wsdl' => WSDL_CACHE_NONE,
'exceptions' => 1,
Expand All @@ -50,7 +51,7 @@ protected function createSoapClient(): SoapClient
'trace' => false, // use this setting for development
];

return new SoapClient($config->getWsdlUrl(), $soapOptions);
return new SoapClient($config->getWsdlLocation(), $soapOptions);
}

public function request(RequestParameters $requestParameters): StatusResponse
Expand Down
Loading

0 comments on commit e3b7c34

Please sign in to comment.