Merge lp:~atin81/openerp-mexico-localization/pac-facturalo into lp:openerp-mexico-localization/7.0

Proposed by Agustín
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
Reviewer Review Type Date Requested Status
Nhomar - Vauxoo Needs Fixing
Review via email: mp+198988@code.launchpad.net

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?

To post a comment you must log in.
Revision history for this message
Moisés López - http://www.vauxoo.com (moylop260) wrote :

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
> lp:~atin81/openerp-mexico-localization/pac-facturalo into
> lp:openerp-mexico-localization/7.0.
>
> Requested reviews:
> OpenERP Mexico Maintainer (openerp-mexico-maintainer)
>
> For more details, see:
>
> https://code.launchpad.net/~atin81/openerp-mexico-localization/pac-facturalo/+merge/198988
>
> 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.

--
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://www.vauxoo.com
twitter: @moylop260
            @vauxoo

Revision history for this message
Nhomar - Vauxoo (nhomar) wrote :
Download full text (3.5 KiB)

Hola Agustin.

Tengo varias dudas, pero ante todo muchas gracias por el aporte.

Dudas Operativas:

1.- No veo a factura-lo aquí:
 http://www.sat.gob.mx/sitio_internet/asistencia_contribuyente/principiantes/comprobantes_fiscales/66_19264.html

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_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml debería estar en "demo" no en "data" en todo caso debería configurarse con un Wizard al momento de colocar la data, no cableada como se encuentra en éste momento. ¿Por qué? sucede que los valores necesarios para la operación en una base de datos para producción por buenas prácticas no deberían pre-configurarse de forma errónea. Te puedes basar en los wizards ir.config para mayor información, o verifica los wizards de configuración del módulo de pacsf.

4.- Lo mismo para l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml son datos contables que afectarían en 1 click una base de datos de producción.

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.algoenespanol.del.ano suena feo verdad? siempre es buena práctica colocar las variables en inglés. NOTA. Si tienes esto en producción éste cambio específico lo puedes hacer en un branch diferente al que uses hasta que quede mergeado.

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...

Read more...

review: Needs Fixing
Revision history for this message
Agustín (atin81) wrote :
Download full text (6.2 KiB)

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://www.sat.gob.mx/sitio_internet/asistencia_contribuyente/principiantes/comprobantes_fiscales/66_19264.html
>
> 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_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml debería estar en "demo" no en "data" en todo caso debería configurarse con un Wizard al momento de colocar la data, no cableada como se encuentra en éste momento. ¿Por qué? sucede que los valores necesarios para la operación en una base de datos para producción por buenas prácticas no deberían pre-configurarse de forma errónea. Te puedes basar en los wizards ir.config para mayor información, o verifica los wizards de configuración del módulo de pacsf.
>
> 4.- Lo mismo para l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml son datos contables que afectarían en 1 click una base de datos de producción.
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...

Read more...

Revision history for this message
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://www.linkedin.com/e/sqgs3h-ht1ot3ke-6z/isd/5852814049212915712/cUZVZuw1/?hs=false&tok=3XAq7FJdGxTS81

--
Estás recibiendo invitaciones a conectar. Haz clic para darte de baja:
http://www.linkedin.com/e/sqgs3h-ht1ot3ke-6z/G033PJVLL4ZD8LelAGsEZFXLYsGx-mf4TSBKG4EWHO7/goo/mp%2B198988%40code%2Elaunchpad%2Enet/20061/I6733902597_1/?hs=false&tok=1mjuzrexuxTS81

(c) 2012 LinkedIn Corporation. 2029 Stierlin Ct, Mountain View, CA 94043, EE.UU.

299. By Agustin Cruz <email address hidden>

Merged changes done on git until commit 87e9a70ac

300. By Agustin Cruz <email address hidden>

