Membuat Module Diskon Nominal pada Odoo

Pada tutorial-tutorial sebelumnya kita sudah membahas dasar-dasar pembuatan module pada odoo, terutama tutorial-tutorial yang ditulis lebih awal. Pada tutorial-tutorial tersebut contoh kasus-nya mungkin terlalu sederhana, sehingga sulit terjadi / dipakai di dunia nyata. Lalu bagaimana jika di tutorial kali ini kita membuat module odoo dengan contoh kasus yang lebih rumit ? Tidak hanya lebih rumit pastinya juga harus dibutuhkan di dunia nyata.

Kasus apa yang sebaiknya kita bahas ? Bagaimana dengan Diskon Nominal di Sale Order ? Kebetulan odoo sudah menyediakan fitur diskon dalam bentuk persentase, tapi tidak ada fitur diskon dalam bentuk nominal. Sebelum memulai membuat module sebaiknya kita bahas dulu bagaimana cara kerja diskon persentase di odoo.

Fitur diskon sale order pada odoo secara default tidak aktif. Untuk mengaktifkan fitur ini masuk menu Sales >> Configuration >> Setting. Kemudian centang pilihan Discounts pada bagian Pricing.

Kemudian masuk menu Sale dan buat sale order baru. Maka akan muncul field diskon di order line seperti gambar dibawah ini.

Module yang kita buat nanti-nya juga harus memiliki fitur seperti diskon default odoo ini. Dalam artian field Diskon Nominal nantinya bisa kita setting agar di tampilkan atau tidak.

Pertama buat module dengan nama sale_nominal_discount. Buat file security.xml dan masukkan kode dibawah ini

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <!-- 
    	Group untuk menampilkan dan menyembunyikan discount nominal
    	group ini nantinya diatur lewat menu Sales >> Configuration >> Setting
    -->
    <record id="group_discount_nominal" model="res.groups">
        <field name="name">Discount Nominal</field>
        <field name="category_id" ref="base.module_category_hidden"/>
    </record>    
</odoo>

Selanjutnya buat file config.py untuk mengatur group diatas

# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError

class Config(models.TransientModel):
    _inherit = 'res.config.settings'

    # jika field group_discount_nominal ini bernilai True
    # maka semua user akan masuk group sale_nominal_discount.group_discount_nominal
    # sehingga field diskon nominal bisa tampil di form Sale Order
    group_discount_nominal = fields.Boolean("Discount Nominal", implied_group='sale_nominal_discount.group_discount_nominal')

Selanjutnya buat view dengan nama config.xml dan masukkan kode dibawah ini agar check box Discount Nominal bisa kita atur di menu Setting

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="res_config_settings_view_form" model="ir.ui.view">
        <field name="name">res.config.settings.view.form.inherit.sale</field>
        <field name="model">res.config.settings</field>
        <field name="inherit_id" ref="sale.res_config_settings_view_form" />
        <field name="arch" type="xml">
            <!--
                Letakkan di bagian paling bawah 
                karena template sale_ebay ada dibagian paling bawah di view sale.res_config_settings_view_form
            -->
            <div id="sale_ebay" position="after">
                <h2>Discount Nominal</h2>
                <div class="row mt16 o_settings_container">
                    <div class="col-12 col-lg-6 o_setting_box" title="Apply discount nominal">
                        <div class="o_setting_left_pane">
                            <field name="group_discount_nominal"/>
                        </div>
                        <div class="o_setting_right_pane">
                            <label for="group_discount_nominal"/>
                            <div class="text-muted">
                                Grant discount nominal on sales order lines
                            </div>
                        </div>
                    </div>
                </div>
            </div>
        </field>
    </record>
</odoo>

Karena field diskon disimpan di model sale.order.line kita juga perlu menambahkan field diskon nominal di model sale.order.line

# -*- coding: utf-8 -*-
from odoo import api, fields, models, _
from odoo.exceptions import UserError, ValidationError
from odoo.addons import decimal_precision as dp

