Dasar-dasar Report

7. Dasar-dasar Report

Penulis: Noprianto

Untuk membuat report sendiri di OpenERP, kita memiliki beberapa pilihan cara. Yang akan kita bahas dalam bab ini adalah penggunaan RML, yang merupakan cara original dalam membuat report di OpenERP. Cara lain juga tersedia, baik yang datang bersama OpenERP ataupun menggunakan modul dari pihak lain.

Report dapat ikut pada model, seperti ketika kita memilih beberapa record dari res.partner, kemudian klik pada tombol Print dan memilih report yang tersedia. Report juga dapat ditempatkan pada menu/menuitem tersendiri. Apabila parameter dibutuhkan dalam menghasilkan report, kita bisa menggunakan wizard.

Pembahasan akan dilakukan langsung pada contoh modul. Catatan: definisi wizard umumnya ditempatkan dalam sub direktori wizard dan definisi report

umumnya ditempatkan dalam sub direktori report.

7.1 Definisi

Sebagaimana halnya view, report juga didefinisikan di dalam file XML. <?xml version="1.0"?>

<openerp> <data> <definisi report> …. …. …. <definisi report>

</data> </openerp>

Untuk mendefinisikan report, kita gunakan tag <report> dengan sejumlah atribut. Di dalam bab ini, kita akan menggunakan: • id: id report (unik).

• name: nama report (dibutuhkan). • string: judul report (dibutuhkan). • model: model dimana report didefinisikan (dibutuhkan). • rml: path ke file RML, relatif terhadap addons (apabila digunakan). • auto: apabila False, custom parser digunakan. • header: apabila False, header tidak digunakan.

Contoh:

<report id="report_buku_partner_nop_13" name="buku.partner.nop.13" string="Report 13" model="res.partner" rml="buku_partner_nop_13/report.rml" auto="True" header="False"/>

7.2 Report dengan RML

Untuk membuat report dengan RML: • Kita perlu membuat satu file RML, yang secara sintaks, merupakan file XML. • Kita tidak membutuhkan modul tambahan pada OpenERP. • Kita juga tidak membutuhkan program/service lain yang berjalan di server, selain OpenERP.

Salah satu kekurangan bekerja dengan RML adalah harus menulis kode RML itu sendiri. Untungnya: • Dokumentasi RML sendiri cukup lengkap. Untuk informasi selengkapnya, kunjungilah

http://www.reportlab.com. • OpenERP datang bersama modul base_report_designer yang menyediakan plugin untuk OpenOffice, dimana kita bisa mendesain report dengan OpenOffice, dan kemudian mengekspor ke

file RML. Fitur pelengkap lain juga disediakan. Pembahasan modul base_report_designer berada di luar cakupan bab ini.

File RML akan di-parse oleh OpenERP dan report dalam format PDF akan dihasilkan. Kita dapat menentukan ukuran kertas apabila diperlukan.

7.3 Kerangka RML

Berikut adalah kerangka isi file RML yang kita gunakan dalam bab ini: <?xml version="1.0"?>

<document filename="report.pdf"> <template pageSize="(612, 792)" title="Report">

<pageTemplate id="page"> <frame id="page" x1="30.0" y1="30.0" width="552" height="732"/> </pageTemplate> </template>

<stylesheet> <blockTableStyle id="Table"> <blockAlignment value="LEFT"/> <blockValign value="TOP"/> <lineStyle kind="GRID" colorName="#000000" start="0,0" stop="-1,-1"/>

</blockTableStyle> <paraStyle name="Standard" fontName="Courier"/>

</stylesheet> <story>

</story> </document>

Di dalam kerangka tersebut, kita: • Menentukan template, termasuk ukuran kertas (contoh: pageSize="(612, 792)", dalam 1/72

inch). • Menentukan nomor halaman (dalam contoh 7.4) • Menentukan style (misal untuk tabel dan paragraf). Khusus untuk paragraf, kerangka tersebut

menggunakan font Courier. • Akan menambahkan paragraf dalam blok <story> • Akan menambahkan tabel dalam blok <story>

7.4 Report pada model

Dalam contoh modul buku_partner_nop_13, kita bekerja pada model res.partner dan akan menambahkan satu report. Untuk menghasilkan report, satu atau lebih record harus dipilih terlebih dahulu. Setelah itu, kita dapat klik pada tombol Print dan memilih Report 13 dari daftar.

