[Odoo 10] Case Study # Travel Umroh -Part 3-

Setelah kita berhasil membuat menu Paket Perjalanan. Maka selanjutnya kita akan masuk ke proses Sales Orders. Pada proses ini kita juga harus mengcustom beberapa field yang dibutuhkan untuk travel umroh, diantaranya adalah :

1. Paket Perjalanan (many2one)
2. Data Persyaratan Dokumen (one2many)
3. Data Passport Jamaah (one2many)

Hasil source codenya seperti berikut :



class sale_order(models.Model):
    _inherit = "sale.order"

    paket_perjalanan_id = fields.Many2one('paket.perjalanan', string='Paket Perjalanan', domain=[('state', '=', 'confirm')])
    dokumen_line = fields.One2many('sale.dokumen.line', 'order_id', string='Document Lines')
    passport_line = fields.One2many('sale.passport.line', 'order_id', string='Passport Lines')

    @api.onchange('paket_perjalanan_id')
    def set_order_line(self):
        res = {}
        if self.paket_perjalanan_id:
            pp = self.paket_perjalanan_id

            ### Otomatis - Nilai di set otomatis dari method onchange product_id_change() ###

            order = self.env['sale.order'].new({
                'partner_id': self.partner_id.id,
                'pricelist_id': self.pricelist_id.id,
                'date_order': self.date_order
            })
            line = self.env['sale.order.line'].new({'product_id': pp.product_id.id, 'order_id': order.id})
            line.product_id_change()
            vals = line._convert_to_write({name: line[name] for name in line._cache})
            res['value'] = {'order_line': [vals]}

            ### Manual - Nilai di set secara manual ###

            # res['value'] = {
            #     'order_line': [{
            #         'product_id': pp.product_id.id,
            #         'name':  pp.product_id.partner_ref,
            #         'product_uom_qty': 1,
            #         'product_uom': pp.product_id.uom_id.id,
            #         'price_unit': pp.product_id.lst_price
            #     }]
            # }

        return res


class sale_dokumen_line(models.Model):
    _name = "sale.dokumen.line"

    order_id = fields.Many2one('sale.order', string='Sales Orders', ondelete='cascade')
    name = fields.Char(string='Name', required=True)
    foto = fields.Binary(string='Photo', required=True)


class sale_passport_line(models.Model):
    _name = "sale.passport.line"

    order_id = fields.Many2one('sale.order', string='Sales Orders', ondelete='cascade')
    partner_id = fields.Many2one('res.partner', string='Jamaah', required=True)
    nomor = fields.Char(string='Passport Number', required=True)
    name = fields.Char(string='Name in Passport', required=True)
    masa_berlaku = fields.Date(string='Date of Expiry', required=True)
    tipe_kamar = fields.Selection([('d', 'Double'), ('t', 'Triple'), ('q', 'Quad')], string='Room Type', required=True)
    foto = fields.Binary(string='Photo', required=True)


Pada model sale_order kita menambahkan 3 field sesuai dengan requirement diatas. Diantaranya :

1. Paket Perjalanan

Berfungsi untuk mengidentifikasi pilihan paket perjalanan saat transaksi penjualan dilakukan. Paket Perjalanan yang dapat dipilih hanya yang telah berstatus confirm. Disana kita juga tambahkan fitur onchange() pada field ini, yaitu tabel Order Lines akan otomatis terisi ketika Paket Perjalanan di pilih.

Ada perbedaan pada api lama dan api baru dalam memanggil sebuah method di object lain, terutama yang memiliki parameter. Jika api lama maka cukup memasukan parameter beserta methodnya, sedangkan api baru kita harus menggunakan method new() sebelum memanggil method yang diinginkan. Contoh penerapan coding diatas adalah product_id_change() pada model sale_order_line.

2. Data Persyaratan Dokumen

Model ini berfungsi untuk mencatat semua dokumen yang telah di berikan jamaah kepada pihak travel sebagai persyaratan ibadah umroh. Model ini terlihat seperti tabel dan kita buatkan didalam sebuah tab (notebook) di sebelah tab Other Information.

