Membuat Report Excel pada Odoo

Untuk membuat report dalam bentuk PDF atau HTML odoo sudah menyediakan model tersendiri, yaitu ir.actions.report. Dengan model ini kita bisa membuat report PDF dan HTML dengan cukup mudah, tanpa perlu memikirkan bagaimana mengambil data dari database atau bagaimana membuat proses download report tersebut. Sayangnya untuk report dalam bentuk excel kita harus membuatnya secara manual. Walaupun tidak susah-susah amat, akan tetapi banyak perkerjaan yang harus dilakukan.

Untuk membuat report dalam bentuk file excel kita butuh library python. Bisa menggunakan xlsxwriter atau xlwt. Odoo sendiri (v12) menggunakan kedua library ini. Di addon web, untuk menangani export dan import odoo menggunakan xlwt, sedangkan pada addon enterprise account_reports odoo menggunakan xlsxwriter. Saya sendiri sudah menggunakan xlsxwriter dan saya rasa cukup mudah, sehingga tidak tertarik untuk mencoba menggunakan xlwt.

Update !!! Sejak odoo v13, mereka tidak lagi menggunakan xlwt. Kini mereka sepenuhnya menggunakan xlsxwriter.

Sebagai contoh kasus kita akan membuat report sales order per sales person dalam bentuk file excel. Nanti user bisa memilih sales person mana saja yang akan dibuat reportnya. Tentu saja dengan tanggal mulai dan tanggal akhir. Tiap sales person akan dibuatkan report dalam worksheet / tab yang berbeda sehingga tidak tercampur dengan sales person lainnya. Isi reportnya sederhana saja, cukup no dokumen, tanggal, nama customer dan total penjualan.

Pertama install library xlsxwriter dengan perintah dibawah ini.

sudo pip3 install xlsxwriter

Kemudian buat Transient Model / wizard untuk menerima input user mengenai sales person mana saja yang akan dibuatkan report-nya. Serta tanggal mulai dan tangal akhir dari sales order yang akan dimasukkan ke dalam report. Dalam wizard ini kita juga perlu menyertakan sebuah tombol, untuk mengarahkan user menuju url tertentu untuk mendownload report.

# -*- coding: utf-8 -*-

from odoo import models, fields, api, exceptions, _

class SaleWizard(models.TransientModel):
    _name = 'ng.sale.wizard'


    user_id = fields.Many2many('res.users', string='Sales Person')
    start_date = fields.Date("Start Date")
    end_date = fields.Date("End Date")


    def get_excel_report(self):
        # redirect ke controller /sale/excel_report untuk generate file excel
        return {
            'type': 'ir.actions.act_url',
            'url': '/sale/excel_report/%s' % (self.id),
            'target': 'new',
        }

Model diatas hanya sebagai perantara saja, untuk menerima input user. Proses membuat report yang sebenarnya ada di controller /sale/excel_report, yaitu ketika user menekan sebuah tombol, method get_excel_report pada model di atas akan dieksekusi, dan user akan diarahkan ke controler /sale/excel_report untuk mendownload report sebenarnya. Kita perlu menambahkan parameter id wizard pada controller, sehingga di controller kita bisa tahu sales person, tanggal mulai dan tanggal akhir yang dipilih oleh user. Jika anda belum mengetahui tentang controller silakan baca tulisan berikut ini.

Selanjutnya buat view yang berupa menuitem dan form untuk menerima input user.

<?xml version="1.0" encoding="utf-8"?>
<odoo>  
    <data>
        <!-- Form untuk memilih sales person dan tanggal mulai dan tanggal akhir -->
        <record id="ng_sale_wizard_form" model="ir.ui.view">
            <field name="name">Sales Report in Excel Format</field>
            <field name="model">ng.sale.wizard</field>
            <field name="arch" type="xml">
                <form string="Sales Report in Excel Format">
                    <group>
                        <group>
                            <field name="user_id" required="1" widget="many2many_tags"/>
                        </group>
                        <group>
                            <field name="start_date" required="1"/>
                            <field name="end_date" required="1"/>
                        </group>
                    </group>
                    <footer>
                        <!-- Tombol untuk mulai download report -->                        
                        <button name="get_excel_report" string="Export (XLS)" type="object"
                                class="btn-primary"/>
                        <button string="Cancel" class="btn-default" special="cancel"/>
                    </footer>
                </form>
            </field>
        </record> 

        <!-- Action untuk membuka form diatas -->
        <record model="ir.actions.act_window" id="action_open_ng_sale_wizard_form">
            <field name="name">Sales Report in Excel Format</field>
            <field name="type">ir.actions.act_window</field>
            <field name="res_model">ng.sale.wizard</field>
            <field name="view_type">form</field>
            <field name="view_mode">form</field>
            <field name="target">new</field>
        </record>
        
        <!-- Tambah menu baru di Sale >> Reportng -->
        <menuitem name="Sales Report in Excel Format"
                id="menu_sale_report_in_excel"
                parent="sale.menu_sale_report"
                action="action_open_ng_sale_wizard_form" />

        
    </data>
</odoo>

Yang terakhir buat controller untuk mulai meng-generate file excel. Silakan baca komentar di dalam kode dibawah ini untuk mengetahui kegunaan beberapa method dari library xlswriter.

# -*- coding: utf-8 -*-

from odoo import http
from odoo.http import content_disposition, request
import io
import xlsxwriter
    


