Search code examples
pythonodoo-11qweb

Odoo 11 Qweb Custom Report : Error to render compiling AST TypeError: 'NoneType' object is not callable


My first time publishing something here, hope it will work !

I'm trying to create a custom report in Odoo 11, in a custom module I did, I've followed the data I could gather but I can't get past the following problem.

  • So I created the Report like this :
      <report 
          id="production_order_report2"
          model="pb.orders.products"
          string="Production Order (English)"
          report_type="qweb-pdf"
          name="probespoketest.production_order_report_en2_template"
          file="probespoketest.production_order_report_en2_template"
          attachment_use="False"
      />
  • My Qweb report is :
  <template id="production_order_report_en2_template">
   <t t-call="web.html_container">
        <t t-foreach="docs" t-as="o">
            <t t-call="web.internal_layout">
                <div class="page">
                    <h2>Report title</h2>
                    <p>This object's name is</p>
                  <t t-esc="testing()"/>
                </div>
            </t>
        </t>
    </t>
      </template>
  • My python code is :
from odoo import api, fields, models, _

class OrderProductsFunctions(models.AbstractModel):
    _name = 'report.probespoketest.production_order_report_en2_template'

    def _testing(self):
      result = 0
        return result
  • My error is :
 Odoo Server Error
    Traceback (most recent call last):
      File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_qweb/qweb.py", line 342, in _compiled_fn
        return compiled(self, append, new, options, log)
      File "<template>", line 1, in template_1454_24554
      File "<template>", line 2, in body_call_content_24553
      File "<template>", line 3, in foreach_24552
      File "<template>", line 4, in body_call_content_24551
    TypeError: 'NoneType' object is not callable

    During handling of the above exception, another exception occurred:

    Traceback (most recent call last):
      File "/opt/odoo/odoo11/addons/web/controllers/main.py", line 1617, in report_download
        response = self.report_routes(reportname, docids=docids, converter='pdf')
      File "/opt/odoo/odoo11/odoo/http.py", line 516, in response_wrap
        def response_wrap(*args, **kw):
      File "/opt/odoo/odoo11/addons/web/controllers/main.py", line 1566, in report_routes
        pdf = report.with_context(context).render_qweb_pdf(docids, data=data)[0]
      File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_actions_report.py", line 628,

in render_qweb_pdf html = self.with_context(context).render_qweb_html(res_ids, data=data)[0] File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_actions_report.py", line 666, in render_qweb_html return self.render_template(self.report_name, data), 'html' File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_actions_report.py", line 470, in render_template return view_obj.render_template(template, values) File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_ui_view.py", line 1195, in render_template return self.browse(self.get_view_id(template)).render(values, engine) File "/opt/odoo/odoo11/addons/web_editor/models/ir_ui_view.py", line 27, in render return super(IrUiView, self).render(values=values, engine=engine) File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_ui_view.py", line 1204, in render return self.env[engine].render(self.id, qcontext) File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_qweb/ir_qweb.py", line 57, in render return super(IrQWeb, self).render(id_or_xml_id, values=values, **context) File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_qweb/qweb.py", line 271, in render self.compile(template, options)(self, body.append, values or {}) File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_qweb/qweb.py", line 349, in _compiled_fn raise QWebException("Error to render compiling AST", e, path, node and etree.tostring(node[0], encoding='unicode'), name) odoo.addons.base.ir.ir_qweb.qweb.QWebException: 'NoneType' object is not callable Traceback (most recent call last): File "/opt/odoo/odoo11/odoo/addons/base/ir/ir_qweb/qweb.py", line 342, in _compiled_fn return compiled(self, append, new, options, log) File "", line 1, in template_1454_24554 File "", line 2, in body_call_content_24553 File "", line 3, in foreach_24552 File "", line 4, in body_call_content_24551 TypeError: 'NoneType' object is not callable

    Error to render compiling AST
    TypeError: 'NoneType' object is not callable
    Template: 1454
    Path: /templates/t/t/t/t/div/t
    Node: <t t-esc="testing()"/>

manifest.py and the two init.py should be ok but I can provide the code if needed.

Maybe the problem is that I should call the method testing() adding something. If I try to do :

> <t t-esc="docs.testing()"/>

Then I get :