3. Data Passport Jamaah

Sama seperti data persyaratan dokumen, data passport jamaah berfungsi untuk mencatat semua informasi passport jamaah beserta pilihan type kamarnya dalam ibadah umroh. Biasanya jamaah yang mendaftar umroh akan mengajak keluarga atau hanya menjadi perantaranya saja. Olehkarna itu, kita akan memastikan siapa saja yang akan mejadi jamaah umroh sebenarnya dengan tabel ini. Dan nantinya data ini akan kita gunakan untuk object paket perjalanan.

Source code tampilannya :





        <!-- Sales Orders Form View -->

        <record model="ir.ui.view" id="view_order_form_umroh">
            <field name="name">sale.order.form.umroh</field>
            <field name="model">sale.order</field>
            <field name="inherit_id" ref="sale.view_order_form"/>
            <field name="arch" type="xml">
                <field name="partner_shipping_id" position="after">
                    <field name="paket_perjalanan_id"/>
                </field>
                <notebook>
                    <page string="Document Lines">
                        <field name="dokumen_line">
                            <tree>
                                <field name="name"/>
                                <field name="foto"/>
                            </tree>
                            <form>
                                <group>
                                    <field name="name"/>
                                    <field name="foto" widget="image"/>
                                </group>
                            </form>
                        </field>
                    </page>
                    <page string="Passport Lines">
                        <field name="passport_line">
                            <tree>
                                <field name="partner_id"/>
                                <field name="nomor"/>
                                <field name="name"/>
                                <field name="masa_berlaku"/>
                                <field name="tipe_kamar" />
                                <field name="foto" widget="image"/>
                            </tree>
                            <form>
                                <group col="4">
                                    <field name="partner_id"/>
                                    <field name="nomor"/>
                                    <field name="name" colspan="4"/>
                                    <field name="masa_berlaku"/>
                                    <field name="tipe_kamar" />
                                    <field name="foto" widget="image"/>
                                </group>
                            </form>
                        </field>
                    </page>
                </notebook>
            </field>
        </record>



Hasilnya sebegai berikut :

Setelah kita membuat sales order, makan selanjutnya kita akan memproses 2 dokumen yang dihasilkan dari sales order yaitu Delivery Order dan Customer Invoice. Kita akan gunakan fitur default ini tanpa custom apapun kecuali hanya reportnya saja. Untuk pertama kita akan proses Delivery Order sampai statusnya Transferred. Pastikan sebelumnya untuk membuat BoM dengan type “Ship this product as a set of components (kit)” agar product yang kita jual di sales order adalah product “Umroh Ramadhan” akan tetapi product yang kita kirim adalah koper, kain ihram, tas, dll.

Setelah itu kita cetak laporan delivery order yang kita custom seperti berikut :

1. Tambahkan button Cetak pada form Delivery Order



        <!-- Report Surat Jalan -->

        <report
            id="cetak_surat_jalan" string="Surat Jalan"
            model="stock.picking" name="surat.jalan"
            rml="aa_travel_umroh/report/surat_jalan.rml"
            auto="False" menu="False" />



        <!-- Stock Picking Form View -->

        <record model="ir.ui.view" id="view_picking_form_cetak">
            <field name="name">stock.picking.form.cetak</field>
            <field name="model">stock.picking</field>
            <field name="inherit_id" ref="stock.view_picking_form"/>
            <field name="arch" type="xml">
                <button name="do_print_picking" position="replace"/>
                <button name="%(stock.action_report_delivery)d" position="replace">
                    <button name="%(cetak_surat_jalan)d" string="Cetak" type="action" state="done" class="btn-primary" />
                </button>
            </field>
        </record>


2. Buat folder report
3. Didalam folder report, buat file __init__.py yang isinya :


import laporan

4. Didalam folder report, buat file laporan.py yang isinya :


import time
from odoo.report import report_sxw


