Minta Saran/Sharing: Implementasi 'onchange' untuk Baris Tree View di Odoo (Custom Module)
Diposting pada 15 Jun 2026, 10:18
Ditanyakan oleh: Bambang
Halo rekan-rekan praktisi Odoo di sini! Saya Bambang, dari tim IT. Semoga sehat selalu ya.
Saya lagi ada sedikit kendala nih saat mengembangkan custom module di Odoo (misalnya di Odoo 16, tapi mungkin kasusnya sama di versi lain).
Ceritanya, saya punya sebuah form utama dengan field `X` (misal `many2one` ke suatu master data). Di bawahnya, ada detail dalam bentuk tree view (biasanya pakai `one2many` field).
Nah, kebutuhan saya adalah bagaimana caranya agar ketika nilai di field `X` pada form utama diubah, secara otomatis baris-baris di tree view tersebut bisa langsung dinamis menyesuaikan.
Contoh konkretnya:
1. Jika field `X` diubah, semua baris yang ada di tree view itu bisa langsung di-clear dan diganti dengan baris-baris baru yang dihasilkan dari logika tertentu (berdasarkan nilai field `X`).
2. Atau, bisa juga mengupdate nilai di kolom tertentu pada baris-baris tree view yang sudah ada.
3. Bahkan bisa menambahkan baris baru secara otomatis tanpa harus klik 'Add a line'.
Saya sudah mencoba pendekatan `@api.onchange('field_x')` di model parent untuk memanipulasi `one2many_field` saya. Namun, saya sering menemui tantangan saat mencoba operasi seperti `(0, 0, {...})`, `(5)`, atau `(6, 0, [...])` untuk `one2many` di dalam onchange. Terutama saat ingin melakukan mass clear and add atau update multiple existing lines. Rasanya kok belum efektif dan datanya kadang jadi kurang konsisten atau butuh refresh manual.
Adakah rekan-rekan yang punya pengalaman atau best practice untuk implementasi 'onchange' yang bisa memodifikasi atau membuat baris di tree view secara dinamis dan reliable? Mungkin ada pattern atau trik khusus yang perlu diperhatikan? Mohon saran dan pencerahannya ya. Terima kasih banyak!
Admin Odoo ForumAdmin & AI Support15 Jun 2026, 10:18
Halo Mas Bambang, Sobat Odoo dari tim IT! Semoga selalu sehat dan semangat ya.
Wah, ini adalah kasus yang cukup umum dan sering menjadi "misteri" bagi banyak developer Odoo, terutama saat berhadapan dengan manipulasi one2many di dalam onchange. Nggak perlu khawatir, Mas, Anda di jalur yang benar kok, cuma memang ada beberapa trik atau "best practice" yang perlu diperhatikan.
Pertama, mari kita pahami dulu cara kerja @api.onchange di Odoo. Fungsi onchange itu pada dasarnya berjalan di sisi client (browser) dan tujuannya adalah untuk memberikan saran perubahan nilai field kepada user di UI, bukan untuk langsung menulis data ke database. Jadi, nilai-nilai yang Anda kembalikan dari onchange akan dipakai untuk update tampilan form tanpa harus menyimpan datanya.
Untuk field one2many, kita memang menggunakan format tuple khusus untuk memberitahu Odoo perubahan apa yang ingin kita lakukan pada baris-baris tree view. Perintah `(0, 0, {...})`, `(1, id, {...})`, `(2, id, False)`, dan seterusnya adalah perintah yang Odoo kenali untuk memanipulasi baris one2many.
Berikut adalah pendekatan yang paling umum dan reliable untuk skenario yang Mas Bambang hadapi:
1. Clearing Existing Lines (Membersihkan Semua Baris)
Jika Anda ingin menghapus semua baris yang ada di tree view dan menggantinya dengan yang baru, perintah yang paling efektif adalah `(5, 0, 0)`. Perintah ini akan "unlink" (memutuskan hubungan) semua baris yang sedang ditampilkan di UI dari parent record, tanpa menghapus data aslinya di database (jika baris tersebut sudah tersimpan). Jika baris tersebut baru dibuat di sesi form yang sama (belum tersimpan), maka baris tersebut akan hilang.
2. Adding New Lines (Menambahkan Baris Baru)
Setelah membersihkan atau jika Anda hanya ingin menambahkan baris baru, gunakan perintah `(0, 0, {field1: value1, field2: value2, ...})`. Perintah ini akan membuat baris baru di UI yang belum memiliki ID database, dan akan mendapatkan ID ketika parent record disimpan.
3. Updating Existing Lines (Mengupdate Baris yang Sudah Ada)
Ini sedikit lebih tricky.
Jika baris yang ingin Anda update sudah tersimpan di database (punya ID asli), Anda bisa menggunakan `(1, existing_line_id, {field_to_update: new_value})`.
Jika baris tersebut baru dibuat di sesi form yang sama dan belum tersimpan (masih punya ID sementara dari `(0,0,{})`), maka biasanya lebih mudah untuk menghapus semua baris (`(5,0,0)`) dan membuat ulang baris-baris tersebut dengan nilai yang baru. Mengupdate baris yang belum tersimpan secara langsung di `onchange` tanpa ID database bisa menjadi rumit.
Mari kita lihat contoh implementasi di custom module Mas Bambang:
Misalkan Anda punya model parent `my.parent.model` dengan field `field_x` (`many2one`) dan `detail_ids` (`one2many` ke `my.child.model`).
```python
from odoo import models, fields, api
class MyParentModel(models.Model):
_name = 'my.parent.model'
_description = 'My Parent Model'
field_x = fields.Many2one('some.master.data', string='Master Data X')
detail_ids = fields.One2many('my.child.model', 'parent_id', string='Details')
@api.onchange('field_x')
def _onchange_field_x_populate_details(self):
# Pastikan self.field_x ada nilainya sebelum memproses
if not self.field_x:
# Jika field_x kosong, mungkin kita ingin membersihkan semua detail
return {'value': {'detail_ids': [(5, 0, 0)]}}
# Inisialisasi list untuk perintah one2many
# Kita akan membersihkan semua baris lama terlebih dahulu
commands = [(5, 0, 0)] # Command untuk menghapus/membersihkan semua baris lama
# Contoh logika untuk membuat baris baru berdasarkan field_x
# Misal, kita ambil data dari field_x dan gunakan untuk membuat detail
# Contoh 1: Ambil data dari field_x itu sendiri
# Ini hanya contoh, di real-case Anda mungkin query model lain
# Asumsi self.field_x punya field 'name' dan 'code'
if self.field_x.name:
new_line_data = {
'name': f"Item A for {self.field_x.name}",
'quantity': 1,
'price': 100.0,
# Tambahkan field lain dari model child Anda
}
commands.append((0, 0, new_line_data)) # Tambahkan baris baru
new_line_data_2 = {
'name': f"Item B for {self.field_x.name}",
'quantity': 2,
'price': 75.0,
}
commands.append((0, 0, new_line_data_2)) # Tambahkan baris baru lagi
# Contoh 2: Query data dari model lain berdasarkan field_x
# Misalnya, Anda punya model 'product.template' dan ingin menambahkan produk terkait
# products = self.env['product.template'].search([('category_id', '=', self.field_x.id)])
# for product in products:
# commands.append((0, 0, {
# 'name': product.name,
# 'quantity': 1,
# 'price': product.list_price,
# }))
# Kembalikan dictionary dengan 'value' yang berisi perubahan field
return {'value': {'detail_ids': commands}}
# Model Child (Detail Baris)
class MyChildModel(models.Model):
_name = 'my.child.model'
_description = 'My Child Model'
parent_id = fields.Many2one('my.parent.model', string='Parent')
name = fields.Char(string='Description')
quantity = fields.Integer(string='Quantity')
price = fields.Float(string='Price')
# Tambahkan field lain yang dibutuhkan
```
Penjelasan Kode:
1. `@api.onchange('field_x')`: Ini decorator yang memastikan fungsi ini dijalankan ketika nilai `field_x` berubah.
2. `if not self.field_x:`: Penting untuk menangani kasus ketika `field_x` dikosongkan. Dalam contoh ini, kita juga membersihkan detailnya.
3. `commands = [(5, 0, 0)]`: Ini adalah baris kunci untuk "mass clear" semua baris yang ada di tree view.
4. `commands.append((0, 0, {...}))`: Setelah semua dibersihkan, kita bisa menambahkan baris baru dengan data yang sudah dihitung atau diambil dari logika Anda.
5. `return {'value': {'detail_ids': commands}}`: Struktur ini adalah cara Odoo mengharapkan nilai dikembalikan dari fungsi onchange untuk memperbarui UI.
Tips dan Best Practice Tambahan:
Performa: Karena onchange berjalan di sisi client setiap kali field berubah, pastikan logika di dalamnya tidak terlalu kompleks atau melakukan query database yang berat secara berlebihan. Jika terlalu lambat, pengalaman pengguna akan terganggu.
Konsistensi Data: Pastikan logika Anda selalu menghasilkan set baris yang konsisten berdasarkan `field_x`. Jika ada kasus di mana `field_x` bisa dikosongkan, pastikan `onchange` juga menangani kasus tersebut (misalnya, mengosongkan detail).
Debugging: Saat pengembangan, gunakan `_logger.info()` atau `print()` (jika Anda menjalankan Odoo di mode debug) untuk melihat nilai `commands` yang dihasilkan. Ini sangat membantu untuk memverifikasi apakah tuple yang Anda bentuk sudah benar.
Field Lain: Jika logika Anda bergantung pada lebih dari satu field di form utama, pastikan semua field tersebut masuk dalam decorator `@api.onchange('field_x', 'another_field', ...)`.
Dengan pendekatan `(5, 0, 0)` diikuti `(0, 0, {...})` ini, Anda akan mendapatkan perilaku yang dinamis dan reliable sesuai kebutuhan Mas Bambang. Ini adalah cara standar yang digunakan di Odoo untuk skenario seperti ini.
Semoga pencerahan ini membantu dan sukses selalu dalam pengembangan modul customnya, Mas Bambang! Kalau ada pertanyaan lebih lanjut, jangan sungkan ya.