Mencetak ‘Object’ Dengan JasperReports: Menampilkan atau Mencetak


Pada artikel merancang laporan,  saya sudah membuat sebuah laporan dan memastikan bahwa laporan tersebut dapat ditampilkan dengan baik.   Langkah berikutnya adalah memakai laporan tersebut di aplikasi.   Saya akan mencoba melakukannya dari sebuah aplikasi yang memakai framework Griffon dan plugin simple-jpa.   Untuk memakai JasperReports di Griffon, sebenarnya sudah ada plugin jasperreports.  Tapi pada tulisan ini, saya akan mencoba cara manual.

Langkah pertama yang harus saya lakukan pada program saya adalah menambahkan dependency JasperReports.  Untuk itu saya perlu membuka file BuildConfig.groovy dan menambahkan baris seperti berikut ini:

griffon.project.dependency.resolution = {
    inherits("global") {
    }
    log "warn" // log level of Ivy resolver, either 'error', 'warn', 'info', 'debug' or 'verbose'
    repositories {
        griffonHome()
        mavenLocal()
        mavenCentral()
        mavenRepo "http://jasperreports.sourceforge.net/maven2"
    }
    dependencies {
        compile 'org.hibernate:hibernate-entitymanager:4.2.0.Final'
        compile('net.sf.jasperreports:jasperreports:5.1.0') {
           exclude 'commons-collections'
        }
    }
}

Saya perlu menambahkan repository dari JasperReports karena mereka memakai versi iText yang telah di-patch dan belum tersedia di repository central Maven.  Setelah perubahan di atas, bila saya menjalankan aplikasi dan terhubung ke internet, maka Griffon akan secara otomatis men-download JAR yang dibutuhkan.

Langkah berikutnya adalah meletakkan laporan JasperReports yang telah di-compile (file berekstensi *.jasper bukan *.jrxml) ke lokasi proyek, misalnya griffon-app/resources/report.  File *.jasper dapat diperoleh dengan men-preview laporan atau men-click icon compile di iReport.

Langkah terakhir, saya perlu menampilkan laporan tersebut di aplikasi saya.   JasperReports memiliki sebuah komponen visual, JRViewer, yang dapat dimasukkan ke dalam view Griffon untuk menampilkan laporan. JRViewer adalah turunan dari JPanel.  Dengan demikian, JRViewer dapat dipakai disemua tempat dimana sebuah JPanel dapat dipakai.

Agar lebih leluasa, saya akan membuat sebuah MVCGroup baru yang khusus dipakai untuk menampilkan preview laporan, dengan memberikan perintah berikut:

griffon create-mvc PreviewLaporan

Setelah Griffon selesai membuat MVCGroup yang dibutuhkan, saya membuka dan mengubah isi file PreviewLaporanView.groovy menjadi seperti berikut ini:

application(title: 'Report Viewer') {

    borderLayout()
    panel(id: 'mainPanel', constraints: CENTER) {
        borderLayout()
    }

}

Saya juga menambahkan sebuah mvcGroupInit() pada PreviewLaporanController.groovy, yang isinya seperti berikut ini:

void mvcGroupInit(Map args) {
  JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(args.'data')
  JasperPrint jp = JasperFillManager.fillReport(getResourceAsStream("report/faktur.jasper"), [:], ds)
  JPanel panel = view.mainPanel
  panel.clear()
  panel.add(new JRViewer(jp), BorderLayout.CENTER)
}

MVCGroup di atas membutuhkan sebuah parameter dengan nama data.   Parameter ini adalah sebuah Collection(misalnya List), yang berisi object yang akan ditampilkan.

MVCGroup ini akan dipanggil melalui MVCGroup lain.    Misalnya, saya memiliki sebuah MVCGroup lain yang menampilkan tabel berisi daftar faktur yang ada.  Bagaimana caranya menambahkan sebuah tombol yang bila di-klik akan menampilkan MVCGroup lain? Plugin simple-jpa memiliki sebuah node bernama mvcPopupButton() untuk keperluan tersebut.   Sebagai contoh, saya menambahkan mvcPopupButton() pada view yang menampilkan faktur:

mvcPopupButton('Cetak Faktur', mvcGroup: 'previewLaporan', args: {[data: ([] <<
    model.fakturSelection.selected[0])]},
    dialogProperties: [title: 'Preview Faktur', size: new Dimension(800,600)])

Pada kode program di atas, bila tombol ‘Cetak Faktur‘ di-klik, maka faktur yang sedang dipilih di JTable (nilai kembalian dari model.fakturSelection.selected[0]) akan dijadikan sebagai parameter dalam memanggil MVCGroup previewLaporan.   Contoh tampilan MVCGroup previewLaporan akan terlihat seperti pada gambar berikut ini:

Contoh Tampilan Komponen JRViewer

Contoh Tampilan Komponen JRViewer

Terlihat bahwa komponen JRViewer telah dilengkapi tombol Save untuk menyimpan dan men-ekspor laporan ke berbagai format lain seperti PDF, HTML, Excel, dan sebagainya.   Selain itu, juga terdapat tombol Print yang dapat dipakai untuk mencetak laporan.

Untuk formulir seperti faktur, biasanya pengguna tidak membutuhkan preview dan ingin langsung melakukan percetakan ke printer.  Bagaimana caranya supaya bila tombol Cetak Faktur di-klik, maka proses percetakan laporan langsung dimulai tanpa memunculkan preview?  Apakah hal tersebut mungkin?

Yup, bisa!   Untuk mencetak sebuah report secara langsung, saya dapat memakai class JasperPrintManager.   Sebagai contoh, berikut ini adalah sebuah tombol Cetak Faktur yang bila di-klik akan langsung mencetak object Faktur yang sedang terpilih di JTable ke printer:

button('Cetak Faktur', actionPerformed: {
  JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(
      [] << model.fakturSelection.selected[0])
  JasperPrint jp = JasperFillManager.fillReport(
      getResourceAsStream('report/faktur.jasper'), [:], ds)
  JasperPrintManager.printReport(jp, false)
})

Proses percetakan ke printer default akan langsung dimulai saat tombol di-klik.   Bila ingin menampilkan dialog dimana pengguna bisa memilih printer dan melakukan konfigurasi (misalnya mengganti ukuran halaman atau mengubah kualitas percetakan), maka saya perlu mengubah argumen false menjadi true pada saat memanggil printReport() seperti:

JasperPrintManager.printReport(jp, true)

Perihal Solid Snake
I'm nothing...