class custom_laporan(report_sxw.rml_parse):
    def __init__(self, cr, uid, name, context):
        super(custom_laporan, self).__init__(cr, uid, name, context=context)
        self.localcontext.update({
            'time': time
        })

report_sxw.report_sxw('report.surat.jalan', 'stock.picking', 'addons/aa_travel_umroh/report/surat_jalan.rml', parser=custom_laporan, header=False)



5. Didalam folder report, buat file surat_jalan.rml yang isinya :


<?xml version="1.0"?>
<document filename="Surat Jalan.pdf">

	<!-- Definisi Layout (Ukuran Kertas) -->

	<template pageSize="(595, 842)" title="Surat Jalan" author="Muahmmad Aziz - 087881071515">
		<pageTemplate id="first">
			<!-- Menentukan luas dari canvas yang dapat kita 'tulis' dengan parameter widht & height beserta titik awal penulisan identasi/margin. x1 dan y1 dimulai dari kiri bawah -->
			<frame id="first" x1="30.0" y1="30.0" width="538" height="800"/>
		</pageTemplate>
	</template>



	<!-- Definisi Style -->

	<stylesheet>

		<!-- Style Tabel -->

		<blockTableStyle id="Table_String_Default">
			<blockAlignment value="LEFT"/>
			<blockValign value="TOP"/>
		</blockTableStyle>

		<blockTableStyle id="Tbl_Left_Grid">
            <blockAlignment value="LEFT"/>
            <blockValign value="TOP"/>
            <lineStyle kind="GRID" colorName="#000000"/>
        </blockTableStyle>

        <blockTableStyle id="Tbl_Signing">
            <blockAlignment value="CENTER"/>
            <lineStyle kind="LINEBEFORE" colorName="black" start="0,0" stop="0,0" />
            <lineStyle kind="LINEBELOW" colorName="black" start="0,0" stop="0,0" />
            <lineStyle kind="LINEABOVE" colorName="black" start="0,0" stop="0,0" />
            <lineStyle kind="LINEAFTER" colorName="black" start="0,0" stop="0,0" />
        </blockTableStyle>

        <initialize>
            <paraStyle name="all" alignment="justify"/>
        </initialize>



        <!-- Style Huruf -->

        <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_table_header" fontName="Helvetica-Bold" fontSize="8.0" leading="8" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_default" fontName="Helvetica" fontSize="7.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_default_right" fontName="Helvetica" fontSize="7.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>

    </stylesheet>



    <story>

    	<!-- Definisi Object Alias-->

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



    	<!-- Judul Report -->

    	<blockTable colWidths="500.0" style="Table_String_Default">
        <tr>
            <td>
                <para style="terp_header">DELIVERY ORDER</para>
            </td>
        </tr>
    	</blockTable>



    	<!-- Gap/Space Baris Kosong -->

    	<para style="terp_default"><font color="white"></font></para>



    	<!-- Header Report -->

    	<blockTable colWidths="300.0, 150.0" style="Table_String_Default">
	        <tr>
	        	<td>
	        		<para style="terp_default">Customer : [[ o.partner_id.name ]]</para>
                    <para style="terp_default">Address : [[ o.partner_id.street ]] [[ o.partner_id.street2 ]]</para>
					<para style="terp_default">Phone : [[ o.partner_id.phone or '-' ]] / [[ o.partner_id.mobile or '-' ]]</para>
				</td>
				<td>
                    <para style="terp_default">No. : [[ o.name ]]</para>
                    <para style="terp_default">Date : [[ time.strftime('%d %B %Y', time.strptime(o.min_date,'%Y-%m-%d %H:%M:%S')) ]]</para>
                </td>
            </tr>
		</blockTable>



    	<!-- Gap/Space Baris Kosong -->

		<para style="terp_default"><font color="white"></font></para>



		<!-- Header Tabel -->

		<blockTable colWidths="200, 125, 125" repeatRows="1" style="Tbl_Left_Grid">
			<tr>
				<td><para style="terp_table_header">Product</para></td>
				<td><para style="terp_table_header">Qty</para></td>
				<td><para style="terp_table_header">UoM</para></td>
			</tr>
		</blockTable>



        <!-- Line Tabel -->

        <section>
            <para style="terp_default">[[ repeatIn(o.move_lines, 'l') ]]
            </para>
            <blockTable colWidths="200, 125, 125" repeatRows="0" style="Tbl_Left_Grid">
                <tr>
                    <td><para style="terp_default">[[ l.product_id.name ]]</para></td>
                    <td><para style="terp_default_right">[[ l.product_uom_qty ]]</para></td>
                    <td><para style="terp_default_right">[[ l.product_uom.name ]]</para></td>
                </tr>
            </blockTable>
        </section>



        <!-- Gap/Space Baris Kosong -->

        <para style="terp_default"><font color="white"></font></para>
        <para style="terp_default"><font color="white"></font></para>

        <blockTable colWidths="150" rowHeights="100" style="Tbl_Signing">
        <tr>
            <td>
                <para style="terp_table_header" spaceAfter="80">Mengetahui</para>
                <para style="terp_table_header">[[ o.create_uid.name ]]</para>
            </td>
        </tr>
        </blockTable>


    </story>

