Converte arquivo de ISO-8859-1 para UTF-8.
[cascardo/irpf-gui.git] / src / contribuinte.py
1 # coding=utf-8
2 #
3 #   Copyright 2013 Thadeu Lima de Souza Cascardo <cascardo@cascardo.info>
4 #
5 #   This program is free software: you can redistribute it and/or modify
6 #   it under the terms of the GNU General Public License as published by
7 #   the Free Software Foundation, either version 3 of the License, or
8 #   (at your option) any later version.
9 #
10 #   This program is distributed in the hope that it will be useful,
11 #   but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #   GNU General Public License for more details.
14 #
15 #   You should have received a copy of the GNU General Public License
16 #   along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 # -*- mode: python; encoding: utf-8; -*-
18 import xml.dom.minidom
19 import dirs
20 import os
21
22 class Contribuinte:
23     def __init__(self, cpf):
24         irpf_dir = dirs.get_default_irpf_dir()
25         self.cpf = self._minimize_cpf(cpf)
26
27         if not self._validate_cpf(self.cpf):
28             raise RuntimeError("Invalid CPF: " + self.cpf)
29
30         if not os.path.exists(irpf_dir.get_resource_dir()):
31             raise RuntimeError("O caminho para o resource não existe: " + \
32                     irpf_dir.get_resource_dir())
33
34         if not os.path.exists(irpf_dir.get_userdata_dir()):
35             raise RuntimeError("O caminho para os dados não existe: " + \
36                     irpf_dir.get_userdata_dir())
37
38         self.cpf_file = irpf_dir.get_userdata_file("%s/%s.xml" % (self.cpf, self.cpf))
39         self.iddecl_file = irpf_dir.get_userdata_file("iddeclaracoes.xml")
40         self.declaracao = self._find_id()
41         self.dados = xml.dom.minidom.parse(self.cpf_file)
42         self.contribuinte = self.dados.getElementsByTagName("contribuinte")[0]
43
44     def _find_id(self):
45         cpf = self._normalize_cpf(self.cpf)
46         self.declaracoes = xml.dom.minidom.parse(self.iddecl_file)
47         for i in self.declaracoes.getElementsByTagName("item"):
48             if "cpf" in i.attributes.keys():
49                 if i.attributes["cpf"].nodeValue == cpf:
50                     return i
51         return None
52
53     # CPF normalizado se parece com 000.000.000-00
54     def _normalize_cpf(self, cpf):
55         ncpf = ""
56         for i in cpf:
57             if len(ncpf) == 3 or len(ncpf) == 7:
58                 ncpf += '.'
59             if len(ncpf) == 11:
60                 ncpf += '-'
61             if len(ncpf) == 14:
62                 break
63             if ord(i) >= ord('0') and ord(i) <= ord('9'):
64                 ncpf += i
65         if len(ncpf) != 14:
66             raise RuntimeError("Invalid CPF")
67         return ncpf
68
69     # CPF minimizado se parece com 01234567890
70     def _minimize_cpf(self, cpf):
71         return self._minimize_valor(cpf)
72
73     def _minimize_valor(self, valor):
74         nvalor = ''.join(e for e in valor if e.isalnum())
75         return str(nvalor)
76
77     def _validate_cpf(self, cpf):
78         if len(cpf) != 11:
79             return False
80         return self._validate_generico(cpf)
81
82     def _validate_generico(self, valor):
83         def calcula_digito_verificador(numero):
84             n = len(numero) + 1
85
86             soma = 0
87             for i in range(n):
88                 if i > len(numero) - 1:
89                     break
90                 soma = soma + int(numero[i]) * ( n - i)
91
92             dv =  soma % 11
93
94             if dv < 2:
95                 dv = 0
96             else:
97                 dv = 11 - dv
98
99             return numero + str(dv)
100
101         mcpf = self._minimize_valor(valor)
102         cpf_sem_dv = mcpf[:-2]
103
104         primeiro_dv = str(calcula_digito_verificador(cpf_sem_dv))
105         segundo_dv = calcula_digito_verificador(primeiro_dv)
106
107         return segundo_dv == mcpf
108
109     def save(self):
110         self.dados.writexml(open(self.cpf_file, "w"))
111         self.declaracoes.writexml(open(self.iddecl_file, "w"))
112
113     def _get_attr(self, el, attr):
114         if attr in el.attributes.keys():
115             return el.attributes[attr].nodeValue
116         return None
117
118     def _set_attr(self, el, attr, val):
119         el.attributes[attr].nodeValue = val
120
121     def get_declaracao(self, attr):
122         return self._get_attr(self.declaracao, attr)
123
124     def set_declaracao(self, attr, val):
125         self._set_attr(self.declaracao, attr, val)
126
127     def get_nome(self):
128         return self.get_declaracao("nome")
129
130     def set_nome(self, nome):
131         self.set_declaracao("nome", nome)
132
133     def get_campo_contribuinte(self, attr):
134         if attr == "nome":
135             return self.get_nome()
136         return self._get_attr(self.contribuinte, attr)
137
138     def set_campo_contribuinte(self, attr, val):
139         if attr == "nome":
140             self.set_nome(val)
141         else:
142             self._set_attr(self.contribuinte, attr, val)
143
144 contribuinte_attributes = [
145         "nome",
146         "dataNascimento",
147         "tituloEleitor",
148         "doencaDeficiencia",
149         "exterior",
150         "pais",
151         "cep",
152         "uf",
153         "cidade",
154         "municipio",
155         "tipoLogradouro",
156         "logradouro",
157         "numero",
158         "complemento",
159         "bairro",
160         "bairroExt",
161         "cepExt",
162         "logradouroExt",
163         "numeroExt",
164         "complementoExt",
165         "ocupacaoPrincipal",
166         "codigoExterior",
167         "ddd",
168         "telefone",
169         "naturezaOcupacao",
170         ]
171
172 declaracao_attributes = [
173         "dataUltimoAcesso",
174         "declaracaoRetificadora",
175         "enderecoDiferente",
176         "enderecoMACRede",
177         "exercicio",
178         "nome",
179         "numReciboDecRetif",
180         "numeroReciboDecAnterior",
181         "resultadoDeclaracao",
182         "tipoDeclaracao",
183         "tipoDeclaracaoAES",
184         "transmitida",
185         "versaoBeta"
186         ]
187
188 if __name__ == '__main__':
189     import sys
190     contribuinte = Contribuinte(sys.argv[1])
191     print "Carregando CPF " + contribuinte._normalize_cpf(sys.argv[1])
192
193     if len(sys.argv) == 4:
194         print "Valor anterior: " + contribuinte.get_campo_contribuinte(sys.argv[2])
195         contribuinte.set_campo_contribuinte(sys.argv[2], sys.argv[3])
196         print "Valor atual: " + contribuinte.get_campo_contribuinte(sys.argv[2])
197         print "Salvando..."
198         contribuinte.save()
199     elif len(sys.argv) == 3:
200         campo = sys.argv[2]
201         valor = contribuinte.get_campo_contribuinte(campo)
202         if valor:
203             print ("Valor de " + campo + ": " + valor)
204         else:
205             print ("Campo " + campo + " retornou vazio")
206     else:
207         print "\nCONTRIBUINTE:"
208         for i in contribuinte_attributes:
209             val = contribuinte.get_campo_contribuinte(i)
210             if val == None:
211                 val = ""
212             print i + ": " + val
213         print "\nDECLARACAO:"
214         for i in declaracao_attributes:
215             val = contribuinte.get_declaracao(i)
216             if val == None:
217                 val = ""
218             print i + ": " + val
219
220 # vim:tabstop=4:expandtab:smartindent