diff --git a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py index 4524df7cd31..56bfaa28f7a 100644 --- a/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py +++ b/addons/hr_payroll/wizard/hr_payroll_contribution_register_report.py @@ -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) diff --git a/addons/product/wizard/product_price.py b/addons/product/wizard/product_price.py index cc488d4f3a7..2f5a1a14b30 100644 --- a/addons/product/wizard/product_price.py +++ b/addons/product/wizard/product_price.py @@ -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) diff --git a/addons/report/__init__.py b/addons/report/__init__.py index 9cea13339d9..d8b1dcd9413 100644 --- a/addons/report/__init__.py +++ b/addons/report/__init__.py @@ -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 diff --git a/addons/report/__openerp__.py b/addons/report/__openerp__.py index 844dedfa307..5ef4ba42d54 100644 --- a/addons/report/__openerp__.py +++ b/addons/report/__openerp__.py @@ -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, } diff --git a/addons/report/controllers/__init__.py b/addons/report/controllers/__init__.py index 8ee9bae18d9..f48716d3a69 100644 --- a/addons/report/controllers/__init__.py +++ b/addons/report/controllers/__init__.py @@ -1 +1,4 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + import main diff --git a/addons/report/controllers/main.py b/addons/report/controllers/main.py index afaa7b415af..fbac92a8926 100644 --- a/addons/report/controllers/main.py +++ b/addons/report/controllers/main.py @@ -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///', ], 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='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: - :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() diff --git a/addons/report/data/report_paperformat.xml b/addons/report/data/report_paperformat_data.xml similarity index 97% rename from addons/report/data/report_paperformat.xml rename to addons/report/data/report_paperformat_data.xml index f05e0afb6f0..f6462cc407e 100644 --- a/addons/report/data/report_paperformat.xml +++ b/addons/report/data/report_paperformat_data.xml @@ -1,6 +1,5 @@ - - + European A4 @@ -32,5 +31,4 @@ 35 90 - - + diff --git a/addons/report/models/__init__.py b/addons/report/models/__init__.py index 8cdd7a3310a..cc85d6af6a2 100644 --- a/addons/report/models/__init__.py +++ b/addons/report/models/__init__.py @@ -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 diff --git a/addons/report/models/abstract_report.py b/addons/report/models/abstract_report.py index 3edce091298..543557bc86c 100644 --- a/addons/report/models/abstract_report.py +++ b/addons/report/models/abstract_report.py @@ -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) diff --git a/addons/report/models/ir_actions.py b/addons/report/models/ir_actions.py deleted file mode 100644 index 23dac2d60a2..00000000000 --- a/addons/report/models/ir_actions.py +++ /dev/null @@ -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.") diff --git a/addons/report/models/ir_actions_report_xml.py b/addons/report/models/ir_actions_report_xml.py new file mode 100644 index 00000000000..667a2b43349 --- /dev/null +++ b/addons/report/models/ir_actions_report_xml.py @@ -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 diff --git a/addons/report/models/ir_http.py b/addons/report/models/ir_http.py index 0cf214b9035..61657a681d7 100644 --- a/addons/report/models/ir_http.py +++ b/addons/report/models/ir_http.py @@ -1,3 +1,6 @@ +# -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + from odoo import models diff --git a/addons/report/models/ir_qweb.py b/addons/report/models/ir_qweb.py index 3e8dc558c08..3c355dffe41 100644 --- a/addons/report/models/ir_qweb.py +++ b/addons/report/models/ir_qweb.py @@ -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('' % ('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 diff --git a/addons/report/models/report.py b/addons/report/models/report.py index 13ee2cb0547..1ad738785fa 100644 --- a/addons/report/models/report.py +++ b/addons/report/models/report.py @@ -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. diff --git a/addons/report/models/report_paperformat.py b/addons/report/models/report_paperformat.py index 948f400bed2..a97a1dfd3e0 100644 --- a/addons/report/models/report_paperformat.py +++ b/addons/report/models/report_paperformat.py @@ -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.')) diff --git a/addons/report/models/res_company.py b/addons/report/models/res_company.py index 198579344b0..450570c9293 100644 --- a/addons/report/models/res_company.py +++ b/addons/report/models/res_company.py @@ -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') diff --git a/addons/report/tests/test_reports.py b/addons/report/tests/test_reports.py index 2f9c723e36a..d1e85494f48 100644 --- a/addons/report/tests/test_reports.py +++ b/addons/report/tests/test_reports.py @@ -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 diff --git a/addons/report/views/ir_actions.xml b/addons/report/views/ir_actions.xml deleted file mode 100644 index 4596177741f..00000000000 --- a/addons/report/views/ir_actions.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ir.actions.report.xml.form.inherit - - ir.actions.report.xml - - - - - - - - - - \ No newline at end of file diff --git a/addons/report/views/ir_actions_report_views.xml b/addons/report/views/ir_actions_report_views.xml new file mode 100644 index 00000000000..c5d483d92fd --- /dev/null +++ b/addons/report/views/ir_actions_report_views.xml @@ -0,0 +1,38 @@ + + + + + ir.actions.report.xml.form.inherit + + ir.actions.report.xml + + + + + + + + + + + + act_report_xml_view_inherit + + ir.actions.report.xml + + + + +