class SaleExcelReportController(http.Controller):
    @http.route([
        '/sale/excel_report/<model("ng.sale.wizard"):wizard>',
    ], type='http', auth="user", csrf=False)
    def get_sale_excel_report(self,wizard=None,**args):
        # wizard ini adalah model yang dikirim dengan method get_excel_report
        # pada model ng.sale.wizard
        # berisi data sales person, tanggal mulai dan tanggal akhir

        # buat response dengan header berupa file excel
        # agar browser segera mendownload response
        # header Content-Disposition ini adalah nama file
        # isi sesuai kebutuhan

        response = request.make_response(
                    None,
                    headers=[
                        ('Content-Type', 'application/vnd.ms-excel'),
                        ('Content-Disposition', content_disposition('Sales Report in Excel Format' + '.xlsx'))
                    ]
                )

        # buat object workbook dari library xlsxwriter
        output = io.BytesIO()
        workbook = xlsxwriter.Workbook(output, {'in_memory': True})

        # buat style untuk mengatur jenis font, ukuran font, border dan alligment
        title_style = workbook.add_format({'font_name': 'Times', 'font_size': 14, 'bold': True, 'align': 'center'})
        header_style = workbook.add_format({'font_name': 'Times', 'bold': True, 'left': 1, 'bottom':1, 'right':1, 'top':1, 'align': 'center'})
        text_style = workbook.add_format({'font_name': 'Times', 'left': 1, 'bottom':1, 'right':1, 'top':1, 'align': 'left'})
        number_style = workbook.add_format({'font_name': 'Times', 'left': 1, 'bottom':1, 'right':1, 'top':1, 'align': 'right'})

        # loop user / sales person yang dipilih
        for user in wizard.user_id:
            # buat worksheet / tab per user 
            sheet = workbook.add_worksheet(user.name)
            # set orientation jadi landscape
            sheet.set_landscape()
            # set ukuran kertas, 9 artinya kertas A4
            sheet.set_paper(9)
            # set margin kertas dalam satuan inchi
            sheet.set_margins(0.5,0.5,0.5,0.5)

            # set lebar kolom
            sheet.set_column('A:A', 5)
            sheet.set_column('B:E', 15)

            # judul report
            # merge cell A1 sampai E1 dengan ukuran font 14 dan bold
            sheet.merge_range('A1:E1', 'Sales Report in Excel Format', title_style)
            
            # judul tabel
            sheet.write(1, 0, 'No.', header_style)
            sheet.write(1, 1, 'No. Dokumen', header_style)
            sheet.write(1, 2, 'Tanggal', header_style)
            sheet.write(1, 3, 'Pelanggan', header_style)
            sheet.write(1, 4, 'Total', header_style)

            row = 2
            number = 1

            # cari sale order untuk sales person terpilih pada rentang tanggal yang dipilih user
            orders = request.env['sale.order'].search([('user_id','=',user.id), ('date_order','>=', wizard.start_date), ('date_order','<=', wizard.end_date)])
            for order in orders:
                # content / isi report
                sheet.write(row, 0, number, text_style)
                sheet.write(row, 1, order.name, text_style)
                sheet.write(row, 2, str(order.date_order), text_style)
                sheet.write(row, 3, order.partner_id.name, text_style)
                sheet.write(row, 4, order.amount_total, number_style)

                row += 1
                number += 1

            # buat formula untuk men-sum / mentotal sales per user
            sheet.merge_range('A' + str(row+1) + ':D' + str(row+1), 'Total', text_style)
            sheet.write_formula(row, 4, '=SUM(E3:E' + str(row) + ')', number_style)

        # masukkan file excel yang sudah digenerate ke response dan return 
        # sehingga browser bisa menerima response dan mendownload file yang sudah digenerate
        workbook.close()
        output.seek(0)
        response.stream.write(output.read())
        output.close()

        return response

Saat membuat file excel dengan xlswriter, yang perlu kita perhatikan adalah index dari baris dan kolom. Jika kita menggunakan perintah write atau write_formula index dimulai dari 0. Jadi baris pertama atau kolom pertama adalah 0. Kode di bawah ini artinya adalah kita menambah teks ‘No.’ di cell ‘A2’. Kenapa begitu ? Karena baris dengan index ‘1’ adalah baris ke ‘2’, sedangkan kolom dengan index ‘0’ adalah kolom pertama yaitu kolom (‘A’).

sheet.write(1, 0, 'No.', header_style)

Sedangkan jika kita menggunakan perintah merge_range index dimulai dari 1, karena kita menulis nama cell-nya langsung, seperti pada kode dibawah ini.

sheet.merge_range('A1:E1', 'Sales Report in Excel Format', title_style)

Untuk lebih detail silakan baca dokumentasi xlsxwriter di sini.

Berikut ini adalah tampilan form untuk menerima input user.

Tampilan wizard download file excel pada odoo

Dan ini adalah tampilan file excel hasil download yang dibuka di libre office.

Tampilan file excel odoo hasil download di libre office

Perlu diketahui formula yang ditulis di xlsxwriter tidak langsung di eksekusi. Jadi kita perlu menghitung ulang formula tersebut. Jika di Libre Office Calc kita bisa menggunakan perintah SHIFT + CTRL + F9

Download Source Code

Update !!!. Ada beberapa perubahan di versi odoo terbaru. Jika anda menggunakan odoo v14 kode diatas tidak akan berjalan. Silakan baca versi bahasa inggris dari tulisan ini untuk mendownload source code untuk odoo v14.

Tulisan Serupa

2 Replies to “Membuat Report Excel pada Odoo”

  1. saya coba install addon ini di odoo 10 namun muncul error berikut :

    raise ValueError(‘External ID not found in the system: %s’ % xmlid)
    ParseError: “External ID not found in the system: sale.menu_sale_report” while parsing /mnt/extra-addons/tutorial_excel/views/views.xml:43, near

    mohon bantuannya Pak

    1. pakai odoo 11 keatas pak, odoo 10 gak punya menu Sales/Reporting jadinya modulenya gak bisa diinstall, atau ubah modulenya agar jadi child dari menu lain.

Leave a Reply