Create Custom Report Odoo 11

Creating a custom report on Odoo 11 is very different from Odoo 10. That’s because on Odoo 11 doesn’t have report module like Odoo 10, and compounded by incorrect documentation on Odoo website, as can be seen in https://www.odoo.com/documentation/11.0/reference/reports.html#custom-reports.

In this tutorial, I will display the employee attendance summary (recap), and display presence and absence data for each employee. The final result of the report is as shown below.

Odoo 11 Custom Report pic 1

Initial Preparation.

For initial preparation, you must create a new database and tick “Load demonstration data”, so that there is preliminary data that we can use to test the code. Then install hr_attendance module, this module is a default module from Odoo. And don’t forget to make sure that the wkhtmltopdf 0.12.1 has been installed.

After installing the hr_attendance module, you can see attendance data with the following demo data is as shown below.

Odoo 11 Custom Report pic 2

Create a wizard model.

Create a wizard model that inherits from TransientModel.

from odoo import models, fields, api


class AttendanceRecapReportWizard(models.TransientModel):
    _name = 'attendance.recap.report.wizard'

    date_start = fields.Date(string="Start Date", required=True, default=fields.Date.today)
    date_end = fields.Date(string="End Date", required=True, default=fields.Date.today)

    @api.multi
    def get_report(self):
        pass

Create an xml wizard view.

Create a view to display the wizard based on the wizard model above.

<odoo>
    <record model="ir.ui.view" id="attendance_recap_report_wizard">
        <field name="name">HR Attendance Custom Recap Report</field>
        <field name="model">attendance.recap.report.wizard</field>
        <field name="type">form</field>
        <field name="arch" type="xml">
            <form string="Attendance Recap Report">
                <group>
                    <group>
                        <field name="date_start"/>
                    </group>
                    <group>
                        <field name="date_end"/>
                    </group>
                </group>
                <footer>
                    <button name="get_report" string="Get Report" type="object" class="oe_highlight"/>
                    <button string="Cancel" special="cancel"/>
                </footer>
            </form>
        </field>
    </record>

    <act_window id="action_attendance_recap_report_wizard"
                name="Attendance Recap Report"
                res_model="attendance.recap.report.wizard"
                view_mode="form"
                target="new"/>

    <menuitem action="action_attendance_recap_report_wizard"
              id="menu_attendance_report_wizard"
              parent="hr_attendance.menu_hr_attendance_report"/>
</odoo>

After creating the wizard, the result is as shown below.

Odoo 11 Custom Report pic 3

Modify wizard model and create new abstract model.

Modify method get_report() in model attendance.recap.report.wizard, and create a model that inherits from AbstractModel. As you can see below.

from datetime import datetime, timedelta

from odoo import models, fields, api
from odoo.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT


class AttendanceRecapReportWizard(models.TransientModel):
    _name = 'attendance.recap.report.wizard'

    date_start = fields.Date(string="Start Date", required=True, default=fields.Date.today)
    date_end = fields.Date(string="End Date", required=True, default=fields.Date.today)

    @api.multi
    def get_report(self):
        """Call when button 'Get Report' clicked.
        """
        data = {
            'ids': self.ids,
            'model': self._name,
            'form': {
                'date_start': self.date_start,
                'date_end': self.date_end,
            },
        }

        # use `module_name.report_id` as reference.
        # `report_action()` will call `get_report_values()` and pass `data` automatically.
        return self.env.ref('cj_custom_report.recap_report').report_action(self, data=data)


class ReportAttendanceRecap(models.AbstractModel):
    """Abstract Model for report template.
    for `_name` model, please use `report.` as prefix then add `module_name.report_name`.
    """

    _name = 'report.cj_custom_report.attendance_recap_report_view'

    @api.model
    def get_report_values(self, docids, data=None):
        date_start = data['form']['date_start']
        date_end = data['form']['date_end']
        date_start_obj = datetime.strptime(date_start, DATE_FORMAT)
        date_end_obj = datetime.strptime(date_end, DATE_FORMAT)
        date_diff = (date_end_obj - date_start_obj).days + 1

        docs = []
        employees = self.env['hr.employee'].search([], order='name asc')
        for employee in employees:
            presence_count = self.env['hr.attendance'].search_count([
                ('employee_id', '=', employee.id),
                ('check_in', '>=', date_start_obj.strftime(DATETIME_FORMAT)),
                ('check_out', '<=', date_end_obj.strftime(DATETIME_FORMAT)),
            ])

            absence_count = date_diff - presence_count

            docs.append({
                'employee': employee.name,
                'presence': presence_count,
                'absence': absence_count,
            })

        return {
            'doc_ids': data['ids'],
            'doc_model': data['model'],
            'date_start': date_start,
            'date_end': date_end,
            'docs': docs,
        }

Create an xml template report.

Last step is to create an xml for template report as needed.

<odoo>
    <record model="report.paperformat" id="paperformat_attendance_recap_report">
        <field name="name">paperformat.attendance.recap.report</field>
        <field name="default" eval="True"/>
        <field name="format">A4</field>
        <field name="page_width">0</field>
        <field name="page_width">0</field>
        <field name="orientation">Portrait</field>
        <field name="margin_top">30</field>
        <field name="margin_right">5</field>
        <field name="margin_bottom">10</field>
        <field name="margin_left">5</field>
        <field name="header_line" eval="False"/>
        <field name="header_spacing">20</field>
        <field name="dpi">90</field>
    </record>

    <report id="recap_report"
            model="attendance.recap.report.wizard"
            string="Attendance Recap Report"
            report_type="qweb-pdf"
            name="cj_custom_report.attendance_recap_report_view"
            paperformat="paperformat_attendance_recap_report"
            menu="False"/>

    <template id="attendance_recap_report_view">
        <div class="header" style="border-bottom: 2px solid black">
            <h3 class="text-center">Attendance Recap Report</h3>
            <h4 class="text-center">
                <strong>From</strong>:
                <t t-esc="date_start"/>
                <strong>To</strong>:
                <t t-esc="date_end"/>
            </h4>
        </div>
        <div class="article">
            <table class="table table-condensed table-bordered" style="width: 100%">
                <thead>
                    <th class="text-center" style="width: 70%">Employee</th>
                    <th class="text-center" style="width: 15%">Presence</th>
                    <th class="text-center" style="width: 15%">Absence</th>
                </thead>
                <tbody>
                    <t t-foreach="docs" t-as="doc">
                        <tr>
                            <td>
                                <span t-esc="doc['employee']"/>
                            </td>
                            <td class="text-center">
                                <span t-esc="doc['presence']"/>
                            </td>
                            <td class="text-center">
                                <span t-esc="doc['absence']"/>
                            </td>
                        </tr>
                    </t>
                </tbody>
            </table>
        </div>
        <div class="footer">
            <div class="row text-center" style="border-top: 1px solid black;">
                <div class="col-xs-3 col-xs-offset-9 text-right">
                    <ul class="list-inline" style="margin-top:10px;">
                        <li>Page:</li>
                        <li>
                            <span class="page"/>
                        </li>
                        <li>/</li>
                        <li>
                            <span class="topage"/>
                        </li>
                    </ul>
                </div>
            </div>
        </div>
    </template>
</odoo>

That’s it. Please check https://github.com/CakJuice/odoo11-custom-report for full code, or you can download then install the module to try.

Thanks for reading, see you.

Further reads:

Comments

2 thoughts on “Create Custom Report Odoo 11”

  1. Pingback: Create Custom Report Odoo 12 - Cak Juice

  2. Pingback: How To Add Images or Barcodes in Odoo Report - Cak Juice

Comments are closed.