[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:
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'
|
||||
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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)),
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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')
|
||||
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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>'
|
||||
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user