Berikut adalah definisi report selengkapnya (buku_partner_nop_13_report.xml): <?xml version="1.0"?>

<openerp> <data>

<report

id="report_buku_partner_nop_13" name="buku.partner.nop.13" string="Report 13" model="res.partner" rml="buku_partner_nop_13/report.rml" auto="True" header="False"/>

</data> </openerp>

Dari definisi tersebut, bisa kita lihat bahwa kita akan bekerja dengan model res.partner, tanpa penggunaan custom parser dan tidak menambahkan header pada laporan.

Laporan sendiri akan dihasilkan dari file report.rml: <?xml version="1.0"?>

<document filename="report.pdf"> <template pageSize="(612, 792)" title="Report">

<pageTemplate id="page"> <frame id="page" x1="30.0" y1="30.0" width="552" height="732"/> </pageTemplate> </template>

<stylesheet> <blockTableStyle id="Table"> <blockAlignment value="LEFT"/> <blockValign value="TOP"/> <lineStyle kind="GRID" colorName="#000000" start="0,0" stop="-1,-1"/>

</blockTableStyle> <paraStyle name="Standard" fontName="Courier"/>

</stylesheet> <story>

<para style="Standard">Report 13<br/><br/></para> <blockTable colWidths="100,200,252" repeatRows="1" style="Table">

<tr> <td> <para style="Standard">ID</para> </td> <td> <para style="Standard">Nama</para> </td> <td> <para style="Standard">Website</para> </td> </tr> <tr> <td> <para style="Standard">[[ repeatIn(objects, 'o') ]]</para> <para style="Standard">[[ o.id ]]</para>

</td> <td> <para style="Standard">[[ o.name ]]</para> </td> <td> <para style="Standard">[[ o.website ]]</para> </td> </tr> </blockTable> </story>

</document> Apa yang kita buat dalam file RML tersebut adalah:

• Menambahkan paragraf, menggunakan style 'Standard', termasuk dalam sel tabel.

<para style="Standard">Report 13<br/><br/></para> <para style="Standard">[[ o.website ]]</para>

• Menambahkan tabel, dengan tiga kolom sesuai ukuran lebar, menggunakan style 'Table' dan baris pertama akan diulang. Baris dan kolom mirip dengan pada HTML.

<blockTable colWidths="100,200,252" repeatRows="1" style="Table"> </blockTable>

Yang perlu kita perhatikan sehubungan ekspresi pada OpenERP adalah: • Kode dalam [[ ]] akan dievaluasi sebagai ekspresi Python. • objects akan berisikan list dari record yang ada. Apabila kita memilih 3 record, maka objects juga

akan terdiri dari 3 record. • Dalam contoh tersebut, kita menggunakan fungsi repeatIn(list, 'var', 'tag'). Fungsi ini akan mengulang parent elemen tag untuk setiap object dalam list. Object dapat diakses dengan variabel

var selama perulangan. • Variabel o kita gunakan untuk setiap object. • Karena kita bekerja dengan model res.partner, maka field id, name dan website tersedia.

Berikut adalah isi file __openerp__.py: {

'name' : 'Partner 13' , 'version' : '1.0' , 'author' : 'noprianto' , 'description' : 'partner, laporan sederhana' , 'category' : 'Buku' , 'website' : 'https://github.com/id-python/buku-openerp' , 'depends' :[ 'base' ], 'data' :[ 'buku_partner_nop_13_report.xml' ],

} Sementara __init__.py merupakan file kosong.

7.5 Custom parser

Dalam contoh modul buku_partner_nop_14, kita bekerja pada model res.partner dan akan menambahkan satu report. Untuk menghasilkan report, satu atau lebih record harus dipilih terlebih dahulu. Setelah itu, kita dapat klik pada tombol Print dan memilih Report 14 dari daftar.

Berbeda dengan contoh sebelumnya, kita akan menggunakan custom parser untuk report yang kita buat.

Custom parser memungkinkan kita untuk menambahkan fungsi tertentu ke dalam report. Sebagai contoh, dengan parser default, kita bisa mengakses diantaranya beberapa informasi berikut, dalam report, sebagai ekspresi Python ([[ ]]): • user: object user OpenERP • repeatIn seperti dibahas sebelumnya • time: modul time

Custom parser yang kita buat diturunkan dari class rml_parse dalam modul report_sxw.py, dalam package openerp.report.

