File Coverage

blib/lib/Business/BR/NFe/RPS/TXT.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             package Business::BR::NFe::RPS::TXT;
2              
3 1     1   4393 use Moose;
  0            
  0            
4             use Moose::Util::TypeConstraints;
5             use namespace::autoclean;
6             use Carp;
7              
8              
9             subtype 'DataRps', as 'Str',
10             where { /^[1-2][0-9][0-9][0-9][0-1][0-9][0-3][0-9]$/ },
11             message { 'The date you provided is not for NFe::RPS' };
12              
13             has data_ini => (
14             is => 'ro',
15             isa => 'DataRps',
16             required => 1
17             );
18              
19             has data_fim => (
20             is => 'ro',
21             isa => 'DataRps',
22             required => 1
23             );
24              
25             has inscricao_municipal => (
26             is => 'ro',
27             required => 1
28             );
29              
30             has _total_servico => (
31             traits => ['Counter'],
32             is => 'ro',
33             isa => 'Num',
34             default => 0,
35             handles => {
36             inc_total_servico => 'inc',
37             dec_total_servico => 'dec',
38             reset_total_servico => 'reset'
39             },
40             );
41              
42             has _total_deducao => (
43             traits => ['Counter'],
44             is => 'ro',
45             isa => 'Num',
46             default => 0,
47             handles => {
48             inc_total_deducao => 'inc',
49             dec_total_deducao => 'dec',
50             reset_total_deducao => 'reset'
51             },
52             );
53              
54             has _rps_lines => (
55             traits => ['String'],
56             is => 'ro',
57             isa => 'Str',
58             default => q{},
59             handles => {
60             add_rps_lines => 'append',
61             replace_rps_lines => 'replace',
62             },
63             );
64              
65             has _total_linhas => (
66             traits => ['Counter'],
67             is => 'ro',
68             isa => 'Num',
69             default => 0,
70             handles => {
71             inc_total_linhas => 'inc',
72             dec_total_linhas => 'dec',
73             reset_total_linhas => 'reset'
74             },
75             );
76              
77             sub _pad_str {
78             my ( $str, $size ) = @_;
79             return $str . ' ' x ( $size - length($str) );
80             }
81              
82             sub _pad_num {
83             my ( $num, $size, $round ) = @_;
84              
85             if ( $round && $round =~ /^[0-9]$/ ) {
86             $round = int( '1' . '0' x ( $round - 1 ) );
87             }
88             else {
89             $round = 1;
90             }
91             $num = $num * $round;
92             if ( ref $size eq 'ARRAY' ) {
93             croak "Not a number";
94             }
95             else {
96             return sprintf( '%0' . $size . 's', $num * $round );
97             }
98             }
99              
100              
101             sub adiciona_rps {
102             my ( $self, %params ) = @_;
103              
104             # TODO Data::Verifier
105             # param 1: esse daqui aceita date, str e num.
106             # param 2: tamanho : positivo = pad, negativo = maximo
107             # param 3:
108             # INT = pra numero = round de truncate
109             # ARRAY = valores aceitos
110              
111             # ps: todos os campos sao relativos ao tomador
112             my $campos = {
113             serie => [ 'str', 5 ],
114             numero => [ 'num', 12 ],
115             emissao => ['date'],
116             situacao => [ 'str', 1, [ 'T', 'I', 'F', 'C', 'E', 'J' ] ],
117             valor_servico => [ 'num', 15, 2 ],
118             valor_deducao => [ 'num', 15, 2 ],
119             codigo_servico => [ 'num', 5 ],
120             aliquota => [ 'num', 4, 2 ],
121             iss_retido => [ 'num', [ 1, 2, 3 ] ],
122             cpf_cnpj_flag => [ 'num', [ 1, 2, 3 ] ],
123             cpf_cnpj => [ 'num', 14 ],
124             inscricao_municipal => [ 'num', 8 ],
125             inscricao_estadual => [ 'num', 12 ],
126             razao_social => [ 'str', 75 ],
127             endereco_tipo => [ 'str', 3 ],
128             endereco => [ 'str', 50 ],
129             endereco_num => [ 'str', 10 ],
130             endereco_complemento => [ 'str', 30 ],
131             endereco_bairro => [ 'str', 30 ],
132             endereco_cidade => [ 'str', 50 ],
133             endereco_uf => [ 'str', 2 ],
134             endereco_cep => [ 'num', 8 ],
135             email => [ 'str', 75 ],
136             discriminacao => [ 'str', -1000 ],
137             };
138             $params{aliquota} = $params{aliquota} * 100 if $params{aliquota};
139             $params{discriminacao} =~ s/\r?\n/|/g;
140              
141             my $out = $self->_formata( $campos, %params );
142             my @ordem = qw/
143             serie
144             numero
145             emissao
146             situacao
147             valor_servico
148             valor_deducao
149             codigo_servico
150             aliquota
151             iss_retido
152             cpf_cnpj_flag
153             cpf_cnpj
154             inscricao_municipal
155             inscricao_estadual
156             razao_social
157             endereco_tipo
158             endereco
159             endereco_num
160             endereco_complemento
161             endereco_bairro
162             endereco_cidade
163             endereco_uf
164             endereco_cep
165             email
166             discriminacao/;
167             my $line = '2'; # registro 2 versao 001
168             $line .= _pad_str( defined $params{tipo} &&
169             $params{tipo} =~ /^RPS(\-M)?$/ ? $params{tipo} : 'RPS', 5 );
170              
171             foreach (@ordem) {
172             $line .= $out->{$_};
173             }
174             $line .= "\r\n";
175              
176             $self->add_rps_lines($line);
177              
178             $self->inc_total_deducao( $params{valor_deducao} );
179             $self->inc_total_servico( $params{valor_servico} );
180              
181             $self->inc_total_linhas;
182             return 1;
183             }
184              
185             sub _formata {
186             my ( $self, $config, %params ) = @_;
187             my $x = {};
188             foreach my $campo ( keys %$config ) {
189             my $ref = $config->{$campo};
190             next unless ref $ref eq 'ARRAY';
191              
192             croak "The field '$campo' not send." unless defined $params{$campo};
193              
194             if ( $ref->[0] eq 'str' ) {
195             my $size = $ref->[1];
196              
197             if ( $size > 0 && length( $params{$campo} ) > $size ) {
198             croak "The field '$campo' (str) is bigger than $size (length)";
199             }
200             elsif ( $size < 0 ) {
201             $size = $size * -1;
202             if ( length( $params{$campo} ) > $size ) {
203             croak
204             "The field '$campo' (str) is bigger than $size (length)";
205             }
206             $x->{$campo} = $params{$campo};
207             }
208             else {
209             $x->{$campo} = _pad_str( $params{$campo}, $size );
210             }
211              
212             }
213             elsif ( $ref->[0] eq 'num' ) {
214             $params{$campo} ||= 0;
215             if ( $params{$campo} !~ /^[0-9]+(?:\.[0-9]+)?$/ ) {
216             croak
217             "The field '$campo' (num) with value $params{$campo} is not valid";
218             }
219             else {
220             my $size = $ref->[1];
221             my $round = $ref->[2];
222             if ( ref $size eq 'ARRAY' ) {
223             my %valid = map { $_ => 1 } @$size;
224             croak "The field '$campo' (num) is invalid."
225             unless $valid{ $params{$campo} };
226             $size = 1; # WARNING nao era pra ser assim..
227             }
228             elsif ( length( $params{$campo} ) > $size ) {
229             croak
230             "The field '$campo' (num) is bigger than $size (length)";
231             }
232             $x->{$campo} = _pad_num( $params{$campo}, $size, $round );
233             }
234              
235             }
236             elsif ( $ref->[0] eq 'date' ) {
237             if ( $params{$campo} !~ /^\d{4}\d{2}\d{2}$/ ) {
238             croak "The field '$campo' (date) is not in format AAAAMMDD";
239             }
240             else {
241             $x->{$campo} = $params{$campo};
242             }
243             }
244             }
245             return $x;
246             }
247              
248              
249             sub gerar_txt {
250             my ($self) = @_;
251              
252             my $campos = {
253             data_fim => ['date'],
254             data_ini => ['date'],
255             inscricao_municipal => [ 'num', 8 ],
256             };
257              
258             my $out = $self->_formata(
259             $campos,
260             data_fim => $self->data_fim,
261             data_ini => $self->data_ini,
262             inscricao_municipal => $self->inscricao_municipal
263             );
264              
265             my $str = '1001'
266             . $out->{inscricao_municipal}
267             . $out->{data_ini}
268             . $out->{data_fim} . "\r\n"
269             . $self->_rps_lines;
270              
271             $str .= '9'
272             . _pad_num( $self->_total_linhas, 7 )
273             . _pad_num( $self->_total_servico, 15, 2 )
274             . _pad_num( $self->_total_deducao, 15, 2 ) . "\r\n";
275              
276             return $str;
277             }
278              
279             1;
280              
281             __END__
282              
283             =pod
284              
285             =head1 NAME
286              
287             Business::BR::NFe::RPS::TXT
288              
289             =head1 VERSION
290              
291             version 0.0124
292              
293             =head1 SYNOPSIS
294              
295             my $txt = new Business::BR::NFe::RPS::TXT(
296             data_ini => '20120202',
297             data_fim => '20120204',
298             inscricao_municipal => '12345667',
299             );
300              
301             $txt->adiciona_rps(
302             serie => '011',
303             numero => '00',
304             emissao => '20121222',
305             situacao => '0',
306             valor_servico => 2400.34,
307             valor_deducao => 140.45,
308             codigo_servico => '00',
309             aliquota => '00',
310             iss_retido => '1',
311             cpf_cnpj_flag => '1',
312             cpf_cnpj => '00',
313             inscricao_municipal => '00',
314             inscricao_estadual => '00',
315             razao_social => '00',
316             endereco_tipo => '00',
317             endereco => '00',
318             endereco_num => '00',
319             endereco_complemento => '00',
320             endereco_bairro => '00',
321             endereco_cidade => '00',
322             endereco_uf => '00',
323             endereco_cep => '00',
324             email => '00',
325             discriminacao => '00',
326             );
327              
328             $txt->gerar_txt;
329              
330             =head1 DESCRIPTION
331              
332             O sistema da Nota Fiscal Paulistana permite que sejam transferidas informações dos contribuintes para a Prefeitura em arquivos no formato texto. Tais arquivos devem atender a um layout pré-definido, apresentado em http://nfpaulistana.prefeitura.sp.gov.br/arquivos/manual/NFe_Layout_RPS.pdf
333              
334             =head1 METHODS
335              
336             =head2 adiciona_rps
337              
338             Adicionar informações sobre um RPS. Verificar a SYNOPSIS para exemplo.
339              
340             =head2 gerar_txt
341              
342             Retorna o conteúdo para ser gravado em um arquivo.
343              
344             Atenção: O arquivo deve ser salvo em ISO 8859-1,
345             este modulo não modifica nenhum campo enviado além de ajustar os paddings.
346              
347             =head1 NAME
348              
349             =UTF8
350              
351             Business::BR::NFe::RPS::TXT - Gerar arquivo de envio de RPS em lote baseado no sistema de nota fiscal paulistana.
352              
353             Formato do arquivo na versao TXT 001.
354              
355             =head1 TODO
356              
357             =over 4
358              
359             =item *
360              
361             Limitar os dados em 10MB.
362              
363             =item *
364              
365             Adicionar suporte para RPS-C = Recibo Provisório de Serviços simplificado (Cupons).
366              
367             =back
368              
369             =head1 SUPPORT
370              
371             =head2 Perldoc
372              
373             Você pode encontrar documentação para este módulo com o comando perldoc (para ler)
374              
375             perldoc Business::BR::NFe::RPS::TXT
376              
377             =head2 Github
378              
379             Se você quiser contribuir com o código, você pode fazer um fork deste módulo no github:
380              
381             L<https://github.com/renatoaware/perl-business-br-nfe-txt>
382              
383             Você também pode reportar problemas por lá!
384              
385             =head1 AUTHOR
386              
387             Renato Cron <renato@aware.com.br>
388              
389             =head1 COPYRIGHT AND LICENSE
390              
391             This software is copyright (c) 2012 by Aware TI <http://www.aware.com.br>.
392              
393             This is free software; you can redistribute it and/or modify it under
394             the same terms as the Perl 5 programming language system itself.
395              
396             =cut