From 0af5cceabdf8861912bd5358c23ba00edec48001 Mon Sep 17 00:00:00 2001 From: MrWaradana Date: Wed, 4 Feb 2026 15:18:13 +0700 Subject: [PATCH] feat: Implement acquisition data change detection, historical archiving, and conditional re-insertion before simulation. --- .../__pycache__/router.cpython-311.pyc | Bin 16277 -> 16275 bytes .../__pycache__/service.cpython-311.pyc | Bin 40445 -> 40950 bytes src/equipment/router.py | 2 - src/equipment/service.py | 25 ++- .../insert_actual_data.cpython-311.pyc | Bin 61819 -> 53968 bytes src/modules/equipment/insert_actual_data.py | 189 +----------------- 6 files changed, 29 insertions(+), 187 deletions(-) diff --git a/src/equipment/__pycache__/router.cpython-311.pyc b/src/equipment/__pycache__/router.cpython-311.pyc index f8e1b4dde76db78a203205d707c58a978448277a..8fc6a3cbedf644ba825d77ac902081e8a87ee3be 100644 GIT binary patch delta 251 zcmbPQKe?W7IWI340}yOuXwE#Pw~=p(7~`JFi^W11SttJxYZhkW^k@3OfFSSk3Qe9M zzLSd;B=qCQj}Kg%gC))}t7x-&0@dLozeY{ouByB_L*@l5W7p6SE5HsVG&7%6!OpH964Gra)7^OD{ z8pkp*nr~ij`i#}UlreyDh2#eY5WOQALL-wOnHiW=t}rTpU;?T5P_X%^^#x`C|3gi2 delta 252 zcmbPSKee83IWI340}#00Xv*}{-^e#bjB(%O#bP0h?2~_pHH)%v`ZIlCK#*VA82E%I z_lfW1;sgo)`0?Wd-{wGxbIdBbte!xX_{gtuleelWZ%&hW!OGaP*CPvxK z{>HIPjFy|%nm%JSFlP*4Tp{^^0YvXehS135M`i{ll`D+OADBQYK9p=eY<+7%Q6yDk0)b@IJV+T8NXi|-#K$b*QN(ntd6{v!sf>6>3fdtleXN)(oz3J?_ zPRf{+zY8=i(SU@MA`lWksyGz+g49DRl|zICa-|+B3yEHkI2r*(LWT0yo4OThXJ_A= zH}9MG-hBJ^pR@ASbF%rRVQ3Nze*I1Nhj-S^9J=<+13&lOLuTor;x^*Ft-j=tc{)z! z=)R&#mcS+i%}LhbW@g*fiu{u*KHZaP;@}T7LgI}CSKddA1b2loqroMPzD|fA>vyj< zG_HaBX|64FlZJps60GDFR%+;A={?H;%jj7qB$eTlH^H{I#)m%+!XQp==N}@ zpBo#}$4X-$7`<^ql1NINs*j@qQLgV5vE}VD50|^dU-cBqis$OXu`J1Ot$jSd6P&EA+0-HBnt28q*3r%6LE1fdY*y(d7Z%ql}C52ZjXBo8sM<%~g_xFOq<- zHF)%#hG9HWqRt|Ai`aG!*IeHXT(62(sLggco*!Vhil4EU-HL~aTP#xM<0A7axa>K0 z8Si`ip>B+)ir6XH)mJH*z&3MA?h%Sht{-@8WdajgrUAtPv#Y*6<8ZegMj zR>~ejlC%c>se*qcR zM~fXP)XnElevkCaJI7k7{FUTLOC1sA^{FAJ+of9#qk?`5-rpC*Cwqp&|6{NrbD8CW zZr4UA_;qB_S>7pIirBGoa-3a*eIO#Nk=-Azm7AorW6_m}A4U*UYu4&C;O+yu-2XBDHZ9`d|$RP(g>`-4&K6(|d)dm=G cx8`4r7Ppa0Y8>r81Ab7O_jc7CMYp2=0QB%vFaQ7m delta 755 zcmaJ;O=uHA6rS1Lw7auE*+e%@E0LB$>f*tResib8v-n#H1_G)$t2=BZ5jb=+}@)Jg&o@3=cG$goqD`J|)GB=3?;f z))Y2fi57V^J;T+>;Be!y-Ali5-%YU34{r9-tneLndal4$=AL4Ql}l;1*33EujLq{U<@PPut$)FDAHNM7e=h?+|nV*Ee2EUf-KPjTWe*=I3JU5 dRM`l^)-(0Un1u7I;~^6E!*Iv`U(a+w`%|Z diff --git a/src/equipment/router.py b/src/equipment/router.py index c845735..262b47a 100644 --- a/src/equipment/router.py +++ b/src/equipment/router.py @@ -89,8 +89,6 @@ async def simulate_equipment(db_session: DbSession, assetnum: str): await update_initial_simulation_data(db_session=db_session, assetnum=assetnum) yield f"data: {json.dumps({'status':'completed','step':'checking update','message':'Initial data check completed'})}\n\n" except Exception as e: - # Log error but proceed? Or maybe yield an error? - # For now, we proceed as this is an enhancement check. print(f"Update simulation data failed: {e}") yield f"data: {json.dumps({'status':'error','step':'checking update','message':f'Update simulation data failed: {str(e)}'})}\n\n" diff --git a/src/equipment/service.py b/src/equipment/service.py index 7f5c44e..9c507d0 100644 --- a/src/equipment/service.py +++ b/src/equipment/service.py @@ -650,10 +650,11 @@ async def delete(*, db_session: DbSession, equipment_id: str): await db_session.commit() -async def update_initial_simulation_data(db_session: DbSession, assetnum: str): +async def check_and_update_acquisition_data(db_session: DbSession, assetnum: str) -> bool: """ - Update acquisition_year and acquisition_cost from wo_maximo and set default forecasting_target_year - before running simulation. + Check if acquisition year/cost in Maximo differs from local DB. + If changed, archive history, delete transaction data, update master, and return True. + Otherwise return False. """ conn = get_production_connection() first_year = None @@ -685,6 +686,8 @@ async def update_initial_simulation_data(db_session: DbSession, assetnum: str): except: pass + updates_performed = False + if first_year: # Fetch equipment to update eq = await get_by_assetnum(db_session=db_session, assetnum=assetnum) @@ -794,7 +797,17 @@ async def update_initial_simulation_data(db_session: DbSession, assetnum: str): eq.forecasting_target_year = first_year + current_life await db_session.commit() - # await db_session.refresh(eq) + updates_performed = True + + return updates_performed + - # Re-insert actual data using query_data - await query_data(target_assetnum=assetnum) +async def update_initial_simulation_data(db_session: DbSession, assetnum: str): + """ + Update acquisition_year and acquisition_cost from wo_maximo and set default forecasting_target_year + before running simulation. + """ + updated = await check_and_update_acquisition_data(db_session, assetnum) + if updated: + # Re-insert actual data using query_data + await query_data(target_assetnum=assetnum) diff --git a/src/modules/equipment/__pycache__/insert_actual_data.cpython-311.pyc b/src/modules/equipment/__pycache__/insert_actual_data.cpython-311.pyc index 307cb2dfea4e6888d8b6578574f7dc0d2550ea81..2a6f1651d19b83510616acc623f587fd9416b9a8 100644 GIT binary patch delta 3826 zcmc&$Yiv}<6`r|w*So%p?e)HQz3YA2>wVc?o3{&?q`aI*V{9mx24nACI}TvT^`^8i zn?-=yrlm^I{D@SFu6jda+QdvWix(0-K*d{6uLsH3Y)yJ8;w#;Qw z%bzM&b7#JD&Ueq5IrF;zxXx#P!WX|?R3spHe(~=EJ?PYtVjF&NB={~49r?tI!$+o< zY>tS|L%7W&x(=Z>uUHPXPjo}=7o(ylY3brb?;&+tAXy>$wsXvr)W)Ht`Q=K6*(3T- z1dd~b-hnswv;~vF{wg{!Y%Z%vTExl|!Q(vG;mti5fjvZ5w)z}xVX+E=MZhI`IXX)K zDMJ6);H2f84#x(=-1ztD*@0lem=U3G*A0F)@FyPMp?`SqE&l<6`u2-j(XgX*ry*^` z2(WR7ai=M5!l)2&h?wYF-c?}+WF%%{I1XcYM;-?Yb`XhZAHNa3#eYkM5j~~yalNH< zO6808M*S$Io$U}p0VKK8mXvxY>oY+W3EiI{Lw{!xp8g3&SqZ5;whf(rc~Ev-!a0m2JU1n;&c* z*;bWj3xLhXY=t4QA{{P7x!HG+FfqJd*TpFpD`w__RDeGBeo-v)*aaF@EYQlC1sY`w z^jUdvhBrJ68ky1>W3~j*&>;tz0kZKi@CG z4K%*SMlUy%WT3+&0I#rJrb?Ws>&2p$#KbE5?B@uikU|P8q=-U_Dx{b~s#Zufc9fZo z0U>G?#<)VNQ%Lm+X}&^QppX_Sq(us8F-WuVA^Z}Baj8OTP)J`=NXrz`@;Rx?nYR#} zGfUS=igJh!oN{Nr!2GlH%Q^Uh@;@8@|B-)rGnVDefooKiA&UOSFzzqMsX!^&1m7ApV4YbTzo3WmT!VMjsMDC`n0_Fzf9tAMXe&$ z{T{e({pQt2RpJ^_1iH1P80vMTCTH<8OB1OTpMmz-V@BoG1SgMPe2%U=9dtcU^za?@ z80xRco?ehWHP3qTG7syI=^pj)ruy)`$K<1k3LEDcTV#K&vcCBtG68#9YE4`CsXZx$-X|&?G3Dwq+OCn{;sFD+w&0L!}y*q zuu2K~%K0+GsQw{q`!|yv(%_NvFJUauzh^(!Mc}VUI>2TxO0Qj*Usw!NDaAlZpC9he zvl{-J6b|7(^`1mh zs)VqkrOd|0(Am+KtWNBbl2VW!9BJh1r2suU^0q^!NkPWd%;ZB_0RtF?UydJGgm$r0 z3b8>|(Kr758Lp?hxB6+e-avmjYOHDkLkIll_5HL2q3K)b7TS+SO3nM5MyzF7m1)H7 z&#IhLTpPVNx{6yZQ0uL$^z7kE+HhIGE9r{|ZFKA92ij)7a65M&HPcO3Ud(8b-Zq7_ zMH_H`Nv<6!ZOtmnNa@n7vP`K|h7EYc6Y1CesOZNt=& zmeW;GLHRI|YlqFPADLUT%3=T+0!+hr!3hJ}!E0BR>$IKl6aBsU9bp>$^qI^mTSpk3 z)9RZ_xN|n+x@GEf5d*`^geFblkcJ05Bw&VhGA_ozA1V==7ORKs=B9e}P_>$A>eWoM zc=aL>hHLBA`&AcwjC#Q@w9M07h?!e7nu{ExUeu^Tb8+4pD+rmH@cW{N0$g& zqUuX3(-ynxk_|I#7q(PtF8MizD=@>AJi{Rl_?IFwC;D_X2v^HY?X~D?4Rdt0R@mm} zuQr&r>AA5I40ud0Y;&o{92~<=%&?0GoEh^lPh&or3;!nF`iB6KY*bQbg1=Ic8P z`Rgh*Xs#EkL36#BXSl-DxfESrDkL=gjRMSSohji#H#|Zj#@~oyR>w_Z0lHZrh(`XV z0khg_5@YCQOb{FRn@cg&nQ>(5j`8DWQ!=a?4`GJGLUNI2d_KqULd@_Yp5X>lmtHkd z!Z;Ipq06P2a4^n<6Eo~$oC&X~YoTglfy`Mbbn}`?#5j{2W|(K3NsX!7shTWfoJosQ z=niQnD`ip@W;mqkjtwsOix1-f-E_MWH_^=PP(d@C4Yit`wdb`UvMM@O8e2Ai?)mxM)O1AudMz-bHmMq7S9n111^qRY}8B3(RTv@TQ zJjQ8&Q=GJ+Jpt^i<3=s)#LWebue#j%(rc0?KQ7JVvUJ%-5nxAo2)I9YP@22KXp6Mp z?2?iwIys;%a43)^clLYDH{X2o&FnX`@>R`$e5NUW(P}jlaIydSc;u(I&lP*fug^99 z4cUC|U#iJv=kBcI1Dx*(vY+89o*??Gxk@N&xGE@XITP<1pg86UZGRot!c{9+%@ex* zdcJ|HJwaiuLO(_D&W9T@j4!V4<@y&$g7^{K>eb)GH%<7_KkRjuH}Ed5@#Ur$G{A>j zy+{K1&4@JBc>9-eerPxVDv5_%asq$_(DM6y7Lwq|Ugh=&Co8GhKV`!6t$?rLeg>CGR#q~GfuH62NQ(KL&McBGJ%-EMlIAP(Z23F zBS}QdYO@GU5~IW;nnT1WDN%6>+as2sPmLbW_n_H?K4FmbDjCc)ng~gc_MLE{(Lt+2 zm+#nwNir#xbNahlZc;7xCd`sKMG9h`t!B$uL?`x2@)RKy zi>`&bXAFz6C@GaBN})1!LJ6q2UMi7FRakgSoDVVxOshm(A~4z+C_0~>?LXm(Yr8cb%ekqJb8_kD~!W<5{XB|NGuvU&9eeCAjF24(O77ReIzmz zW7sIi#Ceh7_yKlgP-F&T0v~4MVkCMjB(lOWUR0_nDo5f#WrQR<%JlB*X2P*(ln-Og z@u0%LA9RF8P{1)FXcof9Bd2)gcq9(ANSGaDz#^833@kQ^QH%;7XQRjXteUYcJJ`se zswCS6);BD~xDnjk<|wm$D>k%H&xq3LU=LdkYn@^fc17-r^Mhb7DmParJ9?Fd0}LA! z_~Ah|%!gxfktwLM)LVrTqsMsx+#F=%;KtBmWt*9{LUHVGHkeeUl`=Xq#B5^f;{|QU z1dbP&qo-9HV}hs-Xgpk~bucm%5t-#1@^lDzWfoq8Vr)%sXLske{Y*2nwSf$t*V8ZNhL}UjQ37>$e z?3?2`PHaSEkMW@_BMef-{VY!0$U;G5TCq-U}WS5BmQzNjWp}|-@9)f@#9uY%(4%P&irNm))-$5P@ zww^z1Y>W-#@PzQ5*MjAT*hp05qii(Hs|%7UuB#7+!b3}&6%^tido(8G38SOS?G9)^ z92y=96|zAW%)ljG0AU~&3Zv`G-DK8iT@Pp!R5uh>hyIJ%MHDN`b#H<6O?|V5Lt-qq z=FU&{9=yCM3lRp`XmkV|H3W+aexC%GMa0}oMkR8Q_;4&f?^G-Y_q^JT|C==mb-d(I z!{kMmt8gXx_GQUlxGbR`_SbwDlIIWO@cu3&Mnju>;m~lpBFuU zZGM3?j;-1b3E%!s^m?BUt$oEojs26dT*e;tkqm~`_IB>idl}-_qmqVxV?v{H(d|cF zii#$zCOKB2?lT~%UC6H(oT{j)o+HTYRPukwV%7Zs7aVKof!)DUaW&;(qCC%K^F(0> zPF?Fd1p$&qg^d%##T8E{vDG-I6v80uFBVMj5GrxejEg2*{1O*|#klz$Y)$ z8i9{bYq6M;uTB@`5;(9{v1Hl+@&Y7E(?$&7YU?s97fvrwL?KHtar|HwG)MW- z?B;LUsP-guQmBEB#_=IVZaW1}^hSFzy4|i%R%gW)5_2#hZ{sO!1_!fn#t^q@3F6oA zdyJT~9H(^jEZvf(TQhX)xM`}yGe=M+&n-)-Z0}85_Gc{nWy}64hdZ@$##uIQo}+B0 z$|*PVk-PDxyD{zdXWahropVXb;Ixf*&e;g7^COGzrp0&hz(n6fSK88+v9!sSwkf;! zBYW*ldu`fYpRw1EZ<{Ky&k>~6pNd~x{_H8)?Y~vQOgJX?U2*;J@Nld;!4 zX&Bdzvr{DwXxHRXyX5kY7tK-MCrxEj&aw-K&mVr(oI0F#Zpb({jPID!6IKT{LN4Dg z9}LM4uxZQDjOD0oIXdO?U5K5JrCp5~SL67-GszOymqeVjI&YO%UL2Jh*JY~Kr^`2F z$~UBpw|rF-hHKUgvpVftlku%dnWk#|a`U!K&GuBu)ap)o^}fsDiPm)GicIAS8UB4o zgZ~HWmJoAe=n) zJEa5)z~JG=R=IXF{I8d*&s6uE+1})l>AJZF!dZTy=X{S`+kSoU&7Jb9J!!fpL-)vZ z&z!kImARi(gpzlc(i7EvWNOzG?ZtW~)?It#+KDv1K0~jU>GjZfO-pL`l)DiE!ntv( zzV*64UBBU?c}}ZkJgJ>?=4|^DYp*?gqxsDkj)9gVX`0Q@tW2{rwC6(C`L1(&p52p5 zV$aFmgEyL>Q2%MVFGKgqbl+4>Ak~%Xn#BS=Z?1(x{io@@8G5fw@13e?#sXkZsdL6+ zf2R9v_me$O_l)<%{G=#ivQE|&?m5aAu z?}GyVZuiuf3tmO1!%kOTA}xb=7L#tJPw-$=q$%ytl&HZPmPQ z)pgqum1@%K;cWE>!O6DNM;h`mWPJ95#wpgKEhoL`xeto9Qt5;pAHO^8`DcJ|oSEgI zP;H43jxK4TQqrOy?{}@>@bM*Gc)}^sT+tYOPDnVT2FW34BnLng2Zx#{it3Xt$pUW19zD?|9JqPl1p~Z^|CwvBS6AiAmb^JDKC&I%a?%{WWrY<H@Of}IoCgz$rtddB{af|9a2hPO=c+&~RZBHetyCw~KN*y&Uaot=03UDQ zR@v$qUSGrNgw($UXR-1TD13E#reNjZg43J zd^0TQb?Eg8{R4?*QjuVkY*Nt}oKbNb$6(sR6xo;vaGN9#d>o?^Em9e0JON2C>@^(R zW~nSoh1EYxHA*eq7FAk#KeV9evt_0CD3iWJYJsW0RcfB7z)Od7+a)cxU9tkL6MuZ; zc1X)qPA%r#D+Rcn@Z5FTk^eq}&r&vSH#+isV?~!_gL+^wl--J^J&LBDoTe(QVQ=9# zzm)Fj$G^=xR`m4&i{m~;@jgZI{fgq=thWrL(ghthbT9htL}PpYDz{&1S~_02r7mjB zEp_O_UgzY4fBCl*d0^6ZX$@zOtPpCsE@1?~1nJ-QVMQ zM7EK2gB^yKm!?Z=f@izh7F1e0%bzKhcvqWurRzi&uX`l9lMC zpB+v9u?toI{euBLVS_CdY28ZRa;>>x zOuITVt`6CDm;ao8()z~cBBKL78$Go$vi5vstjqu zxzB4zJ9R)wdv%ynb*~$csWtQ=3-xgk7X8>_KD0&mv70^=(0;s(#BjiTXr1BX)f9%; zk{Dj6!SF^3%Wqa>wrmRmaw|wbq#=^GDXi(X#{AG~&Fy0Pp<3#84T<4e^FwXA+btA^ zTS*MJX)xT5HQipN$gJ+r1M}sZ_VbM-hBv|Yd&3t73g9nHB!VW}t@eu=Eb~RJnQJqA(W1z- zk{E7NWOl5id6Rb5h=pcNX5M9(bzq@cCy8Mf7Mk_ae5-agsK~UM`HhCzb&AXe62lv{ z1H|O@_iIS~7Kom)N>rtHqThUAAw%fjKk(}ZfH 0 - and a.asset_assetnum = %s - order by a.reportdate asc - limit 1; - """ - p_cursor.execute(query, (assetnum,)) - res = p_cursor.fetchone() - if res and res[0] is not None: - first_year = int(res[0]) - p_cursor.close() - finally: - prod_conn.close() - - if not first_year: - # No data, skip update - return None - - # 2. Update local DB - conn, _ = get_connection() - if not conn: - return None - - updated_acq = None - try: - cursor = conn.cursor(cursor_factory=DictCursor) - - # Fetch current values - cursor.execute("SELECT * FROM lcc_ms_equipment_data WHERE assetnum = %s", (assetnum,)) - current = cursor.fetchone() - - if current: - curr_acq = int(current["acquisition_year"]) - curr_life = int(current["design_life"]) - curr_target_val = current["forecasting_target_year"] - curr_target = int(curr_target_val) if curr_target_val is not None else (curr_acq + curr_life) # Fallback if none - curr_acq_cost = float(current["acquisition_cost"]) if current["acquisition_cost"] is not None else 0.0 - - - # Logic: if current_target matches the "old default" (old_acq + life), then update it too. - is_valid_default = (curr_target == (curr_acq + curr_life)) - - # Prepare target cost. If we fetched a new cost from WO, use it, else keep old. - # Note: first_acq_cost variable availability check - target_acq_cost = first_acq_cost if 'first_acq_cost' in locals() and first_acq_cost is not None else curr_acq_cost - - if curr_acq != first_year or curr_acq_cost != target_acq_cost: - print(f"Acquisition change detected for {assetnum}: Year {curr_acq}->{first_year}, Cost {curr_acq_cost}->{target_acq_cost}. Archiving history.") - - # Define reference for history - acq_year_ref = f"{curr_acq}_{curr_target}" - - # --- ARCHIVE HISTORICAL DATA --- - - # 1. Copy old equipment master data to history - history_ms_query = """ - INSERT INTO lcc_ms_equipment_historical_data ( - id, assetnum, acquisition_year, acquisition_cost, capital_cost_record_time, design_life, - forecasting_start_year, forecasting_target_year, manhours_rate, created_at, created_by, - 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, - efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, category_no, proportion, - acquisition_year_ref - ) - SELECT - uuid_generate_v4(), assetnum, acquisition_year, acquisition_cost, capital_cost_record_time, design_life, - forecasting_start_year, forecasting_target_year, manhours_rate, created_at, created_by, - 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, - efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, category_no, proportion, - %s - FROM lcc_ms_equipment_data - WHERE assetnum = %s - """ - cursor.execute(history_ms_query, (acq_year_ref, assetnum)) - - # 2. Copy old transaction data to lcc_equipment_historical_tr_data - # Format: {acquisition_year}_{forecasting_target_year} - - history_tr_query = """ - INSERT INTO lcc_equipment_historical_tr_data ( - id, assetnum, tahun, seq, is_actual, - 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, - raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human, - raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human, - raw_project_task_material_cost, "raw_loss_output_MW", raw_loss_output_price, - raw_operational_cost, raw_maintenance_cost, - rc_cm_material_cost, rc_cm_labor_cost, - rc_pm_material_cost, rc_pm_labor_cost, - rc_oh_material_cost, rc_oh_labor_cost, - rc_predictive_labor_cost, - rc_project_material_cost, rc_lost_cost, rc_operation_cost, rc_maintenance_cost, - rc_total_cost, - eac_npv, eac_annual_mnt_cost, eac_annual_acq_cost, eac_disposal_cost, eac_eac, - efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, - created_by, created_at, acquisition_year_ref - ) - SELECT - uuid_generate_v4(), assetnum, tahun, seq, is_actual, - 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, - raw_oh_interval, raw_oh_material_cost, raw_oh_labor_time, raw_oh_labor_human, - raw_predictive_interval, raw_predictive_material_cost, raw_predictive_labor_time, raw_predictive_labor_human, - raw_project_task_material_cost, "raw_loss_output_MW", raw_loss_output_price, - raw_operational_cost, raw_maintenance_cost, - rc_cm_material_cost, rc_cm_labor_cost, - rc_pm_material_cost, rc_pm_labor_cost, - rc_oh_material_cost, rc_oh_labor_cost, - rc_predictive_labor_cost, - rc_project_material_cost, rc_lost_cost, rc_operation_cost, rc_maintenance_cost, - rc_total_cost, - eac_npv, eac_annual_mnt_cost, eac_annual_acq_cost, eac_disposal_cost, eac_eac, - efdh_equivalent_forced_derated_hours, foh_forced_outage_hours, - created_by, NOW(), %s - FROM lcc_equipment_tr_data - WHERE assetnum = %s - """ - - - cursor.execute(history_tr_query, (acq_year_ref, assetnum)) - - # 3. Delete old data - del_query = "DELETE FROM lcc_equipment_tr_data WHERE assetnum = %s" - cursor.execute(del_query, (assetnum,)) - - # 4. Update Equipment Master - new_target = curr_target - if is_valid_default: - new_target = first_year + curr_life - - update_q = """ - UPDATE lcc_ms_equipment_data - SET acquisition_year = %s, forecasting_target_year = %s, acquisition_cost = %s - WHERE assetnum = %s - """ - cursor.execute(update_q, (first_year, new_target, target_acq_cost, assetnum)) - - conn.commit() - print(f"Updated acquisition info for {assetnum}. History archived.") - updated_acq = first_year - else: - print(f"No acquisition info update needed for {assetnum}.") - updated_acq = curr_acq - else: - # Logic if equipment not found? Unlikely here. - pass - - cursor.close() - finally: - conn.close() - - return updated_acq - - except Exception as e: - print(f"Error updating acquisition year for {assetnum}: {e}") - return None - - async def query_data(target_assetnum: str = None): connection = None connection_wo_db = None @@ -1152,8 +975,16 @@ async def query_data(target_assetnum: str = None): continue # Check if there is acquisition year that need to be updated because of new equipment replacement - if assetnum: - await update_equipment_acquisition_year(assetnum) + # Only run this check if running in batch mode (no target_assetnum) + if assetnum and not target_assetnum: + from src.equipment.service import check_and_update_acquisition_data + from src.database.core import get_session + + try: + async with get_session() as session: + await check_and_update_acquisition_data(session, assetnum) + except Exception as exc: + print(f"Error checking acquisition data for {assetnum}: {exc}") forecasting_start_year_db = row.get("forecasting_start_year")