</document>


Hasilnya seperti ini :

Setelah proses delivery order kita selesaikan, tahapan selanjutnya adalah penagihan. Kita juga akan gunakan fitur default dari Odoo yaitu Customer Invoice. Buatlah customer invoice dari menu sales order dengan mengklik button “Create Invoice” lalu klik “Create and View Invoices”. Kemudian lakukan payment secara partial sampai lunas. Terakhir kita buat custom laporan penagihan :

1. Tambahkan button Cetak pada form Customer Invoices



        <!-- Report Tagihan Jamaah -->

        <report
            id="cetak_tagihan_jamaah" string="Customer Invoices"
            model="account.invoice" name="tagihan.jamaah"
            rml="aa_travel_umroh/report/tagihan_jamaah.rml"
            auto="False" menu="False" />



        <!-- Customer Invoices Form View -->

        <record model="ir.ui.view" id="view_invoice_form_cetak">
            <field name="name">account.invoice.form.cetak</field>
            <field name="model">account.invoice</field>
            <field name="inherit_id" ref="account.invoice_form"/>
            <field name="arch" type="xml">
                <button name="invoice_print" position="replace"/>
                <button name="invoice_print" position="replace">
                    <button name="%(cetak_tagihan_jamaah)d" string="Cetak" type="action" state="done" class="btn-primary" />
                </button>
            </field>
        </record>


2. Didalam folder report, update file laporan.py seperti ini :


import re
import time
from odoo.report import report_sxw


class custom_laporan(report_sxw.rml_parse):
    def __init__(self, cr, uid, name, context):
        super(custom_laporan, self).__init__(cr, uid, name, context=context)
        self.localcontext.update({
            'time': time,
            'koma': self.FormatWithCommas,
        })

        self.re_digits_nondigits = re.compile(r'\d+|\D+')

    def FormatWithCommas(self, format, value):
        parts = self.re_digits_nondigits.findall(format % (value,))
        for i in xrange(len(parts)):
            s = parts[i]
            if s.isdigit():
                parts[i] = self.commafy(s)
                break
        return ''.join(parts)

    def commafy(self, s):
        r = []
        for i, c in enumerate(reversed(s)):
            if i and (not (i % 3)):
                r.insert(0, ',')
            r.insert(0, c)
        return ''.join(r)


report_sxw.report_sxw('report.surat.jalan', 'stock.picking', 'addons/aa_travel_umroh/report/surat_jalan.rml', parser=custom_laporan, header=False)
report_sxw.report_sxw('report.tagihan.jamaah', 'account.invoice', 'addons/aa_travel_umroh/report/tagihan_jamaah.rml', parser=custom_laporan, header=False)