class SaleLine(models.Model):
    _inherit = 'sale.order.line'

    # tambah field Diskon Nominal
    discount_nominal = fields.Float(string='Discount Nominal', digits=dp.get_precision('Discount'), default=0.0)

Selanjutnya tambahkan field discount_nominal diatas ke dalam form Sale Order

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="view_order_form_inherit" model="ir.ui.view">
        <field name="name">view.order.form.inherit</field>
        <field name="model">sale.order</field>
        <field name="inherit_id" ref="sale.view_order_form" />
        <field name="arch" type="xml">
            <xpath expr="//field[@name='order_line']/tree/field[@name='discount']" position="after">
                <field name="discount_nominal" groups="sale_nominal_discount.group_discount_nominal"/>
            </xpath>
        </field>
    </record>
</odoo>

Atur file manifest.py dan __init__.py agar module bisa diinstall. Silahkan baca tutorial-tutorial sebelumnya jika anda merasa kesulitan.

Setelah module berhasil diinstall, masuk menu Sales >> Configuration >> Setting. Pada bagian paling bawah akan muncul check box untuk mengatur tampil tidak-nya field Diskon Nominal di form sale order.

Centang check box tersebut, klik Save. Buat Sale Order. Lihat, field Diskon Nominal sudah tampil. Selanjutnya kita perlu menambahkan logic / perhitungan diskon.

Secara default diskon pada model sale.order.line dihitung pada method _compute_amount, sehingga kita perlu meng-override method tersebut. Tambahkan kode ini pada model sale.order.line yang baru saja kita buat.

# hitung diskon nominal
# override total perhitungan dari odoo
@api.depends('product_uom_qty', 'discount', 'price_unit', 'tax_id', 'discount_nominal')
def _compute_amount(self):
    for line in self:
        # potong harga setelah dikurangi diskon persentase dengan diskon nominal
        price = line.price_unit * (1 - (line.discount or 0.0) / 100.0) - line.discount_nominal
        # selanjutanya adalah kode asli odoo
        taxes = line.tax_id.compute_all(price, line.order_id.currency_id, line.product_uom_qty, product=line.product_id, partner=line.order_id.partner_shipping_id)
        line.update({
            'price_tax': sum(t.get('amount', 0.0) for t in taxes.get('taxes', [])),
            'price_total': taxes['total_included'],
            'price_subtotal': taxes['total_excluded'],
        })

Restart service odoo kemudian buat Sale Order lagi, sekarang diskon nominal sudah dihitung dengan benar.

Selain itu kita juga perlu membawa nilai Diskon Nominal ini ke Invoice. Jika tidak nilai total antara Sale Order dan Invoice akan berbeda. Pertama tambahkan field discount_nominal pada model account.invoice.line kemudian override method perhitungan diskon-nya

class InvoiceLine(models.Model):
    _inherit = 'account.invoice.line'

    # tambah field Diskon Nominal
    discount_nominal = fields.Float(string='Discount Nominal', digits=dp.get_precision('Discount'), default=0.0)

    # hitung diskon nominal
    # override total perhitungan dari odoo
    @api.one
    @api.depends('price_unit', 'discount', 'invoice_line_tax_ids', 'quantity',
        'product_id', 'invoice_id.partner_id', 'invoice_id.currency_id', 'invoice_id.company_id',
        'invoice_id.date_invoice', 'invoice_id.date', 'discount_nominal')
    def _compute_price(self):
        currency = self.invoice_id and self.invoice_id.currency_id or None
        # potong harga setelah dikurangi diskon persentase dengan diskon nominal
        price = self.price_unit * (1 - (self.discount or 0.0) / 100.0) - self.discount_nominal
        # selanjutanya adalah kode asli odoo
        taxes = False
        if self.invoice_line_tax_ids:
            taxes = self.invoice_line_tax_ids.compute_all(price, currency, self.quantity, product=self.product_id, partner=self.invoice_id.partner_id)
        self.price_subtotal = price_subtotal_signed = taxes['total_excluded'] if taxes else self.quantity * price
        self.price_total = taxes['total_included'] if taxes else self.price_subtotal
        if self.invoice_id.currency_id and self.invoice_id.currency_id != self.invoice_id.company_id.currency_id:
            currency = self.invoice_id.currency_id
            date = self.invoice_id._get_currency_rate_date()
            price_subtotal_signed = currency._convert(price_subtotal_signed, self.invoice_id.company_id.currency_id, self.company_id or self.env.user.company_id, date or fields.Date.today())
        sign = self.invoice_id.type in ['in_refund', 'out_refund'] and -1 or 1
        self.price_subtotal_signed = price_subtotal_signed * sign

