feat: Set forecasting start year to acquisition year, archive historical data only when the acquisition year changes, and prevent duplicate history entries.

main
MrWaradana 4 weeks ago
parent 8797b8cd96
commit 4ce0ad9765

@ -301,11 +301,19 @@ async def get_master_by_assetnum(
None, None,
) )
# Historical data query # Historical data query - filter to only one reference (prioritize oldest acquisition year)
oldest_ref_subquery = (
Select(EquipmentHistoricalTransactionRecords.acquisition_year_ref)
.filter(EquipmentHistoricalTransactionRecords.assetnum == assetnum)
.order_by(EquipmentHistoricalTransactionRecords.acquisition_year_ref.asc())
.limit(1)
.scalar_subquery()
)
historical_query = ( historical_query = (
Select(EquipmentHistoricalTransactionRecords) Select(EquipmentHistoricalTransactionRecords)
.join(EquipmentHistoricalTransactionRecords.equipment) .filter(EquipmentHistoricalTransactionRecords.assetnum == assetnum)
.filter(Equipment.assetnum == assetnum) .filter(EquipmentHistoricalTransactionRecords.acquisition_year_ref == oldest_ref_subquery)
.order_by(EquipmentHistoricalTransactionRecords.tahun.asc()) .order_by(EquipmentHistoricalTransactionRecords.tahun.asc())
) )
historical_result = await db_session.execute(historical_query) historical_result = await db_session.execute(historical_query)
@ -672,7 +680,7 @@ async def check_and_update_acquisition_data(db_session: DbSession, assetnum: str
if conn: if conn:
try: try:
cursor = conn.cursor() cursor = conn.cursor()
# Query the first year from wo_maximo # Query the oldest year from wo_maximo to detect the original acquisition
query = """ query = """
select DATE_PART('year', a.reportdate) AS year, a.asset_replacecost AS cost select DATE_PART('year', a.reportdate) AS year, a.asset_replacecost AS cost
from wo_maximo a from wo_maximo a
@ -720,89 +728,96 @@ async def check_and_update_acquisition_data(db_session: DbSession, assetnum: str
change_year = (eq.acquisition_year != first_year) change_year = (eq.acquisition_year != first_year)
change_cost = (first_cost is not None and eq.acquisition_cost != first_cost) change_cost = (first_cost is not None and eq.acquisition_cost != first_cost)
if change_year or change_cost: # We only archive transaction history if the acquisition year itself changed.
print(f"Acquisition change detected for {assetnum}: Year {current_acq}->{first_year}, Cost {current_acq_cost}->{first_cost}. Archiving history.") # This prevents redundant history entries for cost-only updates.
if change_year:
print(f"Acquisition year change detected for {assetnum}: {current_acq}->{first_year}. Archiving history.")
acq_year_ref = f"{current_acq}_{current_target}" acq_year_ref = f"{current_acq}_{current_target}"
# --- ARCHIVE HISTORICAL DATA --- # --- ARCHIVE HISTORICAL DATA ---
# 1. Copy old equipment master data to history # Check for existing identical archive to prevent duplicates (after calculation failures/retries)
history_ms_query = text(""" check_hist_query = text("SELECT 1 FROM lcc_ms_equipment_historical_data WHERE assetnum = :assetnum AND acquisition_year_ref = :acq_year_ref LIMIT 1")
INSERT INTO lcc_ms_equipment_historical_data ( hist_exists = (await db_session.execute(check_hist_query, {"assetnum": assetnum, "acq_year_ref": acq_year_ref})).fetchone()
id, assetnum, acquisition_year, acquisition_cost, capital_cost_record_time, design_life,
forecasting_start_year, forecasting_target_year, manhours_rate, created_at, created_by, if not hist_exists:
updated_at, updated_by, min_eac_info, harga_saat_ini, minimum_eac_seq, minimum_eac_year, # 1. Copy old equipment master data to history
minimum_eac, minimum_npv, minimum_pmt, minimum_pmt_aq_cost, minimum_is_actual, history_ms_query = text("""
efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, category_no, proportion, INSERT INTO lcc_ms_equipment_historical_data (
acquisition_year_ref id, assetnum, acquisition_year, acquisition_cost, capital_cost_record_time, design_life,
) forecasting_start_year, forecasting_target_year, manhours_rate, created_at, created_by,
SELECT updated_at, updated_by, min_eac_info, harga_saat_ini, minimum_eac_seq, minimum_eac_year,
uuid_generate_v4(), assetnum, acquisition_year, acquisition_cost, capital_cost_record_time, design_life, minimum_eac, minimum_npv, minimum_pmt, minimum_pmt_aq_cost, minimum_is_actual,
forecasting_start_year, forecasting_target_year, manhours_rate, created_at, created_by, efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, category_no, proportion,
updated_at, updated_by, min_eac_info, harga_saat_ini, minimum_eac_seq, minimum_eac_year, acquisition_year_ref
minimum_eac, minimum_npv, minimum_pmt, minimum_pmt_aq_cost, minimum_is_actual, )
efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, category_no, proportion, SELECT
:acq_year_ref uuid_generate_v4(), assetnum, acquisition_year, acquisition_cost, capital_cost_record_time, design_life,
FROM lcc_ms_equipment_data forecasting_start_year, forecasting_target_year, manhours_rate, created_at, created_by,
WHERE assetnum = :assetnum updated_at, updated_by, min_eac_info, harga_saat_ini, minimum_eac_seq, minimum_eac_year,
""") minimum_eac, minimum_npv, minimum_pmt, minimum_pmt_aq_cost, minimum_is_actual,
await db_session.execute(history_ms_query, {"acq_year_ref": acq_year_ref, "assetnum": assetnum}) efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, category_no, proportion,
:acq_year_ref
# 2. Copy old transaction data to lcc_equipment_historical_tr_data FROM lcc_ms_equipment_data
# Format: {acquisition_year}_{forecasting_target_year} WHERE assetnum = :assetnum
""")
history_tr_query = text(""" await db_session.execute(history_ms_query, {"acq_year_ref": acq_year_ref, "assetnum": assetnum})
INSERT INTO lcc_equipment_historical_tr_data (
id, assetnum, tahun, seq, is_actual, # 2. Copy old transaction data to lcc_equipment_historical_tr_data
raw_cm_interval, raw_cm_material_cost, raw_cm_labor_time, raw_cm_labor_human, history_tr_query = text("""
raw_pm_interval, raw_pm_material_cost, raw_pm_labor_time, raw_pm_labor_human, INSERT INTO lcc_equipment_historical_tr_data (
raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human, id, assetnum, tahun, seq, is_actual,
raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human, raw_cm_interval, raw_cm_material_cost, raw_cm_labor_time, raw_cm_labor_human,
raw_project_task_material_cost, "raw_loss_output_MW", raw_loss_output_price, raw_pm_interval, raw_pm_material_cost, raw_pm_labor_time, raw_pm_labor_human,
raw_operational_cost, raw_maintenance_cost, raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human,
rc_cm_material_cost, rc_cm_labor_cost, raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human,
rc_pm_material_cost, rc_pm_labor_cost, raw_project_task_material_cost, "raw_loss_output_MW", raw_loss_output_price,
rc_oh_material_cost, rc_oh_labor_cost, raw_operational_cost, raw_maintenance_cost,
rc_predictive_labor_cost, rc_cm_material_cost, rc_cm_labor_cost,
rc_project_material_cost, rc_lost_cost, rc_operation_cost, rc_maintenance_cost, rc_pm_material_cost, rc_pm_labor_cost,
rc_total_cost, rc_oh_material_cost, rc_oh_labor_cost,
eac_npv, eac_annual_mnt_cost, eac_annual_acq_cost, eac_disposal_cost, eac_eac, rc_predictive_labor_cost,
efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, rc_project_material_cost, rc_lost_cost, rc_operation_cost, rc_maintenance_cost,
created_by, created_at, acquisition_year_ref rc_total_cost,
) eac_npv, eac_annual_mnt_cost, eac_annual_acq_cost, eac_disposal_cost, eac_eac,
SELECT efdh_equivalent_forced_derated_hours, foh_forced_outage_hours,
uuid_generate_v4(), assetnum, tahun, seq, is_actual, created_by, created_at, acquisition_year_ref
raw_cm_interval, raw_cm_material_cost, raw_cm_labor_time, raw_cm_labor_human, )
raw_pm_interval, raw_pm_material_cost, raw_pm_labor_time, raw_pm_labor_human, SELECT
raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human, uuid_generate_v4(), assetnum, tahun, seq, is_actual,
raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human, raw_cm_interval, raw_cm_material_cost, raw_cm_labor_time, raw_cm_labor_human,
raw_project_task_material_cost, "raw_loss_output_MW", raw_loss_output_price, raw_pm_interval, raw_pm_material_cost, raw_pm_labor_time, raw_pm_labor_human,
raw_operational_cost, raw_maintenance_cost, raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human,
rc_cm_material_cost, rc_cm_labor_cost, raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human,
rc_pm_material_cost, rc_pm_labor_cost, raw_project_task_material_cost, "raw_loss_output_MW", raw_loss_output_price,
rc_oh_material_cost, rc_oh_labor_cost, raw_operational_cost, raw_maintenance_cost,
rc_predictive_labor_cost, rc_cm_material_cost, rc_cm_labor_cost,
rc_project_material_cost, rc_lost_cost, rc_operation_cost, rc_maintenance_cost, rc_pm_material_cost, rc_pm_labor_cost,
rc_total_cost, rc_oh_material_cost, rc_oh_labor_cost,
eac_npv, eac_annual_mnt_cost, eac_annual_acq_cost, eac_disposal_cost, eac_eac, rc_predictive_labor_cost,
efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, rc_project_material_cost, rc_lost_cost, rc_operation_cost, rc_maintenance_cost,
created_by, NOW(), :acq_year_ref rc_total_cost,
FROM lcc_equipment_tr_data eac_npv, eac_annual_mnt_cost, eac_annual_acq_cost, eac_disposal_cost, eac_eac,
WHERE assetnum = :assetnum efdh_equivalent_forced_derated_hours, foh_forced_outage_hours,
""") created_by, NOW(), :acq_year_ref
await db_session.execute(history_tr_query, {"acq_year_ref": acq_year_ref, "assetnum": assetnum}) FROM lcc_equipment_tr_data
WHERE assetnum = :assetnum
""")
await db_session.execute(history_tr_query, {"acq_year_ref": acq_year_ref, "assetnum": assetnum})
# 3. Delete old data # 3. Delete old data
del_query = text("DELETE FROM lcc_equipment_tr_data WHERE assetnum = :assetnum") del_query = text("DELETE FROM lcc_equipment_tr_data WHERE assetnum = :assetnum")
await db_session.execute(del_query, {"assetnum": assetnum}) await db_session.execute(del_query, {"assetnum": assetnum})
# Update Equipment Master # Update Equipment Master regardless of if archive was needed/skipped
if change_year or change_cost:
if first_cost is not None and eq.acquisition_cost != first_cost: if first_cost is not None and eq.acquisition_cost != first_cost:
eq.acquisition_cost = first_cost eq.acquisition_cost = first_cost
if eq.acquisition_year != first_year: if eq.acquisition_year != first_year:
eq.acquisition_year = first_year eq.acquisition_year = first_year
eq.forecasting_start_year = first_year # Align start with acquisition
if is_valid_default and current_life: if is_valid_default and current_life:
eq.forecasting_target_year = first_year + current_life eq.forecasting_target_year = first_year + current_life

@ -993,7 +993,7 @@ async def query_data(target_assetnum: str = None):
if acquisition_year: if acquisition_year:
# Remove data before acquisition_year # Remove data before acquisition_year
cursor.execute("DELETE FROM lcc_equipment_tr_data WHERE assetnum = %s AND tahun < %s", (assetnum, acquisition_year)) cursor.execute("DELETE FROM lcc_equipment_tr_data WHERE assetnum = %s AND tahun < %s", (assetnum, acquisition_year))
forecasting_start_year = acquisition_year - 1 forecasting_start_year = acquisition_year
elif forecasting_start_year_db: elif forecasting_start_year_db:
# If no acquisition_year but forecasting_start_year defined in DB # If no acquisition_year but forecasting_start_year defined in DB
forecasting_start_year = forecasting_start_year_db forecasting_start_year = forecasting_start_year_db

Loading…
Cancel
Save