Bringing changes from version 0.3.5

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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== added directory 'l10n_mx_facturae_pac_facturalo'
=== added file 'l10n_mx_facturae_pac_facturalo/__init__.py'
--- l10n_mx_facturae_pac_facturalo/__init__.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/__init__.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,29 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25import invoice
26import report
27import params_pac
28import ir_sequence_approval
29import ir_attachment_facturae
030
=== added file 'l10n_mx_facturae_pac_facturalo/__init__.pyc'
1Binary 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 differ31Binary 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
=== added file 'l10n_mx_facturae_pac_facturalo/__openerp__.py'
--- l10n_mx_facturae_pac_facturalo/__openerp__.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/__openerp__.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,77 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustin Cruz Lozano (agustin.cruz@openpyme.mx)
9# Verónica Paleta (veronica.paleta@openpyme.mx)
10#
11# This program is free software: you can redistribute it and/or modify
12# it under the terms of the GNU Affero General Public License as
13# published by the Free Software Foundation, either version 3 of the
14# License, or (at your option) any later version.
15#
16# This program is distributed in the hope that it will be useful,
17# but WITHOUT ANY WARRANTY; without even the implied warranty of
18# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19# GNU Affero General Public License for more details.
20#
21# You should have received a copy of the GNU Affero General Public License
22# along with this program. If not, see <http://www.gnu.org/licenses/>.
23#
24##############################################################################
25
26{
27 "name" : "Factura Electronica Mexico - Control de Comprobantes Digitales",
28 "version" : "1.0",
29 "author" : "OpenPyme",
30 "category" : "Localization/Mexico",
31 "description" : """This module allows use the electronic invoice service
32provided by control de Comprobantes Digitales S. de R.L. de C.V.
33Instructions for installation:
34- Ubuntu:
35sudo apt-get install xmlsec1 libxml2-dev libxslt-dev libxmlsec-dev
36sudo apt-set install python-dev python-fpconst
37sudo easy_install lxml pyxmlsec
38- Archlinux:
39yaourt -S python2-lxml python2-fpconst pyxmlsec
40- Centos:
41sudo yum install libxml2 libxml2-devel libxslt libxslt-dev
42sudo yum install python-devel python-fpconst pyxmlsec
43All operative systems:
44- Python Soappy
45 cd /tmp
46 git clone https://github.com/Fedrojesa/SOAPpy.git
47 cd SOAPPy
48 python setup.py build
49 sudo python setup.py install
50
51- Python qrcode (Todos los sistemas operativos)\n
52 cd /tmp
53 git clone https://github.com/lincolnloop/python-qrcode.git
54 cd python-qrcode
55 python setup.py build
56 sudo python setup.py install
57""",
58 "website" : "http://www.openpyme.mx/",
59 "license" : "AGPL-3",
60 "depends" : ["l10n_mx_facturae_groups",
61 "l10n_mx_params_pac",
62 "l10n_mx_account_tax_category",
63 "l10n_mx_facturae_seq",
64 "l10n_mx_ir_attachment_facturae",
65 "l10n_mx_facturae_pac",
66 "l10n_mx_facturae_group_show_wizards",
67 "account_cancel"
68 ],
69 "demo" : [],
70 "data" : [
71 "data/l10n_mx_facturae_pac_facturalo_report.xml",
72 "data/l10n_mx_facturae_pac_facturalo.xml",
73 "data/l10n_mx_facturae_seq.xml"
74 ],
75 "installable" : True,
76 "active" : False,
77}
078
=== added directory 'l10n_mx_facturae_pac_facturalo/data'
=== added file 'l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml'
--- l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo.xml 2014-06-25 00:35:08 +0000
@@ -0,0 +1,24 @@
1<?xml version="1.0" ?>
2<openerp>
3
4 <data noupdate="1">
5 <record id="params_pac_facturalo_cancelar" model="params.pac">
6 <field name="method_type">pac_facturalo_cancelar</field>
7 <field name="url_webservice">http://cancelacion.expidetufactura.com.mx:8080/cancelacion/CancelacionPruebas?wsdl</field>
8 <field name="namespace">http://service.timbrado.cim.mx/</field>
9 <field name="user">pruebas</field>
10 <field name="password">123456</field>
11 <field name="name">Facturalo - Cancelar</field>
12 </record>
13
14 <record id="params_pac_facturalo_firmar" model="params.pac">
15 <field name="method_type">pac_facturalo_firmar</field>
16 <field name="url_webservice">http://timbrado.expidetufactura.com.mx:8080/pruebas/TimbradoWS?wsdl</field>
17 <field name="namespace">http://service.timbrado.cim.mx/</field>
18 <field name="user">pruebas</field>
19 <field name="password">123456</field>
20 <field name="name">Facturalo - Firmar</field>
21 </record>
22 </data>
23
24</openerp>
025
=== added file 'l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml'
--- l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_pac_facturalo_report.xml 2014-06-25 00:35:08 +0000
@@ -0,0 +1,18 @@
1<?xml version="1.0" encoding="utf-8"?>
2<openerp>
3 <data>
4 <report
5 auto="False"
6 id="l10n_mx_facturae_report.account_invoice_facturae_webkit"
7 model="account.invoice"
8 name="account.invoice.facturae.webkit"
9 rml="l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml"
10 string="Factura Electronica Report"
11 report_type="pdf"
12 header="False"
13 attachment="(object.state in ('open','paid')) and (object.fname_invoice and (object.fname_invoice + ''))"
14 attachment_use="True"
15 />
16 </data>
17
18</openerp>
019
=== added file 'l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml'
--- l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/data/l10n_mx_facturae_seq.xml 2014-06-25 00:35:08 +0000
@@ -0,0 +1,37 @@
1<?xml version="1.0" ?>
2<openerp>
3 <data noupdate="1">
4 <record id="l10n_mx_facturae_ir_seq_01" model="ir.sequence">
5 <field name="company_id" ref="base.main_company"/>
6 <field name="name">Sequence CFDI</field>
7 <field name="active" eval="True"/>
8 <field name="padding">0</field>
9 <field name="number_next">1</field>
10 <field name="number_increment">1</field>
11 <field name="implementation">standard</field>
12 </record>
13
14 <record id="l10n_mx_facturae_ir_seq_approval_01" model="ir.sequence.approval">
15 <field name="company_id" ref="base.main_company"/>
16 <field name="sequence_id" ref="l10n_mx_facturae_ir_seq_01"/>
17 <field name="approval_number">12345</field>
18 <field name="serie">A</field>
19 <field name="approval_year" eval="time.strftime('%Y')"/>
20 <field name="number_start">1</field>
21 <field name="number_end">9999</field>
22 <field name="type">cfdi32</field>
23 </record>
24
25 <record id="l10n_mx_facturae_account_journal_01" model="account.journal">
26 <field name="company_id" ref="base.main_company"/>
27 <field name="sequence_id" ref="l10n_mx_facturae_ir_seq_01"/>
28 <field name="name">Diario de CFDI</field>
29 <field name="code">CFDI</field>
30 <field name="type">sale</field>
31 <field name="update_posted">1</field>
32 <field name="user_id" ref="base.user_root"/>
33 <field name="company2_id" ref="base.main_company"/>
34 <field name="currency" ref="base.MXN"/>
35 </record>
36 </data>
37</openerp>
038
=== added directory 'l10n_mx_facturae_pac_facturalo/i18n'
=== added file 'l10n_mx_facturae_pac_facturalo/i18n/es_MX.po'
--- l10n_mx_facturae_pac_facturalo/i18n/es_MX.po 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/i18n/es_MX.po 2014-06-25 00:35:08 +0000
@@ -0,0 +1,275 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * l10n_mx_facturae_pac_facturalo
4# es_MX.po <agustin.cruz@openpy.mx>, 2014.
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2014-06-25 00:21+0000\n"
10"PO-Revision-Date: 2014-06-24 19:25-0500\n"
11"Last-Translator: es_MX.po <agustin.cruz@openpy.mx>\n"
12"Language-Team: Laptop\n"
13"Language: es\n"
14"MIME-Version: 1.0\n"
15"Content-Type: text/plain; charset=UTF-8\n"
16"Content-Transfer-Encoding: \n"
17"Plural-Forms: nplurals=2; plural=(n != 1);\n"
18"X-Generator: Virtaal 0.7.1\n"
19
20#. module: l10n_mx_facturae_pac_facturalo
21#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:354
22#, python-format
23msgid "WARNING, CANCEL IN TEST!!!!\n"
24"\n"
25""
26msgstr ""
27"PRECACIÓN, CANCELCIÓN HECHA EN PRUEBAS !!!\n"
28"\n"
29
30#. module: l10n_mx_facturae_pac_facturalo
31#: field:account.invoice,cfdi_folio_fiscal:0
32msgid "CFD-I Folio Fiscal"
33msgstr ""
34
35#. module: l10n_mx_facturae_pac_facturalo
36#: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:36
37#, python-format
38msgid "PAC Factura-lo - Sign"
39msgstr "Firma - PAC Factura-lo"
40
41#. module: l10n_mx_facturae_pac_facturalo
42#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_params_pac
43msgid "params.pac"
44msgstr ""
45
46#. module: l10n_mx_facturae_pac_facturalo
47#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:137
48#, python-format
49msgid "Press in the button 'Upload File'"
50msgstr ""
51
52#. module: l10n_mx_facturae_pac_facturalo
53#: help:account.invoice,cfdi_cadena_original:0
54msgid "Original String used in the electronic invoice"
55msgstr "Cadena Original usada en la factura electrónica"
56
57#. module: l10n_mx_facturae_pac_facturalo
58#: help:account.invoice,cfdi_folio_fiscal:0
59msgid "Folio used in the electronic invoice"
60msgstr "Foluo usado en la factura electrónica"
61
62#. module: l10n_mx_facturae_pac_facturalo
63#: help:account.invoice,cfdi_no_certificado:0
64msgid "Serial Number of the Certificate"
65msgstr "Número de Serie del Certificado"
66
67#. module: l10n_mx_facturae_pac_facturalo
68#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:385
69#, python-format
70msgid "codMensaje"
71msgstr ""
72
73#. module: l10n_mx_facturae_pac_facturalo
74#: field:account.invoice,cfdi_fecha_timbrado:0
75msgid "CFD-I Fecha Timbrado"
76msgstr "CFD-I Fecha Timbrado"
77
78#. module: l10n_mx_facturae_pac_facturalo
79#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:182
80#, python-format
81msgid "Not Vat Number set on partner"
82msgstr "No se ha asignado un RFC en la ficha del cliente"
83
84#. module: l10n_mx_facturae_pac_facturalo
85#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:247
86#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:379
87#, python-format
88msgid "Can't get XML file from PAC."
89msgstr "No se pudo obtener el archivo XML del PAC."
90
91#. module: l10n_mx_facturae_pac_facturalo
92#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:311
93#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:388
94#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:400
95#, python-format
96msgid "Stamped Code: %s. \n"
97" Stamped Message: %s."
98msgstr ""
99"Código devuelto: %s.\n"
100"Mensaje devuelto: %s."
101
102#. module: l10n_mx_facturae_pac_facturalo
103#: help:account.invoice,cfdi_fecha_timbrado:0
104msgid "Date when is stamped the electronic invoice"
105msgstr "Fecha en que fue firmada la factura electrónica"
106
107#. module: l10n_mx_facturae_pac_facturalo
108#: help:account.invoice,cfdi_sello:0
109msgid "Sign assigned by the SAT"
110msgstr "Sello asignado por el SAT"
111
112#. module: l10n_mx_facturae_pac_facturalo
113#: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:37
114#, python-format
115msgid "PAC Factura-lo - Cancel"
116msgstr ""
117
118#. module: l10n_mx_facturae_pac_facturalo
119#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:185
120#, python-format
121msgid "Customer Address Not Invoice Type"
122msgstr "La dirección del cliente no es de tipo Invoice"
123
124#. module: l10n_mx_facturae_pac_facturalo
125#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:537
126#, python-format
127msgid "Not captured a CERTIFICATE file in format PEM, in the company!"
128msgstr ""
129
130#. module: l10n_mx_facturae_pac_facturalo
131#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:551
132#, python-format
133msgid "Check date of invoice and the validity of certificate"
134msgstr "Revise la fecha de la factura y la fecha de validez del certificado"
135
136#. module: l10n_mx_facturae_pac_facturalo
137#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:242
138#, python-format
139msgid "SIN FOLIO O ESTATUS NO VALIDO\n"
140""
141msgstr ""
142
143#. module: l10n_mx_facturae_pac_facturalo
144#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:249
145#, python-format
146msgid "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"
147msgstr ""
148
149#. module: l10n_mx_facturae_pac_facturalo
150#: field:account.invoice,cfdi_sello:0
151msgid "CFD-I Sello"
152msgstr "Sello CFDI"
153
154#. module: l10n_mx_facturae_pac_facturalo
155#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:246
156#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:378
157#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:387
158#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:399
159#, python-format
160msgid "Error"
161msgstr ""
162
163#. module: l10n_mx_facturae_pac_facturalo
164#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:536
165#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546
166#, python-format
167msgid "Error !"
168msgstr ""
169
170#. module: l10n_mx_facturae_pac_facturalo
171#: field:account.invoice,cfdi_fecha_cancelacion:0
172msgid "CFD-I Fecha Cancelacion"
173msgstr ""
174
175#. module: l10n_mx_facturae_pac_facturalo
176#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:550
177#, python-format
178msgid "Warning !"
179msgstr ""
180
181#. module: l10n_mx_facturae_pac_facturalo
182#: field:account.invoice,cfdi_cadena_original:0
183msgid "CFD-I Cadena Original"
184msgstr ""
185
186#. module: l10n_mx_facturae_pac_facturalo
187#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:396
188#, python-format
189msgid "UUID No existe"
190msgstr ""
191
192#. module: l10n_mx_facturae_pac_facturalo
193#: help:account.invoice,cfdi_fecha_cancelacion:0
194msgid "If the invoice is cancel, this field saved the date when is cancel"
195msgstr ""
196
197#. module: l10n_mx_facturae_pac_facturalo
198#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:394
199#, python-format
200msgid "UUID No corresponde al emisor"
201msgstr ""
202
203#. module: l10n_mx_facturae_pac_facturalo
204#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:347
205#, python-format
206msgid "Not found information from web services of PAC, verify that the configuration of PAC is correct"
207msgstr ""
208
209#. module: l10n_mx_facturae_pac_facturalo
210#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:395
211#, python-format
212msgid "UUID No aplicable para cancelación"
213msgstr ""
214
215#. module: l10n_mx_facturae_pac_facturalo
216#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:220
217#, python-format
218msgid "Not found information from web services of PACVerify that the configuration of PAC is correct"
219msgstr ""
220
221#. module: l10n_mx_facturae_pac_facturalo
222#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_sequence_approval
223msgid "ir.sequence.approval"
224msgstr ""
225
226#. module: l10n_mx_facturae_pac_facturalo
227#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546
228#, python-format
229msgid "Not captured a KEY file in format PEM, in the company!"
230msgstr ""
231
232#. module: l10n_mx_facturae_pac_facturalo
233#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:219
234#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:310
235#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:346
236#, python-format
237msgid "Warning"
238msgstr ""
239
240#. module: l10n_mx_facturae_pac_facturalo
241#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_attachment_facturae_mx
242msgid "Email Thread"
243msgstr ""
244
245#. module: l10n_mx_facturae_pac_facturalo
246#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_account_invoice
247msgid "Invoice"
248msgstr ""
249
250#. module: l10n_mx_facturae_pac_facturalo
251#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:239
252#, python-format
253msgid "Número de aprobación SICOFI: %s\n"
254""
255msgstr ""
256
257#. module: l10n_mx_facturae_pac_facturalo
258#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:243
259#, python-format
260msgid "La reproducción apócrifa de este comprobante constituye un delito en los términos de las disposiciones fiscales.\n"
261""
262msgstr ""
263
264#. module: l10n_mx_facturae_pac_facturalo
265#: field:account.invoice,cfdi_no_certificado:0
266msgid "CFD-I Certificado"
267msgstr ""
268
269#. module: l10n_mx_facturae_pac_facturalo
270#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:228
271#, python-format
272msgid "WARNING, SIGNED IN TEST!!!!\n"
273"\n"
274""
275msgstr ""
0276
=== added file 'l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot'
--- l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/i18n/l10n_mx_facturae_pac_facturalo.pot 2014-06-25 00:35:08 +0000
@@ -0,0 +1,270 @@
1# Translation of OpenERP Server.
2# This file contains the translation of the following modules:
3# * l10n_mx_facturae_pac_facturalo
4#
5msgid ""
6msgstr ""
7"Project-Id-Version: OpenERP Server 7.0\n"
8"Report-Msgid-Bugs-To: \n"
9"POT-Creation-Date: 2014-06-25 00:21+0000\n"
10"PO-Revision-Date: 2014-06-25 00:21+0000\n"
11"Last-Translator: <>\n"
12"Language-Team: \n"
13"MIME-Version: 1.0\n"
14"Content-Type: text/plain; charset=UTF-8\n"
15"Content-Transfer-Encoding: \n"
16"Plural-Forms: \n"
17
18#. module: l10n_mx_facturae_pac_facturalo
19#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:354
20#, python-format
21msgid "WARNING, CANCEL IN TEST!!!!\n"
22"\n"
23""
24msgstr ""
25
26#. module: l10n_mx_facturae_pac_facturalo
27#: field:account.invoice,cfdi_folio_fiscal:0
28msgid "CFD-I Folio Fiscal"
29msgstr ""
30
31#. module: l10n_mx_facturae_pac_facturalo
32#: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:36
33#, python-format
34msgid "PAC Factura-lo - Sign"
35msgstr ""
36
37#. module: l10n_mx_facturae_pac_facturalo
38#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_params_pac
39msgid "params.pac"
40msgstr ""
41
42#. module: l10n_mx_facturae_pac_facturalo
43#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:137
44#, python-format
45msgid "Press in the button 'Upload File'"
46msgstr ""
47
48#. module: l10n_mx_facturae_pac_facturalo
49#: help:account.invoice,cfdi_cadena_original:0
50msgid "Original String used in the electronic invoice"
51msgstr ""
52
53#. module: l10n_mx_facturae_pac_facturalo
54#: help:account.invoice,cfdi_folio_fiscal:0
55msgid "Folio used in the electronic invoice"
56msgstr ""
57
58#. module: l10n_mx_facturae_pac_facturalo
59#: help:account.invoice,cfdi_no_certificado:0
60msgid "Serial Number of the Certificate"
61msgstr ""
62
63#. module: l10n_mx_facturae_pac_facturalo
64#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:385
65#, python-format
66msgid "codMensaje"
67msgstr ""
68
69#. module: l10n_mx_facturae_pac_facturalo
70#: field:account.invoice,cfdi_fecha_timbrado:0
71msgid "CFD-I Fecha Timbrado"
72msgstr ""
73
74#. module: l10n_mx_facturae_pac_facturalo
75#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:182
76#, python-format
77msgid "Not Vat Number set on partner"
78msgstr ""
79
80#. module: l10n_mx_facturae_pac_facturalo
81#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:247
82#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:379
83#, python-format
84msgid "Can't get XML file from PAC."
85msgstr ""
86
87#. module: l10n_mx_facturae_pac_facturalo
88#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:311
89#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:388
90#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:400
91#, python-format
92msgid "Stamped Code: %s. \n"
93" Stamped Message: %s."
94msgstr ""
95
96#. module: l10n_mx_facturae_pac_facturalo
97#: help:account.invoice,cfdi_fecha_timbrado:0
98msgid "Date when is stamped the electronic invoice"
99msgstr ""
100
101#. module: l10n_mx_facturae_pac_facturalo
102#: help:account.invoice,cfdi_sello:0
103msgid "Sign assigned by the SAT"
104msgstr ""
105
106#. module: l10n_mx_facturae_pac_facturalo
107#: code:addons/l10n_mx_facturae_pac_facturalo/params_pac.py:37
108#, python-format
109msgid "PAC Factura-lo - Cancel"
110msgstr ""
111
112#. module: l10n_mx_facturae_pac_facturalo
113#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:185
114#, python-format
115msgid "Customer Address Not Invoice Type"
116msgstr ""
117
118#. module: l10n_mx_facturae_pac_facturalo
119#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:537
120#, python-format
121msgid "Not captured a CERTIFICATE file in format PEM, in the company!"
122msgstr ""
123
124#. module: l10n_mx_facturae_pac_facturalo
125#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:551
126#, python-format
127msgid "Check date of invoice and the validity of certificate"
128msgstr ""
129
130#. module: l10n_mx_facturae_pac_facturalo
131#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:242
132#, python-format
133msgid "SIN FOLIO O ESTATUS NO VALIDO\n"
134""
135msgstr ""
136
137#. module: l10n_mx_facturae_pac_facturalo
138#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:249
139#, python-format
140msgid "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"
141msgstr ""
142
143#. module: l10n_mx_facturae_pac_facturalo
144#: field:account.invoice,cfdi_sello:0
145msgid "CFD-I Sello"
146msgstr ""
147
148#. module: l10n_mx_facturae_pac_facturalo
149#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:246
150#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:378
151#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:387
152#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:399
153#, python-format
154msgid "Error"
155msgstr ""
156
157#. module: l10n_mx_facturae_pac_facturalo
158#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:536
159#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546
160#, python-format
161msgid "Error !"
162msgstr ""
163
164#. module: l10n_mx_facturae_pac_facturalo
165#: field:account.invoice,cfdi_fecha_cancelacion:0
166msgid "CFD-I Fecha Cancelacion"
167msgstr ""
168
169#. module: l10n_mx_facturae_pac_facturalo
170#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:550
171#, python-format
172msgid "Warning !"
173msgstr ""
174
175#. module: l10n_mx_facturae_pac_facturalo
176#: field:account.invoice,cfdi_cadena_original:0
177msgid "CFD-I Cadena Original"
178msgstr ""
179
180#. module: l10n_mx_facturae_pac_facturalo
181#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:396
182#, python-format
183msgid "UUID No existe"
184msgstr ""
185
186#. module: l10n_mx_facturae_pac_facturalo
187#: help:account.invoice,cfdi_fecha_cancelacion:0
188msgid "If the invoice is cancel, this field saved the date when is cancel"
189msgstr ""
190
191#. module: l10n_mx_facturae_pac_facturalo
192#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:394
193#, python-format
194msgid "UUID No corresponde al emisor"
195msgstr ""
196
197#. module: l10n_mx_facturae_pac_facturalo
198#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:347
199#, python-format
200msgid "Not found information from web services of PAC, verify that the configuration of PAC is correct"
201msgstr ""
202
203#. module: l10n_mx_facturae_pac_facturalo
204#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:395
205#, python-format
206msgid "UUID No aplicable para cancelación"
207msgstr ""
208
209#. module: l10n_mx_facturae_pac_facturalo
210#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:220
211#, python-format
212msgid "Not found information from web services of PACVerify that the configuration of PAC is correct"
213msgstr ""
214
215#. module: l10n_mx_facturae_pac_facturalo
216#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_sequence_approval
217msgid "ir.sequence.approval"
218msgstr ""
219
220#. module: l10n_mx_facturae_pac_facturalo
221#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:546
222#, python-format
223msgid "Not captured a KEY file in format PEM, in the company!"
224msgstr ""
225
226#. module: l10n_mx_facturae_pac_facturalo
227#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:219
228#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:310
229#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:346
230#, python-format
231msgid "Warning"
232msgstr ""
233
234#. module: l10n_mx_facturae_pac_facturalo
235#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_ir_attachment_facturae_mx
236msgid "Email Thread"
237msgstr ""
238
239#. module: l10n_mx_facturae_pac_facturalo
240#: model:ir.model,name:l10n_mx_facturae_pac_facturalo.model_account_invoice
241msgid "Invoice"
242msgstr ""
243
244#. module: l10n_mx_facturae_pac_facturalo
245#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:239
246#, python-format
247msgid "Número de aprobación SICOFI: %s\n"
248""
249msgstr ""
250
251#. module: l10n_mx_facturae_pac_facturalo
252#: code:addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py:243
253#, python-format
254msgid "La reproducción apócrifa de este comprobante constituye un delito en los términos de las disposiciones fiscales.\n"
255""
256msgstr ""
257
258#. module: l10n_mx_facturae_pac_facturalo
259#: field:account.invoice,cfdi_no_certificado:0
260msgid "CFD-I Certificado"
261msgstr ""
262
263#. module: l10n_mx_facturae_pac_facturalo
264#: code:addons/l10n_mx_facturae_pac_facturalo/invoice.py:228
265#, python-format
266msgid "WARNING, SIGNED IN TEST!!!!\n"
267"\n"
268""
269msgstr ""
270
0271
=== added file 'l10n_mx_facturae_pac_facturalo/invoice.py'
--- l10n_mx_facturae_pac_facturalo/invoice.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/invoice.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,560 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25import logging
26import base64
27import string
28from datetime import datetime
29from pytz import timezone
30import pytz
31import re
32import time
33
34from openerp.tools.translate import _
35from openerp.osv import fields, osv
36from openerp import tools
37
38
39logger = logging.getLogger(__name__)
40
41try:
42 from SOAPpy import WSDL
43except:
44 logger.error("Package SOAPpy missed")
45
46
47class account_invoice(osv.osv):
48 _inherit = 'account.invoice'
49
50 _columns = {
51 'cfdi_sello': fields.text('CFD-I Sello', help='Sign assigned by the SAT'),
52 'cfdi_no_certificado': fields.char('CFD-I Certificado', size=32,
53 help='Serial Number of the Certificate'),
54 'cfdi_cadena_original': fields.text('CFD-I Cadena Original',
55 help='Original String used in the electronic invoice'),
56 'cfdi_fecha_timbrado': fields.datetime('CFD-I Fecha Timbrado',
57 help='Date when is stamped the electronic invoice'),
58 'cfdi_fecha_cancelacion': fields.datetime('CFD-I Fecha Cancelacion',
59 help='If the invoice is cancel, this field saved the date when is cancel'),
60 'cfdi_folio_fiscal': fields.char('CFD-I Folio Fiscal', size=64,
61 help='Folio used in the electronic invoice'),
62 }
63
64 def write_cfd_data(self, cr, uid, ids, cfd_data=None, context=None):
65 """
66 @param cfd_datas : Dictionary with data that is used in facturae CFDI
67 """
68 if not cfd_data:
69 cfd_data = {}
70
71 comprobante = self._get_type_sequence(cr, uid, ids, context=context)
72
73 noCertificado = cfd_data.get(comprobante, {}).get('noCertificado', '')
74 certificado = cfd_data.get(comprobante, {}).get('certificado', '')
75 sello = cfd_data.get(comprobante, {}).get('sello', '')
76 cadena_original = cfd_data.get('cadena_original', '')
77 data = {
78 'no_certificado': noCertificado,
79 'certificado': certificado,
80 'sello': sello,
81 'cadena_original': cadena_original,
82 }
83 self.write(cr, uid, ids, data, context=context)
84 return True
85
86 def copy(self, cr, uid, id, default={}, context=None):
87 if context is None:
88 context = {}
89 default.update({
90 'cfdi_cbb': False,
91 'cfdi_sello': False,
92 'cfdi_no_certificado': False,
93 'cfdi_cadena_original': False,
94 'cfdi_fecha_timbrado': False,
95 'cfdi_folio_fiscal': False,
96 'cfdi_fecha_cancelacion': False,
97 })
98 return super(account_invoice, self).copy(cr, uid, id, default, context)
99
100 def action_cancel_draft(self, cr, uid, ids, *args):
101 self.write(cr, uid, ids, {
102 'cfdi_sello':False,
103 'cfdi_no_certificado':False,
104 'cfdi_cadena_original':False,
105 'cfdi_fecha_timbrado': False,
106 'cfdi_folio_fiscal': False,
107 'cfdi_fecha_cancelacion': False,
108 })
109 return super(account_invoice, self).action_cancel_draft(cr, uid, ids, args)
110
111 def _get_file(self, cr, uid, inv_ids, context={}):
112 if not context:
113 context = {}
114 id = inv_ids[0]
115 invoice = self.browse(cr, uid, [id], context=context)[0]
116 fname_invoice = invoice.fname_invoice and invoice.fname_invoice + \
117 '.xml' or ''
118 aids = self.pool.get('ir.attachment').search(cr, uid, [(
119 'datas_fname', '=', invoice.fname_invoice + '.xml'), (
120 'res_model', '=', 'account.invoice'), ('res_id', '=', id)])
121 xml_data = ""
122 if aids:
123 brow_rec = self.pool.get('ir.attachment').browse(cr, uid, aids[0])
124 if brow_rec.datas:
125 xml_data = base64.decodestring(brow_rec.datas)
126 else:
127 fname, xml_data = self._get_facturae_invoice_xml_data(
128 cr, uid, inv_ids, context=context)
129 self.pool.get('ir.attachment').create(cr, uid, {
130 'name': fname_invoice,
131 'datas': base64.encodestring(xml_data),
132 'datas_fname': fname_invoice,
133 'res_model': 'account.invoice',
134 'res_id': invoice.id,
135 }, context=context)
136 self.fdata = base64.encodestring(xml_data)
137 msg = _("Press in the button 'Upload File'")
138 return {'file': self.fdata, 'fname': fname_invoice,
139 'name': fname_invoice, 'msg': msg}
140
141 def add_node(self, node_name=None, attrs=None, parent_node=None,
142 minidom_xml_obj=None, attrs_types=None, order=False):
143 """
144 @params node_name : Name node to added
145 @params attrs : Attributes to add in node
146 @params parent_node : Node parent where was add new node children
147 @params minidom_xml_obj : File XML where add nodes
148 @params attrs_types : Type of attributes added in the node
149 @params order : If need add the params in order in the XML, add a
150 list with order to params
151 """
152 if not order:
153 order = attrs
154 new_node = minidom_xml_obj.createElement(node_name)
155 for key in order:
156 if attrs_types[key] == 'attribute':
157 new_node.setAttribute(key, attrs[key])
158 elif attrs_types[key] == 'textNode':
159 key_node = minidom_xml_obj.createElement(key)
160 text_node = minidom_xml_obj.createTextNode(attrs[key])
161
162 key_node.appendChild(text_node)
163 new_node.appendChild(key_node)
164 parent_node.appendChild(new_node)
165 return new_node
166
167 def _get_type_sequence(self, cr, uid, ids, context=None):
168 ir_seq_app_obj = self.pool.get('ir.sequence.approval')
169 invoice = self.browse(cr, uid, ids[0], context=context)
170 sequence_app_id = ir_seq_app_obj.search(cr, uid, [(
171 'sequence_id', '=', invoice.invoice_sequence_id.id)], context=context)
172 type_inv = 'cfd22'
173 if sequence_app_id:
174 type_inv = ir_seq_app_obj.browse(
175 cr, uid, sequence_app_id[0], context=context).type
176 if type_inv == 'cfdi32':
177 comprobante = 'cfdi:Comprobante'
178 else:
179 comprobante = 'Comprobante'
180 return comprobante
181
182 def _get_time_zone(self, cr, uid, invoice_id, context=None):
183 res_users_obj = self.pool.get('res.users')
184 userstz = res_users_obj.browse(cr, uid, [uid])[0].partner_id.tz
185 a = 0
186 if userstz:
187 hours = timezone(userstz)
188 fmt = '%Y-%m-%d %H:%M:%S %Z%z'
189 now = datetime.now()
190 loc_dt = hours.localize(datetime(now.year, now.month, now.day,
191 now.hour, now.minute, now.second))
192 timezone_loc = (loc_dt.strftime(fmt))
193 diff_timezone_original = timezone_loc[-5:-2]
194 timezone_original = int(diff_timezone_original)
195 s = str(datetime.now(pytz.timezone(userstz)))
196 s = s[-6:-3]
197 timezone_present = int(s) * -1
198 a = timezone_original + ((
199 timezone_present + timezone_original) * -1)
200 return a
201
202 def _upload_ws_file(self, cr, uid, invoice_id, fdata=None, context={}):
203 """
204 @params
205 invoice_id: Id from invoice to validate
206 fdata : File.xml base64 codificated
207 """
208 pac_params_obj = self.pool.get('params.pac')
209 invoice = self.browse(cr, uid, invoice_id, context=context)[0]
210 f = False
211 msg = ''
212 pac_ids = pac_params_obj.search(cr, uid,
213 [('method_type', '=', 'pac_facturalo_firmar'),
214 ('company_id', '=', invoice.company_emitter_id.id),
215 ('active', '=', True)
216 ], limit=1, context=context)
217
218 if not pac_ids:
219 raise osv.except_osv(_('Warning'),
220 _('Not found information from web services of PAC'
221 'Verify that the configuration of PAC is correct')
222 )
223
224 # Get pac params for use on this invoice
225 pac = pac_params_obj.browse(cr, uid, pac_ids, context)[0]
226
227 if pac.user == 'pruebas':
228 msg += _(u'WARNING, SIGNED IN TEST!!!!\n\n')
229
230 try:
231 wsdl = WSDL.SOAPProxy(pac.url_webservice, pac.namespace)
232
233 # Webservice configuration
234 wsdl.soapproxy.config.dumpSOAPOut = 0
235 wsdl.soapproxy.config.dumpSOAPIn = 0
236 wsdl.soapproxy.config.debug = 0
237 wsdl.soapproxy.config.dict_encoding = 'UTF-8'
238
239 # Timbrar factura
240 resultado = wsdl.timbrar(usuario=pac.user,
241 contrasena=pac.password,
242 cfdi=fdata)
243 except Exception, e:
244 # Error al establecer comunicación con el PAC
245 logger.error(str(e))
246 raise osv.except_osv(_('Error'),
247 _(u"Can't get XML file from PAC."))
248
249 # Procesar los resultados obtenidos del PAC
250 mensaje = _(tools.ustr(resultado['mensaje']))
251 logger.debug(str(resultado))
252 valid_codes = ['200', '504']
253 if resultado['codigo'] in valid_codes:
254 # Proceso satisfactorio
255 timbre = resultado['timbre']
256 regex = '<cfdi:Complemento>(.*?)</cfdi:Complemento>'
257 timbref = re.search(regex, resultado['timbre']).group(1)
258
259 # Obtiene el sello del SAT
260 try:
261 selloSAT = re.search('selloSAT="(.+?)" ', timbref).group(1)
262 except:
263 selloSAT = ''
264
265 # Obtiene el nuúmero de certificado
266 try:
267 no_certificado = re.search('noCertificadoSAT="(.+?)" ', timbref).group(1)
268 except:
269 no_certificado = ''
270
271 # Obtiene la fecha en que se timbó la factura
272 try:
273 fecha = re.search('FechaTimbrado="(.+?)" ', timbref).group(1)
274 except:
275 fecha = ''
276
277 # Obtiene el folio de la factura
278 try:
279 uuid = re.search('UUID="(.+?)" ', timbref).group(1)
280 except:
281 uuid = ''
282
283 # Obtiene la cadena original
284 try:
285 version = re.search('version="(.+?)" ', timbref).group(1)
286 cadena = ('|').join([version,
287 uuid,
288 invoice.date_invoice_tz,
289 invoice.sello,
290 no_certificado])
291 cadena = "||{0}||".format(cadena)
292 except:
293 cadena = ''
294
295 cfdi_data = {
296 'cfdi_sello': selloSAT,
297 'cfdi_no_certificado': no_certificado,
298 'cfdi_cadena_original': cadena,
299 'cfdi_fecha_timbrado': fecha,
300 'cfdi_folio_fiscal': uuid,
301 }
302 self.write(cr, uid, invoice_id, cfdi_data)
303
304 # Codifica el XML para almacenarlo
305 f = base64.encodestring(timbre or '')
306
307 msg += string.join([mensaje, ". Folio Fiscal ", uuid, "."])
308 else:
309 # El CFDI no ha sido timbrado
310 raise osv.except_osv(_(u'Warning'),
311 _(u'Stamped Code: %s. \n Stamped Message: %s.') %
312 (resultado['codigo'], mensaje))
313
314 return {'file': f, 'msg': msg, 'cfdi_xml': timbre}
315
316 def _get_file_cancel(self, cr, uid, inv_ids, context={}):
317 inv_ids = inv_ids[0]
318 atta_obj = self.pool.get('ir.attachment')
319 atta_id = atta_obj.search(cr, uid, [('res_id', '=', inv_ids), (
320 'name', 'ilike', '%.xml')], context=context)
321 if atta_id:
322 atta_brw = atta_obj.browse(cr, uid, atta_id, context)[0]
323 inv_xml = atta_brw.datas or False
324 else:
325 inv_xml = False
326 raise osv.except_osv(('State of Cancellation!'), (
327 "This invoice hasn't stamped, so that not possible cancel."))
328 return {'file': inv_xml}
329
330 def sf_cancel(self, cr, uid, inv_ids, context=None):
331 msg = ''
332 invoice_id = inv_ids[0]
333 pac_params_obj = self.pool.get('params.pac')
334 invoice = self.browse(cr, uid, invoice_id, context=context)
335 xml_data = self.cancel_invoice_xml(cr, uid, invoice_id, context=context)
336
337 # ****************** upload file encoded at PAC ******************************************
338
339 pac_ids = pac_params_obj.search(cr, uid,
340 [('method_type', '=', 'pac_facturalo_cancelar'),
341 ('company_id', '=', invoice.company_emitter_id.id),
342 ('active', '=', True)
343 ], limit=1, context=context)
344
345 if not pac_ids:
346 raise osv.except_osv(_('Warning'),
347 _('Not found information from web services of PAC, verify that the configuration of PAC is correct'))
348
349 pac = pac_params_obj.browse(cr, uid, pac_ids, context)[0]
350
351 #####################################################################
352
353 if pac.user == 'pruebas':
354 msg += _(u'WARNING, CANCEL IN TEST!!!!\n\n')
355
356 try:
357 from SOAPpy import Types
358
359 wsdl = WSDL.SOAPProxy(pac.url_webservice, pac.namespace)
360
361 # Webservice configuration
362 wsdl.soapproxy.config.dumpSOAPOut = 0
363 wsdl.soapproxy.config.dumpSOAPIn = 0
364 wsdl.soapproxy.config.debug = 0
365 wsdl.config.preventescape = 1
366 wsdl.soapproxy.config.dict_encoding = 'UTF-8'
367
368 # SUBIR factura al PAC
369 resultado = wsdl.cancelar(usuario=pac.user,
370 # For test environment uncomment this and comment contrasena
371 token=pac.password,
372 # contrasena=pac.password,
373 xmlBytes=Types.base64BinaryType(xml_data))
374
375 except Exception, e:
376 # Error al establecer comunicación con el PAC
377 logger.debug(str(e))
378 raise osv.except_osv(_('Error'),
379 _(u"Can't get XML file from PAC."))
380
381 #######################################################################
382 # Procesar los resultados obtenidos del PAC
383
384 if not(resultado['codEstatus'] == '200'):
385 mensaje = _(resultado['codMensaje'])
386 # error
387 raise osv.except_osv(_(u'Error'),
388 _(u'Stamped Code: %s. \n Stamped Message: %s.') %
389 (resultado['codEstatus'], mensaje))
390
391 cancel_status = ['201', '202']
392 if not(resultado['estatusUUIDs'] in cancel_status):
393 errors = {
394 '203':_('UUID No corresponde al emisor'),
395 '204':_('UUID No aplicable para cancelación'),
396 '205':_('UUID No existe')
397 }
398 # error
399 raise osv.except_osv(_(u'Error'),
400 _(u'Stamped Code: %s. \n Stamped Message: %s.') %
401 (resultado['estatusUUIDs'],
402 errors[resultado['estatusUUIDs']]))
403
404 # Invoice canceled, save the cancelation date on database
405 self.write(cr, uid, [invoice_id], {'cfdi_fecha_cancelacion':time.strftime('%Y-%m-%d %H:%M:%S')}, context=None)
406
407 return {'message':msg, 'status':resultado['codEstatus']}
408
409 def cancel_invoice_dict(self, cr, uid, ids, context={}):
410 """ This function create a dictionary with the necesary data to create the DigestValue.
411 @params
412 default params
413 """
414 from lxml import etree
415 from openerp import SUPERUSER_ID
416
417 # get user's timezone
418 user_pool = self.pool.get('res.users')
419 user = user_pool.browse(cr, SUPERUSER_ID, uid)
420 tz = timezone(user.partner_id.tz) or pytz.utc
421
422 # get localized dates
423 date = datetime.now(pytz.utc).astimezone(tz)
424
425
426 invoice = self.browse(cr, uid, ids, context=context)
427 xmlns = "http://cancelacfd.sat.gob.mx"
428 xsd = 'http://www.w3.org/2001/XMLSchema'
429 xsi = 'http://www.w3.org/2001/XMLSchema-instance'
430 signed_xmlns = 'http://www.w3.org/2000/09/xmldsig#'
431
432 nsmap = {None: xmlns,
433 'xsd':xsd,
434 'xsi':xsi}
435
436 signed_nsmap = {None: signed_xmlns,
437 'xsd':xsd,
438 'xsi':xsi}
439 # section: Cancelation
440 # create XML
441 cancelacion = etree.Element('Cancelacion', nsmap=nsmap)
442 cancelacion.set('Fecha', date.strftime('%Y-%m-%dT%H:%M:%S'))
443 cancelacion.set('RfcEmisor', invoice.company_id.partner_id.vat_split)
444
445 folios = etree.Element('Folios')
446 uuid = etree.Element('UUID')
447 uuid.text = invoice.cfdi_folio_fiscal
448 folios.append(uuid)
449 cancelacion.append(folios)
450
451 signature = etree.Element('Signature', nsmap={None:signed_xmlns})
452 signedinfo = etree.Element('SignedInfo', nsmap=signed_nsmap)
453
454 canonicalizacion = etree.Element('CanonicalizationMethod')
455 canonicalizacion.set('Algorithm', "http://www.w3.org/TR/2001/REC-xml-c14n-20010315")
456 signaturemethod = etree.Element('SignatureMethod')
457 signaturemethod.set('Algorithm', "http://www.w3.org/2000/09/xmldsig#rsa-sha1")
458 reference = etree.Element('Reference')
459 reference.set('URI', "")
460 transforms = etree.Element('Transforms')
461 transform = etree.Element('Transform')
462 transform.set('Algorithm', "http://www.w3.org/2000/09/xmldsig#enveloped-signature")
463 digestme = etree.Element('DigestMethod')
464 digestme.set('Algorithm', "http://www.w3.org/2000/09/xmldsig#sha1")
465 digestva = etree.Element('DigestValue')
466 digestva.text = 'TEMPLATE'
467
468 signedinfo.append(canonicalizacion)
469 signedinfo.append(signaturemethod)
470 signedinfo.append(reference)
471
472 transforms.append(transform)
473 reference.append(transforms)
474 reference.append(digestme)
475 reference.append(digestva)
476 signature.append(signedinfo)
477
478 signaturevalue = etree.Element('SignatureValue')
479 signaturevalue.text = 'TEMPLATE'
480 signature.append(signaturevalue)
481
482 keyinfo = etree.Element('KeyInfo')
483 # Issuername & Serialnumber not added because library
484 # auto fill the fields on X509IssuerSerial
485 # see www.aleksey.com/pipermail/xmlsec/2007/008125.html
486 x509 = etree.Element('X509Data')
487 xISerial = etree.Element('X509IssuerSerial')
488 xISerial.text = ''
489 xCertificate = etree.Element('X509Certificate')
490 xCertificate.text = ''
491
492 x509.append(xISerial)
493 x509.append(xCertificate)
494 keyinfo.append(x509)
495 signature.append(keyinfo)
496
497 cancelacion.append(signature)
498 # pretty string
499 logger.debug('XML file %s' % etree.tostring(cancelacion))
500 return etree.tostring(cancelacion)
501
502 def cancel_invoice_xml(self, cr, uid, ids, context=None):
503 """ This function call at cancel_invoice_dict to cancel a invoice.
504 @params
505 default params
506 """
507 import pyxmldsig
508 import tempfile
509
510 invoice = self.browse(cr, uid, ids, context=context)
511 pool_company = self.pool.get('res.company')
512
513 data_xml = self.cancel_invoice_dict(cr, uid, ids, context=context)
514
515 new_file = tempfile.NamedTemporaryFile(delete=False)
516 new_file.write(data_xml)
517 new_file.close()
518
519 cert_id = pool_company._get_current_certificate(cr, uid, [invoice.company_emitter_id.id],
520 context=context)[invoice.company_emitter_id.id]
521
522 cert_id = cert_id and self.pool.get('res.company.facturae.certificate') \
523 .browse(cr, uid, [cert_id], context=context)[0] or False
524
525 # Get certificate files for sign the xml
526 fname_cer_pem = fname_key_pem = False
527 name = 'openerp_' + (cert_id.serial_number or '') + '__certificate__'
528 if cert_id:
529 try:
530
531 fname_cer_pem = self.binary2file(cr, uid, ids,
532 cert_id.certificate_file_pem,
533 name,
534 '.cer.pem')
535 except:
536 raise osv.except_osv(_('Error !'),
537 _('Not captured a CERTIFICATE file in format PEM, in the company!'))
538
539
540 try:
541 fname_key_pem = self.binary2file(cr, uid, ids,
542 cert_id.certificate_key_file_pem,
543 name,
544 '.key.pem')
545 except:
546 raise osv.except_osv(_('Error !'), _(
547 'Not captured a KEY file in format PEM, in the company!'))
548
549 else:
550 raise osv.except_osv(_('Warning !'),
551 _('Check date of invoice and the validity of certificate'))
552
553 signed_xml = pyxmldsig.sign_file(template_file=new_file.name,
554 cert_file=fname_cer_pem.encode('ascii', 'ignore'),
555 key_file=fname_key_pem.encode('ascii', 'ignore'),
556 password=cert_id.certificate_password.encode('ascii', 'ignore'))
557
558 logger.debug('Signed file %s' % signed_xml)
559
560 return signed_xml # data_dict
0561
=== added file 'l10n_mx_facturae_pac_facturalo/invoice.pyc'
1Binary 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 differ562Binary 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
=== added file 'l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py'
--- l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,40 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25from openerp.osv import fields, osv
26
27
28class ir_attachment_facturae_mx(osv.osv):
29 _inherit = 'ir.attachment.facturae.mx'
30
31 def _get_type(self, cr, uid, ids=None, context=None):
32 types = super(ir_attachment_facturae_mx, self)._get_type(
33 cr, uid, ids, context=context)
34 types.extend([('cfdi32', 'CFDI 3.2')])
35 return types
36
37 _columns = {
38 'type': fields.selection(_get_type, 'Type', type='char', size=64,
39 required=True, readonly=True, help="Type of Electronic Invoice"),
40 }
041
=== added file 'l10n_mx_facturae_pac_facturalo/ir_attachment_facturae.pyc'
1Binary 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 differ42Binary 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
=== added file 'l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py'
--- l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/ir_sequence_approval.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,43 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25from openerp.osv import fields, osv
26
27
28class ir_sequence_approval(osv.osv):
29 _inherit = 'ir.sequence.approval'
30
31 def _get_type(self, cr, uid, ids=None, context=None):
32 types = super(ir_sequence_approval, self)._get_type(
33 cr, uid, ids, context=context)
34 """TODO: Encontrar una forma en la que se pueda tener
35 instalado al mismo tiempo este módulo y el de SF
36 """
37 types.extend([('cfdi32', 'CFDI 3.2')])
38 return types
39
40 _columns = {
41 'type': fields.selection(_get_type, 'Type', type='char', size=64,
42 required=True, help="Type of Electronic Invoice"),
43 }
044
=== added file 'l10n_mx_facturae_pac_facturalo/ir_sequence_approval.pyc'
1Binary 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 differ45Binary 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
=== added file 'l10n_mx_facturae_pac_facturalo/params_pac.py'
--- l10n_mx_facturae_pac_facturalo/params_pac.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/params_pac.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,45 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25from openerp.osv import fields, osv
26from openerp.tools.translate import _
27
28
29class params_pac(osv.osv):
30 _inherit = 'params.pac'
31
32 def _get_method_type_selection(self, cr, uid, context=None):
33 types = super(params_pac, self)._get_method_type_selection(
34 cr, uid, context=context)
35 types.extend([
36 ('pac_facturalo_firmar', _('PAC Factura-lo - Sign')),
37 ('pac_facturalo_cancelar', _('PAC Factura-lo - Cancel')),
38 ])
39 return types
40
41 _columns = {
42 'method_type': fields.selection(_get_method_type_selection,
43 "Process to perform", type='char', size=64, required=True,
44 help='Type of process to configure in this pac'),
45 }
046
=== added file 'l10n_mx_facturae_pac_facturalo/params_pac.pyc'
1Binary 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 differ47Binary 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
=== added file 'l10n_mx_facturae_pac_facturalo/pyxmldsig.py'
--- l10n_mx_facturae_pac_facturalo/pyxmldsig.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/pyxmldsig.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,578 @@
1"""
2pyxmldsig.py:
3
4A Python module to create and verify XML Digital Signatures (XML-DSig)
5
6This is a simple interface to the PyXMLSec library, aiming to provide a more
7pythonic API suitable for Python applications.
8See http://www.decalage.info/python/pyxmldsig to download the latest version.
9
10May be used as a command-line tool or as a Python module.
11This code is inspired from PyXMLSec samples, with a more pythonic interface:
12http://pyxmlsec.labs.libre-entreprise.org/index.php?section=examples
13
14AUTHOR: Philippe Lagadec (decalage at laposte dot net)
15
16PROJECT WEBSITE: http://www.decalage.info/python/pyxmldsig
17
18LICENSE:
19
20Copyright (c) 2009-2010, Philippe Lagadec (decalage at laposte dot net)
21
22Permission to use, copy, modify, and/or distribute this software for any
23purpose with or without fee is hereby granted, provided that the above copyright
24notice and this permission notice appear in all copies.
25
26THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
27REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
28FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
29INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
30LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
31OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
32PERFORMANCE OF THIS SOFTWARE.
33
34
35USAGE AS A TOOL:
36pyxmldsig.py <data.xml> -k <key-file.pem> [-c cert-file.pem] [-p password]
37
38USAGE IN A PYTHON APPLICATION:
39
40import pyxmldsig
41
42# simple function interface:
43signed_xml = pyxmldsig.sign_file(template_file='myfile.xml',
44 key_file='mykey.pem', cert_file='myx509cert.pem', password='mypassword')
45print signed_xml
46
47# sign with class interface:
48xdsig = pyxmldsig.Xmldsig(key_file='mykey.pem', cert_file='myx509cert.pem',
49 password='mypassword')
50signed_xml1 = xdsig.sign_file('myfile.xml')
51signed_xml2 = xdsig.sign_file(pyxmldsig.TEMPLATE_WITH_CERT)
52
53# verify with class interface:
54xdsig2 = pyxmldsig.Xmldsig()
55xdsig2.load_certs(['cacert.pem', 'myx509cert.pem'])
56assert xdsig2.verify_xmlstring(signed_xml1) == True
57assert xdsig2.verify_xmlstring(signed_xml2) == True
58
59REQUIREMENTS:
60- pyxmlsec: http://pyxmlsec.labs.libre-entreprise.org/
61- xmlsec: http://www.aleksey.com/xmlsec/
62- libxml2: http://xmlsoft.org
63- On Windows see also this site for convenient compiled binaries:
64 http://returnbooleantrue.blogspot.com/2009/04/pyxmlsec-windows-binary.html
65
66REFERENCES:
67- http://www.w3.org/TR/xmldsig-core/
68"""
69
70__version__ = '0.05'
71
72#=== CHANGELOG ================================================================
73
74# 2009-09-10 v0.01 PL: - first version, inspired from pyxmlsec samples
75# 2009-09-14 v0.02 PL: - small improvements, license
76# 2010-05-11 v0.03 PL: - renamed to pyxmldsig
77# - X509 cert is now optional
78# - a key password may be provided
79# 2010-06-29 v0.04 PL: - added Xmldsig class to load a key once for several
80# signatures
81# - added signature verification
82# - added simple XML-DSIG templates
83# 2010-07-06 v0.05 PL: - added load_cert to load several certificates at once
84
85#=== TODO =====================================================================
86
87# - add option to generate XML-DSig template automatically, appended to a chosen
88# node.
89# - add option to improve detached signature with http URI: fix URI after
90# signature.
91# - check if all temporary xmlsec objects are destroyed after each operation
92# - find a solution to load a certificate with a key name to allow signature
93# verification without embedded X509 cert
94# - add option to use keys manager or single key?
95
96#=== IMPORTS ==================================================================
97
98import sys
99
100try:
101 import libxml2
102except ImportError:
103 raise ImportError, "libxml2 is required: see http://xmlsoft.org"
104
105try:
106 import xmlsec
107except ImportError:
108 raise ImportError, "pyxmlsec is required: see http://pyxmlsec.labs.libre-entreprise.org"
109
110
111#=== CONSTANTS ================================================================
112
113# XML Signature template with X509 certificate:
114# - the X.509 cert tag must be empty, else another one will be appended
115# - KeyName is optional
116TEMPLATE_WITH_CERT = \
117"""<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
118<SignedInfo>
119 <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
120 <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
121 <Reference URI="">
122 <Transforms>
123 <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
124 </Transforms>
125 <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
126 <DigestValue>TEMPLATE</DigestValue>
127 </Reference>
128</SignedInfo>
129<SignatureValue>TEMPLATE</SignatureValue>
130<KeyInfo>
131 <KeyName/>
132 <X509Data>
133 <X509Certificate></X509Certificate>
134 </X509Data>
135</KeyInfo>
136</Signature>
137"""
138
139# XML Signature template without X509 certificate:
140# - KeyInfo / KeyName is optional
141TEMPLATE_WITHOUT_CERT = \
142"""<Signature xmlns="http://www.w3.org/2000/09/xmldsig#">
143<SignedInfo>
144 <CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"/>
145 <SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"/>
146 <Reference URI="">
147 <Transforms>
148 <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"/>
149 </Transforms>
150 <DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"/>
151 <DigestValue>TEMPLATE</DigestValue>
152 </Reference>
153</SignedInfo>
154<SignatureValue>TEMPLATE</SignatureValue>
155<KeyInfo>
156 <KeyName/>
157</KeyInfo>
158</Signature>
159"""
160
161#=== CLASSES ==================================================================
162
163class Xmldsig (object):
164 """
165 class to sign and verify XML signatures (XML DSig)
166 """
167
168 def __init__(self, key_file=None, cert_file=None, password='', key_name=None):
169 """
170 - key_file: str, filename of PEM file containing the private key.
171 (the file should NOT be password-protected)
172 - cert_file: str, filename of PEM file containing the X509 certificate.
173 (optional: can be None)
174 - password: str, password to open key file, or None if no password.
175 - key_name: str, name for the key in the signature, or None if omitted.
176 """
177 self.dsig_ctx = None
178 # TEST: single key
179 self.key = None
180 # Create and initialize keys manager
181 self.keysmngr = xmlsec.KeysMngr()
182 if self.keysmngr is None:
183 raise RuntimeError, "Error: failed to create keys manager."
184 if xmlsec.cryptoAppDefaultKeysMngrInit(self.keysmngr) < 0:
185 self.keysmngr.destroy()
186 raise RuntimeError, "Error: failed to initialize keys manager."
187 # load key
188 self.load(key_file, cert_file, password, key_name)
189
190
191 def load(self, key_file=None, cert_file=None, password='', key_name=None):
192 """
193 load a private key and/or a public certificate for signature and verification
194
195 - key_file: str, filename of PEM file containing the private key.
196 (the file should NOT be password-protected)
197 - cert_file: str, filename of PEM file containing the X509 certificate.
198 (optional: can be None)
199 - password: str, password to open key file, or None if no password.
200 """
201 # TODO: try except block to destroy key if error
202 if key_file is not None:
203 # Load private key, with optional password
204 # print 'PASSWORD: %s' % password
205 key = xmlsec.cryptoAppKeyLoad(filename=key_file,
206 format=xmlsec.KeyDataFormatPem, pwd=password,
207 pwdCallback=None, pwdCallbackCtx=None)
208 # API references:
209 # http://pyxmlsec.labs.libre-entreprise.org/docs/html/xmlsec-module.html#cryptoAppKeyLoad
210 # http://www.aleksey.com/xmlsec/api/xmlsec-app.html#XMLSECCRYPTOAPPKEYLOAD
211 # http://www.aleksey.com/xmlsec/api/xmlsec-keysdata.html#XMLSECKEYDATAFORMAT
212 if key is None:
213 raise RuntimeError, "Error: failed to load private PEM key from \"%s\"" % key_file
214 if key_name is not None:
215 # Set key name
216 if key.setName(key_name) < 0:
217 raise RuntimeError, "Error: failed to set key name to \"%s\"" % key_name
218 if cert_file is not None:
219 # Load certificate and add to the key
220 if xmlsec.cryptoAppKeyCertLoad(key, cert_file, xmlsec.KeyDataFormatPem) < 0:
221 raise RuntimeError, "Error: failed to load PEM certificate \"%s\"" % cert_file
222 # load key into manager:
223 if xmlsec.cryptoAppDefaultKeysMngrAdoptKey(self.keysmngr, key) < 0:
224 raise RuntimeError, "Error: failed to load key into keys manager"
225
226 elif cert_file is not None:
227 # case when we only want to load a cert without private key
228 if self.keysmngr.certLoad(cert_file, xmlsec.KeyDataFormatPem,
229 xmlsec.KeyDataTypeTrusted) < 0:
230 # is it better to keep the keys manager if an error occurs?
231 # self.keysmngr.destroy()
232 raise RuntimeError, "Error: failed to load PEM certificate from \"%s\"" % cert_file
233 # THIS DOES NOT WORK: it seems a certificate cannot be loaded like a key with a name...
234# # key = xmlsec.cryptoAppKeyLoad(filename = cert_file,
235# # format = xmlsec.KeyDataFormatPem, pwd = password,
236# # pwdCallback = None, pwdCallbackCtx = None)
237# # if key is None:
238# # raise RuntimeError, "Error: failed to load PEM certificate from \"%s\"", cert_file
239# # if key_name is not None:
240# # # Set key name
241# # if key.setName(key_name) < 0:
242# # raise RuntimeError, "Error: failed to set key name to \"%s\"" % key_name
243# # # load key into manager:
244# # if xmlsec.cryptoAppDefaultKeysMngrAdoptKey(self.keysmngr, key) < 0:
245# # raise RuntimeError, "Error: failed to load certificate into keys manager"
246
247 def load_certs(self, certificates):
248 """
249 load one or several certificates into the keys manager for signature
250 verification. For example, load the CA cert, any number of intermediate
251 certs, and the cert corresponding to the key used for the signature.
252
253 certificates: list or tuple containing certificate file names
254 """
255 for cert in certificates:
256 self.load(cert_file=cert)
257
258
259 def sign_file (self, template_file):
260 """
261 Sign a XML file using the signature template in the XML file.
262 The certificate from cert_file is placed in the <dsig:X509Data/> node.
263
264 - template_file: str, filename of XML file containing an XML-DSig template.
265
266 Returns a string containing the signed XML data.
267 Raises an exception if an error occurs.
268 """
269 xmlstring = open(template_file).read()
270 return self.sign_xmlstring(xmlstring)
271
272
273 def sign_xmlstring (self, xmlstring):
274 """
275 Sign xmlstring using the signature template in xmlstring.
276 The certificate from cert_file is placed in the <dsig:X509Data/> node.
277
278 - xmlstring: str, XML data containing an XML-DSig template.
279
280 Returns a string containing the signed XML data.
281 Raises an exception if an error occurs.
282 """
283 # try block to ensure cleanup is called even if an exception is raised:
284 try:
285 # Create signature context
286 self._create_context()
287 # Load template
288 doc = self._parse_xmlstring(xmlstring)
289 # find the XML-DSig start node
290 node = xmlsec.findNode(doc.getRootElement(), xmlsec.NodeSignature,
291 xmlsec.DSigNs)
292 if node is None:
293 raise RuntimeError, "Error: XML-DSIG node not found"
294 # Sign the template
295 if self.dsig_ctx.sign(node) < 0:
296 raise RuntimeError, "Error: signature failed"
297 output = str(doc)
298 output = output.replace('\n', '')
299 finally:
300 # cleanup, even if an exception has been raised:
301 self._cleanup_context()
302 if doc is not None:
303 doc.freeDoc()
304 # return output if no exception was raised:
305 return output
306
307
308 def verify_file (self, xmlfile):
309 """
310 Verify signature in XML file using the loaded certificate.
311
312 - xmlfile: str, filename of XML file containing an XML-DSig signature.
313
314 Returns True if the signature is valid, False otherwise.
315 Raises an exception if an error occurs.
316 """
317 xmlstring = open(xmlfile).read()
318 return self.verify_xmlstring(xmlstring)
319
320
321 def verify_xmlstring (self, xmlstring):
322 """
323 Verify signature in xmlstring using the loaded certificate.
324
325 - xmlstring: str, XML data containing an XML-DSig signature.
326
327 Returns True if the signature is valid, False otherwise.
328 Raises an exception if an error occurs.
329 """
330 doc = None
331 # try block to ensure cleanup is called even if an exception is raised:
332 try:
333 # Create signature context
334 self._create_context()
335 # Load XML data
336 doc = self._parse_xmlstring(xmlstring)
337 # find the XML-DSig start node
338 node = xmlsec.findNode(doc.getRootElement(), xmlsec.NodeSignature,
339 xmlsec.DSigNs)
340 if node is None:
341 raise RuntimeError, "Error: XML-DSIG node not found"
342 # Verify signature
343 if self.dsig_ctx.verify(node) < 0:
344 # An error occured, the signature could not be verified
345 raise RuntimeError, "Error: An error occured, the signature could not be verified"
346 if self.dsig_ctx.status == xmlsec.DSigStatusSucceeded:
347 # Signature is OK
348 return True
349 else:
350 # Signature is INVALID
351 return False
352 finally:
353 # cleanup, even if an exception has been raised:
354 self._cleanup_context()
355 if doc is not None:
356 doc.freeDoc()
357 # return output if no exception was raised:
358 return output
359
360
361 def _parse_xmlstring(self, xmlstring):
362 """
363 parse XML string containing XML-DSIG nodes for signature (template) or
364 verification (signed data)
365 """
366 # doc = libxml2.parseFile(tmpl_file)
367 doc = libxml2.parseDoc(xmlstring)
368 if doc is None or doc.getRootElement() is None:
369 raise RuntimeError, "Error: unable to parse XML data"
370 return doc
371
372
373 def _create_context(self):
374 """
375 create the xmlsec context for signature or verification
376 """
377 self.dsig_ctx = xmlsec.DSigCtx(self.keysmngr)
378 if self.dsig_ctx is None:
379 raise RuntimeError, "Error: failed to create signature context"
380
381
382 def _cleanup_context (self):
383 """
384 cleanup the xmlsec context in case of error
385 """
386 if self.dsig_ctx is not None:
387 self.dsig_ctx.destroy()
388 self.dsig_ctx = None
389
390
391
392
393#=== FUNCTIONS ================================================================
394
395def sign_file(template_file, key_file, cert_file=None, password='', key_name=None):
396 """
397 Sign a XML file using private key from key_file and the signature template
398 in the XML file.
399 The certificate from cert_file is placed in the <dsig:X509Data/> node.
400
401 - template_file: str, filename of XML file containing an XML-DSig template.
402 - key_file: str, filename of PEM file containing the private key.
403 (the file should NOT be password-protected)
404 - cert_file: str, filename of PEM file containing the X509 certificate.
405 (optional: can be None)
406 - password: str, password to open key file, or None if no password.
407
408 Returns a string containing the signed XML data.
409 Raises an exception if an error occurs.
410 """
411 xmldsig = Xmldsig(key_file, cert_file, password, key_name)
412 return xmldsig.sign_file(template_file)
413# # xmlstring = open(template_file).read()
414# # return sign_xmlstring(xmlstring, key_file, cert_file, password)
415
416
417def sign_xmlstring(xmlstring, key_file, cert_file=None, password='', key_name=None):
418 """
419 Sign xmlstring using private key from key_file and the signature template
420 in xmlstring.
421 The certificate from cert_file is placed in the <dsig:X509Data/> node.
422
423 - xmlstring: str, XML data containing an XML-DSig template.
424 - key_file: str, filename of PEM file containing the private key.
425 (the file should NOT be password-protected)
426 - cert_file: str, filename of PEM file containing the X509 certificate.
427 (optional: can be None)
428 - password: str, password to open key file, or "" if no password.
429 (never use None because libxmlsec will ask on the console)
430
431 Returns a string containing the signed XML data.
432 Raises an exception if an error occurs.
433 """
434 xmldsig = Xmldsig(key_file, cert_file, password, key_name)
435 return xmldsig.sign_xmlstring(xmlstring)
436# # # Load template
437# # #doc = libxml2.parseFile(tmpl_file)
438# # doc = libxml2.parseDoc(xmlstring)
439# # if doc is None or doc.getRootElement() is None:
440# # raise RuntimeError, "Error: unable to parse XML data"
441# #
442# # # try block to ensure cleanup is called even if an exception is raised:
443# # try:
444# # dsig_ctx = None
445# #
446# # # Find XML-DSig start node
447# # node = xmlsec.findNode(doc.getRootElement(), xmlsec.NodeSignature,
448# # xmlsec.DSigNs)
449# # if node is None:
450# # raise RuntimeError, "Error: start node not found"
451# #
452# # # Create signature context, we don't need keys manager in this example
453# # dsig_ctx = xmlsec.DSigCtx()
454# # if dsig_ctx is None:
455# # raise RuntimeError, "Error: failed to create signature context"
456# #
457# # # Load private key, assuming that there is not password
458# # #print 'PASSWORD: %s' % password
459# # key = xmlsec.cryptoAppKeyLoad(filename = key_file,
460# # format = xmlsec.KeyDataFormatPem, pwd = password,
461# # pwdCallback = None, pwdCallbackCtx = None)
462# # # API references:
463# # # http://pyxmlsec.labs.libre-entreprise.org/docs/html/xmlsec-module.html#cryptoAppKeyLoad
464# # # http://www.aleksey.com/xmlsec/api/xmlsec-app.html#XMLSECCRYPTOAPPKEYLOAD
465# # # http://www.aleksey.com/xmlsec/api/xmlsec-keysdata.html#XMLSECKEYDATAFORMAT
466# # if key is None:
467# # raise RuntimeError, "Error: failed to load private PEM key from \"%s\"" % key_file
468# # dsig_ctx.signKey = key
469# #
470# # if cert_file is not None:
471# # # Load certificate and add to the key
472# # ## if not check_filename(cert_file):
473# # ## return cleanup(doc, dsig_ctx)
474# # if xmlsec.cryptoAppKeyCertLoad(key, cert_file, xmlsec.KeyDataFormatPem) < 0:
475# # raise RuntimeError, "Error: failed to load PEM certificate \"%s\"" % cert_file
476# #
477# # # Set key name to the file name, this is just an example!
478# # if key.setName(key_file) < 0:
479# # raise RuntimeError, "Error: failed to set key name for key from \"%s\"" % key_file
480# #
481# # # Sign the template
482# # if dsig_ctx.sign(node) < 0:
483# # raise RuntimeError, "Error: signature failed"
484# #
485# # ## # Print signed document to stdout
486# # ## doc.dump("-")
487# # ## doc.saveFile("test.xml")
488# # output = str(doc)
489# # finally:
490# # # cleanup, even if an exception has been raised:
491# # cleanup(doc, dsig_ctx)
492# # # return output if no exception was raised:
493# # return output
494
495
496# #def cleanup(doc=None, dsig_ctx=None, res=-1):
497# # """
498# # Cleans libxml2 context after usage.
499# # """
500# # if dsig_ctx is not None:
501# # dsig_ctx.destroy()
502# # if doc is not None:
503# # doc.freeDoc()
504# # return res
505
506
507def _init():
508 """
509 Initialize necessary libraries (libxml2 and xmlsec).
510 Should be called once only: this is automatic when this module is imported.
511 Raises an exception if an error occurs.
512 """
513 # Init libxml library
514 libxml2.initParser()
515 libxml2.substituteEntitiesDefault(1)
516 # Init xmlsec library
517 assert xmlsec.init() >= 0, "Error: xmlsec initialization failed."
518 # Check loaded library version
519 assert xmlsec.checkVersion() == 1, "Error: loaded xmlsec library version is not compatible."
520 # Init crypto library
521 assert xmlsec.cryptoAppInit(None) >= 0, "Error: crypto initialization failed."
522 # Init xmlsec-crypto library
523 assert xmlsec.cryptoInit() >= 0, "Error: xmlsec-crypto initialization failed."
524
525
526def shutdown():
527 """
528 Shutdown all libraries cleanly.
529 Should only be called at the end of all xmlsec actions.
530 """
531 # Shutdown xmlsec-crypto library
532 xmlsec.cryptoShutdown()
533 # Shutdown crypto library
534 xmlsec.cryptoAppShutdown()
535 # Shutdown xmlsec library
536 xmlsec.shutdown()
537 # Shutdown LibXML2
538 libxml2.cleanupParser()
539
540
541#=== MAIN =====================================================================
542
543# always initialize the xmlsec and libxml2 libraries:
544_init()
545
546def main():
547 """
548 To use this module as a command-line tool.
549 """
550 from optparse import OptionParser
551 usage = "usage: %prog [options] file.xml"
552 parser = OptionParser(usage=usage, version='%prog ' + __version__)
553 parser.add_option("-k", "--keyfile",
554 metavar="KEYFILE", help="PEM file containing private key",
555 action="store", type="string", dest="keyfile")
556 parser.add_option("-c", "--certfile",
557 metavar="CERTFILE", help="PEM file containing the X.509 certificate",
558 action="store", type="string", dest="certfile")
559 parser.add_option("-p", "--password", default='', # not None!
560 metavar="PASSWORD", help="Password of the private key file",
561 action="store", type="string", dest="password")
562 (options, args) = parser.parse_args()
563
564 if len(args) != 1 and not options.keyfile:
565 print __doc__
566 parser.print_help()
567 sys.exit()
568
569 signed_xml = sign_file(template_file=args[0], key_file=options.keyfile,
570 cert_file=options.certfile, password=options.password)
571 print signed_xml
572 shutdown()
573
574
575if __name__ == "__main__":
576 main()
577
578# This code was developed while listening to Fleet Foxes
0579
=== added directory 'l10n_mx_facturae_pac_facturalo/report'
=== added file 'l10n_mx_facturae_pac_facturalo/report/__init__.py'
--- l10n_mx_facturae_pac_facturalo/report/__init__.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/report/__init__.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,25 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25import account_print_invoice
026
=== added file 'l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py'
--- l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/report/account_print_invoice.py 2014-06-25 00:35:08 +0000
@@ -0,0 +1,266 @@
1# -*- encoding: utf-8 -*-
2###########################################################################
3# Module Writen to OpenERP, Open Source Management Solution
4#
5# Copyright (c) 2013 OpenPyme - http://www.openpyme.mx
6# All Rights Reserved.
7# info OpenPyme (info@openpyme.mx)
8# Coded by: Agustín Cruz (agustin.cruz@openpyme.mx)
9#
10# This program is free software: you can redistribute it and/or modify
11# it under the terms of the GNU Affero General Public License as
12# published by the Free Software Foundation, either version 3 of the
13# License, or (at your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18# GNU Affero General Public License for more details.
19#
20# You should have received a copy of the GNU Affero General Public License
21# along with this program. If not, see <http://www.gnu.org/licenses/>.
22#
23##############################################################################
24
25import string
26import logging
27import base64
28import StringIO
29
30from openerp.tools.translate import _
31from account.report import account_print_invoice
32from openerp.report import report_sxw
33import openerp
34
35
36logger = logging.getLogger(__name__)
37
38try:
39 import qrcode
40except:
41 logger.error("Package Python Qrcode missed")
42
43
44class account_invoice(account_print_invoice.account_invoice):
45 def __init__(self, cr, uid, name, context):
46 super(account_invoice, self).__init__(cr, uid, name, context=context)
47
48 self.localcontext.update({
49 'split_string': self._split_string,
50 'get_taxes': self._get_taxes,
51 'get_taxes_ret': self._get_taxes_ret,
52 'float': float,
53 'exists_key': self._exists_key,
54 'get_text_promissory': self._get_text_promissory,
55 'get_emitter_data': self._get_emitter_data,
56 'get_partner_data': self._get_partner_data,
57 'qrcode': self._get_qrcode,
58 'legend': self._get_legend,
59 })
60
61
62 def _exists_key(self, invoice, key):
63 return key in invoice._columns
64
65
66 def _get_text_promissory(self, company, partner):
67 text = ''
68 context = {}
69 lang = self.pool.get('res.partner').browse(self.cr, self.uid,
70 partner.id).lang
71 if lang:
72 context.update({'lang' : lang})
73 company = self.pool.get('res.company').browse(self.cr, self.uid,
74 company.id, context=context)
75 if company.dinamic_text:
76 try:
77 text = company.dinamic_text % eval("{" + company.dict_var + "}")
78 except:
79 pass
80 return text
81
82
83 def _get_taxes(self, invoice):
84 # TODO: Optimizar esta funcion y combinarla con _get_taxes_ret
85 lista = []
86 lista2 = []
87
88 taxes = [tax for tax in invoice.tax_line if tax.tax_percent >= 0.0]
89
90 # comparacion de los taxes, para que todos sean distintos entre sí
91 for tax in taxes:
92 lista.append([tax.name2, tax.amount])
93
94 for i in range(0, len(lista)):
95 for j in range(i + 1, len(lista)):
96 if (lista[i][0] == lista[j][0])and (lista[j][0] <> 0) :
97 lista[j][0] = 0
98 lista[i][1] = lista[i][1] + lista[j][1]
99
100
101 for k in range(0, len(lista)):
102 if lista[k][0] <> 0:
103 lista2.append(lista[k])
104
105 return lista2
106
107
108 def _get_taxes_ret(self, invoice):
109 lista = []
110 lista2 = []
111
112 taxes = [tax for tax in invoice.tax_line if tax.tax_percent < 0.0]
113
114 # comparacion de los taxes, para que todos sean distintos entre sí
115 for tax in taxes:
116 lista.append([tax.name2, tax.amount])
117
118 for i in range(0, len(lista)):
119 for j in range(i + 1, len(lista)):
120 if (lista[i][0] == lista[j][0])and (lista[j][0] <> 0) :
121 lista[j][0] = 0
122 lista[i][1] = lista[i][1] + lista[j][1]
123
124
125 for k in range(0, len(lista)):
126 if lista[k][0] <> 0:
127 lista2.append(lista[k])
128
129 return lista2
130
131
132 def _split_string(self, string, length=75):
133 if string:
134 for i in range(0, len(string), length):
135 string = string[:i] + ' ' + string[i:]
136 return string
137
138
139 def _get_emitter_data(self, partner, data='name'):
140 # Simple cache for speed up
141 if not hasattr(self, 'emitter_data'):
142 self.emitter_data = self._get_invoice_address(partner)
143 return self.emitter_data[data]
144
145
146 def _get_partner_data(self, partner, data='name'):
147 # Simple cache for speed up
148 if not hasattr(self, 'partner_data'):
149 self.partner_data = self._get_invoice_address(partner)
150 return self.partner_data[data]
151
152
153 def _get_invoice_address(self, partner):
154 # Si la dirección del partner no es default o invoice
155 if partner.type not in ['invoice', 'default']:
156 # Obtiene la dirección del padre
157 add_invoice = partner.parent_id
158 else:
159 add_invoice = partner
160
161 # Aseguramos que la dirección sea de facturación
162 if add_invoice.type in ['invoice', 'default']:
163 res = {
164 'name': add_invoice.name or '',
165 'vat': add_invoice.vat_split or add_invoice.vat or '',
166 'street': add_invoice.street or False,
167 'no_ext': add_invoice.l10n_mx_street3 or '',
168 'no_int': add_invoice.l10n_mx_street4 or '',
169 'suburb': add_invoice.street2 or '',
170 'city': add_invoice.city or '',
171 'state': add_invoice.state_id.name or '',
172 'country': add_invoice.country_id.name or '',
173 'county': add_invoice.l10n_mx_city2 or '',
174 'zip': add_invoice.zip or '',
175 'phone': add_invoice.phone or '',
176 'fax': add_invoice.fax or '',
177 'mobile': add_invoice.mobile or '',
178 }
179 if not res['vat']:
180 # Comprobamos que tengamos un RFC definido
181 raise openerp.exceptions.Warning(
182 _('Not Vat Number set on partner'))
183 else:
184 raise openerp.exceptions.Warning(
185 _('Customer Address Not Invoice Type'))
186 return res
187
188
189 def _get_qrcode(self, invoice):
190 """ If invoice type is cbb then return the uplodaded code
191 Else return the generated code
192 """
193 if invoice.invoice_sequence_id.approval_id.type == 'cbb':
194 return invoice.invoice_sequence_id.approval_id.cbb_image
195 else:
196 return self._gen_qrcode(invoice)
197
198
199 def _gen_qrcode(self, invoice):
200 """Genera el código de barras bidimensional para una factura
201 @param invoice: Objeto invoice con los datos de la factura
202 @param timbre: Cadena del XML obtenido del PAC
203
204 @return: Imagen del código de barras o None
205 """
206 # Procesar invoice para obtener el total con 17 posiciones
207 tt = string.zfill('%.6f' % invoice.amount_total, 17)
208
209 # Init qr code
210 qr = qrcode.QRCode(version=4, box_size=4, border=1)
211 # Add the data to qr code
212 qr.add_data('?re=' + invoice.company_id.partner_id.vat_split or invoice.company_id.partner_id.vat)
213 qr.add_data('&rr=' + invoice.partner_id.vat_split or invoice.company_id.vat)
214 qr.add_data('&tt=' + tt)
215 qr.add_data('&id=' + invoice.cfdi_folio_fiscal)
216 qr.make(fit=True)
217
218 # Genera la imagen y la pone en memoria para poder
219 # codificarla en 64bits y mandarla al reporte
220 img = qr.make_image()
221 output = StringIO.StringIO()
222 img.save(output, 'PNG')
223 output_s = output.getvalue()
224
225 return base64.b64encode(output_s)
226
227
228 def _get_legend(self, invoice):
229 """ Helper funcion to print legend according
230 to invoice type.
231 """
232 if invoice.invoice_sequence_id.approval_id.type == 'cbb':
233 approval = invoice.invoice_sequence_id.approval_id.approval_number
234 date = invoice.invoice_sequence_id.approval_id.date_start
235 types = ['out_invoice', 'out_refund']
236 states = ['open', 'paid', 'cancel']
237 if (invoice.type in types) and (invoice.state in states):
238 legend = _('Número de aprobación SICOFI: %s\n' %
239 approval.encode('utf-8', 'ignore')
240 )
241 else:
242 legend = _('SIN FOLIO O ESTATUS NO VALIDO\n')
243 legend += _('La reproducción apócrifa de este comprobante '
244 'constituye un delito en los términos de las '
245 'disposiciones fiscales.\n')
246 legend += _('Este comprobante tendrá una vigencia de dos años '
247 'contados a partir de la fecha aprobación de la '
248 'asignación de folios, la cual es: %s' %
249 date.encode('utf-8', 'ignore')
250 )
251 else:
252 legend = '“Este documento es una representacion impresa de un CFDI”'
253 return legend
254
255
256from openerp.netsvc import Service
257# Borra el servicio del reporte para poder declararlo nuevamente
258del Service._services['report.account.invoice.facturae.webkit']
259
260report_sxw.report_sxw(
261 'report.account.invoice.facturae.webkit',
262 'account.invoice',
263 'addons/l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml',
264 header=False,
265 parser=account_invoice,
266)
0267
=== added file 'l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml'
--- l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml 1970-01-01 00:00:00 +0000
+++ l10n_mx_facturae_pac_facturalo/report/account_print_invoice.rml 2014-06-25 00:35:08 +0000
@@ -0,0 +1,476 @@
1<?xml version="1.0"?>
2<document filename="test.pdf">
3 <template pageSize="(612.0,780.0)" title="Test" allowSplitting="20">
4 <pageTemplate id="first">
5 <frame id="first" x1="57.0" y1="46.0" width="498" height="729"/>
6 <pageGraphics>
7 <!--page bottom(footer)-->
8 <lines>1.33cm 1.33cm 20cm 1.33cm</lines>
9 <place x="1.30cm" y="0cm" height="1.30cm" width="34.0cm">
10 <para style="main_footer">Generado por openpyme.mx</para>
11 </place>
12 </pageGraphics>
13 </pageTemplate>
14 </template>
15
16 <stylesheet>
17 <blockTableStyle id="Standard_Outline">
18 <blockAlignment value="LEFT"/>
19 <blockValign value="TOP"/>
20 </blockTableStyle>
21 <blockTableStyle id="Tabla1">
22 <blockAlignment value="LEFT"/>
23 <blockValign value="TOP"/>
24 </blockTableStyle>
25 <blockTableStyle id="Tabla2">
26 <blockAlignment value="LEFT"/>
27 <blockValign value="TOP"/>
28 <lineStyle kind="LINEABOVE" colorName="#000080" start="0,0" stop="0,0"/>
29 <lineStyle kind="LINEABOVE" colorName="#000080" start="1,0" stop="1,0"/>
30 </blockTableStyle>
31 <blockTableStyle id="Tabla3">
32 <blockAlignment value="LEFT"/>
33 <blockValign value="TOP"/>
34 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="0,0" stop="0,-1"/>
35 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="0,0" stop="0,0"/>
36 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="0,-1" stop="0,-1"/>
37 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="1,0" stop="1,-1"/>
38 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="1,0" stop="1,0"/>
39 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="1,-1" stop="1,-1"/>
40 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="2,0" stop="2,-1"/>
41 <lineStyle kind="LINEAFTER" colorName="#cccccc" start="2,0" stop="2,-1"/>
42 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="2,0" stop="2,0"/>
43 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="2,-1" stop="2,-1"/>
44 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="3,0" stop="3,-1"/>
45 <lineStyle kind="LINEAFTER" colorName="#cccccc" start="3,0" stop="3,-1"/>
46 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="3,0" stop="3,0"/>
47 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="3,-1" stop="3,-1"/>
48 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="4,0" stop="4,-1"/>
49 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="4,0" stop="4,0"/>
50 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="4,-1" stop="4,-1"/>
51 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="5,0" stop="5,-1"/>
52 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="5,0" stop="5,0"/>
53 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="5,-1" stop="5,-1"/>
54 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="6,0" stop="6,-1"/>
55 <lineStyle kind="LINEAFTER" colorName="#cccccc" start="6,0" stop="6,-1"/>
56 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="6,0" stop="6,0"/>
57 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="6,-1" stop="6,-1"/>
58 <lineStyle kind="LINEBEFORE" colorName="#cccccc" start="7,0" stop="7,-1"/>
59 <lineStyle kind="LINEAFTER" colorName="#cccccc" start="7,0" stop="7,-1"/>
60 <lineStyle kind="LINEABOVE" colorName="#cccccc" start="7,0" stop="7,0"/>
61 <lineStyle kind="LINEBELOW" colorName="#cccccc" start="7,-1" stop="7,-1"/>
62 </blockTableStyle>
63 <blockTableStyle id="Tabla4">
64 <blockAlignment value="LEFT"/>
65 <blockValign value="TOP"/>
66 </blockTableStyle>
67 <blockTableStyle id="Tabla5">
68 <blockAlignment value="LEFT"/>
69 <blockValign value="TOP"/>
70 <lineStyle kind="LINEBEFORE" colorName="#000000" start="0,0" stop="0,-1"/>
71 <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/>
72 <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/>
73 <lineStyle kind="LINEBEFORE" colorName="#000000" start="1,0" stop="1,-1"/>
74 <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/>
75 <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/>
76 <lineStyle kind="LINEBEFORE" colorName="#000000" start="2,0" stop="2,-1"/>
77 <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/>
78 <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/>
79 <lineStyle kind="LINEBEFORE" colorName="#000000" start="3,0" stop="3,-1"/>
80 <lineStyle kind="LINEAFTER" colorName="#000000" start="3,0" stop="3,-1"/>
81 <lineStyle kind="LINEABOVE" colorName="#000000" start="3,0" stop="3,0"/>
82 <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/>
83 <lineStyle kind="LINEBEFORE" colorName="#000000" start="0,1" stop="0,-1"/>
84 <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/>
85 <lineStyle kind="LINEBEFORE" colorName="#000000" start="1,1" stop="1,-1"/>
86 <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/>
87 <lineStyle kind="LINEBEFORE" colorName="#000000" start="2,1" stop="2,-1"/>
88 <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/>
89 <lineStyle kind="LINEBEFORE" colorName="#000000" start="3,1" stop="3,-1"/>
90 <lineStyle kind="LINEAFTER" colorName="#000000" start="3,1" stop="3,-1"/>
91 <lineStyle kind="LINEBELOW" colorName="#000000" start="3,-1" stop="3,-1"/>
92 </blockTableStyle>
93 <blockTableStyle id="Tabla6">
94 <blockAlignment value="LEFT"/>
95 <blockValign value="TOP"/>
96 <lineStyle kind="LINEBEFORE" colorName="#000000" start="0,0" stop="0,-1"/>
97 <lineStyle kind="LINEABOVE" colorName="#000000" start="0,0" stop="0,0"/>
98 <lineStyle kind="LINEBELOW" colorName="#000000" start="0,-1" stop="0,-1"/>
99 <lineStyle kind="LINEBEFORE" colorName="#000000" start="1,0" stop="1,-1"/>
100 <lineStyle kind="LINEABOVE" colorName="#000000" start="1,0" stop="1,0"/>
101 <lineStyle kind="LINEBELOW" colorName="#000000" start="1,-1" stop="1,-1"/>
102 <lineStyle kind="LINEBEFORE" colorName="#000000" start="2,0" stop="2,-1"/>
103 <lineStyle kind="LINEAFTER" colorName="#000000" start="2,0" stop="2,-1"/>
104 <lineStyle kind="LINEABOVE" colorName="#000000" start="2,0" stop="2,0"/>
105 <lineStyle kind="LINEBELOW" colorName="#000000" start="2,-1" stop="2,-1"/>
106 </blockTableStyle>
107 <blockTableStyle id="Tabla7">
108 <blockAlignment value="LEFT"/>
109 <blockValign value="TOP"/>
110 </blockTableStyle>
111 <initialize>
112 <paraStyle name="all" alignment="justify"/>
113 </initialize>
114
115 <paraStyle name="P1" fontName="Helvetica-Bold" textColor="#ff3333" fontSize="30.0" alignment="CENTER"/>
116 <paraStyle name="P7" fontName="Helvetica" fontSize="6.5" alignment="RIGHT"/>
117 <paraStyle name="P8" fontName="Helvetica-Bold" fontSize="6.5" alignment="RIGHT" textColor="#280099"/>
118 <paraStyle name="P9" fontName="Helvetica-Bold" fontSize="6.5" alignment="CENTER" textColor="#280099"/>
119 <paraStyle name="P10" fontName="Helvetica-Bold" alignment="CENTER" fontSize="6.5" textColor="#280099"/>
120 <paraStyle name="P11" fontName="Helvetica-Bold" textColor="#ff3333" size="6.0" alignment="CENTER"/>
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"/>
122 <paraStyle name="P13" rightIndent="0.0" leftIndent="0.0" fontName="Helvetica" fontSize="4.0" leading="10" spaceBefore="0.0" spaceAfter="0.0"/>
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" />
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
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"/>
132 <paraStyle name="Standard" fontName="Helvetica"/>
133 <paraStyle name="Heading" fontName="Helvetica" fontSize="14.0" leading="17" spaceBefore="12.0" spaceAfter="6.0"/>
134 <paraStyle name="Text body" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/>
135 <paraStyle name="List" fontName="Helvetica" spaceBefore="0.0" spaceAfter="6.0"/>
136 <paraStyle name="Caption" fontName="Helvetica" fontSize="12.0" leading="15" spaceBefore="6.0" spaceAfter="6.0"/>
137 <paraStyle name="Index" fontName="Helvetica"/>
138 <paraStyle name="Table Contents" fontName="Helvetica" alignment="LEFT" fontSize="6.5"/>
139 <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="LEFT" spaceBefore="12.0" spaceAfter="6.0"/>
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"/>
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"/>
142 <paraStyle name="Table Heading" fontName="Helvetica" alignment="CENTER"/>
143 <paraStyle name="Quotations" rightIndent="28.0" leftIndent="28.0" fontName="Helvetica" spaceBefore="0.0" spaceAfter="14.0"/>
144 <paraStyle name="main_footer" fontSize="6.5" fontName="Helvetica" alignment="CENTER"/>
145 <paraStyle name="main_footer2" fontSize="7.5" fontName="Helvetica-Bold" alignment="RIGHT"/>
146 <images/>
147 </stylesheet>
148 <story>
149 <section>
150 <para style="P31">[[ repeatIn(objects,'o') ]] </para>
151 <para style="P31">[[ setLang(o.partner_id.lang) ]] </para>
152
153 <blockTable colWidths="150.0,220.0,200.0" style="Tabla1">
154 <tr>
155 <td>
156 <blockTable>
157 <tr>
158 </tr>
159 <tr>
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>
161 </tr>
162 </blockTable>
163 </td>
164 <td>
165 <blockTable>
166 <tr>
167 <td>
168 <para style="P23">[[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'name') ]]</para>
169 <para style="P24">[[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'vat') ]]</para>
170 <para style="P25">Domicilio Fiscal</para>
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>
172 <para style="P24">Colonia: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'street2') or removeParentNode('para') ]]</para>
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>
174 <para style="P24">Localidad: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'county') or removeParentNode('para') ]]</para>
175 <para style="P24">CP: [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'zip') or removeParentNode('para') ]]</para>
176 <para style="P24">Phone(s): [[ get_emitter_data(o.company_emitter_id.address_invoice_parent_company_id, 'phone') or removeParentNode('para') ]]</para>
177 </td>
178 </tr>
179 </blockTable>
180 </td>
181 <td>
182 <blockTable rowWidths="10.0" style="Tabla5">
183 <tr><td>
184 <para style="P11">Factura[[ ((o.type == 'out_invoice' and (o.state == 'open' or o.state == 'paid')) or removeParentNode('para')) and '' ]] [[ o.number ]]</para>
185 <para style="P11">FACTURA CANCELADA[[ ((o.type == 'out_invoice' and o.state == 'cancel') or removeParentNode('para')) and '' ]] [[ o.number ]]</para>
186 <para style="P11">Credit Node [[ (o.type=='out_refund' or removeParentNode('para')) and '' ]] [[ o.number ]]</para>
187 </td></tr>
188 <tr><td>
189 <para style="terp_default_8">FOLIO FISCAL(UUID):</para>
190 <para style="terp_default_9">[[ o.cfdi_folio_fiscal or removeParentNode('tr') ]]</para>
191 </td></tr>
192 <tr><td>
193 <para style="terp_default_8">NO. DE SERIE DEL CERTIFICADO DEL SAT:</para>
194 <para style="terp_default_9">[[ o.cfdi_no_certificado or removeParentNode('tr') ]]</para>
195 </td></tr>
196 <tr><td>
197 <para style="terp_default_8">NO. DE SERIE DEL CERTIFICADO DEL EMISOR:</para>
198 <para style="terp_default_9">[[ o.no_certificado or removeParentNode('tr') ]]</para>
199 </td></tr>
200 <tr><td>
201 <para style="terp_default_8">CERTIFICATION DATE AND TIME:</para>
202 <para style="terp_default_9">[[o.date_invoice_tz or removeParentNode('tr') ]]</para>
203 </td></tr>
204 <tr><td>
205 <para style="terp_default_8">ISSUE DATE AND TIME:</para>
206 <para style="terp_default_9">[[ (o.invoice_sequence_id.approval_id.type != 'cbb') and o.cfdi_fecha_timbrado or removeParentNode('tr') ]]</para>
207 </td></tr>
208 </blockTable>
209 </td>
210 </tr>
211 </blockTable>
212
213 <blockTable colWidths="250.0,50.0,250.0" style="Tabla4">
214 <tr>
215 <td>
216 <blockTable colWidths="60.0,190.0">
217 <tr>
218 <td><para style="P21"><font face="Helvetica">CLIENTE:</font></para></td>
219 <td><para style="P22">[[ get_partner_data(o.partner_id, 'name') ]]</para></td>
220 </tr>
221 <tr>
222 <td><para style="P21"><font face="Helvetica">RFC:</font></para></td>
223 <td><para style="P22">[[ get_partner_data(o.partner_id, 'vat') ]]</para></td>
224 </tr>
225 <tr>
226 <td><para style="P21"><font face="Helvetica">ADDRESS:</font></para></td>
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>
228 </td>
229 </tr>
230 <tr>
231 <td><para style="P21"><font face="Helvetica">COLONIA:</font></para></td>
232 <td><para style="P22">[[ get_partner_data(o.partner_id, 'suburb') ]] [[ get_partner_data(o.partner_id, 'county') ]]</para></td>
233 </tr>
234 <tr>
235 <td><para style="P21"><font face="Helvetica">LOCALIDAD:</font></para></td>
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>
237 </tr>
238 <tr>
239 <td><para style="P21"><font face="Helvetica">CP:</font></para></td>
240 <td><para style="P22">[[ get_partner_data(o.partner_id, 'zip') or 'N/D' ]] </para></td>
241 </tr>
242 <tr>
243 <td><para style="P21"><font face="Helvetica">PHONE:</font></para></td>
244 <td><para style="P22">[[ get_partner_data(o.partner_id, 'phone') or 'N/D']]</para></td>
245 </tr>
246 </blockTable>
247 </td>
248 <td></td>
249 <td>
250 <blockTable colWidths="90.0,160.0" >
251 <tr>
252 <td><para style="P21"><font face="Helvetica">Taxation: </font></para></td>
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>
254 </tr>
255 <tr>
256 <td><para style="P21"><font face="Helvetica">Place of issue: </font></para></td>
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>
258 </tr>
259 <tr>
260 <td><para style="P21"><font face="Helvetica">Condiciones de Pago: </font></para></td>
261 <td><para style="P22">[[ format(o.payment_term and (o.payment_term.note or o.payment_term.name) or removeParentNode('tr') ) ]]</para></td>
262 </tr>
263 <tr>
264 <td><para style="P21"><font face="Helvetica">NumCtaPago:</font></para></td>
265 <td><para style="P22">[[o.acc_payment.last_acc_number or removeParentNode('tr')]]</para></td>
266 </tr>
267 <tr>
268 <td><para style="P21"><font face="Helvetica">Payment Method:</font></para></td>
269 <td><para style="P22"> [[o.pay_method_id.name or removeParentNode('tr')]]</para></td>
270 </tr>
271 <tr>
272 <td><para style="P21"><font face="Helvetica">BANCO:</font></para></td>
273 <td><para style="P22"> [[ o.acc_payment.bank_name or removeParentNode('tr')]] </para></td>
274 </tr>
275 <tr>
276 <td><para style="P21"><font face="Helvetica">Clave de Moneda:</font></para></td>
277 <td><para style="P22">[[o.currency_id.name or removeParentNode('tr')]]</para></td>
278 </tr>
279 <tr>
280 <td><para style="P21"><font face="Helvetica">Origin:</font></para></td>
281 <td><para style="P22">[[ o.origin or removeParentNode('tr')]]</para></td>
282 </tr>
283 <tr>
284 <td><para style="P21"><font face="Helvetica">Customer Reference:</font></para></td>
285 <td><para style="P22">[[ o.name or removeParentNode('tr')]]</para></td>
286 </tr>
287 </blockTable>
288 </td>
289 </tr>
290 </blockTable>
291
292
293 <blockTable colWidths="70.0,70.0,70.0,210.0,70.0,70.0" style="Tabla3">
294 <tr style="Tabla3">
295 <td>
296 <para style="P9">CANTIDAD</para>
297 </td>
298 <td>
299 <para style="P9">UNIDAD DE MEDIDA</para>
300 </td>
301 <td>
302 <para style="P9">CODE</para>
303 </td>
304 <td>
305 <para style="P9">DESCRIPTION</para>
306 </td>
307 <td>
308 <para style="P9">PRECIO UNITARIO</para>
309 </td>
310 <td>
311 <para style="P9">IMPORTES</para>
312 </td>
313 </tr>
314 <tr>
315 <td>
316 <para style="Table Contents"><font face="Helvetica" size="8.0">[[ repeatIn(o.invoice_line,'l') ]] </font>[[ formatLang(l.quantity) ]]</para>
317 </td>
318 <td >
319 <para style="Table Contents">[[ (l.uos_id and l.uos_id.name) or '' ]]</para>
320 </td>
321 <td >
322 <para style="Table Contents">[[ l.product_id and l.product_id.default_code ]]</para>
323 </td>
324 <td >
325 <para style="Table Contents">[[ l.name ]]</para>
326 <para style="Table Contents">Notas: [[l.note or removeParentNode('para')]]</para>
327 </td>
328 <td>
329 <para style="P15">[[ formatLang(l.price_unit) ]]</para>
330 </td>
331 <td>
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>
333 </td>
334 </tr>
335 </blockTable>
336
337 <blockTable colWidths="425,70.0,75.0" style="Tabla4">
338 <tr>
339 <td>
340 <para style="P10">[[ o.amount_to_text ]]</para>
341 </td>
342 <td>
343 <blockTable rowHeights="10.0" style="Tabla4">
344 <tr>
345 <td><para style="P8">SUMA: [[ exists_key(o, 'global_discount_percent') and o.global_discount_percent or removeParentNode('tr')]]$</para></td>
346 </tr>
347 <tr>
348 <td><para style="P8">DESCUENTO: [[ exists_key(o, 'global_discount_percent') and o.global_discount_percent or removeParentNode('tr')]] %</para>
349 </td>
350 </tr>
351 <tr>
352 <td>
353 <para style="P8">SUBTOTAL: $</para>
354 </td>
355 </tr>
356 <tr>
357 <td>
358 <para style="P8">
359 <font face="Helvetica" >[[ repeatIn( get_taxes(o), 'tax' ) ]]</font>
360 </para>
361 <para style="P8">
362 <font face="Helvetica" >[[ tax[0] ]]: $</font>
363 </para>
364 </td>
365 </tr>
366 <tr>
367 <td>
368 <para style="P8">
369 <font face="Helvetica" >[[ repeatIn( get_taxes_ret(o), 'tax_ret' ) ]]</font>
370 </para>
371 <para style="P8">
372 <font face="Helvetica" >[[ tax_ret[0] ]] Ret: $</font>
373 </para>
374 </td>
375 </tr>
376 <tr>
377 <td>
378 <para style="P8">TOTAL: $</para>
379 </td>
380 </tr>
381 </blockTable>
382
383 </td>
384 <td>
385 <blockTable rowHeights="10.0" style="Tabla4">
386 <tr>
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>
388 </tr>
389 <tr>
390 <td><para style="P7">[[o.global_discount_amount and formatLang( o.global_discount_amount) or removeParentNode('tr')]]</para></td>
391 </tr>
392 <tr>
393 <td><para style="P7">[[ formatLang(o.amount_untaxed) ]]</para></td>
394 </tr>
395 <tr>
396 <td><para style="P7">
397 <font face="Helvetica">[[ repeatIn( get_taxes(o), 'tax' ) ]]</font>
398 </para>
399 <para style="P7">
400 <font face="Helvetica">[[ formatLang(float(tax[1])) ]]</font>
401 </para>
402 </td>
403 </tr>
404 <tr>
405 <td><para style="P7">
406 <font face="Helvetica">[[ repeatIn( get_taxes_ret(o), 'tax_ret' ) ]]</font>
407 </para>
408 <para style="P7">
409 <font face="Helvetica">[[ formatLang(float(tax_ret[1])*-1) ]]</font>
410 </para>
411 </td>
412 </tr>
413 <tr>
414 <td><para style="P7">[[ formatLang(o.amount_total) ]]</para></td>
415 </tr>
416 </blockTable>
417 </td>
418 </tr>
419 </blockTable>
420
421 <blockTable colWidths="500.00">
422 <tr>
423 <td><para style="P22">[[ (o.comment and format(o.comment )) or removeParentNode('blockTable') ]]</para></td>
424 </tr>
425 </blockTable>
426
427 <blockTable style="Tabla4">
428 <tr><td>
429 <para style="P1">[[ (o.state == 'cancel' and 'CANCELADA') or removeParentNode('blockTable') ]]</para>
430 </td></tr>
431 </blockTable>
432
433 <blockTable colWidths="470.0,110.0" style="Tabla4">
434 <tr>
435 <td>
436 <blockTable colWidths="465.0" rowHeights="10.0,10.0,10.0,10.0,10.0,10.0,20.0" style="Tabla4">
437 <tr></tr>
438 <tr>
439 <td><para style="P12">SELLO DIGITAL DEL EMISOR</para></td>
440 </tr>
441 <tr style ="Tabla3">
442 <td><para style="P13">[[ split_string( o.sello or '') ]]</para></td>
443 </tr>
444 <tr>
445 <td><para style="P12">SELLO DIGITAL DEL SAT:</para></td>
446 </tr>
447 <tr style ="Tabla3">
448 <td><para style="P13">[[ split_string( o.cfdi_sello or '') ]]</para></td>
449 </tr>
450 <tr>
451 <td><para style="P12">CADENA ORIGINAL DEL COMPLEMENTO DE CERTIFICACION DIGITAL DEL SAT:</para></td>
452 </tr>
453 <tr style ="Tabla3" >
454 <td><para style="P13">[[ split_string( o.cfdi_cadena_original or '' ) ]]</para></td>
455 </tr>
456 </blockTable>
457 </td>
458 <td>
459 <blockTable style="Tabla4">
460 <tr>
461 <td><para style="P13">[[ setTag('para','image',{'width':'3cm','height':'3cm'}) ]] [[ qrcode(o) ]]</para></td>
462 </tr>
463 </blockTable>
464 </td>
465 </tr>
466 </blockTable>
467
468 <blockTable style="Tabla4">
469 <tr>
470 <td><para style="P14"> [[ legend(o) ]] </para></td>
471 </tr>
472 </blockTable>
473 </section>
474
475 </story>
476</document>
0\ No newline at end of file477\ No newline at end of file