Tambahkan field discount_nominal diatas ke dalam view

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <record id="invoice_form_inherit" model="ir.ui.view">
        <field name="name">invoice.form.inherit</field>
        <field name="model">account.invoice</field>
        <field name="inherit_id" ref="account.invoice_form" />
        <field name="arch" type="xml">
            <xpath expr="//field[@name='invoice_line_ids']/tree/field[@name='discount']" position="after">
                <field name="discount_nominal" groups="sale_nominal_discount.group_discount_nominal"/>
            </xpath>
        </field>
    </record>
</odoo>

Terakhir override method yang membawa nilai dari Sale Order ke Invoice dengan cara menambahkan kode ini di model sale.order.line

# menambah field discount_nominal agar nilainya dibawa ke invoice
@api.multi
def _prepare_invoice_line(self, qty):
    # panggil method super untuk mendapatkan field-field yang dibawa ke invoice beserta nilainya
    res = super(SaleLine, self)._prepare_invoice_line(qty)
    # tambahkan field discount_nominal agar nilainya dibawa ke invoice
    res['discount_nominal'] = self.discount_nominal
    return res

Restart service odoo, kemudian buat Sale Order baru. Selanjutnya buat Invoice dari Sale Order tersebut. Perhatikan, nilai Diskon Nominal sudah dibawa dari Sale Order ke Invoice, perhitungannya pun sudah benar. Sayangnya perhitungan tax atau pajak di Sale Order dan Invoice berbeda, sehingga pada Invoice walaupun nilai diskon-nya sudah benar nilai tax / pajaknya masih salah.

Tax / pajak di invoice dihitung di method get_taxes_values di model account.invoice sehingga kita perlu meng-override method tersebut

class Invoice(models.Model):
    _inherit = 'account.invoice'

    # hitung tax / pajak
    # override total perhitungan dari odoo
    @api.multi
    def get_taxes_values(self):
        tax_grouped = {}
        for line in self.invoice_line_ids:
            if not line.account_id:
                continue

            # potong harga setelah dikurangi diskon persentase dengan diskon nominal
            price_unit = line.price_unit * (1 - (line.discount or 0.0) / 100.0) - line.discount_nominal
            
            taxes = line.invoice_line_tax_ids.compute_all(price_unit, self.currency_id, line.quantity, line.product_id, self.partner_id)['taxes']
            for tax in taxes:
                val = self._prepare_tax_line_vals(line, tax)
                key = self.env['account.tax'].browse(tax['id']).get_grouping_key(val)

                if key not in tax_grouped:
                    tax_grouped[key] = val
                else:
                    tax_grouped[key]['amount'] += val['amount']
                    tax_grouped[key]['base'] += val['base']
        return tax_grouped

Restart service odoo, kemudian coba hapus tax / pajak dari invoice yang sudah ada, kemudian tambahkan lagi, sekarang diskon dan pajak-nya sudah dihitung dengan benar.

Selamat anda sudah berhasil membuat module odoo yang bisa dipakai pada proses bisnis yang sebenarnya. Tapi perlu anda ingat setiap perusahaan memiliki rule yang berbeda mengenai Diskon Nominal ini. Pada tutorial ini diskon nominal dihitung setelah diskon persentase. Tapi mungkin diluar sana ada juga perusahaan yang menghitung diskon nominal sebelum diskon persentase. Atau malah seperti yang saya alami, memakai 2 diskon nominal sekaligus, sebelum dan sesudah diskon persentase.

Download Source Code

Tulisan Serupa

Leave a Reply

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