[IMP] base: avoid render with env mixup

The render API was confusing as mixing the access to the report and
the rendering env.

The ambiguity was present for code such as
`report.sudo()._render(record_ids)` where it was not clear if the
`sudo()` is needed to access to `report` or to `record_ids`. For low
priviledge users (such as portal or public), it was common to use
`report.with_user(SUPERUSER_ID)._render(record_ids)`.

This PR changes the render methods signature to be `api.model`. The
`report_ref` can be:
- ir.actions.report external id
- ir.actions.report id
- ir.actions.report recod
- `report_name` value

This will allow to call the report methods with any user and no longer
need to use `with_user(1)` to render reports as public user.

Task-id 2670865

closes odoo/odoo#91341

Related: odoo/upgrade#3650
Related: odoo/enterprise#27323
Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
This commit is contained in:
Martin Trigaux
2022-08-02 11:48:46 +02:00
parent 96d7fd67e1
commit ffc525419a
28 changed files with 127 additions and 132 deletions
@@ -434,7 +434,7 @@ class AccountBankStatement(models.Model):
# Bank statement report.
if statement.journal_id.type == 'bank':
content, content_type = self.env.ref('account.action_report_account_statement')._render(statement.id)
content = self.env["ir.actions.report"]._render_qweb_pdf('account.action_report_account_statement', statement.id)[0]
self.env['ir.attachment'].create({
'name': statement.name and _("Bank Statement %s.pdf", statement.name) or _("Bank Statement.pdf"),
'type': 'binary',
+6 -6
View File
@@ -11,10 +11,10 @@ from odoo.tools import pdf
class IrActionsReport(models.Model):
_inherit = 'ir.actions.report'
def _render_qweb_pdf_prepare_streams(self, data, res_ids=None):
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
# Custom behavior for 'account.report_original_vendor_bill'.
if self.report_name != 'account.report_original_vendor_bill':
return super()._render_qweb_pdf_prepare_streams(data, res_ids=res_ids)
if self._get_report(report_ref).report_name != 'account.report_original_vendor_bill':
return super()._render_qweb_pdf_prepare_streams(report_ref, data, res_ids=res_ids)
invoices = self.env['account.move'].browse(res_ids)
if any(x.move_type not in ('in_invoice', 'in_receipt') for x in invoices):
@@ -44,11 +44,11 @@ class IrActionsReport(models.Model):
}
return collected_streams
def _render_qweb_pdf(self, res_ids=None, data=None):
def _render_qweb_pdf(self, report_ref, res_ids=None, data=None):
# Check for reports only available for invoices.
if self.report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
if self._get_report(report_ref).report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
invoices = self.env['account.move'].browse(res_ids)
if any(x.move_type == 'entry' for x in invoices):
raise UserError(_("Only invoices could be printed."))
return super()._render_qweb_pdf(res_ids=res_ids, data=data)
return super()._render_qweb_pdf(report_ref, res_ids=res_ids, data=data)
@@ -9,14 +9,14 @@ from odoo.tools.pdf import OdooPdfFileReader, OdooPdfFileWriter
class IrActionsReport(models.Model):
_inherit = 'ir.actions.report'
def _render_qweb_pdf_prepare_streams(self, data, res_ids=None):
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
# EXTENDS base
collected_streams = super()._render_qweb_pdf_prepare_streams(data, res_ids=res_ids)
collected_streams = super()._render_qweb_pdf_prepare_streams(report_ref, data, res_ids=res_ids)
if collected_streams \
and res_ids \
and len(res_ids) == 1 \
and self.report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
and self._get_report(report_ref).report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
invoice = self.env['account.move'].browse(res_ids)
if invoice.is_sale_document() and invoice.state != 'draft':
to_embed = invoice.edi_document_ids
@@ -50,14 +50,14 @@ class IrActionsReport(models.Model):
'mimetype': 'application/xml',
})
def _render_qweb_pdf_prepare_streams(self, data, res_ids=None):
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
# EXTENDS base
# Add the pdf report in the XML as base64 string.
collected_streams = super()._render_qweb_pdf_prepare_streams(data, res_ids=res_ids)
collected_streams = super()._render_qweb_pdf_prepare_streams(report_ref, data, res_ids=res_ids)
if collected_streams \
and res_ids \
and self.report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
and self._get_report(report_ref).report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
for res_id, stream_data in collected_streams.items():
invoice = self.env['account.move'].browse(res_id)
self._add_pdf_into_invoice_xml(invoice, stream_data)
+4 -4
View File
@@ -28,14 +28,14 @@ class TestMultiCompany(TestHrCommon):
cls.env.invalidate_all()
def test_multi_company_report(self):
content, content_type = self.env.ref('hr.hr_employee_print_badge').with_user(self.res_users_hr_officer).with_context(
content, _ = self.env['ir.actions.report'].with_user(self.res_users_hr_officer).with_context(
allowed_company_ids=[self.company_1.id, self.company_2.id]
)._render_qweb_pdf(res_ids=self.employees.ids)
)._render_qweb_pdf('hr.hr_employee_print_badge', res_ids=self.employees.ids)
self.assertIn(b'Bidule', content)
self.assertIn(b'Machin', content)
def test_single_company_report(self):
with self.assertRaises(QWebException): # CacheMiss followed by AccessError
content, content_type = self.env.ref('hr.hr_employee_print_badge').with_user(self.res_users_hr_officer).with_company(
self.env['ir.actions.report'].with_user(self.res_users_hr_officer).with_company(
self.company_1
)._render_qweb_pdf(res_ids=self.employees.ids)
)._render_qweb_pdf('hr.hr_employee_print_badge', res_ids=self.employees.ids)
+9 -6
View File
@@ -27,11 +27,14 @@ class IrActionsReport(models.Model):
qr_cross = ReportLabImage((width/2 - cross_width/2) / mm, (height/2 - cross_height/2) / mm, cross_width / mm, cross_height / mm, cross_path.as_posix())
barcode_drawing.add(qr_cross)
def _render_qweb_pdf_prepare_streams(self, data, res_ids=None):
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
# OVERRIDE
res = super()._render_qweb_pdf_prepare_streams(data, res_ids)
if res_ids and self.report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
invoices = self.env[self.model].browse(res_ids)
res = super()._render_qweb_pdf_prepare_streams(report_ref, data, res_ids)
if not res_ids:
return res
report = self._get_report(report_ref)
if report.report_name in ('account.report_invoice_with_payments', 'account.report_invoice'):
invoices = self.env[report.model].browse(res_ids)
# Determine which invoices need a QR/ISR.
qr_inv_ids = []
isr_inv_ids = []
@@ -45,11 +48,11 @@ class IrActionsReport(models.Model):
# Render the additional reports.
streams_to_append = {}
if qr_inv_ids:
qr_res = self.env.ref('l10n_ch.l10n_ch_qr_report')._render_qweb_pdf_prepare_streams(data, res_ids=qr_inv_ids)
qr_res = self._render_qweb_pdf_prepare_streams('l10n_ch.l10n_ch_qr_report', data, res_ids=qr_inv_ids)
for invoice_id, stream in qr_res.items():
streams_to_append[invoice_id] = stream
if isr_inv_ids:
isr_res = self.env.ref('l10n_ch.l10n_ch_isr_report')._render_qweb_pdf_prepare_streams(data, res_ids=isr_inv_ids)
isr_res = self._render_qweb_pdf_prepare_streams('l10n_ch.l10n_ch_isr_report', data, res_ids=isr_inv_ids)
for invoice_id, stream in isr_res.items():
streams_to_append[invoice_id] = stream
# Add to results
+3 -3
View File
@@ -4,7 +4,7 @@ from odoo import models
class IrActionsReport(models.Model):
_inherit = 'ir.actions.report'
def _get_rendering_context(self, docids, data):
data = super()._get_rendering_context(docids, data)
data['din_header_spacing'] = self.get_paperformat().header_spacing
def _get_rendering_context(self, report, docids, data):
data = super()._get_rendering_context(report, docids, data)
data['din_header_spacing'] = report.get_paperformat().header_spacing
return data
+1 -1
View File
@@ -148,7 +148,7 @@ class AccountMove(models.Model):
# b64encode returns a bytestring, the template tries to turn it to string,
# but only gets the repr(pdf) --> "b'<base64_data>'"
pdf = self.env.ref('account.account_invoices')._render_qweb_pdf(self.id)[0]
pdf = self.env['ir.actions.report']._render_qweb_pdf("account.account_invoices", self.id)[0]
pdf = base64.b64encode(pdf).decode()
pdf_name = re.sub(r'\W+', '', self.name) + '.pdf'
+4 -4
View File
@@ -223,18 +223,18 @@ class MailTemplate(models.Model):
report_service = report.report_name
if report.report_type in ['qweb-html', 'qweb-pdf']:
result, format = report._render_qweb_pdf([res_id])
result, report_format = self.env['ir.actions.report']._render_qweb_pdf(report, [res_id])
else:
res = report._render([res_id])
res = self.env['ir.actions.report']._render(report, [res_id])
if not res:
raise UserError(_('Unsupported report type %s found.', report.report_type))
result, format = res
result, report_format = res
# TODO in trunk, change return format to binary to match message_post expected format
result = base64.b64encode(result)
if not report_name:
report_name = 'report.' + report_service
ext = "." + format
ext = "." + report_format
if not report_name.endswith(ext):
report_name += ext
attachments.append((report_name, result))
+2 -2
View File
@@ -241,8 +241,8 @@ class TestMrpStockReports(TestReportsCommon):
move.move_line_ids.result_package_id = self.env['stock.quant.package'].create({'name': 'Package0001'})
picking.button_validate()
report = self.env['ir.actions.report']._get_report_from_name('stock.report_deliveryslip')
html_report = report._render_qweb_html(picking.ids)[0].decode('utf-8').split('\n')
html_report = self.env['ir.actions.report']._render_qweb_html(
'stock.report_deliveryslip', picking.ids)[0].decode('utf-8').split('\n')
keys = [
"Package0001", "Compo 03",
"Products with no package assigned", "Compo 01", "Compo 02",
+1 -1
View File
@@ -82,7 +82,7 @@ class PosController(http.Controller):
@http.route('/pos/sale_details_report', type='http', auth='user')
def print_sale_details(self, date_start=False, date_stop=False, **kw):
r = request.env['report.point_of_sale.report_saledetails']
pdf, _ = request.env.ref('point_of_sale.sale_details_report').with_context(date_start=date_start, date_stop=date_stop)._render_qweb_pdf(r)
pdf, _ = request.env['ir.actions.report'].with_context(date_start=date_start, date_stop=date_stop)._render_qweb_pdf('point_of_sale.sale_details_report', r)
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
+1 -1
View File
@@ -786,7 +786,7 @@ class PosOrder(models.Model):
attachment = [(4, receipt.id)]
if self.mapped('account_move'):
report = self.env.ref('account.account_invoices')._render_qweb_pdf(self.account_move.ids[0])
report = self.env['ir.actions.report']._render_qweb_pdf("account.account_invoices", self.account_move.ids[0])
filename = name + '.pdf'
invoice = self.env['ir.attachment'].create({
'name': filename,
+3 -6
View File
@@ -473,18 +473,15 @@ class CustomerPortal(Controller):
if report_type not in ('html', 'pdf', 'text'):
raise UserError(_("Invalid report type: %s", report_type))
report_sudo = request.env.ref(report_ref).with_user(SUPERUSER_ID)
if not isinstance(report_sudo, type(request.env['ir.actions.report'])):
raise UserError(_("%s is not the reference of a report", report_ref))
ReportAction = request.env['ir.actions.report'].sudo()
if hasattr(model, 'company_id'):
if len(model.company_id) > 1:
raise UserError(_('Multi company reports are not supported.'))
report_sudo = report_sudo.with_company(model.company_id)
ReportAction = ReportAction.with_company(model.company_id)
method_name = '_render_qweb_%s' % (report_type)
report = getattr(report_sudo, method_name)(list(model.ids), data={'report_type': report_type})[0]
report = getattr(ReportAction, method_name)(report_ref, list(model.ids), data={'report_type': report_type})[0]
reporthttpheaders = [
('Content-Type', 'application/pdf' if report_type == 'pdf' else 'text/html'),
('Content-Length', len(report)),
+1 -1
View File
@@ -234,7 +234,7 @@ class CustomerPortal(portal.CustomerPortal):
order_sudo.action_confirm()
order_sudo._send_order_confirmation_mail()
pdf = request.env.ref('sale.action_report_saleorder').with_user(SUPERUSER_ID)._render_qweb_pdf([order_sudo.id])[0]
pdf = request.env['ir.actions.report'].sudo()._render_qweb_pdf('sale.action_report_saleorder', [order_sudo.id])[0]
_message_post_helper(
'sale.order',
@@ -64,7 +64,7 @@ class TestSaleMrpInvoices(common.TransactionCase):
invoice = so._create_invoices()
invoice.action_post()
report = self.env['ir.actions.report']._get_report_from_name('account.report_invoice_with_payments')
html = report._render_qweb_html(invoice.ids)[0]
html = self.env['ir.actions.report']._render_qweb_html(
'account.report_invoice_with_payments', invoice.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By Lot\n1.00Units\nLOT0001', "There should be a line that specifies 1 x LOT0001")
+2 -2
View File
@@ -29,8 +29,8 @@ class SaleStockPortal(CustomerPortal):
except exceptions.AccessError:
return request.redirect('/my')
# print report as SUPERUSER, since it require access to product, taxes, payment term etc.. and portal does not have those access rights.
pdf = request.env.ref('stock.action_report_delivery').with_user(SUPERUSER_ID)._render_qweb_pdf([picking_sudo.id])[0]
# print report with sudo, since it require access to product, taxes, payment term etc.. and portal does not have those access rights.
pdf = request.env['ir.actions.report'].sudo()._render_qweb_pdf('stock.action_report_delivery', [picking_sudo.id])[0]
pdfhttpheaders = [
('Content-Type', 'application/pdf'),
('Content-Length', len(pdf)),
@@ -151,8 +151,8 @@ class TestSaleStockInvoices(TestSaleCommon):
line.quantity = 2
invoice.action_post()
report = self.env['ir.actions.report']._get_report_from_name('account.report_invoice_with_payments')
html = report._render_qweb_html(invoice.ids)[0]
html = self.env['ir.actions.report']._render_qweb_html(
'account.report_invoice_with_payments', invoice.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By Lot\n2.00Units\nLOT0001', "There should be a line that specifies 2 x LOT0001")
@@ -184,8 +184,8 @@ class TestSaleStockInvoices(TestSaleCommon):
picking.move_ids.quantity_done = 4
picking.button_validate()
report = self.env['ir.actions.report']._get_report_from_name('account.report_invoice_with_payments')
html = report._render_qweb_html(invoice.ids)[0]
html = self.env['ir.actions.report']._render_qweb_html(
'account.report_invoice_with_payments', invoice.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By Lot\n4.00Units\nLOT0001', "There should be a line that specifies 4 x LOT0001")
@@ -196,7 +196,6 @@ class TestSaleStockInvoices(TestSaleCommon):
Then, he delivers the other one and invoices it too. Each invoice should have the
correct USN
"""
report = self.env['ir.actions.report']._get_report_from_name('account.report_invoice_with_payments')
display_lots = self.env.ref('stock_account.group_lot_on_invoice')
display_uom = self.env.ref('uom.group_uom')
self.env.user.write({'groups_id': [(4, display_lots.id), (4, display_uom.id)]})
@@ -226,20 +225,21 @@ class TestSaleStockInvoices(TestSaleCommon):
backorder.move_ids.move_line_ids.qty_done = 1
backorder.button_validate()
html = report._render_qweb_html(invoice01.ids)[0]
IrActionsReport = self.env['ir.actions.report']
html = IrActionsReport._render_qweb_html('account.report_invoice_with_payments', invoice01.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By USN\n1.00Units\nUSN0001', "There should be a line that specifies 1 x USN0001")
self.assertNotIn('USN0002', text)
invoice02 = so._create_invoices()
invoice02.action_post()
html = report._render_qweb_html(invoice02.ids)[0]
html = IrActionsReport._render_qweb_html('account.report_invoice_with_payments', invoice02.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By USN\n1.00Units\nUSN0002', "There should be a line that specifies 1 x USN0002")
self.assertNotIn('USN0001', text)
# Posting the second invoice shouldn't change the result of the first one
html = report._render_qweb_html(invoice01.ids)[0]
html = IrActionsReport._render_qweb_html('account.report_invoice_with_payments', invoice01.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By USN\n1.00Units\nUSN0001', "There should still be a line that specifies 1 x USN0001")
self.assertNotIn('USN0002', text)
@@ -247,11 +247,11 @@ class TestSaleStockInvoices(TestSaleCommon):
# Resetting and posting again the first invoice shouldn't change the results
invoice01.button_draft()
invoice01.action_post()
html = report._render_qweb_html(invoice01.ids)[0]
html = IrActionsReport._render_qweb_html('account.report_invoice_with_payments', invoice01.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By USN\n1.00Units\nUSN0001', "There should still be a line that specifies 1 x USN0001")
self.assertNotIn('USN0002', text)
html = report._render_qweb_html(invoice02.ids)[0]
html = IrActionsReport._render_qweb_html('account.report_invoice_with_payments', invoice02.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By USN\n1.00Units\nUSN0002', "There should be a line that specifies 1 x USN0002")
self.assertNotIn('USN0001', text)
@@ -268,7 +268,6 @@ class TestSaleStockInvoices(TestSaleCommon):
- Deliver 05 x Lot02 + 02 x Lot03
- Invoice 08 x P
"""
report = self.env['ir.actions.report']._get_report_from_name('account.report_invoice_with_payments')
display_lots = self.env.ref('stock_account.group_lot_on_invoice')
display_uom = self.env.ref('uom.group_uom')
self.env.user.write({'groups_id': [(4, display_lots.id), (4, display_uom.id)]})
@@ -333,7 +332,8 @@ class TestSaleStockInvoices(TestSaleCommon):
line.quantity = 2
invoice01.action_post()
html = report._render_qweb_html(invoice01.ids)[0]
html = self.env['ir.actions.report']._render_qweb_html(
'account.report_invoice_with_payments', invoice01.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By Lot\n2.00Units\nLOT0002', "There should be a line that specifies 2 x LOT0002")
self.assertNotIn('LOT0001', text)
@@ -355,7 +355,8 @@ class TestSaleStockInvoices(TestSaleCommon):
invoice02 = so._create_invoices()
invoice02.action_post()
html = report._render_qweb_html(invoice02.ids)[0]
html = self.env['ir.actions.report']._render_qweb_html(
'account.report_invoice_with_payments', invoice02.ids)[0]
text = html2plaintext(html)
self.assertRegex(text, r'Product By Lot\n6.00Units\nLOT0002', "There should be a line that specifies 6 x LOT0002")
self.assertRegex(text, r'Product By Lot\n2.00Units\nLOT0003', "There should be a line that specifies 2 x LOT0003")
+1 -1
View File
@@ -138,7 +138,7 @@ class SnailmailLetter(models.Model):
else:
report_name = 'Document'
filename = "%s.%s" % (report_name, "pdf")
pdf_bin, _ = report.with_context(snailmail_layout=not self.cover)._render_qweb_pdf(self.res_id)
pdf_bin, _ = self.env['ir.actions.report'].with_context(snailmail_layout=not self.cover)._render_qweb_pdf(report, self.res_id)
attachment = self.env['ir.attachment'].create({
'name': filename,
'datas': base64.b64encode(pdf_bin),
+1 -1
View File
@@ -1544,7 +1544,7 @@ class Picking(models.Model):
def _attach_sign(self):
""" Render the delivery report in pdf and attach it to the picking in `self`. """
self.ensure_one()
report = self.env.ref('stock.action_report_delivery')._render_qweb_pdf(self.id)
report = self.env['ir.actions.report']._render_qweb_pdf("stock.action_report_delivery", self.id)
filename = "%s_signed_delivery_slip" % self.name
if self.partner_id:
message = _('Order signed by %s') % (self.partner_id.name)
+1 -2
View File
@@ -53,10 +53,9 @@ class TestReports(TestReportsCommon):
'product_id': product1.id,
'company_id': self.env.company.id,
})
report = self.env.ref('stock.label_lot_template')
target = b'\n\n^XA\n^FO100,50\n^A0N,44,33^FD[C418]Mellohi^FS\n^FO100,100\n^A0N,44,33^FDLN/SN:Volume-Beta^FS\n^FO100,150^BY3\n^BCN,100,Y,N,N\n^FDVolume-Beta^FS\n^XZ\n'
rendering, qweb_type = report._render_qweb_text(lot1.id)
rendering, qweb_type = self.env['ir.actions.report']._render_qweb_text('stock.label_lot_template', lot1.id)
self.assertEqual(target, rendering.replace(b' ', b''), 'The rendering is not good')
self.assertEqual(qweb_type, 'text', 'the report type is not good')
+1 -1
View File
@@ -696,7 +696,7 @@ class Survey(http.Controller):
return request.render('survey.survey_page_statistics', template_values)
def _generate_report(self, user_input, download=True):
report = request.env.ref('survey.certification_report').with_user(SUPERUSER_ID)._render_qweb_pdf([user_input.id], data={'report_type': 'pdf'})[0]
report = request.env["ir.actions.report"].sudo()._render_qweb_pdf('survey.certification_report', [user_input.id], data={'report_type': 'pdf'})[0]
report_content_disposition = content_disposition('Certification.pdf')
if not download:
@@ -280,7 +280,7 @@ class TestRegistrationPerformance(EventPerformanceCase):
form like) """
event = self.env['event.event'].browse(self.test_event.ids)
with freeze_time(self.reference_now), self.assertQueryCount(event_user=695): # tef only: 650 - com runbot 692
with freeze_time(self.reference_now), self.assertQueryCount(event_user=697): # tef only: 650 - com runbot 692
self.env.cr._now = self.reference_now # force create_date to check schedulers
registration_values = [
dict(reg_data,
@@ -317,7 +317,7 @@ class TestRegistrationPerformance(EventPerformanceCase):
""" Test a single registration creation using Form """
event = self.env['event.event'].browse(self.test_event.ids)
with freeze_time(self.reference_now), self.assertQueryCount(event_user=206): # tef only: 188 - com runbot: 193
with freeze_time(self.reference_now), self.assertQueryCount(event_user=208): # tef only: 188 - com runbot: 193
self.env.cr._now = self.reference_now # force create_date to check schedulers
with Form(self.env['event.registration']) as reg_form:
reg_form.event_id = event
@@ -330,7 +330,7 @@ class TestRegistrationPerformance(EventPerformanceCase):
""" Test a single registration creation using Form """
event = self.env['event.event'].browse(self.test_event.ids)
with freeze_time(self.reference_now), self.assertQueryCount(event_user=123): # tef only: 120? - com runbot 108 - ent runbot 122
with freeze_time(self.reference_now), self.assertQueryCount(event_user=124): # tef only: 120? - com runbot 108 - ent runbot 122
self.env.cr._now = self.reference_now # force create_date to check schedulers
with Form(self.env['event.registration'].with_context(event_lead_rule_skip=True)) as reg_form:
reg_form.event_id = event
@@ -358,7 +358,7 @@ class TestRegistrationPerformance(EventPerformanceCase):
event = self.env['event.event'].browse(self.test_event.ids)
# partner-based customer
with freeze_time(self.reference_now), self.assertQueryCount(event_user=126): # tef only: 118 - com runbot: 125
with freeze_time(self.reference_now), self.assertQueryCount(event_user=128): # tef only: 118 - com runbot: 125
self.env.cr._now = self.reference_now # force create_date to check schedulers
registration_values = {
'event_id': event.id,
@@ -373,7 +373,7 @@ class TestRegistrationPerformance(EventPerformanceCase):
event = self.env['event.event'].browse(self.test_event.ids)
# partner-based customer
with freeze_time(self.reference_now), self.assertQueryCount(event_user=44): # tef only: 40 - com runbot: 42
with freeze_time(self.reference_now), self.assertQueryCount(event_user=46): # tef only: 40 - com runbot: 42
self.env.cr._now = self.reference_now # force create_date to check schedulers
registration_values = {
'event_id': event.id,
@@ -388,7 +388,7 @@ class TestRegistrationPerformance(EventPerformanceCase):
event = self.env['event.event'].browse(self.test_event.ids)
# website customer data
with freeze_time(self.reference_now), self.assertQueryCount(event_user=132): # tef only: 124 - com runbot: 128
with freeze_time(self.reference_now), self.assertQueryCount(event_user=134): # tef only: 124 - com runbot: 128
self.env.cr._now = self.reference_now # force create_date to check schedulers
registration_values = dict(
self.website_customer_data[0],
+1 -1
View File
@@ -58,7 +58,7 @@ class TestMailComposer(TestMailCommon, TestRecipients):
'report_type': 'qweb-pdf',
'report_name': 'test_mail.mail_test_ticket_test_template',
})
cls.test_record_report = cls.test_report._render_qweb_pdf(cls.test_report.ids)
cls.test_record_report = cls.env['ir.actions.report']._render_qweb_pdf(cls.test_report, cls.test_record.ids)
cls.test_from = '"John Doe" <john@example.com>'
+4 -4
View File
@@ -29,7 +29,7 @@ class ReportController(http.Controller):
'/report/<converter>/<reportname>/<docids>',
], type='http', auth='user', website=True)
def report_routes(self, reportname, docids=None, converter=None, **data):
report = request.env['ir.actions.report']._get_report_from_name(reportname)
report = request.env['ir.actions.report']
context = dict(request.env.context)
if docids:
@@ -40,14 +40,14 @@ class ReportController(http.Controller):
data['context'] = json.loads(data['context'])
context.update(data['context'])
if converter == 'html':
html = report.with_context(context)._render_qweb_html(docids, data=data)[0]
html = report.with_context(context)._render_qweb_html(reportname, docids, data=data)[0]
return request.make_response(html)
elif converter == 'pdf':
pdf = report.with_context(context)._render_qweb_pdf(docids, data=data)[0]
pdf = report.with_context(context)._render_qweb_pdf(reportname, docids, data=data)[0]
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
elif converter == 'text':
text = report.with_context(context)._render_qweb_text(docids, data=data)[0]
text = report.with_context(context)._render_qweb_text(reportname, docids, data=data)[0]
texthttpheaders = [('Content-Type', 'text/plain'), ('Content-Length', len(text))]
return request.make_response(text, headers=texthttpheaders)
else:
+1 -1
View File
@@ -1348,7 +1348,7 @@ class WebsiteSale(http.Controller):
def print_saleorder(self, **kwargs):
sale_order_id = request.session.get('sale_last_order_id')
if sale_order_id:
pdf, _ = request.env.ref('sale.action_report_saleorder').with_user(SUPERUSER_ID)._render_qweb_pdf([sale_order_id])
pdf, _ = request.env['ir.actions.report'].sudo()._render_qweb_pdf('sale.action_report_saleorder', [sale_order_id])
pdfhttpheaders = [('Content-Type', 'application/pdf'), ('Content-Length', u'%s' % len(pdf))]
return request.make_response(pdf, headers=pdfhttpheaders)
else:
+46 -48
View File
@@ -490,15 +490,20 @@ class IrActionsReport(models.Model):
@api.model
def _get_report(self, report_ref):
"""Get the report (with sudo) from an id or xmlid
"""Get the report (with sudo) from a reference
report_ref: can be one of
- ir.actions.report id
- ir.actions.report record
- ir.model.data reference to ir.actions.report
- ir.actions.report report_name
- ir.model.data reference
"""
ReportSudo = self.env['ir.actions.report'].sudo()
if isinstance(report_ref, int):
return ReportSudo.browse(report_ref)
if isinstance(report_ref, models.Model):
if report_ref._name != self._name:
raise ValueError("Expected report of type %s, got %s" % (self._name, report_ref._name))
return report_ref.sudo()
report = ReportSudo.search([('report_name', '=', report_ref)], limit=1)
if report:
return report
@@ -614,29 +619,28 @@ class IrActionsReport(models.Model):
writer.write(result_stream)
return result_stream
def _render_qweb_pdf_prepare_streams(self, data, res_ids=None):
self.ensure_one()
def _render_qweb_pdf_prepare_streams(self, report_ref, data, res_ids=None):
if not data:
data = {}
data.setdefault('report_type', 'pdf')
# access the report details with sudo() but evaluation context as sudo(False)
self_sudo = self.sudo()
# access the report details with sudo() but evaluation context as current user
report_sudo = self._get_report(report_ref)
collected_streams = OrderedDict()
# Fetch the existing attachments from the database for later use.
# Reload the stream from the attachment in case of 'attachment_use'.
if res_ids:
records = self.env[self_sudo.model].browse(res_ids)
records = self.env[report_sudo.model].browse(res_ids)
for record in records:
stream = None
attachment = None
if self_sudo.attachment:
attachment = self_sudo.retrieve_attachment(record)
if report_sudo.attachment:
attachment = report_sudo.retrieve_attachment(record)
# Extract the stream from the attachment.
if attachment and self_sudo.attachment_use:
if attachment and report_sudo.attachment_use:
stream = io.BytesIO(attachment.raw)
# Ensure the stream can be saved in Image.
@@ -684,11 +688,11 @@ class IrActionsReport(models.Model):
if not config['test_enable']:
additional_context['commit_assetsbundle'] = True
html = self_sudo.with_context(**additional_context)._render_qweb_html(res_ids_wo_stream, data=data)[0]
html = self.with_context(**additional_context)._render_qweb_html(report_ref, res_ids_wo_stream, data=data)[0]
bodies, html_ids, header, footer, specific_paperformat_args = self._prepare_html(html, report_model=self_sudo.model)
bodies, html_ids, header, footer, specific_paperformat_args = self._prepare_html(html, report_model=report_sudo.model)
if self_sudo.attachment and set(res_ids_wo_stream) != set(html_ids):
if report_sudo.attachment and set(res_ids_wo_stream) != set(html_ids):
raise UserError(_(
"The report's template %r is wrong, please contact your administrator. \n\n"
"Can not separate file to save as attachment because the report's template does not contains the"
@@ -771,21 +775,21 @@ class IrActionsReport(models.Model):
return collected_streams
def _render_qweb_pdf(self, res_ids=None, data=None):
self.ensure_one()
def _render_qweb_pdf(self, report_ref, res_ids=None, data=None):
if not data:
data = {}
if isinstance(res_ids, int):
res_ids = [res_ids]
data.setdefault('report_type', 'pdf')
self_sudo = self.sudo()
# In case of test environment without enough workers to perform calls to wkhtmltopdf,
# fallback to render_html.
if (tools.config['test_enable'] or tools.config['test_file']) and not self.env.context.get('force_report_rendering'):
return self_sudo._render_qweb_html(res_ids, data=data)
return self._render_qweb_html(report_ref, res_ids, data=data)
collected_streams = self._render_qweb_pdf_prepare_streams(data, res_ids=res_ids)
collected_streams = self._render_qweb_pdf_prepare_streams(report_ref, data, res_ids=res_ids)
# access the report details with sudo() but keep evaluation context as current user
report_sudo = self._get_report(report_ref)
# Generate the ir.attachment if needed.
if self.attachment:
@@ -795,8 +799,8 @@ class IrActionsReport(models.Model):
if stream_data['attachment']:
continue
record = self.env[self_sudo.model].browse(res_id)
attachment_name = safe_eval(self.attachment, {'object': record, 'time': time})
record = self.env[report_sudo.model].browse(res_id)
attachment_name = safe_eval(report_sudo.attachment, {'object': record, 'time': time})
# Unable to compute a name for the attachment.
if not attachment_name:
@@ -805,7 +809,7 @@ class IrActionsReport(models.Model):
attachment_vals_list.append({
'name': attachment_name,
'raw': stream_data['stream'].getvalue(),
'res_model': self.model,
'res_model': report_sudo.model,
'res_id': record.id,
'type': 'binary',
})
@@ -831,65 +835,59 @@ class IrActionsReport(models.Model):
stream.close()
if res_ids:
_logger.info("The PDF report has been generated for model: %s, records %s.", self_sudo.model, str(res_ids))
_logger.info("The PDF report has been generated for model: %s, records %s.", report_sudo.model, str(res_ids))
return pdf_content, 'pdf'
@api.model
def _render_qweb_text(self, docids, data=None):
"""
:rtype: bytes
"""
def _render_qweb_text(self, report_ref, docids, data=None):
if not data:
data = {}
data.setdefault('report_type', 'text')
data = self._get_rendering_context(docids, data)
return self._render_template(self.report_name, data), 'text'
report = self._get_report(report_ref)
data = self._get_rendering_context(report, docids, data)
return self._render_template(report.report_name, data), 'text'
@api.model
def _render_qweb_html(self, docids, data=None):
"""This method generates and returns html version of a report.
:rtype: bytes
"""
def _render_qweb_html(self, report_ref, docids, data=None):
if not data:
data = {}
data.setdefault('report_type', 'html')
data = self._get_rendering_context(docids, data)
return self._render_template(self.report_name, data), 'html'
report = self._get_report(report_ref)
data = self._get_rendering_context(report, docids, data)
return self._render_template(report.report_name, data), 'html'
def _get_rendering_context_model(self):
report_model_name = 'report.%s' % self.report_name
def _get_rendering_context_model(self, report):
report_model_name = 'report.%s' % report.report_name
return self.env.get(report_model_name)
def _get_rendering_context(self, docids, data):
def _get_rendering_context(self, report, docids, data):
# 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 = self._get_rendering_context_model()
report_model = self._get_rendering_context_model(report)
data = data and dict(data) or {}
if report_model is not None:
# _render_ may be executed in sudo but evaluation context as real user
report_model = report_model.sudo(False)
data.update(report_model._get_report_values(docids, data=data))
else:
# _render_ may be executed in sudo but evaluation context as real user
docs = self.env[self.model].sudo(False).browse(docids)
docs = self.env[report.model].browse(docids)
data.update({
'doc_ids': docids,
'doc_model': self.model,
'doc_model': report.model,
'docs': docs,
})
data['is_html_empty'] = is_html_empty
return data
def _render(self, res_ids, data=None):
report_type = self.report_type.lower().replace('-', '_')
@api.model
def _render(self, report_ref, res_ids, data):
report = self._get_report(report_ref)
report_type = report.report_type.lower().replace('-', '_')
render_func = getattr(self, '_render_' + report_type, None)
if not render_func:
return None
return render_func(res_ids, data=data)
return render_func(report_ref, res_ids, data=data)
def report_action(self, docids, data=None, config=True):
"""Return an action of type ir.actions.report.
+4 -3
View File
@@ -18,7 +18,8 @@ class TestReports(odoo.tests.TransactionCase):
'account.report_invoice_with_payments': invoice_domain,
'account.report_invoice': invoice_domain,
}
for report in self.env['ir.actions.report'].search([('report_type', 'like', 'qweb')]):
Report = self.env['ir.actions.report']
for report in Report.search([('report_type', 'like', 'qweb')]):
report_model = 'report.%s' % report.report_name
try:
self.env[report_model]
@@ -33,8 +34,8 @@ class TestReports(odoo.tests.TransactionCase):
# Test report generation
if not report.multi:
for record in report_records:
report._render_qweb_html(record.ids)
Report._render_qweb_html(report.id, record.ids)
else:
report._render_qweb_html(report_records.ids)
Report._render_qweb_html(report.id, report_records.ids)
else:
continue
+1 -5
View File
@@ -32,11 +32,7 @@ def try_report(cr, uid, rname, ids, data=None, context=None, our_module=None, re
env = api.Environment(cr, uid, context)
report_id = env['ir.actions.report'].search([('report_name', '=', rname)], limit=1)
if not report_id:
raise Exception("Required report does not exist: %s" % rname)
res_data, res_format = report_id._render(ids, data=data)
res_data, res_format = env['ir.actions.report']._render(rname, ids, data=data)
if not res_data:
raise ValueError("Report %s produced an empty result!" % rname)