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 buat secara manual. Walaupun tidak susah-susah amat memang agak banyak perkerjaan yang harus dilakukan.

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

Sebagai contoh kasus kita akan membuat report sale order per sales person dalam bentuk 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 report 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 sale order yang akan dimasukkan ke dalam 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. Kita perlu menambahkan parameter id wizard pada controller, sehingga di controller nantinya 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>                        
                        <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 generate file excel.

# -*- 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

Yang perlu kita perhatikan adalah index dari row atau column. Jika kita menggunakan perintah write atau write_formula index dimulai dari 0. Jadi baris pertama atau kolom pertama adalah 0. Sedangkan jika kita menggunakan perintah merge_range index dimulai dari 1. Untuk lebih detail silakan baca dokumentasi xlsxwriter di sini

Tampilan form untuk menerima input user

Hasilnya

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

Leave a Reply

Your email address will not be published. Required fields are marked *