Aplikasi catat jual-beli saham: Odoo 17 + Postgres 16 + addon saat_invest
- docker-compose.yml: odoo:17.0 (port 8069) + postgres:16 (internal, no publish) - Addon saat_invest: model saat.saham / saat.transaksi / saat.portofolio - P/L computed (realized FIFO, unrealized dari harga_sekarang) - action_recalculate() endpoint untuk Flutter - seed 5 saham IDX (BBRI, TLKM, BBCA, ASII, PTBA) - JSON-RPC end-to-end terverifikasi: create beli/jual -> recalculate -> portfolio (avg_cost 4525, realized 65000, unrealized 220000, nilai 3840000) - PLAN.md: rancangan disetujui Bozz - Login Odoo admin/admin (ganti password master di .env, jangan commit)
This commit is contained in:
51
addons/saat_invest/models/transaksi.py
Normal file
51
addons/saat_invest/models/transaksi.py
Normal file
@@ -0,0 +1,51 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class SaatTransaksi(models.Model):
|
||||
"""Catat setiap pembelian / penjualan saham."""
|
||||
_name = 'saat.transaksi'
|
||||
_description = "Transaksi Jual/Beli Saham"
|
||||
_order = 'tanggal desc, id desc'
|
||||
|
||||
TIPE = [('buy', 'Beli'), ('sell', 'Jual')]
|
||||
|
||||
name = fields.Char(string="Referensi", compute='_compute_name', store=True)
|
||||
tanggal = fields.Date(string="Tanggal", required=True, default=fields.Date.context_today)
|
||||
tipe = fields.Selection(TIPE, string="Tipe", required=True, default='buy')
|
||||
saham_id = fields.Many2one('saat.saham', string="Saham", required=True, ondelete='restrict')
|
||||
volume = fields.Integer(string="Jumlah Lembar", required=True)
|
||||
harga = fields.Monetary(string="Harga/Lembar", currency_field='currency_id', required=True)
|
||||
fee = fields.Monetary(string="Biaya (Komisi/PPN)", currency_field='currency_id', default=0.0)
|
||||
total = fields.Monetary(
|
||||
string="Total Biaya", currency_field='currency_id',
|
||||
compute='_compute_total', store=True)
|
||||
catatan = fields.Text(string="Catatan")
|
||||
user_id = fields.Many2one('res.users', string="Pencatat", default=lambda self: self.env.user)
|
||||
currency_id = fields.Many2one(
|
||||
'res.currency', string="Mata Uang", default=lambda self: self._default_currency())
|
||||
|
||||
@api.model
|
||||
def _default_currency(self):
|
||||
# IDR
|
||||
return self.env.ref('base.IDR', raise_if_not_found=False) or self.env.company.currency_id
|
||||
|
||||
@api.depends('tanggal', 'tipe', 'saham_id')
|
||||
def _compute_name(self):
|
||||
for r in self:
|
||||
ticker = r.saham_id.name or ''
|
||||
tipe_label = dict(self.TIPE).get(r.tipe, '')
|
||||
r.name = f"{r.tanggal or ''} {tipe_label} {ticker}"
|
||||
|
||||
@api.depends('volume', 'harga', 'fee', 'tipe')
|
||||
def _compute_total(self):
|
||||
for r in self:
|
||||
r.total = (r.volume or 0) * (r.harga or 0) + (r.fee or 0)
|
||||
|
||||
@api.constrains('volume', 'harga')
|
||||
def _check_positive(self):
|
||||
for r in self:
|
||||
if r.volume <= 0:
|
||||
raise models.ValidationError("Volume harus lebih dari 0.")
|
||||
if r.harga < 0:
|
||||
raise models.ValidationError("Harga tidak boleh negatif.")
|
||||
Reference in New Issue
Block a user