Fungsi class custom_laporan() diatas untuk memparsing data yang dibutuhkan oleh laporan. Kita tambahkan fitur untuk separator nominal.

3. Didalam folder report, buat file tagihan_jamaah.rml yang isinya :


<?xml version="1.0"?>
<document filename="Tagihan Jamaah.pdf">

	<!-- Definisi Layout (Ukuran Kertas) -->

    <template pageSize="(595, 842)" title="Tagihan Jamaah" author="Muahmmad Aziz - 087881071515">
		<pageTemplate id="first">
			<!-- Menentukan luas dari canvas yang dapat kita 'tulis' dengan parameter widht & height beserta titik awal penulisan identasi/margin. x1 dan y1 dimulai dari kiri bawah -->
			<frame id="first" x1="30.0" y1="30.0" width="538" height="800"/>
		</pageTemplate>
	</template>



    <!-- Definisi Style -->

	<stylesheet>

		<!-- Style Tabel -->

		<blockTableStyle id="Table_String_Default">
			<blockAlignment value="LEFT"/>
			<blockValign value="TOP"/>
		</blockTableStyle>

		<blockTableStyle id="Tbl_Left_Grid">
            <blockAlignment value="LEFT"/>
            <blockValign value="TOP"/>
            <lineStyle kind="GRID" colorName="#000000"/>
        </blockTableStyle>

        <blockTableStyle id="Tbl_Signing">
            <blockAlignment value="CENTER"/>
            <lineStyle kind="LINEBEFORE" colorName="black" start="0,0" stop="0,0" />
            <lineStyle kind="LINEBELOW" colorName="black" start="0,0" stop="0,0" />
            <lineStyle kind="LINEABOVE" colorName="black" start="0,0" stop="0,0" />
            <lineStyle kind="LINEAFTER" colorName="black" start="0,0" stop="0,0" />
        </blockTableStyle>

        <blockTableStyle id="Tbl_Center">
            <blockAlignment value="CENTER"/>
            <lineStyle kind="GRID" colorName="#000000"/>
        </blockTableStyle>

        <initialize>
            <paraStyle name="all" alignment="justify"/>
        </initialize>



        <!-- Style Huruf -->

        <paraStyle name="terp_header" fontName="Helvetica-Bold" fontSize="12.0" leading="15" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_table_header" fontName="Helvetica-Bold" fontSize="8.0" leading="8" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_default" fontName="Helvetica" fontSize="7.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_default_left" fontName="Helvetica-Bold" fontSize="8.0" leading="11" alignment="LEFT" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_default_right" fontName="Helvetica" fontSize="7.0" leading="11" alignment="RIGHT" spaceBefore="0.0" spaceAfter="0.0"/>
        <paraStyle name="terp_default_center" fontName="Helvetica" fontSize="7.0" leading="11" alignment="CENTER" spaceBefore="0.0" spaceAfter="0.0"/>

    </stylesheet>



    <story>

        <!-- Definisi Object Alias-->

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



    	<!-- Judul Report -->

    	<blockTable colWidths="500.0" style="Table_String_Default">
    		<tr>
    			<td>
    				<para style="terp_header">CUSTOMER INVOICES</para>
    			</td>
    		</tr>
    	</blockTable>



        <!-- Gap/Space Baris Kosong -->

    	<para style="terp_default"><font color="white"></font></para>



    	<!-- Header Report -->

    	<blockTable colWidths="300.0, 150.0" style="Table_String_Default">
	        <tr>
	        	<td>
	        		<para style="terp_default">Customer : [[ o.partner_id.name ]]</para>
                    <para style="terp_default">Address : [[ o.partner_id.street ]] [[ o.partner_id.street2 ]]</para>
					<para style="terp_default">Phone : [[ o.partner_id.phone or '-' ]] / [[ o.partner_id.mobile or '-' ]]</para>
				</td>
				<td>
                    <para style="terp_default">No. : [[ o.number ]]</para>
                    <para style="terp_default">Date : [[ time.strftime('%d %B %Y', time.strptime(o.date_invoice,'%Y-%m-%d')) ]]</para>
                </td>
            </tr>
		</blockTable>



        <!-- Gap/Space Baris Kosong -->

    	<para style="terp_default"><font color="white"></font></para>



		<!-- Header Tabel Invoice -->

		<blockTable colWidths="210.0, 80.0, 80.0, 80.0" repeatRows="1" style="Tbl_Left_Grid">
			<tr>
				<td><para style="terp_table_header">Product</para></td>
				<td><para style="terp_table_header">Qty (UoM)</para></td>
				<td><para style="terp_table_header">Price Unit</para></td>
				<td><para style="terp_table_header">Subtotal</para></td>
			</tr>
		</blockTable>



        <!-- Line Tabel Invoice -->

		<section>
			<para style="terp_default">[[ repeatIn(o.invoice_line_ids, 'x') ]]</para>
			<blockTable colWidths="210.0, 80.0, 80.0, 80.0" repeatRows="0" style="Tbl_Left_Grid">
				<tr>
					<td><para style="terp_default">[[ x.product_id.name ]]</para></td>
					<td><para style="terp_default_right">[[ x.quantity ]] [[ x.uom_id.name ]]</para></td>
					<td><para style="terp_default_right">[[ koma('%.0f', x.price_unit) ]]</para></td>
					<td><para style="terp_default_right">[[ koma('%.0f', x.price_subtotal) ]]</para></td>
				</tr>
			</blockTable>
		</section>

        <blockTable colWidths="370, 80.0" repeatRows="0" style="Tbl_Left_Grid">
            <tr>
                <td><para style="terp_default_right">Total : </para></td>
                <td><para style="terp_default_right">[[ koma('%.0f', o.amount_total) ]]</para></td>
            </tr>
        </blockTable>



        <!-- Gap/Space Baris Kosong -->

    	<para style="terp_default"><font color="white"></font></para>
        <para style="terp_default"><font color="white"></font></para>



        <!-- Gap/Space Baris Kosong -->

    	<para style="terp_default"><font color="white"></font></para>



        <!-- Header Tabel Payment-->


        <blockTable colWidths="450" repeatRows="0" style="Tbl_Left_Grid">
            <tr>
                <td><para style="terp_default_left">PAYMENT</para></td>
            </tr>
        </blockTable>

        <blockTable colWidths="200.0, 125.0, 125.0" repeatRows="0" style="Tbl_Left_Grid">
            <tr>
                <td><para style="terp_table_header">Method</para></td>
                <td><para style="terp_table_header">Date</para></td>
                <td><para style="terp_table_header">Amount</para></td>
            </tr>
        </blockTable>



        <!-- Line Tabel Payment-->

        <section>
            <para style="terp_default">[[ repeatIn(o.payment_ids, 'x') ]]</para>
            <blockTable colWidths="200.0, 125.0, 125.0" repeatRows="0" style="Tbl_Left_Grid">
                <tr>
                    <td><para style="terp_default">[[ x.journal_id.name ]]</para></td>
                    <td><para style="terp_default_center">[[ x.payment_date ]]</para></td>
					<td><para style="terp_default_right">[[ koma('%.0f', x.amount) ]]</para></td>
                </tr>
            </blockTable>
        </section>



        <!-- Gap/Space Baris Kosong -->

        <para style="terp_default"><font color="white"></font></para>
        <para style="terp_default"><font color="white"></font></para>

        <blockTable colWidths="150" rowHeights="100" style="Tbl_Signing">
        <tr>
            <td>
                <para style="terp_table_header" spaceAfter="80">Mengetahui</para>
                <para style="terp_table_header">[[ o.create_uid.name ]]</para>
            </td>
        </tr>
        </blockTable>

    </story>


</document>


Report yang dihasilkan adalah :

Cukup sekian untuk pertemuan kali ini, saran dan kritik yang membangun sangat diharapkan.
Semoga Bermanfaat.

NB : Download source code disini

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s