> Error to render compiling AST AttributeError: 'pb.orders.products' object has no attribute 'testing' Template: 1454 Path:
> /templates/t/t/t/t/div/t Node: <t t-esc="docs.testing()"/>

Because my testing method is stored in another module that depends on the one I try to modify. But I don't know how to resolve this...

=== EDIT ===

@WaKo

Following your answer I coded this in my models.py (in the module where pb.orders.products is coded, aka "probespokeq") :

class OrderProductsFunctions(models.AbstractModel):
    _name = 'report.probespokeq.production_order_report_en2_template'
def _testing(self):
    result = 0
    return result

@api.model
def render_html(self, docids, data=None):
    report_obj = self.env['report']
    report = report_obj._get_report_from_name('probespokeq.production_order_report_en2_template')
    docargs = {
        'doc_ids': docids,
        'doc_model': report.pb.orders.products,
        'docs': self,
        'testing': self._testing,  # You need to add this line
    }
    return report_obj.render('probespokeq.production_order_report_en2_template', docargs)

Yet, I still have the same error message. Any idea ?

===== EDIT 2 ====

So, now I changed my class according to @WaKo suggestion with get_report_values instead of render_html so it looks like this :

class OrderProductsFunctions(models.AbstractModel):
    _name = 'report.probespokeq.production_order_report_en2_template'

    def _testing(self):
        result = 0
        return result

    @api.multi
  def get_report_values(self, docids, data=None):
      docs = self.env['pb.orders.products'].browse(docids)
      return {
          'doc_ids': docs.ids,
          'doc_model': 'pb.orders.products',
          'docs': docs,
          'testing': self._testing,  # You need to add this line
      }

So, now I change my xml as follows :

> <?xml version="1.0"?> <t
> t-name="probespokeq.production_order_report_en2_template">    <t
> t-call="web.html_container">
>         <t t-foreach="docs" t-as="o">
>             <t t-call="web.internal_layout">
>                 <div class="page">
>                     <h2>Report title</h2>
>                     <p>This object's name is probespokeq</p>
>                   <t t-esc="o.testing()"/>
>                 </div>
>             </t>
>         </t>
>     </t>      </t>

And I get :

> Error to render compiling AST AttributeError: 'pb.orders.products'
> object has no attribute 'testing' Template: 1455 Path:
> /templates/t/t/t/t/div/t Node:  <t t-esc="o.testing()"/>

Solution

  • To use testing in you report template like you did you should define it in a custom report parser and add it in docargs dict.

    from odoo import api, models
    
    class ParticularReport(models.AbstractModel):
        _name = 'report.module.report_name'
    
        def _testing(self):
            result = 0
            return result
    
        @api.model
        def render_html(self, docids, data=None):
            report_obj = self.env['report']
            report = report_obj._get_report_from_name('module.report_name')
            docargs = {
                'doc_ids': docids,
                'doc_model': report.model,
                'docs': self,
                'testing': self._testing,  # You need to add this line
            }
            return report_obj.render('module.report_name', docargs)
    

    You can also define testing method in pb.orders.products and use o.testing() in your template.

    You can find an example of a custom report which overwrites get_report_values function:

    @api.multi
    def get_report_values(self, docids, data=None):
        docs = self.env['pb.orders.products'].browse(docids)
        return {
            'doc_ids': docs.ids,
            'doc_model': 'pb.orders.products',
            'docs': docs,
            'testing': self._testing,  # You need to add this line
        }
    

    Examples

    To use a method from your model:

    class PBOrdersProducts(models.Model):
        _name = "pb.orders.products"
    
        name = fields.Char()
    
        def testing(self):
            result = 0
            return result
    

    And use:

    <t t-esc="o.testing()"/>
    

    In the template.

    To use a custom parser like they did in sale module:

    class ParticularReport(models.AbstractModel):
        _name = 'report.test.production_order_report'
    
        def _testing(self):
            result = 0
            return result
    
        @api.multi
        def get_report_values(self, docids, data=None):
            docs = self.env['pb.orders.products'].browse(docids)
            return {
                'doc_ids': docs.ids,
                'doc_model': 'pb.orders.products',
                'docs': docs,
                'testing': self._testing,  # You need to add this line
            }
    

    And in this case you will need to use:

    <t t-esc="testing()"/>