|
|
|
@ -1,14 +1,19 @@
|
|
|
|
import os
|
|
|
|
import os
|
|
|
|
import sys
|
|
|
|
import sys
|
|
|
|
|
|
|
|
|
|
|
|
# Tambah path ke config.py (seperti di kode-kode kamu sebelumnya)
|
|
|
|
# Tambah path ke config.py (seperti di kode-kode kamu sebelumnya)
|
|
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
|
|
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), '..')))
|
|
|
|
from config import get_connection # harus mengembalikan koneksi psycopg2
|
|
|
|
from config import get_connection # harus mengembalikan koneksi psycopg2
|
|
|
|
from math import pow
|
|
|
|
from math import pow
|
|
|
|
import numpy_financial as npf
|
|
|
|
import numpy_financial as npf
|
|
|
|
|
|
|
|
import math
|
|
|
|
|
|
|
|
import uuid
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def validate_number(n):
|
|
|
|
def validate_number(n):
|
|
|
|
return n if n is not None else 0
|
|
|
|
return n if n is not None else 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def cumulative_npv(values, rate, initial_cf0=0.0):
|
|
|
|
def cumulative_npv(values, rate, initial_cf0=0.0):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Penggunaan:
|
|
|
|
Penggunaan:
|
|
|
|
@ -24,6 +29,8 @@ def cumulative_npv(values, rate, initial_cf0=0.0):
|
|
|
|
cumulative_results.append(initial_cf0 + running_npv)
|
|
|
|
cumulative_results.append(initial_cf0 + running_npv)
|
|
|
|
|
|
|
|
|
|
|
|
return cumulative_results
|
|
|
|
return cumulative_results
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def pmt_excel_style(rate, periods, pv):
|
|
|
|
def pmt_excel_style(rate, periods, pv):
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
Fungsi ini menghasilkan nilai setara Excel:
|
|
|
|
Fungsi ini menghasilkan nilai setara Excel:
|
|
|
|
@ -46,12 +53,17 @@ def pmt_excel_style(rate, periods, pv):
|
|
|
|
|
|
|
|
|
|
|
|
# Excel memberi hasil negatif, tapi rumusmu pakai -PMT, maka hasilnya positif
|
|
|
|
# Excel memberi hasil negatif, tapi rumusmu pakai -PMT, maka hasilnya positif
|
|
|
|
return abs(payment)
|
|
|
|
return abs(payment)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def hitung_pv(rate, nper, fv):
|
|
|
|
def hitung_pv(rate, nper, fv):
|
|
|
|
pv = npf.pv(rate, nper, pmt=0, fv=fv)
|
|
|
|
pv = npf.pv(rate, nper, pmt=0, fv=fv)
|
|
|
|
return -pv
|
|
|
|
return -pv
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def hitung_irr(cashflows: list):
|
|
|
|
def hitung_irr(cashflows: list):
|
|
|
|
return npf.irr(cashflows)
|
|
|
|
return npf.irr(cashflows)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def main():
|
|
|
|
def main():
|
|
|
|
conn = get_connection()
|
|
|
|
conn = get_connection()
|
|
|
|
if conn is None:
|
|
|
|
if conn is None:
|
|
|
|
@ -59,14 +71,90 @@ def main():
|
|
|
|
sys.exit(1)
|
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
try:
|
|
|
|
|
|
|
|
# ### LOCKING: pastikan transaksi manual (non-autocommit)
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
conn.autocommit = False
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
# Kalau driver tidak punya autocommit, abaikan
|
|
|
|
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
|
|
cur = conn.cursor()
|
|
|
|
cur = conn.cursor()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ### LOCKING: kunci tabel lcc_plant_tr_data
|
|
|
|
|
|
|
|
# Mode SHARE ROW EXCLUSIVE:
|
|
|
|
|
|
|
|
# - Menghalangi INSERT/UPDATE/DELETE di tabel ini
|
|
|
|
|
|
|
|
# - Menghalangi lock SHARE ROW EXCLUSIVE lain → script ngantri satu per satu
|
|
|
|
|
|
|
|
cur.execute("LOCK TABLE lcc_plant_tr_data IN SHARE ROW EXCLUSIVE MODE")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 0 Mendapatkan master parameter dari tabel lcc_ms_master
|
|
|
|
|
|
|
|
cur.execute("""
|
|
|
|
|
|
|
|
SELECT name,
|
|
|
|
|
|
|
|
value_num AS value
|
|
|
|
|
|
|
|
FROM lcc_ms_master
|
|
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
param_rows = cur.fetchall()
|
|
|
|
|
|
|
|
param_map = {name: val for (name, val) in param_rows}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# helper biar aman
|
|
|
|
|
|
|
|
def get_param(name, default=0.0):
|
|
|
|
|
|
|
|
v = param_map.get(name, default)
|
|
|
|
|
|
|
|
return float(v) if v is not None else float(default)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# 0-1 Generate New data Projection (is_actual=0) if not exist
|
|
|
|
|
|
|
|
# Hapus data projection lama (is_actual = 0)
|
|
|
|
|
|
|
|
cur.execute("""
|
|
|
|
|
|
|
|
DELETE
|
|
|
|
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
|
|
|
|
WHERE is_actual = 0
|
|
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Hitung kebutuhan jumlah baris projection baru agar total (actual + projection)
|
|
|
|
|
|
|
|
# sama dengan parameter umur_teknis
|
|
|
|
|
|
|
|
cur.execute("""
|
|
|
|
|
|
|
|
SELECT COALESCE(COUNT(*), 0)
|
|
|
|
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
|
|
|
|
WHERE is_actual = 1
|
|
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
count_actual = cur.fetchone()[0] if cur.rowcount != -1 else 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
umur_teknis = int(get_param("umur_teknis"))
|
|
|
|
|
|
|
|
proj_needed = max(0, umur_teknis - int(count_actual))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Ambil seq dan tahun terakhir sebagai titik awal penomoran berikutnya
|
|
|
|
|
|
|
|
cur.execute("SELECT COALESCE(MAX(seq), 0) FROM lcc_plant_tr_data")
|
|
|
|
|
|
|
|
last_seq = int(cur.fetchone()[0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cur.execute("SELECT COALESCE(MAX(tahun), 0) FROM lcc_plant_tr_data")
|
|
|
|
|
|
|
|
last_year = int(cur.fetchone()[0])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Jika belum ada tahun sama sekali, gunakan tahun_cod-1 sebagai dasar
|
|
|
|
|
|
|
|
if last_year == 0:
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
|
|
|
last_year = int(get_param("tahun_cod")) - 1
|
|
|
|
|
|
|
|
except Exception:
|
|
|
|
|
|
|
|
last_year = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if proj_needed > 0:
|
|
|
|
|
|
|
|
# Siapkan rows untuk INSERT projection baru
|
|
|
|
|
|
|
|
values = []
|
|
|
|
|
|
|
|
next_seq = last_seq + 1
|
|
|
|
|
|
|
|
next_year = last_year + 1
|
|
|
|
|
|
|
|
for _ in range(proj_needed):
|
|
|
|
|
|
|
|
values.append((str(uuid.uuid4()), next_seq, next_year))
|
|
|
|
|
|
|
|
next_seq += 1
|
|
|
|
|
|
|
|
next_year += 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
insert_sql = (
|
|
|
|
|
|
|
|
"INSERT INTO lcc_plant_tr_data (id, seq, tahun, is_actual, created_at, created_by) "
|
|
|
|
|
|
|
|
"VALUES (%s, %s, %s, 0, CURRENT_TIMESTAMP, 'SYS')"
|
|
|
|
|
|
|
|
)
|
|
|
|
|
|
|
|
cur.executemany(insert_sql, values)
|
|
|
|
|
|
|
|
|
|
|
|
# 1. Ambil data awal
|
|
|
|
# 1. Ambil data awal
|
|
|
|
select_sql = """
|
|
|
|
select_sql = """
|
|
|
|
SELECT
|
|
|
|
SELECT *
|
|
|
|
*
|
|
|
|
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
FROM lcc_plant_tr_data
|
|
|
|
ORDER BY seq
|
|
|
|
ORDER BY seq \
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
cur.execute(select_sql)
|
|
|
|
cur.execute(select_sql)
|
|
|
|
|
|
|
|
|
|
|
|
@ -78,8 +166,7 @@ def main():
|
|
|
|
# 2. Siapkan data untuk bulk UPDATE
|
|
|
|
# 2. Siapkan data untuk bulk UPDATE
|
|
|
|
update_sql = """
|
|
|
|
update_sql = """
|
|
|
|
UPDATE lcc_plant_tr_data
|
|
|
|
UPDATE lcc_plant_tr_data
|
|
|
|
SET
|
|
|
|
SET net_capacity_factor = %s,
|
|
|
|
net_capacity_factor = %s,
|
|
|
|
|
|
|
|
eaf = %s,
|
|
|
|
eaf = %s,
|
|
|
|
production_bruto = %s,
|
|
|
|
production_bruto = %s,
|
|
|
|
production_netto = %s,
|
|
|
|
production_netto = %s,
|
|
|
|
@ -145,22 +232,9 @@ def main():
|
|
|
|
chart_capex_biaya_investasi_tambahan = %s,
|
|
|
|
chart_capex_biaya_investasi_tambahan = %s,
|
|
|
|
chart_capex_acquisition_cost = %s,
|
|
|
|
chart_capex_acquisition_cost = %s,
|
|
|
|
chart_capex_annualized = %s
|
|
|
|
chart_capex_annualized = %s
|
|
|
|
WHERE seq = %s
|
|
|
|
WHERE seq = %s \
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
cur.execute("""
|
|
|
|
|
|
|
|
SELECT name,
|
|
|
|
|
|
|
|
value_num AS value
|
|
|
|
|
|
|
|
FROM lcc_ms_master
|
|
|
|
|
|
|
|
""")
|
|
|
|
|
|
|
|
param_rows = cur.fetchall()
|
|
|
|
|
|
|
|
param_map = {name: val for (name, val) in param_rows}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# helper biar aman
|
|
|
|
|
|
|
|
def get_param(name, default=0.0):
|
|
|
|
|
|
|
|
v = param_map.get(name, default)
|
|
|
|
|
|
|
|
return float(v) if v is not None else float(default)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Ambil parameter dari tabel (fungsi get_param sudah kamu buat sebelumnya)
|
|
|
|
# Ambil parameter dari tabel (fungsi get_param sudah kamu buat sebelumnya)
|
|
|
|
discount_rate = get_param("discount_rate") / 100
|
|
|
|
discount_rate = get_param("discount_rate") / 100
|
|
|
|
total_project_cost = get_param("total_project_cost")
|
|
|
|
total_project_cost = get_param("total_project_cost")
|
|
|
|
@ -255,7 +329,6 @@ def main():
|
|
|
|
cost_bd_pm_nonmi = cost_bd_pm_nonmi # last value
|
|
|
|
cost_bd_pm_nonmi = cost_bd_pm_nonmi # last value
|
|
|
|
cost_bd_bd = cost_bd_bd # last value
|
|
|
|
cost_bd_bd = cost_bd_bd # last value
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# ++++++ REVENUE +++++++
|
|
|
|
# ++++++ REVENUE +++++++
|
|
|
|
revenue_total = revenue_a + revenue_b + revenue_c + revenue_d
|
|
|
|
revenue_total = revenue_a + revenue_b + revenue_c + revenue_d
|
|
|
|
if seq > 0:
|
|
|
|
if seq > 0:
|
|
|
|
@ -491,7 +564,8 @@ def main():
|
|
|
|
calc4_free_cash_flow_on_equity_array[0]
|
|
|
|
calc4_free_cash_flow_on_equity_array[0]
|
|
|
|
|
|
|
|
|
|
|
|
ROA_ALL = sum(calc2_earning_after_tax_array) / sum(total_residual_value_array) * 100 # dalam %
|
|
|
|
ROA_ALL = sum(calc2_earning_after_tax_array) / sum(total_residual_value_array) * 100 # dalam %
|
|
|
|
ROA_TO_L = sum(calc2_earning_after_tax_array_sampai_sekarang) / sum(total_residual_value_array_sampai_sekarang) * 100 # dalam %
|
|
|
|
ROA_TO_L = sum(calc2_earning_after_tax_array_sampai_sekarang) / sum(
|
|
|
|
|
|
|
|
total_residual_value_array_sampai_sekarang) * 100 # dalam %
|
|
|
|
|
|
|
|
|
|
|
|
update_kpi_sql = """
|
|
|
|
update_kpi_sql = """
|
|
|
|
UPDATE lcc_ms_master
|
|
|
|
UPDATE lcc_ms_master
|
|
|
|
@ -499,7 +573,7 @@ def main():
|
|
|
|
WHERE name = %s \
|
|
|
|
WHERE name = %s \
|
|
|
|
"""
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
kpi_params = [
|
|
|
|
kpi_params_raw = [
|
|
|
|
(IRR_ON_EQUITY * 100, "calc_on_equity_irr"),
|
|
|
|
(IRR_ON_EQUITY * 100, "calc_on_equity_irr"),
|
|
|
|
(NPV_ON_EQUITY, "calc_on_equity_npv"),
|
|
|
|
(NPV_ON_EQUITY, "calc_on_equity_npv"),
|
|
|
|
(IRR_ON_PROJECT * 100, "calc_on_project_irr"),
|
|
|
|
(IRR_ON_PROJECT * 100, "calc_on_project_irr"),
|
|
|
|
@ -508,6 +582,11 @@ def main():
|
|
|
|
(ROA_TO_L, "calc_roa_current"),
|
|
|
|
(ROA_TO_L, "calc_roa_current"),
|
|
|
|
]
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
kpi_params = [
|
|
|
|
|
|
|
|
(None if (value is None or isinstance(value, float) and math.isnan(value)) else value, key)
|
|
|
|
|
|
|
|
for value, key in kpi_params_raw
|
|
|
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
|
|
cur.executemany(update_kpi_sql, kpi_params)
|
|
|
|
cur.executemany(update_kpi_sql, kpi_params)
|
|
|
|
conn.commit()
|
|
|
|
conn.commit()
|
|
|
|
# ===========================================================================
|
|
|
|
# ===========================================================================
|
|
|
|
|