# Pengembangan Custom Module Odoo ERP: Panduan Developer untuk Alur Kerja Bisnis Indonesia

> Pelajari cara membangun custom module Odoo yang siap produksi dari awal, disesuaikan dengan kebutuhan bisnis Indonesia seperti validasi NPWP dan rantai persetujuan pembelian bertingkat untuk perusahaan PT. Panduan ini mencakup dasar ORM, pewarisan model, view XML, dan packaging module yang aman.

URL: https://www.matthewswong.com/id/blog/odoo-erp-custom-module-development-indonesia/

- Author: Matthews Wong (https://www.matthewswong.com/id/about/)
- Published: 2026-02-25
- Updated: 2026-10-12
- Category: ERP

Arsitektur modular Odoo menjadikannya salah satu platform ERP paling dapat dikembangkan, namun menyesuaikannya dengan regulasi Indonesia — NPWP, NIK, dan hierarki persetujuan khusus perusahaan — membutuhkan pemahaman mendalam tentang ORM dan sistem inheritance framework tersebut. Panduan ini memandu pembangunan custom module lengkap untuk alur kerja persetujuan pembelian perusahaan PT, dari scaffold direktori hingga deployment produksi.

## Memahami Struktur Module Odoo

Setiap module Odoo adalah package Python dengan file __manifest__.py wajib yang mendeklarasikan metadata, dependensi, dan file data module. Module yang terorganisir dengan baik memisahkan concern ke dalam direktori models/, views/, security/, controllers/, dan static/. Menjaga struktur ini konsisten membuat module Anda dapat dirawat oleh developer lain dan kompatibel dengan mekanisme upgrade Odoo.

### File __manifest__.py

Manifest mendeklarasikan nama, versi, author, lisensi module, dan yang terpenting daftar depends — aplikasi Odoo lain yang harus diinstal agar module Anda berfungsi. Selalu cantumkan versi Odoo minimum dengan format '17.0.1.0.0'. Menyertakan 'auto_install': False memastikan module hanya aktif saat diinstal secara manual, mencegah perilaku tak terduga di produksi.

### Keamanan: Access Control List dan Record Rules

File security/ir.model.access.csv mendefinisikan grup user mana yang dapat membuat, membaca, menulis, atau menghapus record di model custom Anda. Record rules (ir.rule) menyediakan keamanan tingkat baris, misalnya membatasi permintaan pembelian hanya untuk kantor cabang pembuat. Selalu definisikan keamanan terlebih dahulu — men-deploy module tanpa ACL yang tepat mengekspos semua data ke semua user internal.

> Gunakan 'odoo scaffold nama_module .' untuk membuat skeleton direktori lengkap secara otomatis. Perintah ini membuat __manifest__.py, __init__.py, models/__init__.py, views/, dan security/ dengan import yang benar, menghemat 10–15 menit setup boilerplate per module.

## Membangun Data Model dengan Odoo ORM

ORM Odoo memetakan kelas Python yang mewarisi models.Model langsung ke tabel PostgreSQL, menangani migrasi secara otomatis ketika Anda menaikkan versi module. Field dideklarasikan sebagai atribut kelas menggunakan tipe field seperti fields.Char, fields.Many2one, dan fields.One2many. Computed field menggunakan @api.depends menghitung ulang otomatis ketika dependensinya berubah, menjaga UI responsif tanpa trigger manual.

### Field Khusus Indonesia: Validasi NPWP dan NIK

Administrasi perpajakan Indonesia mengidentifikasi wajib pajak dengan NPWP 16 digit: orang pribadi penduduk memakai NIK 16 digit, wajib pajak lainnya memiliki NPWP 16 digit, dan setiap tempat kegiatan usaha mendapat NITKU 22 digit, yaitu NPWP 16 digit ditambah 6 digit nomor urut. PER-11/PJ/2025 mewajibkan NPWP pembeli dicantumkan di faktur pajak, atau NIK bagi orang pribadi dalam negeri, dan format lama 15 digit XX.XXX.XXX.X-XXX.XXX hanya boleh dipakai pihak lain sampai 31 Desember 2024. Menggunakan @api.constrains untuk memvalidasi format ini di level ORM memastikan data tidak valid tidak pernah mencapai database, terlepas dari apakah record dibuat melalui UI web, XML-RPC, atau impor data. Demikian pula, NIK harus tepat 16 digit — constraint yang layak diterapkan untuk kepatuhan audit.

### Level Persetujuan Dinamis dengan Computed Field

Perusahaan PT Indonesia biasanya memerlukan persetujuan pembelian bertingkat: manager untuk jumlah di bawah Rp 50 juta, direktur hingga Rp 500 juta, dan dewan direksi di atas itu. Mengkodekan logika ini dalam computed field yang disimpan (store=True) berarti level persetujuan dipertahankan ke database, memungkinkan filtering dan pelaporan cepat tanpa menghitung ulang di setiap load view.

> **Jangan Pernah Memodifikasi Module Odoo Inti Secara Langsung**

> Mengedit file di dalam source tree Odoo (addons/account/, addons/purchase/, dll.) akan ditimpa di setiap upgrade, menghancurkan perubahan Anda dan berpotensi merusak seluruh instalasi. Selalu gunakan _inherit untuk memperluas model yang ada dan inherit_id di XML untuk memperluas view yang ada. Ini tidak bisa ditawar untuk deployment produksi yang akan menerima patch keamanan.

## Mendefinisikan View XML dan Menu

View di Odoo adalah record XML yang disimpan di model ir.ui.view. Form view mendefinisikan tata letak record individual, tree view menampilkan list, dan search view menambahkan opsi filter ke list. Ketiganya biasanya diperlukan agar model baru dapat digunakan sepenuhnya. Widget field referensi seperti many2one, statusbar, dan stat_button secara signifikan meningkatkan UX dengan kode tambahan minimal.

### Memperluas View yang Ada dengan xpath

Saat mewarisi dari model yang ada seperti purchase.order, Anda dapat menyisipkan field baru ke dalam form view yang ada menggunakan inherit_id yang menunjuk ke view asli dan ekspresi xpath untuk menargetkan titik penyisipan dengan tepat. Gunakan position='after', position='before', atau position='inside' untuk mengontrol penempatan. Hindari mengganti seluruh bagian view — targetkan node sekecil mungkin untuk meminimalkan konflik upgrade.

### Sequence dan Nomor Referensi

Dokumen bisnis Indonesia memerlukan nomor referensi terformat (misalnya PA/2026/05/0001 untuk persetujuan pembelian). Definisikan record ir.sequence di data XML Anda dengan pengaturan prefix dan padding, lalu panggil self.env['ir.sequence'].next_by_code() di override create() model. Menggunakan 'New' sebagai default dan menggantinya saat simpan adalah konvensi Odoo yang mencegah gap sequence dari record draft yang dibuang.

### Model Wizard untuk Aksi Multi-Langkah

TransientModel (models.TransientModel) membuat form wizard yang tidak bertahan di database dalam jangka panjang — Odoo membersihkannya secara otomatis setelah periode yang dapat dikonfigurasi. Gunakan wizard untuk dialog konfirmasi persetujuan, operasi massal, atau aksi multi-langkah yang memerlukan input user sebelum commit. Mengikat wizard ke action melalui binding_model_id membuatnya muncul sebagai tombol Action kontekstual di list view yang relevan.

> Jalankan 'odoo-bin -d your_db -u your_module --test-enable --stop-after-init' di pipeline CI Anda untuk mengeksekusi unit test di setiap push. Framework test Odoo mendukung TransactionCase untuk test terisolasi dan suite test bertag, memudahkan otomatisasi regression testing untuk logika custom module.

## Controller dan JSON-RPC API

Odoo mengekspos endpoint JSON-RPC dan XML-RPC secara bawaan, tetapi terkadang Anda memerlukan HTTP route custom — untuk aplikasi mobile, penerima webhook, atau integrasi pihak ketiga seperti pengiriman e-Faktur ke DJP. Controller yang mewarisi http.Controller menggunakan dekorator @http.route() dengan auth='user' atau auth='public' tergantung pada persyaratan akses endpoint.

### Packaging dan Deploy Module

Kemas module Anda sebagai file ZIP untuk klien yang tidak memiliki akses server langsung, atau deploy melalui Git dengan menambahkan direktori module Anda ke addons_path di odoo.conf. Untuk lingkungan produksi di provider hosting Indonesia seperti Biznet atau IDCloudHost, gunakan user odoo khusus, jalankan server di belakang Nginx dengan TLS, dan simpan addons custom di luar source tree Odoo agar bertahan dari upgrade.

## Testing dan Kualitas Module

Module Odoo produksi harus menyertakan minimal smoke test yang menginstal module di database bersih dan membuat satu record melalui alur kerja lengkap. Framework test Odoo mendukung unit test (TransactionCase) dan tour test (HttpCase) untuk otomasi UI. Mengintegrasikan ini ke pipeline GitLab CI atau GitHub Actions menangkap regresi sebelum mencapai instance produksi.

> **Key takeaway:** Odoo sangat bisa diperluas, tetapi menyesuaikannya dengan kebutuhan Indonesia berarti menambahkan NPWP, NIK dan hierarki persetujuan khas perusahaan lewat ORM dan model inheritance framework-nya. Memperluas model yang sudah ada, bukan menggantinya, adalah yang menjaga modul custom tetap bisa di-upgrade antar versi Odoo.

## Source links

- https://www.odoo.com/documentation/17.0/developer/tutorials/server_framework_101.html
- https://www.odoo.com/documentation/17.0/developer/reference/backend/orm.html
- https://www.pajak.go.id/sites/default/files/2024-09/Petunjuk%20Penggunaan%20Nomor%20Pokok%20Wajib%20Pajak%20dan%20Nomor%20Identitas%20Tempat%20Kegiatan%20Usaha%20bagi%20Wajib%20Pajak.pdf
- https://www.pajak.go.id/index.php/system/files/2023-12/FAQ%20Terkait%20NITKU.pdf
- https://www.odoo.com/documentation/17.0/developer/howtos/rdtraining/13_inheritance.html

## FAQ

### Mengapa kita tidak boleh memodifikasi file module inti Odoo secara langsung?

Mengedit file di dalam source tree Odoo (seperti addons/account/ atau addons/purchase/) akan ditimpa setiap kali Anda melakukan upgrade Odoo, menghancurkan perubahan Anda dan berpotensi merusak seluruh instalasi. Cara yang benar adalah menggunakan _inherit untuk memperluas model yang ada di Python dan inherit_id dengan xpath di XML untuk memperluas view yang ada. Ini tidak bisa ditawar untuk deployment produksi yang akan menerima patch keamanan.

### Bagaimana post ini merekomendasikan penanganan validasi NPWP dan NIK dalam custom module Odoo?

Post ini merekomendasikan penggunaan @api.constrains untuk memvalidasi format identitas pajak yang berlaku di level ORM: NPWP 16 digit (orang pribadi penduduk memakai NIK 16 digit), NITKU 22 digit untuk setiap tempat kegiatan usaha, dan NIK tepat 16 digit. Format lama 15 digit XX.XXX.XXX.X-XXX.XXX hanya boleh dipakai pihak lain sampai 31 Desember 2024. Validasi di layer ini memastikan data tidak valid tidak pernah mencapai database PostgreSQL, terlepas dari apakah record dibuat melalui UI web, XML-RPC, atau impor data massal.

### Bagaimana alur persetujuan pembelian bertingkat bekerja untuk perusahaan PT Indonesia di Odoo?

Perusahaan PT Indonesia biasanya memerlukan persetujuan pembelian bertingkat: manager menyetujui jumlah di bawah Rp 50 juta, direktur menangani hingga Rp 500 juta, dan dewan direksi menyetujui di atas ambang tersebut. Post ini mengkodekan logika ini dalam computed field yang disimpan (store=True) agar level persetujuan dipertahankan ke database, memungkinkan filtering dan pelaporan cepat tanpa menghitung ulang di setiap load view. Dekorator @api.depends memastikan field menghitung ulang secara otomatis ketika jumlah pembelian berubah.

### Apa peran TransientModel (wizard) dalam custom module Odoo?

TransientModel membuat form wizard yang tidak bertahan di database dalam jangka panjang — Odoo membersihkannya secara otomatis setelah periode yang dapat dikonfigurasi. Wizard cocok untuk dialog konfirmasi persetujuan, operasi massal, atau aksi multi-langkah yang memerlukan input pengguna sebelum menyimpan perubahan. Mengikat wizard ke action melalui binding_model_id membuatnya muncul sebagai tombol Action kontekstual di list view yang relevan.

### Apa cara yang direkomendasikan untuk membuat nomor referensi terformat seperti PA/2026/05/0001 di Odoo?

Post ini merekomendasikan pendefinisian record ir.sequence di data XML Anda dengan pengaturan prefix dan padding yang sesuai, lalu memanggil self.env['ir.sequence'].next_by_code() di dalam override create() model. Menggunakan 'New' sebagai nilai tampilan default dan menggantinya saat simpan adalah konvensi standar Odoo — ini mencegah gap sequence yang terjadi akibat record draft yang dibuang sebelum disimpan.

---

- HTML version: https://www.matthewswong.com/id/blog/odoo-erp-custom-module-development-indonesia/
- English: https://www.matthewswong.com/en/blog/odoo-erp-custom-module-development-indonesia/
- Site index for agents: https://www.matthewswong.com/llms.txt
- Sitemap: https://www.matthewswong.com/sitemap.xml
- API description (OpenAPI): https://www.matthewswong.com/openapi.json
