diff --git a/schemes/PL_009_V4/consReciNFe_v4.00.xsd b/schemes/PL_009_V4/consReciNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/consSitNFe_v4.00.xsd b/schemes/PL_009_V4/consSitNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/consStatServ_v4.00.xsd b/schemes/PL_009_V4/consStatServ_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/enviNFe_v4.00.xsd b/schemes/PL_009_V4/enviNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/inutNFe_v4.00.xsd b/schemes/PL_009_V4/inutNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/leiauteConsSitNFe_v4.00.xsd b/schemes/PL_009_V4/leiauteConsSitNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/leiauteConsStatServ_v4.00.xsd b/schemes/PL_009_V4/leiauteConsStatServ_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/leiauteInutNFe_v4.00.xsd b/schemes/PL_009_V4/leiauteInutNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/leiauteNFe_v4.00.xsd b/schemes/PL_009_V4/leiauteNFe_v4.00.xsd old mode 100755 new mode 100644 index e2a44282..1c59fa9c --- a/schemes/PL_009_V4/leiauteNFe_v4.00.xsd +++ b/schemes/PL_009_V4/leiauteNFe_v4.00.xsd @@ -1,5 +1,5 @@ - + @@ -556,7 +556,8 @@ Preencher com "2B", quando se tratar de Cupom Fiscal emitido por máqu Este campo será obrigatoriamente preenchido com: 1 – Simples Nacional; 2 – Simples Nacional – excesso de sublimite de receita bruta; -3 – Regime Normal. +3 – Regime Normal. +4 - Simples Nacional - Microempreendedor individual - MEI @@ -564,6 +565,7 @@ Este campo será obrigatoriamente preenchido com: + @@ -2756,14 +2758,13 @@ Informar o motivo da desoneração: origem da mercadoria: 0 - Nacional -1 - Estrangeira - Importação direta -2 - Estrangeira - Adquirida no mercado interno + 1 - Estrangeira - Importação direta + 2 - Estrangeira - Adquirida no mercado interno - Tributção pelo ICMS -20 - Com redução de base de cálculo + Tributação pelo ICMS 51 - Tributação com Diferimento @@ -2775,10 +2776,10 @@ Informar o motivo da desoneração: Modalidade de determinação da BC do ICMS: -0 - Margem Valor Agregado (%); -1 - Pauta (valor); -2 - Preço Tabelado Máximo (valor); -3 - Valor da Operação. + 0 - Margem Valor Agregado (%); + 1 - Pauta (valor); + 2 - Preço Tabelado Máximo (valor); + 3 - Valor da Operação. @@ -3779,7 +3780,7 @@ Operação interestadual para consumidor final com partilha do ICMS devido na o - + origem da mercadoria: 0 - Nacional 1 - Estrangeira - Importação direta @@ -4098,11 +4099,11 @@ Operação interestadual para consumidor final com partilha do ICMS devido na o - Tributação do ICMS pelo SIMPLES NACIONAL, CRT=1 – Simples Nacional e CSOSN=900 (v2.0) + Tributação do ICMS pelo SIMPLES NACIONAL, CRT=1 – Simples Nacional, CRT=4 - MEI e CSOSN=900 (v2.0) - + origem da mercadoria: 0 - Nacional 1 - Estrangeira - Importação direta diff --git a/schemes/PL_009_V4/nfe_v4.00.xsd b/schemes/PL_009_V4/nfe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/procInutNFe_v4.00.xsd b/schemes/PL_009_V4/procInutNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/procNFe_v4.00.xsd b/schemes/PL_009_V4/procNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/retConsReciNFe_v4.00.xsd b/schemes/PL_009_V4/retConsReciNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/retConsSitNFe_v4.00.xsd b/schemes/PL_009_V4/retConsSitNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/retConsStatServ_v4.00.xsd b/schemes/PL_009_V4/retConsStatServ_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/retEnviNFe_v4.00.xsd b/schemes/PL_009_V4/retEnviNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/retInutNFe_v4.00.xsd b/schemes/PL_009_V4/retInutNFe_v4.00.xsd old mode 100755 new mode 100644 diff --git a/schemes/PL_009_V4/tiposBasico_v4.00.xsd b/schemes/PL_009_V4/tiposBasico_v4.00.xsd old mode 100755 new mode 100644 index 872801e5..d51b658e --- a/schemes/PL_009_V4/tiposBasico_v4.00.xsd +++ b/schemes/PL_009_V4/tiposBasico_v4.00.xsd @@ -521,7 +521,7 @@ - + diff --git a/schemes/PL_009_V4/xmldsig-core-schema_v1.01.xsd b/schemes/PL_009_V4/xmldsig-core-schema_v1.01.xsd old mode 100755 new mode 100644 diff --git a/src/Make.php b/src/Make.php index f8190b4d..bca97969 100755 --- a/src/Make.php +++ b/src/Make.php @@ -446,11 +446,6 @@ public function montaNFe(): string */ public function monta(): string { - if (!empty($this->errors)) { - $this->errors = array_merge($this->errors, $this->dom->errors); - } else { - $this->errors = $this->dom->errors; - } //cria a tag raiz da Nfe $this->buildNFe(); //processa nfeRef e coloca as tags na tag ide @@ -516,9 +511,11 @@ public function monta(): string // testa da chave $this->checkNFeKey($this->dom); $this->xml = $this->dom->saveXML(); - if (count($this->errors) > 0) { + + if (count($this->getErrors()) > 0) { throw new RuntimeException('Existem erros nas tags. Obtenha os erros com getErrors().'); } + return $this->xml; } @@ -1761,7 +1758,7 @@ public function tagCreditoPresumidoProd(stdClass $std) $std->cCredPresumido, true, $identificador . "[item $std->item] cCredPresumido Código de Benefício Fiscal de Crédito " - . "Presumido na UF aplicado ao item", + . "Presumido na UF aplicado ao item", true ); $this->dom->addChild( @@ -1845,6 +1842,14 @@ public function tagprod(stdClass $std): DOMElement $this->errors[] = "cEANTrib {$ceantrib} " . $e->getMessage(); } + $CRT = $this->emit->getElementsByTagName("CRT")->item(0)->nodeValue ?? null; + $idDest = $this->ide->getElementsByTagName("idDest")->item(0)->nodeValue ?? null; + $allowEmptyNcm = $CRT == 4 && $idDest == 1; + + if ($allowEmptyNcm && empty($std->NCM)) { + $std->NCM = '00000000'; + } + $identificador = 'I01 - '; $prod = $this->dom->createElement("prod"); $this->dom->addChild( @@ -2886,7 +2891,7 @@ public function tagcomb(stdClass $std): DOMElement $this->conditionalNumberFormatting($std->pBio, 4), false, "$identificador [item $std->item] Percentual do índice de mistura do Biodiesel (B100) no Óleo Diesel B " - . "instituído pelo órgão regulamentador" + . "instituído pelo órgão regulamentador" ); $this->aComb[$std->item] = $comb; return $comb; @@ -3631,7 +3636,7 @@ public function tagICMS(stdClass $std): DOMElement $std->indDeduzDeson, false, "$identificador [item $std->item] Indica se o valor do ICMS desonerado (vICMSDeson) " - . "deduz do valor do item (vProd)." + . "deduz do valor do item (vProd)." ); break; case '40': @@ -4181,7 +4186,7 @@ public function tagICMS(stdClass $std): DOMElement $std->indDeduzDeson, false, "$identificador [item $std->item] Indica se o valor do ICMS desonerado (vICMSDeson) " - . "deduz do valor do item (vProd)." + . "deduz do valor do item (vProd)." ); $this->dom->addChild( $icms, @@ -4364,7 +4369,7 @@ public function tagICMS(stdClass $std): DOMElement $std->indDeduzDeson, false, "$identificador [item $std->item] Indica se o valor do ICMS desonerado (vICMSDeson) " - . "deduz do valor do item (vProd)." + . "deduz do valor do item (vProd)." ); $this->dom->addChild( $icms, @@ -4528,7 +4533,7 @@ public function tagICMSPart(stdClass $std): DOMElement $this->conditionalNumberFormatting($std->pFCPST, 4), false, "[item $std->item] Percentual do Fundo de " - . "Combate à Pobreza (FCP) ST" + . "Combate à Pobreza (FCP) ST" ); $this->dom->addChild( $icmsPart, @@ -4749,6 +4754,12 @@ public function tagICMSSN(stdClass $std): DOMElement //totalizador generico $this->stdTot->vFCPST += (float) !empty($std->vFCPST) ? $std->vFCPST : 0; $this->stdTot->vFCPSTRet += (float) !empty($std->vFCPSTRet) ? $std->vFCPSTRet : 0; + + $CRT = $this->emit->getElementsByTagName("CRT")->item(0)->nodeValue ?? null; + $allowEmptyOrig = $CRT == 4 && in_array($std->CSOSN, [ + '102', '103', '300', '400', '900', + ]); + switch ($std->CSOSN) { case '101': $icmsSN = $this->dom->createElement("ICMSSN101"); @@ -4791,8 +4802,9 @@ public function tagICMSSN(stdClass $std): DOMElement $icmsSN, 'orig', $std->orig, - true, - "[item $std->item] Origem da mercadoria" + !$allowEmptyOrig, + "[item $std->item] Origem da mercadoria", + $allowEmptyOrig, ); $this->dom->addChild( $icmsSN, @@ -5096,8 +5108,9 @@ public function tagICMSSN(stdClass $std): DOMElement $icmsSN, 'orig', $std->orig, - true, - "[item $std->item] Origem da mercadoria" + !$allowEmptyOrig, + "[item $std->item] Origem da mercadoria", + $allowEmptyOrig, ); $this->dom->addChild( $icmsSN, @@ -8018,7 +8031,7 @@ protected function buildDet() //incluso NT 2023.001-1.10 /1.20 if (!empty($this->aOrigComb[$nItem])) { foreach ($this->aOrigComb[$nItem] as $origcomb) { - $this->dom->appChild($child, $origcomb, "inclusão do node origComb na tag comb"); + $this->dom->appChild($child, $origcomb, "inclusão do node origComb na tag comb"); } } $this->dom->appChild($prod, $child, "Inclusão do node combustivel"); @@ -8257,7 +8270,7 @@ protected function checkNFeKey(Dom $dom): void */ public function getErrors(): array { - return $this->errors; + return array_merge($this->errors, $this->dom->errors); } /** diff --git a/tests/MakeTest.php b/tests/MakeTest.php index 0a38572d..cc0d5f24 100755 --- a/tests/MakeTest.php +++ b/tests/MakeTest.php @@ -1019,6 +1019,9 @@ public function test_tagII(): void public function test_tagISSQN(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->cProd = '1111'; @@ -1156,6 +1159,9 @@ public function test_tagICMSST(): void public function test_tagICMSSN_101(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1173,6 +1179,9 @@ public function test_tagICMSSN_101(): void public function test_tagICMSSN_102(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1188,6 +1197,9 @@ public function test_tagICMSSN_102(): void public function test_tagICMSSN_103(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1203,6 +1215,9 @@ public function test_tagICMSSN_103(): void public function test_tagICMSSN_300(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1218,6 +1233,9 @@ public function test_tagICMSSN_300(): void public function test_tagICMSSN_400(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1233,6 +1251,9 @@ public function test_tagICMSSN_400(): void public function test_tagICMSSN_201(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1260,6 +1281,9 @@ public function test_tagICMSSN_201(): void public function test_tagICMSSN_202(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1284,6 +1308,9 @@ public function test_tagICMSSN_202(): void public function test_tagICMSSN_203(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1308,6 +1335,9 @@ public function test_tagICMSSN_203(): void public function test_tagICMSSN_500(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1335,6 +1365,9 @@ public function test_tagICMSSN_500(): void public function test_tagICMSSN_900(): void { + $this->make->tagide(new \stdClass()); + $this->make->tagemit(new \stdClass()); + $std = new \stdClass(); $std->item = 1; $std->orig = 0; @@ -1365,6 +1398,219 @@ public function test_tagICMSSN_900(): void $this->validarExistenciaCampos($std, $tag2); } + public function test_tagICMSSNShouldAcceptEmptyOrig_whenCrtIs4AndCsosnInAllowedList(): void + { + $std = new \stdClass(); + $std->CRT = 4; + $this->make->tagemit($std); + + $std = new \stdClass(); + $std->item = 1; + $std->orig = null; + $std->CSOSN = '900'; + $std->modBC = 3; + $std->vBC = 100; + $std->pRedBC = 1; + $std->pICMS = 1; + $std->vICMS = 1; + $std->pCredSN = 3; + $std->vCredICMSSN = 4; + $std->modBCST = 3; + $std->pMVAST = 1; + $std->pRedBCST = 1; + $std->vBCST = 1; + $std->pICMSST = 1; + $std->vICMSST = 1; + $std->vBCFCPST = 1; + $std->pFCPST = 1; + $std->vFCPST = 1; + + $icmssn = $this->make->tagICMSSN($std); + $result = $icmssn->getElementsByTagName('ICMSSN900')->item(0); + + $this->assertEquals('ICMS', $icmssn->nodeName); + $this->assertEquals('ICMSSN900', $result->nodeName); + $this->assertNull($result->getElementsByTagName('orig')->item(0)); + + $this->validarExistenciaCampos($std, $result); + } + + public function test_tagICMSSNShouldNotAcceptEmptyOrig_whenCrtIs1(): void + { + $std = new \stdClass(); + $std->CRT = 1; + $this->make->tagemit($std); + + $std = new \stdClass(); + $std->item = 1; + $std->orig = null; + $std->CSOSN = '900'; + $std->modBC = 3; + $std->vBC = 100; + $std->pRedBC = 1; + $std->pICMS = 1; + $std->vICMS = 1; + $std->pCredSN = 3; + $std->vCredICMSSN = 4; + $std->modBCST = 3; + $std->pMVAST = 1; + $std->pRedBCST = 1; + $std->vBCST = 1; + $std->pICMSST = 1; + $std->vICMSST = 1; + $std->vBCFCPST = 1; + $std->pFCPST = 1; + $std->vFCPST = 1; + + $icmssn = $this->make->tagICMSSN($std); + $result = $icmssn->getElementsByTagName('ICMSSN900')->item(0); + + $this->assertEquals('ICMS', $icmssn->nodeName); + $this->assertEquals('ICMSSN900', $result->nodeName); + $this->assertContains( + 'Preenchimento Obrigatório! [orig] [item 1] Origem da mercadoria', + $this->make->getErrors() + ); + $this->validarExistenciaCampos($std, $result); + } + + public function test_tagICMSSNShouldNotAcceptEmptyOrig_whenCrtIs4AndCsosnIsNotInAllowedList(): void + { + $std = new \stdClass(); + $std->CRT = 1; + $this->make->tagemit($std); + + $std = new \stdClass(); + $std->item = 1; + $std->orig = null; + $std->CSOSN = '500'; + $std->modBC = 3; + $std->vBC = 100; + $std->pRedBC = 1; + $std->pICMS = 1; + $std->vICMS = 1; + $std->pCredSN = 3; + $std->vCredICMSSN = 4; + $std->modBCST = 3; + $std->pMVAST = 1; + $std->pRedBCST = 1; + $std->vBCST = 1; + $std->pICMSST = 1; + $std->vICMSST = 1; + $std->vBCFCPST = 1; + $std->pFCPST = 1; + $std->vFCPST = 1; + + $icmssn = $this->make->tagICMSSN($std); + $result = $icmssn->getElementsByTagName('ICMSSN500')->item(0); + + $this->assertEquals('ICMS', $icmssn->nodeName); + $this->assertEquals('ICMSSN500', $result->nodeName); + $this->assertContains( + 'Preenchimento Obrigatório! [orig] [item 1] Origem da mercadoria', + $this->make->getErrors() + ); + } + + public function test_tagNCMShouldAcceptEmptyValue_andChangeToDefaultValue_whenCrtIs4AndIdDestIs1(): void + { + $std = new \stdClass(); + $std->idDest = 1; + $this->make->tagide($std); + + $std = new \stdClass(); + $std->CRT = 4; + $this->make->tagemit($std); + + $std = new \stdClass(); + $std->item = 1; + $std->cProd = '1111'; + $std->cEAN = "SEM GTIN"; + $std->xProd = 'CAMISETA REGATA GG'; + $std->NCM = null; + $std->CFOP = 5101; + $std->uCom = 'UN'; + $std->qCom = 1; + $std->vUnCom = 100.00; + $std->vProd = 100.00; + $std->cEANTrib = "SEM GTIN"; + $std->uTrib = 'UN'; + $std->qTrib = 1; + $std->vUnTrib = 100.00; + $std->indTot = 1; + $prod = $this->make->tagprod($std); + + $this->assertEquals('00000000', $prod->getElementsByTagName('NCM')->item(0)->nodeValue); + } + + public function test_tagNCMShouldNotAcceptEmptyValue_whenCrtIs1(): void + { + $std = new \stdClass(); + $std->idDest = 1; + $this->make->tagide($std); + + $std = new \stdClass(); + $std->CRT = 1; + $this->make->tagemit($std); + + $std = new \stdClass(); + $std->item = 1; + $std->cProd = '1111'; + $std->cEAN = "SEM GTIN"; + $std->xProd = 'CAMISETA REGATA GG'; + $std->NCM = null; + $std->CFOP = 5101; + $std->uCom = 'UN'; + $std->qCom = 1; + $std->vUnCom = 100.00; + $std->vProd = 100.00; + $std->cEANTrib = "SEM GTIN"; + $std->uTrib = 'UN'; + $std->qTrib = 1; + $std->vUnTrib = 100.00; + $std->indTot = 1; + $this->make->tagprod($std); + + $this->assertContains( + 'Preenchimento Obrigatório! [NCM] I01 - [item 1] Código NCM com 8 dígitos ou 2 dígitos (gênero)', + $this->make->getErrors() + ); + } + + public function test_tagNCMShouldNotAcceptEmptyValue_whenCrtIs4AndIdDestIsNot1(): void + { + $std = new \stdClass(); + $std->idDest = 2; + $this->make->tagide($std); + + $std = new \stdClass(); + $std->CRT = 4; + $this->make->tagemit($std); + + $std = new \stdClass(); + $std->item = 1; + $std->cProd = '1111'; + $std->cEAN = "SEM GTIN"; + $std->xProd = 'CAMISETA REGATA GG'; + $std->NCM = null; + $std->CFOP = 5101; + $std->uCom = 'UN'; + $std->qCom = 1; + $std->vUnCom = 100.00; + $std->vProd = 100.00; + $std->cEANTrib = "SEM GTIN"; + $std->uTrib = 'UN'; + $std->qTrib = 1; + $std->vUnTrib = 100.00; + $std->indTot = 1; + $this->make->tagprod($std); + + $this->assertContains( + 'Preenchimento Obrigatório! [NCM] I01 - [item 1] Código NCM com 8 dígitos ou 2 dígitos (gênero)', + $this->make->getErrors() + ); + } + private function validarExistenciaCampos(\stdClass $std, \DOMElement $tag): void { $attributos = get_object_vars($std);