From 6d497759b43538dc18d953283b1a49cc9426e22e Mon Sep 17 00:00:00 2001 From: Carlos C Soto Date: Fri, 30 Sep 2022 17:18:07 -0500 Subject: [PATCH] Fix query by uuid --- README.md | 19 ++++- docs/CHANGELOG.md | 14 ++++ docs/problema-filtros-no-aplicados.md | 11 ++- .../FielRequestBuilder/FielRequestBuilder.php | 73 +++++++++++-------- .../ConsumeCfdiServicesUsingFakeFielTest.php | 17 ++++- ...meRetencionesServicesUsingFakeFielTest.php | 19 ++++- .../FielRequestBuilderTest.php | 24 +++++- ...ed.xml => request-received-by-filters.xml} | 10 ++- .../_files/query/request-received-by-uuid.xml | 33 +++++++++ 9 files changed, 175 insertions(+), 45 deletions(-) rename tests/_files/query/{request-received.xml => request-received-by-filters.xml} (86%) create mode 100644 tests/_files/query/request-received-by-uuid.xml diff --git a/README.md b/README.md index 259f80f..1b58855 100644 --- a/README.md +++ b/README.md @@ -272,7 +272,7 @@ Puede que los cambios del ejemplo no sean lógicos, es solo para ilustrar cómo - Filtrando por únicamente documentos vigentes (excluye cancelados). - Filtrando por el RFC a cuenta de terceros `XXX01010199A`. - Filtrando por el RFC contraparte `MAG041126GT8`. Como se solicitan recibidos, entonces son los emidos por ese RFC. -- Filtrando por el UUID `96623061-61fe-49de-b298-c7156476aa8b` +- Filtrando por el UUID `96623061-61fe-49de-b298-c7156476aa8b`. ```php withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) +; +``` + ### Verificar una consulta La verificación depende de que la consulta haya sido aceptada. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index b13595e..a4f437e 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -14,6 +14,20 @@ que nombraremos así: ` Breaking . Feature . Fix `, donde: **Importante:** Las reglas de SEMVER no aplican si estás usando una rama (por ejemplo `main-dev`) o estás usando una versión cero (por ejemplo `0.18.4`). +## Versión 0.5.2 2022-09-30 + +#### Consulta por UUID + +Gracias a la solicitud de cambios en [`luisiturrios1/python-cfdiclient#42`](https://github.com/luisiturrios1/python-cfdiclient/pull/42) +por `@alan196`, hemos podido verificar que la documentación del servicio con respecto a la consulta por UUID está incorrecta. + +- El campo no se llama `UUID`, se llama `Folio`. +- El campo `RfcSolicitante` no se debe omitir. +- El campo `TipoSolicitud` no se debe omitir. +- Los demás campos no deben existir. + +Por lo tanto, se han hecho las correcciones necesarias para hacer la consulta por `UUID`. + ## Versión 0.5.1 2022-09-28 ### Se corrigen XML mal formados diff --git a/docs/problema-filtros-no-aplicados.md b/docs/problema-filtros-no-aplicados.md index 3f3e7c6..7d8dc93 100644 --- a/docs/problema-filtros-no-aplicados.md +++ b/docs/problema-filtros-no-aplicados.md @@ -19,17 +19,24 @@ En esta versión se agregan nuevos filtros, permitiendo hacer solicitudes más e Los siguientes filtros no están funcionando en la consulta de CFDI Regulares: -- Filtro por UUID. - Filtro de documentos cancelados cuando se solicita un paquete de documentos XML. El filtro funciona correctamente para paquetes de Metadata. Los siguientes filtros no están funcionando en la consulta de CFDI de retenciones e información de pagos: - Filtro por complemento. -- Filtro por UUID. ## Actualizaciones +### 2022-09-30 + +Hemos notado que la documentación del SAT en relación con la consulta por UUID está incorrecta: + +- El campo no se llama `UUID`, se llama `Folio`. +- El campo `RfcSolicitante` se debe especificar. +- El campo `TipoSolicitud` se debe especificar. +- Los demás campos no deben existir. + ### 2022-03-04 El SAT actualiza el servicio a la versión 1.2, no resuelve las solicitudes ingresadas. diff --git a/src/RequestBuilder/FielRequestBuilder/FielRequestBuilder.php b/src/RequestBuilder/FielRequestBuilder/FielRequestBuilder.php index 2680038..d6877e6 100644 --- a/src/RequestBuilder/FielRequestBuilder/FielRequestBuilder.php +++ b/src/RequestBuilder/FielRequestBuilder/FielRequestBuilder.php @@ -74,34 +74,57 @@ public function authorization(DateTime $created, DateTime $expires, string $secu public function query(QueryParameters $queryParameters): string { - // normalize input - $start = $queryParameters->getPeriod()->getStart()->format('Y-m-d\TH:i:s'); - $end = $queryParameters->getPeriod()->getEnd()->format('Y-m-d\TH:i:s'); + $queryByUuid = ! $queryParameters->getUuid()->isEmpty(); + $xmlRfcReceived = ''; $requestType = $queryParameters->getRequestType()->getQueryAttributeValue($queryParameters->getServiceType()); $rfcSigner = mb_strtoupper($this->getFiel()->getRfc()); - if ($queryParameters->getDownloadType()->isIssued()) { - // issued documents, counterparts are receivers - $rfcIssuer = $rfcSigner; - $rfcReceivers = $queryParameters->getRfcMatches(); - } else { - // received documents, counterpart is issuer - $rfcIssuer = $queryParameters->getRfcMatches()->getFirst()->getValue(); - $rfcReceivers = RfcMatches::createFromValues($rfcSigner); - } - $solicitudAttributes = array_filter( - [ - 'RfcSolicitante' => $rfcSigner, + $solicitudAttributes = [ + 'RfcSolicitante' => $rfcSigner, + 'TipoSolicitud' => $requestType, + ]; + + if ($queryByUuid) { + $solicitudAttributes = $solicitudAttributes + [ + 'Folio' => $queryParameters->getUuid()->getValue(), + ]; + } else { + $start = $queryParameters->getPeriod()->getStart()->format('Y-m-d\TH:i:s'); + $end = $queryParameters->getPeriod()->getEnd()->format('Y-m-d\TH:i:s'); + if ($queryParameters->getDownloadType()->isIssued()) { + // issued documents, counterparts are receivers + $rfcIssuer = $rfcSigner; + $rfcReceivers = $queryParameters->getRfcMatches(); + } else { + // received documents, counterpart is issuer + $rfcIssuer = $queryParameters->getRfcMatches()->getFirst()->getValue(); + $rfcReceivers = RfcMatches::createFromValues($rfcSigner); + } + $solicitudAttributes = $solicitudAttributes + [ 'FechaInicial' => $start, 'FechaFinal' => $end, - 'TipoSolicitud' => $requestType, 'RfcEmisor' => $rfcIssuer, 'TipoComprobante' => $queryParameters->getDocumentType()->value(), 'EstadoComprobante' => $queryParameters->getDocumentStatus()->value(), - 'UUID' => $queryParameters->getUuid()->getValue(), 'RfcACuentaTerceros' => $queryParameters->getRfcOnBehalf()->getValue(), 'Complemento' => $queryParameters->getComplement()->value(), - ], + ]; + if (! $rfcReceivers->isEmpty()) { + $xmlRfcReceived = implode('', array_map( + function (RfcMatch $rfcMatch): string { + return sprintf( + '%s', + $this->parseXml($rfcMatch->getValue()) + ); + }, + iterator_to_array($rfcReceivers) + )); + $xmlRfcReceived = "{$xmlRfcReceived}"; + } + } + + $solicitudAttributes = array_filter( + $solicitudAttributes, static function (string $value): bool { return '' !== $value; } @@ -116,20 +139,6 @@ function (string $name, string $value): string { $solicitudAttributes, )); - $xmlRfcReceived = ''; - if (! $rfcReceivers->isEmpty()) { - $xmlRfcReceived = implode('', array_map( - function (RfcMatch $rfcMatch): string { - return sprintf( - '%s', - $this->parseXml($rfcMatch->getValue()) - ); - }, - iterator_to_array($rfcReceivers) - )); - $xmlRfcReceived = "{$xmlRfcReceived}"; - } - $toDigestXml = << diff --git a/tests/Integration/ConsumeCfdiServicesUsingFakeFielTest.php b/tests/Integration/ConsumeCfdiServicesUsingFakeFielTest.php index 4dff080..369e241 100644 --- a/tests/Integration/ConsumeCfdiServicesUsingFakeFielTest.php +++ b/tests/Integration/ConsumeCfdiServicesUsingFakeFielTest.php @@ -36,7 +36,6 @@ public function testQueryChangeAllParameters(): void ->withDocumentType(DocumentType::nomina()) ->withComplement(ComplementoCfdi::nomina12()) ->withDocumentStatus(DocumentStatus::active()) - ->withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) ->withRfcOnBehalf(RfcOnBehalf::create('XXX01010199A')) ->withRfcMatch(RfcMatch::create('AAA010101AAA')) ; @@ -49,6 +48,22 @@ public function testQueryChangeAllParameters(): void ); } + public function testQueryUuid(): void + { + $service = $this->createService(); + + $parameters = QueryParameters::create() + ->withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) + ; + + $result = $service->query($parameters); + $this->assertSame( + 305, + $result->getStatus()->getCode(), + 'Expected to receive a 305 - Certificado Inválido from SAT since FIEL is for testing' + ); + } + public function testServiceEndpointsDifferentThanQueryEndpointsThrowsError(): void { $service = $this->createService(); diff --git a/tests/Integration/ConsumeRetencionesServicesUsingFakeFielTest.php b/tests/Integration/ConsumeRetencionesServicesUsingFakeFielTest.php index 6cd1119..7a1c0d9 100644 --- a/tests/Integration/ConsumeRetencionesServicesUsingFakeFielTest.php +++ b/tests/Integration/ConsumeRetencionesServicesUsingFakeFielTest.php @@ -27,7 +27,7 @@ protected function getServiceEndpoints(): ServiceEndpoints return ServiceEndpoints::retenciones(); } - public function testQueryChangeAllParameters(): void + public function testQueryChangeFilters(): void { $service = $this->createService(); @@ -37,7 +37,6 @@ public function testQueryChangeAllParameters(): void ->withRequestType(RequestType::xml()) ->withComplement(ComplementoRetenciones::undefined()) ->withDocumentStatus(DocumentStatus::active()) - ->withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) ->withRfcOnBehalf(RfcOnBehalf::create('XXX01010199A')) ->withRfcMatch(RfcMatch::create('AAA010101AAA')) ; @@ -50,6 +49,22 @@ public function testQueryChangeAllParameters(): void ); } + public function testQueryByUuid(): void + { + $service = $this->createService(); + + $parameters = QueryParameters::create() + ->withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) + ; + + $result = $service->query($parameters); + $this->assertSame( + 305, + $result->getStatus()->getCode(), + 'Expected to receive a 305 - Certificado Inválido from SAT since FIEL is for testing' + ); + } + public function testServiceEndpointsDifferentThanQueryEndpointsThrowsError(): void { $service = $this->createService(); diff --git a/tests/Unit/RequestBuilder/FielRequestBuilder/FielRequestBuilderTest.php b/tests/Unit/RequestBuilder/FielRequestBuilder/FielRequestBuilderTest.php index 2c5ea3c..65c429b 100644 --- a/tests/Unit/RequestBuilder/FielRequestBuilder/FielRequestBuilderTest.php +++ b/tests/Unit/RequestBuilder/FielRequestBuilder/FielRequestBuilderTest.php @@ -91,7 +91,7 @@ private function extractSecurityTokenFromXml(string $requestBody): string return $matches['id'] ?? ''; } - public function testQueryReceived(): void + public function testQueryReceivedByFilters(): void { $requestBuilder = $this->createFielRequestBuilderUsingTestingFiles(); $parameters = QueryParameters::create() @@ -102,14 +102,32 @@ public function testQueryReceived(): void ->withDocumentType(DocumentType::nomina()) ->withComplement(ComplementoCfdi::nomina12()) ->withDocumentStatus(DocumentStatus::active()) - ->withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) ->withRfcOnBehalf(RfcOnBehalf::create('XXX01010199A')) ->withRfcMatch(RfcMatch::create('AAA010101AAA')) ; $requestBody = $requestBuilder->query($parameters); $this->assertSame( - $this->xmlFormat(Helpers::nospaces($this->fileContents('query/request-received.xml'))), + $this->xmlFormat(Helpers::nospaces($this->fileContents('query/request-received-by-filters.xml'))), + $this->xmlFormat($requestBody) + ); + + $xmlSecVerification = (new EnvelopSignatureVerifier()) + ->verify($requestBody, 'http://DescargaMasivaTerceros.sat.gob.mx', 'SolicitaDescarga'); + $this->assertTrue($xmlSecVerification, 'The signature cannot be verified using XMLSecLibs'); + } + + public function testQueryReceivedByUuid(): void + { + $requestBuilder = $this->createFielRequestBuilderUsingTestingFiles(); + $parameters = QueryParameters::create() + ->withServiceType(ServiceType::cfdi()) + ->withUuid(Uuid::create('96623061-61fe-49de-b298-c7156476aa8b')) + ; + $requestBody = $requestBuilder->query($parameters); + + $this->assertSame( + $this->xmlFormat(Helpers::nospaces($this->fileContents('query/request-received-by-uuid.xml'))), $this->xmlFormat($requestBody) ); diff --git a/tests/_files/query/request-received.xml b/tests/_files/query/request-received-by-filters.xml similarity index 86% rename from tests/_files/query/request-received.xml rename to tests/_files/query/request-received-by-filters.xml index b12ac5b..ed6be76 100644 --- a/tests/_files/query/request-received.xml +++ b/tests/_files/query/request-received-by-filters.xml @@ -3,8 +3,10 @@ - - EKU9003173C9 + + + EKU9003173C9 + @@ -14,10 +16,10 @@ - lvCqCw1nWeW7biQuSsjD2KltrQ4= + urJ4pg32vV2oozJcBBKRcxoAydo= - KSTQj+KcLjZEw2iLt2YZn/KB7b96ciHZLci9cHAKl/uGkt7XpQnV1X18ZX9spZZZvlkQyH/gDoEgtMA0yb1oO6ACO66hnfQTaY6b3luhIKrIN9VOdBus6ymrKNhj4mI+ev+HIn1jX1vH8KuVm0lcQ87via8RoOulyvR7lmbuSGILJsshGheofV+eibBdqWe8ukGYYu/iXE9xp+NmI2ltNYizn8fM3wV7F7H51WoJL0lokapcnRk3AeQAbwWw9Yh1dCsb1CqhyRQp6D9fOHpPbQSdhdpdHn774DbH/sqG658Hvx343WrwjDzotLPLVKfGnSTcihDSka6nqXb+EGc4UQ== + U0+zQI1YH5DGtlULhm01lGwoBd1vdbIv/myz/gPshujerNvRzzw9mhGCCq8E7BMsLKefbl4TTOOkL5wvFqczzcKrHS8lFTkw7073CIPVrUyhbajCaqDGRtTvHx84xpi83vknZJhuOu+Pl6gyNIu/g4/MSFtSZ9CKqIu62VBAuepGggY2jsceyMIqNhtLOuR9yGSPR2kTABypkIjvyK9NqM/JivCmHV3FXv6280eHcf40gAtHpTw++ERSRuhW/rkimyJrHLIeMvZpfT9JpORsUCbUHmdEOQae0zXHp0mcGV0woWiRKSBcA2yogl+rrqLc1cbsX5I6jfYTdaegcoe0eQ== diff --git a/tests/_files/query/request-received-by-uuid.xml b/tests/_files/query/request-received-by-uuid.xml new file mode 100644 index 0000000..d39bad1 --- /dev/null +++ b/tests/_files/query/request-received-by-uuid.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + A0teWlShxOD/65O111KTBA+TOSk= + + + d0KaOIjhmXRKuSmbqP/tFoyEqBiNwab5rd8L9mCnjg0TxtbRgJz9+5I7LP0JwJiVWlzcPFoOwth68vSmFqr+hEtvpYlYPmUrPMMiheIv+6D28DLErBlZSRmnXuu3EJuI7D29JAMB2MYML8N8n8Y/NOLGhO3qQIJDAa8AjQCIxgokipU9iZsHtDcHcHZOocCFMWNOBTDLdd8TOsi6HNgWEDeUL18nJ7W1QOamQAU3fmKSPF3I37pISu/MHExPayqDZEaDkbKl0u9j8GGSlFyOreLjvkyfZWj9azlu/jyMIy/DwlEeIviPrccc/nnDQ1T3wYrB2QNeSeP7Xb8QCOx+ug== + + + + CN=AC UAT,O=SERVICIO DE ADMINISTRACION TRIBUTARIA,OU=SAT-IES Authority,emailAddress=oscar.martinez@sat.gob.mx,street=3ra cerrada de cadiz,postalCode=06370,C=MX,ST=CIUDAD DE MEXICO,L=COYOACAN,x500UniqueIdentifier=2.5.4.45,unstructuredName=responsable: ACDMA-SAT + 292233162870206001759766198444326234574038511927 + + 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 + + + + + + +