Before this commit, website needed to be installed to edit
a HTML report because the context `inherit_branding` was not
correctly passed.
Credits to @sle-odoo
If wkhtmltopdf is missing, a call to a report will be catched to warn the user.
In case of a report generated indirectly (e.g. via an attachment on an email
template), the rendering would crash.
As warning the user is better than returning a traceback, raise a UserError.
Closes#14535
It's possible that the command `wkhtmltopdf --version` returns an empty
string (in the use case, it output later in stdout "wkhtmltopdf: error
while loading shared libraries: libfontconfig.so.1: cannot open shared
object file: No such file or directory").
We chose to display the report in html in this case and also to show
a warning to the user inviting him to fix his installation.
This commit reverts the API change introduced in b3302398bc
that changes `ir.qweb#render()` to return a unicode string instead of a utf8
encoded bytestring.
Returning a unicode string is undoubtedly cleaner and a step in the direction
of python 3 but still is an API change that can break third party modules.
Let's wait the migration to python 3 before breaking other people's toys.
In the 'get_html' method, the report is using a custom model to render its html,
we should use its own 'render_html' method to generate the report. During the
module migration, we replaced
'report_model.render_html(cr, uid, ids, data=data, context=context)'
by
'report_model.render_html(data=data)'
The new API signature of 'get_html' is
@api.model
def get_html(self, docids, report_name, data=None):
That way, 'render_html' doesn't receive docids anymore when called. We should do instead
if report_model is not None:
return report_model.browse(docids).render_html(data=data)
As render_html is an api.multi method, this is all wrong too, because we'll send a
browse record with the custom report model to the render_html method, to use the
ids with 'self.ids' that are absolutely not corresponding to the report model but to
to the record model we want to print (an invoice for example).
In old API style we were sending a list of ids, but it's not correct to keep the same
behavior by sending a browse record with only the ids relevant. We have also to
change render_html method decorator to @api.model with a new signature
@api.model
def render_html(self, docids, data=None):
And replace the call to render_html by
if report_model is not None:
return report_model.render_html(docids, data=data)
In the case the report is printed from a wizard, this is sufficient because the ids
are taken from the active_ids in context, or in data.
In the case the report is printed from a simple action, we should replace the occurence
of 'self.ids' (which doesn't mean anything) to 'docids'
This fix doesn't solve the printing issues for the custom accounting reports, because
the method get_pdf has been reimplemented in the module account_reports.
1/ If no docids are passed in get_action (empty list) do not put in context
2/ The method should accept ids or browse records as docids for retrocompatibility.
- The api.v7 method took ids as docids
- The api.v8 method took a browse records 'records' to call the api.v7 method with
'records.ids' as docids
In python, we cannot write 2 functions with the same name that takes differents types
for the same argument. The function 'guess' in api.py will lead to a crash if we expect
a browse record and we send a list
The only way to allow the method to receive different types for the same argument is
to decorate it with '@api.noguess' to prevent any effect from guess method.
That way, we check directly if 'docids' is an id, ids or a browserecord and we always set
a list instead.
This change concerns reports of `qweb-html` type. Previously, they were
openned in a popup and it was not very practical. We chose to open these
reports in an iframe (as opposed to what's done in `account_report`) in
order to be the most close to the pdf version in terms of apperance.
The client action allows an integration with the control panel. Apart from
the breadcrumbs integration, we also add buttons to handle the web_editor
(save, discard, etc). We also add a button to print the pdf version.
As there is some security concern to communicate with an iframe, we use
the postMessage[1] technology and we fix the `targetOrigin` to the
`web.base.url` parameter, that means that if the window to which we send
the message does not match the `web.base.url` origin, the event is not
even dispatched.
In order to to that, the report module uses the ir_http `session_info`
override mechanism to add the `web.base.url` in the DOM of the webclient,
and the report module adds to the DOM of report the `web.base.url`
content. This way, both scripts (in the webclient and in the iframe)
have the information of the trusted origin.
The client action does not have a complicated logic: it opens the
report's url in an iframe to show the report or open the web_editor
(the editor is activated by using the `enable_editor` query string.
If you click on the control panel button (save/discard) it'll send
message to the web_editor in the iframe to ask for save and discard.
It also waits for messages to know if the edition is done / cancelled
and toggle buttons of the control panel accordingly.
To allow that, we override the web_editor mechanism if it's opened in
an iframe and we chain to the `save` and `cancel` method deferred that
send message to the client action's window. We also make the web_editor
waits for message to start and stop the edition (controlled by the
control panel buttons).
The override of the action manager has been refactored / improved in
order to open the clien action and not the popup anymore. This way
we are able to, in the client action, return the current action with
the type `qweb-pdf` and the report is downloaded.
[1] https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage
Closes#12131
[FIX] report: don't hide errors in custom reports
If a custom report's render_html raises a KeyError for some
reason, Report.get_html would swallow the exception and fallback
on generic HTML rendering, usually blowing up with unfathomable
errors of missing rendering context data.
Check if a custom report object exists instead of waiting for a
KeyError.
A lock occurs when the user wants to print a report having multiple barcode while the server is
started in threaded-mode. The reason is that reportlab has to build a cache of the T1 fonts
before rendering a barcode (done in a C extension) and this part is not thread safe. We attempt
here to init the T1 fonts cache at the start-up of Odoo so that rendering of barcode in multiple
thread does not lock the server.
Another workaround was to set an explicit lock around the reportlab's function call, but it's
more dangerous as it may introduce undetectable lock between python and postgresql.
Thanks to @odony and @KangOl.
Fixes#12236
When cancelling and resetting to draft an invoice,
delete only the invoice report attachment from the
attachment, not all of them.
The reason why the report is removed from the
attachments on the invoice resetted to draft
is explained in
db0def38cc
opw-680295
If a custom report's render_html raises a KeyError for some
reason, Report.get_html would swallow the exception and fallback
on generic HTML rendering, usually blowing up with unfathomable
errors of missing rendering context data.
Check if a custom report object exists instead of waiting for a
KeyError.
- add or fix decorators on methods
- fix most compute methods (wrong dependencies)
- revert changes in `eval_context` of `ir.actions`
- various code simplifications and improvements
- migrate methods that were not
- ir_qweb: modify `QWebContext` to take an environment instead of `cr`, `uid`, `context`
- ir_qweb: convert `AssetsBundle` to use new API `env`
- ir_ui_view: use decorator `multi` on method `read_combined`
- ir_ui_view: ensure that method `render` is never called with an xml_id
- ir_ui_view: factor out part of the `ormcache` key of method `_read_template`
Up to this revision, barcodes set in reports are generated
using the controller `/report/barcode`,
using a `img` HTML tag, e.g. (from `report_location_barcode`)
```
<img t-if="not o.loc_barcode"
t-att-src="'/report/barcode/?type=%s&value=%s&width=%s&height=%s'
% ('Code128', o.name, 600, 100)" style="width:300px;height:50px"/>
``
This `/report/barcode` route is set as `auth='user'`, meaning
the route can only be accessed by signed in users.
When wkhtmltopdf prints a report containing such a barcode,
it calls this `report/barcode` route, making sure to pass
the request session in the cookies, so wkhtmltopdf
uses the same session than the one of the user. This is
needed, as only users can access this `report/barcode/` route.
This session is passed in `report.py`, in the method
`_run_wkhtmltopdf`, thanks to the --cookie wkhtmltopdf
parameter.
Nevertheless, if a report is printed through the website
front-end, as public (without a signed in user), the request
session is not associated to a signed in user, and therefore
the route `/report/barcode` refuses the access / redirects
to the login, making it impossible to print a report
containing a barcode without being signed in. This even
if the report is printed as `sudo` through a controller
(as this is the session of the not signed in user which is passed,
not a session associated to `sudo).
Fixes#10621
opw-667797
Specifically, when one API implementation calls the other one, it has to call
the method *from the same class*. Otherwise, overriding the method may result
in an infinite recursion. Consider:
class A(Model):
_name = 'stuff'
@api.v8
def foo(self):
return 42
@api.v7
def foo(self, cr, uid, context=None):
return self.browse(cr, uid, [], context).foo()
class B(Model):
_inherit = 'stuff'
def foo(self, cr, uid, context=None):
return super(B, self).foo(cr, uid, context=context) + 1
and now call: `env['stuff'].foo()`. This invokes `B.foo` (new-API), which
calls `B.foo` (old-API), which calls `A.foo` (old-API), which calls `B.foo`
(new-API) instead of `A.foo`!
This issue would not be present if old-API `A.foo` was defined as:
@api.v7
def foo(self, cr, uid, context=None):
return A.foo(self.browse(cr, uid, [], context))
As the report are downloaded from a controller, the context of the
webclient is lost. We chose to use `context_get` to get back one
instead of serializing the context trough the url.
When rendering QWeb reports, the user is being browsed without the
context, which means that any translatable field being accessed
through it will be in English.
rev 6327cda0a tried to workaround the fact that the assets bundle were
unreadable when they are created in the same request by opening multiple
cursors and commiting the writes in ir.attachment. Sadly, this strategy
introduces sometimes hard to reproduce and hard to understand deadlocks
when regenerating the assets. This commit tries a ~safer strategy by not
opening multiple cursors but commiting the writes in ir.attachment when
a magic key is in the context.