|
|
|
|
@ -18,9 +18,12 @@ class Eac:
|
|
|
|
|
try:
|
|
|
|
|
# Mendapatkan koneksi dari config.py
|
|
|
|
|
connections = get_connection()
|
|
|
|
|
connection = (
|
|
|
|
|
connections[0] if isinstance(connections, tuple) else connections
|
|
|
|
|
)
|
|
|
|
|
if isinstance(connections, tuple):
|
|
|
|
|
connection, connection_wo_db = connections
|
|
|
|
|
else:
|
|
|
|
|
connection = connections
|
|
|
|
|
connection_wo_db = None
|
|
|
|
|
|
|
|
|
|
if connection is None:
|
|
|
|
|
print("Database connection failed.")
|
|
|
|
|
return None
|
|
|
|
|
@ -28,40 +31,65 @@ class Eac:
|
|
|
|
|
# Membuat cursor menggunakan DictCursor
|
|
|
|
|
cursor = connection.cursor(cursor_factory=DictCursor)
|
|
|
|
|
|
|
|
|
|
# Query untuk mendapatkan data2 dasar
|
|
|
|
|
# Query untuk mendapatkan data2 dasar dari LCCA DB
|
|
|
|
|
query_inflation_rate = """
|
|
|
|
|
select
|
|
|
|
|
(SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'inflation_rate') as inflation_rate
|
|
|
|
|
, (SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'discount_rate') as discount_rate
|
|
|
|
|
, (select COALESCE(rc_total_cost,0) from lcc_equipment_tr_data ltd where assetnum = %s and seq = 0) as rc_total_cost_0
|
|
|
|
|
, (SELECT acquisition_cost FROM lcc_ms_equipment_data WHERE assetnum = %s) as rc_total_cost_0
|
|
|
|
|
, (SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'history_inflation_rate') as history_inflation_rate
|
|
|
|
|
, (SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'history_future_inflation_rate_annual') as history_future_inflation_rate_annual
|
|
|
|
|
, (SELECT acquisition_year FROM lcc_ms_equipment_data WHERE assetnum = %s) as acquisition_year_fallback
|
|
|
|
|
;
|
|
|
|
|
"""
|
|
|
|
|
cursor.execute(query_inflation_rate, (equipment_id,))
|
|
|
|
|
cursor.execute(query_inflation_rate, (equipment_id, equipment_id))
|
|
|
|
|
inflation_rate_result = cursor.fetchone()
|
|
|
|
|
|
|
|
|
|
if not inflation_rate_result:
|
|
|
|
|
print("Inflation rate tidak ditemukan.")
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
# Fetch acquisition year from Maximo/Production DB if available
|
|
|
|
|
acquisition_year_maximo = None
|
|
|
|
|
if connection_wo_db:
|
|
|
|
|
try:
|
|
|
|
|
cursor_wo = connection_wo_db.cursor()
|
|
|
|
|
query_maximo = """
|
|
|
|
|
SELECT CAST(DATE_PART('year', max(reportdate)) AS INTEGER)
|
|
|
|
|
FROM wo_maximo wm
|
|
|
|
|
WHERE wm.asset_assetnum = %s AND wm.asset_replacecost > 0
|
|
|
|
|
GROUP BY CAST(DATE_PART('year', reportdate) AS INTEGER)
|
|
|
|
|
ORDER BY CAST(DATE_PART('year', reportdate) AS INTEGER) ASC
|
|
|
|
|
LIMIT 1
|
|
|
|
|
"""
|
|
|
|
|
cursor_wo.execute(query_maximo, (equipment_id,))
|
|
|
|
|
res_wo = cursor_wo.fetchone()
|
|
|
|
|
if res_wo and res_wo[0]:
|
|
|
|
|
acquisition_year_maximo = res_wo[0]
|
|
|
|
|
cursor_wo.close()
|
|
|
|
|
except Exception as e:
|
|
|
|
|
print(f"Error fetching from maximo DB: {e}")
|
|
|
|
|
|
|
|
|
|
# Use Maximo year if available, else fallback
|
|
|
|
|
acquisition_year = acquisition_year_maximo if acquisition_year_maximo else inflation_rate_result["acquisition_year_fallback"]
|
|
|
|
|
inflation_rate = inflation_rate_result["inflation_rate"]
|
|
|
|
|
history_inflation_rate = inflation_rate_result["history_inflation_rate"]
|
|
|
|
|
history_future_inflation_rate = inflation_rate_result["history_future_inflation_rate_annual"]
|
|
|
|
|
disc_rate = inflation_rate_result["discount_rate"]
|
|
|
|
|
rc_total_cost_0 = inflation_rate_result["rc_total_cost_0"]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
last_seq = 0
|
|
|
|
|
last_npv = 0
|
|
|
|
|
# Query untuk mendapatkan data dengan seq dan is_actual
|
|
|
|
|
query_data_actual = """
|
|
|
|
|
SELECT assetnum, tahun, seq, is_actual, rc_total_cost
|
|
|
|
|
FROM lcc_equipment_tr_data
|
|
|
|
|
WHERE is_actual = 1 AND seq != 0
|
|
|
|
|
WHERE is_actual = 1 AND seq != 0 AND tahun >= %s
|
|
|
|
|
AND assetnum = %s
|
|
|
|
|
ORDER BY seq;
|
|
|
|
|
"""
|
|
|
|
|
cursor.execute(query_data_actual, (equipment_id,))
|
|
|
|
|
cursor.execute(query_data_actual, (acquisition_year, equipment_id))
|
|
|
|
|
data_actual = cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
# Variabel untuk menyimpan hasil NPV dan PMT per baris
|
|
|
|
|
@ -77,24 +105,36 @@ class Eac:
|
|
|
|
|
value / ((1 + history_inflation_rate) ** (i + 1))
|
|
|
|
|
for i, value in enumerate(cumulative_values)
|
|
|
|
|
)
|
|
|
|
|
# Recalculate seq based on new acquisition year
|
|
|
|
|
current_seq = row["tahun"] - acquisition_year
|
|
|
|
|
|
|
|
|
|
if current_seq <= 0:
|
|
|
|
|
current_seq = 0 # Avoid negative or zero periods for PMT if year <= acquisition_year
|
|
|
|
|
|
|
|
|
|
# Menghitung PMT biaya maintenance
|
|
|
|
|
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n – 1]
|
|
|
|
|
# dimana PV = final_value, r = history_future_inflation_rate, n = row["seq"]
|
|
|
|
|
pmt_mnt_cost = -npf.pmt(history_future_inflation_rate, row["seq"], final_value)
|
|
|
|
|
# dimana PV = final_value, r = history_future_inflation_rate, n = current_seq
|
|
|
|
|
if current_seq > 0:
|
|
|
|
|
pmt_mnt_cost = -npf.pmt(history_future_inflation_rate, current_seq, final_value)
|
|
|
|
|
else:
|
|
|
|
|
pmt_mnt_cost = 0.0
|
|
|
|
|
|
|
|
|
|
# Menghitung PMT biaya disposal
|
|
|
|
|
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n – 1]
|
|
|
|
|
# dimana PV = 0.05 * rc_total_cost_0, r = disc_rate, n = row["seq"]
|
|
|
|
|
# rc_total_cost_0 adalah biaya akuisisi awal (seq = 0)
|
|
|
|
|
eac_disposal_cost = -npf.pmt(disc_rate, row["seq"], 0, 0.05 * rc_total_cost_0) if row["seq"] > 0 else 0.0
|
|
|
|
|
# dimana PV = 0.05 * rc_total_cost_0, r = disc_rate, n = current_seq
|
|
|
|
|
# rc_total_cost_0 adalah biaya akuisisi awal
|
|
|
|
|
eac_disposal_cost = -npf.pmt(disc_rate, current_seq, 0, 0.05 * rc_total_cost_0) if current_seq > 0 else 0.0
|
|
|
|
|
|
|
|
|
|
# Menghitung PMT biaya akuisisi
|
|
|
|
|
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n – 1]
|
|
|
|
|
# dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"]
|
|
|
|
|
# rc_total_cost_0 adalah biaya akuisisi awal (seq = 0)
|
|
|
|
|
# dimana PV = rc_total_cost_0, r = disc_rate, n = current_seq
|
|
|
|
|
# rc_total_cost_0 adalah biaya akuisisi awal
|
|
|
|
|
# disc_rate adalah discount rate dari database
|
|
|
|
|
# row["seq"] adalah periode ke-n
|
|
|
|
|
pmt_aq_cost = -npf.pmt(disc_rate, row["seq"], rc_total_cost_0)
|
|
|
|
|
# current_seq adalah periode ke-n
|
|
|
|
|
if current_seq > 0:
|
|
|
|
|
pmt_aq_cost = -npf.pmt(disc_rate, current_seq, rc_total_cost_0)
|
|
|
|
|
else:
|
|
|
|
|
pmt_aq_cost = 0.0
|
|
|
|
|
eac = pmt_mnt_cost + pmt_aq_cost
|
|
|
|
|
|
|
|
|
|
npv_results.append(
|
|
|
|
|
@ -129,21 +169,24 @@ class Eac:
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
last_seq = row["seq"]
|
|
|
|
|
last_seq = row["seq"] # Keep logical linking if needed, but calculations rely on current_seq
|
|
|
|
|
last_npv = float(final_value)
|
|
|
|
|
|
|
|
|
|
# Commit perubahan
|
|
|
|
|
connection.commit()
|
|
|
|
|
|
|
|
|
|
# Query untuk mendapatkan data dengan seq dan is_actual = 0
|
|
|
|
|
# Filter by acquisition_year_ref to match current acquisition_year
|
|
|
|
|
query_data_proyeksi = """
|
|
|
|
|
SELECT assetnum, tahun, seq, is_actual, rc_total_cost
|
|
|
|
|
FROM lcc_equipment_tr_data
|
|
|
|
|
WHERE assetnum = %s AND is_actual = 0
|
|
|
|
|
AND (acquisition_year_ref = %s OR acquisition_year_ref IS NULL)
|
|
|
|
|
ORDER BY seq;
|
|
|
|
|
"""
|
|
|
|
|
cursor.execute(query_data_proyeksi, (equipment_id,))
|
|
|
|
|
cursor.execute(query_data_proyeksi, (equipment_id, acquisition_year))
|
|
|
|
|
data_proyeksi = cursor.fetchall()
|
|
|
|
|
|
|
|
|
|
cumulative_values = []
|
|
|
|
|
|
|
|
|
|
# Menghitung NPV dan PMT secara bertahap untuk data proyeksi
|
|
|
|
|
@ -170,37 +213,43 @@ class Eac:
|
|
|
|
|
# Total NPV pada titik proyeksi ini = NPV aktual terakhir + biaya proyeksi yang didiskontokan
|
|
|
|
|
final_value = float(last_npv) + float(discounted_proj)
|
|
|
|
|
|
|
|
|
|
# Recalculate seq based on new acquisition year
|
|
|
|
|
current_seq = row["tahun"] - acquisition_year
|
|
|
|
|
if current_seq <= 0:
|
|
|
|
|
current_seq = 1 # Fallback, though for projection it should be > 0
|
|
|
|
|
|
|
|
|
|
# Gunakan seq penuh (jumlah periode dari akuisisi) untuk menghitung pembayaran tahunan pemeliharaan.
|
|
|
|
|
# Menggunakan hanya selisih dari seq aktual terakhir
|
|
|
|
|
# (sisa_periode) mengamortisasi seluruh nilai sekarang selama
|
|
|
|
|
# sejumlah periode yang sangat kecil untuk proyeksi pertama dan menghasilkan lonjakan.
|
|
|
|
|
# Menggunakan row["seq"] menjaga periode amortisasi konsisten dengan perhitungan lain
|
|
|
|
|
# Menggunakan current_seq menjaga periode amortisasi konsisten dengan perhitungan lain
|
|
|
|
|
# dan mencegah lonjakan setelah tahun berjalan.
|
|
|
|
|
# amortisasi adalah proses pencatatan biaya aset selama masa manfaatnya.
|
|
|
|
|
periods = int(row["seq"]) if int(row.get("seq", 0)) > 0 else 1
|
|
|
|
|
periods = int(current_seq)
|
|
|
|
|
|
|
|
|
|
# Menghitung PMT
|
|
|
|
|
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n – 1]
|
|
|
|
|
# dimana PV = final_value, r = disc_rate, n = row["seq"]
|
|
|
|
|
# dimana PV = final_value, r = disc_rate, n = current_seq
|
|
|
|
|
# periods adalah jumlah periode
|
|
|
|
|
# final_value adalah PV pada titik proyeksi periods
|
|
|
|
|
pmt_mnt_cost = -float(npf.pmt(disc_rate, periods, final_value))
|
|
|
|
|
|
|
|
|
|
eac_disposal_cost_proyeksi = -npf.pmt(disc_rate, row["seq"], 0, 0.05 * rc_total_cost_0) if row["seq"] > 0 else 0.0
|
|
|
|
|
eac_disposal_cost_proyeksi = -npf.pmt(disc_rate, current_seq, 0, 0.05 * rc_total_cost_0) if current_seq > 0 else 0.0
|
|
|
|
|
|
|
|
|
|
# menghitung PMT biaya akuisisi
|
|
|
|
|
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n – 1]
|
|
|
|
|
# dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"]
|
|
|
|
|
# rc_total_cost_0 adalah biaya akuisisi awal (seq = 0)
|
|
|
|
|
# dimana PV = rc_total_cost_0, r = disc_rate, n = current_seq
|
|
|
|
|
# rc_total_cost_0 adalah biaya akuisisi awal
|
|
|
|
|
# disc_rate adalah discount rate dari database
|
|
|
|
|
# row["seq"] adalah periode ke-n
|
|
|
|
|
pmt_aq_cost = -float(npf.pmt(disc_rate, row["seq"], rc_total_cost_0))
|
|
|
|
|
# current_seq adalah periode ke-n
|
|
|
|
|
pmt_aq_cost = -float(npf.pmt(disc_rate, current_seq, rc_total_cost_0))
|
|
|
|
|
|
|
|
|
|
eac = float(pmt_mnt_cost) + float(pmt_aq_cost)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
npv_results.append(
|
|
|
|
|
{
|
|
|
|
|
"seq": row["seq"],
|
|
|
|
|
"seq": current_seq,
|
|
|
|
|
"year": row["tahun"],
|
|
|
|
|
"npv": final_value,
|
|
|
|
|
"pmt": pmt_mnt_cost,
|
|
|
|
|
@ -279,6 +328,7 @@ class Eac:
|
|
|
|
|
}
|
|
|
|
|
else:
|
|
|
|
|
positives = [r for r in rslt if float(r.get("eac", 0)) > 0]
|
|
|
|
|
|
|
|
|
|
if positives:
|
|
|
|
|
lowest_eac_record = min(positives, key=lambda x: float(x["eac"]))
|
|
|
|
|
else:
|
|
|
|
|
|