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.
Dan ini adalah tampilan file excel hasil download yang dibuka 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
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.
2 Replies to “Membuat Report Excel pada Odoo”
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
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.