Browse Source

[MIG] report_qweb_signer: Migration to 10.0

pull/253/head
Pedro M. Baeza 7 years ago
committed by Pedro M. Baeza
parent
commit
97f9aba98a
  1. 26
      report_qweb_signer/README.rst
  2. 1
      report_qweb_signer/__init__.py
  3. 14
      report_qweb_signer/__manifest__.py
  4. 21
      report_qweb_signer/demo/report_certificate.xml
  5. 20
      report_qweb_signer/demo/report_certificate_demo.xml
  6. 46
      report_qweb_signer/demo/report_partner.xml
  7. 45
      report_qweb_signer/demo/report_partner_demo.xml
  8. 4
      report_qweb_signer/i18n/es.po
  9. 140
      report_qweb_signer/models/report.py
  10. 2
      report_qweb_signer/models/report_certificate.py
  11. 2
      report_qweb_signer/models/res_company.py
  12. 4
      report_qweb_signer/tests/__init__.py
  13. 27
      report_qweb_signer/tests/test_report_qweb_signer.py
  14. 9
      report_qweb_signer/views/report_certificate_view.xml
  15. 40
      report_qweb_signer/views/res_company_view.xml

26
report_qweb_signer/README.rst

@ -50,17 +50,15 @@ but signed PDF is automatically downloaded if this document model is configured
as indicated above.
If 'Save as attachment' is configured, signed PDF is saved as attachment and
next time saved one is downloaded without signing again. This is appropiate when
signing date is important, for example, when signing customer invoices.
next time saved one is downloaded without signing again. This is appropiate
when signing date is important, for example, when signing customer invoices.
You can try the signing with the demo report that is included for customers
called "Test PDF certificate".
.. image:: https://odoo-community.org/website/image/ir.attachment/5784_f2813bd/datas
:alt: Try me on Runbot
:target: https://runbot.odoo-community.org/runbot/143/8.0
For further information, please visit:
* https://www.odoo.com/forum/help-1
:target: https://runbot.odoo-community.org/runbot/143/10.0
Known issues / Roadmap
======================
@ -69,6 +67,7 @@ Known issues / Roadmap
then 'Save as attachment' is not applied and signed result is not
saved as attachment.
* To have a visible signature through an image embedded in the resulting PDF.
* Add tests.
Bug Tracker
@ -76,8 +75,8 @@ Bug Tracker
Bugs are tracked on `GitHub Issues <https://github.com/OCA/reporting-engine/issues>`_.
In case of trouble, please check there if your issue has already been reported.
If you spotted it first, help us smashing it by providing a detailed and welcomed feedback
`here <https://github.com/OCA/reporting-engine/issues/new?body=module:%20report_qweb_signer%0Aversion:%208.0%0A%0A**Steps%20to%20reproduce**%0A-%20...%0A%0A**Current%20behavior**%0A%0A**Expected%20behavior**>`_.
If you spotted it first, help us smashing it by providing a detailed and
welcomed feedback `here <https://github.com/OCA/reporting-engine/issues/new>`_.
Credits
@ -98,8 +97,9 @@ Icon
Contributors
------------
* Rafael Blasco <rafabn@antiun.com>
* Antonio Espinosa <antonioea@antiun.com>
* Rafael Blasco <rafael.blasco@tecnativa.com>
* Antonio Espinosa <antonio.espinosa@tecnativa.com>
* Pedro M. Baeza <pedro.baeza@tecnativa.com>
Maintainer
----------
@ -114,4 +114,4 @@ OCA, or the Odoo Community Association, is a nonprofit organization whose
mission is to support the collaborative development of Odoo features and
promote its widespread use.
To contribute to this module, please visit http://odoo-community.org.
To contribute to this module, please visit https://odoo-community.org.

1
report_qweb_signer/__init__.py

@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import models

14
report_qweb_signer/__openerp__.py → report_qweb_signer/__manifest__.py