Apa yang akan kita lakukan dalam contoh ini adalah menambah akses untuk modul random pada localcontext, yang akan dilewatkan pada report, sehingga kita bisa menggunakannya dalam report. Contoh lain dalam penggunaan OpenERP sehari-hari adalah penambahan fungsi terbilang.

Berikut adalah definisi report selengkapnya (buku_partner_nop_14_report.xml): <?xml version="1.0"?>

<openerp> <data>

<report

id="report_buku_partner_nop_14" name="buku.partner.nop.14" string="Report 14" model="res.partner" rml="buku_partner_nop_14/report.rml" auto="False" header="False"/>

</data> </openerp>

Karena kita menggunakan custom parser, auto kita berikan nilai “False”. Laporan sendiri akan dihasilkan dari file report.rml: <?xml version="1.0"?>

<document filename="report.pdf"> <template pageSize="(612, 792)" title="Report">

<pageTemplate id="page">

<frame id="page" x1="30.0" y1="30.0" width="552" height="732"/> <pageGraphics>

<setFont name="Courier" size="8"/> <drawString x="30" y="20">Halaman <pageNumber/></drawString>

</pageGraphics> </pageTemplate> </template>

<stylesheet> <blockTableStyle id="Table"> <blockAlignment value="LEFT"/> <blockValign value="TOP"/> <lineStyle kind="GRID" colorName="#000000" start="0,0" stop="-1,-1"/>

</blockTableStyle> <paraStyle name="Standard" fontName="Courier"/>

</stylesheet> <story>

<para style="Standard">Report 14<br/><br/></para> <para style="Standard">[[ time.asctime() ]]<br/><br/></para> <para style="Standard">[[ user.name ]]<br/><br/></para> <para style="Standard">[[ random.random() ]]<br/><br/></para> <blockTable colWidths="100,200,252" repeatRows="1" style="Table">

<tr> <td> <para style="Standard">ID</para> </td> <td> <para style="Standard">Nama</para> </td> <td> <para style="Standard">Website</para> </td> </tr> <tr> <td> <para style="Standard">[[ repeatIn(objects, 'o') ]]</para> <para style="Standard">[[ o.id ]]</para>

</td> <td> <para style="Standard">[[ o.name ]]</para> </td> <td> <para style="Standard">[[ o.website ]]</para> </td> </tr> </blockTable> </story>

</document> Untuk menambahkan nomor halaman, kita bisa tempatkan dalam blok <pageGraphics> di dalam

<pageTemplate>. Untuk menggambar string, kita gunakan tag <drawString> pada posisi x dan y tertentu. Nomor halaman aktif didapatkan dengan tag <pageNumber>.

<pageGraphics> <setFont name="Courier" size="8"/> <drawString x="30" y="20">Halaman <pageNumber/></drawString>

</pageGraphics>

Karena kita punya akses ke modul time, maka kita bisa gunakan:

<para style="Standard">[[ time.asctime() ]]<br/><br/></para>

User yang menjalankan report bisa diakses dengan cara berikut (perhatikanlah bahwa ini adalah object user OpenERP):

<para style="Standard">[[ user.name ]]<br/><br/></para>

Berikut adalah parser kita, yang disimpan dalam file buku_partner_nop_14_report.py: import random Berikut adalah parser kita, yang disimpan dalam file buku_partner_nop_14_report.py: import random

class buku_partner_nop_14(report_sxw.rml_parse): def __init__(self, cr, uid, name, context): super(buku_partner_nop_14, self).__init__(cr, uid, name, context=context) self.localcontext.update(

{ 'random' : random, } )

report_sxw.report_sxw( 'report.buku.partner.nop.14' , 'res.partner' ,

'addons/buku_partner_nop_14/report.rml' , parser=buku_partner_nop_14)

Dalam constructor class yang diturunkan dari rml_parse, kita memanggil constructor parent dan mengupdate localcontext. Akses untuk modul random kita berikan dengan key 'random'. Kita bisa gunakan dalam report:

<para style="Standard">[[ random.random() ]]<br/><br/></para>

Tidak lupa, kita instansiasi class report_sxw dari modul report_sxw, dengan informasi laporan kita, termasuk parser yang digunakan.

Dalam contoh yang lebih umum, kita bisa tambahkan method dalam class parser, yang kemudian ditambahkan cara akses pada localcontext sehingga bisa dipanggil dari dalam report.

