[IMP] report_qweb_signer: Black python code

pull/522/head
Laurent-Corron 2021-06-29 10:10:17 +02:00
parent 4e321eb209
commit ae6d5a659c
16 changed files with 226 additions and 185 deletions

View File

@ -237,8 +237,8 @@ class BiSQLViewField(models.Model):
self.ensure_one() self.ensure_one()
res = "" res = ""
if self.field_description and self.is_group_by: if self.field_description and self.is_group_by:
res = """<filter name="%s" string="%s" res = """<filter name="{}" string="{}"
context="{'group_by':'%s'}"/>""" % ( context="{{'group_by':'{}'}}"/>""".format(
self.field_description.lower().replace(" ", "_"), self.field_description.lower().replace(" ", "_"),
self.field_description, self.field_description,
self.name, self.name,

View File

@ -9,16 +9,11 @@
"version": "12.0.1.0.1", "version": "12.0.1.0.1",
"category": "Reporting", "category": "Reporting",
"website": "https://github.com/oca/reporting-engine", "website": "https://github.com/oca/reporting-engine",
"author": "Tecnativa, " "author": "Tecnativa, " "Odoo Community Association (OCA)",
"Odoo Community Association (OCA)",
"license": "AGPL-3", "license": "AGPL-3",
"installable": True, "installable": True,
"depends": [ "depends": ["web_editor"],
"web_editor", "external_dependencies": {"bin": ["/usr/bin/java"]},
],
"external_dependencies": {
"bin": ['/usr/bin/java'],
},
"data": [ "data": [
"data/defaults.xml", "data/defaults.xml",
"security/ir.model.access.csv", "security/ir.model.access.csv",

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<odoo> <odoo>
<record model="ir.config_parameter" id="report_qweb_signer_java_param"> <record model="ir.config_parameter" id="report_qweb_signer_java_param">
<field name="key">report_qweb_signer.java_parameters</field> <field name="key">report_qweb_signer.java_parameters</field>

View File

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

View File

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

View File

@ -3,41 +3,43 @@
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
import base64 import base64
from contextlib import closing import logging
import os import os
import subprocess import subprocess
import tempfile import tempfile
import time import time
from contextlib import closing
from odoo import models, api, _ from odoo import _, api, models
from odoo.exceptions import UserError, AccessError from odoo.exceptions import AccessError, UserError
from odoo.tools.safe_eval import safe_eval from odoo.tools.safe_eval import safe_eval
import logging
_logger = logging.getLogger(__name__) _logger = logging.getLogger(__name__)
def _normalize_filepath(path): def _normalize_filepath(path):
path = path or '' path = path or ""
path = path.strip() path = path.strip()
if not os.path.isabs(path): if not os.path.isabs(path):
me = os.path.dirname(__file__) me = os.path.dirname(__file__)
path = '{}/../static/certificate/'.format(me) + path path = "{}/../static/certificate/".format(me) + path
path = os.path.normpath(path) path = os.path.normpath(path)
return path if os.path.exists(path) else False return path if os.path.exists(path) else False
class IrActionsReport(models.Model): class IrActionsReport(models.Model):
_inherit = 'ir.actions.report' _inherit = "ir.actions.report"
def _certificate_get(self, res_ids): def _certificate_get(self, res_ids):
"""Obtain the proper certificate for the report and the conditions.""" """Obtain the proper certificate for the report and the conditions."""
if self.report_type != 'qweb-pdf': if self.report_type != "qweb-pdf":
return False return False
certificates = self.env['report.certificate'].search([ certificates = self.env["report.certificate"].search(
('company_id', '=', self.env.user.company_id.id), [
('model_id', '=', self.model), ("company_id", "=", self.env.user.company_id.id),
]) ("model_id", "=", self.model),
]
)
if not certificates: if not certificates:
return False return False
for cert in certificates: for cert in certificates:
@ -46,16 +48,19 @@ class IrActionsReport(models.Model):
_logger.debug( _logger.debug(
"Certificate '%s' allows only one document, " "Certificate '%s' allows only one document, "
"but printing %d documents", "but printing %d documents",
cert.name, len(res_ids)) cert.name,
len(res_ids),
)
continue continue
# Check domain # Check domain
if cert.domain: if cert.domain:
domain = [('id', 'in', tuple(res_ids))] domain = [("id", "in", tuple(res_ids))]
domain = domain + safe_eval(cert.domain) domain = domain + safe_eval(cert.domain)
docs = self.env[cert.model_id.model].search(domain) docs = self.env[cert.model_id.model].search(domain)
if not docs: if not docs:
_logger.debug( _logger.debug(
"Certificate '%s' domain not satisfied", cert.name) "Certificate '%s' domain not satisfied", cert.name
)
continue continue
# Certificate match! # Certificate match!
return cert return cert
@ -65,10 +70,7 @@ class IrActionsReport(models.Model):
if len(res_ids) != 1: if len(res_ids) != 1:
return False return False
doc = self.env[certificate.model_id.model].browse(res_ids[0]) doc = self.env[certificate.model_id.model].browse(res_ids[0])
return safe_eval(certificate.attachment, { return safe_eval(certificate.attachment, {"object": doc, "time": time})
'object': doc,
'time': time
})
def _attach_signed_read(self, res_ids, certificate): def _attach_signed_read(self, res_ids, certificate):
if len(res_ids) != 1: if len(res_ids) != 1:
@ -76,11 +78,14 @@ class IrActionsReport(models.Model):
filename = self._attach_filename_get(res_ids, certificate) filename = self._attach_filename_get(res_ids, certificate)
if not filename: if not filename:
return False return False
attachment = self.env['ir.attachment'].search([ attachment = self.env["ir.attachment"].search(
('datas_fname', '=', filename), [
('res_model', '=', certificate.model_id.model), ("datas_fname", "=", filename),
('res_id', '=', res_ids[0]), ("res_model", "=", certificate.model_id.model),
], limit=1) ("res_id", "=", res_ids[0]),
],
limit=1,
)
if attachment: if attachment:
return base64.decodestring(attachment.datas) return base64.decodestring(attachment.datas)
return False return False
@ -92,45 +97,57 @@ class IrActionsReport(models.Model):
if not filename: if not filename:
return False return False
try: try:
attachment = self.env['ir.attachment'].create({ attachment = self.env["ir.attachment"].create(
'name': filename, {
'datas': base64.encodestring(signed), "name": filename,
'datas_fname': filename, "datas": base64.encodestring(signed),
'res_model': certificate.model_id.model, "datas_fname": filename,
'res_id': res_ids[0], "res_model": certificate.model_id.model,
}) "res_id": res_ids[0],
}
)
except AccessError: except AccessError:
raise UserError( raise UserError(
_('Saving signed report (PDF): ' _(
'You do not have enough access rights to save attachments')) "Saving signed report (PDF): "
"You do not have enough access rights to save attachments"
)
)
return attachment return attachment
def _signer_bin(self, opts): def _signer_bin(self, opts):
me = os.path.dirname(__file__) me = os.path.dirname(__file__)
irc_param = self.env['ir.config_parameter'].sudo() irc_param = self.env["ir.config_parameter"].sudo()
java_bin = 'java -jar' java_bin = "java -jar"
java_param = irc_param.get_param('report_qweb_signer.java_parameters') java_param = irc_param.get_param("report_qweb_signer.java_parameters")
jar = '{}/../static/jar/jPdfSign.jar'.format(me) jar = "{}/../static/jar/jPdfSign.jar".format(me)
return '%s %s %s %s' % (java_bin, java_param, jar, opts) return "{} {} {} {}".format(java_bin, java_param, jar, opts)
def pdf_sign(self, pdf, certificate): def pdf_sign(self, pdf, certificate):
pdfsigned = pdf + '.signed.pdf' pdfsigned = pdf + ".signed.pdf"
p12 = _normalize_filepath(certificate.path) p12 = _normalize_filepath(certificate.path)
passwd = _normalize_filepath(certificate.password_file) passwd = _normalize_filepath(certificate.password_file)
if not (p12 and passwd): if not (p12 and passwd):
raise UserError( raise UserError(
_('Signing report (PDF): ' _(
'Certificate or password file not found')) "Signing report (PDF): "
signer_opts = '"%s" "%s" "%s" "%s"' % (p12, pdf, pdfsigned, passwd) "Certificate or password file not found"
)
)
signer_opts = '"{}" "{}" "{}" "{}"'.format(p12, pdf, pdfsigned, passwd)
signer = self._signer_bin(signer_opts) signer = self._signer_bin(signer_opts)
process = subprocess.Popen( process = subprocess.Popen(
signer, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) signer, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True
)
out, err = process.communicate() out, err = process.communicate()
if process.returncode: if process.returncode:
raise UserError( raise UserError(
_('Signing report (PDF): jPdfSign failed (error code: %s). ' _(
'Message: %s. Output: %s') % "Signing report (PDF): jPdfSign failed (error code: %s). "
(process.returncode, err, out)) "Message: %s. Output: %s"
)
% (process.returncode, err, out)
)
return pdfsigned return pdfsigned
@api.multi @api.multi
@ -141,32 +158,36 @@ class IrActionsReport(models.Model):
if signed_content: if signed_content:
_logger.debug( _logger.debug(
"The signed PDF document '%s/%s' was loaded from the " "The signed PDF document '%s/%s' was loaded from the "
"database", self.report_name, res_ids, "database",
self.report_name,
res_ids,
) )
return signed_content, 'pdf' return signed_content, "pdf"
content, ext = super(IrActionsReport, self).render_qweb_pdf(res_ids, content, ext = super(IrActionsReport, self).render_qweb_pdf(
data) res_ids, data
)
if certificate: if certificate:
# Creating temporary origin PDF # Creating temporary origin PDF
pdf_fd, pdf = tempfile.mkstemp( pdf_fd, pdf = tempfile.mkstemp(suffix=".pdf", prefix="report.tmp.")
suffix='.pdf', prefix='report.tmp.') with closing(os.fdopen(pdf_fd, "wb")) as pf:
with closing(os.fdopen(pdf_fd, 'wb')) as pf:
pf.write(content) pf.write(content)
_logger.debug( _logger.debug(
"Signing PDF document '%s' for IDs %s with certificate '%s'", "Signing PDF document '%s' for IDs %s with certificate '%s'",
self.report_name, res_ids, certificate.name, self.report_name,
res_ids,
certificate.name,
) )
signed = self.pdf_sign(pdf, certificate) signed = self.pdf_sign(pdf, certificate)
# Read signed PDF # Read signed PDF
if os.path.exists(signed): if os.path.exists(signed):
with open(signed, 'rb') as pf: with open(signed, "rb") as pf:
content = pf.read() content = pf.read()
# Manual cleanup of the temporary files # Manual cleanup of the temporary files
for fname in (pdf, signed): for fname in (pdf, signed):
try: try:
os.unlink(fname) os.unlink(fname)
except (OSError, IOError): except (OSError, IOError):
_logger.error('Error when trying to remove file %s', fname) _logger.error("Error when trying to remove file %s", fname)
if certificate.attachment: if certificate.attachment:
self._attach_signed_write(res_ids, certificate, content) self._attach_signed_write(res_ids, certificate, content)
return content, ext return content, ext

View File

@ -5,38 +5,51 @@ from odoo import api, fields, models
class ReportCertificate(models.Model): class ReportCertificate(models.Model):
_name = 'report.certificate' _name = "report.certificate"
_description = 'Report Certificate' _description = "Report Certificate"
_order = 'sequence,id' _order = "sequence,id"
@api.model @api.model
def _default_company(self): def _default_company(self):
m_company = self.env['res.company'] m_company = self.env["res.company"]
return m_company._company_default_get('report.certificate') return m_company._company_default_get("report.certificate")
sequence = fields.Integer(default=10) sequence = fields.Integer(default=10)
name = fields.Char(required=True) name = fields.Char(required=True)
path = fields.Char( path = fields.Char(
string="Certificate file path", required=True, string="Certificate file path",
help="Path to PKCS#12 certificate file") required=True,
help="Path to PKCS#12 certificate file",
)
password_file = fields.Char( password_file = fields.Char(
string="Password file path", required=True, string="Password file path",
help="Path to certificate password file") required=True,
help="Path to certificate password file",
)
model_id = fields.Many2one( model_id = fields.Many2one(
string="Model", required=True, string="Model",
comodel_name='ir.model', required=True,
help="Model where apply this certificate") comodel_name="ir.model",
help="Model where apply this certificate",
)
domain = fields.Char( domain = fields.Char(
string="Domain", string="Domain",
help="Domain for filtering if sign or not the document") help="Domain for filtering if sign or not the document",
)
allow_only_one = fields.Boolean( allow_only_one = fields.Boolean(
string="Allow only one document", default=True, string="Allow only one document",
default=True,
help="If True, this certificate can not be useb to sign " help="If True, this certificate can not be useb to sign "
"a PDF from several documents.") "a PDF from several documents.",
)
attachment = fields.Char( attachment = fields.Char(
string="Save as attachment", string="Save as attachment",
help="Filename used to store signed document as attachment. " help="Filename used to store signed document as attachment. "
"Keep empty to not save signed document.") "Keep empty to not save signed document.",
)
company_id = fields.Many2one( company_id = fields.Many2one(
string='Company', comodel_name='res.company', string="Company",
required=True, default=_default_company) comodel_name="res.company",
required=True,
default=_default_company,
)

View File

@ -1,13 +1,14 @@
# Copyright 2015 Tecnativa - Antonio Espinosa # Copyright 2015 Tecnativa - Antonio Espinosa
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
from odoo import models, fields from odoo import fields, models
class ResCompany(models.Model): class ResCompany(models.Model):
_inherit = 'res.company' _inherit = "res.company"
report_certificate_ids = fields.One2many( report_certificate_ids = fields.One2many(
string="PDF report certificates", string="PDF report certificates",
comodel_name='report.certificate', comodel_name="report.certificate",
inverse_name='company_id') inverse_name="company_id",
)

View File

@ -1 +1 @@
admin admin

View File

@ -1 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" enable-background="new 0 0 100 100" xml:space="preserve"><rect x="24.469" y="28.037" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="16.401" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="39.672" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="51.309" fill="#000000" width="24.345" height="5.592"/><path fill="#000000" d="M80.018,5.03H19.982c-3.724,0-6.745,3.019-6.745,6.745v73.077c0,3.727,3.021,6.744,6.745,6.744h33.784v-4.66 H22.829c-2.485,0-4.498-2.014-4.498-4.496V14.187c0-2.483,2.013-4.496,4.498-4.496h54.342c2.483,0,4.498,2.013,4.498,4.496v68.252 c0,2.482-2.015,4.496-4.498,4.496h-1.819v4.66h4.666c3.726,0,6.746-3.019,6.746-6.744V11.775C86.764,8.049,83.742,5.03,80.018,5.03z "/><path fill="#000000" d="M75.308,69.185l5.763-2.743l-5.723-2.822l3.619-5.255l-6.369,0.417l0.51-6.362l-5.309,3.543l-2.741-5.763 l-2.823,5.722l-5.256-3.616l0.416,6.366l-6.36-0.506l3.544,5.306l-5.764,2.742l5.723,2.822l-3.619,5.256l6.367-0.415l-0.508,6.359 l5.309-3.545l2.742,5.765l2.822-5.723l5.255,3.618l-0.414-6.366l6.36,0.509L75.308,69.185z M73.956,63.635L64.892,74.36 c-0.456,0.546-1.134,0.862-1.847,0.867c-0.004,0-0.01,0-0.016,0c-0.707,0-1.379-0.308-1.843-0.838l-4.05-4.647 c-0.886-1.019-0.78-2.561,0.235-3.446c1.019-0.887,2.562-0.781,3.448,0.237l2.178,2.5l7.228-8.55 c0.868-1.03,2.411-1.162,3.441-0.289C74.696,61.065,74.826,62.605,73.956,63.635z"/><polygon fill="#000000" points="72.905,94.957 64.818,88.149 56.777,94.957 56.777,81.658 61.884,79.125 64.818,84.539 67.877,79.141 72.905,81.658 "/><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Anton Noskov</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg> <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" x="0px" y="0px" viewBox="0 0 100 125" enable-background="new 0 0 100 100" xml:space="preserve"><rect x="24.469" y="28.037" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="16.401" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="39.672" fill="#000000" width="51.159" height="5.592"/><rect x="24.469" y="51.309" fill="#000000" width="24.345" height="5.592"/><path fill="#000000" d="M80.018,5.03H19.982c-3.724,0-6.745,3.019-6.745,6.745v73.077c0,3.727,3.021,6.744,6.745,6.744h33.784v-4.66 H22.829c-2.485,0-4.498-2.014-4.498-4.496V14.187c0-2.483,2.013-4.496,4.498-4.496h54.342c2.483,0,4.498,2.013,4.498,4.496v68.252 c0,2.482-2.015,4.496-4.498,4.496h-1.819v4.66h4.666c3.726,0,6.746-3.019,6.746-6.744V11.775C86.764,8.049,83.742,5.03,80.018,5.03z "/><path fill="#000000" d="M75.308,69.185l5.763-2.743l-5.723-2.822l3.619-5.255l-6.369,0.417l0.51-6.362l-5.309,3.543l-2.741-5.763 l-2.823,5.722l-5.256-3.616l0.416,6.366l-6.36-0.506l3.544,5.306l-5.764,2.742l5.723,2.822l-3.619,5.256l6.367-0.415l-0.508,6.359 l5.309-3.545l2.742,5.765l2.822-5.723l5.255,3.618l-0.414-6.366l6.36,0.509L75.308,69.185z M73.956,63.635L64.892,74.36 c-0.456,0.546-1.134,0.862-1.847,0.867c-0.004,0-0.01,0-0.016,0c-0.707,0-1.379-0.308-1.843-0.838l-4.05-4.647 c-0.886-1.019-0.78-2.561,0.235-3.446c1.019-0.887,2.562-0.781,3.448,0.237l2.178,2.5l7.228-8.55 c0.868-1.03,2.411-1.162,3.441-0.289C74.696,61.065,74.826,62.605,73.956,63.635z"/><polygon fill="#000000" points="72.905,94.957 64.818,88.149 56.777,94.957 56.777,81.658 61.884,79.125 64.818,84.539 67.877,79.141 72.905,81.658 "/><text x="0" y="115" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">Created by Anton Noskov</text><text x="0" y="120" fill="#000000" font-size="5px" font-weight="bold" font-family="'Helvetica Neue', Helvetica, Arial-Unicode, Arial, Sans-serif">from the Noun Project</text></svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -1,3 +1,3 @@
productname=jPdfSign productname=jPdfSign
version=0.3.1 version=0.3.1
jar-filename=jPdfSign.jar jar-filename=jPdfSign.jar

View File

@ -7,21 +7,20 @@ from odoo.tests.common import HttpCase
class TestReportQwebSigner(HttpCase): class TestReportQwebSigner(HttpCase):
def setUp(self): def setUp(self):
super(TestReportQwebSigner, self).setUp() super(TestReportQwebSigner, self).setUp()
self.partner = self.env['res.partner'].create({ self.partner = self.env["res.partner"].create(
'name': 'Test partner', {"name": "Test partner", "customer": True}
'customer': True, )
})
self.report = self.env.ref( self.report = self.env.ref(
'report_qweb_signer.partner_demo_report' "report_qweb_signer.partner_demo_report"
).with_context(force_report_rendering=True) ).with_context(force_report_rendering=True)
def test_report_qweb_signer(self): def test_report_qweb_signer(self):
self.report.render_qweb_pdf(self.partner.ids) self.report.render_qweb_pdf(self.partner.ids)
# Reprint again for taking the PDF from attachment # Reprint again for taking the PDF from attachment
IrAttachment = self.env['ir.attachment'] IrAttachment = self.env["ir.attachment"]
domain = [ domain = [
('res_id', '=', self.partner.id), ("res_id", "=", self.partner.id),
('res_model', '=', self.partner._name), ("res_model", "=", self.partner._name),
] ]
num_attachments = IrAttachment.search_count(domain) num_attachments = IrAttachment.search_count(domain)
self.report.render_qweb_pdf(self.partner.ids) self.report.render_qweb_pdf(self.partner.ids)

View File

@ -1,65 +1,67 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- <!--
Copyright 2015 Tecnativa - Antonio Espinosa Copyright 2015 Tecnativa - Antonio Espinosa
Copyright 2017 Tecnativa - Pedro M. Baeza Copyright 2017 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
--> -->
<odoo> <odoo>
<record id="view_report_certificate_form" model="ir.ui.view">
<record id="view_report_certificate_form" model="ir.ui.view"> <field name="name">report.certificate.form</field>
<field name="name">report.certificate.form</field> <field name="model">report.certificate</field>
<field name="model">report.certificate</field> <field name="arch" type="xml">
<field name="arch" type="xml"> <form string="PDF report certificate">
<form string="PDF report certificate"> <sheet string="PDF report certificate">
<sheet string="PDF report certificate"> <div class="oe_title">
<div class="oe_title"> <label for="name" class="oe_edit_only" />
<label for="name" class="oe_edit_only"/> <h1>
<h1><field name="name"/></h1> <field name="name" />
</div> </h1>
<group> </div>
<group> <group>
<field name="path"/> <group>
<field name="password_file"/> <field name="path" />
<field name="model_id"/> <field name="password_file" />
<field name="model_id" />
</group>
<group>
<field name="domain" />
<field name="allow_only_one" />
<field name="attachment" />
<field
name="company_id"
widget="selection"
groups="base.group_multi_company"
/>
</group>
</group> </group>
<group> </sheet>
<field name="domain"/> </form>
<field name="allow_only_one"/> </field>
<field name="attachment"/> </record>
<field name="company_id" widget="selection" <record id="view_report_certificate_tree" model="ir.ui.view">
groups="base.group_multi_company"/> <field name="name">report.certificate.tree</field>
</group> <field name="model">report.certificate</field>
</group> <field name="arch" type="xml">
</sheet> <tree string="PDF report certificates">
</form> <field name="sequence" widget="handle" />
</field> <field name="name" />
</record> <field name="path" />
<field name="model_id" />
<record id="view_report_certificate_tree" model="ir.ui.view" > <field name="domain" />
<field name="name">report.certificate.tree</field> <field name="company_id" />
<field name="model">report.certificate</field> </tree>
<field name="arch" type="xml"> </field>
<tree string="PDF report certificates"> </record>
<field name="sequence" widget="handle"/> <record id="action_report_certificate" model="ir.actions.act_window">
<field name="name"/> <field name="name">PDF certificates</field>
<field name="path"/> <field name="res_model">report.certificate</field>
<field name="model_id"/> <field name="view_type">form</field>
<field name="domain"/> <field name="view_mode">tree,form</field>
<field name="company_id"/> </record>
</tree> <menuitem
</field> id="menu_report_certificate"
</record> name="PDF certificates"
parent="base.reporting_menuitem"
<record id="action_report_certificate" model="ir.actions.act_window"> action="action_report_certificate"
<field name="name">PDF certificates</field> />
<field name="res_model">report.certificate</field>
<field name="view_type">form</field>
<field name="view_mode">tree,form</field>
</record>
<menuitem id="menu_report_certificate"
name="PDF certificates"
parent="base.reporting_menuitem"
action="action_report_certificate"/>
</odoo> </odoo>

View File

@ -1,11 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8" ?>
<!-- <!--
Copyright 2015 Tecnativa - Antonio Espinosa Copyright 2015 Tecnativa - Antonio Espinosa
Copyright 2017 Tecnativa - Pedro M. Baeza Copyright 2017 Tecnativa - Pedro M. Baeza
License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
--> -->
<odoo> <odoo>
<record id="view_company_form" model="ir.ui.view"> <record id="view_company_form" model="ir.ui.view">
<field name="name">Add PDF report certificates list</field> <field name="name">Add PDF report certificates list</field>
<field name="inherit_id" ref="base.view_company_form" /> <field name="inherit_id" ref="base.view_company_form" />
@ -13,14 +12,17 @@ License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html).
<field name="arch" type="xml"> <field name="arch" type="xml">
<data> <data>
<notebook position="inside"> <notebook position="inside">
<page name="pdf_sign_certificate" string="Certificates (PDF signing)"> <page
<field name="report_certificate_ids" name="pdf_sign_certificate"
context="{'default_company_id': active_id}" string="Certificates (PDF signing)"
>
<field
name="report_certificate_ids"
context="{'default_company_id': active_id}"
/> />
</page> </page>
</notebook> </notebook>
</data> </data>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@ -0,0 +1 @@
../../../../report_qweb_signer

View File

@ -0,0 +1,6 @@
import setuptools
setuptools.setup(
setup_requires=['setuptools-odoo'],
odoo_addon=True,
)