@ -1,17 +1,17 @@
# -*- coding: utf-8 -*-
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
# Copyright 2015 Tecnativa - Antonio Espinosa
# Copyright 2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
{
"name": "Qweb PDF reports signer",
"summary": "Sign Qweb PDFs usign a PKCS#12 certificate",
"version": "8.0.1.0.0",
"version": "10.0.1.0.0",
"category": "Reporting",
"website": "http://www.antiun.com",
"author": "Antiun Ingeniería S.L., "
"website": "https://www.tecnativa.com",
"author": "Tecnativa, "
"Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"installable": True,
"depends": [
"report",
@ -25,7 +25,7 @@
"views/res_company_view.xml",
],
"demo": [
"demo/report_partner.xml",
"demo/report_certificate.xml",
"demo/report_partner_demo.xml",
"demo/report_certificate_demo.xml",
],
}

21
report_qweb_signer/demo/report_certificate.xml

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp>
<data noupdate="1">
<record id="demo_certificate_test" model="report.certificate">
<field name="company_id" ref="base.main_company"/>
<field name="name">Test OCA certificate</field>
<field name="path">test.p12</field>
<field name="password_file">test.passwd</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain">[('customer', '=', True)]</field>
<field name="allow_only_one" eval="True"/>
<field name="attachment">'test_' + (object.name or '').replace(' ', '_').lower() + '.signed.pdf'</field>
</record>
</data>
</openerp>

20
report_qweb_signer/demo/report_certificate_demo.xml

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015 Tecnativa - Antonio Espinosa
Copyright 2017 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo noupdate="1">
<record id="demo_certificate_test" model="report.certificate">
<field name="company_id" ref="base.main_company"/>
<field name="name">Test OCA certificate</field>
<field name="path">test.p12</field>
<field name="password_file">test.passwd</field>
<field name="model_id" ref="base.model_res_partner"/>
<field name="domain">[('customer', '=', True)]</field>
<field name="allow_only_one" eval="True"/>
<field name="attachment">'test_' + (object.name or '').replace(' ', '_').lower() + '.signed.pdf'</field>
</record>
</odoo>

46
report_qweb_signer/demo/report_partner.xml

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp>
<data>
<template id="report_partner_demo_document">
<t t-call="report.external_layout">
<div class="page">
<div class="row">
<div class="col-md-12">
This is a sample report for testing PDF certificates
</div>
</div>
<div class="row">
<div class="col-md-12">
<strong>Partner:</strong> <span t-field="o.name"/>
</div>
</div>
</div>
</t>
</template>
<template id="report_partner_demo">
<t t-call="report.html_container">
<t t-foreach="doc_ids" t-as="doc_id">
<t t-raw="translate_doc(doc_id, doc_model, 'lang', 'report_qweb_signer.report_partner_demo_document')"/>
</t>
</t>
</template>
<report
id="partner_demo"
model="res.partner"
string="Test PDF certificate"
report_type="qweb-pdf"
name="report_qweb_signer.report_partner_demo"
file="report_qweb_signer.report_partner_demo"
attachment_use="True"
attachment="'test_' + (object.name or '').replace(' ', '_').lower() + '.pdf'"
/>
</data>
</openerp>

45
report_qweb_signer/demo/report_partner_demo.xml

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2015 Tecnativa - Antonio Espinosa
Copyright 2017 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<odoo>
<template id="report_partner_demo_document">
<t t-call="report.external_layout">
<div class="page">
<div class="row">
<div class="col-md-12">
<span>This is a sample report for testing PDF certificates.</span>
</div>
</div>
<div class="row">
<div class="col-md-12">
<strong>Partner:</strong> <span t-field="o.name"/>
</div>
</div>
</div>
</t>
</template>
<template id="report_partner_demo">
<t t-call="report.html_container">
<t t-foreach="docs" t-as="o">
<t t-call="report_qweb_signer.report_partner_demo_document" t-lang="o.lang"/>
</t>
</t>
</template>
<report
id="partner_demo_report"
model="res.partner"
string="Test PDF certificate"
report_type="qweb-pdf"
name="report_qweb_signer.report_partner_demo"
file="report_qweb_signer.report_partner_demo"
attachment_use="True"
attachment="'test_' + (object.name or '').replace(' ', '_').lower() + '.pdf'"
/>
</odoo>