Berikut adalah isi file __openerp__.py: {

'name' : 'Partner 14' , 'version' : '1.0' , 'author' : 'noprianto' , 'description' : 'partner, laporan (dengan parser)' , 'category' : 'Buku' , 'website' : 'https://github.com/id-python/buku-openerp' , 'depends' :[ 'base' ], 'data' :[ 'buku_partner_nop_14_report.xml' ],

Berikut adalah isi file __init__.py: from . import buku_partner_nop_14_report

7.6 Wizard

Dalam contoh modul buku_nop_report_1, kita bekerja pada model res.partner dan akan menambahkan satu report.

Report akan diakses lewat menuitem tersendiri. Hasil dari report adalah daftar partner dengan nama mengandung karakter tertentu. Untuk kebutuhan ini, kita tidak perlu memilih satu atau lebih record, karena justru yang ingin kita lakukan adalah mencari.

Kriteria pencarian dapat diinput pada wizard. Ini merupakan contoh sederhana, namun dapat dikembangkan lebih lanjut.

Berikut adalah definisi report selengkapnya (buku_nop_report_1_report.xml): <?xml version="1.0"?>

<openerp> <data>

<report

id="report_buku_nop_report_1" name="buku.nop.report.1" id="report_buku_nop_report_1" name="buku.nop.report.1"

</data> </openerp>

Laporan sendiri akan dihasilkan dari file report.rml: <?xml version="1.0"?>

<document filename="report.pdf"> <template pageSize="(612, 792)" title="Report">

<pageTemplate id="page">

<frame id="page" x1="30.0" y1="30.0" width="552" height="732"/> <pageGraphics>

<setFont name="Courier" size="8"/> <drawString x="30" y="20">Halaman <pageNumber/></drawString>

</pageGraphics> </pageTemplate> </template>

<stylesheet> <blockTableStyle id="Table"> <blockAlignment value="LEFT"/> <blockValign value="TOP"/> <lineStyle kind="GRID" colorName="#000000" start="0,0" stop="-1,-1"/>

</blockTableStyle> <paraStyle name="Standard" fontName="Courier"/>

</stylesheet> <story>

<para style="Standard">Buku Report 1<br/><br/></para> <para style="Standard">Nama mengandung teks: [[ data.get('form',

{}).get('name') ]]<br/><br/></para> <blockTable colWidths="100,200,252" repeatRows="1" style="Table"> <tr> <td> <para style="Standard">ID</para> </td> <td> <para style="Standard">Nama</para> </td> <td> <para style="Standard">Website</para> </td> </tr> <tr> <td>

<para style="Standard">[[ repeatIn(objects, 'o') ]]</para> <para style="Standard">[[ o.id ]]</para>

</td> <td> <para style="Standard">[[ o.name ]]</para> </td> <td> <para style="Standard">[[ o.website ]]</para> </td> </tr> </blockTable> </story>

</document>

Kriteria pencarian yang diinput oleh user lewat wizard dapat kita akses dengan cara berikut. Variabel data merupakan sebuah dictionary, mengandung key 'form', yang juga merupakan sebuah dictionary, mengandung key 'name' berisi apa yang diinput oleh user.

data.get('form', {}).get('name') ]]<br/><br/></para>

<para style="Standard">Nama mengandung teks:

Berikut adalah parser kita, yang disimpan dalam file buku_nop_report_1_report.py: from openerp.report import report_sxw

class buku_nop_report_1(report_sxw.rml_parse): def __init__(self, cr, uid, name, context): super(buku_nop_report_1, self).__init__(cr, uid, name, context=context)

report_sxw.report_sxw( 'report.buku.nop.report.1' , 'res.partner' ,

'addons/buku_nop_report_1/report.rml' , parser=buku_nop_report_1)

Karena kita bekerja dengan wizard, kita perlu membuatnya terlebih dahulu, dalam bentuk sebuah class, yang diturunkan dari orm.TransientModel. Berikut adalah isi file buku_nop_report_1_wizard.py:

from openerp.osv import orm, fields

class buku_nop_report_1(orm.TransientModel): _name = 'buku.nop.report.1' _description = 'buku nop report 1 wizard'

_columns = {

'name' : fields.char( 'Nama mengandung teks' , size= 64 ), }

def _get_data(self, cr, uid, ids, context= None ): ret = {}

res = self.read(cr, uid, ids, context=context) if res:

ret = res[ 0 ]

return ret

def download_pdf(self, cr, uid, ids, context= None ): data = self._get_data(cr, uid, ids, context=context)

