[MERGE] report: Migration to new API

This commit is contained in:
Yannick Tivisse
2016-07-19 11:58:44 +02:00
30 changed files with 376 additions and 419 deletions
@@ -18,9 +18,10 @@ class PayslipLinesContributionRegister(models.TransientModel):
@api.multi
def print_report(self):
active_ids = self.env.context.get('active_ids', [])
datas = {
'ids': self.env.context.get('active_ids', []),
'ids': active_ids,
'model': 'hr.contribution.register',
'form': self.read()[0]
}
return self.env['report'].get_action(self.browse(), 'hr_payroll.report_contributionregister', data=datas)
return self.env['report'].get_action(self.browse(active_ids), 'hr_payroll.report_contributionregister', data=datas)
+6 -7
View File
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import fields, osv
from odoo import api
class product_price_list(osv.osv_memory):
_name = 'product.price_list'
@@ -24,16 +24,15 @@ class product_price_list(osv.osv_memory):
'qty5': 0,
}
def print_report(self, cr, uid, ids, context=None):
@api.multi
def print_report(self):
"""
To get the date and print the report
@return : return report
"""
if context is None:
context = {}
datas = {'ids': context.get('active_ids', [])}
res = self.read(cr, uid, ids, ['price_list','qty1', 'qty2','qty3','qty4','qty5'], context=context)
datas = {'ids': self.env.context.get('active_ids', [])}
res = self.read(['price_list', 'qty1', 'qty2', 'qty3', 'qty4', 'qty5'])
res = res and res[0] or {}
res['price_list'] = res['price_list'][0]
datas['form'] = res
return self.pool['report'].get_action(cr, uid, [], 'product.report_pricelist', data=datas, context=context)
return self.env['report'].get_action(self, 'product.report_pricelist', data=datas)
+3
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import controllers
import models
import tests
+9 -7
View File
@@ -1,23 +1,25 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
{
'name': 'Report',
'category': 'Base',
'summary': 'Hidden',
'version': '1.0',
'description': """
Report
""",
'depends': ['base', 'web'],
'data': [
'views/layouts.xml',
'views/views.xml',
'data/report_paperformat.xml',
'data/report_paperformat_data.xml',
'security/ir.model.access.csv',
'views/report.xml',
'views/ir_actions.xml',
'views/layout_templates.xml',
'views/report_paperformat_views.xml',
'views/report_templates.xml',
'views/res_company_views.xml',
'views/ir_actions_report_views.xml',
],
'qweb' : [
'static/src/xml/*.xml',
],
'installable': True,
'auto_install': True,
}
+3
View File
@@ -1 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import main
+16 -19
View File
@@ -1,18 +1,17 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.addons.web.http import Controller, route, request
from openerp.addons.web.controllers.main import _serialize_exception, content_disposition
from openerp.tools import html_escape
from openerp.tools.safe_eval import safe_eval as eval
import json
import time
from werkzeug import exceptions, url_decode
from werkzeug.datastructures import Headers
from werkzeug.test import Client
from werkzeug.wrappers import BaseResponse
from werkzeug.datastructures import Headers
from reportlab.graphics.barcode import createBarcodeDrawing
from odoo.http import Controller, route, request
from odoo.tools import html_escape
from odoo.addons.web.controllers.main import _serialize_exception, content_disposition
from odoo.tools.safe_eval import safe_eval as eval
class ReportController(Controller):
@@ -25,8 +24,8 @@ class ReportController(Controller):
'/report/<converter>/<reportname>/<docids>',
], type='http', auth='user', website=True)
def report_routes(self, reportname, docids=None, converter=None, **data):
report_obj = request.registry['report']
cr, uid, context = request.cr, request.uid, request.context
report_obj = request.env['report']
context = dict(request.env.context)
if docids:
docids = [int(i) for i in docids.split(',')]
@@ -39,12 +38,11 @@ class ReportController(Controller):
if data['context'].get('lang'):
del data['context']['lang']
context.update(data['context'])
if converter == 'html':
html = report_obj.get_html(cr, uid, docids, reportname, data=data, context=context)
html = report_obj.with_context(context).get_html(docids, reportname, data=data)
return request.make_response(html)
elif converter == 'pdf':
pdf = report_obj.get_pdf(cr, uid, docids, reportname, data=data, context=context)
pdf = report_obj.with_context(context).get_pdf(docids, reportname, data=data)
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
else:
@@ -56,9 +54,9 @@ class ReportController(Controller):
@route(['/report/barcode', '/report/barcode/<type>/<path:value>'], type='http', auth="user")
def report_barcode(self, type, value, width=600, height=100, humanreadable=0):
"""Contoller able to render barcode images thanks to reportlab.
Samples:
Samples:
<img t-att-src="'/report/barcode/QR/%s' % o.name"/>
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' %
<img t-att-src="'/report/barcode/?type=%s&amp;value=%s&amp;width=%s&amp;height=%s' %
('QR', o.name, 200, 200)"/>
:param type: Accepted types: 'Codabar', 'Code11', 'Code128', 'EAN13', 'EAN8', 'Extended39',
@@ -68,7 +66,7 @@ class ReportController(Controller):
at the bottom of the output image
"""
try:
barcode = request.registry['report'].barcode(type, value, width=width, height=height, humanreadable=humanreadable)
barcode = request.env['report'].barcode(type, value, width=width, height=height, humanreadable=humanreadable)
except (ValueError, AttributeError):
raise exceptions.HTTPException(description='Cannot convert into barcode.')
@@ -101,8 +99,7 @@ class ReportController(Controller):
data = url_decode(url.split('?')[1]).items() # decoding the args represented in JSON
response = self.report_routes(reportname, converter='pdf', **dict(data))
cr, uid = request.cr, request.uid
report = request.registry['report']._get_report_from_name(cr, uid, reportname)
report = request.env['report']._get_report_from_name(reportname)
filename = "%s.%s" % (report.name, "pdf")
if docids:
ids = [int(x) for x in docids.split(",")]
@@ -112,7 +109,7 @@ class ReportController(Controller):
response.headers.add('Content-Disposition', content_disposition(filename))
response.set_cookie('fileToken', token)
return response
elif type =='controller':
elif type == 'controller':
reqheaders = Headers(request.httprequest.headers)
response = Client(request.httprequest.app, BaseResponse).get(url, headers=reqheaders, follow_redirects=True)
response.set_cookie('fileToken', token)
@@ -130,4 +127,4 @@ class ReportController(Controller):
@route(['/report/check_wkhtmltopdf'], type='json', auth="user")
def check_wkhtmltopdf(self):
return request.registry['report']._check_wkhtmltopdf()
return request.env['report']._check_wkhtmltopdf()
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<record id="paperformat_euro" model="report.paperformat">
<field name="name">European A4</field>
<field name="default" eval="True" />
@@ -32,5 +31,4 @@
<field name="header_spacing">35</field>
<field name="dpi">90</field>
</record>
</data>
</openerp>
</odoo>
+5 -3
View File
@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import abstract_report
import ir_actions
import ir_actions_report_xml
import ir_http
import ir_qweb
import report
import report_paperformat
import abstract_report
import ir_qweb
import res_company
+13 -12
View File
@@ -1,17 +1,18 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv
from odoo import api, models
class AbstractReport(osv.AbstractModel):
class AbstractReport(models.AbstractModel):
"""Model used to embed old style reports"""
_name = 'report.abstract_report'
_template = None
_wrapped_report_class = None
def render_html(self, cr, uid, ids, data=None, context=None):
context = dict(context or {})
@api.multi
def render_html(self, data=None):
context = dict(self.env.context or {})
# If the key 'landscape' is present in data['form'], passing it into the context
if data and data.get('form', {}).get('landscape'):
@@ -20,18 +21,18 @@ class AbstractReport(osv.AbstractModel):
if context and context.get('active_ids'):
# Browse the selected objects via their reference in context
model = context.get('active_model') or context.get('model')
objects_model = self.pool[model]
objects = objects_model.browse(cr, uid, context['active_ids'], context=context)
objects_model = self.env[model]
objects = objects_model.with_context(context).browse(context['active_ids'])
else:
# If no context is set (for instance, during test execution), build one
model = self.pool['report']._get_report_from_name(cr, uid, self._template).model
objects_model = self.pool[model]
objects = objects_model.browse(cr, uid, ids, context=context)
model = self.env['report']._get_report_from_name(self._template).model
objects_model = self.env[model]
objects = objects_model.with_context(context).browse(self.ids)
context['active_model'] = model
context['active_ids'] = ids
context['active_ids'] = self.ids
# Generate the old style report
wrapped_report = self._wrapped_report_class(cr, uid, '', context=context)
wrapped_report = self.with_context(context)._wrapped_report_class(self.env.cr, self.env.uid, '', context=self.env.context)
wrapped_report.set_context(objects, data, context['active_ids'])
# Rendering self._template with the wrapped report instance localcontext as
@@ -45,4 +46,4 @@ class AbstractReport(osv.AbstractModel):
docargs['doc_ids'] = context['active_ids']
docargs['doc_model'] = model
return self.pool['report'].render(cr, uid, [], self._template, docargs, context=context)
return self.env['report'].with_context(context).render(self._template, docargs)
-6
View File
@@ -1,6 +0,0 @@
from openerp import models, fields
class Actions(models.Model):
_inherit = 'ir.actions.report.xml'
print_report_name = fields.Char('Printed Report Name', help="This is the filename of the report going to download. Keep empty to not change the report filename. You can use a python expression with the object and time variables.")
@@ -0,0 +1,24 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import api, fields, models
class ir_actions_report(models.Model):
_inherit = 'ir.actions.report.xml'
paperformat_id = fields.Many2one('report.paperformat', 'Paper format')
print_report_name = fields.Char('Printed Report Name',
help="This is the filename of the report going to download. Keep empty to not change the report filename. You can use a python expression with the object and time variables.")
@api.multi
def associated_view(self):
"""Used in the ir.actions.report.xml form view in order to search naively after the view(s)
used in the rendering.
"""
self.ensure_one()
action_ref = self.env.ref('base.action_ui_view')
if not action_ref:
return False
action_data = action_ref.read()[0]
action_data['domain'] = [('name', 'ilike', self.report_name.split('.')[1]), ('type', '=', 'qweb')]
return action_data
+3
View File
@@ -1,3 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo import models
+10 -8
View File
@@ -1,10 +1,11 @@
from reportlab.graphics.barcode import createBarcodeDrawing
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv
from openerp.addons.base.ir.ir_qweb import unicodifier
from odoo import api, models
from odoo.addons.base.ir.ir_qweb import unicodifier
class BarcodeConverter(osv.AbstractModel):
class IrQwebFieldBarcode(models.AbstractModel):
""" ``barcode`` widget rendering, inserts a data:uri-using image tag in the
document. May be overridden by e.g. the website module to generate links
instead.
@@ -12,14 +13,15 @@ class BarcodeConverter(osv.AbstractModel):
_name = 'ir.qweb.field.barcode'
_inherit = 'ir.qweb.field'
def value_to_html(self, cr, uid, value, options, context=None):
options = options or {}
@api.model
def value_to_html(self, value, options=None):
barcode_type = options.get('type', 'Code128')
barcode = self.pool['report'].barcode(
barcode = self.env['report'].barcode(
barcode_type,
value,
**dict((key, value) for key, value in options.items() if key in ['width', 'height', 'humanreadable']))
return unicodifier('<img src="data:%s;base64,%s">' % ('png', barcode.encode('base64')))
def from_html(self, cr, uid, model, field, element, context=None):
@api.model
def from_html(self, model, field, element):
return None
+71 -99
View File
@@ -1,26 +1,24 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp import api
from openerp import SUPERUSER_ID
from openerp.exceptions import AccessError
from openerp.osv import osv, fields
from openerp.sql_db import TestCursor
from openerp.tools import config
from openerp.tools.misc import find_in_path
from openerp.tools.translate import _
from openerp.addons.web.http import request
from openerp.tools.safe_eval import safe_eval as eval
from openerp.exceptions import UserError
from odoo import api, fields, models, _
from odoo.exceptions import AccessError
from odoo.sql_db import TestCursor
from odoo.tools import config
from odoo.tools.misc import find_in_path
from odoo.addons.web.http import request
from odoo.tools.safe_eval import safe_eval as eval
from odoo.exceptions import UserError
import re
import time
import base64
import logging
import tempfile
import lxml.html
import os
import re
import subprocess
import tempfile
import time
from contextlib import closing
from distutils.version import LooseVersion
from functools import partial
@@ -73,7 +71,7 @@ else:
wkhtmltopdf_state = 'workers'
class Report(osv.Model):
class Report(models.Model):
_name = "report"
_description = "Report"
@@ -83,7 +81,8 @@ class Report(osv.Model):
# Extension of ir_ui_view.render with arguments frequently used in reports
#--------------------------------------------------------------------------
def render(self, cr, uid, ids, template, values=None, context=None):
@api.multi
def render(self, template, values=None):
"""Allow to render a QWeb template python-side. This function returns the 'ir.ui.view'
render but embellish it with some variables/methods used in reports.
@@ -93,14 +92,11 @@ class Report(osv.Model):
if values is None:
values = {}
if context is None:
context = {}
context = dict(self.env.context, inherit_branding=True) # Tell QWeb to brand the generated html
context = dict(context, inherit_branding=True) # Tell QWeb to brand the generated html
view_obj = self.pool['ir.ui.view']
user = self.pool['res.users'].browse(cr, uid, uid, context=context)
view_obj = self.env['ir.ui.view']
# Browse the user instead of using the sudo self.env.user
user = self.env['res.users'].browse(self.env.uid)
website = None
if request and hasattr(request, 'website'):
if request.website is not None:
@@ -109,51 +105,42 @@ class Report(osv.Model):
values.update(
time=time,
context_timestamp=lambda t: fields.datetime.context_timestamp(cr, uid, t, context),
context_timestamp=lambda t: fields.Datetime.context_timestamp(self.with_context(tz=user.tz), t),
editable=True,
user=user,
res_company=user.company_id,
website=website,
web_base_url=self.pool['ir.config_parameter'].get_param(cr, SUPERUSER_ID, 'web.base.url', default='')
web_base_url=self.env['ir.config_parameter'].get_param('web.base.url', default='')
)
return view_obj.render_template(cr, uid, template, values, context=context)
return view_obj.render_template(template, values)
#--------------------------------------------------------------------------
# Main report methods
#--------------------------------------------------------------------------
@api.v7
def get_html(self, cr, uid, ids, report_name, data=None, context=None):
@api.model
def get_html(self, docids, report_name, data=None):
"""This method generates and returns html version of a report.
"""
# If the report is using a custom model to render its html, we must use it.
# Otherwise, fallback on the generic html rendering.
report_model_name = 'report.%s' % report_name
particularreport_obj = self.pool.get(report_model_name)
if particularreport_obj is not None:
return particularreport_obj.render_html(cr, uid, ids, data=data, context=context)
report_model = self.env.get(report_model_name)
if report_model is not None:
return report_model.render_html(data=data)
else:
report = self._get_report_from_name(cr, uid, report_name)
report_obj = self.pool[report.model]
docs = report_obj.browse(cr, uid, ids, context=context)
report = self._get_report_from_name(report_name)
docs = self.env[report.model].browse(docids)
docargs = {
'doc_ids': ids,
'doc_ids': docids,
'doc_model': report.model,
'docs': docs,
}
return self.render(cr, uid, [], report.report_name, docargs, context=context).encode('utf-8')
return self.render(report.report_name, docargs).encode('utf-8')
@api.v8
def get_html(self, records, report_name, data=None):
return Report.get_html(self._model, self._cr, self._uid, records.ids,
report_name, data=data, context=self._context)
@api.v7
def get_pdf(self, cr, uid, ids, report_name, html=None, data=None, context=None):
@api.model
def get_pdf(self, docids, report_name, html=None, data=None):
"""This method generates and returns pdf version of a report.
"""
if context is None:
context = {}
# As the assets are generated during the same transaction as the rendering of the
# templates calling them, there is a scenario where the assets are unreachable: when
# you make a request to read the assets while the transaction creating them is not done.
@@ -162,8 +149,9 @@ class Report(osv.Model):
# This scenario happens when you want to print a PDF report for the first time, as the
# assets are not in cache and must be generated. To workaround this issue, we manually
# commit the writes in the `ir.attachment` table. It is done thanks to a key in the context.
context = dict(self.env.context)
if not config['test_enable']:
context = dict(context, commit_assetsbundle=True)
context['commit_assetsbundle'] = True
# Disable the debug mode in the PDF rendering in order to not split the assets bundle
# into separated files to load. This is done because of an issue in wkhtmltopdf
@@ -171,25 +159,25 @@ class Report(osv.Model):
# Without this, the header/footer of the reports randomly disapear
# because the resources files are not loaded in time.
# https://github.com/wkhtmltopdf/wkhtmltopdf/issues/2083
context = dict(context, debug=False)
context['debug'] = False
if html is None:
html = self.get_html(cr, uid, ids, report_name, data=data, context=context)
html = self.get_html(docids, report_name, data=data)
# The test cursor prevents the use of another environnment while the current
# transaction is not finished, leading to a deadlock when the report requests
# an asset bundle during the execution of test scenarios. In this case, return
# the html version.
if isinstance(cr, TestCursor):
if isinstance(self.env.cr, TestCursor):
return html
# Get the ir.actions.report.xml record we are working on.
report = self._get_report_from_name(cr, uid, report_name)
report = self._get_report_from_name(report_name)
# Check if we have to save the report or if we have to get one from the db.
save_in_attachment = self._check_attachment_use(cr, uid, ids, report)
save_in_attachment = self._check_attachment_use(docids, report)
# Get the paperformat associated to the report, otherwise fallback on the company one.
if not report.paperformat_id:
user = self.pool['res.users'].browse(cr, uid, uid)
user = self.env['res.users'].browse(self.env.uid) # Rebrowse to avoid sudo user from self.env.user
paperformat = user.company_id.paperformat_id
else:
paperformat = report.paperformat_id
@@ -198,12 +186,12 @@ class Report(osv.Model):
headerhtml = []
contenthtml = []
footerhtml = []
irconfig_obj = self.pool['ir.config_parameter']
base_url = irconfig_obj.get_param(cr, SUPERUSER_ID, 'report.url') or irconfig_obj.get_param(cr, SUPERUSER_ID, 'web.base.url')
irconfig_obj = self.env['ir.config_parameter'].sudo()
base_url = irconfig_obj.get_param('report.url') or irconfig_obj.get_param('web.base.url')
# Minimal page renderer
view_obj = self.pool['ir.ui.view']
render_minimal = partial(view_obj.render_template, cr, uid, 'report.minimal_layout', context=context)
view_obj = self.env['ir.ui.view']
render_minimal = partial(view_obj.render_template, 'report.minimal_layout')
# The received html report must be simplified. We convert it in a xml tree
# in order to extract headers, bodies and footers.
@@ -226,8 +214,8 @@ class Report(osv.Model):
# must set a relation between report ids and report's content. We use the QWeb
# branding in order to do so: searching after a node having a data-oe-model
# attribute with the value of the current report model and read its oe-id attribute
if ids and len(ids) == 1:
reportid = ids[0]
if docids and len(docids) == 1:
reportid = docids[0]
else:
oemodelnode = node.find(".//*[@data-oe-model='%s']" % report.model)
if oemodelnode is not None:
@@ -257,33 +245,26 @@ class Report(osv.Model):
# Run wkhtmltopdf process
return self._run_wkhtmltopdf(
cr, uid, headerhtml, footerhtml, contenthtml, context.get('landscape'),
headerhtml, footerhtml, contenthtml, context.get('landscape'),
paperformat, specific_paperformat_args, save_in_attachment,
context.get('set_viewport_size')
)
@api.v8
def get_pdf(self, records, report_name, html=None, data=None):
return Report.get_pdf(self._model, self._cr, self._uid, records.ids,
report_name, html=html, data=data, context=self._context)
@api.v7
def get_action(self, cr, uid, ids, report_name, data=None, context=None):
@api.model
def get_action(self, docids, report_name, data=None):
"""Return an action of type ir.actions.report.xml.
:param ids: Ids of the records to print (if not used, pass an empty list)
:param report_name: Name of the template to generate an action for
"""
if ids:
if not isinstance(ids, list):
ids = [ids]
context = dict(context or {}, active_ids=ids)
if isinstance(docids, list):
active_ids = docids
else:
active_ids = docids.ids
report_obj = self.pool['ir.actions.report.xml']
idreport = report_obj.search(cr, uid, [('report_name', '=', report_name)], context=context)
try:
report = report_obj.browse(cr, uid, idreport[0], context=context)
except IndexError:
context = dict(self.env.context, active_ids=active_ids)
report = self.env['ir.actions.report.xml'].with_context(context).search([('report_name', '=', report_name)])
if not report:
raise UserError(_("Bad Report Reference") + _("This report is not loaded into the database: %s.") % report_name)
return {
@@ -296,16 +277,11 @@ class Report(osv.Model):
'context': context,
}
@api.v8
def get_action(self, records, report_name, data=None):
return Report.get_action(self._model, self._cr, self._uid, records.ids,
report_name, data=data, context=self._context)
#--------------------------------------------------------------------------
# Report generation helpers
#--------------------------------------------------------------------------
@api.v7
def _check_attachment_use(self, cr, uid, ids, report):
@api.model
def _check_attachment_use(self, docids, report):
""" Check attachment_use field. If set to true and an existing pdf is already saved, load
this one now. Else, mark save it.
"""
@@ -314,12 +290,12 @@ class Report(osv.Model):
save_in_attachment['loaded_documents'] = {}
if report.attachment:
records = self.pool[report.model].browse(cr, uid, ids)
filenames = self._attachment_filename(cr, uid, records, report)
records = self.env[report.model].browse(docids)
filenames = self._attachment_filename(records, report)
attachments = None
if report.attachment_use:
attachments = self._attachment_stored(cr, uid, records, report, filenames=filenames)
for record_id in ids:
attachments = self._attachment_stored(records, report, filenames=filenames)
for record_id in docids:
filename = filenames[record_id]
# If the user has checked 'Reload from Attachment'
@@ -344,11 +320,6 @@ class Report(osv.Model):
return save_in_attachment
@api.v8
def _check_attachment_use(self, records, report):
return Report._check_attachment_use(
self._model, self._cr, self._uid, records.ids, report, context=self._context)
@api.model
def _attachment_filename(self, records, report):
return dict((record.id, eval(report.attachment, {'object': record, 'time': time})) for record in records)
@@ -366,7 +337,8 @@ class Report(osv.Model):
def _check_wkhtmltopdf(self):
return wkhtmltopdf_state
def _run_wkhtmltopdf(self, cr, uid, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None, set_viewport_size=False):
@api.model
def _run_wkhtmltopdf(self, headers, footers, bodies, landscape, paperformat, spec_paperformat_args=None, save_in_attachment=None, set_viewport_size=False):
"""Execute wkhtmltopdf as a subprocess in order to convert html given in input into a pdf
document.
@@ -469,7 +441,7 @@ class Report(osv.Model):
'res_id': reporthtml[0],
}
try:
self.pool['ir.attachment'].create(cr, uid, attachment)
self.env['ir.attachment'].create(attachment)
except AccessError:
_logger.info("Cannot save PDF report %r as attachment", attachment['name'])
else:
@@ -499,16 +471,16 @@ class Report(osv.Model):
return content
def _get_report_from_name(self, cr, uid, report_name):
@api.model
def _get_report_from_name(self, report_name):
"""Get the first record of ir.actions.report.xml having the ``report_name`` as value for
the field report_name.
"""
report_obj = self.pool['ir.actions.report.xml']
report_obj = self.env['ir.actions.report.xml']
qwebtypes = ['qweb-pdf', 'qweb-html']
conditions = [('report_type', 'in', qwebtypes), ('report_name', '=', report_name)]
context = self.pool['res.users'].context_get(cr, uid)
idreport = report_obj.search(cr, uid, conditions, context=context)[0]
return report_obj.browse(cr, uid, idreport, context=context)
context = self.env['res.users'].context_get()
return report_obj.with_context(context).search(conditions, limit=1)
def _build_wkhtmltopdf_args(self, paperformat, specific_paperformat_args=None):
"""Build arguments understandable by wkhtmltopdf from a report.paperformat record.
+60 -129
View File
@@ -1,137 +1,68 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from functools import partial
from odoo import api, fields, models, _
from odoo.exceptions import ValidationError
from openerp import SUPERUSER_ID
from openerp.osv import osv, fields
class report_paperformat(osv.Model):
class report_paperformat(models.Model):
_name = "report.paperformat"
_description = "Allows customization of a report."
_columns = {'name': fields.char('Name', required=True),
'default': fields.boolean('Default paper format ?'),
'format': fields.selection([('A0', 'A0 5 841 x 1189 mm'),
('A1', 'A1 6 594 x 841 mm'),
('A2', 'A2 7 420 x 594 mm'),
('A3', 'A3 8 297 x 420 mm'),
('A4', 'A4 0 210 x 297 mm, 8.26 x 11.69 inches'),
('A5', 'A5 9 148 x 210 mm'),
('A6', 'A6 10 105 x 148 mm'),
('A7', 'A7 11 74 x 105 mm'),
('A8', 'A8 12 52 x 74 mm'),
('A9', 'A9 13 37 x 52 mm'),
('B0', 'B0 14 1000 x 1414 mm'),
('B1', 'B1 15 707 x 1000 mm'),
('B2', 'B2 17 500 x 707 mm'),
('B3', 'B3 18 353 x 500 mm'),
('B4', 'B4 19 250 x 353 mm'),
('B5', 'B5 1 176 x 250 mm, 6.93 x 9.84 inches'),
('B6', 'B6 20 125 x 176 mm'),
('B7', 'B7 21 88 x 125 mm'),
('B8', 'B8 22 62 x 88 mm'),
('B9', 'B9 23 33 x 62 mm'),
('B10', ':B10 16 31 x 44 mm'),
('C5E', 'C5E 24 163 x 229 mm'),
('Comm10E', 'Comm10E 25 105 x 241 mm, U.S. '
'Common 10 Envelope'),
('DLE', 'DLE 26 110 x 220 mm'),
('Executive', 'Executive 4 7.5 x 10 inches, '
'190.5 x 254 mm'),
('Folio', 'Folio 27 210 x 330 mm'),
('Ledger', 'Ledger 28 431.8 x 279.4 mm'),
('Legal', 'Legal 3 8.5 x 14 inches, '
'215.9 x 355.6 mm'),
('Letter', 'Letter 2 8.5 x 11 inches, '
'215.9 x 279.4 mm'),
('Tabloid', 'Tabloid 29 279.4 x 431.8 mm'),
('custom', 'Custom')],
'Paper size',
help="Select Proper Paper size"),
'margin_top': fields.float('Top Margin (mm)'),
'margin_bottom': fields.float('Bottom Margin (mm)'),
'margin_left': fields.float('Left Margin (mm)'),
'margin_right': fields.float('Right Margin (mm)'),
'page_height': fields.integer('Page height (mm)'),
'page_width': fields.integer('Page width (mm)'),
'orientation': fields.selection([('Landscape', 'Landscape'),
('Portrait', 'Portrait')],
'Orientation'),
'header_line': fields.boolean('Display a header line'),
'header_spacing': fields.integer('Header spacing'),
'dpi': fields.integer('Output DPI', required=True),
'report_ids': fields.one2many('ir.actions.report.xml',
'paperformat_id',
'Associated reports',
help="Explicitly associated reports")
}
name = fields.Char('Name', required=True)
default = fields.Boolean('Default paper format ?')
format = fields.Selection([
('A0', 'A0 5 841 x 1189 mm'),
('A1', 'A1 6 594 x 841 mm'),
('A2', 'A2 7 420 x 594 mm'),
('A3', 'A3 8 297 x 420 mm'),
('A4', 'A4 0 210 x 297 mm, 8.26 x 11.69 inches'),
('A5', 'A5 9 148 x 210 mm'),
('A6', 'A6 10 105 x 148 mm'),
('A7', 'A7 11 74 x 105 mm'),
('A8', 'A8 12 52 x 74 mm'),
('A9', 'A9 13 37 x 52 mm'),
('B0', 'B0 14 1000 x 1414 mm'),
('B1', 'B1 15 707 x 1000 mm'),
('B2', 'B2 17 500 x 707 mm'),
('B3', 'B3 18 353 x 500 mm'),
('B4', 'B4 19 250 x 353 mm'),
('B5', 'B5 1 176 x 250 mm, 6.93 x 9.84 inches'),
('B6', 'B6 20 125 x 176 mm'),
('B7', 'B7 21 88 x 125 mm'),
('B8', 'B8 22 62 x 88 mm'),
('B9', 'B9 23 33 x 62 mm'),
('B10', ':B10 16 31 x 44 mm'),
('C5E', 'C5E 24 163 x 229 mm'),
('Comm10E', 'Comm10E 25 105 x 241 mm, U.S. '
'Common 10 Envelope'),
('DLE', 'DLE 26 110 x 220 mm'),
('Executive', 'Executive 4 7.5 x 10 inches, '
'190.5 x 254 mm'),
('Folio', 'Folio 27 210 x 330 mm'),
('Ledger', 'Ledger 28 431.8 x 279.4 mm'),
('Legal', 'Legal 3 8.5 x 14 inches, '
'215.9 x 355.6 mm'),
('Letter', 'Letter 2 8.5 x 11 inches, '
'215.9 x 279.4 mm'),
('Tabloid', 'Tabloid 29 279.4 x 431.8 mm'),
('custom', 'Custom')
], 'Paper size', default='A4', help="Select Proper Paper size")
margin_top = fields.Float('Top Margin (mm)', default=40)
margin_bottom = fields.Float('Bottom Margin (mm)', default=20)
margin_left = fields.Float('Left Margin (mm)', default=7)
margin_right = fields.Float('Right Margin (mm)', default=7)
page_height = fields.Integer('Page height (mm)', default=False)
page_width = fields.Integer('Page width (mm)', default=False)
orientation = fields.Selection([
('Landscape', 'Landscape'),
('Portrait', 'Portrait')
], 'Orientation', default='Landscape')
header_line = fields.Boolean('Display a header line', default=False)
header_spacing = fields.Integer('Header spacing', default=35)
dpi = fields.Integer('Output DPI', required=True, default=90)
report_ids = fields.One2many('ir.actions.report.xml', 'paperformat_id', 'Associated reports', help="Explicitly associated reports")
def _check_format_or_page(self, cr, uid, ids, context=None):
for paperformat in self.browse(cr, uid, ids, context=context):
if paperformat.format != 'custom' and (paperformat.page_width or paperformat.page_height):
return False
return True
_constraints = [
(_check_format_or_page, 'Error ! You cannot select a format AND specific '
'page width/height.', ['format']),
]
_defaults = {
'format': 'A4',
'margin_top': 40,
'margin_bottom': 20,
'margin_left': 7,
'margin_right': 7,
'page_height': False,
'page_width': False,
'orientation': 'Landscape',
'header_line': False,
'header_spacing': 35,
'dpi': 90,
}
class res_company(osv.Model):
_inherit = 'res.company'
_columns = {'paperformat_id': fields.many2one('report.paperformat', 'Paper format')}
def init(self, cr):
# set a default paperformat based on rml one.
ref = partial(self.pool['ir.model.data'].xmlid_to_res_id, cr, SUPERUSER_ID)
ids = self.search(cr, SUPERUSER_ID, [('paperformat_id', '=', False)])
for company in self.browse(cr, SUPERUSER_ID, ids):
paperformat_id = {
'a4': ref('report.paperformat_euro'),
'us_letter': ref('report.paperformat_us'),
}.get(company.rml_paper_format) or ref('report.paperformat_euro')
if paperformat_id:
company.write({'paperformat_id': paperformat_id})
sup = super(res_company, self)
if hasattr(sup, 'init'):
sup.init(cr)
class ir_actions_report(osv.Model):
_inherit = 'ir.actions.report.xml'
def associated_view(self, cr, uid, ids, context):
"""Used in the ir.actions.report.xml form view in order to search naively after the view(s)
used in the rendering.
"""
try:
report_name = self.browse(cr, uid, ids[0], context).report_name
act_window_obj = self.pool.get('ir.actions.act_window')
view_action = act_window_obj.for_xml_id(cr, uid, 'base', 'action_ui_view', context=context)
view_action['domain'] = [('name', 'ilike', report_name.split('.')[1]), ('type', '=', 'qweb')]
return view_action
except:
return False
_columns = {'paperformat_id': fields.many2one('report.paperformat', 'Paper format')}
@api.constrains('format')
def _check_format_or_page(self):
if self.filtered(lambda x: x.format != 'custom' and (x.page_width or x.page_height)):
raise ValidationError(_('Error ! You cannot select a format AND specific page width/height.'))
+36 -12
View File
@@ -1,14 +1,36 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from openerp.osv import osv
from odoo import api, fields, models
class ResCompany(osv.Model):
class ResCompany(models.Model):
_inherit = 'res.company'
def _prepare_report_view_action(self, cr, uid, template):
template_id = self.pool['ir.model.data'].xmlid_to_res_id(cr, uid, template)
paperformat_id = fields.Many2one('report.paperformat', 'Paper format')
@api.model_cr
def init(self):
# set a default paperformat based on rml one.
for company in self.search([('paperformat_id', '=', False)]):
paperformat_euro = self.env.ref('report.paperformat_euro', False)
paperformat_us = self.env.ref('report.paperformat_us', False)
paperformat_id = {
'a4': paperformat_euro and paperformat_euro.id or False,
'us_letter': paperformat_us and paperformat_us.id or False,
}.get(company.rml_paper_format) or paperformat_euro
if paperformat_id:
company.write({'paperformat_id': paperformat_id})
sup = super(ResCompany, self)
if hasattr(sup, 'init'):
sup.init()
@api.model
def _prepare_report_view_action(self, template):
template_id = self.env.ref(template)
return {
'type': 'ir.actions.act_window',
'res_model': 'ir.ui.view',
@@ -17,12 +39,14 @@ class ResCompany(osv.Model):
'res_id': template_id,
}
def edit_external_header(self, cr, uid, ids, context=None):
return self._prepare_report_view_action(cr, uid, 'report.external_layout_header')
@api.multi
def edit_external_header(self):
return self._prepare_report_view_action('report.external_layout_header')
def edit_external_footer(self, cr, uid, ids, context=None):
return self._prepare_report_view_action(cr, uid, 'report.external_layout_footer')
def edit_internal_header(self, cr, uid, ids, context=None):
return self._prepare_report_view_action(cr, uid, 'report.internal_layout')
@api.multi
def edit_external_footer(self):
return self._prepare_report_view_action('report.external_layout_footer')
@api.multi
def edit_internal_header(self):
return self._prepare_report_view_action('report.internal_layout')
+17 -19
View File
@@ -2,35 +2,33 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import logging
import openerp
import openerp.tests
import odoo
import odoo.tests
_logger = logging.getLogger(__name__)
@openerp.tests.common.at_install(False)
@openerp.tests.common.post_install(True)
class TestReports(openerp.tests.TransactionCase):
@odoo.tests.common.at_install(False)
@odoo.tests.common.post_install(True)
class TestReports(odoo.tests.TransactionCase):
def test_reports(self):
registry, cr, uid = self.registry, self.cr, self.uid
r_model = registry('ir.actions.report.xml')
domain = [('report_type', 'like', 'qweb')]
for r in r_model.browse(cr, uid, r_model.search(cr, uid, domain)):
report_model = 'report.%s' % r.report_name
for report in self.env['ir.actions.report.xml'].search(domain):
report_model = 'report.%s' % report.report_name
try:
registry(report_model)
self.env[report_model]
except KeyError:
# Only test the generic reports here
_logger.info("testing report %s", r.report_name)
report_model = registry(r.model)
report_model_ids = report_model.search(cr, uid, [], limit=10)
if not report_model_ids:
_logger.info("no record found skipping report %s", r.report_name)
if not r.multi:
report_model_ids = report_model_ids[:1]
# Only test the generic reports here
_logger.info("testing report %s", report.report_name)
report_model = self.env[report.model]
report_records = report_model.search([], limit=10)
if not report_records:
_logger.info("no record found skipping report %s", report.report_name)
if not report.multi:
report_records = report_records[:1]
# Test report generation
registry('report').get_html(cr, uid, report_model_ids, r.report_name)
self.env['report'].get_html(report_records.ids, report.report_name)
else:
continue
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<!-- Adding a print_report_name field inside to the report action form view -->
<record id="act_report_xml_view_inherit_report" model="ir.ui.view">
<field name="name">ir.actions.report.xml.form.inherit</field>
<field name="inherit_id" ref="base.act_report_xml_view" />
<field name="model">ir.actions.report.xml</field>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='report_name']" position="after">
<field name="print_report_name" />
</xpath>
</data>
</field>
</record>
</data>
</openerp>
@@ -0,0 +1,38 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<!-- Adding a print_report_name field inside to the report action form view -->
<record id="act_report_xml_view_inherit_report" model="ir.ui.view">
<field name="name">ir.actions.report.xml.form.inherit</field>
<field name="inherit_id" ref="base.act_report_xml_view" />
<field name="model">ir.actions.report.xml</field>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='report_name']" position="after">
<field name="print_report_name" />
</xpath>
</data>
</field>
</record>
<!-- Add a paperformat and web button to the report form view -->
<record id="act_report_xml_view_inherit" model="ir.ui.view">
<field name="name">act_report_xml_view_inherit</field>
<field name="inherit_id" ref="base.act_report_xml_view" />
<field name="model">ir.actions.report.xml</field>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='attachment']" position="after">
<field name="paperformat_id" attrs="{'invisible':[('report_type','not in',['qweb-pdf'])]}"/>
<button
type="object"
class="oe_link"
string="Search associated QWeb views"
name="associated_view"
attrs="{'invisible':[('report_type', 'not in', ['qweb-pdf', 'qweb-html'])]}"
/>
</xpath>
</data>
</field>
</record>
</odoo>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<template id="report.assets_common">
<t t-call="web.less_helpers"/>
@@ -213,5 +212,4 @@
</html>
</template>
</data>
</openerp>
</odoo>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<!-- report.paperformat -->
<record id="paperformat_view_tree" model="ir.ui.view">
@@ -71,26 +70,6 @@
groups="base.group_no_one"
/>
<!-- Add a paperformat and web button to the report form view -->
<record id="act_report_xml_view_inherit" model="ir.ui.view">
<field name="name">act_report_xml_view_inherit</field>
<field name="inherit_id" ref="base.act_report_xml_view" />
<field name="model">ir.actions.report.xml</field>
<field name="arch" type="xml">
<data>
<xpath expr="//field[@name='attachment']" position="after">
<field name="paperformat_id" attrs="{'invisible':[('report_type','not in',['qweb-pdf'])]}"/>
<button
type="object"
class="oe_link"
string="Search associated QWeb views"
name="associated_view"
attrs="{'invisible':[('report_type', 'not in', ['qweb-pdf', 'qweb-html'])]}"
/>
</xpath>
</data>
</field>
</record>
<!-- Customize the company form view by adding paperformat field as well as demo reports -->
<!-- Demo reports definition -->
@@ -125,27 +104,4 @@
string="Preview External Report"
report_type="qweb-pdf"/>
<record id="reporting_settings_form_inherited" model="ir.ui.view">
<field name="name">view_company_form_inherited</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[@name='report']/group[1]" position="inside">
<group col="2" colspan="2">
<field name="paperformat_id" />
</group>
<group col="1" colspan="2" groups="base.group_no_one">
<button name="%(action_report_internalpreview)d" string="Preview Internal Report" type="action"/>
<button name="%(action_report_externalpreview)d" string="Preview External Report" type="action"/>
<button name="edit_external_header" string="Edit external header" type="object" class="oe_link"/>
<button name="edit_external_footer" string="Edit external footer" type="object" class="oe_link"/>
<button name="edit_internal_header" string="Edit internal header" type="object" class="oe_link"/>
</group>
</xpath>
</data>
</field>
</record>
</data>
</openerp>
</odoo>
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<openerp>
<data>
<odoo>
<template id="assets_backend" name="report assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script type="text/javascript" src="/report/static/src/js/qwebactionmanager.js"></script>
@@ -9,5 +8,4 @@
<link rel="stylesheet" type="text/less" href="/report/static/src/less/report_backend.less"/>
</xpath>
</template>
</data>
</openerp>
</odoo>
+25
View File
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="reporting_settings_form_inherited" model="ir.ui.view">
<field name="name">view_company_form_inherited</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[@name='report']/group[1]" position="inside">
<group col="2" colspan="2">
<field name="paperformat_id" />
</group>
<group col="1" colspan="2" groups="base.group_no_one">
<button name="%(action_report_internalpreview)d" string="Preview Internal Report" type="action"/>
<button name="%(action_report_externalpreview)d" string="Preview External Report" type="action"/>
<button name="edit_external_header" string="Edit external header" type="object" class="oe_link"/>
<button name="edit_external_footer" string="Edit external footer" type="object" class="oe_link"/>
<button name="edit_internal_header" string="Edit internal header" type="object" class="oe_link"/>
</group>
</xpath>
</data>
</field>
</record>
</odoo>
-3
View File
@@ -68,9 +68,6 @@ TODO:
'demo': [
"report/webkit_report_demo.xml",
],
'test': [
"test/print.yml",
],
'installable': True,
'auto_install': False,
}
-11
View File
@@ -1,11 +0,0 @@
-
Print the report_webkit demo report.
-
!python {model: ir.actions.report.xml}: |
import os
from openerp import tools
from openerp.report import render_report
ids = self.pool['ir.actions.report.xml'].search(cr, uid, [], {})
data, format = render_report(cr, uid, ids, 'webkit.ir.actions.report.xml', {})
if tools.config['test_report_directory']:
file(os.path.join(tools.config['test_report_directory'], 'report_webkit_demo_report.'+format), 'wb+').write(data)
+4
View File
@@ -0,0 +1,4 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import test_report_webkit
@@ -0,0 +1,15 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import os
import odoo.tests
from odoo import tools
from odoo.report import render_report
class TestReportsWebkit(odoo.tests.TransactionCase):
def test_report_webkit(self):
report_ids = self.env['ir.actions.report.xml'].search([]).ids
data, format = render_report(self.env.cr, self.env.uid, report_ids, 'webkit.ir.actions.report.xml', {}, context=self.env.context)
if tools.config['test_report_directory']:
file(os.path.join(tools.config['test_report_directory'], 'report_webkit_demo_report.' + format), 'wb+').write(data)
+2 -2
View File
@@ -227,9 +227,9 @@ class IrActionsReportXml(models.Model):
# yml tests originally written for RML reports.
if tools.config['test_enable'] and not tools.config['test_report_directory']:
# Only generate the pdf when a destination folder has been provided.
return self.pool['report'].get_html(self._cr, self._uid, res_ids, report, data=data, context=self._context), 'html'
return self.env['report'].get_html(res_ids, report, data=data), 'html'
else:
return self.pool['report'].get_pdf(self._cr, self._uid, res_ids, report, data=data, context=self._context), 'pdf'
return self.env['report'].get_pdf(res_ids, report, data=data), 'pdf'
else:
return report.create(self._cr, self._uid, res_ids, data, context=self._context)
+3 -3
View File
@@ -2,7 +2,7 @@
# Part of Odoo. See LICENSE file for full copyright and licensing details.
import openerp
from openerp import api
import interface
import print_xml
import print_fnc
@@ -18,5 +18,5 @@ def render_report(cr, uid, ids, name, data, context=None):
"""
Helper to call ``ir.actions.report.xml.render_report()``.
"""
registry = openerp.modules.registry.RegistryManager.get(cr.dbname)
return registry['ir.actions.report.xml'].render_report(cr, uid, ids, name, data, context)
env = api.Environment(cr, uid, context or {})
return env['ir.actions.report.xml'].render_report(ids, name, data)
+2 -1
View File
@@ -34,7 +34,8 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re
else:
rname_s = rname
_test_logger.info(" - Trying %s.create(%r)", rname, ids)
res = openerp.report.render_report(cr, uid, ids, rname_s, data, context)
res = openerp.report.render_report(cr, uid, ids, rname_s, data, context=context)
if not isinstance(res, tuple):
raise RuntimeError("Result of %s.create() should be a (data,format) tuple, now it is a %s" % \
(rname, type(res)))