# Aplikasi Catat Jual-Beli Saham — Odoo 17 + Flutter Backend Odoo ERP (Community 17) untuk mencatat pembelian & penjualan saham, diakses dari aplikasi **Flutter** (Android). ## Arsitektur ``` Flutter app (Android) ──JSON-RPC──► Odoo 17 (Python) │ custom addon: saat_invest ▼ PostgreSQL 16 ``` ## Stack | Layer | Teknologi | Port | |---|---|---| | Backend | Odoo 17 Community (`odoo:17.0`) | `18069` (host) | | Database | PostgreSQL 16 (`postgres:16`) | internal (5432, tdk di-publish) | | Mobile | Flutter (Android) | — | ## Struktur proyek ``` saat-app/ ├── docker-compose.yml # Odoo 17 + PostgreSQL 16 ├── .env # RAHASIA: password (jangan commit) ├── addons/ # custom addon buatan sendiri (saat_invest) └── README.md ``` ## Cara menjalankan ```bash cd /home/ardhi/workspace/erp/odoo-app/saat-app docker compose config # validasi docker compose up -d # start ``` Akses UI: http://localhost:18069 — login `admin` dengan password `ODOO_ADMIN_PASSWORD` di `.env`. ## Status - ✅ Odoo 17 container jalan (port 18069), DB PostgreSQL 16 healthy - ✅ JSON-RPC aktif (uid `admin` verified) — endpoint yang akan dipakai Flutter - ✅ **Addon `saat_invest` terinstal** (model saham, transaksi, portofolio + seed 5 saham IDX) - ✅ **Portofolio auto-sync**: otomatis diperbarui tiap transaksi ditambah/ubah/hapus (tidak perlu tombol manual; buka menu Portofolio selalu datanya segar) - ✅ **Live harga pasar (cron)**: ambil & update harga dari Yahoo Finance (.JK) tiap 6 menit SAAT jam bursa IDX (08:45–16:00 WIB). Update hanya bila berubah. - ✅ End-to-end teruji: buat beli/jual → portofolio ter-update → hitung avg cost & realized/unrealized P/L - ⏳ Aplikasi Flutter (login, catat jual/beli, portfolio, P/L) ## Install / upgrade addon saat_invest ```bash cd /home/ardhi/Application/saat-app set -a && source .env && set +a # install pertama kali docker exec -e PASSWORD="$POSTGRES_PASSWORD" saat-odoo odoo \ -d "$POSTGRES_DB" --db_host=db --db_user="$POSTGRES_USER" \ --db_password="$POSTGRES_PASSWORD" --no-http --stop-after-init -i saat_invest # upgrade setelah ubah kode docker exec -e PASSWORD="$POSTGRES_PASSWORD" saat-odoo odoo \ -d "$POSTGRES_DB" --db_host=db --db_user="$POSTGRES_USER" \ --db_password="$POSTGRES_PASSWORD" --no-http --stop-after-init -u saat_invest docker restart saat-odoo ``` > Catatan Odoo: `` view di Odoo 17 kadang bermasalah, pakai `` > (kompatibel lintas versi) seperti yang sudah dilakukan. ## Troubleshooting PermissionError sessions Jika muncul: ```text PermissionError: [Errno 13] Permission denied: '/var/lib/odoo/sessions' ``` Odoo 17 berjalan sebagai user `odoo` dengan UID/GID `101`. Perbaiki ownership seluruh volume data Odoo dari container yang sedang berjalan: ```bash cid=$(docker compose ps -q odoo) docker exec --user root "$cid" sh -c \ 'chown -R 101:101 /var/lib/odoo && \ chmod 755 /var/lib/odoo && chmod 700 /var/lib/odoo/sessions' ``` Verifikasi write access sebagai user Odoo, lalu restart dan cek log: ```bash docker exec --user odoo "$cid" sh -c \ 'touch /var/lib/odoo/sessions/.write-check && \ rm /var/lib/odoo/sessions/.write-check' docker compose restart odoo docker compose logs --since=30s odoo ``` Gunakan hanya satu mount volume utama berikut pada Compose: ```yaml volumes: - saat_odoo_filestore:/var/lib/odoo ``` Jangan menambahkan mount terpisah ke `/var/lib/odoo/session` (singular). Path yang dipakai Odoo adalah `/var/lib/odoo/sessions` (plural). Jangan menggunakan `chmod 777`; cukup berikan ownership kepada user Odoo. ## Rencana migrasi Odoo 17 ke Odoo 18 Migrasi major version harus mengubah schema database. Mengganti image secara langsung dari `odoo:17.0` ke `odoo:18.0` **bukan** prosedur migrasi dan dapat menyebabkan error seperti `column res_lang.short_time_format does not exist`. Compose harus tetap menggunakan `odoo:17.0` sampai database selesai dimigrasi. ### 1. Persiapan dan backup Pastikan `.env` terisi dan jangan commit file tersebut. Hentikan penulisan data, lalu buat backup database dan filestore: ```bash set -a && source .env && set +a backup_dir="backups/odoo17-$(date +%Y%m%d-%H%M%S)" mkdir -p "$backup_dir" docker compose exec -T db pg_dump -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Fc \ > "$backup_dir/database.dump" volume=$(docker inspect saat-odoo --format '{{range .Mounts}}{{if eq .Destination "/var/lib/odoo"}}{{.Name}}{{end}}{{end}}') docker run --rm -v "$volume:/source:ro" -v "$PWD/$backup_dir:/backup" alpine \ sh -c 'tar czf /backup/filestore.tar.gz -C /source .' ``` Validasi backup dengan `pg_restore --list` dari image PostgreSQL 16 dan pastikan arsip filestore berisi direktori `filestore/`. ### 2. Migrasi pada salinan Jangan menguji migrasi pada volume produksi. Restore `database.dump` dan `filestore.tar.gz` ke environment staging yang terpisah, lalu gunakan salah satu jalur resmi berikut: - Odoo Upgrade Service untuk database Enterprise. - OpenUpgrade dengan branch yang sesuai untuk migrasi Community 17 ke 18. Migrasikan database terlebih dahulu. Sesuaikan custom addon `saat_invest` dengan API, view, security, dan dependency Odoo 18, lalu install/upgrade addon di database staging hasil migrasi. ### 3. Validasi staging Uji login, menu saham, create/update/delete transaksi, kalkulasi portofolio, cron harga pasar, attachment/filestore, dan seluruh endpoint JSON-RPC yang dipakai Flutter. Periksa log untuk `ERROR`, `Traceback`, `UndefinedColumn`, dan `PermissionError`. ### 4. Cutover produksi Setelah staging lulus, hentikan Odoo 17, buat backup final, restore atau jalankan migrasi pada database produksi sesuai prosedur tool migrasi, kemudian ubah: ```yaml image: odoo:18.0 ``` Pastikan ownership volume sesuai user Odoo 18 sebelum start, jalankan `docker compose up -d`, dan ulangi seluruh validasi staging. ### 5. Rollback Jika migrasi gagal, jangan menjalankan Odoo 17 pada database yang sudah sebagian dimigrasi. Hentikan stack, restore database dan filestore dari backup Odoo 17 ke volume/database terpisah, ubah image kembali ke `odoo:17.0`, lalu validasi aplikasi sebelum cutover ulang. ## API untuk Flutter (via JSON-RPC `execute_kw`) Semua melalui `POST /jsonrpc` dengan `{service:'object', method:'execute_kw'}`. Login: `common.authenticate(db, user, pass)` → `uid`. - List saham: `saat.saham search_read [domain] {fields}` - Catat transaksi: `saat.transaksi create [{vals}]` - List transaksi: `saat.transaksi search_read [domain] {fields}` - **Rekalkulasi portofolio**: `saat.portofolio action_recalculate [[]]` - Baca portofolio: `saat.portofolio search_read [domain] {fields}` (sisa_lembar, avg_cost, total_beli_biaya, realized_pl, unrealized_pl, nilai_sekarang) ## Catatan keamanan - `.env` berisi rahasia → **tidak boleh di-commit** (sudah di `.gitignore`) - PostgreSQL tidak di-publish ke host, hanya akses antar-container - Ganti `ADMIN_PASSWD` segera setelah setup awal