domain = [( 'name' , 'ilike' , data.get( 'name' ))] search_result = self.pool.get( 'res.partner' ).search(cr, uid, domain) datas = {

'ids' : search_result, 'model' : 'res.partner' , 'form' : data,

} ret = {

'type' : 'ir.actions.report.xml' , 'report_name' : self._name, 'datas' : datas,

} return ret

Catatan: • Wizard kita akan berisikan satu field, yaitu name (bertipe char). Kita akan gunakan ini sebagai

kriteria pencarian sebelum menghasilkan report. • Fungsi _get_data digunakan untuk mendapatkan input dari user, menggunakan method read. Ingatlah bahwa kita berada dalam model buku.nop.report.1 ketika kita melakukan read. Dengan ids

yang kita lewatkan, kita akan mendapatkan anggota pertama dari list yang dikembalikan. Apabila diperlukan, bacalah juga contoh fungsi read dalam Bab 4.

• Yang sangat menarik adalah fungsi download_pdf. Nama ini bukan nama baku, dan diasosiasikan dengan sebuah tombol di view. • Di dalam fungsi download_pdf, setelah mendapatkan informasi apa yang diinput oleh user, kita lakukan pencarian:

data = self._get_data(cr, uid, ids, context=context)

domain = [( 'name' , 'ilike' , data.get( 'name' ))] search_result = self.pool.get( 'res.partner' ).search(cr, uid, domain)

• Hasil pencarian, kita tempatkan pada sebuah dictionary dengan key adalah ids:

datas = {

'ids' : search_result,

• Kita bekerja dengan model res.partner:

'model' : 'res.partner' ,

• Dan, apa yang diinput oleh user bisa diakses dengan key form, sebagaimana kita lihat dalam file RML:

'form' : data,

• Fungsi akan mengembalikan satu dictionary dengan key memiliki arti khusus. Sebagai contoh, key datas dapat diakses dari dalam report sebagai variabel data.

'datas' : datas,

data.get('form', {}).get('name') ]]<br/><br/></para>

<para style="Standard">Nama mengandung teks:

• Kita memberitahu OpenERP bahwa kita ingin menghasilkan report. Perhatikanlah key type berikut.

'type' : 'ir.actions.report.xml' , 'report_name' : self._name,

• Kita bisa menggunakan kursor koneksi database (variabel cr) untuk melakukan SQL Query, langsung pada database.

Bagaimana wizard ditampilkan, kita definisikan dalam view buku_nop_report_1_view.xml: <?xml version="1.0"?>

<openerp> <data>

<record model="ir.ui.view" id="buku_nop_report_1_form"> <field name="name">buku.nop.report.1</field> <field name="model">buku.nop.report.1</field> <field name="arch" type="xml">

<form string="Buku Report 1"> <group>

<field name="name"/> <button <field name="name"/> <button

<button string="Cancel" special="cancel"/>

</group> </form>

</field> </record>

<record model="ir.actions.act_window" id="action_buku_nop_report_1">

<field name="name">Report 1</field> <field name="res_model">buku.nop.report.1</field> <field name="view_id" ref="buku_nop_report_1_form"/> <field name="target">new</field>

</record> <menuitem name="Buku" id="menu_buku"/>

<menuitem name="Report" id="menu_buku_report" parent="menu_buku"/> <menuitem name="Buku Report 1" id="menu_buku_report_nop_report_1"

parent="menu_buku_report" action="action_buku_nop_report_1"/> </data>

</openerp>

Perhatikanlah bahwa kita mendefinisikan tombol PDF dengan cara berikut:

<button name="download_pdf" type="object" string="PDF" class="oe_highlight"/>

dimana fungsi akan menjalankan fungsi download_pdf (name) ketika diklik (type object). Tombol cancel merupakan tombol khusus:

<button string="Cancel" special="cancel"/>

Berikut adalah isi file __openerp__.py: {

'name' : 'Report 1' , 'version' : '1.0' , 'author' : 'noprianto' , 'description' : 'Laporan dengan wizard' , 'category' : 'Buku' , 'website' : 'https://github.com/id-python/buku-openerp' , 'depends' :[ 'base' ], 'data' :[

'buku_nop_report_1_view.xml' , 'buku_nop_report_1_report.xml' ,

], }

Berikut adalah isi file __init__.py: from . import buku_nop_report_1_report from . import buku_nop_report_1_wizard