Merge lp:~atin81/openerp-mexico-localization/pac-facturalo into lp:openerp-mexico-localization/7.0
- pac-facturalo
- Merge into 7.0
Status: | Needs review |
---|---|
Proposed branch: | lp:~atin81/openerp-mexico-localization/pac-facturalo |
Merge into: | lp:openerp-mexico-localization/7.0 |
Diff against target: |
2852 lines (+2763/-0) 15 files modified
l10n_mx_facturae_pac_facturalo/__init__.py (+29/-0) l10n_mx_facturae_pac_facturalo/__openerp__.py (+77/-0) l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml (+24/-0) l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml (+18/-0) l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml (+37/-0) l10n_mx_facturae_pac_facturalo/i18n/es_MX.po (+275/-0) l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot (+270/-0) l10n_mx_facturae_pac_facturalo/invoice.py (+560/-0) l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py (+40/-0) l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py (+43/-0) l10n_mx_facturae_pac_facturalo/params_pac.py (+45/-0) l10n_mx_facturae_pac_facturalo/pyxmldsig.py (+578/-0) l10n_mx_facturae_pac_facturalo/report/__init__.py (+25/-0) l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py (+266/-0) l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml (+476/-0) |
To merge this branch: | bzr merge lp:~atin81/openerp-mexico-localization/pac-facturalo |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Nhomar - Vauxoo | Needs Fixing | ||
Review via email: mp+198988@code.launchpad.net |
Commit message
Description of the change
Agregamos un módulo para realizar la facturación electrónica con el pac factura-lo.mx.
Falta hacer la cancelación de las facturas, pero subimos por anticipado el código para obtener retroalimentación departe de ustedes sobre lo que estamos desarrollando.
De momento los todo's y preguntas que tenemos son las siguientes:
1.- De que manera podríamos poner a funcionar los dos módulos al mismo tiempo, creo que podría llegar a ser útil en algún momento para las empresas contar con dos servicios de facturación, uno principal y otro de respaldo, pero actualmente no es posible tener los dos módulos funcionando.
2.- Nosotros en el módulo sobreescribimos el reporte de invoices que viene por defecto en OpenERP, de acuerdo a nuestra experiencia, contar con dos reportes es confuso para el usuario, sobre todo porque uno de ellos jamás se usa. Porqué no dejar únicamente un sólo reporte?
Nhomar - Vauxoo (nhomar) wrote : | # |
Hola Agustin.
Tengo varias dudas, pero ante todo muchas gracias por el aporte.
Dudas Operativas:
1.- No veo a factura-lo aquí:
http://
Debido a la apertura del software y su fácil auditoría debemon colocar los nombres "Registrados" en la página del SAT, ¿crees que nos ayudas puedas apoyar con colocar eso por favor?
2.- Al momento de revisar el PAC con los dominios suministrados en los parámetros de conexión tenía un virus la página, ¿Me comentas cómo es eso posible? creo que como usuarios Linux pensar en Virus es nocivo para nuestra Salud </joke>
3.- Por favor los créditos no deben ir en el description del __openerp__.py, para eso es el campo website en el descriptor, lo puedes apuntar a tu página y los correos ya están en los comentarios del código junto con la licencia.
Dudas Técnicas:
3.- l10n_mx_
4.- Lo mismo para l10n_mx_
5.- Hay código comentado, en python eso no se hace ;-) (no es PHP) puedes removerlo y comentar en el commit el TODO, después puedes volverlo a colocar solo leyendo un bzr diff.
6.- Yo sé que nuestros módulos tienen comentarios en español, pero es debido a que son nuestra evolución y no han sido limpiados, me apoyas colocandolos en Inglés, de ésta manera nos ahorramos muchísimos errores con unicode y podemos usar tu caso de código como ejemplo cuando discutimos propuestas al core oficial.
7.- Sucede lo mismo con las variables (pero éstas déjalas así) las variables en español son inconsistentes es decir, leer browse.
8.- Hay muchísimo código copiado y pegado, creo que tenemos que hacer una limpieza antes de éste merge para poder unificar los criterios y darle respuesta a tus preguntas.
9.- El módulo no tiene un solo test unitario, te comento que estabilizar los otros nos ha costado un monton que marque verde en los servidores de integración contínua, veo sumamante riesgoso colocar un módulo sin tests "Tienes en planes trabajar en ésto".
10.- Sobre el reporte nosotros hemos hecho varias iteraciones, podemos discutirlas en un bug sobre ese tema específico (sin trancar esta MP por ese concepto esppecífico) ya que tenemos que diseñarlo.
Sobre tus dudas.
11.- Si nos propones alguna clase de diseño podemos hacer un sprint, nostros estamos plan...
Agustín (atin81) wrote : | # |
Antes que nada muchas gracias por la revisión Nhomar. Trataré de
responder lo mejor que pueda a todas tus dudas.
On 14/12/13 02:47, Nhomar - Vauxoo wrote:
> Review: Needs Fixing
>
> Hola Agustin.
>
> Tengo varias dudas, pero ante todo muchas gracias por el aporte.
>
> Dudas Operativas:
>
> 1.- No veo a factura-lo aquí:
> http://
>
> Debido a la apertura del software y su fácil auditoría debemon colocar los nombres "Registrados" en la página del SAT, ¿crees que nos ayudas puedas apoyar con colocar eso por favor?
Nosotros estamos trabajando en conjunto con la empresa de factura-lo.mx
en la realización de este código y para ofrecer la solución a nuestros
clientes. Voy a ponerme en contacto con ellos para ver cual es el nombre
que tienen registrado.
>
> 2.- Al momento de revisar el PAC con los dominios suministrados en los parámetros de conexión tenía un virus la página, ¿Me comentas cómo es eso posible? creo que como usuarios Linux pensar en Virus es nocivo para nuestra Salud </joke>
Mismo comentario anterior, no se exactamente que problema tuvieron en
días pasados con su web.
>
> 3.- Por favor los créditos no deben ir en el description del __openerp__.py, para eso es el campo website en el descriptor, lo puedes apuntar a tu página y los correos ya están en los comentarios del código junto con la licencia.
No intentaban ser créditos sino más bien lugares donde acudir en caso de
dudas, pero los vamos a retirar para dejar sólo la página web.
>
> Dudas Técnicas:
>
> 3.- l10n_mx_
>
> 4.- Lo mismo para l10n_mx_
Ok, los moveremos a demo y consideraremos la realización del wizard.
>
> 5.- Hay código comentado, en python eso no se hace ;-) (no es PHP) puedes removerlo y comentar en el commit el TODO, después puedes volverlo a colocar solo leyendo un bzr diff.
Intentamos empezar con un módulo nuevo pero no pudimos hacer que
funcionara por el desconocimiento del trabajo interno del módulo. Había
datos que no se creaban y cosas así. Así que decidimos hacer copy/paste
del módulo de pac_sf y de ahí ir refactorizando las funciones como las
íbamos necesitando. Esa es la razón del código comentado, irá
desapareciendo conforme el módulo vaya siendo pulido.
>
> 6.- Yo sé que nuestros módulos tienen comentarios en español, pero es debido a que son nuestra evolución y no han sido limpiados, me apoyas colocandolos en Inglés, de ésta manera nos ahorramos muchísimos errores con unicode y podemos usar tu caso de códi...
Agustín (atin81) wrote : | # |
LinkedIn
------------
Me gustaría añadirte a mi red profesional en LinkedIn.
-Agustín
Agustín Cruz
Director de Tecnologías de la Información en Fedrojesa SA de CV
Puebla de Zaragoza y alrededores, México
Confirma que conoces a Agustín Cruz:
https:/
--
Estás recibiendo invitaciones a conectar. Haz clic para darte de baja:
http://
(c) 2012 LinkedIn Corporation. 2029 Stierlin Ct, Mountain View, CA 94043, EE.UU.
Unmerged revisions
- 300. By Agustin Cruz <email address hidden>
-
Bringing changes from version 0.3.5
- 299. By Agustin Cruz <email address hidden>
-
Merged changes done on git until commit 87e9a70ac
- 298. By Agustin Cruz <email address hidden>
-
Agregado módulo para facturación electrónica con factura-lo.mx
- 297. By Agustin Cruz <email address hidden>
-
Nuevo módulo de facturación electrónica con factura-lo.mx
- 296. By Agustin Cruz <email address hidden>
-
Merged
Preview Diff
1 | === added directory 'l10n_mx_facturae_pac_facturalo' | |||
2 | === added file 'l10n_mx_facturae_pac_facturalo/__init__.py' | |||
3 | --- l10n_mx_facturae_pac_facturalo/__init__.py 1970-01-01 00:00:00 +0000 | |||
4 | +++ l10n_mx_facturae_pac_facturalo/__init__.py 2014-06-25 00:35:08 +0000 | |||
5 | @@ -0,0 +1,29 @@ | |||
6 | 1 | # -*- encoding: utf-8 -*- | ||
7 | 2 | ########################################################################### | ||
8 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
9 | 4 | # | ||
10 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
11 | 6 | # All Rights Reserved. | ||
12 | 7 | # info OpenPyme (info@openpyme.mx) | ||
13 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
14 | 9 | # | ||
15 | 10 | # This program is free software: you can redistribute it and/or modify | ||
16 | 11 | # it under the terms of the GNU Affero General Public License as | ||
17 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
18 | 13 | # License, or (at your option) any later version. | ||
19 | 14 | # | ||
20 | 15 | # This program is distributed in the hope that it will be useful, | ||
21 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
22 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
23 | 18 | # GNU Affero General Public License for more details. | ||
24 | 19 | # | ||
25 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
26 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
27 | 22 | # | ||
28 | 23 | ############################################################################## | ||
29 | 24 | |||
30 | 25 | import invoice | ||
31 | 26 | import report | ||
32 | 27 | import params_pac | ||
33 | 28 | import ir_sequence_approval | ||
34 | 29 | import ir_attachment_facturae | ||
35 | 0 | 30 | ||
36 | === added file 'l10n_mx_facturae_pac_facturalo/__init__.pyc' | |||
37 | 1 | Binary files l10n_mx_facturae_pac_facturalo/__init__.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/__init__.pyc 2014-06-25 00:35:08 +0000 differ | 31 | Binary files l10n_mx_facturae_pac_facturalo/__init__.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/__init__.pyc 2014-06-25 00:35:08 +0000 differ |
38 | === added file 'l10n_mx_facturae_pac_facturalo/__openerp__.py' | |||
39 | --- l10n_mx_facturae_pac_facturalo/__openerp__.py 1970-01-01 00:00:00 +0000 | |||
40 | +++ l10n_mx_facturae_pac_facturalo/__openerp__.py 2014-06-25 00:35:08 +0000 | |||
41 | @@ -0,0 +1,77 @@ | |||
42 | 1 | # -*- encoding: utf-8 -*- | ||
43 | 2 | ########################################################################### | ||
44 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
45 | 4 | # | ||
46 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
47 | 6 | # All Rights Reserved. | ||
48 | 7 | # info OpenPyme (info@openpyme.mx) | ||
49 | 8 | # Coded by: Agustin Cruz Lozano (agustin.cruz@openpyme.mx) | ||
50 | 9 | # Verónica Paleta (veronica.paleta@openpyme.mx) | ||
51 | 10 | # | ||
52 | 11 | # This program is free software: you can redistribute it and/or modify | ||
53 | 12 | # it under the terms of the GNU Affero General Public License as | ||
54 | 13 | # published by the Free Software Foundation, either version 3 of the | ||
55 | 14 | # License, or (at your option) any later version. | ||
56 | 15 | # | ||
57 | 16 | # This program is distributed in the hope that it will be useful, | ||
58 | 17 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
59 | 18 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
60 | 19 | # GNU Affero General Public License for more details. | ||
61 | 20 | # | ||
62 | 21 | # You should have received a copy of the GNU Affero General Public License | ||
63 | 22 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
64 | 23 | # | ||
65 | 24 | ############################################################################## | ||
66 | 25 | |||
67 | 26 | { | ||
68 | 27 | "name" : "Factura Electronica Mexico - Control de Comprobantes Digitales", | ||
69 | 28 | "version" : "1.0", | ||
70 | 29 | "author" : "OpenPyme", | ||
71 | 30 | "category" : "Localization/Mexico", | ||
72 | 31 | "description" : """This module allows use the electronic invoice service | ||
73 | 32 | provided by control de Comprobantes Digitales S. de R.L. de C.V. | ||
74 | 33 | Instructions for installation: | ||
75 | 34 | - Ubuntu: | ||
76 | 35 | sudo apt-get install xmlsec1 libxml2-dev libxslt-dev libxmlsec-dev | ||
77 | 36 | sudo apt-set install python-dev python-fpconst | ||
78 | 37 | sudo easy_install lxml pyxmlsec | ||
79 | 38 | - Archlinux: | ||
80 | 39 | yaourt -S python2-lxml python2-fpconst pyxmlsec | ||
81 | 40 | - Centos: | ||
82 | 41 | sudo yum install libxml2 libxml2-devel libxslt libxslt-dev | ||
83 | 42 | sudo yum install python-devel python-fpconst pyxmlsec | ||
84 | 43 | All operative systems: | ||
85 | 44 | - Python Soappy | ||
86 | 45 | cd /tmp | ||
87 | 46 | git clone https://github.com/Fedrojesa/SOAPpy.git | ||
88 | 47 | cd SOAPPy | ||
89 | 48 | python setup.py build | ||
90 | 49 | sudo python setup.py install | ||
91 | 50 | |||
92 | 51 | - Python qrcode (Todos los sistemas operativos)\n | ||
93 | 52 | cd /tmp | ||
94 | 53 | git clone https://github.com/lincolnloop/python-qrcode.git | ||
95 | 54 | cd python-qrcode | ||
96 | 55 | python setup.py build | ||
97 | 56 | sudo python setup.py install | ||
98 | 57 | """, | ||
99 | 58 | "website" : "http://www.openpyme.mx/", | ||
100 | 59 | "license" : "AGPL-3", | ||
101 | 60 | "depends" : ["l10n_mx_facturae_groups", | ||
102 | 61 | "l10n_mx_params_pac", | ||
103 | 62 | "l10n_mx_account_tax_category", | ||
104 | 63 | "l10n_mx_facturae_seq", | ||
105 | 64 | "l10n_mx_ir_attachment_facturae", | ||
106 | 65 | "l10n_mx_facturae_pac", | ||
107 | 66 | "l10n_mx_facturae_group_show_wizards", | ||
108 | 67 | "account_cancel" | ||
109 | 68 | ], | ||
110 | 69 | "demo" : [], | ||
111 | 70 | "data" : [ | ||
112 | 71 | "data/l10n_mx_facturae_pac_facturalo_report.xml", | ||
113 | 72 | "data/l10n_mx_facturae_pac_facturalo.xml", | ||
114 | 73 | "data/l10n_mx_facturae_seq.xml" | ||
115 | 74 | ], | ||
116 | 75 | "installable" : True, | ||
117 | 76 | "active" : False, | ||
118 | 77 | } | ||
119 | 0 | 78 | ||
120 | === added directory 'l10n_mx_facturae_pac_facturalo/data' | |||
121 | === added file 'l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml' | |||
122 | --- l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml 1970-01-01 00:00:00 +0000 | |||
123 | +++ l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml 2014-06-25 00:35:08 +0000 | |||
124 | @@ -0,0 +1,24 @@ | |||
125 | 1 | <?xml version="1.0" ?> | ||
126 | 2 | <openerp> | ||
127 | 3 | |||
128 | 4 | <data noupdate="1"> | ||
129 | 5 | <record id="params_pac_facturalo_cancelar" model="params.pac"> | ||
130 | 6 | <field name="method_type">pac_facturalo_cancelar</field> | ||
131 | 7 | <field name="url_webservice">http://cancelacion.expidetufactura.com.mx:8080/cancelacion/CancelacionPruebas?wsdl</field> | ||
132 | 8 | <field name="namespace">http://service.timbrado.cim.mx/</field> | ||
133 | 9 | <field name="user">pruebas</field> | ||
134 | 10 | <field name="password">123456</field> | ||
135 | 11 | <field name="name">Facturalo - Cancelar</field> | ||
136 | 12 | </record> | ||
137 | 13 | |||
138 | 14 | <record id="params_pac_facturalo_firmar" model="params.pac"> | ||
139 | 15 | <field name="method_type">pac_facturalo_firmar</field> | ||
140 | 16 | <field name="url_webservice">http://timbrado.expidetufactura.com.mx:8080/pruebas/TimbradoWS?wsdl</field> | ||
141 | 17 | <field name="namespace">http://service.timbrado.cim.mx/</field> | ||
142 | 18 | <field name="user">pruebas</field> | ||
143 | 19 | <field name="password">123456</field> | ||
144 | 20 | <field name="name">Facturalo - Firmar</field> | ||
145 | 21 | </record> | ||
146 | 22 | </data> | ||
147 | 23 | |||
148 | 24 | </openerp> | ||
149 | 0 | 25 | ||
150 | === added file 'l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml' | |||
151 | --- l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml 1970-01-01 00:00:00 +0000 | |||
152 | +++ l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml 2014-06-25 00:35:08 +0000 | |||
153 | @@ -0,0 +1,18 @@ | |||
154 | 1 | <?xml version="1.0" encoding="utf-8"?> | ||
155 | 2 | <openerp> | ||
156 | 3 | <data> | ||
157 | 4 | <report | ||
158 | 5 | auto="False" | ||
159 | 6 | id="l10n_mx_facturae_report.account_invoice_facturae_webkit" | ||
160 | 7 | model="account.invoice" | ||
161 | 8 | name="account.invoice.facturae.webkit" | ||
162 | 9 | rml="l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml" | ||
163 | 10 | string="Factura Electronica Report" | ||
164 | 11 | report_type="pdf" | ||
165 | 12 | header="False" | ||
166 | 13 | attachment="(object.state in ('open','paid')) and (object.fname_invoice and (object.fname_invoice + ''))" | ||
167 | 14 | attachment_use="True" | ||
168 | 15 | /> | ||
169 | 16 | </data> | ||
170 | 17 | |||
171 | 18 | </openerp> | ||
172 | 0 | 19 | ||
173 | === added file 'l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml' | |||
174 | --- l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml 1970-01-01 00:00:00 +0000 | |||
175 | +++ l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml 2014-06-25 00:35:08 +0000 | |||
176 | @@ -0,0 +1,37 @@ | |||
177 | 1 | <?xml version="1.0" ?> | ||
178 | 2 | <openerp> | ||
179 | 3 | <data noupdate="1"> | ||
180 | 4 | <record id="l10n_mx_facturae_ir_seq_01" model="ir.sequence"> | ||
181 | 5 | <field name="company_id" ref="base.main_company"/> | ||
182 | 6 | <field name="name">Sequence CFDI</field> | ||
183 | 7 | <field name="active" eval="True"/> | ||
184 | 8 | <field name="padding">0</field> | ||
185 | 9 | <field name="number_next">1</field> | ||
186 | 10 | <field name="number_increment">1</field> | ||
187 | 11 | <field name="implementation">standard</field> | ||
188 | 12 | </record> | ||
189 | 13 | |||
190 | 14 | <record id="l10n_mx_facturae_ir_seq_approval_01" model="ir.sequence.approval"> | ||
191 | 15 | <field name="company_id" ref="base.main_company"/> | ||
192 | 16 | <field name="sequence_id" ref="l10n_mx_facturae_ir_seq_01"/> | ||
193 | 17 | <field name="approval_number">12345</field> | ||
194 | 18 | <field name="serie">A</field> | ||
195 | 19 | <field name="approval_year" eval="time.strftime('%Y')"/> | ||
196 | 20 | <field name="number_start">1</field> | ||
197 | 21 | <field name="number_end">9999</field> | ||
198 | 22 | <field name="type">cfdi32</field> | ||
199 | 23 | </record> | ||
200 | 24 | |||
201 | 25 | <record id="l10n_mx_facturae_account_journal_01" model="account.journal"> | ||
202 | 26 | <field name="company_id" ref="base.main_company"/> | ||
203 | 27 | <field name="sequence_id" ref="l10n_mx_facturae_ir_seq_01"/> | ||
204 | 28 | <field name="name">Diario de CFDI</field> | ||
205 | 29 | <field name="code">CFDI</field> | ||
206 | 30 | <field name="type">sale</field> | ||
207 | 31 | <field name="update_posted">1</field> | ||
208 | 32 | <field name="user_id" ref="base.user_root"/> | ||
209 | 33 | <field name="company2_id" ref="base.main_company"/> | ||
210 | 34 | <field name="currency" ref="base.MXN"/> | ||
211 | 35 | </record> | ||
212 | 36 | </data> | ||
213 | 37 | </openerp> | ||
214 | 0 | 38 | ||
215 | === added directory 'l10n_mx_facturae_pac_facturalo/i18n' | |||
216 | === added file 'l10n_mx_facturae_pac_facturalo/i18n/es_MX.po' | |||
217 | --- l10n_mx_facturae_pac_facturalo/i18n/es_MX.po 1970-01-01 00:00:00 +0000 | |||
218 | +++ l10n_mx_facturae_pac_facturalo/i18n/es_MX.po 2014-06-25 00:35:08 +0000 | |||
219 | @@ -0,0 +1,275 @@ | |||
220 | 1 | # Translation of OpenERP Server. | ||
221 | 2 | # This file contains the translation of the following modules: | ||
222 | 3 | # * l10n_mx_facturae_pac_facturalo | ||
223 | 4 | # es_MX.po <agustin.cruz@openpy.mx>, 2014. | ||
224 | 5 | msgid "" | ||
225 | 6 | msgstr "" | ||
226 | 7 | "Project-Id-Version: OpenERP Server 7.0\n" | ||
227 | 8 | "Report-Msgid-Bugs-To: \n" | ||
228 | 9 | "POT-Creation-Date: 2014-06-25 00:21+0000\n" | ||
229 | 10 | "PO-Revision-Date: 2014-06-24 19:25-0500\n" | ||
230 | 11 | "Last-Translator: es_MX.po <agustin.cruz@openpy.mx>\n" | ||
231 | 12 | "Language-Team: Laptop\n" | ||
232 | 13 | "Language: es\n" | ||
233 | 14 | "MIME-Version: 1.0\n" | ||
234 | 15 | "Content-Type: text/plain; charset=UTF-8\n" | ||
235 | 16 | "Content-Transfer-Encoding: \n" | ||
236 | 17 | "Plural-Forms: nplurals=2; plural=(n != 1);\n" | ||
237 | 18 | "X-Generator: Virtaal 0.7.1\n" | ||
238 | 19 | |||
239 | 20 | #. module: l10n_mx_facturae_pac_facturalo | ||
240 | 21 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:354 | ||
241 | 22 | #, python-format | ||
242 | 23 | msgid "WARNING, CANCEL IN TEST!!!!\n" | ||
243 | 24 | "\n" | ||
244 | 25 | "" | ||
245 | 26 | msgstr "" | ||
246 | 27 | "PRECACIÓN, CANCELCIÓN HECHA EN PRUEBAS !!!\n" | ||
247 | 28 | "\n" | ||
248 | 29 | |||
249 | 30 | #. module: l10n_mx_facturae_pac_facturalo | ||
250 | 31 | #: field:account.invoice,cfdi_folio_fiscal:0 | ||
251 | 32 | msgid "CFD-I Folio Fiscal" | ||
252 | 33 | msgstr "" | ||
253 | 34 | |||
254 | 35 | #. module: l10n_mx_facturae_pac_facturalo | ||
255 | 36 | #: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:36 | ||
256 | 37 | #, python-format | ||
257 | 38 | msgid "PAC Factura-lo - Sign" | ||
258 | 39 | msgstr "Firma - PAC Factura-lo" | ||
259 | 40 | |||
260 | 41 | #. module: l10n_mx_facturae_pac_facturalo | ||
261 | 42 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_params_pac | ||
262 | 43 | msgid "params.pac" | ||
263 | 44 | msgstr "" | ||
264 | 45 | |||
265 | 46 | #. module: l10n_mx_facturae_pac_facturalo | ||
266 | 47 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:137 | ||
267 | 48 | #, python-format | ||
268 | 49 | msgid "Press in the button 'Upload File'" | ||
269 | 50 | msgstr "" | ||
270 | 51 | |||
271 | 52 | #. module: l10n_mx_facturae_pac_facturalo | ||
272 | 53 | #: help:account.invoice,cfdi_cadena_original:0 | ||
273 | 54 | msgid "Original String used in the electronic invoice" | ||
274 | 55 | msgstr "Cadena Original usada en la factura electrónica" | ||
275 | 56 | |||
276 | 57 | #. module: l10n_mx_facturae_pac_facturalo | ||
277 | 58 | #: help:account.invoice,cfdi_folio_fiscal:0 | ||
278 | 59 | msgid "Folio used in the electronic invoice" | ||
279 | 60 | msgstr "Foluo usado en la factura electrónica" | ||
280 | 61 | |||
281 | 62 | #. module: l10n_mx_facturae_pac_facturalo | ||
282 | 63 | #: help:account.invoice,cfdi_no_certificado:0 | ||
283 | 64 | msgid "Serial Number of the Certificate" | ||
284 | 65 | msgstr "Número de Serie del Certificado" | ||
285 | 66 | |||
286 | 67 | #. module: l10n_mx_facturae_pac_facturalo | ||
287 | 68 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:385 | ||
288 | 69 | #, python-format | ||
289 | 70 | msgid "codMensaje" | ||
290 | 71 | msgstr "" | ||
291 | 72 | |||
292 | 73 | #. module: l10n_mx_facturae_pac_facturalo | ||
293 | 74 | #: field:account.invoice,cfdi_fecha_timbrado:0 | ||
294 | 75 | msgid "CFD-I Fecha Timbrado" | ||
295 | 76 | msgstr "CFD-I Fecha Timbrado" | ||
296 | 77 | |||
297 | 78 | #. module: l10n_mx_facturae_pac_facturalo | ||
298 | 79 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:182 | ||
299 | 80 | #, python-format | ||
300 | 81 | msgid "Not Vat Number set on partner" | ||
301 | 82 | msgstr "No se ha asignado un RFC en la ficha del cliente" | ||
302 | 83 | |||
303 | 84 | #. module: l10n_mx_facturae_pac_facturalo | ||
304 | 85 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:247 | ||
305 | 86 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:379 | ||
306 | 87 | #, python-format | ||
307 | 88 | msgid "Can't get XML file from PAC." | ||
308 | 89 | msgstr "No se pudo obtener el archivo XML del PAC." | ||
309 | 90 | |||
310 | 91 | #. module: l10n_mx_facturae_pac_facturalo | ||
311 | 92 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:311 | ||
312 | 93 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:388 | ||
313 | 94 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:400 | ||
314 | 95 | #, python-format | ||
315 | 96 | msgid "Stamped Code: %s. \n" | ||
316 | 97 | " Stamped Message: %s." | ||
317 | 98 | msgstr "" | ||
318 | 99 | "Código devuelto: %s.\n" | ||
319 | 100 | "Mensaje devuelto: %s." | ||
320 | 101 | |||
321 | 102 | #. module: l10n_mx_facturae_pac_facturalo | ||
322 | 103 | #: help:account.invoice,cfdi_fecha_timbrado:0 | ||
323 | 104 | msgid "Date when is stamped the electronic invoice" | ||
324 | 105 | msgstr "Fecha en que fue firmada la factura electrónica" | ||
325 | 106 | |||
326 | 107 | #. module: l10n_mx_facturae_pac_facturalo | ||
327 | 108 | #: help:account.invoice,cfdi_sello:0 | ||
328 | 109 | msgid "Sign assigned by the SAT" | ||
329 | 110 | msgstr "Sello asignado por el SAT" | ||
330 | 111 | |||
331 | 112 | #. module: l10n_mx_facturae_pac_facturalo | ||
332 | 113 | #: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:37 | ||
333 | 114 | #, python-format | ||
334 | 115 | msgid "PAC Factura-lo - Cancel" | ||
335 | 116 | msgstr "" | ||
336 | 117 | |||
337 | 118 | #. module: l10n_mx_facturae_pac_facturalo | ||
338 | 119 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:185 | ||
339 | 120 | #, python-format | ||
340 | 121 | msgid "Customer Address Not Invoice Type" | ||
341 | 122 | msgstr "La dirección del cliente no es de tipo Invoice" | ||
342 | 123 | |||
343 | 124 | #. module: l10n_mx_facturae_pac_facturalo | ||
344 | 125 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:537 | ||
345 | 126 | #, python-format | ||
346 | 127 | msgid "Not captured a CERTIFICATE file in format PEM, in the company!" | ||
347 | 128 | msgstr "" | ||
348 | 129 | |||
349 | 130 | #. module: l10n_mx_facturae_pac_facturalo | ||
350 | 131 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:551 | ||
351 | 132 | #, python-format | ||
352 | 133 | msgid "Check date of invoice and the validity of certificate" | ||
353 | 134 | msgstr "Revise la fecha de la factura y la fecha de validez del certificado" | ||
354 | 135 | |||
355 | 136 | #. module: l10n_mx_facturae_pac_facturalo | ||
356 | 137 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:242 | ||
357 | 138 | #, python-format | ||
358 | 139 | msgid "SIN FOLIO O ESTATUS NO VALIDO\n" | ||
359 | 140 | "" | ||
360 | 141 | msgstr "" | ||
361 | 142 | |||
362 | 143 | #. module: l10n_mx_facturae_pac_facturalo | ||
363 | 144 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:249 | ||
364 | 145 | #, python-format | ||
365 | 146 | msgid "Este comprobante tendrá una vigencia de dos años contados a partir de la fecha aprobación de la asignación de folios, la cual es: %s" | ||
366 | 147 | msgstr "" | ||
367 | 148 | |||
368 | 149 | #. module: l10n_mx_facturae_pac_facturalo | ||
369 | 150 | #: field:account.invoice,cfdi_sello:0 | ||
370 | 151 | msgid "CFD-I Sello" | ||
371 | 152 | msgstr "Sello CFDI" | ||
372 | 153 | |||
373 | 154 | #. module: l10n_mx_facturae_pac_facturalo | ||
374 | 155 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:246 | ||
375 | 156 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:378 | ||
376 | 157 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:387 | ||
377 | 158 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:399 | ||
378 | 159 | #, python-format | ||
379 | 160 | msgid "Error" | ||
380 | 161 | msgstr "" | ||
381 | 162 | |||
382 | 163 | #. module: l10n_mx_facturae_pac_facturalo | ||
383 | 164 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:536 | ||
384 | 165 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546 | ||
385 | 166 | #, python-format | ||
386 | 167 | msgid "Error !" | ||
387 | 168 | msgstr "" | ||
388 | 169 | |||
389 | 170 | #. module: l10n_mx_facturae_pac_facturalo | ||
390 | 171 | #: field:account.invoice,cfdi_fecha_cancelacion:0 | ||
391 | 172 | msgid "CFD-I Fecha Cancelacion" | ||
392 | 173 | msgstr "" | ||
393 | 174 | |||
394 | 175 | #. module: l10n_mx_facturae_pac_facturalo | ||
395 | 176 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:550 | ||
396 | 177 | #, python-format | ||
397 | 178 | msgid "Warning !" | ||
398 | 179 | msgstr "" | ||
399 | 180 | |||
400 | 181 | #. module: l10n_mx_facturae_pac_facturalo | ||
401 | 182 | #: field:account.invoice,cfdi_cadena_original:0 | ||
402 | 183 | msgid "CFD-I Cadena Original" | ||
403 | 184 | msgstr "" | ||
404 | 185 | |||
405 | 186 | #. module: l10n_mx_facturae_pac_facturalo | ||
406 | 187 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:396 | ||
407 | 188 | #, python-format | ||
408 | 189 | msgid "UUID No existe" | ||
409 | 190 | msgstr "" | ||
410 | 191 | |||
411 | 192 | #. module: l10n_mx_facturae_pac_facturalo | ||
412 | 193 | #: help:account.invoice,cfdi_fecha_cancelacion:0 | ||
413 | 194 | msgid "If the invoice is cancel, this field saved the date when is cancel" | ||
414 | 195 | msgstr "" | ||
415 | 196 | |||
416 | 197 | #. module: l10n_mx_facturae_pac_facturalo | ||
417 | 198 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:394 | ||
418 | 199 | #, python-format | ||
419 | 200 | msgid "UUID No corresponde al emisor" | ||
420 | 201 | msgstr "" | ||
421 | 202 | |||
422 | 203 | #. module: l10n_mx_facturae_pac_facturalo | ||
423 | 204 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:347 | ||
424 | 205 | #, python-format | ||
425 | 206 | msgid "Not found information from web services of PAC, verify that the configuration of PAC is correct" | ||
426 | 207 | msgstr "" | ||
427 | 208 | |||
428 | 209 | #. module: l10n_mx_facturae_pac_facturalo | ||
429 | 210 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:395 | ||
430 | 211 | #, python-format | ||
431 | 212 | msgid "UUID No aplicable para cancelación" | ||
432 | 213 | msgstr "" | ||
433 | 214 | |||
434 | 215 | #. module: l10n_mx_facturae_pac_facturalo | ||
435 | 216 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:220 | ||
436 | 217 | #, python-format | ||
437 | 218 | msgid "Not found information from web services of PACVerify that the configuration of PAC is correct" | ||
438 | 219 | msgstr "" | ||
439 | 220 | |||
440 | 221 | #. module: l10n_mx_facturae_pac_facturalo | ||
441 | 222 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_sequence_approval | ||
442 | 223 | msgid "ir.sequence.approval" | ||
443 | 224 | msgstr "" | ||
444 | 225 | |||
445 | 226 | #. module: l10n_mx_facturae_pac_facturalo | ||
446 | 227 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546 | ||
447 | 228 | #, python-format | ||
448 | 229 | msgid "Not captured a KEY file in format PEM, in the company!" | ||
449 | 230 | msgstr "" | ||
450 | 231 | |||
451 | 232 | #. module: l10n_mx_facturae_pac_facturalo | ||
452 | 233 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:219 | ||
453 | 234 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:310 | ||
454 | 235 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:346 | ||
455 | 236 | #, python-format | ||
456 | 237 | msgid "Warning" | ||
457 | 238 | msgstr "" | ||
458 | 239 | |||
459 | 240 | #. module: l10n_mx_facturae_pac_facturalo | ||
460 | 241 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_attachment_facturae_mx | ||
461 | 242 | msgid "Email Thread" | ||
462 | 243 | msgstr "" | ||
463 | 244 | |||
464 | 245 | #. module: l10n_mx_facturae_pac_facturalo | ||
465 | 246 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_account_invoice | ||
466 | 247 | msgid "Invoice" | ||
467 | 248 | msgstr "" | ||
468 | 249 | |||
469 | 250 | #. module: l10n_mx_facturae_pac_facturalo | ||
470 | 251 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:239 | ||
471 | 252 | #, python-format | ||
472 | 253 | msgid "Número de aprobación SICOFI: %s\n" | ||
473 | 254 | "" | ||
474 | 255 | msgstr "" | ||
475 | 256 | |||
476 | 257 | #. module: l10n_mx_facturae_pac_facturalo | ||
477 | 258 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:243 | ||
478 | 259 | #, python-format | ||
479 | 260 | msgid "La reproducción apócrifa de este comprobante constituye un delito en los términos de las disposiciones fiscales.\n" | ||
480 | 261 | "" | ||
481 | 262 | msgstr "" | ||
482 | 263 | |||
483 | 264 | #. module: l10n_mx_facturae_pac_facturalo | ||
484 | 265 | #: field:account.invoice,cfdi_no_certificado:0 | ||
485 | 266 | msgid "CFD-I Certificado" | ||
486 | 267 | msgstr "" | ||
487 | 268 | |||
488 | 269 | #. module: l10n_mx_facturae_pac_facturalo | ||
489 | 270 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:228 | ||
490 | 271 | #, python-format | ||
491 | 272 | msgid "WARNING, SIGNED IN TEST!!!!\n" | ||
492 | 273 | "\n" | ||
493 | 274 | "" | ||
494 | 275 | msgstr "" | ||
495 | 0 | 276 | ||
496 | === added file 'l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot' | |||
497 | --- l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot 1970-01-01 00:00:00 +0000 | |||
498 | +++ l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot 2014-06-25 00:35:08 +0000 | |||
499 | @@ -0,0 +1,270 @@ | |||
500 | 1 | # Translation of OpenERP Server. | ||
501 | 2 | # This file contains the translation of the following modules: | ||
502 | 3 | # * l10n_mx_facturae_pac_facturalo | ||
503 | 4 | # | ||
504 | 5 | msgid "" | ||
505 | 6 | msgstr "" | ||
506 | 7 | "Project-Id-Version: OpenERP Server 7.0\n" | ||
507 | 8 | "Report-Msgid-Bugs-To: \n" | ||
508 | 9 | "POT-Creation-Date: 2014-06-25 00:21+0000\n" | ||
509 | 10 | "PO-Revision-Date: 2014-06-25 00:21+0000\n" | ||
510 | 11 | "Last-Translator: <>\n" | ||
511 | 12 | "Language-Team: \n" | ||
512 | 13 | "MIME-Version: 1.0\n" | ||
513 | 14 | "Content-Type: text/plain; charset=UTF-8\n" | ||
514 | 15 | "Content-Transfer-Encoding: \n" | ||
515 | 16 | "Plural-Forms: \n" | ||
516 | 17 | |||
517 | 18 | #. module: l10n_mx_facturae_pac_facturalo | ||
518 | 19 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:354 | ||
519 | 20 | #, python-format | ||
520 | 21 | msgid "WARNING, CANCEL IN TEST!!!!\n" | ||
521 | 22 | "\n" | ||
522 | 23 | "" | ||
523 | 24 | msgstr "" | ||
524 | 25 | |||
525 | 26 | #. module: l10n_mx_facturae_pac_facturalo | ||
526 | 27 | #: field:account.invoice,cfdi_folio_fiscal:0 | ||
527 | 28 | msgid "CFD-I Folio Fiscal" | ||
528 | 29 | msgstr "" | ||
529 | 30 | |||
530 | 31 | #. module: l10n_mx_facturae_pac_facturalo | ||
531 | 32 | #: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:36 | ||
532 | 33 | #, python-format | ||
533 | 34 | msgid "PAC Factura-lo - Sign" | ||
534 | 35 | msgstr "" | ||
535 | 36 | |||
536 | 37 | #. module: l10n_mx_facturae_pac_facturalo | ||
537 | 38 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_params_pac | ||
538 | 39 | msgid "params.pac" | ||
539 | 40 | msgstr "" | ||
540 | 41 | |||
541 | 42 | #. module: l10n_mx_facturae_pac_facturalo | ||
542 | 43 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:137 | ||
543 | 44 | #, python-format | ||
544 | 45 | msgid "Press in the button 'Upload File'" | ||
545 | 46 | msgstr "" | ||
546 | 47 | |||
547 | 48 | #. module: l10n_mx_facturae_pac_facturalo | ||
548 | 49 | #: help:account.invoice,cfdi_cadena_original:0 | ||
549 | 50 | msgid "Original String used in the electronic invoice" | ||
550 | 51 | msgstr "" | ||
551 | 52 | |||
552 | 53 | #. module: l10n_mx_facturae_pac_facturalo | ||
553 | 54 | #: help:account.invoice,cfdi_folio_fiscal:0 | ||
554 | 55 | msgid "Folio used in the electronic invoice" | ||
555 | 56 | msgstr "" | ||
556 | 57 | |||
557 | 58 | #. module: l10n_mx_facturae_pac_facturalo | ||
558 | 59 | #: help:account.invoice,cfdi_no_certificado:0 | ||
559 | 60 | msgid "Serial Number of the Certificate" | ||
560 | 61 | msgstr "" | ||
561 | 62 | |||
562 | 63 | #. module: l10n_mx_facturae_pac_facturalo | ||
563 | 64 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:385 | ||
564 | 65 | #, python-format | ||
565 | 66 | msgid "codMensaje" | ||
566 | 67 | msgstr "" | ||
567 | 68 | |||
568 | 69 | #. module: l10n_mx_facturae_pac_facturalo | ||
569 | 70 | #: field:account.invoice,cfdi_fecha_timbrado:0 | ||
570 | 71 | msgid "CFD-I Fecha Timbrado" | ||
571 | 72 | msgstr "" | ||
572 | 73 | |||
573 | 74 | #. module: l10n_mx_facturae_pac_facturalo | ||
574 | 75 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:182 | ||
575 | 76 | #, python-format | ||
576 | 77 | msgid "Not Vat Number set on partner" | ||
577 | 78 | msgstr "" | ||
578 | 79 | |||
579 | 80 | #. module: l10n_mx_facturae_pac_facturalo | ||
580 | 81 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:247 | ||
581 | 82 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:379 | ||
582 | 83 | #, python-format | ||
583 | 84 | msgid "Can't get XML file from PAC." | ||
584 | 85 | msgstr "" | ||
585 | 86 | |||
586 | 87 | #. module: l10n_mx_facturae_pac_facturalo | ||
587 | 88 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:311 | ||
588 | 89 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:388 | ||
589 | 90 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:400 | ||
590 | 91 | #, python-format | ||
591 | 92 | msgid "Stamped Code: %s. \n" | ||
592 | 93 | " Stamped Message: %s." | ||
593 | 94 | msgstr "" | ||
594 | 95 | |||
595 | 96 | #. module: l10n_mx_facturae_pac_facturalo | ||
596 | 97 | #: help:account.invoice,cfdi_fecha_timbrado:0 | ||
597 | 98 | msgid "Date when is stamped the electronic invoice" | ||
598 | 99 | msgstr "" | ||
599 | 100 | |||
600 | 101 | #. module: l10n_mx_facturae_pac_facturalo | ||
601 | 102 | #: help:account.invoice,cfdi_sello:0 | ||
602 | 103 | msgid "Sign assigned by the SAT" | ||
603 | 104 | msgstr "" | ||
604 | 105 | |||
605 | 106 | #. module: l10n_mx_facturae_pac_facturalo | ||
606 | 107 | #: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:37 | ||
607 | 108 | #, python-format | ||
608 | 109 | msgid "PAC Factura-lo - Cancel" | ||
609 | 110 | msgstr "" | ||
610 | 111 | |||
611 | 112 | #. module: l10n_mx_facturae_pac_facturalo | ||
612 | 113 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:185 | ||
613 | 114 | #, python-format | ||
614 | 115 | msgid "Customer Address Not Invoice Type" | ||
615 | 116 | msgstr "" | ||
616 | 117 | |||
617 | 118 | #. module: l10n_mx_facturae_pac_facturalo | ||
618 | 119 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:537 | ||
619 | 120 | #, python-format | ||
620 | 121 | msgid "Not captured a CERTIFICATE file in format PEM, in the company!" | ||
621 | 122 | msgstr "" | ||
622 | 123 | |||
623 | 124 | #. module: l10n_mx_facturae_pac_facturalo | ||
624 | 125 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:551 | ||
625 | 126 | #, python-format | ||
626 | 127 | msgid "Check date of invoice and the validity of certificate" | ||
627 | 128 | msgstr "" | ||
628 | 129 | |||
629 | 130 | #. module: l10n_mx_facturae_pac_facturalo | ||
630 | 131 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:242 | ||
631 | 132 | #, python-format | ||
632 | 133 | msgid "SIN FOLIO O ESTATUS NO VALIDO\n" | ||
633 | 134 | "" | ||
634 | 135 | msgstr "" | ||
635 | 136 | |||
636 | 137 | #. module: l10n_mx_facturae_pac_facturalo | ||
637 | 138 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:249 | ||
638 | 139 | #, python-format | ||
639 | 140 | msgid "Este comprobante tendrá una vigencia de dos años contados a partir de la fecha aprobación de la asignación de folios, la cual es: %s" | ||
640 | 141 | msgstr "" | ||
641 | 142 | |||
642 | 143 | #. module: l10n_mx_facturae_pac_facturalo | ||
643 | 144 | #: field:account.invoice,cfdi_sello:0 | ||
644 | 145 | msgid "CFD-I Sello" | ||
645 | 146 | msgstr "" | ||
646 | 147 | |||
647 | 148 | #. module: l10n_mx_facturae_pac_facturalo | ||
648 | 149 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:246 | ||
649 | 150 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:378 | ||
650 | 151 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:387 | ||
651 | 152 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:399 | ||
652 | 153 | #, python-format | ||
653 | 154 | msgid "Error" | ||
654 | 155 | msgstr "" | ||
655 | 156 | |||
656 | 157 | #. module: l10n_mx_facturae_pac_facturalo | ||
657 | 158 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:536 | ||
658 | 159 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546 | ||
659 | 160 | #, python-format | ||
660 | 161 | msgid "Error !" | ||
661 | 162 | msgstr "" | ||
662 | 163 | |||
663 | 164 | #. module: l10n_mx_facturae_pac_facturalo | ||
664 | 165 | #: field:account.invoice,cfdi_fecha_cancelacion:0 | ||
665 | 166 | msgid "CFD-I Fecha Cancelacion" | ||
666 | 167 | msgstr "" | ||
667 | 168 | |||
668 | 169 | #. module: l10n_mx_facturae_pac_facturalo | ||
669 | 170 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:550 | ||
670 | 171 | #, python-format | ||
671 | 172 | msgid "Warning !" | ||
672 | 173 | msgstr "" | ||
673 | 174 | |||
674 | 175 | #. module: l10n_mx_facturae_pac_facturalo | ||
675 | 176 | #: field:account.invoice,cfdi_cadena_original:0 | ||
676 | 177 | msgid "CFD-I Cadena Original" | ||
677 | 178 | msgstr "" | ||
678 | 179 | |||
679 | 180 | #. module: l10n_mx_facturae_pac_facturalo | ||
680 | 181 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:396 | ||
681 | 182 | #, python-format | ||
682 | 183 | msgid "UUID No existe" | ||
683 | 184 | msgstr "" | ||
684 | 185 | |||
685 | 186 | #. module: l10n_mx_facturae_pac_facturalo | ||
686 | 187 | #: help:account.invoice,cfdi_fecha_cancelacion:0 | ||
687 | 188 | msgid "If the invoice is cancel, this field saved the date when is cancel" | ||
688 | 189 | msgstr "" | ||
689 | 190 | |||
690 | 191 | #. module: l10n_mx_facturae_pac_facturalo | ||
691 | 192 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:394 | ||
692 | 193 | #, python-format | ||
693 | 194 | msgid "UUID No corresponde al emisor" | ||
694 | 195 | msgstr "" | ||
695 | 196 | |||
696 | 197 | #. module: l10n_mx_facturae_pac_facturalo | ||
697 | 198 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:347 | ||
698 | 199 | #, python-format | ||
699 | 200 | msgid "Not found information from web services of PAC, verify that the configuration of PAC is correct" | ||
700 | 201 | msgstr "" | ||
701 | 202 | |||
702 | 203 | #. module: l10n_mx_facturae_pac_facturalo | ||
703 | 204 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:395 | ||
704 | 205 | #, python-format | ||
705 | 206 | msgid "UUID No aplicable para cancelación" | ||
706 | 207 | msgstr "" | ||
707 | 208 | |||
708 | 209 | #. module: l10n_mx_facturae_pac_facturalo | ||
709 | 210 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:220 | ||
710 | 211 | #, python-format | ||
711 | 212 | msgid "Not found information from web services of PACVerify that the configuration of PAC is correct" | ||
712 | 213 | msgstr "" | ||
713 | 214 | |||
714 | 215 | #. module: l10n_mx_facturae_pac_facturalo | ||
715 | 216 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_sequence_approval | ||
716 | 217 | msgid "ir.sequence.approval" | ||
717 | 218 | msgstr "" | ||
718 | 219 | |||
719 | 220 | #. module: l10n_mx_facturae_pac_facturalo | ||
720 | 221 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546 | ||
721 | 222 | #, python-format | ||
722 | 223 | msgid "Not captured a KEY file in format PEM, in the company!" | ||
723 | 224 | msgstr "" | ||
724 | 225 | |||
725 | 226 | #. module: l10n_mx_facturae_pac_facturalo | ||
726 | 227 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:219 | ||
727 | 228 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:310 | ||
728 | 229 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:346 | ||
729 | 230 | #, python-format | ||
730 | 231 | msgid "Warning" | ||
731 | 232 | msgstr "" | ||
732 | 233 | |||
733 | 234 | #. module: l10n_mx_facturae_pac_facturalo | ||
734 | 235 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_attachment_facturae_mx | ||
735 | 236 | msgid "Email Thread" | ||
736 | 237 | msgstr "" | ||
737 | 238 | |||
738 | 239 | #. module: l10n_mx_facturae_pac_facturalo | ||
739 | 240 | #: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_account_invoice | ||
740 | 241 | msgid "Invoice" | ||
741 | 242 | msgstr "" | ||
742 | 243 | |||
743 | 244 | #. module: l10n_mx_facturae_pac_facturalo | ||
744 | 245 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:239 | ||
745 | 246 | #, python-format | ||
746 | 247 | msgid "Número de aprobación SICOFI: %s\n" | ||
747 | 248 | "" | ||
748 | 249 | msgstr "" | ||
749 | 250 | |||
750 | 251 | #. module: l10n_mx_facturae_pac_facturalo | ||
751 | 252 | #: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:243 | ||
752 | 253 | #, python-format | ||
753 | 254 | msgid "La reproducción apócrifa de este comprobante constituye un delito en los términos de las disposiciones fiscales.\n" | ||
754 | 255 | "" | ||
755 | 256 | msgstr "" | ||
756 | 257 | |||
757 | 258 | #. module: l10n_mx_facturae_pac_facturalo | ||
758 | 259 | #: field:account.invoice,cfdi_no_certificado:0 | ||
759 | 260 | msgid "CFD-I Certificado" | ||
760 | 261 | msgstr "" | ||
761 | 262 | |||
762 | 263 | #. module: l10n_mx_facturae_pac_facturalo | ||
763 | 264 | #: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:228 | ||
764 | 265 | #, python-format | ||
765 | 266 | msgid "WARNING, SIGNED IN TEST!!!!\n" | ||
766 | 267 | "\n" | ||
767 | 268 | "" | ||
768 | 269 | msgstr "" | ||
769 | 270 | |||
770 | 0 | 271 | ||
771 | === added file 'l10n_mx_facturae_pac_facturalo/invoice.py' | |||
772 | --- l10n_mx_facturae_pac_facturalo/invoice.py 1970-01-01 00:00:00 +0000 | |||
773 | +++ l10n_mx_facturae_pac_facturalo/invoice.py 2014-06-25 00:35:08 +0000 | |||
774 | @@ -0,0 +1,560 @@ | |||
775 | 1 | # -*- encoding: utf-8 -*- | ||
776 | 2 | ########################################################################### | ||
777 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
778 | 4 | # | ||
779 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
780 | 6 | # All Rights Reserved. | ||
781 | 7 | # info OpenPyme (info@openpyme.mx) | ||
782 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
783 | 9 | # | ||
784 | 10 | # This program is free software: you can redistribute it and/or modify | ||
785 | 11 | # it under the terms of the GNU Affero General Public License as | ||
786 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
787 | 13 | # License, or (at your option) any later version. | ||
788 | 14 | # | ||
789 | 15 | # This program is distributed in the hope that it will be useful, | ||
790 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
791 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
792 | 18 | # GNU Affero General Public License for more details. | ||
793 | 19 | # | ||
794 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
795 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
796 | 22 | # | ||
797 | 23 | ############################################################################## | ||
798 | 24 | |||
799 | 25 | import logging | ||
800 | 26 | import base64 | ||
801 | 27 | import string | ||
802 | 28 | from datetime import datetime | ||
803 | 29 | from pytz import timezone | ||
804 | 30 | import pytz | ||
805 | 31 | import re | ||
806 | 32 | import time | ||
807 | 33 | |||
808 | 34 | from openerp.tools.translate import _ | ||
809 | 35 | from openerp.osv import fields, osv | ||
810 | 36 | from openerp import tools | ||
811 | 37 | |||
812 | 38 | |||
813 | 39 | logger = logging.getLogger(__name__) | ||
814 | 40 | |||
815 | 41 | try: | ||
816 | 42 | from SOAPpy import WSDL | ||
817 | 43 | except: | ||
818 | 44 | logger.error("Package SOAPpy missed") | ||
819 | 45 | |||
820 | 46 | |||
821 | 47 | class account_invoice(osv.osv): | ||
822 | 48 | _inherit = 'account.invoice' | ||
823 | 49 | |||
824 | 50 | _columns = { | ||
825 | 51 | 'cfdi_sello': fields.text('CFD-I Sello', help='Sign assigned by the SAT'), | ||
826 | 52 | 'cfdi_no_certificado': fields.char('CFD-I Certificado', size=32, | ||
827 | 53 | help='Serial Number of the Certificate'), | ||
828 | 54 | 'cfdi_cadena_original': fields.text('CFD-I Cadena Original', | ||
829 | 55 | help='Original String used in the electronic invoice'), | ||
830 | 56 | 'cfdi_fecha_timbrado': fields.datetime('CFD-I Fecha Timbrado', | ||
831 | 57 | help='Date when is stamped the electronic invoice'), | ||
832 | 58 | 'cfdi_fecha_cancelacion': fields.datetime('CFD-I Fecha Cancelacion', | ||
833 | 59 | help='If the invoice is cancel, this field saved the date when is cancel'), | ||
834 | 60 | 'cfdi_folio_fiscal': fields.char('CFD-I Folio Fiscal', size=64, | ||
835 | 61 | help='Folio used in the electronic invoice'), | ||
836 | 62 | } | ||
837 | 63 | |||
838 | 64 | def write_cfd_data(self, cr, uid, ids, cfd_data=None, context=None): | ||
839 | 65 | """ | ||
840 | 66 | @param cfd_datas : Dictionary with data that is used in facturae CFDI | ||
841 | 67 | """ | ||
842 | 68 | if not cfd_data: | ||
843 | 69 | cfd_data = {} | ||
844 | 70 | |||
845 | 71 | comprobante = self._get_type_sequence(cr, uid, ids, context=context) | ||
846 | 72 | |||
847 | 73 | noCertificado = cfd_data.get(comprobante, {}).get('noCertificado', '') | ||
848 | 74 | certificado = cfd_data.get(comprobante, {}).get('certificado', '') | ||
849 | 75 | sello = cfd_data.get(comprobante, {}).get('sello', '') | ||
850 | 76 | cadena_original = cfd_data.get('cadena_original', '') | ||
851 | 77 | data = { | ||
852 | 78 | 'no_certificado': noCertificado, | ||
853 | 79 | 'certificado': certificado, | ||
854 | 80 | 'sello': sello, | ||
855 | 81 | 'cadena_original': cadena_original, | ||
856 | 82 | } | ||
857 | 83 | self.write(cr, uid, ids, data, context=context) | ||
858 | 84 | return True | ||
859 | 85 | |||
860 | 86 | def copy(self, cr, uid, id, default={}, context=None): | ||
861 | 87 | if context is None: | ||
862 | 88 | context = {} | ||
863 | 89 | default.update({ | ||
864 | 90 | 'cfdi_cbb': False, | ||
865 | 91 | 'cfdi_sello': False, | ||
866 | 92 | 'cfdi_no_certificado': False, | ||
867 | 93 | 'cfdi_cadena_original': False, | ||
868 | 94 | 'cfdi_fecha_timbrado': False, | ||
869 | 95 | 'cfdi_folio_fiscal': False, | ||
870 | 96 | 'cfdi_fecha_cancelacion': False, | ||
871 | 97 | }) | ||
872 | 98 | return super(account_invoice, self).copy(cr, uid, id, default, context) | ||
873 | 99 | |||
874 | 100 | def action_cancel_draft(self, cr, uid, ids, *args): | ||
875 | 101 | self.write(cr, uid, ids, { | ||
876 | 102 | 'cfdi_sello':False, | ||
877 | 103 | 'cfdi_no_certificado':False, | ||
878 | 104 | 'cfdi_cadena_original':False, | ||
879 | 105 | 'cfdi_fecha_timbrado': False, | ||
880 | 106 | 'cfdi_folio_fiscal': False, | ||
881 | 107 | 'cfdi_fecha_cancelacion': False, | ||
882 | 108 | }) | ||
883 | 109 | return super(account_invoice, self).action_cancel_draft(cr, uid, ids, args) | ||
884 | 110 | |||
885 | 111 | def _get_file(self, cr, uid, inv_ids, context={}): | ||
886 | 112 | if not context: | ||
887 | 113 | context = {} | ||
888 | 114 | id = inv_ids[0] | ||
889 | 115 | invoice = self.browse(cr, uid, [id], context=context)[0] | ||
890 | 116 | fname_invoice = invoice.fname_invoice and invoice.fname_invoice + \ | ||
891 | 117 | '.xml' or '' | ||
892 | 118 | aids = self.pool.get('ir.attachment').search(cr, uid, [( | ||
893 | 119 | 'datas_fname', '=', invoice.fname_invoice + '.xml'), ( | ||
894 | 120 | 'res_model', '=', 'account.invoice'), ('res_id', '=', id)]) | ||
895 | 121 | xml_data = "" | ||
896 | 122 | if aids: | ||
897 | 123 | brow_rec = self.pool.get('ir.attachment').browse(cr, uid, aids[0]) | ||
898 | 124 | if brow_rec.datas: | ||
899 | 125 | xml_data = base64.decodestring(brow_rec.datas) | ||
900 | 126 | else: | ||
901 | 127 | fname, xml_data = self._get_facturae_invoice_xml_data( | ||
902 | 128 | cr, uid, inv_ids, context=context) | ||
903 | 129 | self.pool.get('ir.attachment').create(cr, uid, { | ||
904 | 130 | 'name': fname_invoice, | ||
905 | 131 | 'datas': base64.encodestring(xml_data), | ||
906 | 132 | 'datas_fname': fname_invoice, | ||
907 | 133 | 'res_model': 'account.invoice', | ||
908 | 134 | 'res_id': invoice.id, | ||
909 | 135 | }, context=context) | ||
910 | 136 | self.fdata = base64.encodestring(xml_data) | ||
911 | 137 | msg = _("Press in the button 'Upload File'") | ||
912 | 138 | return {'file': self.fdata, 'fname': fname_invoice, | ||
913 | 139 | 'name': fname_invoice, 'msg': msg} | ||
914 | 140 | |||
915 | 141 | def add_node(self, node_name=None, attrs=None, parent_node=None, | ||
916 | 142 | minidom_xml_obj=None, attrs_types=None, order=False): | ||
917 | 143 | """ | ||
918 | 144 | @params node_name : Name node to added | ||
919 | 145 | @params attrs : Attributes to add in node | ||
920 | 146 | @params parent_node : Node parent where was add new node children | ||
921 | 147 | @params minidom_xml_obj : File XML where add nodes | ||
922 | 148 | @params attrs_types : Type of attributes added in the node | ||
923 | 149 | @params order : If need add the params in order in the XML, add a | ||
924 | 150 | list with order to params | ||
925 | 151 | """ | ||
926 | 152 | if not order: | ||
927 | 153 | order = attrs | ||
928 | 154 | new_node = minidom_xml_obj.createElement(node_name) | ||
929 | 155 | for key in order: | ||
930 | 156 | if attrs_types[key] == 'attribute': | ||
931 | 157 | new_node.setAttribute(key, attrs[key]) | ||
932 | 158 | elif attrs_types[key] == 'textNode': | ||
933 | 159 | key_node = minidom_xml_obj.createElement(key) | ||
934 | 160 | text_node = minidom_xml_obj.createTextNode(attrs[key]) | ||
935 | 161 | |||
936 | 162 | key_node.appendChild(text_node) | ||
937 | 163 | new_node.appendChild(key_node) | ||
938 | 164 | parent_node.appendChild(new_node) | ||
939 | 165 | return new_node | ||
940 | 166 | |||
941 | 167 | def _get_type_sequence(self, cr, uid, ids, context=None): | ||
942 | 168 | ir_seq_app_obj = self.pool.get('ir.sequence.approval') | ||
943 | 169 | invoice = self.browse(cr, uid, ids[0], context=context) | ||
944 | 170 | sequence_app_id = ir_seq_app_obj.search(cr, uid, [( | ||
945 | 171 | 'sequence_id', '=', invoice.invoice_sequence_id.id)], context=context) | ||
946 | 172 | type_inv = 'cfd22' | ||
947 | 173 | if sequence_app_id: | ||
948 | 174 | type_inv = ir_seq_app_obj.browse( | ||
949 | 175 | cr, uid, sequence_app_id[0], context=context).type | ||
950 | 176 | if type_inv == 'cfdi32': | ||
951 | 177 | comprobante = 'cfdi:Comprobante' | ||
952 | 178 | else: | ||
953 | 179 | comprobante = 'Comprobante' | ||
954 | 180 | return comprobante | ||
955 | 181 | |||
956 | 182 | def _get_time_zone(self, cr, uid, invoice_id, context=None): | ||
957 | 183 | res_users_obj = self.pool.get('res.users') | ||
958 | 184 | userstz = res_users_obj.browse(cr, uid, [uid])[0].partner_id.tz | ||
959 | 185 | a = 0 | ||
960 | 186 | if userstz: | ||
961 | 187 | hours = timezone(userstz) | ||
962 | 188 | fmt = '%Y-%m-%d %H:%M:%S %Z%z' | ||
963 | 189 | now = datetime.now() | ||
964 | 190 | loc_dt = hours.localize(datetime(now.year, now.month, now.day, | ||
965 | 191 | now.hour, now.minute, now.second)) | ||
966 | 192 | timezone_loc = (loc_dt.strftime(fmt)) | ||
967 | 193 | diff_timezone_original = timezone_loc[-5:-2] | ||
968 | 194 | timezone_original = int(diff_timezone_original) | ||
969 | 195 | s = str(datetime.now(pytz.timezone(userstz))) | ||
970 | 196 | s = s[-6:-3] | ||
971 | 197 | timezone_present = int(s) * -1 | ||
972 | 198 | a = timezone_original + (( | ||
973 | 199 | timezone_present + timezone_original) * -1) | ||
974 | 200 | return a | ||
975 | 201 | |||
976 | 202 | def _upload_ws_file(self, cr, uid, invoice_id, fdata=None, context={}): | ||
977 | 203 | """ | ||
978 | 204 | @params | ||
979 | 205 | invoice_id: Id from invoice to validate | ||
980 | 206 | fdata : File.xml base64 codificated | ||
981 | 207 | """ | ||
982 | 208 | pac_params_obj = self.pool.get('params.pac') | ||
983 | 209 | invoice = self.browse(cr, uid, invoice_id, context=context)[0] | ||
984 | 210 | f = False | ||
985 | 211 | msg = '' | ||
986 | 212 | pac_ids = pac_params_obj.search(cr, uid, | ||
987 | 213 | [('method_type', '=', 'pac_facturalo_firmar'), | ||
988 | 214 | ('company_id', '=', invoice.company_emitter_id.id), | ||
989 | 215 | ('active', '=', True) | ||
990 | 216 | ], limit=1, context=context) | ||
991 | 217 | |||
992 | 218 | if not pac_ids: | ||
993 | 219 | raise osv.except_osv(_('Warning'), | ||
994 | 220 | _('Not found information from web services of PAC' | ||
995 | 221 | 'Verify that the configuration of PAC is correct') | ||
996 | 222 | ) | ||
997 | 223 | |||
998 | 224 | # Get pac params for use on this invoice | ||
999 | 225 | pac = pac_params_obj.browse(cr, uid, pac_ids, context)[0] | ||
1000 | 226 | |||
1001 | 227 | if pac.user == 'pruebas': | ||
1002 | 228 | msg += _(u'WARNING, SIGNED IN TEST!!!!\n\n') | ||
1003 | 229 | |||
1004 | 230 | try: | ||
1005 | 231 | wsdl = WSDL.SOAPProxy(pac.url_webservice, pac.namespace) | ||
1006 | 232 | |||
1007 | 233 | # Webservice configuration | ||
1008 | 234 | wsdl.soapproxy.config.dumpSOAPOut = 0 | ||
1009 | 235 | wsdl.soapproxy.config.dumpSOAPIn = 0 | ||
1010 | 236 | wsdl.soapproxy.config.debug = 0 | ||
1011 | 237 | wsdl.soapproxy.config.dict_encoding = 'UTF-8' | ||
1012 | 238 | |||
1013 | 239 | # Timbrar factura | ||
1014 | 240 | resultado = wsdl.timbrar(usuario=pac.user, | ||
1015 | 241 | contrasena=pac.password, | ||
1016 | 242 | cfdi=fdata) | ||
1017 | 243 | except Exception, e: | ||
1018 | 244 | # Error al establecer comunicación con el PAC | ||
1019 | 245 | logger.error(str(e)) | ||
1020 | 246 | raise osv.except_osv(_('Error'), | ||
1021 | 247 | _(u"Can't get XML file from PAC.")) | ||
1022 | 248 | |||
1023 | 249 | # Procesar los resultados obtenidos del PAC | ||
1024 | 250 | mensaje = _(tools.ustr(resultado['mensaje'])) | ||
1025 | 251 | logger.debug(str(resultado)) | ||
1026 | 252 | valid_codes = ['200', '504'] | ||
1027 | 253 | if resultado['codigo'] in valid_codes: | ||
1028 | 254 | # Proceso satisfactorio | ||
1029 | 255 | timbre = resultado['timbre'] | ||
1030 | 256 | regex = '<cfdi:Complemento>(.*?)</cfdi:Complemento>' | ||
1031 | 257 | timbref = re.search(regex, resultado['timbre']).group(1) | ||
1032 | 258 | |||
1033 | 259 | # Obtiene el sello del SAT | ||
1034 | 260 | try: | ||
1035 | 261 | selloSAT = re.search('selloSAT="(.+?)" ', timbref).group(1) | ||
1036 | 262 | except: | ||
1037 | 263 | selloSAT = '' | ||
1038 | 264 | |||
1039 | 265 | # Obtiene el nuúmero de certificado | ||
1040 | 266 | try: | ||
1041 | 267 | no_certificado = re.search('noCertificadoSAT="(.+?)" ', timbref).group(1) | ||
1042 | 268 | except: | ||
1043 | 269 | no_certificado = '' | ||
1044 | 270 | |||
1045 | 271 | # Obtiene la fecha en que se timbó la factura | ||
1046 | 272 | try: | ||
1047 | 273 | fecha = re.search('FechaTimbrado="(.+?)" ', timbref).group(1) | ||
1048 | 274 | except: | ||
1049 | 275 | fecha = '' | ||
1050 | 276 | |||
1051 | 277 | # Obtiene el folio de la factura | ||
1052 | 278 | try: | ||
1053 | 279 | uuid = re.search('UUID="(.+?)" ', timbref).group(1) | ||
1054 | 280 | except: | ||
1055 | 281 | uuid = '' | ||
1056 | 282 | |||
1057 | 283 | # Obtiene la cadena original | ||
1058 | 284 | try: | ||
1059 | 285 | version = re.search('version="(.+?)" ', timbref).group(1) | ||
1060 | 286 | cadena = ('|').join([version, | ||
1061 | 287 | uuid, | ||
1062 | 288 | invoice.date_invoice_tz, | ||
1063 | 289 | invoice.sello, | ||
1064 | 290 | no_certificado]) | ||
1065 | 291 | cadena = "||{0}||".format(cadena) | ||
1066 | 292 | except: | ||
1067 | 293 | cadena = '' | ||
1068 | 294 | |||
1069 | 295 | cfdi_data = { | ||
1070 | 296 | 'cfdi_sello': selloSAT, | ||
1071 | 297 | 'cfdi_no_certificado': no_certificado, | ||
1072 | 298 | 'cfdi_cadena_original': cadena, | ||
1073 | 299 | 'cfdi_fecha_timbrado': fecha, | ||
1074 | 300 | 'cfdi_folio_fiscal': uuid, | ||
1075 | 301 | } | ||
1076 | 302 | self.write(cr, uid, invoice_id, cfdi_data) | ||
1077 | 303 | |||
1078 | 304 | # Codifica el XML para almacenarlo | ||
1079 | 305 | f = base64.encodestring(timbre or '') | ||
1080 | 306 | |||
1081 | 307 | msg += string.join([mensaje, ". Folio Fiscal ", uuid, "."]) | ||
1082 | 308 | else: | ||
1083 | 309 | # El CFDI no ha sido timbrado | ||
1084 | 310 | raise osv.except_osv(_(u'Warning'), | ||
1085 | 311 | _(u'Stamped Code: %s. \n Stamped Message: %s.') % | ||
1086 | 312 | (resultado['codigo'], mensaje)) | ||
1087 | 313 | |||
1088 | 314 | return {'file': f, 'msg': msg, 'cfdi_xml': timbre} | ||
1089 | 315 | |||
1090 | 316 | def _get_file_cancel(self, cr, uid, inv_ids, context={}): | ||
1091 | 317 | inv_ids = inv_ids[0] | ||
1092 | 318 | atta_obj = self.pool.get('ir.attachment') | ||
1093 | 319 | atta_id = atta_obj.search(cr, uid, [('res_id', '=', inv_ids), ( | ||
1094 | 320 | 'name', 'ilike', '%.xml')], context=context) | ||
1095 | 321 | if atta_id: | ||
1096 | 322 | atta_brw = atta_obj.browse(cr, uid, atta_id, context)[0] | ||
1097 | 323 | inv_xml = atta_brw.datas or False | ||
1098 | 324 | else: | ||
1099 | 325 | inv_xml = False | ||
1100 | 326 | raise osv.except_osv(('State of Cancellation!'), ( | ||
1101 | 327 | "This invoice hasn't stamped, so that not possible cancel.")) | ||
1102 | 328 | return {'file': inv_xml} | ||
1103 | 329 | |||
1104 | 330 | def sf_cancel(self, cr, uid, inv_ids, context=None): | ||
1105 | 331 | msg = '' | ||
1106 | 332 | invoice_id = inv_ids[0] | ||
1107 | 333 | pac_params_obj = self.pool.get('params.pac') | ||
1108 | 334 | invoice = self.browse(cr, uid, invoice_id, context=context) | ||
1109 | 335 | xml_data = self.cancel_invoice_xml(cr, uid, invoice_id, context=context) | ||
1110 | 336 | |||
1111 | 337 | # ****************** upload file encoded at PAC ****************************************** | ||
1112 | 338 | |||
1113 | 339 | pac_ids = pac_params_obj.search(cr, uid, | ||
1114 | 340 | [('method_type', '=', 'pac_facturalo_cancelar'), | ||
1115 | 341 | ('company_id', '=', invoice.company_emitter_id.id), | ||
1116 | 342 | ('active', '=', True) | ||
1117 | 343 | ], limit=1, context=context) | ||
1118 | 344 | |||
1119 | 345 | if not pac_ids: | ||
1120 | 346 | raise osv.except_osv(_('Warning'), | ||
1121 | 347 | _('Not found information from web services of PAC, verify that the configuration of PAC is correct')) | ||
1122 | 348 | |||
1123 | 349 | pac = pac_params_obj.browse(cr, uid, pac_ids, context)[0] | ||
1124 | 350 | |||
1125 | 351 | ##################################################################### | ||
1126 | 352 | |||
1127 | 353 | if pac.user == 'pruebas': | ||
1128 | 354 | msg += _(u'WARNING, CANCEL IN TEST!!!!\n\n') | ||
1129 | 355 | |||
1130 | 356 | try: | ||
1131 | 357 | from SOAPpy import Types | ||
1132 | 358 | |||
1133 | 359 | wsdl = WSDL.SOAPProxy(pac.url_webservice, pac.namespace) | ||
1134 | 360 | |||
1135 | 361 | # Webservice configuration | ||
1136 | 362 | wsdl.soapproxy.config.dumpSOAPOut = 0 | ||
1137 | 363 | wsdl.soapproxy.config.dumpSOAPIn = 0 | ||
1138 | 364 | wsdl.soapproxy.config.debug = 0 | ||
1139 | 365 | wsdl.config.preventescape = 1 | ||
1140 | 366 | wsdl.soapproxy.config.dict_encoding = 'UTF-8' | ||
1141 | 367 | |||
1142 | 368 | # SUBIR factura al PAC | ||
1143 | 369 | resultado = wsdl.cancelar(usuario=pac.user, | ||
1144 | 370 | # For test environment uncomment this and comment contrasena | ||
1145 | 371 | token=pac.password, | ||
1146 | 372 | # contrasena=pac.password, | ||
1147 | 373 | xmlBytes=Types.base64BinaryType(xml_data)) | ||
1148 | 374 | |||
1149 | 375 | except Exception, e: | ||
1150 | 376 | # Error al establecer comunicación con el PAC | ||
1151 | 377 | logger.debug(str(e)) | ||
1152 | 378 | raise osv.except_osv(_('Error'), | ||
1153 | 379 | _(u"Can't get XML file from PAC.")) | ||
1154 | 380 | |||
1155 | 381 | ####################################################################### | ||
1156 | 382 | # Procesar los resultados obtenidos del PAC | ||
1157 | 383 | |||
1158 | 384 | if not(resultado['codEstatus'] == '200'): | ||
1159 | 385 | mensaje = _(resultado['codMensaje']) | ||
1160 | 386 | # error | ||
1161 | 387 | raise osv.except_osv(_(u'Error'), | ||
1162 | 388 | _(u'Stamped Code: %s. \n Stamped Message: %s.') % | ||
1163 | 389 | (resultado['codEstatus'], mensaje)) | ||
1164 | 390 | |||
1165 | 391 | cancel_status = ['201', '202'] | ||
1166 | 392 | if not(resultado['estatusUUIDs'] in cancel_status): | ||
1167 | 393 | errors = { | ||
1168 | 394 | '203':_('UUID No corresponde al emisor'), | ||
1169 | 395 | '204':_('UUID No aplicable para cancelación'), | ||
1170 | 396 | '205':_('UUID No existe') | ||
1171 | 397 | } | ||
1172 | 398 | # error | ||
1173 | 399 | raise osv.except_osv(_(u'Error'), | ||
1174 | 400 | _(u'Stamped Code: %s. \n Stamped Message: %s.') % | ||
1175 | 401 | (resultado['estatusUUIDs'], | ||
1176 | 402 | errors[resultado['estatusUUIDs']])) | ||
1177 | 403 | |||
1178 | 404 | # Invoice canceled, save the cancelation date on database | ||
1179 | 405 | self.write(cr, uid, [invoice_id], {'cfdi_fecha_cancelacion':time.strftime('%Y-%m-%d %H:%M:%S')}, context=None) | ||
1180 | 406 | |||
1181 | 407 | return {'message':msg, 'status':resultado['codEstatus']} | ||
1182 | 408 | |||
1183 | 409 | def cancel_invoice_dict(self, cr, uid, ids, context={}): | ||
1184 | 410 | """ This function create a dictionary with the necesary data to create the DigestValue. | ||
1185 | 411 | @params | ||
1186 | 412 | default params | ||
1187 | 413 | """ | ||
1188 | 414 | from lxml import etree | ||
1189 | 415 | from openerp import SUPERUSER_ID | ||
1190 | 416 | |||
1191 | 417 | # get user's timezone | ||
1192 | 418 | user_pool = self.pool.get('res.users') | ||
1193 | 419 | user = user_pool.browse(cr, SUPERUSER_ID, uid) | ||
1194 | 420 | tz = timezone(user.partner_id.tz) or pytz.utc | ||
1195 | 421 | |||
1196 | 422 | # get localized dates | ||
1197 | 423 | date = datetime.now(pytz.utc).astimezone(tz) | ||
1198 | 424 | |||
1199 | 425 | |||
1200 | 426 | invoice = self.browse(cr, uid, ids, context=context) | ||
1201 | 427 | xmlns = "http://cancelacfd.sat.gob.mx" | ||
1202 | 428 | xsd = 'http://www.w3.org/2001/XMLSchema' | ||
1203 | 429 | xsi = 'http://www.w3.org/2001/XMLSchema-instance' | ||
1204 | 430 | signed_xmlns = 'http://www.w3.org/2000/09/xmldsig#' | ||
1205 | 431 | |||
1206 | 432 | nsmap = {None: xmlns, | ||
1207 | 433 | 'xsd':xsd, | ||
1208 | 434 | 'xsi':xsi} | ||
1209 | 435 | |||
1210 | 436 | signed_nsmap = {None: signed_xmlns, | ||
1211 | 437 | 'xsd':xsd, | ||
1212 | 438 | 'xsi':xsi} | ||
1213 | 439 | # section: Cancelation | ||
1214 | 440 | # create XML | ||
1215 | 441 | cancelacion = etree.Element('Cancelacion', nsmap=nsmap) | ||
1216 | 442 | cancelacion.set('Fecha', date.strftime('%Y-%m-%dT%H:%M:%S')) | ||
1217 | 443 | cancelacion.set('RfcEmisor', invoice.company_id.partner_id.vat_split) | ||
1218 | 444 | |||
1219 | 445 | folios = etree.Element('Folios') | ||
1220 | 446 | uuid = etree.Element('UUID') | ||
1221 | 447 | uuid.text = invoice.cfdi_folio_fiscal | ||
1222 | 448 | folios.append(uuid) | ||
1223 | 449 | cancelacion.append(folios) | ||
1224 | 450 | |||
1225 | 451 | signature = etree.Element('Signature', nsmap={None:signed_xmlns}) | ||
1226 | 452 | signedinfo = etree.Element('SignedInfo', nsmap=signed_nsmap) | ||
1227 | 453 | |||
1228 | 454 | canonicalizacion = etree.Element('CanonicalizationMethod') | ||
1229 | 455 | canonicalizacion.set('Algorithm', "http://www.w3.org/TR/2001/REC-xml-c14n-20010315") | ||
1230 | 456 | signaturemethod = etree.Element('SignatureMethod') | ||
1231 | 457 | signaturemethod.set('Algorithm', "http://www.w3.org/2000/09/xmldsig#rsa-sha1") | ||
1232 | 458 | reference = etree.Element('Reference') | ||
1233 | 459 | reference.set('URI', "") | ||
1234 | 460 | transforms = etree.Element('Transforms') | ||
1235 | 461 | transform = etree.Element('Transform') | ||
1236 | 462 | transform.set('Algorithm', "http://www.w3.org/2000/09/xmldsig#enveloped-signature") | ||
1237 | 463 | digestme = etree.Element('DigestMethod') | ||
1238 | 464 | digestme.set('Algorithm', "http://www.w3.org/2000/09/xmldsig#sha1") | ||
1239 | 465 | digestva = etree.Element('DigestValue') | ||
1240 | 466 | digestva.text = 'TEMPLATE' | ||
1241 | 467 | |||
1242 | 468 | signedinfo.append(canonicalizacion) | ||
1243 | 469 | signedinfo.append(signaturemethod) | ||
1244 | 470 | signedinfo.append(reference) | ||
1245 | 471 | |||
1246 | 472 | transforms.append(transform) | ||
1247 | 473 | reference.append(transforms) | ||
1248 | 474 | reference.append(digestme) | ||
1249 | 475 | reference.append(digestva) | ||
1250 | 476 | signature.append(signedinfo) | ||
1251 | 477 | |||
1252 | 478 | signaturevalue = etree.Element('SignatureValue') | ||
1253 | 479 | signaturevalue.text = 'TEMPLATE' | ||
1254 | 480 | signature.append(signaturevalue) | ||
1255 | 481 | |||
1256 | 482 | keyinfo = etree.Element('KeyInfo') | ||
1257 | 483 | # Issuername & Serialnumber not added because library | ||
1258 | 484 | # auto fill the fields on X509IssuerSerial | ||
1259 | 485 | # see www.aleksey.com/pipermail/xmlsec/2007/008125.html | ||
1260 | 486 | x509 = etree.Element('X509Data') | ||
1261 | 487 | xISerial = etree.Element('X509IssuerSerial') | ||
1262 | 488 | xISerial.text = '' | ||
1263 | 489 | xCertificate = etree.Element('X509Certificate') | ||
1264 | 490 | xCertificate.text = '' | ||
1265 | 491 | |||
1266 | 492 | x509.append(xISerial) | ||
1267 | 493 | x509.append(xCertificate) | ||
1268 | 494 | keyinfo.append(x509) | ||
1269 | 495 | signature.append(keyinfo) | ||
1270 | 496 | |||
1271 | 497 | cancelacion.append(signature) | ||
1272 | 498 | # pretty string | ||
1273 | 499 | logger.debug('XML file %s' % etree.tostring(cancelacion)) | ||
1274 | 500 | return etree.tostring(cancelacion) | ||
1275 | 501 | |||
1276 | 502 | def cancel_invoice_xml(self, cr, uid, ids, context=None): | ||
1277 | 503 | """ This function call at cancel_invoice_dict to cancel a invoice. | ||
1278 | 504 | @params | ||
1279 | 505 | default params | ||
1280 | 506 | """ | ||
1281 | 507 | import pyxmldsig | ||
1282 | 508 | import tempfile | ||
1283 | 509 | |||
1284 | 510 | invoice = self.browse(cr, uid, ids, context=context) | ||
1285 | 511 | pool_company = self.pool.get('res.company') | ||
1286 | 512 | |||
1287 | 513 | data_xml = self.cancel_invoice_dict(cr, uid, ids, context=context) | ||
1288 | 514 | |||
1289 | 515 | new_file = tempfile.NamedTemporaryFile(delete=False) | ||
1290 | 516 | new_file.write(data_xml) | ||
1291 | 517 | new_file.close() | ||
1292 | 518 | |||
1293 | 519 | cert_id = pool_company._get_current_certificate(cr, uid, [invoice.company_emitter_id.id], | ||
1294 | 520 | context=context)[invoice.company_emitter_id.id] | ||
1295 | 521 | |||
1296 | 522 | cert_id = cert_id and self.pool.get('res.company.facturae.certificate') \ | ||
1297 | 523 | .browse(cr, uid, [cert_id], context=context)[0] or False | ||
1298 | 524 | |||
1299 | 525 | # Get certificate files for sign the xml | ||
1300 | 526 | fname_cer_pem = fname_key_pem = False | ||
1301 | 527 | name = 'openerp_' + (cert_id.serial_number or '') + '__certificate__' | ||
1302 | 528 | if cert_id: | ||
1303 | 529 | try: | ||
1304 | 530 | |||
1305 | 531 | fname_cer_pem = self.binary2file(cr, uid, ids, | ||
1306 | 532 | cert_id.certificate_file_pem, | ||
1307 | 533 | name, | ||
1308 | 534 | '.cer.pem') | ||
1309 | 535 | except: | ||
1310 | 536 | raise osv.except_osv(_('Error !'), | ||
1311 | 537 | _('Not captured a CERTIFICATE file in format PEM, in the company!')) | ||
1312 | 538 | |||
1313 | 539 | |||
1314 | 540 | try: | ||
1315 | 541 | fname_key_pem = self.binary2file(cr, uid, ids, | ||
1316 | 542 | cert_id.certificate_key_file_pem, | ||
1317 | 543 | name, | ||
1318 | 544 | '.key.pem') | ||
1319 | 545 | except: | ||
1320 | 546 | raise osv.except_osv(_('Error !'), _( | ||
1321 | 547 | 'Not captured a KEY file in format PEM, in the company!')) | ||
1322 | 548 | |||
1323 | 549 | else: | ||
1324 | 550 | raise osv.except_osv(_('Warning !'), | ||
1325 | 551 | _('Check date of invoice and the validity of certificate')) | ||
1326 | 552 | |||
1327 | 553 | signed_xml = pyxmldsig.sign_file(template_file=new_file.name, | ||
1328 | 554 | cert_file=fname_cer_pem.encode('ascii', 'ignore'), | ||
1329 | 555 | key_file=fname_key_pem.encode('ascii', 'ignore'), | ||
1330 | 556 | password=cert_id.certificate_password.encode('ascii', 'ignore')) | ||
1331 | 557 | |||
1332 | 558 | logger.debug('Signed file %s' % signed_xml) | ||
1333 | 559 | |||
1334 | 560 | return signed_xml # data_dict | ||
1335 | 0 | 561 | ||
1336 | === added file 'l10n_mx_facturae_pac_facturalo/invoice.pyc' | |||
1337 | 1 | Binary files l10n_mx_facturae_pac_facturalo/invoice.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/invoice.pyc 2014-06-25 00:35:08 +0000 differ | 562 | Binary files l10n_mx_facturae_pac_facturalo/invoice.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/invoice.pyc 2014-06-25 00:35:08 +0000 differ |
1338 | === added file 'l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py' | |||
1339 | --- l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py 1970-01-01 00:00:00 +0000 | |||
1340 | +++ l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py 2014-06-25 00:35:08 +0000 | |||
1341 | @@ -0,0 +1,40 @@ | |||
1342 | 1 | # -*- encoding: utf-8 -*- | ||
1343 | 2 | ########################################################################### | ||
1344 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
1345 | 4 | # | ||
1346 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
1347 | 6 | # All Rights Reserved. | ||
1348 | 7 | # info OpenPyme (info@openpyme.mx) | ||
1349 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
1350 | 9 | # | ||
1351 | 10 | # This program is free software: you can redistribute it and/or modify | ||
1352 | 11 | # it under the terms of the GNU Affero General Public License as | ||
1353 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
1354 | 13 | # License, or (at your option) any later version. | ||
1355 | 14 | # | ||
1356 | 15 | # This program is distributed in the hope that it will be useful, | ||
1357 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1358 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1359 | 18 | # GNU Affero General Public License for more details. | ||
1360 | 19 | # | ||
1361 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
1362 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1363 | 22 | # | ||
1364 | 23 | ############################################################################## | ||
1365 | 24 | |||
1366 | 25 | from openerp.osv import fields, osv | ||
1367 | 26 | |||
1368 | 27 | |||
1369 | 28 | class ir_attachment_facturae_mx(osv.osv): | ||
1370 | 29 | _inherit = 'ir.attachment.facturae.mx' | ||
1371 | 30 | |||
1372 | 31 | def _get_type(self, cr, uid, ids=None, context=None): | ||
1373 | 32 | types = super(ir_attachment_facturae_mx, self)._get_type( | ||
1374 | 33 | cr, uid, ids, context=context) | ||
1375 | 34 | types.extend([('cfdi32', 'CFDI 3.2')]) | ||
1376 | 35 | return types | ||
1377 | 36 | |||
1378 | 37 | _columns = { | ||
1379 | 38 | 'type': fields.selection(_get_type, 'Type', type='char', size=64, | ||
1380 | 39 | required=True, readonly=True, help="Type of Electronic Invoice"), | ||
1381 | 40 | } | ||
1382 | 0 | 41 | ||
1383 | === added file 'l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.pyc' | |||
1384 | 1 | Binary files l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.pyc 2014-06-25 00:35:08 +0000 differ | 42 | Binary files l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.pyc 2014-06-25 00:35:08 +0000 differ |
1385 | === added file 'l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py' | |||
1386 | --- l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py 1970-01-01 00:00:00 +0000 | |||
1387 | +++ l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py 2014-06-25 00:35:08 +0000 | |||
1388 | @@ -0,0 +1,43 @@ | |||
1389 | 1 | # -*- encoding: utf-8 -*- | ||
1390 | 2 | ########################################################################### | ||
1391 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
1392 | 4 | # | ||
1393 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
1394 | 6 | # All Rights Reserved. | ||
1395 | 7 | # info OpenPyme (info@openpyme.mx) | ||
1396 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
1397 | 9 | # | ||
1398 | 10 | # This program is free software: you can redistribute it and/or modify | ||
1399 | 11 | # it under the terms of the GNU Affero General Public License as | ||
1400 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
1401 | 13 | # License, or (at your option) any later version. | ||
1402 | 14 | # | ||
1403 | 15 | # This program is distributed in the hope that it will be useful, | ||
1404 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1405 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1406 | 18 | # GNU Affero General Public License for more details. | ||
1407 | 19 | # | ||
1408 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
1409 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1410 | 22 | # | ||
1411 | 23 | ############################################################################## | ||
1412 | 24 | |||
1413 | 25 | from openerp.osv import fields, osv | ||
1414 | 26 | |||
1415 | 27 | |||
1416 | 28 | class ir_sequence_approval(osv.osv): | ||
1417 | 29 | _inherit = 'ir.sequence.approval' | ||
1418 | 30 | |||
1419 | 31 | def _get_type(self, cr, uid, ids=None, context=None): | ||
1420 | 32 | types = super(ir_sequence_approval, self)._get_type( | ||
1421 | 33 | cr, uid, ids, context=context) | ||
1422 | 34 | """TODO: Encontrar una forma en la que se pueda tener | ||
1423 | 35 | instalado al mismo tiempo este módulo y el de SF | ||
1424 | 36 | """ | ||
1425 | 37 | types.extend([('cfdi32', 'CFDI 3.2')]) | ||
1426 | 38 | return types | ||
1427 | 39 | |||
1428 | 40 | _columns = { | ||
1429 | 41 | 'type': fields.selection(_get_type, 'Type', type='char', size=64, | ||
1430 | 42 | required=True, help="Type of Electronic Invoice"), | ||
1431 | 43 | } | ||
1432 | 0 | 44 | ||
1433 | === added file 'l10n_mx_facturae_pac_facturalo/ir_sequence_approval.pyc' | |||
1434 | 1 | Binary files l10n_mx_facturae_pac_facturalo/ir_sequence_approval.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/ir_sequence_approval.pyc 2014-06-25 00:35:08 +0000 differ | 45 | Binary files l10n_mx_facturae_pac_facturalo/ir_sequence_approval.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/ir_sequence_approval.pyc 2014-06-25 00:35:08 +0000 differ |
1435 | === added file 'l10n_mx_facturae_pac_facturalo/params_pac.py' | |||
1436 | --- l10n_mx_facturae_pac_facturalo/params_pac.py 1970-01-01 00:00:00 +0000 | |||
1437 | +++ l10n_mx_facturae_pac_facturalo/params_pac.py 2014-06-25 00:35:08 +0000 | |||
1438 | @@ -0,0 +1,45 @@ | |||
1439 | 1 | # -*- encoding: utf-8 -*- | ||
1440 | 2 | ########################################################################### | ||
1441 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
1442 | 4 | # | ||
1443 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
1444 | 6 | # All Rights Reserved. | ||
1445 | 7 | # info OpenPyme (info@openpyme.mx) | ||
1446 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
1447 | 9 | # | ||
1448 | 10 | # This program is free software: you can redistribute it and/or modify | ||
1449 | 11 | # it under the terms of the GNU Affero General Public License as | ||
1450 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
1451 | 13 | # License, or (at your option) any later version. | ||
1452 | 14 | # | ||
1453 | 15 | # This program is distributed in the hope that it will be useful, | ||
1454 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
1455 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
1456 | 18 | # GNU Affero General Public License for more details. | ||
1457 | 19 | # | ||
1458 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
1459 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
1460 | 22 | # | ||
1461 | 23 | ############################################################################## | ||
1462 | 24 | |||
1463 | 25 | from openerp.osv import fields, osv | ||
1464 | 26 | from openerp.tools.translate import _ | ||
1465 | 27 | |||
1466 | 28 | |||
1467 | 29 | class params_pac(osv.osv): | ||
1468 | 30 | _inherit = 'params.pac' | ||
1469 | 31 | |||
1470 | 32 | def _get_method_type_selection(self, cr, uid, context=None): | ||
1471 | 33 | types = super(params_pac, self)._get_method_type_selection( | ||
1472 | 34 | cr, uid, context=context) | ||
1473 | 35 | types.extend([ | ||
1474 | 36 | ('pac_facturalo_firmar', _('PAC Factura-lo - Sign')), | ||
1475 | 37 | ('pac_facturalo_cancelar', _('PAC Factura-lo - Cancel')), | ||
1476 | 38 | ]) | ||
1477 | 39 | return types | ||
1478 | 40 | |||
1479 | 41 | _columns = { | ||
1480 | 42 | 'method_type': fields.selection(_get_method_type_selection, | ||
1481 | 43 | "Process to perform", type='char', size=64, required=True, | ||
1482 | 44 | help='Type of process to configure in this pac'), | ||
1483 | 45 | } | ||
1484 | 0 | 46 | ||
1485 | === added file 'l10n_mx_facturae_pac_facturalo/params_pac.pyc' | |||
1486 | 1 | Binary files l10n_mx_facturae_pac_facturalo/params_pac.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/params_pac.pyc 2014-06-25 00:35:08 +0000 differ | 47 | Binary files l10n_mx_facturae_pac_facturalo/params_pac.pyc 1970-01-01 00:00:00 +0000 and l10n_mx_facturae_pac_facturalo/params_pac.pyc 2014-06-25 00:35:08 +0000 differ |
1487 | === added file 'l10n_mx_facturae_pac_facturalo/pyxmldsig.py' | |||
1488 | --- l10n_mx_facturae_pac_facturalo/pyxmldsig.py 1970-01-01 00:00:00 +0000 | |||
1489 | +++ l10n_mx_facturae_pac_facturalo/pyxmldsig.py 2014-06-25 00:35:08 +0000 | |||
1490 | @@ -0,0 +1,578 @@ | |||
1491 | 1 | """ | ||
1492 | 2 | pyxmldsig.py: | ||
1493 | 3 | |||
1494 | 4 | A Python module to create and verify XML Digital Signatures (XML-DSig) | ||
1495 | 5 | |||
1496 | 6 | This is a simple interface to the PyXMLSec library, aiming to provide a more | ||
1497 | 7 | pythonic API suitable for Python applications. | ||
1498 | 8 | See http://www.decalage.info/python/pyxmldsig to download the latest version. | ||
1499 | 9 | |||
1500 | 10 | May be used as a command-line tool or as a Python module. | ||
1501 | 11 | This code is inspired from PyXMLSec samples, with a more pythonic interface: | ||
1502 | 12 | http://pyxmlsec.labs.libre-entreprise.org/index.php?section=examples | ||
1503 | 13 | |||
1504 | 14 | AUTHOR: Philippe Lagadec (decalage at laposte dot net) | ||
1505 | 15 | |||
1506 | 16 | PROJECT WEBSITE: http://www.decalage.info/python/pyxmldsig | ||
1507 | 17 | |||
1508 | 18 | LICENSE: | ||
1509 | 19 | |||
1510 | 20 | Copyright (c) 2009-2010, Philippe Lagadec (decalage at laposte dot net) | ||
1511 | 21 | |||
1512 | 22 | Permission to use, copy, modify, and/or distribute this software for any | ||
1513 | 23 | purpose with or without fee is hereby granted, provided that the above copyright | ||
1514 | 24 | notice and this permission notice appear in all copies. | ||
1515 | 25 | |||
1516 | 26 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH | ||
1517 | 27 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND | ||
1518 | 28 | FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, | ||
1519 | 29 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM | ||
1520 | 30 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR | ||
1521 | 31 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR | ||
1522 | 32 | PERFORMANCE OF THIS SOFTWARE. | ||
1523 | 33 | |||
1524 | 34 | |||
1525 | 35 | USAGE AS A TOOL: | ||
1526 | 36 | pyxmldsig.py <data.xml> -k <key-file.pem> [-c cert-file.pem] [-p password] | ||
1527 | 37 | |||
1528 | 38 | USAGE IN A PYTHON APPLICATION: | ||
1529 | 39 | |||
1530 | 40 | import pyxmldsig | ||
1531 | 41 | |||
1532 | 42 | # simple function interface: | ||
1533 | 43 | signed_xml = pyxmldsig.sign_file(template_file='myfile.xml', | ||
1534 | 44 | key_file='mykey.pem', cert_file='myx509cert.pem', password='mypassword') | ||
1535 | 45 | print signed_xml | ||
1536 | 46 | |||
1537 | 47 | # sign with class interface: | ||
1538 | 48 | xdsig = pyxmldsig.Xmldsig(key_file='mykey.pem', cert_file='myx509cert.pem', | ||
1539 | 49 | password='mypassword') | ||
1540 | 50 | signed_xml1 = xdsig.sign_file('myfile.xml') | ||
1541 | 51 | signed_xml2 = xdsig.sign_file(pyxmldsig.TEMPLATE_WITH_CERT) | ||
1542 | 52 | |||
1543 | 53 | # verify with class interface: | ||
1544 | 54 | xdsig2 = pyxmldsig.Xmldsig() | ||
1545 | 55 | xdsig2.load_certs(['cacert.pem', 'myx509cert.pem']) | ||
1546 | 56 | assert xdsig2.verify_xmlstring(signed_xml1) == True | ||
1547 | 57 | assert xdsig2.verify_xmlstring(signed_xml2) == True | ||
1548 | 58 | |||
1549 | 59 | REQUIREMENTS: | ||
1550 | 60 | - pyxmlsec: http://pyxmlsec.labs.libre-entreprise.org/ | ||
1551 | 61 | - xmlsec: http://www.aleksey.com/xmlsec/ | ||
1552 | 62 | - libxml2: http://xmlsoft.org | ||
1553 | 63 | - On Windows see also this site for convenient compiled binaries: | ||
1554 | 64 | http://returnbooleantrue.blogspot.com/2009/04/pyxmlsec-windows-binary.html | ||
1555 | 65 | |||
1556 | 66 | REFERENCES: | ||
1557 | 67 | - http://www.w3.org/TR/xmldsig-core/ | ||
1558 | 68 | """ | ||
1559 | 69 | |||
1560 | 70 | __version__ = '0.05' | ||
1561 | 71 | |||
1562 | 72 | #=== CHANGELOG ================================================================ | ||
1563 | 73 | |||
1564 | 74 | # 2009-09-10 v0.01 PL: - first version, inspired from pyxmlsec samples | ||
1565 | 75 | # 2009-09-14 v0.02 PL: - small improvements, license | ||
1566 | 76 | # 2010-05-11 v0.03 PL: - renamed to pyxmldsig | ||
1567 | 77 | # - X509 cert is now optional | ||
1568 | 78 | # - a key password may be provided | ||
1569 | 79 | # 2010-06-29 v0.04 PL: - added Xmldsig class to load a key once for several | ||
1570 | 80 | # signatures | ||
1571 | 81 | # - added signature verification | ||
1572 | 82 | # - added simple XML-DSIG templates | ||
1573 | 83 | # 2010-07-06 v0.05 PL: - added load_cert to load several certificates at once | ||
1574 | 84 | |||
1575 | 85 | #=== TODO ===================================================================== | ||
1576 | 86 | |||
1577 | 87 | # - add option to generate XML-DSig template automatically, appended to a chosen | ||
1578 | 88 | # node. | ||
1579 | 89 | # - add option to improve detached signature with http URI: fix URI after | ||
1580 | 90 | # signature. | ||
1581 | 91 | # - check if all temporary xmlsec objects are destroyed after each operation | ||
1582 | 92 | # - find a solution to load a certificate with a key name to allow signature | ||
1583 | 93 | # verification without embedded X509 cert | ||
1584 | 94 | # - add option to use keys manager or single key? | ||
1585 | 95 | |||
1586 | 96 | #=== IMPORTS ================================================================== | ||
1587 | 97 | |||
1588 | 98 | import sys | ||
1589 | 99 | |||
1590 | 100 | try: | ||
1591 | 101 | import libxml2 | ||
1592 | 102 | except ImportError: | ||
1593 | 103 | raise ImportError, "libxml2 is required: see http://xmlsoft.org" | ||
1594 | 104 | |||
1595 | 105 | try: | ||
1596 | 106 | import xmlsec | ||
1597 | 107 | except ImportError: | ||
1598 | 108 | raise ImportError, "pyxmlsec is required: see http://pyxmlsec.labs.libre-entreprise.org" | ||
1599 | 109 | |||
1600 | 110 | |||
1601 | 111 | #=== CONSTANTS ================================================================ | ||
1602 | 112 | |||
1603 | 113 | # XML Signature template with X509 certificate: | ||
1604 | 114 | # - the X.509 cert tag must be empty, else another one will be appended | ||
1605 | 115 | # - KeyName is optional | ||
1606 | 116 | TEMPLATE_WITH_CERT = \ | ||
1607 | 117 | """<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> | ||
1608 | 118 | <SignedInfo> | ||
1609 | 119 | <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> | ||
1610 | 120 | <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> | ||
1611 | 121 | <Reference URI=""> | ||
1612 | 122 | <Transforms> | ||
1613 | 123 | <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> | ||
1614 | 124 | </Transforms> | ||
1615 | 125 | <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> | ||
1616 | 126 | <DigestValue>TEMPLATE</DigestValue> | ||
1617 | 127 | </Reference> | ||
1618 | 128 | </SignedInfo> | ||
1619 | 129 | <SignatureValue>TEMPLATE</SignatureValue> | ||
1620 | 130 | <KeyInfo> | ||
1621 | 131 | <KeyName/> | ||
1622 | 132 | <X509Data> | ||
1623 | 133 | <X509Certificate></X509Certificate> | ||
1624 | 134 | </X509Data> | ||
1625 | 135 | </KeyInfo> | ||
1626 | 136 | </Signature> | ||
1627 | 137 | """ | ||
1628 | 138 | |||
1629 | 139 | # XML Signature template without X509 certificate: | ||
1630 | 140 | # - KeyInfo / KeyName is optional | ||
1631 | 141 | TEMPLATE_WITHOUT_CERT = \ | ||
1632 | 142 | """<Signature xmlns="http://www.w3.org/2000/09/xmldsig#"> | ||
1633 | 143 | <SignedInfo> | ||
1634 | 144 | <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/> | ||
1635 | 145 | <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/> | ||
1636 | 146 | <Reference URI=""> | ||
1637 | 147 | <Transforms> | ||
1638 | 148 | <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/> | ||
1639 | 149 | </Transforms> | ||
1640 | 150 | <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/> | ||
1641 | 151 | <DigestValue>TEMPLATE</DigestValue> | ||
1642 | 152 | </Reference> | ||
1643 | 153 | </SignedInfo> | ||
1644 | 154 | <SignatureValue>TEMPLATE</SignatureValue> | ||
1645 | 155 | <KeyInfo> | ||
1646 | 156 | <KeyName/> | ||
1647 | 157 | </KeyInfo> | ||
1648 | 158 | </Signature> | ||
1649 | 159 | """ | ||
1650 | 160 | |||
1651 | 161 | #=== CLASSES ================================================================== | ||
1652 | 162 | |||
1653 | 163 | class Xmldsig (object): | ||
1654 | 164 | """ | ||
1655 | 165 | class to sign and verify XML signatures (XML DSig) | ||
1656 | 166 | """ | ||
1657 | 167 | |||
1658 | 168 | def __init__(self, key_file=None, cert_file=None, password='', key_name=None): | ||
1659 | 169 | """ | ||
1660 | 170 | - key_file: str, filename of PEM file containing the private key. | ||
1661 | 171 | (the file should NOT be password-protected) | ||
1662 | 172 | - cert_file: str, filename of PEM file containing the X509 certificate. | ||
1663 | 173 | (optional: can be None) | ||
1664 | 174 | - password: str, password to open key file, or None if no password. | ||
1665 | 175 | - key_name: str, name for the key in the signature, or None if omitted. | ||
1666 | 176 | """ | ||
1667 | 177 | self.dsig_ctx = None | ||
1668 | 178 | # TEST: single key | ||
1669 | 179 | self.key = None | ||
1670 | 180 | # Create and initialize keys manager | ||
1671 | 181 | self.keysmngr = xmlsec.KeysMngr() | ||
1672 | 182 | if self.keysmngr is None: | ||
1673 | 183 | raise RuntimeError, "Error: failed to create keys manager." | ||
1674 | 184 | if xmlsec.cryptoAppDefaultKeysMngrInit(self.keysmngr) < 0: | ||
1675 | 185 | self.keysmngr.destroy() | ||
1676 | 186 | raise RuntimeError, "Error: failed to initialize keys manager." | ||
1677 | 187 | # load key | ||
1678 | 188 | self.load(key_file, cert_file, password, key_name) | ||
1679 | 189 | |||
1680 | 190 | |||
1681 | 191 | def load(self, key_file=None, cert_file=None, password='', key_name=None): | ||
1682 | 192 | """ | ||
1683 | 193 | load a private key and/or a public certificate for signature and verification | ||
1684 | 194 | |||
1685 | 195 | - key_file: str, filename of PEM file containing the private key. | ||
1686 | 196 | (the file should NOT be password-protected) | ||
1687 | 197 | - cert_file: str, filename of PEM file containing the X509 certificate. | ||
1688 | 198 | (optional: can be None) | ||
1689 | 199 | - password: str, password to open key file, or None if no password. | ||
1690 | 200 | """ | ||
1691 | 201 | # TODO: try except block to destroy key if error | ||
1692 | 202 | if key_file is not None: | ||
1693 | 203 | # Load private key, with optional password | ||
1694 | 204 | # print 'PASSWORD: %s' % password | ||
1695 | 205 | key = xmlsec.cryptoAppKeyLoad(filename=key_file, | ||
1696 | 206 | format=xmlsec.KeyDataFormatPem, pwd=password, | ||
1697 | 207 | pwdCallback=None, pwdCallbackCtx=None) | ||
1698 | 208 | # API references: | ||
1699 | 209 | # http://pyxmlsec.labs.libre-entreprise.org/docs/html/xmlsec-module.html#cryptoAppKeyLoad | ||
1700 | 210 | # http://www.aleksey.com/xmlsec/api/xmlsec-app.html#XMLSECCRYPTOAPPKEYLOAD | ||
1701 | 211 | # http://www.aleksey.com/xmlsec/api/xmlsec-keysdata.html#XMLSECKEYDATAFORMAT | ||
1702 | 212 | if key is None: | ||
1703 | 213 | raise RuntimeError, "Error: failed to load private PEM key from \"%s\"" % key_file | ||
1704 | 214 | if key_name is not None: | ||
1705 | 215 | # Set key name | ||
1706 | 216 | if key.setName(key_name) < 0: | ||
1707 | 217 | raise RuntimeError, "Error: failed to set key name to \"%s\"" % key_name | ||
1708 | 218 | if cert_file is not None: | ||
1709 | 219 | # Load certificate and add to the key | ||
1710 | 220 | if xmlsec.cryptoAppKeyCertLoad(key, cert_file, xmlsec.KeyDataFormatPem) < 0: | ||
1711 | 221 | raise RuntimeError, "Error: failed to load PEM certificate \"%s\"" % cert_file | ||
1712 | 222 | # load key into manager: | ||
1713 | 223 | if xmlsec.cryptoAppDefaultKeysMngrAdoptKey(self.keysmngr, key) < 0: | ||
1714 | 224 | raise RuntimeError, "Error: failed to load key into keys manager" | ||
1715 | 225 | |||
1716 | 226 | elif cert_file is not None: | ||
1717 | 227 | # case when we only want to load a cert without private key | ||
1718 | 228 | if self.keysmngr.certLoad(cert_file, xmlsec.KeyDataFormatPem, | ||
1719 | 229 | xmlsec.KeyDataTypeTrusted) < 0: | ||
1720 | 230 | # is it better to keep the keys manager if an error occurs? | ||
1721 | 231 | # self.keysmngr.destroy() | ||
1722 | 232 | raise RuntimeError, "Error: failed to load PEM certificate from \"%s\"" % cert_file | ||
1723 | 233 | # THIS DOES NOT WORK: it seems a certificate cannot be loaded like a key with a name... | ||
1724 | 234 | # # key = xmlsec.cryptoAppKeyLoad(filename = cert_file, | ||
1725 | 235 | # # format = xmlsec.KeyDataFormatPem, pwd = password, | ||
1726 | 236 | # # pwdCallback = None, pwdCallbackCtx = None) | ||
1727 | 237 | # # if key is None: | ||
1728 | 238 | # # raise RuntimeError, "Error: failed to load PEM certificate from \"%s\"", cert_file | ||
1729 | 239 | # # if key_name is not None: | ||
1730 | 240 | # # # Set key name | ||
1731 | 241 | # # if key.setName(key_name) < 0: | ||
1732 | 242 | # # raise RuntimeError, "Error: failed to set key name to \"%s\"" % key_name | ||
1733 | 243 | # # # load key into manager: | ||
1734 | 244 | # # if xmlsec.cryptoAppDefaultKeysMngrAdoptKey(self.keysmngr, key) < 0: | ||
1735 | 245 | # # raise RuntimeError, "Error: failed to load certificate into keys manager" | ||
1736 | 246 | |||
1737 | 247 | def load_certs(self, certificates): | ||
1738 | 248 | """ | ||
1739 | 249 | load one or several certificates into the keys manager for signature | ||
1740 | 250 | verification. For example, load the CA cert, any number of intermediate | ||
1741 | 251 | certs, and the cert corresponding to the key used for the signature. | ||
1742 | 252 | |||
1743 | 253 | certificates: list or tuple containing certificate file names | ||
1744 | 254 | """ | ||
1745 | 255 | for cert in certificates: | ||
1746 | 256 | self.load(cert_file=cert) | ||
1747 | 257 | |||
1748 | 258 | |||
1749 | 259 | def sign_file (self, template_file): | ||
1750 | 260 | """ | ||
1751 | 261 | Sign a XML file using the signature template in the XML file. | ||
1752 | 262 | The certificate from cert_file is placed in the <dsig:X509Data/> node. | ||
1753 | 263 | |||
1754 | 264 | - template_file: str, filename of XML file containing an XML-DSig template. | ||
1755 | 265 | |||
1756 | 266 | Returns a string containing the signed XML data. | ||
1757 | 267 | Raises an exception if an error occurs. | ||
1758 | 268 | """ | ||
1759 | 269 | xmlstring = open(template_file).read() | ||
1760 | 270 | return self.sign_xmlstring(xmlstring) | ||
1761 | 271 | |||
1762 | 272 | |||
1763 | 273 | def sign_xmlstring (self, xmlstring): | ||
1764 | 274 | """ | ||
1765 | 275 | Sign xmlstring using the signature template in xmlstring. | ||
1766 | 276 | The certificate from cert_file is placed in the <dsig:X509Data/> node. | ||
1767 | 277 | |||
1768 | 278 | - xmlstring: str, XML data containing an XML-DSig template. | ||
1769 | 279 | |||
1770 | 280 | Returns a string containing the signed XML data. | ||
1771 | 281 | Raises an exception if an error occurs. | ||
1772 | 282 | """ | ||
1773 | 283 | # try block to ensure cleanup is called even if an exception is raised: | ||
1774 | 284 | try: | ||
1775 | 285 | # Create signature context | ||
1776 | 286 | self._create_context() | ||
1777 | 287 | # Load template | ||
1778 | 288 | doc = self._parse_xmlstring(xmlstring) | ||
1779 | 289 | # find the XML-DSig start node | ||
1780 | 290 | node = xmlsec.findNode(doc.getRootElement(), xmlsec.NodeSignature, | ||
1781 | 291 | xmlsec.DSigNs) | ||
1782 | 292 | if node is None: | ||
1783 | 293 | raise RuntimeError, "Error: XML-DSIG node not found" | ||
1784 | 294 | # Sign the template | ||
1785 | 295 | if self.dsig_ctx.sign(node) < 0: | ||
1786 | 296 | raise RuntimeError, "Error: signature failed" | ||
1787 | 297 | output = str(doc) | ||
1788 | 298 | output = output.replace('\n', '') | ||
1789 | 299 | finally: | ||
1790 | 300 | # cleanup, even if an exception has been raised: | ||
1791 | 301 | self._cleanup_context() | ||
1792 | 302 | if doc is not None: | ||
1793 | 303 | doc.freeDoc() | ||
1794 | 304 | # return output if no exception was raised: | ||
1795 | 305 | return output | ||
1796 | 306 | |||
1797 | 307 | |||
1798 | 308 | def verify_file (self, xmlfile): | ||
1799 | 309 | """ | ||
1800 | 310 | Verify signature in XML file using the loaded certificate. | ||
1801 | 311 | |||
1802 | 312 | - xmlfile: str, filename of XML file containing an XML-DSig signature. | ||
1803 | 313 | |||
1804 | 314 | Returns True if the signature is valid, False otherwise. | ||
1805 | 315 | Raises an exception if an error occurs. | ||
1806 | 316 | """ | ||
1807 | 317 | xmlstring = open(xmlfile).read() | ||
1808 | 318 | return self.verify_xmlstring(xmlstring) | ||
1809 | 319 | |||
1810 | 320 | |||
1811 | 321 | def verify_xmlstring (self, xmlstring): | ||
1812 | 322 | """ | ||
1813 | 323 | Verify signature in xmlstring using the loaded certificate. | ||
1814 | 324 | |||
1815 | 325 | - xmlstring: str, XML data containing an XML-DSig signature. | ||
1816 | 326 | |||
1817 | 327 | Returns True if the signature is valid, False otherwise. | ||
1818 | 328 | Raises an exception if an error occurs. | ||
1819 | 329 | """ | ||
1820 | 330 | doc = None | ||
1821 | 331 | # try block to ensure cleanup is called even if an exception is raised: | ||
1822 | 332 | try: | ||
1823 | 333 | # Create signature context | ||
1824 | 334 | self._create_context() | ||
1825 | 335 | # Load XML data | ||
1826 | 336 | doc = self._parse_xmlstring(xmlstring) | ||
1827 | 337 | # find the XML-DSig start node | ||
1828 | 338 | node = xmlsec.findNode(doc.getRootElement(), xmlsec.NodeSignature, | ||
1829 | 339 | xmlsec.DSigNs) | ||
1830 | 340 | if node is None: | ||
1831 | 341 | raise RuntimeError, "Error: XML-DSIG node not found" | ||
1832 | 342 | # Verify signature | ||
1833 | 343 | if self.dsig_ctx.verify(node) < 0: | ||
1834 | 344 | # An error occured, the signature could not be verified | ||
1835 | 345 | raise RuntimeError, "Error: An error occured, the signature could not be verified" | ||
1836 | 346 | if self.dsig_ctx.status == xmlsec.DSigStatusSucceeded: | ||
1837 | 347 | # Signature is OK | ||
1838 | 348 | return True | ||
1839 | 349 | else: | ||
1840 | 350 | # Signature is INVALID | ||
1841 | 351 | return False | ||
1842 | 352 | finally: | ||
1843 | 353 | # cleanup, even if an exception has been raised: | ||
1844 | 354 | self._cleanup_context() | ||
1845 | 355 | if doc is not None: | ||
1846 | 356 | doc.freeDoc() | ||
1847 | 357 | # return output if no exception was raised: | ||
1848 | 358 | return output | ||
1849 | 359 | |||
1850 | 360 | |||
1851 | 361 | def _parse_xmlstring(self, xmlstring): | ||
1852 | 362 | """ | ||
1853 | 363 | parse XML string containing XML-DSIG nodes for signature (template) or | ||
1854 | 364 | verification (signed data) | ||
1855 | 365 | """ | ||
1856 | 366 | # doc = libxml2.parseFile(tmpl_file) | ||
1857 | 367 | doc = libxml2.parseDoc(xmlstring) | ||
1858 | 368 | if doc is None or doc.getRootElement() is None: | ||
1859 | 369 | raise RuntimeError, "Error: unable to parse XML data" | ||
1860 | 370 | return doc | ||
1861 | 371 | |||
1862 | 372 | |||
1863 | 373 | def _create_context(self): | ||
1864 | 374 | """ | ||
1865 | 375 | create the xmlsec context for signature or verification | ||
1866 | 376 | """ | ||
1867 | 377 | self.dsig_ctx = xmlsec.DSigCtx(self.keysmngr) | ||
1868 | 378 | if self.dsig_ctx is None: | ||
1869 | 379 | raise RuntimeError, "Error: failed to create signature context" | ||
1870 | 380 | |||
1871 | 381 | |||
1872 | 382 | def _cleanup_context (self): | ||
1873 | 383 | """ | ||
1874 | 384 | cleanup the xmlsec context in case of error | ||
1875 | 385 | """ | ||
1876 | 386 | if self.dsig_ctx is not None: | ||
1877 | 387 | self.dsig_ctx.destroy() | ||
1878 | 388 | self.dsig_ctx = None | ||
1879 | 389 | |||
1880 | 390 | |||
1881 | 391 | |||
1882 | 392 | |||
1883 | 393 | #=== FUNCTIONS ================================================================ | ||
1884 | 394 | |||
1885 | 395 | def sign_file(template_file, key_file, cert_file=None, password='', key_name=None): | ||
1886 | 396 | """ | ||
1887 | 397 | Sign a XML file using private key from key_file and the signature template | ||
1888 | 398 | in the XML file. | ||
1889 | 399 | The certificate from cert_file is placed in the <dsig:X509Data/> node. | ||
1890 | 400 | |||
1891 | 401 | - template_file: str, filename of XML file containing an XML-DSig template. | ||
1892 | 402 | - key_file: str, filename of PEM file containing the private key. | ||
1893 | 403 | (the file should NOT be password-protected) | ||
1894 | 404 | - cert_file: str, filename of PEM file containing the X509 certificate. | ||
1895 | 405 | (optional: can be None) | ||
1896 | 406 | - password: str, password to open key file, or None if no password. | ||
1897 | 407 | |||
1898 | 408 | Returns a string containing the signed XML data. | ||
1899 | 409 | Raises an exception if an error occurs. | ||
1900 | 410 | """ | ||
1901 | 411 | xmldsig = Xmldsig(key_file, cert_file, password, key_name) | ||
1902 | 412 | return xmldsig.sign_file(template_file) | ||
1903 | 413 | # # xmlstring = open(template_file).read() | ||
1904 | 414 | # # return sign_xmlstring(xmlstring, key_file, cert_file, password) | ||
1905 | 415 | |||
1906 | 416 | |||
1907 | 417 | def sign_xmlstring(xmlstring, key_file, cert_file=None, password='', key_name=None): | ||
1908 | 418 | """ | ||
1909 | 419 | Sign xmlstring using private key from key_file and the signature template | ||
1910 | 420 | in xmlstring. | ||
1911 | 421 | The certificate from cert_file is placed in the <dsig:X509Data/> node. | ||
1912 | 422 | |||
1913 | 423 | - xmlstring: str, XML data containing an XML-DSig template. | ||
1914 | 424 | - key_file: str, filename of PEM file containing the private key. | ||
1915 | 425 | (the file should NOT be password-protected) | ||
1916 | 426 | - cert_file: str, filename of PEM file containing the X509 certificate. | ||
1917 | 427 | (optional: can be None) | ||
1918 | 428 | - password: str, password to open key file, or "" if no password. | ||
1919 | 429 | (never use None because libxmlsec will ask on the console) | ||
1920 | 430 | |||
1921 | 431 | Returns a string containing the signed XML data. | ||
1922 | 432 | Raises an exception if an error occurs. | ||
1923 | 433 | """ | ||
1924 | 434 | xmldsig = Xmldsig(key_file, cert_file, password, key_name) | ||
1925 | 435 | return xmldsig.sign_xmlstring(xmlstring) | ||
1926 | 436 | # # # Load template | ||
1927 | 437 | # # #doc = libxml2.parseFile(tmpl_file) | ||
1928 | 438 | # # doc = libxml2.parseDoc(xmlstring) | ||
1929 | 439 | # # if doc is None or doc.getRootElement() is None: | ||
1930 | 440 | # # raise RuntimeError, "Error: unable to parse XML data" | ||
1931 | 441 | # # | ||
1932 | 442 | # # # try block to ensure cleanup is called even if an exception is raised: | ||
1933 | 443 | # # try: | ||
1934 | 444 | # # dsig_ctx = None | ||
1935 | 445 | # # | ||
1936 | 446 | # # # Find XML-DSig start node | ||
1937 | 447 | # # node = xmlsec.findNode(doc.getRootElement(), xmlsec.NodeSignature, | ||
1938 | 448 | # # xmlsec.DSigNs) | ||
1939 | 449 | # # if node is None: | ||
1940 | 450 | # # raise RuntimeError, "Error: start node not found" | ||
1941 | 451 | # # | ||
1942 | 452 | # # # Create signature context, we don't need keys manager in this example | ||
1943 | 453 | # # dsig_ctx = xmlsec.DSigCtx() | ||
1944 | 454 | # # if dsig_ctx is None: | ||
1945 | 455 | # # raise RuntimeError, "Error: failed to create signature context" | ||
1946 | 456 | # # | ||
1947 | 457 | # # # Load private key, assuming that there is not password | ||
1948 | 458 | # # #print 'PASSWORD: %s' % password | ||
1949 | 459 | # # key = xmlsec.cryptoAppKeyLoad(filename = key_file, | ||
1950 | 460 | # # format = xmlsec.KeyDataFormatPem, pwd = password, | ||
1951 | 461 | # # pwdCallback = None, pwdCallbackCtx = None) | ||
1952 | 462 | # # # API references: | ||
1953 | 463 | # # # http://pyxmlsec.labs.libre-entreprise.org/docs/html/xmlsec-module.html#cryptoAppKeyLoad | ||
1954 | 464 | # # # http://www.aleksey.com/xmlsec/api/xmlsec-app.html#XMLSECCRYPTOAPPKEYLOAD | ||
1955 | 465 | # # # http://www.aleksey.com/xmlsec/api/xmlsec-keysdata.html#XMLSECKEYDATAFORMAT | ||
1956 | 466 | # # if key is None: | ||
1957 | 467 | # # raise RuntimeError, "Error: failed to load private PEM key from \"%s\"" % key_file | ||
1958 | 468 | # # dsig_ctx.signKey = key | ||
1959 | 469 | # # | ||
1960 | 470 | # # if cert_file is not None: | ||
1961 | 471 | # # # Load certificate and add to the key | ||
1962 | 472 | # # ## if not check_filename(cert_file): | ||
1963 | 473 | # # ## return cleanup(doc, dsig_ctx) | ||
1964 | 474 | # # if xmlsec.cryptoAppKeyCertLoad(key, cert_file, xmlsec.KeyDataFormatPem) < 0: | ||
1965 | 475 | # # raise RuntimeError, "Error: failed to load PEM certificate \"%s\"" % cert_file | ||
1966 | 476 | # # | ||
1967 | 477 | # # # Set key name to the file name, this is just an example! | ||
1968 | 478 | # # if key.setName(key_file) < 0: | ||
1969 | 479 | # # raise RuntimeError, "Error: failed to set key name for key from \"%s\"" % key_file | ||
1970 | 480 | # # | ||
1971 | 481 | # # # Sign the template | ||
1972 | 482 | # # if dsig_ctx.sign(node) < 0: | ||
1973 | 483 | # # raise RuntimeError, "Error: signature failed" | ||
1974 | 484 | # # | ||
1975 | 485 | # # ## # Print signed document to stdout | ||
1976 | 486 | # # ## doc.dump("-") | ||
1977 | 487 | # # ## doc.saveFile("test.xml") | ||
1978 | 488 | # # output = str(doc) | ||
1979 | 489 | # # finally: | ||
1980 | 490 | # # # cleanup, even if an exception has been raised: | ||
1981 | 491 | # # cleanup(doc, dsig_ctx) | ||
1982 | 492 | # # # return output if no exception was raised: | ||
1983 | 493 | # # return output | ||
1984 | 494 | |||
1985 | 495 | |||
1986 | 496 | # #def cleanup(doc=None, dsig_ctx=None, res=-1): | ||
1987 | 497 | # # """ | ||
1988 | 498 | # # Cleans libxml2 context after usage. | ||
1989 | 499 | # # """ | ||
1990 | 500 | # # if dsig_ctx is not None: | ||
1991 | 501 | # # dsig_ctx.destroy() | ||
1992 | 502 | # # if doc is not None: | ||
1993 | 503 | # # doc.freeDoc() | ||
1994 | 504 | # # return res | ||
1995 | 505 | |||
1996 | 506 | |||
1997 | 507 | def _init(): | ||
1998 | 508 | """ | ||
1999 | 509 | Initialize necessary libraries (libxml2 and xmlsec). | ||
2000 | 510 | Should be called once only: this is automatic when this module is imported. | ||
2001 | 511 | Raises an exception if an error occurs. | ||
2002 | 512 | """ | ||
2003 | 513 | # Init libxml library | ||
2004 | 514 | libxml2.initParser() | ||
2005 | 515 | libxml2.substituteEntitiesDefault(1) | ||
2006 | 516 | # Init xmlsec library | ||
2007 | 517 | assert xmlsec.init() >= 0, "Error: xmlsec initialization failed." | ||
2008 | 518 | # Check loaded library version | ||
2009 | 519 | assert xmlsec.checkVersion() == 1, "Error: loaded xmlsec library version is not compatible." | ||
2010 | 520 | # Init crypto library | ||
2011 | 521 | assert xmlsec.cryptoAppInit(None) >= 0, "Error: crypto initialization failed." | ||
2012 | 522 | # Init xmlsec-crypto library | ||
2013 | 523 | assert xmlsec.cryptoInit() >= 0, "Error: xmlsec-crypto initialization failed." | ||
2014 | 524 | |||
2015 | 525 | |||
2016 | 526 | def shutdown(): | ||
2017 | 527 | """ | ||
2018 | 528 | Shutdown all libraries cleanly. | ||
2019 | 529 | Should only be called at the end of all xmlsec actions. | ||
2020 | 530 | """ | ||
2021 | 531 | # Shutdown xmlsec-crypto library | ||
2022 | 532 | xmlsec.cryptoShutdown() | ||
2023 | 533 | # Shutdown crypto library | ||
2024 | 534 | xmlsec.cryptoAppShutdown() | ||
2025 | 535 | # Shutdown xmlsec library | ||
2026 | 536 | xmlsec.shutdown() | ||
2027 | 537 | # Shutdown LibXML2 | ||
2028 | 538 | libxml2.cleanupParser() | ||
2029 | 539 | |||
2030 | 540 | |||
2031 | 541 | #=== MAIN ===================================================================== | ||
2032 | 542 | |||
2033 | 543 | # always initialize the xmlsec and libxml2 libraries: | ||
2034 | 544 | _init() | ||
2035 | 545 | |||
2036 | 546 | def main(): | ||
2037 | 547 | """ | ||
2038 | 548 | To use this module as a command-line tool. | ||
2039 | 549 | """ | ||
2040 | 550 | from optparse import OptionParser | ||
2041 | 551 | usage = "usage: %prog [options] file.xml" | ||
2042 | 552 | parser = OptionParser(usage=usage, version='%prog ' + __version__) | ||
2043 | 553 | parser.add_option("-k", "--keyfile", | ||
2044 | 554 | metavar="KEYFILE", help="PEM file containing private key", | ||
2045 | 555 | action="store", type="string", dest="keyfile") | ||
2046 | 556 | parser.add_option("-c", "--certfile", | ||
2047 | 557 | metavar="CERTFILE", help="PEM file containing the X.509 certificate", | ||
2048 | 558 | action="store", type="string", dest="certfile") | ||
2049 | 559 | parser.add_option("-p", "--password", default='', # not None! | ||
2050 | 560 | metavar="PASSWORD", help="Password of the private key file", | ||
2051 | 561 | action="store", type="string", dest="password") | ||
2052 | 562 | (options, args) = parser.parse_args() | ||
2053 | 563 | |||
2054 | 564 | if len(args) != 1 and not options.keyfile: | ||
2055 | 565 | print __doc__ | ||
2056 | 566 | parser.print_help() | ||
2057 | 567 | sys.exit() | ||
2058 | 568 | |||
2059 | 569 | signed_xml = sign_file(template_file=args[0], key_file=options.keyfile, | ||
2060 | 570 | cert_file=options.certfile, password=options.password) | ||
2061 | 571 | print signed_xml | ||
2062 | 572 | shutdown() | ||
2063 | 573 | |||
2064 | 574 | |||
2065 | 575 | if __name__ == "__main__": | ||
2066 | 576 | main() | ||
2067 | 577 | |||
2068 | 578 | # This code was developed while listening to Fleet Foxes | ||
2069 | 0 | 579 | ||
2070 | === added directory 'l10n_mx_facturae_pac_facturalo/report' | |||
2071 | === added file 'l10n_mx_facturae_pac_facturalo/report/__init__.py' | |||
2072 | --- l10n_mx_facturae_pac_facturalo/report/__init__.py 1970-01-01 00:00:00 +0000 | |||
2073 | +++ l10n_mx_facturae_pac_facturalo/report/__init__.py 2014-06-25 00:35:08 +0000 | |||
2074 | @@ -0,0 +1,25 @@ | |||
2075 | 1 | # -*- encoding: utf-8 -*- | ||
2076 | 2 | ########################################################################### | ||
2077 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
2078 | 4 | # | ||
2079 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
2080 | 6 | # All Rights Reserved. | ||
2081 | 7 | # info OpenPyme (info@openpyme.mx) | ||
2082 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
2083 | 9 | # | ||
2084 | 10 | # This program is free software: you can redistribute it and/or modify | ||
2085 | 11 | # it under the terms of the GNU Affero General Public License as | ||
2086 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
2087 | 13 | # License, or (at your option) any later version. | ||
2088 | 14 | # | ||
2089 | 15 | # This program is distributed in the hope that it will be useful, | ||
2090 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2091 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2092 | 18 | # GNU Affero General Public License for more details. | ||
2093 | 19 | # | ||
2094 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
2095 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2096 | 22 | # | ||
2097 | 23 | ############################################################################## | ||
2098 | 24 | |||
2099 | 25 | import account_print_invoice | ||
2100 | 0 | 26 | ||
2101 | === added file 'l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py' | |||
2102 | --- l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py 1970-01-01 00:00:00 +0000 | |||
2103 | +++ l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py 2014-06-25 00:35:08 +0000 | |||
2104 | @@ -0,0 +1,266 @@ | |||
2105 | 1 | # -*- encoding: utf-8 -*- | ||
2106 | 2 | ########################################################################### | ||
2107 | 3 | # Module Writen to OpenERP, Open Source Management Solution | ||
2108 | 4 | # | ||
2109 | 5 | # Copyright (c) 2013 OpenPyme - http://www.openpyme.mx | ||
2110 | 6 | # All Rights Reserved. | ||
2111 | 7 | # info OpenPyme (info@openpyme.mx) | ||
2112 | 8 | # Coded by: Agustín Cruz (agustin.cruz@openpyme.mx) | ||
2113 | 9 | # | ||
2114 | 10 | # This program is free software: you can redistribute it and/or modify | ||
2115 | 11 | # it under the terms of the GNU Affero General Public License as | ||
2116 | 12 | # published by the Free Software Foundation, either version 3 of the | ||
2117 | 13 | # License, or (at your option) any later version. | ||
2118 | 14 | # | ||
2119 | 15 | # This program is distributed in the hope that it will be useful, | ||
2120 | 16 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
2121 | 17 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
2122 | 18 | # GNU Affero General Public License for more details. | ||
2123 | 19 | # | ||
2124 | 20 | # You should have received a copy of the GNU Affero General Public License | ||
2125 | 21 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
2126 | 22 | # | ||
2127 | 23 | ############################################################################## | ||
2128 | 24 | |||
2129 | 25 | import string | ||
2130 | 26 | import logging | ||
2131 | 27 | import base64 | ||
2132 | 28 | import StringIO | ||
2133 | 29 | |||
2134 | 30 | from openerp.tools.translate import _ | ||
2135 | 31 | from account.report import account_print_invoice | ||
2136 | 32 | from openerp.report import report_sxw | ||
2137 | 33 | import openerp | ||
2138 | 34 | |||
2139 | 35 | |||
2140 | 36 | logger = logging.getLogger(__name__) | ||
2141 | 37 | |||
2142 | 38 | try: | ||
2143 | 39 | import qrcode | ||
2144 | 40 | except: | ||
2145 | 41 | logger.error("Package Python Qrcode missed") | ||
2146 | 42 | |||
2147 | 43 | |||
2148 | 44 | class account_invoice(account_print_invoice.account_invoice): | ||
2149 | 45 | def __init__(self, cr, uid, name, context): | ||
2150 | 46 | super(account_invoice, self).__init__(cr, uid, name, context=context) | ||
2151 | 47 | |||
2152 | 48 | self.localcontext.update({ | ||
2153 | 49 | 'split_string': self._split_string, | ||
2154 | 50 | 'get_taxes': self._get_taxes, | ||
2155 | 51 | 'get_taxes_ret': self._get_taxes_ret, | ||
2156 | 52 | 'float': float, | ||
2157 | 53 | 'exists_key': self._exists_key, | ||
2158 | 54 | 'get_text_promissory': self._get_text_promissory, | ||
2159 | 55 | 'get_emitter_data': self._get_emitter_data, | ||
2160 | 56 | 'get_partner_data': self._get_partner_data, | ||
2161 | 57 | 'qrcode': self._get_qrcode, | ||
2162 | 58 | 'legend': self._get_legend, | ||
2163 | 59 | }) | ||
2164 | 60 | |||
2165 | 61 | |||
2166 | 62 | def _exists_key(self, invoice, key): | ||
2167 | 63 | return key in invoice._columns | ||
2168 | 64 | |||
2169 | 65 | |||
2170 | 66 | def _get_text_promissory(self, company, partner): | ||
2171 | 67 | text = '' | ||
2172 | 68 | context = {} | ||
2173 | 69 | lang = self.pool.get('res.partner').browse(self.cr, self.uid, | ||
2174 | 70 | partner.id).lang | ||
2175 | 71 | if lang: | ||
2176 | 72 | context.update({'lang' : lang}) | ||
2177 | 73 | company = self.pool.get('res.company').browse(self.cr, self.uid, | ||
2178 | 74 | company.id, context=context) | ||
2179 | 75 | if company.dinamic_text: | ||
2180 | 76 | try: | ||
2181 | 77 | text = company.dinamic_text % eval("{" + company.dict_var + "}") | ||
2182 | 78 | except: | ||
2183 | 79 | pass | ||
2184 | 80 | return text | ||
2185 | 81 | |||
2186 | 82 | |||
2187 | 83 | def _get_taxes(self, invoice): | ||
2188 | 84 | # TODO: Optimizar esta funcion y combinarla con _get_taxes_ret | ||
2189 | 85 | lista = [] | ||
2190 | 86 | lista2 = [] | ||
2191 | 87 | |||
2192 | 88 | taxes = [tax for tax in invoice.tax_line if tax.tax_percent >= 0.0] | ||
2193 | 89 | |||
2194 | 90 | # comparacion de los taxes, para que todos sean distintos entre sí | ||
2195 | 91 | for tax in taxes: | ||
2196 | 92 | lista.append([tax.name2, tax.amount]) | ||
2197 | 93 | |||
2198 | 94 | for i in range(0, len(lista)): | ||
2199 | 95 | for j in range(i + 1, len(lista)): | ||
2200 | 96 | if (lista[i][0] == lista[j][0])and (lista[j][0] <> 0) : | ||
2201 | 97 | lista[j][0] = 0 | ||
2202 | 98 | lista[i][1] = lista[i][1] + lista[j][1] | ||
2203 | 99 | |||
2204 | 100 | |||
2205 | 101 | for k in range(0, len(lista)): | ||
2206 | 102 | if lista[k][0] <> 0: | ||
2207 | 103 | lista2.append(lista[k]) | ||
2208 | 104 | |||
2209 | 105 | return lista2 | ||
2210 | 106 | |||
2211 | 107 | |||
2212 | 108 | def _get_taxes_ret(self, invoice): | ||
2213 | 109 | lista = [] | ||
2214 | 110 | lista2 = [] | ||
2215 | 111 | |||
2216 | 112 | taxes = [tax for tax in invoice.tax_line if tax.tax_percent < 0.0] | ||
2217 | 113 | |||
2218 | 114 | # comparacion de los taxes, para que todos sean distintos entre sí | ||
2219 | 115 | for tax in taxes: | ||
2220 | 116 | lista.append([tax.name2, tax.amount]) | ||
2221 | 117 | |||
2222 | 118 | for i in range(0, len(lista)): | ||
2223 | 119 | for j in range(i + 1, len(lista)): | ||
2224 | 120 | if (lista[i][0] == lista[j][0])and (lista[j][0] <> 0) : | ||
2225 | 121 | lista[j][0] = 0 | ||
2226 | 122 | lista[i][1] = lista[i][1] + lista[j][1] | ||
2227 | 123 | |||
2228 | 124 | |||
2229 | 125 | for k in range(0, len(lista)): | ||
2230 | 126 | if lista[k][0] <> 0: | ||
2231 | 127 | lista2.append(lista[k]) | ||
2232 | 128 | |||
2233 | 129 | return lista2 | ||
2234 | 130 | |||
2235 | 131 | |||
2236 | 132 | def _split_string(self, string, length=75): | ||
2237 | 133 | if string: | ||
2238 | 134 | for i in range(0, len(string), length): | ||
2239 | 135 | string = string[:i] + ' ' + string[i:] | ||
2240 | 136 | return string | ||
2241 | 137 | |||
2242 | 138 | |||
2243 | 139 | def _get_emitter_data(self, partner, data='name'): | ||
2244 | 140 | # Simple cache for speed up | ||
2245 | 141 | if not hasattr(self, 'emitter_data'): | ||
2246 | 142 | self.emitter_data = self._get_invoice_address(partner) | ||
2247 | 143 | return self.emitter_data[data] | ||
2248 | 144 | |||
2249 | 145 | |||
2250 | 146 | def _get_partner_data(self, partner, data='name'): | ||
2251 | 147 | # Simple cache for speed up | ||
2252 | 148 | if not hasattr(self, 'partner_data'): | ||
2253 | 149 | self.partner_data = self._get_invoice_address(partner) | ||
2254 | 150 | return self.partner_data[data] | ||
2255 | 151 | |||
2256 | 152 | |||
2257 | 153 | def _get_invoice_address(self, partner): | ||
2258 | 154 | # Si la dirección del partner no es default o invoice | ||
2259 | 155 | if partner.type not in ['invoice', 'default']: | ||
2260 | 156 | # Obtiene la dirección del padre | ||
2261 | 157 | add_invoice = partner.parent_id | ||
2262 | 158 | else: | ||
2263 | 159 | add_invoice = partner | ||
2264 | 160 | |||
2265 | 161 | # Aseguramos que la dirección sea de facturación | ||
2266 | 162 | if add_invoice.type in ['invoice', 'default']: | ||
2267 | 163 | res = { | ||
2268 | 164 | 'name': add_invoice.name or '', | ||
2269 | 165 | 'vat': add_invoice.vat_split or add_invoice.vat or '', | ||
2270 | 166 | 'street': add_invoice.street or False, | ||
2271 | 167 | 'no_ext': add_invoice.l10n_mx_street3 or '', | ||
2272 | 168 | 'no_int': add_invoice.l10n_mx_street4 or '', | ||
2273 | 169 | 'suburb': add_invoice.street2 or '', | ||
2274 | 170 | 'city': add_invoice.city or '', | ||
2275 | 171 | 'state': add_invoice.state_id.name or '', | ||
2276 | 172 | 'country': add_invoice.country_id.name or '', | ||
2277 | 173 | 'county': add_invoice.l10n_mx_city2 or '', | ||
2278 | 174 | 'zip': add_invoice.zip or '', | ||
2279 | 175 | 'phone': add_invoice.phone or '', | ||
2280 | 176 | 'fax': add_invoice.fax or '', | ||
2281 | 177 | 'mobile': add_invoice.mobile or '', | ||
2282 | 178 | } | ||
2283 | 179 | if not res['vat']: | ||
2284 | 180 | # Comprobamos que tengamos un RFC definido | ||
2285 | 181 | raise openerp.exceptions.Warning( | ||
2286 | 182 | _('Not Vat Number set on partner')) | ||
2287 | 183 | else: | ||
2288 | 184 | raise openerp.exceptions.Warning( | ||
2289 | 185 | _('Customer Address Not Invoice Type')) | ||
2290 | 186 | return res | ||
2291 | 187 | |||
2292 | 188 | |||
2293 | 189 | def _get_qrcode(self, invoice): | ||
2294 | 190 | """ If invoice type is cbb then return the uplodaded code | ||
2295 | 191 | Else return the generated code | ||
2296 | 192 | """ | ||
2297 | 193 | if invoice.invoice_sequence_id.approval_id.type == 'cbb': | ||
2298 | 194 | return invoice.invoice_sequence_id.approval_id.cbb_image | ||
2299 | 195 | else: | ||
2300 | 196 | return self._gen_qrcode(invoice) | ||
2301 | 197 | |||
2302 | 198 | |||
2303 | 199 | def _gen_qrcode(self, invoice): | ||
2304 | 200 | """Genera el código de barras bidimensional para una factura | ||
2305 | 201 | @param invoice: Objeto invoice con los datos de la factura | ||
2306 | 202 | @param timbre: Cadena del XML obtenido del PAC | ||
2307 | 203 | |||
2308 | 204 | @return: Imagen del código de barras o None | ||
2309 | 205 | """ | ||
2310 | 206 | # Procesar invoice para obtener el total con 17 posiciones | ||
2311 | 207 | tt = string.zfill('%.6f' % invoice.amount_total, 17) | ||
2312 | 208 | |||
2313 | 209 | # Init qr code | ||
2314 | 210 | qr = qrcode.QRCode(version=4, box_size=4, border=1) | ||
2315 | 211 | # Add the data to qr code | ||
2316 | 212 | qr.add_data('?re=' + invoice.company_id.partner_id.vat_split or invoice.company_id.partner_id.vat) | ||
2317 | 213 | qr.add_data('&rr=' + invoice.partner_id.vat_split or invoice.company_id.vat) | ||
2318 | 214 | qr.add_data('&tt=' + tt) | ||
2319 | 215 | qr.add_data('&id=' + invoice.cfdi_folio_fiscal) | ||
2320 | 216 | qr.make(fit=True) | ||
2321 | 217 | |||
2322 | 218 | # Genera la imagen y la pone en memoria para poder | ||
2323 | 219 | # codificarla en 64bits y mandarla al reporte | ||
2324 | 220 | img = qr.make_image() | ||
2325 | 221 | output = StringIO.StringIO() | ||
2326 | 222 | img.save(output, 'PNG') | ||
2327 | 223 | output_s = output.getvalue() | ||
2328 | 224 | |||
2329 | 225 | return base64.b64encode(output_s) | ||
2330 | 226 | |||
2331 | 227 | |||
2332 | 228 | def _get_legend(self, invoice): | ||
2333 | 229 | """ Helper funcion to print legend according | ||
2334 | 230 | to invoice type. | ||
2335 | 231 | """ | ||
2336 | 232 | if invoice.invoice_sequence_id.approval_id.type == 'cbb': | ||
2337 | 233 | approval = invoice.invoice_sequence_id.approval_id.approval_number | ||
2338 | 234 | date = invoice.invoice_sequence_id.approval_id.date_start | ||
2339 | 235 | types = ['out_invoice', 'out_refund'] | ||
2340 | 236 | states = ['open', 'paid', 'cancel'] | ||
2341 | 237 | if (invoice.type in types) and (invoice.state in states): | ||
2342 | 238 | legend = _('Número de aprobación SICOFI: %s\n' % | ||
2343 | 239 | approval.encode('utf-8', 'ignore') | ||
2344 | 240 | ) | ||
2345 | 241 | else: | ||
2346 | 242 | legend = _('SIN FOLIO O ESTATUS NO VALIDO\n') | ||
2347 | 243 | legend += _('La reproducción apócrifa de este comprobante ' | ||
2348 | 244 | 'constituye un delito en los términos de las ' | ||
2349 | 245 | 'disposiciones fiscales.\n') | ||
2350 | 246 | legend += _('Este comprobante tendrá una vigencia de dos años ' | ||
2351 | 247 | 'contados a partir de la fecha aprobación de la ' | ||
2352 | 248 | 'asignación de folios, la cual es: %s' % | ||
2353 | 249 | date.encode('utf-8', 'ignore') | ||
2354 | 250 | ) | ||
2355 | 251 | else: | ||
2356 | 252 | legend = '“Este documento es una representacion impresa de un CFDI”' | ||
2357 | 253 | return legend | ||
2358 | 254 | |||
2359 | 255 | |||
2360 | 256 | from openerp.netsvc import Service | ||
2361 | 257 | # Borra el servicio del reporte para poder declararlo nuevamente | ||
2362 | 258 | del Service._services['report.account.invoice.facturae.webkit'] | ||
2363 | 259 | |||
2364 | 260 | report_sxw.report_sxw( | ||
2365 | 261 | 'report.account.invoice.facturae.webkit', | ||
2366 | 262 | 'account.invoice', | ||
2367 | 263 | 'addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml', | ||
2368 | 264 | header=False, | ||
2369 | 265 | parser=account_invoice, | ||
2370 | 266 | ) | ||
2371 | 0 | 267 | ||
2372 | === added file 'l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml' | |||
2373 | --- l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml 1970-01-01 00:00:00 +0000 | |||
2374 | +++ l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml 2014-06-25 00:35:08 +0000 | |||
2375 | @@ -0,0 +1,476 @@ | |||
2376 | 1 | <?xml version="1.0"?> | ||
2377 | 2 | <document filename="test.pdf"> | ||
2378 | 3 | <template pageSize="(612.0,780.0)" title="Test" allowSplitting="20"> | ||
2379 | 4 | <pageTemplate id="first"> | ||
2380 | 5 | <frame id="first" x1="57.0" y1="46.0" width="498" height="729"/> | ||
2381 | 6 | <pageGraphics> | ||
2382 | 7 | <!--page bottom(footer)--> | ||
2383 | 8 | <lines>1.33cm 1.33cm 20cm 1.33cm</lines> | ||
2384 | 9 | <place x="1.30cm" y="0cm" height="1.30cm" width="34.0cm"> | ||
2385 | 10 | <para style="main_footer">Generado por openpyme.mx</para> | ||
2386 | 11 | </place> | ||
2387 | 12 | </pageGraphics> | ||
2388 | 13 | </pageTemplate> | ||
2389 | 14 | </template> | ||
2390 | 15 | |||
2391 | 16 | <stylesheet> | ||
2392 | 17 | <blockTableStyle id="Standard_Outline"> | ||
2393 | 18 | <blockAlignment value="LEFT"/> | ||
2394 | 19 | <blockValign value="TOP"/> | ||
2395 | 20 | </blockTableStyle> | ||
2396 | 21 | <blockTableStyle id="Tabla1"> | ||
2397 | 22 | <blockAlignment value="LEFT"/> | ||
2398 | 23 | <blockValign value="TOP"/> | ||
2399 | 24 | </blockTableStyle> | ||
2400 | 25 | <blockTableStyle id="Tabla2"> | ||
2401 | 26 | <blockAlignment value="LEFT"/> | ||
2402 | 27 | <blockValign value="TOP"/> | ||
2403 | 28 | <lineStyle kind="LINEABOVE" colorName="#000080" start="0,0" stop="0,0"/> | ||
2404 | 29 | <lineStyle kind="LINEABOVE" colorName="#000080" start="1,0" stop="1,0"/> | ||
2405 | 30 | </blockTableStyle> | ||
2406 | 31 | <blockTableStyle id="Tabla3"> | ||
2407 | 32 | <blockAlignment value="LEFT"/> | ||
2408 | 33 | <blockValign value="TOP"/> | ||
2409 | 34 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/> | ||
2410 | 35 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/> | ||
2411 | 36 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/> | ||
2412 | 37 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/> | ||
2413 | 38 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/> | ||
2414 | 39 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/> | ||
2415 | 40 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/> | ||
2416 | 41 | <lineStyle kind="LINEAFTER" colorName="#cccccc" start="2,0" stop="2,-1"/> | ||
2417 | 42 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/> | ||
2418 | 43 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/> | ||
2419 | 44 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/> | ||
2420 | 45 | <lineStyle kind="LINEAFTER" colorName="#cccccc" start="3,0" stop="3,-1"/> | ||
2421 | 46 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/> | ||
2422 | 47 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/> | ||
2423 | 48 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="4,0" stop="4,-1"/> | ||
2424 | 49 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="4,0" stop="4,0"/> | ||
2425 | 50 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/> | ||
2426 | 51 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="5,0" stop="5,-1"/> | ||
2427 | 52 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="5,0" stop="5,0"/> | ||
2428 | 53 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/> | ||
2429 | 54 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="6,0" stop="6,-1"/> | ||
2430 | 55 | <lineStyle kind="LINEAFTER" colorName="#cccccc" start="6,0" stop="6,-1"/> | ||
2431 | 56 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="6,0" stop="6,0"/> | ||
2432 | 57 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="6,-1" stop="6,-1"/> | ||
2433 | 58 | <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="7,0" stop="7,-1"/> | ||
2434 | 59 | <lineStyle kind="LINEAFTER" colorName="#cccccc" start="7,0" stop="7,-1"/> | ||
2435 | 60 | <lineStyle kind="LINEABOVE" colorName="#cccccc" start="7,0" stop="7,0"/> | ||
2436 | 61 | <lineStyle kind="LINEBELOW" colorName="#cccccc" start="7,-1" stop="7,-1"/> | ||
2437 | 62 | </blockTableStyle> | ||
2438 | 63 | <blockTableStyle id="Tabla4"> | ||
2439 | 64 | <blockAlignment value="LEFT"/> | ||
2440 | 65 | <blockValign value="TOP"/> | ||
2441 | 66 | </blockTableStyle> | ||
2442 | 67 | <blockTableStyle id="Tabla5"> | ||
2443 | 68 | <blockAlignment value="LEFT"/> | ||
2444 | 69 | <blockValign value="TOP"/> | ||
2445 | 70 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="0,0" stop="0,-1"/> | ||
2446 | 71 | <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> | ||
2447 | 72 | <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> | ||
2448 | 73 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="1,0" stop="1,-1"/> | ||
2449 | 74 | <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> | ||
2450 | 75 | <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> | ||
2451 | 76 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="2,0" stop="2,-1"/> | ||
2452 | 77 | <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> | ||
2453 | 78 | <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> | ||
2454 | 79 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="3,0" stop="3,-1"/> | ||
2455 | 80 | <lineStyle kind="LINEAFTER" colorName="#000000" start="3,0" stop="3,-1"/> | ||
2456 | 81 | <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/> | ||
2457 | 82 | <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> | ||
2458 | 83 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="0,1" stop="0,-1"/> | ||
2459 | 84 | <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> | ||
2460 | 85 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="1,1" stop="1,-1"/> | ||
2461 | 86 | <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> | ||
2462 | 87 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="2,1" stop="2,-1"/> | ||
2463 | 88 | <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> | ||
2464 | 89 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="3,1" stop="3,-1"/> | ||
2465 | 90 | <lineStyle kind="LINEAFTER" colorName="#000000" start="3,1" stop="3,-1"/> | ||
2466 | 91 | <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/> | ||
2467 | 92 | </blockTableStyle> | ||
2468 | 93 | <blockTableStyle id="Tabla6"> | ||
2469 | 94 | <blockAlignment value="LEFT"/> | ||
2470 | 95 | <blockValign value="TOP"/> | ||
2471 | 96 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="0,0" stop="0,-1"/> | ||
2472 | 97 | <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/> | ||
2473 | 98 | <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/> | ||
2474 | 99 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="1,0" stop="1,-1"/> | ||
2475 | 100 | <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/> | ||
2476 | 101 | <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/> | ||
2477 | 102 | <lineStyle kind="LINEBEFORE" colorName="#000000" start="2,0" stop="2,-1"/> | ||
2478 | 103 | <lineStyle kind="LINEAFTER" colorName="#000000" start="2,0" stop="2,-1"/> | ||
2479 | 104 | <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/> | ||
2480 | 105 | <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/> | ||
2481 | 106 | </blockTableStyle> | ||
2482 | 107 | <blockTableStyle id="Tabla7"> | ||
2483 | 108 | <blockAlignment value="LEFT"/> | ||
2484 | 109 | <blockValign value="TOP"/> | ||
2485 | 110 | </blockTableStyle> | ||
2486 | 111 | <initialize> | ||
2487 | 112 | <paraStyle name="all" alignment="justify"/> | ||
2488 | 113 | </initialize> | ||
2489 | 114 | |||
2490 | 115 | <paraStyle name="P1" fontName="Helvetica-Bold" textColor="#ff3333" fontSize="30.0" alignment="CENTER"/> | ||
2491 | 116 | <paraStyle name="P7" fontName="Helvetica" fontSize="6.5" alignment="RIGHT"/> | ||
2492 | 117 | <paraStyle name="P8" fontName="Helvetica-Bold" fontSize="6.5" alignment="RIGHT" textColor="#280099"/> | ||
2493 | 118 | <paraStyle name="P9" fontName="Helvetica-Bold" fontSize="6.5" alignment="CENTER" textColor="#280099"/> | ||
2494 | 119 | <paraStyle name="P10" fontName="Helvetica-Bold" alignment="CENTER" fontSize="6.5" textColor="#280099"/> | ||
2495 | 120 | <paraStyle name="P11" fontName="Helvetica-Bold" textColor="#ff3333" size="6.0" alignment="CENTER"/> | ||
2496 | 121 | <paraStyle name="P12" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="4.5" alignment="LEFT" leading="10" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2497 | 122 | <paraStyle name="P13" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="4.0" leading="10" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2498 | 123 | <paraStyle name="P14" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="5.0" leading="10" spaceBefore="0.0" spaceAfter="0.0" alignment="CENTER" /> | ||
2499 | 124 | <paraStyle name="P15" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="6.5" alignment="RIGHT" leading="10" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2500 | 125 | <paraStyle name="P21" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="6.0" leading="8" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2501 | 126 | <paraStyle name="P22" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="7.0" leading="8" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2502 | 127 | <paraStyle name="P23" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="9.0" leading="8" alignment="LEFT" spaceBefore="0.0" spaceAfter="2.0"/> | ||
2503 | 128 | <paraStyle name="P24" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="7.0" leading="8" alignment="LEFT" spaceBefore="1.0" spaceAfter="0.0"/> | ||
2504 | 129 | <paraStyle name="P25" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="5.0" leading="8" alignment="LEFT" spaceBefore="1.0" spaceAfter="0.0"/> | ||
2505 | 130 | <paraStyle name="P30" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="8.0" leading="10" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2506 | 131 | <paraStyle name="P31" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="6.0" leading="8" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2507 | 132 | <paraStyle name="Standard" fontName="Helvetica"/> | ||
2508 | 133 | <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/> | ||
2509 | 134 | <paraStyle name="Text body" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> | ||
2510 | 135 | <paraStyle name="List" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/> | ||
2511 | 136 | <paraStyle name="Caption" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/> | ||
2512 | 137 | <paraStyle name="Index" fontName="Helvetica"/> | ||
2513 | 138 | <paraStyle name="Table Contents" fontName="Helvetica" alignment="LEFT" fontSize="6.5"/> | ||
2514 | 139 | <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/> | ||
2515 | 140 | <paraStyle name="terp_default_8" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica-Bold" fontSize="6.0" leading="8" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2516 | 141 | <paraStyle name="terp_default_9" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="7.0" leading="8" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/> | ||
2517 | 142 | <paraStyle name="Table Heading" fontName="Helvetica" alignment="CENTER"/> | ||
2518 | 143 | <paraStyle name="Quotations" rightIndent="28.0" leftIndent="28.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="14.0"/> | ||
2519 | 144 | <paraStyle name="main_footer" fontSize="6.5" fontName="Helvetica" alignment="CENTER"/> | ||
2520 | 145 | <paraStyle name="main_footer2" fontSize="7.5" fontName="Helvetica-Bold" alignment="RIGHT"/> | ||
2521 | 146 | <images/> | ||
2522 | 147 | </stylesheet> | ||
2523 | 148 | <story> | ||
2524 | 149 | <section> | ||
2525 | 150 | <para style="P31">[[ repeatIn(objects,'o') ]] </para> | ||
2526 | 151 | <para style="P31">[[ setLang(o.partner_id.lang) ]] </para> | ||
2527 | 152 | |||
2528 | 153 | <blockTable colWidths="150.0,220.0,200.0" style="Tabla1"> | ||
2529 | 154 | <tr> | ||
2530 | 155 | <td> | ||
2531 | 156 | <blockTable> | ||
2532 | 157 | <tr> | ||
2533 | 158 | </tr> | ||
2534 | 159 | <tr> | ||
2535 | 160 | <td><para style="P30">[[ o.company_emitter_id.logo and setTag('para','image',{'width':'4.5cm','height':'2.25cm'}) ]] [[ o.company_emitter_id.logo ]]</para></td> | ||
2536 | 161 | </tr> | ||
2537 | 162 | </blockTable> | ||
2538 | 163 | </td> | ||
2539 | 164 | <td> | ||
2540 | 165 | <blockTable> | ||
2541 | 166 | <tr> | ||
2542 | 167 | <td> | ||
2543 | 168 | <para style="P23">[[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'name') ]]</para> | ||
2544 | 169 | <para style="P24">[[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'vat') ]]</para> | ||
2545 | 170 | <para style="P25">Domicilio Fiscal</para> | ||
2546 | 171 | <para style="P24">[[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'street') ]] Nro. Ext: [[ get_emitter_data(o.company_emitter_id.partner_id, 'no_ext') ]] <font>Int: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'no_int') or removeParentNode('font') ]]</font></para> | ||
2547 | 172 | <para style="P24">Colonia: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'street2') or removeParentNode('para') ]]</para> | ||
2548 | 173 | <para style="P24">Ciudad: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'city') ]] Estado: [[ get_emitter_data(o.company_emitter_id.partner_id, 'state') ]]</para> | ||
2549 | 174 | <para style="P24">Localidad: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'county') or removeParentNode('para') ]]</para> | ||
2550 | 175 | <para style="P24">CP: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'zip') or removeParentNode('para') ]]</para> | ||
2551 | 176 | <para style="P24">Phone(s): [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'phone') or removeParentNode('para') ]]</para> | ||
2552 | 177 | </td> | ||
2553 | 178 | </tr> | ||
2554 | 179 | </blockTable> | ||
2555 | 180 | </td> | ||
2556 | 181 | <td> | ||
2557 | 182 | <blockTable rowWidths="10.0" style="Tabla5"> | ||
2558 | 183 | <tr><td> | ||
2559 | 184 | <para style="P11">Factura[[ ((o.type == 'out_invoice' and (o.state == 'open' or o.state == 'paid')) or removeParentNode('para')) and '' ]] [[ o.number ]]</para> | ||
2560 | 185 | <para style="P11">FACTURA CANCELADA[[ ((o.type == 'out_invoice' and o.state == 'cancel') or removeParentNode('para')) and '' ]] [[ o.number ]]</para> | ||
2561 | 186 | <para style="P11">Credit Node [[ (o.type=='out_refund' or removeParentNode('para')) and '' ]] [[ o.number ]]</para> | ||
2562 | 187 | </td></tr> | ||
2563 | 188 | <tr><td> | ||
2564 | 189 | <para style="terp_default_8">FOLIO FISCAL(UUID):</para> | ||
2565 | 190 | <para style="terp_default_9">[[ o.cfdi_folio_fiscal or removeParentNode('tr') ]]</para> | ||
2566 | 191 | </td></tr> | ||
2567 | 192 | <tr><td> | ||
2568 | 193 | <para style="terp_default_8">NO. DE SERIE DEL CERTIFICADO DEL SAT:</para> | ||
2569 | 194 | <para style="terp_default_9">[[ o.cfdi_no_certificado or removeParentNode('tr') ]]</para> | ||
2570 | 195 | </td></tr> | ||
2571 | 196 | <tr><td> | ||
2572 | 197 | <para style="terp_default_8">NO. DE SERIE DEL CERTIFICADO DEL EMISOR:</para> | ||
2573 | 198 | <para style="terp_default_9">[[ o.no_certificado or removeParentNode('tr') ]]</para> | ||
2574 | 199 | </td></tr> | ||
2575 | 200 | <tr><td> | ||
2576 | 201 | <para style="terp_default_8">CERTIFICATION DATE AND TIME:</para> | ||
2577 | 202 | <para style="terp_default_9">[[o.date_invoice_tz or removeParentNode('tr') ]]</para> | ||
2578 | 203 | </td></tr> | ||
2579 | 204 | <tr><td> | ||
2580 | 205 | <para style="terp_default_8">ISSUE DATE AND TIME:</para> | ||
2581 | 206 | <para style="terp_default_9">[[ (o.invoice_sequence_id.approval_id.type != 'cbb') and o.cfdi_fecha_timbrado or removeParentNode('tr') ]]</para> | ||
2582 | 207 | </td></tr> | ||
2583 | 208 | </blockTable> | ||
2584 | 209 | </td> | ||
2585 | 210 | </tr> | ||
2586 | 211 | </blockTable> | ||
2587 | 212 | |||
2588 | 213 | <blockTable colWidths="250.0,50.0,250.0" style="Tabla4"> | ||
2589 | 214 | <tr> | ||
2590 | 215 | <td> | ||
2591 | 216 | <blockTable colWidths="60.0,190.0"> | ||
2592 | 217 | <tr> | ||
2593 | 218 | <td><para style="P21"><font face="Helvetica">CLIENTE:</font></para></td> | ||
2594 | 219 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'name') ]]</para></td> | ||
2595 | 220 | </tr> | ||
2596 | 221 | <tr> | ||
2597 | 222 | <td><para style="P21"><font face="Helvetica">RFC:</font></para></td> | ||
2598 | 223 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'vat') ]]</para></td> | ||
2599 | 224 | </tr> | ||
2600 | 225 | <tr> | ||
2601 | 226 | <td><para style="P21"><font face="Helvetica">ADDRESS:</font></para></td> | ||
2602 | 227 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'street') ]] [[ get_partner_data(o.partner_id, 'no_ext') ]] <font>INT. [[ get_partner_data(o.partner_id, 'no_int') or removeParentNode('font')]]</font></para> | ||
2603 | 228 | </td> | ||
2604 | 229 | </tr> | ||
2605 | 230 | <tr> | ||
2606 | 231 | <td><para style="P21"><font face="Helvetica">COLONIA:</font></para></td> | ||
2607 | 232 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'suburb') ]] [[ get_partner_data(o.partner_id, 'county') ]]</para></td> | ||
2608 | 233 | </tr> | ||
2609 | 234 | <tr> | ||
2610 | 235 | <td><para style="P21"><font face="Helvetica">LOCALIDAD:</font></para></td> | ||
2611 | 236 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'city') ]] [[ get_partner_data(o.partner_id, 'state') ]] [[ get_partner_data(o.partner_id, 'country') ]]</para></td> | ||
2612 | 237 | </tr> | ||
2613 | 238 | <tr> | ||
2614 | 239 | <td><para style="P21"><font face="Helvetica">CP:</font></para></td> | ||
2615 | 240 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'zip') or 'N/D' ]] </para></td> | ||
2616 | 241 | </tr> | ||
2617 | 242 | <tr> | ||
2618 | 243 | <td><para style="P21"><font face="Helvetica">PHONE:</font></para></td> | ||
2619 | 244 | <td><para style="P22">[[ get_partner_data(o.partner_id, 'phone') or 'N/D']]</para></td> | ||
2620 | 245 | </tr> | ||
2621 | 246 | </blockTable> | ||
2622 | 247 | </td> | ||
2623 | 248 | <td></td> | ||
2624 | 249 | <td> | ||
2625 | 250 | <blockTable colWidths="90.0,160.0" > | ||
2626 | 251 | <tr> | ||
2627 | 252 | <td><para style="P21"><font face="Helvetica">Taxation: </font></para></td> | ||
2628 | 253 | <td><para style="P22">[[o.company_emitter_id.partner_id.regimen_fiscal_id and o.company_emitter_id.partner_id.regimen_fiscal_id.name or removeParentNode('tr')]]</para></td> | ||
2629 | 254 | </tr> | ||
2630 | 255 | <tr> | ||
2631 | 256 | <td><para style="P21"><font face="Helvetica">Place of issue: </font></para></td> | ||
2632 | 257 | <td><para style="P22">[[ o.address_issued_id and o.address_issued_id.city and o.address_issued_id.state_id and o.address_issued_id.state_id.name or removeParentNode('tr') ]]</para></td> | ||
2633 | 258 | </tr> | ||
2634 | 259 | <tr> | ||
2635 | 260 | <td><para style="P21"><font face="Helvetica">Condiciones de Pago: </font></para></td> | ||
2636 | 261 | <td><para style="P22">[[ format(o.payment_term and (o.payment_term.note or o.payment_term.name) or removeParentNode('tr') ) ]]</para></td> | ||
2637 | 262 | </tr> | ||
2638 | 263 | <tr> | ||
2639 | 264 | <td><para style="P21"><font face="Helvetica">NumCtaPago:</font></para></td> | ||
2640 | 265 | <td><para style="P22">[[o.acc_payment.last_acc_number or removeParentNode('tr')]]</para></td> | ||
2641 | 266 | </tr> | ||
2642 | 267 | <tr> | ||
2643 | 268 | <td><para style="P21"><font face="Helvetica">Payment Method:</font></para></td> | ||
2644 | 269 | <td><para style="P22"> [[o.pay_method_id.name or removeParentNode('tr')]]</para></td> | ||
2645 | 270 | </tr> | ||
2646 | 271 | <tr> | ||
2647 | 272 | <td><para style="P21"><font face="Helvetica">BANCO:</font></para></td> | ||
2648 | 273 | <td><para style="P22"> [[ o.acc_payment.bank_name or removeParentNode('tr')]] </para></td> | ||
2649 | 274 | </tr> | ||
2650 | 275 | <tr> | ||
2651 | 276 | <td><para style="P21"><font face="Helvetica">Clave de Moneda:</font></para></td> | ||
2652 | 277 | <td><para style="P22">[[o.currency_id.name or removeParentNode('tr')]]</para></td> | ||
2653 | 278 | </tr> | ||
2654 | 279 | <tr> | ||
2655 | 280 | <td><para style="P21"><font face="Helvetica">Origin:</font></para></td> | ||
2656 | 281 | <td><para style="P22">[[ o.origin or removeParentNode('tr')]]</para></td> | ||
2657 | 282 | </tr> | ||
2658 | 283 | <tr> | ||
2659 | 284 | <td><para style="P21"><font face="Helvetica">Customer Reference:</font></para></td> | ||
2660 | 285 | <td><para style="P22">[[ o.name or removeParentNode('tr')]]</para></td> | ||
2661 | 286 | </tr> | ||
2662 | 287 | </blockTable> | ||
2663 | 288 | </td> | ||
2664 | 289 | </tr> | ||
2665 | 290 | </blockTable> | ||
2666 | 291 | |||
2667 | 292 | |||
2668 | 293 | <blockTable colWidths="70.0,70.0,70.0,210.0,70.0,70.0" style="Tabla3"> | ||
2669 | 294 | <tr style="Tabla3"> | ||
2670 | 295 | <td> | ||
2671 | 296 | <para style="P9">CANTIDAD</para> | ||
2672 | 297 | </td> | ||
2673 | 298 | <td> | ||
2674 | 299 | <para style="P9">UNIDAD DE MEDIDA</para> | ||
2675 | 300 | </td> | ||
2676 | 301 | <td> | ||
2677 | 302 | <para style="P9">CODE</para> | ||
2678 | 303 | </td> | ||
2679 | 304 | <td> | ||
2680 | 305 | <para style="P9">DESCRIPTION</para> | ||
2681 | 306 | </td> | ||
2682 | 307 | <td> | ||
2683 | 308 | <para style="P9">PRECIO UNITARIO</para> | ||
2684 | 309 | </td> | ||
2685 | 310 | <td> | ||
2686 | 311 | <para style="P9">IMPORTES</para> | ||
2687 | 312 | </td> | ||
2688 | 313 | </tr> | ||
2689 | 314 | <tr> | ||
2690 | 315 | <td> | ||
2691 | 316 | <para style="Table Contents"><font face="Helvetica" size="8.0">[[ repeatIn(o.invoice_line,'l') ]] </font>[[ formatLang(l.quantity) ]]</para> | ||
2692 | 317 | </td> | ||
2693 | 318 | <td > | ||
2694 | 319 | <para style="Table Contents">[[ (l.uos_id and l.uos_id.name) or '' ]]</para> | ||
2695 | 320 | </td> | ||
2696 | 321 | <td > | ||
2697 | 322 | <para style="Table Contents">[[ l.product_id and l.product_id.default_code ]]</para> | ||
2698 | 323 | </td> | ||
2699 | 324 | <td > | ||
2700 | 325 | <para style="Table Contents">[[ l.name ]]</para> | ||
2701 | 326 | <para style="Table Contents">Notas: [[l.note or removeParentNode('para')]]</para> | ||
2702 | 327 | </td> | ||
2703 | 328 | <td> | ||
2704 | 329 | <para style="P15">[[ formatLang(l.price_unit) ]]</para> | ||
2705 | 330 | </td> | ||
2706 | 331 | <td> | ||
2707 | 332 | <para style="P15">[[ exists_key(o, 'global_discount_percent') and (formatLang(l.quantity * l.price_unit, digits=get_digits(dp='Account'))) or formatLang(l.price_subtotal) ]]</para> | ||
2708 | 333 | </td> | ||
2709 | 334 | </tr> | ||
2710 | 335 | </blockTable> | ||
2711 | 336 | |||
2712 | 337 | <blockTable colWidths="425,70.0,75.0" style="Tabla4"> | ||
2713 | 338 | <tr> | ||
2714 | 339 | <td> | ||
2715 | 340 | <para style="P10">[[ o.amount_to_text ]]</para> | ||
2716 | 341 | </td> | ||
2717 | 342 | <td> | ||
2718 | 343 | <blockTable rowHeights="10.0" style="Tabla4"> | ||
2719 | 344 | <tr> | ||
2720 | 345 | <td><para style="P8">SUMA: [[ exists_key(o, 'global_discount_percent') and o.global_discount_percent or removeParentNode('tr')]]$</para></td> | ||
2721 | 346 | </tr> | ||
2722 | 347 | <tr> | ||
2723 | 348 | <td><para style="P8">DESCUENTO: [[ exists_key(o, 'global_discount_percent') and o.global_discount_percent or removeParentNode('tr')]] %</para> | ||
2724 | 349 | </td> | ||
2725 | 350 | </tr> | ||
2726 | 351 | <tr> | ||
2727 | 352 | <td> | ||
2728 | 353 | <para style="P8">SUBTOTAL: $</para> | ||
2729 | 354 | </td> | ||
2730 | 355 | </tr> | ||
2731 | 356 | <tr> | ||
2732 | 357 | <td> | ||
2733 | 358 | <para style="P8"> | ||
2734 | 359 | <font face="Helvetica" >[[ repeatIn( get_taxes(o), 'tax' ) ]]</font> | ||
2735 | 360 | </para> | ||
2736 | 361 | <para style="P8"> | ||
2737 | 362 | <font face="Helvetica" >[[ tax[0] ]]: $</font> | ||
2738 | 363 | </para> | ||
2739 | 364 | </td> | ||
2740 | 365 | </tr> | ||
2741 | 366 | <tr> | ||
2742 | 367 | <td> | ||
2743 | 368 | <para style="P8"> | ||
2744 | 369 | <font face="Helvetica" >[[ repeatIn( get_taxes_ret(o), 'tax_ret' ) ]]</font> | ||
2745 | 370 | </para> | ||
2746 | 371 | <para style="P8"> | ||
2747 | 372 | <font face="Helvetica" >[[ tax_ret[0] ]] Ret: $</font> | ||
2748 | 373 | </para> | ||
2749 | 374 | </td> | ||
2750 | 375 | </tr> | ||
2751 | 376 | <tr> | ||
2752 | 377 | <td> | ||
2753 | 378 | <para style="P8">TOTAL: $</para> | ||
2754 | 379 | </td> | ||
2755 | 380 | </tr> | ||
2756 | 381 | </blockTable> | ||
2757 | 382 | |||
2758 | 383 | </td> | ||
2759 | 384 | <td> | ||
2760 | 385 | <blockTable rowHeights="10.0" style="Tabla4"> | ||
2761 | 386 | <tr> | ||
2762 | 387 | <td><para style="P7">[[formatLang( ( exists_key(o, 'global_discount_percent') and o.global_discount_amount or removeParentNode('tr') ) + (o.amount_untaxed or removeParentNode('tr')), digits=get_digits(dp='Account') ) or removeParentNode('tr')]]</para></td> | ||
2763 | 388 | </tr> | ||
2764 | 389 | <tr> | ||
2765 | 390 | <td><para style="P7">[[o.global_discount_amount and formatLang( o.global_discount_amount) or removeParentNode('tr')]]</para></td> | ||
2766 | 391 | </tr> | ||
2767 | 392 | <tr> | ||
2768 | 393 | <td><para style="P7">[[ formatLang(o.amount_untaxed) ]]</para></td> | ||
2769 | 394 | </tr> | ||
2770 | 395 | <tr> | ||
2771 | 396 | <td><para style="P7"> | ||
2772 | 397 | <font face="Helvetica">[[ repeatIn( get_taxes(o), 'tax' ) ]]</font> | ||
2773 | 398 | </para> | ||
2774 | 399 | <para style="P7"> | ||
2775 | 400 | <font face="Helvetica">[[ formatLang(float(tax[1])) ]]</font> | ||
2776 | 401 | </para> | ||
2777 | 402 | </td> | ||
2778 | 403 | </tr> | ||
2779 | 404 | <tr> | ||
2780 | 405 | <td><para style="P7"> | ||
2781 | 406 | <font face="Helvetica">[[ repeatIn( get_taxes_ret(o), 'tax_ret' ) ]]</font> | ||
2782 | 407 | </para> | ||
2783 | 408 | <para style="P7"> | ||
2784 | 409 | <font face="Helvetica">[[ formatLang(float(tax_ret[1])*-1) ]]</font> | ||
2785 | 410 | </para> | ||
2786 | 411 | </td> | ||
2787 | 412 | </tr> | ||
2788 | 413 | <tr> | ||
2789 | 414 | <td><para style="P7">[[ formatLang(o.amount_total) ]]</para></td> | ||
2790 | 415 | </tr> | ||
2791 | 416 | </blockTable> | ||
2792 | 417 | </td> | ||
2793 | 418 | </tr> | ||
2794 | 419 | </blockTable> | ||
2795 | 420 | |||
2796 | 421 | <blockTable colWidths="500.00"> | ||
2797 | 422 | <tr> | ||
2798 | 423 | <td><para style="P22">[[ (o.comment and format(o.comment )) or removeParentNode('blockTable') ]]</para></td> | ||
2799 | 424 | </tr> | ||
2800 | 425 | </blockTable> | ||
2801 | 426 | |||
2802 | 427 | <blockTable style="Tabla4"> | ||
2803 | 428 | <tr><td> | ||
2804 | 429 | <para style="P1">[[ (o.state == 'cancel' and 'CANCELADA') or removeParentNode('blockTable') ]]</para> | ||
2805 | 430 | </td></tr> | ||
2806 | 431 | </blockTable> | ||
2807 | 432 | |||
2808 | 433 | <blockTable colWidths="470.0,110.0" style="Tabla4"> | ||
2809 | 434 | <tr> | ||
2810 | 435 | <td> | ||
2811 | 436 | <blockTable colWidths="465.0" rowHeights="10.0,10.0,10.0,10.0,10.0,10.0,20.0" style="Tabla4"> | ||
2812 | 437 | <tr></tr> | ||
2813 | 438 | <tr> | ||
2814 | 439 | <td><para style="P12">SELLO DIGITAL DEL EMISOR</para></td> | ||
2815 | 440 | </tr> | ||
2816 | 441 | <tr style ="Tabla3"> | ||
2817 | 442 | <td><para style="P13">[[ split_string( o.sello or '') ]]</para></td> | ||
2818 | 443 | </tr> | ||
2819 | 444 | <tr> | ||
2820 | 445 | <td><para style="P12">SELLO DIGITAL DEL SAT:</para></td> | ||
2821 | 446 | </tr> | ||
2822 | 447 | <tr style ="Tabla3"> | ||
2823 | 448 | <td><para style="P13">[[ split_string( o.cfdi_sello or '') ]]</para></td> | ||
2824 | 449 | </tr> | ||
2825 | 450 | <tr> | ||
2826 | 451 | <td><para style="P12">CADENA ORIGINAL DEL COMPLEMENTO DE CERTIFICACION DIGITAL DEL SAT:</para></td> | ||
2827 | 452 | </tr> | ||
2828 | 453 | <tr style ="Tabla3" > | ||
2829 | 454 | <td><para style="P13">[[ split_string( o.cfdi_cadena_original or '' ) ]]</para></td> | ||
2830 | 455 | </tr> | ||
2831 | 456 | </blockTable> | ||
2832 | 457 | </td> | ||
2833 | 458 | <td> | ||
2834 | 459 | <blockTable style="Tabla4"> | ||
2835 | 460 | <tr> | ||
2836 | 461 | <td><para style="P13">[[ setTag('para','image',{'width':'3cm','height':'3cm'}) ]] [[ qrcode(o) ]]</para></td> | ||
2837 | 462 | </tr> | ||
2838 | 463 | </blockTable> | ||
2839 | 464 | </td> | ||
2840 | 465 | </tr> | ||
2841 | 466 | </blockTable> | ||
2842 | 467 | |||
2843 | 468 | <blockTable style="Tabla4"> | ||
2844 | 469 | <tr> | ||
2845 | 470 | <td><para style="P14"> [[ legend(o) ]] </para></td> | ||
2846 | 471 | </tr> | ||
2847 | 472 | </blockTable> | ||
2848 | 473 | </section> | ||
2849 | 474 | |||
2850 | 475 | </story> | ||
2851 | 476 | </document> | ||
2852 | 0 | \ No newline at end of file | 477 | \ No newline at end of file |
Hola Agustin,
Te respondo entre líneas.
El 13 de diciembre de 2013 13:05, Agustín <email address hidden> escribió:
> Agustín has proposed merging mexico- maintainer) /code.launchpad .net/~atin81/ openerp- mexico- localization/ pac-facturalo/ +merge/ 198988
> lp:~atin81/openerp-mexico-localization/pac-facturalo into
> lp:openerp-mexico-localization/7.0.
>
> Requested reviews:
> OpenERP Mexico Maintainer (openerp-
>
> For more details, see:
>
> https:/
>
> Agregamos un módulo para realizar la facturación electrónica con el pac
> factura-lo.mx.
>
¡Excelente!
> Falta hacer la cancelación de las facturas, pero subimos por anticipado el
> código para obtener retroalimentación departe de ustedes sobre lo que
> estamos desarrollando.
>
Ok, perfecto.
> De momento los todo's y preguntas que tenemos son las siguientes:
>
> 1.- De que manera podríamos poner a funcionar los dos módulos al mismo
> tiempo, creo que podría llegar a ser útil en algún momento para las
> empresas contar con dos servicios de facturación, uno principal y otro de
> respaldo, pero actualmente no es posible tener los dos módulos funcionando.
>
Para hacer funcionar 2 módulos al mismo tiempo de PAC's solo hay que hacer
pruebas, ya que está toda la arquitectura para que así se pueda hacer (pero
no se ha probado, todo es teoría), teniendo cuidado de definir los "type's"
en la secuencia y que coincida (como si fuera un driver, pues).
A lo mejor lo único que faltaría es tener un módulo de PAC dummy, para
agregar los campos que se utilizarán en común (analizando el caso).
>
> 2.- Nosotros en el módulo sobreescribimos el reporte de invoices que viene
> por defecto en OpenERP, de acuerdo a nuestra experiencia, contar con dos
> reportes es confuso para el usuario, sobre todo porque uno de ellos jamás
> se usa. Porqué no dejar únicamente un sólo reporte?
>
Aquí el tema es más profundo, lo que pasa, es que los módulos tienen que
cumplir con:
Multi-Company
Multi-Moneda
Multi-Localización
Multi-PAC
Multi-Usuario
Multi-*
En el caso de Multi-Localización, si todas las localizaciónes hiciéramos lo
que propones, todas sobre-escribirían el mismo reporte, y al final se
corrompería el reporte que se quiere mostrar. ¿Me explico?
Es decir, imagínate que yo uso el sistema en Venezuela y en México en una
misma base de datos multi-company y multi-localización, partiendo del
principio de que Venezuela no imprime facturación electrónica mexicana, no
deberían de verse perjudicados por este tema. Espero haberme explicado.
Pero, lo que tú dices es muy cierto, puede prestarse a confusión tener 2
reportes, aquí lo puedes solucionar, ocultando reportes, por medio de
permisos, a ciertos grupos o usuarios.
-- www.vauxoo. com
Moisés López Calderón
Vauxoo - OpenERP's Gold Partner
mobile: (+521) 477-752-22-30
Tel: (+52) 477-773-33-46
skype: moylop260
web: http://
twitter: @moylop260
@vauxoo