@@ -357,6 +357,177 @@ public function test_dump_unknown_tag_throws_exception(): void
357357 $ parser ->dump (['UNKNOWN|test| ' ]);
358358 }
359359
360+ public function test_dump_skips_empty_lines (): void
361+ {
362+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
363+ $ result = $ parser ->dump (['' , 'BA03|35|1708|25028332000105|01|1|12345| ' , '' ]);
364+ $ this ->assertCount (1 , $ result );
365+ $ this ->assertEquals ('BA03 ' , $ result [0 ]->tag );
366+ }
367+
368+ public function test_toXml_unknown_tag_throws_exception (): void
369+ {
370+ $ this ->expectException (\NFePHP \NFe \Exception \DocumentsException::class);
371+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
372+ $ parser ->toXml (['DESCONHECIDO|teste| ' ]);
373+ }
374+
375+ public function test_toXml_array2xml_skips_empty_lines (): void
376+ {
377+ // Linhas vazias dentro do array de entrada devem ser ignoradas
378+ // pelo laço de array2xml (cobre o ramo de continue).
379+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
380+ try {
381+ // NOTA: a nota é incompleta de propósito — o objetivo é apenas
382+ // disparar o array2xml com uma linha vazia. montaNFe() pode
383+ // falhar e isso é esperado.
384+ $ parser ->toXml (['' , 'BA| ' ]);
385+ } catch (\Throwable $ e ) {
386+ // tolerado
387+ }
388+ $ this ->assertTrue (true );
389+ }
390+
391+ // =========================================================================
392+ // LOCAL_V12 extras fixture — cobre muitas entidades não atingidas
393+ // pelos demais fixtures (refNFP, autXML, NVE, detExport, combustível,
394+ // medicamento, arma, RECOPI, ICMS10/ICMSPart/ICMSST, PIS/COFINS qBCProd,
395+ // ISSQN, impostoDevol, totais W17/W23, transporte completo,
396+ // intermediário, exporta, compra, cana, infRespTec, infNFeSupl).
397+ // =========================================================================
398+
399+ public function test_toXml_local_v12_extras_fixture (): void
400+ {
401+ $ txt = file_get_contents ($ this ->fixturesPath . 'nfe_4.00_local_v12_extras.txt ' );
402+ $ notas = $ this ->parseTxt ($ txt );
403+
404+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
405+ $ xml = $ parser ->toXml ($ notas [0 ]);
406+
407+ $ this ->assertNotNull ($ xml );
408+ $ nfe = new \SimpleXMLElement ($ xml );
409+ $ this ->assertCount (9 , $ nfe ->infNFe ->det );
410+ }
411+
412+ public function test_toXml_local_v12_cpf_variants_first_nota (): void
413+ {
414+ $ txt = file_get_contents ($ this ->fixturesPath . 'nfe_4.00_local_v12_cpf_variants.txt ' );
415+ $ notas = $ this ->parseTxt ($ txt );
416+
417+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
418+ $ xml = $ parser ->toXml ($ notas [0 ]);
419+
420+ $ this ->assertNotNull ($ xml );
421+ $ nfe = new \SimpleXMLElement ($ xml );
422+ // Destinatário estrangeiro (E03a)
423+ $ this ->assertNotEmpty ((string )$ nfe ->infNFe ->dest ->idEstrangeiro );
424+ }
425+
426+ public function test_toXml_local_v12_cpf_variants_second_nota_csn (): void
427+ {
428+ $ txt = file_get_contents ($ this ->fixturesPath . 'nfe_4.00_local_v12_cpf_variants.txt ' );
429+ $ notas = $ this ->parseTxt ($ txt );
430+
431+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
432+ $ xml = $ parser ->toXml ($ notas [1 ]);
433+
434+ $ this ->assertNotNull ($ xml );
435+ $ nfe = new \SimpleXMLElement ($ xml );
436+ // Produtor rural com CPF (C02a)
437+ $ this ->assertNotEmpty ((string )$ nfe ->infNFe ->emit ->CPF );
438+ $ this ->assertCount (6 , $ nfe ->infNFe ->det );
439+ }
440+
441+ public function test_toXml_local_v13_ibscbs_fixture (): void
442+ {
443+ // Cobre entidades do grupo IBS/CBS (LOCAL_V13): B31, UB*, W31, W34.
444+ // Make pode rejeitar o XML final por validações de PL_010 — o foco
445+ // aqui é a execução das linhas do Parser, portanto erros do Make
446+ // são tolerados.
447+ $ txt = file_get_contents ($ this ->fixturesPath . 'nfe_4.00_local_v13_ibscbs.txt ' );
448+ $ notas = $ this ->parseTxt ($ txt );
449+
450+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V13 );
451+ try {
452+ $ parser ->toXml ($ notas [0 ]);
453+ } catch (\Throwable $ e ) {
454+ // tolerado
455+ }
456+ $ this ->assertTrue (true );
457+ }
458+
459+ public function test_dump_local_v13_ibscbs_tags (): void
460+ {
461+ $ txt = file_get_contents ($ this ->fixturesPath . 'nfe_4.00_local_v13_ibscbs.txt ' );
462+ $ notas = $ this ->parseTxt ($ txt );
463+
464+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V13 );
465+ $ result = $ parser ->dump ($ notas [0 ]);
466+ $ tags = array_map (fn ($ item ) => $ item ->tag , $ result );
467+
468+ $ this ->assertContains ('B31 ' , $ tags );
469+ $ this ->assertContains ('UB01 ' , $ tags );
470+ $ this ->assertContains ('UB12 ' , $ tags );
471+ $ this ->assertContains ('UB68 ' , $ tags );
472+ $ this ->assertContains ('UB73 ' , $ tags );
473+ $ this ->assertContains ('UB82A ' , $ tags );
474+ $ this ->assertContains ('UB84 ' , $ tags );
475+ $ this ->assertContains ('UB112 ' , $ tags );
476+ $ this ->assertContains ('UB116 ' , $ tags );
477+ $ this ->assertContains ('UB120 ' , $ tags );
478+ $ this ->assertContains ('UB131 ' , $ tags );
479+ $ this ->assertContains ('W31 ' , $ tags );
480+ $ this ->assertContains ('W34 ' , $ tags );
481+ }
482+
483+ public function test_dump_local_v12_extras_fixture_tags (): void
484+ {
485+ $ txt = file_get_contents ($ this ->fixturesPath . 'nfe_4.00_local_v12_extras.txt ' );
486+ $ notas = $ this ->parseTxt ($ txt );
487+
488+ $ parser = new Parser ('4.00 ' , Parser::LOCAL_V12 );
489+ $ result = $ parser ->dump ($ notas [0 ]);
490+
491+ $ tags = array_map (fn ($ item ) => $ item ->tag , $ result );
492+ // seções adicionais / tags não cobertas pelos outros fixtures
493+ $ this ->assertContains ('BA10 ' , $ tags );
494+ $ this ->assertContains ('BA13 ' , $ tags );
495+ $ this ->assertContains ('GA02 ' , $ tags );
496+ $ this ->assertContains ('I05A ' , $ tags );
497+ $ this ->assertContains ('I50 ' , $ tags );
498+ $ this ->assertContains ('O11 ' , $ tags );
499+ $ this ->assertContains ('R04 ' , $ tags );
500+ $ this ->assertContains ('T04 ' , $ tags );
501+ $ this ->assertContains ('Q10 ' , $ tags );
502+ $ this ->assertContains ('S09 ' , $ tags );
503+ $ this ->assertContains ('JA ' , $ tags );
504+ $ this ->assertContains ('K ' , $ tags );
505+ $ this ->assertContains ('L ' , $ tags );
506+ $ this ->assertContains ('LA ' , $ tags );
507+ $ this ->assertContains ('LA07 ' , $ tags );
508+ $ this ->assertContains ('LA11 ' , $ tags );
509+ $ this ->assertContains ('LB ' , $ tags );
510+ $ this ->assertContains ('N10 ' , $ tags );
511+ $ this ->assertContains ('N10b ' , $ tags );
512+ $ this ->assertContains ('U ' , $ tags );
513+ $ this ->assertContains ('UA ' , $ tags );
514+ $ this ->assertContains ('W17 ' , $ tags );
515+ $ this ->assertContains ('W23 ' , $ tags );
516+ $ this ->assertContains ('X11 ' , $ tags );
517+ $ this ->assertContains ('X18 ' , $ tags );
518+ $ this ->assertContains ('X22 ' , $ tags );
519+ $ this ->assertContains ('X25a ' , $ tags );
520+ $ this ->assertContains ('X25b ' , $ tags );
521+ $ this ->assertContains ('YB ' , $ tags );
522+ $ this ->assertContains ('ZA ' , $ tags );
523+ $ this ->assertContains ('ZB ' , $ tags );
524+ $ this ->assertContains ('ZC ' , $ tags );
525+ $ this ->assertContains ('ZC04 ' , $ tags );
526+ $ this ->assertContains ('ZC10 ' , $ tags );
527+ $ this ->assertContains ('ZD ' , $ tags );
528+ $ this ->assertContains ('ZX01 ' , $ tags );
529+ }
530+
360531 // =========================================================================
361532 // Helper to split TXT into notes arrays (mimicking Convert logic)
362533 // =========================================================================
0 commit comments