4
report_qweb_signer/i18n/es.po

@ -168,9 +168,9 @@ msgstr "Salvar como adjunto"
#: code:addons/report_qweb_signer/models/report.py:119
#, python-format
msgid ""
"Saving signed report (PDF): You do not have enought access rights to save "
"Saving signed report (PDF): You do not have enough access rights to save "
"attachments"
msgstr "Guardar informe firmado (PDF): no tienes suficientes permisos de acceso para guardar adjuntos"
msgstr "Guardar informe firmado (PDF): no tiene suficientes permisos de acceso para guardar adjuntos"
#. module: report_qweb_signer
#: field:report.certificate,sequence:0

140
report_qweb_signer/models/report.py

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
# Copyright 2015 Tecnativa - Antonio Espinosa
# Copyright 2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import base64
@ -9,9 +10,9 @@ import subprocess
import tempfile
import time
from openerp import models, api, _
from openerp.exceptions import Warning, AccessError
from openerp.tools.safe_eval import safe_eval
from odoo import models, api, _
from odoo.exceptions import UserError, AccessError
from odoo.tools.safe_eval import safe_eval
import logging
_logger = logging.getLogger(__name__)
@ -30,99 +31,80 @@ def _normalize_filepath(path):
class Report(models.Model):
_inherit = 'report'
def _certificate_get(self, cr, uid, ids, report, context=None):
def _certificate_get(self, report, docids):
"""Obtain the proper certificate for the report and the conditions."""
if report.report_type != 'qweb-pdf':
_logger.info(
"Can only sign qweb-pdf reports, this one is '%s' type",
report.report_type)
return False
m_cert = self.pool['report.certificate']
company_id = self.pool['res.users']._get_company(cr, uid)
certificate_ids = m_cert.search(cr, uid, [
('company_id', '=', company_id),
('model_id', '=', report.model)], context=context)
if not certificate_ids:
_logger.info(
"No PDF certificate found for report '%s'",
report.report_name)
certificates = self.env['report.certificate'].search([
('company_id', '=', self.env.user.company_id.id),
('model_id', '=', report.model),
])
if not certificates:
return False
for cert in m_cert.browse(cr, uid, certificate_ids, context=context):
for cert in certificates:
# Check allow only one document
if cert.allow_only_one and len(ids) > 1:
_logger.info(
if cert.allow_only_one and len(self) > 1:
_logger.debug(
"Certificate '%s' allows only one document, "
"but printing %d documents",
cert.name, len(ids))
cert.name, len(docids))
continue
# Check domain
if cert.domain:
m_model = self.pool[cert.model_id.model]
domain = [('id', 'in', tuple(ids))]
domain = [('id', 'in', tuple(docids))]
domain = domain + safe_eval(cert.domain)
doc_ids = m_model.search(cr, uid, domain, context=context)
if not doc_ids:
_logger.info(
docs = self.env[cert.model_id.model].search(domain)
if not docs:
_logger.debug(
"Certificate '%s' domain not satisfied", cert.name)
continue
# Certificate match!
return cert
return False
def _attach_filename_get(self, cr, uid, ids, certificate, context=None):
if len(ids) != 1:
def _attach_filename_get(self, docids, certificate):
if len(docids) != 1:
return False
obj = self.pool[certificate.model_id.model].browse(cr, uid, ids[0])
filename = safe_eval(certificate.attachment, {
'object': obj,
doc = self.env[certificate.model_id.model].browse(docids[0])
return safe_eval(certificate.attachment, {
'object': doc,
'time': time
})
return filename
def _attach_signed_read(self, cr, uid, ids, certificate, context=None):
if len(ids) != 1:
def _attach_signed_read(self, docids, certificate):
if len(docids) != 1:
return False
filename = self._attach_filename_get(
cr, uid, ids, certificate, context=context)
filename = self._attach_filename_get(docids, certificate)
if not filename:
return False
signed = False
m_attachment = self.pool['ir.attachment']
attach_ids = m_attachment.search(cr, uid, [
attachment = self.env['ir.attachment'].search([
('datas_fname', '=', filename),
('res_model', '=', certificate.model_id.model),
('res_id', '=', ids[0])
])
if attach_ids:
signed = m_attachment.browse(cr, uid, attach_ids[0]).datas
signed = base64.decodestring(signed)
return signed
('res_id', '=', docids[0]),
], limit=1)
if attachment:
return base64.decodestring(attachment.datas)
return False
def _attach_signed_write(self, cr, uid, ids, certificate, signed,
context=None):
if len(ids) != 1:
def _attach_signed_write(self, docids, certificate, signed):
if len(docids) != 1:
return False
filename = self._attach_filename_get(
cr, uid, ids, certificate, context=context)
filename = self._attach_filename_get(docids, certificate)
if not filename:
return False
m_attachment = self.pool['ir.attachment']
try:
attach_id = m_attachment.create(cr, uid, {
attachment = self.env['ir.attachment'].create({
'name': filename,
'datas': base64.encodestring(signed),
'datas_fname': filename,
'res_model': certificate.model_id.model,
'res_id': ids[0],
'res_id': docids[0],
})
except AccessError:
raise Warning(
raise UserError(
_('Saving signed report (PDF): '
'You do not have enought access rights to save attachments'))
else:
_logger.info(
"The signed PDF document '%s' is now saved in the database",
filename)
return attach_id
'You do not have enough access rights to save attachments'))
return attachment
def _signer_bin(self, opts):
me = os.path.dirname(__file__)
@ -135,7 +117,7 @@ class Report(models.Model):
p12 = _normalize_filepath(certificate.path)
passwd = _normalize_filepath(certificate.password_file)
if not (p12 and passwd):
raise Warning(
raise UserError(
_('Signing report (PDF): '
'Certificate or password file not found'))
signer_opts = '"%s" "%s" "%s" "%s"' % (p12, pdf, pdfsigned, passwd)
@ -144,38 +126,37 @@ class Report(models.Model):
signer, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
out, err = process.communicate()
if process.returncode:
raise Warning(
raise UserError(
_('Signing report (PDF): jPdfSign failed (error code: %s). '
'Message: %s. Output: %s') %
(process.returncode, err, out))
return pdfsigned
@api.v7
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None,
context=None):
signed_content = False
report = self._get_report_from_name(cr, uid, report_name)
certificate = self._certificate_get(
cr, uid, ids, report, context=context)
@api.model
def get_pdf(self, docids, report_name, html=None, data=None):
report = self._get_report_from_name(report_name)
certificate = self._certificate_get(report, docids)
if certificate and certificate.attachment:
signed_content = self._attach_signed_read(
cr, uid, ids, certificate, context=context)
signed_content = self._attach_signed_read(docids, certificate)
if signed_content:
_logger.info("The signed PDF document '%s/%s' was loaded from "
"the database", report_name, ids)
_logger.debug(
"The signed PDF document '%s/%s' was loaded from the "
"database", report_name, docids,
)
return signed_content
content = super(Report, self).get_pdf(
cr, uid, ids, report_name, html=html, data=data,
context=context)
docids, report_name, html=html, data=data,
)
if certificate:
# Creating temporary origin PDF
pdf_fd, pdf = tempfile.mkstemp(
suffix='.pdf', prefix='report.tmp.')
with closing(os.fdopen(pdf_fd, 'w')) as pf:
pf.write(content)
_logger.info(
"Signing PDF document '%s/%s' with certificate '%s'",
report_name, ids, certificate.name)
_logger.debug(
"Signing PDF document '%s' for IDs %s with certificate '%s'",
report_name, docids, certificate.name,
)
signed = self.pdf_sign(pdf, certificate)
# Read signed PDF
if os.path.exists(signed):
@ -188,6 +169,5 @@ class Report(models.Model):
except (OSError, IOError):
_logger.error('Error when trying to remove file %s', fname)
if certificate.attachment:
self._attach_signed_write(
cr, uid, ids, certificate, content, context=context)
self._attach_signed_write(docids, certificate, content)
return content

2
report_qweb_signer/models/report_certificate.py

@ -2,7 +2,7 @@
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import api, fields, models
from odoo import api, fields, models
class ReportCertificate(models.Model):

2
report_qweb_signer/models/res_company.py

@ -2,7 +2,7 @@
# © 2015 Antiun Ingenieria S.L. - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp import models, fields
from odoo import models, fields
class ResCompany(models.Model):

4
report_qweb_signer/tests/__init__.py

@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from . import test_report_qweb_signer

27
report_qweb_signer/tests/test_report_qweb_signer.py

@ -0,0 +1,27 @@
# -*- coding: utf-8 -*-
# Copyright 2017 Tecnativa - Pedro M. Baeza
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from openerp.tests import common
@common.at_install(False)
@common.post_install(True)
class TestReportQwebSigner(common.SavepointCase):
@classmethod
def setUpClass(cls):
super(TestReportQwebSigner, cls).setUpClass()
cls.partner = cls.env['res.partner'].create({
'name': 'Test partner',
'customer': True,
})
cls.report = cls.env.ref('report_qweb_signer.partner_demo_report')
def test_report_qweb_signer(self):
self.env['report'].get_pdf(
self.partner.ids, self.report.report_name, data={},
)
# Reprint again for taking the PDF from attachment
self.env['report'].get_pdf(
self.partner.ids, self.report.report_name, data={},
)

9
report_qweb_signer/views/report_certificate_view.xml

@ -1,10 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
Copyright 2015 Tecnativa - Antonio Espinosa
Copyright 2017 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp>
<data>
<odoo>
<record id="view_report_certificate_form" model="ir.ui.view">
<field name="name">report.certificate.form</field>
@ -62,5 +62,4 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
parent="report.reporting_menuitem"
action="action_report_certificate"/>
</data>
</openerp>
</odoo>

40
report_qweb_signer/views/res_company_view.xml

@ -1,26 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
© 2015 Antiun Ingenieria S.L. - Antonio Espinosa
Copyright 2015 Tecnativa - Antonio Espinosa
Copyright 2017 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
-->
<openerp>
<data>
<odoo>
<record id="view_company_form" model="ir.ui.view">
<field name="name">Add PDF report certificates list</field>
<field name="inherit_id" ref="base.view_company_form" />
<field name="model">res.company</field>
<field name="arch" type="xml">
<data>
<xpath expr="//page[@string='Report Configuration']/group[@string='Configuration']" position="after">
<group string="Certificates" col="2">
<field name="report_certificate_ids"
context="{'default_company_id': active_id}"/>
</group>
</xpath>
</data>
</field>
</record>
<record id="view_company_form" model="ir.ui.view">
<field name="name">Add PDF report certificates list</field>
<field name="inherit_id" ref="base.view_company_form" />
<field name="model">res.company</field>
<field name="arch" type="xml">
<data>
<notebook position="inside">
<page name="pdf_sign_certificate" string="Certificates (PDF signing)">
<field name="report_certificate_ids"
context="{'default_company_id': active_id}"
/>
</page>
</notebook>
</data>
</field>
</record>
</data>
</openerp>
</odoo>
Loading…
Cancel
Save