5 Responses to Mencetak ‘Object’ Dengan JasperReports: Menampilkan atau Mencetak

  1. Tolhah Hamzah mengatakan:

    Permisi mas, mau tanya.
    Saya telah mengikuti tutorial dari artikel sebelumnya (merancang laporan) dan artikel ini, tapi menggunakan nama domain class berbeda, milik saya sendiri.
    Saya ganti Faktur menjadi Level, dan ItemFaktur menjadi Pengguna.
    Jadi, Level OneToMany Pengguna. Konsepnya sama kaya di contoh artikel..

    Saya mendesain report dan menamainya “level”. Setelah desain report berhasil di-compile menjadi level.jasper, saya meletakkan file hasil compile tadi ke dalam folder resources/report/.
    Kemudian saya meletakkan mvcPopupButton nya di LevelView.groovy.
    Aplikasi berhasil dijalankan dengan “griffon run-app”.
    Tapi kenapa yaa setelah saya coba klik mvcPopupButton tersebut, saya mendapatkan error seperti ini:

    [AWT-EventQueue-0] ERROR griffon.util.GriffonExceptionHandler – Uncaught Exception
    groovy.lang.MissingPropertyException: No such property: levelSelection for class: project.LevelModel
    at project.LevelView$_run_closure1_closure2_closure5_closure11_closure16.doCall(LevelView.groovy:80)
    at project.LevelView$_run_closure1_closure2_closure5_closure11_closure16.doCall(LevelView.groovy)
    at simplejpa.swing.MVCPopupButtonFactory$_newInstance_closure1.doCall(MVCPopupButtonFactory.groovy:45)
    at com.sun.proxy.$Proxy26.actionPerformed(Unknown Source)

    Method mvcGroupInit pada PreviewLaporanController.groovy saya seperti ini:
    ……………..
    void mvcGroupInit(Map args) {
    JRBeanCollectionDataSource ds = new JRBeanCollectionDataSource(args.’data’)
    JasperPrint jp = JasperFillManager.fillReport(getResourceAsStream(“report/level.jasper”), [:], ds)
    JPanel panel = view.mainPanel
    panel.clear()
    panel.add(new JRViewer(jp), BorderLayout.CENTER)
    }

    Button mvcPopupButton pada LevelView.groovy saya seperti ini:
    ……………..
    mvcPopupButton(‘Cetak Faktur’, mvcGroup: ‘previewLaporan’, args: {[data: ([] <<
    model.levelSelection.selected[0])]},
    dialogProperties: [title: 'Preview Faktur', size: new Dimension(600,600)])

    saya sudah sesuaikan menjadi model.levelSelection.selected[0]

    Domain class Level.groovy saya seperti ini:
    ……………
    @DomainClass @Entity @Canonical
    class Level {

    @NotEmpty @Size(min=3, max=30)
    String nama
    
    @NotEmpty @ManyToMany(fetch=FetchType.EAGER)
    List menuList
    
    @OneToMany(fetch=FetchType.EAGER)
    List penggunaList  
    

    }

    Domain class Pengguna.groovy saya seperti ini:
    ……………..
    @DomainClass @Entity @Canonical
    class Pengguna {

    @NotEmpty @Size(min=4, max=20)
    String username
    
    @NotEmpty @Size(min=5, max=100)
    String password
    
    @NotEmpty @Size(min=4, max=20)
    String kode
    
    @ManyToOne
    Level level
    

    }

    Saya menggunakan Griffon 1.5 dan simple-jpa 0.6

    Mohon bantuannya mas… semoga ada solusinya..
    Terima kasih banyak…

  2. Solid Snake mengatakan:

    Artikel ini tidak berlaku lagi untuk simple-jpa 0.6 dan perlu diperbaharui.

    Salah satu perubahan pada simple-jpa 0.6 menyebabkan kode program lama seperti ini:

    mvcPopupButton(‘Cetak Faktur’, mvcGroup: ‘previewLaporan’, args: {[data: ([] <<
    model.levelSelection.selected[0])]}, ...
    

    dapat diganti menjadi seperti:

    mvcPopupButton(‘Cetak Faktur’, mvcGroup: ‘previewLaporan’, args: {[data:
    [view.table.selectionModel.selected[0]]]}, ...
    

    Saat ini saya tidak banyak dokumentasi terbaru tentang simple-jpa, bila banyak yang tertarik pada proyek ini, mungkin saya akan memperbaharui tulisan yang ada.

    Untuk sementara, bila suka belajar dari contoh, bisa coba lihat proyek sample https://github.com/JockiHendry/simple-jpa-demo-laundry yang sudah memakai simple-jpa 0.6. Proyek tersebut memiliki contoh mencetak laporan, dimana terdapat tombol Cetak pada WorkOrderView yang akan menampilkan preview untuk invoice work order yang terpilih.

    Juga ada proyek https://github.com/JockiHendry/simple-jpa-demo-inventory yang memakai simple-jpa 0.7 (belum dirilis) dan membutuhkan Hibernate yang bug named entity graph-nya telah diperbaiki (belum dirilis). Proyek sample ini lebih terstruktur dan menerapkan metode sejenis domain driven design (tetapi repository-nya terkontaminasi dengan application layer services).

    P.S: Rencananya API untuk simple-jpa akan stabil setelah mencapai versi 1.0. Versi yang dirilis setelah 1.0 akan mempertahankan backward compatibility sebisa mungkin.

  3. Tolhah Hamzah mengatakan:

    Saya sudah mencoba mengganti kode untuk mvcPopupButton nya seperti di atas, namun masih error mas..
    Kemudian saya coba menyamakan konsep kode programnya sama seperti sample proyek laundry, sehingga:

    PreviewLaporanController.groovy saya ubah seperti ini:

    package gfsimlkm6

    import net.sf.jasperreports.engine.JasperFillManager
    import net.sf.jasperreports.engine.JasperPrint
    import net.sf.jasperreports.engine.data.JRBeanCollectionDataSource
    import net.sf.jasperreports.swing.JRViewer

    import javax.imageio.ImageIO
    import java.awt.BorderLayout

    class PreviewLaporanController {

    def model
    def view
    
    void mvcGroupInit(Map args) {
      List source = []
        source &lt;&lt; args.&#039;source&#039;
        String fileReport = args.&#039;fileReport&#039;
    
        Map parameters = [&#039;logo&#039;: ImageIO.read(getResourceAsStream(&quot;report/logo.jpg&quot;))]
    
        JRBeanCollectionDataSource dataSource = new JRBeanCollectionDataSource(source)
        JasperPrint jasperPrint = JasperFillManager.fillReport(
            getResourceAsStream(&quot;report/${fileReport}.jasper&quot;), parameters, dataSource)
    
        view.mainPanel.clear()
        view.mainPanel.add(new JRViewer(jasperPrint), BorderLayout.CENTER)
    }
    

    }

    Dan, pada kode LevelView.groovy, saya mengubah kode mvcPopupButton saya menjadi:

    mvcPopupButton('Cetak Laporan', mvcGroup: 'previewLaporan', args: {[
    source: view.table.selectionModel.selected[0],
    fileReport: 'level'
    ]}, dialogProperties: [
    title: 'Preview Laporan', size: new Dimension(600,600)
    ], visible: bind { table.isRowSelected } )

    Namun, saya masih menjumpai error seperti di bawah ini:

    2014-04-29 23:05:21,928 [AWT-EventQueue-0] ERROR griffon.util.GriffonExceptionHa
    ndler – Uncaught Exception
    java.lang.NoClassDefFoundError: net/sf/jasperreports/compilers/GroovyEvaluator
    at net.sf.jasperreports.engine.util.JRClassLoader.loadClass(JRClassLoade
    r.java:338)
    at net.sf.jasperreports.engine.util.JRClassLoader.loadClassFromBytes(JRC
    lassLoader.java:261)
    at net.sf.jasperreports.engine.design.JRAbstractJavaCompiler.loadEvaluat
    or(JRAbstractJavaCompiler.java:102)
    at net.sf.jasperreports.engine.design.JRAbstractCompiler.loadEvaluator(J
    RAbstractCompiler.java:333)
    at net.sf.jasperreports.engine.JasperCompileManager.getEvaluator(JasperC
    ompileManager.java:265)
    at net.sf.jasperreports.engine.fill.JRFillDataset.createCalculator(JRFil
    lDataset.java:457)
    at net.sf.jasperreports.engine.fill.JRBaseFiller.(JRBaseFiller.jav
    a:379)
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.(JRVerticalFi
    ller.java:88)
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.(JRVerticalFi
    ller.java:103)
    at net.sf.jasperreports.engine.fill.JRVerticalFiller.(JRVerticalFi
    ller.java:61)
    at net.sf.jasperreports.engine.fill.JRFiller.createFiller(JRFiller.java:
    153)
    at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:82)
    at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.
    java:653)
    at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.
    java:634)
    at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillMa
    nager.java:956)
    at net.sf.jasperreports.engine.JasperFillManager$fillReport.call(Unknown
    Source)
    at gfsimlkm6.PreviewLaporanController.mvcGroupInit(PreviewLaporanControl
    ler.groovy:29)
    at org.codehaus.griffon.runtime.core.DefaultMVCGroupManager.initializeMe
    mbers(DefaultMVCGroupManager.java:239)
    at org.codehaus.griffon.runtime.core.DefaultMVCGroupManager.buildMVCGrou
    p(DefaultMVCGroupManager.java:152)
    at org.codehaus.griffon.runtime.core.AbstractMVCGroupManager.withMVCGrou
    p(AbstractMVCGroupManager.java:263)
    at org.codehaus.griffon.runtime.core.AbstractMVCGroupManager.withMVCGrou
    p(AbstractMVCGroupManager.java:228)
    at org.codehaus.griffon.runtime.core.AbstractGriffonApplication.withMVCG
    roup(AbstractGriffonApplication.java:562)
    at griffon.core.MVCHandler$withMVCGroup$1.call(Unknown Source)
    at simplejpa.swing.DialogUtils.showMVCGroup(DialogUtils.groovy:16)
    at simplejpa.swing.DialogUtils$showMVCGroup.call(Unknown Source)
    at simplejpa.swing.MVCPopupButtonFactory$_newInstance_closure1.doCall(MV
    CPopupButtonFactory.groovy:49)
    at com.sun.proxy.$Proxy26.actionPerformed(Unknown Source)
    Caused by: java.lang.ClassNotFoundException: net.sf.jasperreports.compilers.Groo
    vyEvaluator
    … 27 more
    2014-04-29 23:05:21,938 [AWT-EventQueue-0] ERROR griffon.util.GriffonExceptionHa
    ndler – An error occurred while handling uncaught exception java.lang.NoClassDef
    FoundError: net/sf/jasperreports/compilers/GroovyEvaluator
    org.codehaus.groovy.runtime.metaclass.MissingMethodExceptionNoStack: No signatur
    e of method: Events$_run_closure1.doCall() is applicable for argument types: (ja
    va.lang.NoClassDefFoundError) values: [java.lang.NoClassDefFoundError: net/sf/ja
    sperreports/compilers/GroovyEvaluator]
    Possible solutions: doCall(java.lang.Exception), call(), call([Ljava.lang.Object😉, call(java.lang.Object), call(java.lang.Exception), findAll()

    Saya baru di griffon dan tidak tahu harus berbuat apa..😦
    Semoga ada solusinya.. Terima kasih…

    • Solid Snake mengatakan:

      Kesalahan tersebut disebabkan karena kamu men-compile laporan JasperReport dengan iReport yang memiliki versi berbeda dengan versi Jasper yang kamu pakai di proyek Griffon. Periksa versi JasperReport yang dipakai oleh iReport. Sebagai contoh, di folder instalasi iReport 5.5.1 terdapat folder iReport\modules\ext. Disini saya menemukan file jasperreports-5.5.1.jar. Pada proyek Griffon, di file BuildConfig.groovy, saya harus memakai JasperReport sesuai dengan versi tersebut.

      Atau sebaliknya, download versi iReport yang sesuai dengan versi JasperReport yang dipakai oleh proyek Griffon.

      P.S: ada bug pada JasperReport-5.5.1 yang menyebabkan ia tidak bisa jalan tanpa servlet-api.jar. Ini akan diperbaiki pada rilis JasperReport berikutnya (semoga! karena JasperReport sudah dibeli oleh perusahaan lain sejak kemarin!). Bila memakai versi ini, jangan lupa copy file jar tersebut ke folder lib di proyek Griffon.

  4. Tolhah Hamzah mengatakan:

    saya telah menggunakan iReport 5.1.0 dengan jasperreports 5.1.0 seperti yang saya pakai di griffon agar versinya sama.

    and thank you so much, master. it works perfectly!😀

Apa komentar Anda?

Please log in using one of these methods to post your comment:

Logo WordPress.com

You are commenting using your WordPress.com account. Logout / Ubah )

Gambar Twitter

You are commenting using your Twitter account. Logout / Ubah )

Foto Facebook

You are commenting using your Facebook account. Logout / Ubah )

Foto Google+

You are commenting using your Google+ account. Logout / Ubah )

Connecting to %s

%d blogger menyukai ini: