@@ -34,6 +34,29 @@ public function testBuildDpsReturnsWellFormedXml(): void
3434 self ::assertTrue ($ doc ->loadXML ($ xml ), 'Generated XML must be valid XML ' );
3535 }
3636
37+ public function testBuildDpsSetsSchemaVersionAttribute (): void
38+ {
39+ $ xml = $ this ->builder ->buildDps ($ this ->makeDps ());
40+
41+ $ doc = new \DOMDocument ();
42+ $ doc ->loadXML ($ xml );
43+
44+ self ::assertSame ('1.01 ' , $ doc ->documentElement ?->getAttribute('versao ' ));
45+ }
46+
47+ public function testBuildDpsUsesOfficialIdentifierShape (): void
48+ {
49+ $ xml = $ this ->builder ->buildDps ($ this ->makeDps (cnpjPrestador: '11222333000181 ' , municipioIbge: '3303302 ' , serie: '12 ' , numeroDps: '345 ' ));
50+
51+ $ doc = new \DOMDocument ();
52+ $ doc ->loadXML ($ xml );
53+
54+ self ::assertSame (
55+ 'DPS330330221122233300018100012000000000000345 ' ,
56+ $ doc ->getElementsByTagName ('infDPS ' )->item (0 )?->attributes?->getNamedItem('Id ' )?->nodeValue,
57+ );
58+ }
59+
3760 public function testBuildDpsContainsCnpjPrestador (): void
3861 {
3962 $ dps = $ this ->makeDps (cnpjPrestador: '11222333000181 ' );
@@ -48,6 +71,15 @@ public function testBuildDpsContainsMunicipioIbge(): void
4871 $ xml = $ this ->builder ->buildDps ($ dps );
4972
5073 self ::assertStringContainsString ('3303302 ' , $ xml );
74+ self ::assertStringContainsString ('<cLocEmi>3303302</cLocEmi> ' , $ xml );
75+ }
76+
77+ public function testBuildDpsUsesNationalTaxCodeInCtribnac (): void
78+ {
79+ $ dps = $ this ->makeDps (itemListaServico: '0107 ' , codigoTributacaoNacional: '101011 ' );
80+ $ xml = $ this ->builder ->buildDps ($ dps );
81+
82+ self ::assertStringContainsString ('<cServ><cTribNac>101011</cTribNac></cServ> ' , str_replace (["\n" , ' ' ], '' , $ xml ));
5183 }
5284
5385 public function testBuildDpsContainsValorServico (): void
@@ -56,6 +88,7 @@ public function testBuildDpsContainsValorServico(): void
5688 $ xml = $ this ->builder ->buildDps ($ dps );
5789
5890 self ::assertStringContainsString ('1500.00 ' , $ xml );
91+ self ::assertStringContainsString ('<vServPrest><vServ>1500.00</vServ></vServPrest> ' , str_replace (["\n" , ' ' ], '' , $ xml ));
5992 }
6093
6194 public function testDiscriminacaoIsXmlEscaped (): void
@@ -70,14 +103,16 @@ public function testDiscriminacaoIsXmlEscaped(): void
70103
71104 public function testIssRetidoSetsTribCode (): void
72105 {
73- $ dpsRetido = $ this ->makeDps (issRetido: true );
74- $ dpsProprio = $ this ->makeDps (issRetido: false );
106+ $ dpsRetido = $ this ->makeDps (issRetido: true , tipoRetencaoIss: 2 );
107+ $ dpsProprio = $ this ->makeDps (issRetido: false , tipoRetencaoIss: 1 );
75108
76109 $ xmlRetido = $ this ->builder ->buildDps ($ dpsRetido );
77110 $ xmlProprio = $ this ->builder ->buildDps ($ dpsProprio );
78111
79112 self ::assertStringContainsString ('<tribISSQN>2</tribISSQN> ' , $ xmlRetido );
80113 self ::assertStringContainsString ('<tribISSQN>1</tribISSQN> ' , $ xmlProprio );
114+ self ::assertStringContainsString ('<tpRetISSQN>2</tpRetISSQN> ' , $ xmlRetido );
115+ self ::assertStringContainsString ('<tpRetISSQN>1</tpRetISSQN> ' , $ xmlProprio );
81116 }
82117
83118 // -------------------------------------------------------------------------
@@ -140,23 +175,71 @@ public function testRegimeEspecialTributacaoIsIncludedWhenSet(): void
140175 $ xpath = new \DOMXPath ($ doc );
141176 $ xpath ->registerNamespace ('n ' , 'http://www.sped.fazenda.gov.br/nfse ' );
142177
143- $ nodes = $ xpath ->query ('//n:regEspTrib ' );
144- self ::assertSame (1 , $ nodes ->length , '<regEspTrib> expected when regimeEspecialTributacao is set ' );
178+ $ nodes = $ xpath ->query ('//n:prest/n:regTrib/n: regEspTrib ' );
179+ self ::assertSame (1 , $ nodes ->length , '<prest><regTrib>< regEspTrib> expected when regimeEspecialTributacao is set ' );
145180 self ::assertSame ('1 ' , $ nodes ->item (0 )->textContent );
146181 }
147182
148- public function testRegimeEspecialTributacaoIsAbsentWhenNull (): void
183+ public function testBuildDpsAlwaysIncludesProviderTaxRegime (): void
149184 {
150- $ dps = $ this ->makeDps (regimeEspecialTributacao: null );
185+ $ dps = $ this ->makeDps (regimeEspecialTributacao: 0 );
151186 $ xml = $ this ->builder ->buildDps ($ dps );
152187
153188 $ doc = new \DOMDocument ();
154189 $ doc ->loadXML ($ xml );
155190 $ xpath = new \DOMXPath ($ doc );
156191 $ xpath ->registerNamespace ('n ' , 'http://www.sped.fazenda.gov.br/nfse ' );
157192
158- $ nodes = $ xpath ->query ('//n:regEspTrib ' );
159- self ::assertSame (0 , $ nodes ->length , '<regEspTrib> must be absent when null ' );
193+ $ nodes = $ xpath ->query ('//n:prest/n:regTrib/n:regEspTrib ' );
194+ self ::assertSame (1 , $ nodes ->length , '<prest><regTrib><regEspTrib> must always be present ' );
195+ self ::assertSame ('0 ' , $ nodes ->item (0 )->textContent );
196+ }
197+
198+ // -------------------------------------------------------------------------
199+
200+ public function testNonSimplesnacionalMustNotIncludeIndtottribAndPaliq (): void
201+ {
202+ // opSimpNac=1 = "não optante" (per SEFIN error messages)
203+ $ dpsNaoOptante = $ this ->makeDps (
204+ cnpjPrestador: '11222333000181 ' ,
205+ municipioIbge: '3303302 ' ,
206+ itemListaServico: '0107 ' ,
207+ valorServico: '1000.00 ' ,
208+ aliquota: '5.00 ' ,
209+ opcaoSimplesNacional: 1 , // não optante
210+ );
211+ $ xml = $ this ->builder ->buildDps ($ dpsNaoOptante );
212+
213+ // For "não optante" (opSimpNac = 1), pAliq must NOT be present
214+ // but indTotTrib is now ALWAYS included (even if 0) to avoid schema validation errors
215+ self ::assertStringNotContainsString ('<pAliq> ' , $ xml );
216+ self ::assertStringContainsString ('<indTotTrib> ' , $ xml );
217+
218+ // totTrib container must exist with content
219+ self ::assertStringContainsString ('<totTrib ' , $ xml );
220+
221+ // tribMun and tribISSQN must still exist
222+ self ::assertStringContainsString ('<tribMun> ' , $ xml );
223+ self ::assertStringContainsString ('<tribISSQN> ' , $ xml );
224+ }
225+
226+ public function testOptiontSimplesnacionalIncludesIndtottribAndPaliq (): void
227+ {
228+ // opSimpNac=2 = optante de Simples Nacional (inverse naming)
229+ $ dpsOptante = $ this ->makeDps (
230+ cnpjPrestador: '11222333000181 ' ,
231+ municipioIbge: '3303302 ' ,
232+ itemListaServico: '0107 ' ,
233+ valorServico: '1000.00 ' ,
234+ aliquota: '5.00 ' ,
235+ opcaoSimplesNacional: 2 , // optante
236+ indicadorTributacao: 1 ,
237+ );
238+ $ xml = $ this ->builder ->buildDps ($ dpsOptante );
239+
240+ // For "optante" (opSimpNac = 2), indTotTrib and pAliq MUST be present
241+ self ::assertStringContainsString ('<indTotTrib> ' , $ xml );
242+ self ::assertStringContainsString ('<pAliq> ' , $ xml );
160243 }
161244
162245 // -------------------------------------------------------------------------
@@ -165,13 +248,19 @@ private function makeDps(
165248 string $ cnpjPrestador = '11222333000181 ' ,
166249 string $ municipioIbge = '3303302 ' ,
167250 string $ itemListaServico = '0107 ' ,
251+ string $ codigoTributacaoNacional = '000000 ' ,
168252 string $ valorServico = '1000.00 ' ,
169253 string $ aliquota = '5.00 ' ,
170254 string $ discriminacao = 'Consultoria em TI ' ,
255+ string $ serie = '00001 ' ,
256+ string $ numeroDps = '1 ' ,
171257 bool $ issRetido = false ,
172258 string $ documentoTomador = '' ,
173259 string $ nomeTomador = '' ,
174- ?int $ regimeEspecialTributacao = null ,
260+ int $ regimeEspecialTributacao = 0 ,
261+ int $ tipoRetencaoIss = 1 ,
262+ int $ opcaoSimplesNacional = 1 ,
263+ int $ indicadorTributacao = 0 ,
175264 ): DpsData {
176265 return new DpsData (
177266 cnpjPrestador: $ cnpjPrestador ,
@@ -180,10 +269,16 @@ private function makeDps(
180269 valorServico: $ valorServico ,
181270 aliquota: $ aliquota ,
182271 discriminacao: $ discriminacao ,
272+ serie: $ serie ,
273+ numeroDps: $ numeroDps ,
274+ codigoTributacaoNacional: $ codigoTributacaoNacional ,
183275 documentoTomador: $ documentoTomador ,
184276 nomeTomador: $ nomeTomador ,
185277 regimeEspecialTributacao: $ regimeEspecialTributacao ,
278+ tipoRetencaoIss: $ tipoRetencaoIss ,
186279 issRetido: $ issRetido ,
280+ opcaoSimplesNacional: $ opcaoSimplesNacional ,
281+ indicadorTributacao: $ indicadorTributacao ,
187282 );
188283 }
189284}
0 commit comments