diff --git a/src/Xml/XmlBuilder.php b/src/Xml/XmlBuilder.php index 2404cfd..a9939cc 100644 --- a/src/Xml/XmlBuilder.php +++ b/src/Xml/XmlBuilder.php @@ -53,12 +53,22 @@ public function buildDps(DpsData $dps): string $serv->appendChild($doc->createElement('xDescServ', htmlspecialchars($dps->discriminacao, ENT_XML1))); $infDps->appendChild($serv); + // Tomador (optional — absent for foreign buyers with no document) + if ($dps->documentoTomador !== '') { + $infDps->appendChild($this->buildToma($doc, $dps)); + } + // Values $valores = $doc->createElement('valores'); $valores->appendChild($doc->createElement('vServ', $dps->valorServico)); $valores->appendChild($this->buildTrib($doc, $dps)); $infDps->appendChild($valores); + // Regime especial de tributação (optional) + if ($dps->regimeEspecialTributacao !== null) { + $infDps->appendChild($doc->createElement('regEspTrib', (string) $dps->regimeEspecialTributacao)); + } + return $doc->saveXML() ?: ''; } @@ -70,4 +80,23 @@ private function buildTrib(\DOMDocument $doc, DpsData $dps): \DOMElement return $trib; } + + private function buildToma(\DOMDocument $doc, DpsData $dps): \DOMElement + { + $toma = $doc->createElement('toma'); + + $docLen = strlen($dps->documentoTomador); + + if ($docLen === 14) { + $toma->appendChild($doc->createElement('CNPJ', $dps->documentoTomador)); + } elseif ($docLen === 11) { + $toma->appendChild($doc->createElement('CPF', $dps->documentoTomador)); + } + + if ($dps->nomeTomador !== '') { + $toma->appendChild($doc->createElement('xNome', htmlspecialchars($dps->nomeTomador, ENT_XML1))); + } + + return $toma; + } } diff --git a/tests/Unit/Xml/XmlBuilderTest.php b/tests/Unit/Xml/XmlBuilderTest.php index 8f24f6d..d5d6bf1 100644 --- a/tests/Unit/Xml/XmlBuilderTest.php +++ b/tests/Unit/Xml/XmlBuilderTest.php @@ -82,6 +82,85 @@ public function testIssRetidoSetsTribCode(): void // ------------------------------------------------------------------------- + public function testTomadorCnpjBlockIsIncludedWhenDocumentHas14Digits(): void + { + $dps = $this->makeDps(documentoTomador: '12345678000195', nomeTomador: 'Empresa Tomadora S.A.'); + $xml = $this->builder->buildDps($dps); + + $doc = new \DOMDocument(); + $doc->loadXML($xml); + $xpath = new \DOMXPath($doc); + $xpath->registerNamespace('n', 'http://www.sped.fazenda.gov.br/nfse'); + + $nodes = $xpath->query('//n:toma/n:CNPJ'); + self::assertSame(1, $nodes->length, ' block expected'); + self::assertSame('12345678000195', $nodes->item(0)->textContent); + + $nameNodes = $xpath->query('//n:toma/n:xNome'); + self::assertSame(1, $nameNodes->length, ' block expected'); + self::assertSame('Empresa Tomadora S.A.', $nameNodes->item(0)->textContent); + } + + public function testTomadorCpfBlockIsIncludedWhenDocumentHas11Digits(): void + { + $dps = $this->makeDps(documentoTomador: '12345678901', nomeTomador: 'Pessoa Física Tomadora'); + $xml = $this->builder->buildDps($dps); + + $doc = new \DOMDocument(); + $doc->loadXML($xml); + $xpath = new \DOMXPath($doc); + $xpath->registerNamespace('n', 'http://www.sped.fazenda.gov.br/nfse'); + + $nodes = $xpath->query('//n:toma/n:CPF'); + self::assertSame(1, $nodes->length, ' block expected for 11-digit doc'); + self::assertSame('12345678901', $nodes->item(0)->textContent); + } + + public function testTomadorBlockIsAbsentWhenDocumentoTomadorIsEmpty(): void + { + $dps = $this->makeDps(documentoTomador: ''); + $xml = $this->builder->buildDps($dps); + + $doc = new \DOMDocument(); + $doc->loadXML($xml); + $xpath = new \DOMXPath($doc); + $xpath->registerNamespace('n', 'http://www.sped.fazenda.gov.br/nfse'); + + $nodes = $xpath->query('//n:toma'); + self::assertSame(0, $nodes->length, ' block must be absent when no buyer document'); + } + + public function testRegimeEspecialTributacaoIsIncludedWhenSet(): void + { + $dps = $this->makeDps(regimeEspecialTributacao: 1); + $xml = $this->builder->buildDps($dps); + + $doc = new \DOMDocument(); + $doc->loadXML($xml); + $xpath = new \DOMXPath($doc); + $xpath->registerNamespace('n', 'http://www.sped.fazenda.gov.br/nfse'); + + $nodes = $xpath->query('//n:regEspTrib'); + self::assertSame(1, $nodes->length, ' expected when regimeEspecialTributacao is set'); + self::assertSame('1', $nodes->item(0)->textContent); + } + + public function testRegimeEspecialTributacaoIsAbsentWhenNull(): void + { + $dps = $this->makeDps(regimeEspecialTributacao: null); + $xml = $this->builder->buildDps($dps); + + $doc = new \DOMDocument(); + $doc->loadXML($xml); + $xpath = new \DOMXPath($doc); + $xpath->registerNamespace('n', 'http://www.sped.fazenda.gov.br/nfse'); + + $nodes = $xpath->query('//n:regEspTrib'); + self::assertSame(0, $nodes->length, ' must be absent when null'); + } + + // ------------------------------------------------------------------------- + private function makeDps( string $cnpjPrestador = '11222333000181', string $municipioIbge = '3303302', @@ -90,15 +169,21 @@ private function makeDps( string $aliquota = '5.00', string $discriminacao = 'Consultoria em TI', bool $issRetido = false, + string $documentoTomador = '', + string $nomeTomador = '', + ?int $regimeEspecialTributacao = null, ): DpsData { return new DpsData( - cnpjPrestador: $cnpjPrestador, - municipioIbge: $municipioIbge, - itemListaServico: $itemListaServico, - valorServico: $valorServico, - aliquota: $aliquota, - discriminacao: $discriminacao, - issRetido: $issRetido, + cnpjPrestador: $cnpjPrestador, + municipioIbge: $municipioIbge, + itemListaServico: $itemListaServico, + valorServico: $valorServico, + aliquota: $aliquota, + discriminacao: $discriminacao, + documentoTomador: $documentoTomador, + nomeTomador: $nomeTomador, + regimeEspecialTributacao: $regimeEspecialTributacao, + issRetido: $issRetido, ); } }