remove the acquisition_year_ref

rest-api
MrWaradana 1 month ago
parent 10cab6d4be
commit 9f9238c088

@ -60,7 +60,6 @@ class EquipmentTransactionRecords(Base, DefaultMixin, IdentityMixin):
tahun = Column(Integer, nullable=False) tahun = Column(Integer, nullable=False)
seq = Column(Integer, nullable=False) seq = Column(Integer, nullable=False)
is_actual = Column(Integer, nullable=False) is_actual = Column(Integer, nullable=False)
acquisition_year_ref = Column(Integer, nullable=True)
raw_cm_interval = Column(Float, nullable=False) raw_cm_interval = Column(Float, nullable=False)
raw_cm_material_cost = Column(Float, nullable=False) raw_cm_material_cost = Column(Float, nullable=False)
raw_cm_labor_time = Column(Float, nullable=False) raw_cm_labor_time = Column(Float, nullable=False)

@ -18,12 +18,9 @@ class Eac:
try: try:
# Mendapatkan koneksi dari config.py # Mendapatkan koneksi dari config.py
connections = get_connection() connections = get_connection()
if isinstance(connections, tuple): connection = (
connection, connection_wo_db = connections connections[0] if isinstance(connections, tuple) else connections
else: )
connection = connections
connection_wo_db = None
if connection is None: if connection is None:
print("Database connection failed.") print("Database connection failed.")
return None return None
@ -31,15 +28,15 @@ class Eac:
# Membuat cursor menggunakan DictCursor # Membuat cursor menggunakan DictCursor
cursor = connection.cursor(cursor_factory=DictCursor) cursor = connection.cursor(cursor_factory=DictCursor)
# Query untuk mendapatkan data2 dasar dari LCCA DB # Query untuk mendapatkan data2 dasar
query_inflation_rate = """ query_inflation_rate = """
select 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 = 'inflation_rate') as inflation_rate
, (SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'discount_rate') as discount_rate , (SELECT value_num / 100 FROM lcc_ms_master WHERE name = 'discount_rate') as discount_rate
, (SELECT acquisition_cost FROM lcc_ms_equipment_data WHERE assetnum = %s) as rc_total_cost_0 , (select COALESCE(rc_total_cost,0) from lcc_equipment_tr_data ltd where assetnum = %s and seq = 0) 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_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 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 , (select COALESCE(forecasting_target_year, 2056) from lcc_ms_equipment_data where assetnum = %s) as forecasting_target_year
; ;
""" """
cursor.execute(query_inflation_rate, (equipment_id, equipment_id)) cursor.execute(query_inflation_rate, (equipment_id, equipment_id))
@ -48,36 +45,13 @@ class Eac:
if not inflation_rate_result: if not inflation_rate_result:
print("Inflation rate tidak ditemukan.") print("Inflation rate tidak ditemukan.")
return None 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"] inflation_rate = inflation_rate_result["inflation_rate"]
history_inflation_rate = inflation_rate_result["history_inflation_rate"] history_inflation_rate = inflation_rate_result["history_inflation_rate"]
history_future_inflation_rate = inflation_rate_result["history_future_inflation_rate_annual"] history_future_inflation_rate = inflation_rate_result["history_future_inflation_rate_annual"]
disc_rate = inflation_rate_result["discount_rate"] disc_rate = inflation_rate_result["discount_rate"]
rc_total_cost_0 = inflation_rate_result["rc_total_cost_0"] rc_total_cost_0 = inflation_rate_result["rc_total_cost_0"]
forecasting_target_year = inflation_rate_result["forecasting_target_year"]
last_seq = 0 last_seq = 0
last_npv = 0 last_npv = 0
@ -85,13 +59,13 @@ class Eac:
query_data_actual = """ query_data_actual = """
SELECT assetnum, tahun, seq, is_actual, rc_total_cost SELECT assetnum, tahun, seq, is_actual, rc_total_cost
FROM lcc_equipment_tr_data FROM lcc_equipment_tr_data
WHERE is_actual = 1 AND seq != 0 AND tahun >= %s WHERE is_actual = 1 AND seq != 0
AND assetnum = %s AND assetnum = %s
ORDER BY seq; ORDER BY seq;
""" """
cursor.execute(query_data_actual, (acquisition_year, equipment_id)) cursor.execute(query_data_actual, (equipment_id,))
data_actual = cursor.fetchall() data_actual = cursor.fetchall()
# Variabel untuk menyimpan hasil NPV dan PMT per baris # Variabel untuk menyimpan hasil NPV dan PMT per baris
npv_results = [] npv_results = []
cumulative_values = [] # Menyimpan nilai kumulatif hingga baris ke-n cumulative_values = [] # Menyimpan nilai kumulatif hingga baris ke-n
@ -105,36 +79,24 @@ class Eac:
value / ((1 + history_inflation_rate) ** (i + 1)) value / ((1 + history_inflation_rate) ** (i + 1))
for i, value in enumerate(cumulative_values) 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 # Menghitung PMT biaya maintenance
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1] # Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = final_value, r = history_future_inflation_rate, n = current_seq # dimana PV = final_value, r = history_future_inflation_rate, n = row["seq"]
if current_seq > 0: pmt_mnt_cost = -npf.pmt(history_future_inflation_rate, row["seq"], final_value)
pmt_mnt_cost = -npf.pmt(history_future_inflation_rate, current_seq, final_value)
else:
pmt_mnt_cost = 0.0
# Menghitung PMT biaya disposal # Menghitung PMT biaya disposal
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1] # Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = 0.05 * rc_total_cost_0, r = disc_rate, n = current_seq # dimana PV = 0.05 * rc_total_cost_0, r = disc_rate, n = row["seq"]
# rc_total_cost_0 adalah biaya akuisisi awal # rc_total_cost_0 adalah biaya akuisisi awal (seq = 0)
eac_disposal_cost = -npf.pmt(disc_rate, current_seq, 0, 0.05 * rc_total_cost_0) if current_seq > 0 else 0.0 eac_disposal_cost = -npf.pmt(disc_rate, row["seq"], 0, 0.05 * rc_total_cost_0) if row["seq"] > 0 else 0.0
# Menghitung PMT biaya akuisisi # Menghitung PMT biaya akuisisi
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1] # Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = rc_total_cost_0, r = disc_rate, n = current_seq # dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"]
# rc_total_cost_0 adalah biaya akuisisi awal # rc_total_cost_0 adalah biaya akuisisi awal (seq = 0)
# disc_rate adalah discount rate dari database # disc_rate adalah discount rate dari database
# current_seq adalah periode ke-n # row["seq"] adalah periode ke-n
if current_seq > 0: pmt_aq_cost = -npf.pmt(disc_rate, row["seq"], rc_total_cost_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 eac = pmt_mnt_cost + pmt_aq_cost
npv_results.append( npv_results.append(
@ -169,31 +131,32 @@ class Eac:
), ),
) )
last_seq = row["seq"] # Keep logical linking if needed, but calculations rely on current_seq last_seq = row["seq"]
last_npv = float(final_value) last_npv = float(final_value)
# Commit perubahan # Commit perubahan
connection.commit() connection.commit()
# Query untuk mendapatkan data dengan seq dan is_actual = 0 # Query untuk mendapatkan data dengan seq dan is_actual = 0
# Filter by acquisition_year_ref to match current acquisition_year
query_data_proyeksi = """ query_data_proyeksi = """
SELECT assetnum, tahun, seq, is_actual, rc_total_cost SELECT assetnum, tahun, seq, is_actual, rc_total_cost
FROM lcc_equipment_tr_data FROM lcc_equipment_tr_data
WHERE assetnum = %s AND is_actual = 0 WHERE assetnum = %s AND is_actual = 0 AND tahun <= %s
AND (acquisition_year_ref = %s OR acquisition_year_ref IS NULL)
ORDER BY seq; ORDER BY seq;
""" """
cursor.execute(query_data_proyeksi, (equipment_id, acquisition_year)) cursor.execute(query_data_proyeksi, (equipment_id, forecasting_target_year))
data_proyeksi = cursor.fetchall() data_proyeksi = cursor.fetchall()
cumulative_values = [] cumulative_values = []
# Menghitung NPV dan PMT secara bertahap untuk data proyeksi # Menghitung NPV dan PMT secara bertahap untuk data proyeksi
# NOTE: sebelumnya kode mencoba menggeser PV proyeksi menggunakan npf.pv + sign flips,
# yang dapat menghasilkan nilai pemeliharaan yang sangat besar (meledak). Sebaiknya hitung
# nilai diskonto dari biaya proyeksi menggunakan offset waktu yang benar (last_seq) dan
# tambahkan ke last_npv. Kemudian hitung pembayaran tahunan tingkat (PMT) selama sisa
# jumlah periode (remaining_periods). Ini menjaga nilai pemeliharaan tahunan proyeksi tetap konsisten dan mencegah lonjakan eksponensial.
for idx, row in enumerate(data_proyeksi): for idx, row in enumerate(data_proyeksi):
# Menyimpan nilai kumulatif hingga baris ke-n # Menyimpan nilai kumulatif hingga baris ke-n
cumulative_values.append(row["rc_total_cost"]) cumulative_values.append(row["rc_total_cost"])
# Nilai proyeksi yang didiskontokan menggunakan offset eksponen dari urutan aktual terakhir # Nilai proyeksi yang didiskontokan menggunakan offset eksponen dari urutan aktual terakhir
# sehingga offset tahun berlanjut dari aktual yang sudah diproses. # sehingga offset tahun berlanjut dari aktual yang sudah diproses.
# Rumus NPV: NPV = Σ [Ct / (1 + r)^t] # Rumus NPV: NPV = Σ [Ct / (1 + r)^t]
@ -208,43 +171,37 @@ class Eac:
# Total NPV pada titik proyeksi ini = NPV aktual terakhir + biaya proyeksi yang didiskontokan # Total NPV pada titik proyeksi ini = NPV aktual terakhir + biaya proyeksi yang didiskontokan
final_value = float(last_npv) + float(discounted_proj) 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. # Gunakan seq penuh (jumlah periode dari akuisisi) untuk menghitung pembayaran tahunan pemeliharaan.
# Menggunakan hanya selisih dari seq aktual terakhir # Menggunakan hanya selisih dari seq aktual terakhir
# (sisa_periode) mengamortisasi seluruh nilai sekarang selama # (sisa_periode) mengamortisasi seluruh nilai sekarang selama
# sejumlah periode yang sangat kecil untuk proyeksi pertama dan menghasilkan lonjakan. # sejumlah periode yang sangat kecil untuk proyeksi pertama dan menghasilkan lonjakan.
# Menggunakan current_seq menjaga periode amortisasi konsisten dengan perhitungan lain # Menggunakan row["seq"] menjaga periode amortisasi konsisten dengan perhitungan lain
# dan mencegah lonjakan setelah tahun berjalan. # dan mencegah lonjakan setelah tahun berjalan.
# amortisasi adalah proses pencatatan biaya aset selama masa manfaatnya. # amortisasi adalah proses pencatatan biaya aset selama masa manfaatnya.
periods = int(current_seq) periods = int(row["seq"]) if int(row.get("seq", 0)) > 0 else 1
# Menghitung PMT # Menghitung PMT
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1] # Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = final_value, r = disc_rate, n = current_seq # dimana PV = final_value, r = disc_rate, n = row["seq"]
# periods adalah jumlah periode # periods adalah jumlah periode
# final_value adalah PV pada titik proyeksi periods # final_value adalah PV pada titik proyeksi periods
pmt_mnt_cost = -float(npf.pmt(disc_rate, periods, final_value)) pmt_mnt_cost = -float(npf.pmt(disc_rate, periods, final_value))
eac_disposal_cost_proyeksi = -npf.pmt(disc_rate, current_seq, 0, 0.05 * rc_total_cost_0) if current_seq > 0 else 0.0 eac_disposal_cost_proyeksi = -npf.pmt(disc_rate, row["seq"], 0, 0.05 * rc_total_cost_0) if row["seq"] > 0 else 0.0
# menghitung PMT biaya akuisisi # menghitung PMT biaya akuisisi
# Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1] # Rumus PMT: PMT = PV * [r(1 + r)^n] / [(1 + r)^n 1]
# dimana PV = rc_total_cost_0, r = disc_rate, n = current_seq # dimana PV = rc_total_cost_0, r = disc_rate, n = row["seq"]
# rc_total_cost_0 adalah biaya akuisisi awal # rc_total_cost_0 adalah biaya akuisisi awal (seq = 0)
# disc_rate adalah discount rate dari database # disc_rate adalah discount rate dari database
# current_seq adalah periode ke-n # row["seq"] adalah periode ke-n
pmt_aq_cost = -float(npf.pmt(disc_rate, current_seq, rc_total_cost_0)) pmt_aq_cost = -float(npf.pmt(disc_rate, row["seq"], rc_total_cost_0))
eac = float(pmt_mnt_cost) + float(pmt_aq_cost) eac = float(pmt_mnt_cost) + float(pmt_aq_cost)
npv_results.append( npv_results.append(
{ {
"seq": current_seq, "seq": row["seq"],
"year": row["tahun"], "year": row["tahun"],
"npv": final_value, "npv": final_value,
"pmt": pmt_mnt_cost, "pmt": pmt_mnt_cost,
@ -323,7 +280,6 @@ class Eac:
} }
else: else:
positives = [r for r in rslt if float(r.get("eac", 0)) > 0] positives = [r for r in rslt if float(r.get("eac", 0)) > 0]
if positives: if positives:
lowest_eac_record = min(positives, key=lambda x: float(x["eac"])) lowest_eac_record = min(positives, key=lambda x: float(x["eac"]))
else: else:

@ -151,7 +151,7 @@ class Prediksi:
# connection.close() # connection.close()
# Fungsi untuk menyimpan data proyeksi ke database # Fungsi untuk menyimpan data proyeksi ke database
async def __insert_predictions_to_db(self, data, equipment_id, token, acquisition_year_ref=None): async def __insert_predictions_to_db(self, data, equipment_id, token):
try: try:
connection, connection_wo_db = get_connection() connection, connection_wo_db = get_connection()
if connection is None: if connection is None:
@ -173,7 +173,6 @@ class Prediksi:
id, id,
seq, seq,
is_actual, is_actual,
acquisition_year_ref,
tahun, assetnum, tahun, assetnum,
rc_cm_material_cost, rc_cm_material_cost,
rc_cm_labor_cost, rc_cm_labor_cost,
@ -185,7 +184,7 @@ class Prediksi:
rc_predictive_labor_cost, rc_predictive_labor_cost,
created_by, created_at created_by, created_at
) VALUES ( ) VALUES (
%s, %s, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'Sys', NOW() %s, %s, 0, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, 'Sys', NOW()
) )
""" """
@ -236,7 +235,7 @@ class Prediksi:
check_existence_query = """ check_existence_query = """
SELECT id FROM lcc_equipment_tr_data SELECT id FROM lcc_equipment_tr_data
WHERE assetnum = %s AND tahun = %s AND is_actual = 0 AND (acquisition_year_ref = %s OR (acquisition_year_ref IS NULL AND %s IS NULL)) WHERE assetnum = %s AND tahun = %s AND is_actual = 0
""" """
update_query = """ update_query = """
@ -257,10 +256,10 @@ class Prediksi:
for _, row in data.iterrows(): for _, row in data.iterrows():
# Check if data exists # Check if data exists
cursor.execute(check_existence_query, (equipment_id, int(row["year"]), acquisition_year_ref, acquisition_year_ref)) cursor.execute(check_existence_query, (equipment_id, int(row["year"])))
existing_record = cursor.fetchone() existing_record = cursor.fetchone()
if existing_record: if existing_record:
# print("Update existing record")
# Update existing record # Update existing record
record_id = existing_record[0] record_id = existing_record[0]
cursor.execute(update_query, ( cursor.execute(update_query, (
@ -281,7 +280,6 @@ class Prediksi:
( (
str(uuid4()), # id str(uuid4()), # id
int(max_seq), # seq int(max_seq), # seq
int(acquisition_year_ref) if acquisition_year_ref is not None else None, # acquisition_year_ref
int(row["year"]), int(row["year"]),
equipment_id, equipment_id,
float(row.get("rc_cm_material_cost", 0)) if not pd.isna(row.get("rc_cm_material_cost", 0)) else 0.0, float(row.get("rc_cm_material_cost", 0)) if not pd.isna(row.get("rc_cm_material_cost", 0)) else 0.0,
@ -835,7 +833,6 @@ class Prediksi:
try: try:
# Update acquisition year first # Update acquisition year first
acquisition_year_ref = await self.__update_equipment_acquisition_year(assetnum) acquisition_year_ref = await self.__update_equipment_acquisition_year(assetnum)
# Mengambil data dari database # Mengambil data dari database
df = self.__fetch_data_from_db(assetnum) df = self.__fetch_data_from_db(assetnum)
if df is None: if df is None:
@ -846,6 +843,7 @@ class Prediksi:
# Tahun proyeksi # Tahun proyeksi
future_years = list(range(df["year"].max() + 1, par_tahun_target + 1)) future_years = list(range(df["year"].max() + 1, par_tahun_target + 1))
print("future_years", future_years)
# Hasil prediksi # Hasil prediksi
predictions = {"year": future_years} predictions = {"year": future_years}
@ -1090,7 +1088,7 @@ class Prediksi:
# Insert hasil prediksi ke database # Insert hasil prediksi ke database
try: try:
await self.__insert_predictions_to_db( await self.__insert_predictions_to_db(
predictions_df, assetnum, token, acquisition_year_ref predictions_df, assetnum, token
) )
except Exception as e: except Exception as e:
print(f"Error saat insert data ke database: {e}") print(f"Error saat insert data ke database: {e}")

@ -940,9 +940,14 @@ async def query_data(target_assetnum: str = None):
# Query untuk mendapatkan semua data dari tabel `lcc_ms_equipment_data` # Query untuk mendapatkan semua data dari tabel `lcc_ms_equipment_data`
# query_main = "SELECT * FROM lcc_ms_equipment_data" # query_main = "SELECT * FROM lcc_ms_equipment_data"
query_main = "SELECT DISTINCT(assetnum) FROM ms_equipment_master" query_main = """
SELECT em.assetnum, ed.forecasting_start_year, ed.acquisition_year
FROM ms_equipment_master em
LEFT JOIN lcc_ms_equipment_data ed ON em.assetnum = ed.assetnum
WHERE ed.assetnum IS NOT NULL
"""
if target_assetnum: if target_assetnum:
query_main += " WHERE assetnum = %s" query_main += " AND em.assetnum = %s"
cursor.execute(query_main, (target_assetnum,)) cursor.execute(query_main, (target_assetnum,))
else: else:
cursor.execute(query_main) cursor.execute(query_main)
@ -966,8 +971,14 @@ async def query_data(target_assetnum: str = None):
if not assetnum or str(assetnum).strip() == "": if not assetnum or str(assetnum).strip() == "":
print(f"[{idx}/{total_assets}] Skipping empty assetnum") print(f"[{idx}/{total_assets}] Skipping empty assetnum")
continue continue
forecasting_start_year = row["forecasting_start_year"] - 1 if row["forecasting_start_year"] else 2014
# forecasting_start_year = 2014 forecasting_start_year_db = row.get("forecasting_start_year")
acquisition_year = row.get("acquisition_year")
if forecasting_start_year_db:
forecasting_start_year = forecasting_start_year_db
else:
forecasting_start_year = (acquisition_year-1) if acquisition_year else 2014
asset_start = datetime.now() asset_start = datetime.now()
processed_assets += 1 processed_assets += 1

Loading…
Cancel
Save