b|2sS@!ZWKy$c8~ZAT)}Gj@Yc zg91fd&KJkAFH;Cc*`4dp(LvQ!bhyWtuB69Hp~gEM9BK-BBEd@0%v-N2nO+E!b`nhN zDy^Ia+{e?e$B#~wd{KYBrT `- zg#C!>?Uy(oK99Ry%{c71pT~mt-4}h*C @?67;BU_&YPL+mXYpv_TaGHBU=jNXD z#^Xi`Zvhc|CkXz!H}J_o=&g#thkogw0Q|QsA$1!5bL+xj8FhsoHf2PLms&4&NAF-+ zk%LnAK*RgEJZjCf3amp_cKmBXibn2k5y#5OHNi_560uoms`B|DUb54c`6O2R%q?A5 zp#_gBwcC%sZpQhR`+vN_;KR$V#jv$5dE7Uz=KMlXU7mIl;D!MYQ;ScL_i^OyYG~c} zI8$7Q!sUYUlT~-8%mc@cQjL{tmiHgU)da 2EhoA*B=_6VZ5QWVUjf~wQ5UMJBkgRVpxc(${|xjeDw>V ztiAWZ6i+>;P4xk;Ho#^%L-}yHQt0Zw*Z4eq6q=21AcEcRwt$>Yj3HG9+ww`Ubub&N z>@Sxn#fJf4{)sA^ztpP7D%JCFV(fcBW!1Z&Vc88p$f&mhz|*RJp`zRU9bw3*-?n`E zNnhjT@h%xV3HLe6)g{O^U8rITRL&6x03G)Szo*G#y)x=z7rbB%`k(zB=_0WUGx-aA zoua!%K745CxJx#ZnQtT5wwdZ<_X{dGc`qnNX7&~#-x59E72&X;bwp|&At-w0dgH{A zNm~uO3(-5jMRVn|Z1s7iCsm@}IvaM#jxB~fb28|W9i09RO*&+Ou>}pjk-0fhM2WV@ zATyJ8Ja6vBT+{~vLbsfRpj0?yE02_DGyjg3Zto4>sW}^_!}2PTspb4jfNlRRB^UAH z`{PFgM(0|amF{*%4CLY2a0u?63@#UStrME{XE&*&nysdr^~_}DxVz~5-P?8IM7zTY z+AtYSP=9Ng!L%*V!2BuCEQR}NnD8z|Uin+nMtD*oRF%6@H-d!ai*i5Mf_P5xeN@|= znJ=uVP-NHgP0jP58Q*H-Pt}humh PGq#2^^D_yeJnZo^BoARMaO+s8Xrsja&DVQG5 zo{ef +Kafx#1a>Jy~-=zIp57j+8?k y}=sL$RV#&|0kn*I9dQQVkl%t5!{yteMUluv}K*{+(?a z$D@j3jS_=TR% C=2XoH5TceHk)tgE@zK`X1($k@wbDa z?EO&$T-}zpC?pZo#R+si1ZAK4Bu?|E=Gw!F&;2v3h-nA=oE7*m>8hm28XVm}*J;jm zW-;| $_1*K=IcMI}u8`&_FwNmV(PGi8wn2wID4O;!(SW-gOy_VQ^(9ID*r zQzu&mg?%Y1u66KpMJ}ot+^-AEOqW;gk0@j%w1yiH+>gf&C}e}O_R_H`ij3VkgsO-+ z^a6_gqOMwxVo6?B@pUn{FbLT =DJvJxYGl3tecC=Z1UKDL8&o5Lpd z?I)wPSXVnR5vBuaI_V*qMjlXiDwnFX&T2ngQLoloEJ;bF$MJr!T1end@rxZCE-U~{ z*3vp08L@0$s>q8&IfCt*mf`%hJUpz!aYO_v*E&y*(PG89XcDgF=7UB1%7yINvc=q8 z7mzx}>m u0Jx6$YDPFTg}=J^()6l!b-yZz%E) zS82UTQ5}uvsMiju{ejY^i?Ne!Y3SH=93~x-USA!G=95N7x@POj`K{T(W;@O4vbT^G z@o1*y_~p!fFc3=$=rxt5PWy&?-iXnDw=@+}c+mcUcRs}qyfj_la G>iXPU7~&aGTsf83&F8qCM!Mh8+C5c>vK?ONCA>L95wy?vMw zDk cupTTcX?W5v))uljvN z(~9`@D=TILg0 l<`SiCEuxI?`cO2mEV5Y4 z=GLt>G$!pZ07Tv-66;rlN{eMj`G*!XLF@pqxbyKMTBESe=t_}lFB;158MFRjI-e8g zm6%RmLRp1ZP?6nBY;G;}Zj)wYTOU}B>fp1yt>HeSV9XGz5Jlm{VH7?QJO?Z!Bj08n z$yZ&~Tsc%q^t7iR92?1R63Oh%Qi7p9+2wuLp70XTz{A@RyR)MHZJEgL+TU*}YKTtb z NF|YQEGm8{r|0pqL+>OvUbhp@xkuK* fR8#{QDrzS1d>~lQLJfB*%bR;!XZ7vD-Pz5Y*dAqY=6_z;`A9+yU;PjAKwlt+I#ELRC%6KtUwG zST-KlNS%PUEdnDFgPpe5q>Xp4Nqq6~nmvaEXa;TlA%;@?8BQ2d*Agw 8DDy^#y8Qk?L7drom9){whbxjww^3b%zG?Q! zX2MO1xn0CbkS~3z+9 ImSzkHiT8Vz4lQ4~%kf1eSc z@Mtym%Gc3hu?bFebw+jLo`5Ukif@KNg|%|hj9g4lAy5eiS7m8vYP8vut-Qcy<#Rm< zt1m>q*CxZuvt~W}2~6h#ojo#ge-4j`c8s?}2)Ci7)^H#HRHnC) h4k3$A?89@cUh@Z$Pb%`BxRh_^(R&&(8oHu=gmEiWBH$gYJ}A)#k)}BXTzC2ZC@j zA8gKrt rqwj>Eb1xt3$U|EopnR@ti2I75-jp%GYjok#>Hf z_Q!OLyyUp*=jlmG;fJtFzkf@x`{6{J*GW%vET*q9;;1`rIpnhy$8d`U3C$^Dc^+_I zrX_JIy0Tf9qbj?yyRaZ{Q& `VL)IykiZj7X! z)Vhx*Ok8f1!%lAcRqmQlo>jm|(cMCL;tfBNj7Y?3Bgv2EV>i%rwYh&oB;Ol*M|U|< zPEPWK&F#bOrMjnyLfJIm*}6m(w*}BqBvTtW@xJSl)#6<`*JJrBRj571m3V!lBEAtp zCw-QDC}m7nEu`0mw#kWK3M>}kdD{=z9&EtqaGq2G(}>!o7}lL__$g30P(pB+=~J`I zT8mkoUv?M=f;FqrRXfpXai!VEl|$p r)X)i> zT8qJtA0RcNV7kiojf#F)+aDa7;qKY1L`~G2>FmGeF*sLRIdVqRHa7mD8~kNKIeDo( z&zGN7_4$}t`MHj6iX8b600|&nM`JrLf`N3K?dMI(I4>R|&gx8Rp5Ijzrgu2k$BKBo zXFkAn>e=77_YMGJz@MNj$Yh9l#R<@N2@WZg;~Q*^(fcrx5QAG=Gup65_;Fq8Q@+m* z5G**{@yGi(j{G4 T5kg`|*;ulyDiI5iPg$y4Ww$y}F82$1fW(TFR-Dr3 zPKkBeDEwjDibYF7jaQkV%}hn&UYd8FbHFaw VIZ;X87rcd*E*d(#^CFx509%H;jMniUV9~}vt>%4B{-eMB2{t9 zdsn|yOj8K`T(!xWQo?GJeMa9bI&){*{p-^tP33M`qALlB7xoClK^`{Dj2;#Cmthqm zoErW#sJV@~z+1|&Lk#=eJ1qlm>m8!SuQrlTXeRBpJ;vxbmOx6fS6GRAT8TGod9h<% znW{W_2fyd&t7>-MYPm~A+*dIe)#pttqXWe}jEOXl0^u)Y`F^&sQo=w6XvSGt6*?l>wh)j _A7pTnqO+y6+y+iJ}?3VH(n4?AWi$R 2w-#S4Le}ccM(Vf>^Ab; PvTd#`{&YCTSHY<`{Xh9VXYx^qX=5^DcC?|V}NLJ9hhGCn+r1AMpKWI5leaU zt?+t5v=TUprT+G;0IeUKPK@FD&6=m|78E8w`2)RfqlMj})0BKTdvuuI0H4BG22G5! z^fC4rXmZ8ha23$=6}9_Bs*9Xj$%PjQjjy~H!VXUu-`L$CI%|z$h#%g5hhwDflen0L zgNL|k3s0FPMzp4ya4q5^@T{{I2idsnd(`boV_fEWnh+f`L9MXuJ~Xnkrh36?+l;u~ ze$s_~Zi}{o4>90LL0?SepP*zltrMOfB^KfVh@Kb@VqlZKrxLcYz^2Wz@B4Waj#_hQ z3JZr}JUE-cGIPnKuHN>~I5l0HFTpvauHDJJ=s+$Lx46-Fr*a~N9dk*bUnQ`+r6Y14 zjXqo%ye)W%C>O5Y5lL`PSZjnVqE EleiCBtV6f~ z-Npka`mTe?jPVY-I%qsG&-7ulNAuF_Hrc$^2O926n;PL}xnt~=CLe|Z?AKJ5Fn2h5 zSH_NPI%7xEUDcP=`W5>R(aNz>7oVFppVC8hrZ$`9&7#4V7Uh-jmbj<4(aW28_zL}T zJrD@kX;Oc>WpkeHZMkX850`t~0GN0n@5^N+L#f|n{SlvPe)R8?YOlfO?a!B3>o@ig z@g3_M5}KSznChNV%6iL!mTFF^SxrX?Y=z6Cvi1^e1<#-RgQqQ(&U!(4TT6uabURH> zvAdSJJRX@H;+%rmL9$TZP=m!&Csh#!{oCQaY$AQ4cacvVHJ*C|L%DBH#{<1U*5^3U z3X+~D^+0v+6*X7b>@&mWH`5i%mG3OjV$1ivKfA68xu~_`h+3}D2T4x9VOP`i+RwN! zLfDX+5^)(iR^UIl;pi>$eRqwEe7}!mU;UgAx}jnYkQQ`X%J|$!e|MyRES>0w6>;fR zbV(2)yFK$|o0ol_7?^QAS{0T8G^S1nFJp$q;|QPiic*joQns6NqYsy!Y!}a-L~-`G z> d#W|xV z@?ei8TcR)WAa}Mi@-PIKc=PC*{qMkBA)|bak(y(TW4n!^4EI~b5qeeugFPF@v^62o zsA%JFSTYwRMnme12=;ewj3C*U#4&oKpBfIG!PLZOkK 3q%u$4$XMNIKEhbz$A0{NTLo2#{_X&8@T8LS zI)5o6=0XquF3lL!*_p`=h#%wtzs3enxoKr&=I(6She2|_n-Bk<%pjB_x&jh#h)ssb zbcrjYnDS!UL};TBhsgG%Zs4Dz-%#Zc< _@Isc6A0jwN2H+ zs3y=ZZ<-%kcl`w>;Mo$}AY-JO_j32x-Q&}UDv9c!erXdY4lnKjkm4U0#IVd^-_23d z`5ULfqT0r*XYE%M_T47Y_G_BIFUI4%L1>PP0BLaW9sDm}J;9~?U;QV)x;Qmfb@yDLUSAlXB3ueu`RV0fAT=mUvS`2l}Er-ET_g;68q#+G|&d6l) zqo_B!43%v6Sb&Q3Uz0|qRQC`P3Q}^aYOw{>all`5My&reb;L-|%KUFrM_$lw$|5Zf zYK4nG(`9F*IMZca3uljsabu#Q0=&tnk(ULDf3%j>Py~-cf##fnQU+5UZ3PEos{J+i z*?!U#{)>rLJ8gSWLEhd;TVbZ~v+*P?mRXuVX}kG$)BS^2F24krR4hDsf2b@G3JXRt zM?Nkx-g$H9Ey)H=TMji$<2D-^E6w=XiG2P^II}2|Z2!K;Fjy!{%=i@C;zRE|^F-BQ z<7M_kp)$6q(2!C#?~c&~<({_tF|C;CP-n5uQXRKraqGDatClQ6ul}}$LHON )SatspT3Vl4!C^>(btU?z*KLEn94ddgt8KX~FWl9Tp=-F)0lmx6?+qQ%v>_(YrD z+GyfMAlipyK|)hyi?Y88Cc|niukaPIwaD7YST`nKUt+NS9*p+l >AKRgU6v8x94(h_tWYuX`>wzx(KA zzP$U3&0SueKP=8~(;D51E6^n+R75NXFkr(@(Nnd &Q7%sZnl%e!PfKskNvX z;+;G~tN_3Ow5VCeQANfVlr}M)DmgGMW_4}NZ&$6)A;0*x2H8(NcGvBH&-aSnb&J1y z ztZ^Z{|UFfDE+7 zh~lC{**iW2Yd&rJF=>wLz&4Q9O)&Y!CU_Cziz5jHV(L&JMi+eFAfXC}SPjCah`x*a zcClRm!(1wFsX&@LCf1B*LNszIpCROqw$BqcDeogJ@qN9QAA 5icqT$7s`M;!T>fMUIzT@1e2$roofU z$g3To@3=7ZimkiGdU}HU7AXqx#|aH$6(szF+?jk rIQ#;W4Mcty0PEB`M9V6_EB28M z*t&IAE4)*q@5SG6_MeW4;vrhOELIdfGaQC>n@kp(T&q|})NjcMQA5$Msj)rAni58_ zK&`5(sh|;UNMopYoLQmOda=USPWI{7Y2uHY;o?6MH&O-6GTo@VuQ_6C01sRbU5?lQ z-=zYR(!gQ=1&^gEeTW3z! W7(HBA!h zTa7={%DT}?8U>^Ak?W3j=&_O?C5UCGsN?SMs?+`CQ@LwbT)$jnXDVZ*ztg4g!^euk zqZS!}vPfqILeJ6rekYah{c4%S{}%%PrO=k_zR8J31!Rp~)L7b+R@g6x&Xks^m{ywd zKU6np*`lmr@}HI73jWk6f{ubNr72MJ*ne~(vuX2vyx0ReWVoSk<#uBMJkosD*-sdCNvAn+|K_W1Jwv; z{5~LYNBGj9exBbT|B>DU(W*UY8t}yYEB{k~m&Xura88Z>=EQQQDAd9u?=xUjY$#$X z7T #wP7`xb0I zcIQ?<#mAGXS^NhgLY 5y@w(RF zdZ^7U;c=37 1>W-`p=eo>hubPvx%$N`Gd()n!*( CV${?N!+vCQKB z?RhihnX%xB=P?NgUAiCfAhsW7KXTt?1Z~_-{X_qS6snNgMqo7`V1K|Vch*qln}ZBo z+8Sc@&^Y;)`@bmKm!d_g`)1>wJJrojKV+x>c0q5{` rk zY6j8yqxY=X2a25!!iBHJ>hQ3nwHWSW_wfaIR^kZRZ@+WvCP`F5B fG7)31dn}-O(1|xrxT_qA#NMGjUG;QJBdF2ppry6H%jcn^>stD2*b-}? zOV`Z&VWr|oyn^X$3d`jPj>{MS7RXGw06|WU@P~0j(UUa&e7^yZzQo=K_36fwY&Y?>3#dCbzqyg zmfP{1R&GMHOx<8`oAgprn&?CDNeI{*61kx=q#AI$jgQQS8+v;GTFLK`fn=wG^|3Rk zpbf{@-h#w<>IMgV?0o;#|7Z2X*FKtd5{_>9Hb**voklhjFu%vWt@0kIXokL95fY6L z65oJAMi%T@xVOw=W8d4v8XpU}!EIo*r<3qF9UlKa;Bvujq`qhdAD-MyH$3=pbvCX= zs*2%uPsb9krD&zM-0==H4;Q1`Y?A(RdFAKAUtiy`c2jUF$wZ3O7`$~fFU(FKs1Ka- z^Xs6US_FS-;n%}BItBIC$*P3CbN2l^&iv)hIz6^i7Cu6xUp1?JBWCXqs2&S{?JRR7 zIa->CB+FIKtl7xy^9natq}JKhwLLFS?wxZ150|=*!TD7+g3nct)gpA>9$uG6Jnh7f za?yd&RJz7W8}D&C3=N-!w#pOgSG$cj6wk_^F5x(k6=pE}uV23xY()jTfB%D~p+b%b zKqrZtV>bnI=ggUUvE|5e=Dd6-{YHv+pvogVmIbGKl^}UCcHb%NKmc!YryA*Hn zkkCY|1u&Ei@MkdC{XXN-VR8(BYXb^A%Gx^TsVAqNq|h>2v5f$Yu=dy+ZNC?7zxYTs zZ&|&vaJl0oXcdgYhB)42K7@}ZihY}~>V$RlXa15Hj8Ue}ZvM(>RS{haP3rm2ACKK! zmt-@1KbN2dTZ{%NH}bs|4y`-!+i_WdGw%6MvVPl*fNKc2xAjB{b;pmHVy}Dy+(JVd zv{LkAsKL*$W=y%%P!1Q`HTmsH(t}vhD(2_+p;&q NlFIL` zmkdRq)foLgQzzh3{bvOy)BIQcm#W0#QW+0Y8V3;_DAb9@L}8&O(pRvo2<$p)D9jNv zXj#^3DJ+)WT~knHn6@v|+z7g7wrS}Ot@u3b#b$cdSuH9YM~bnHyHl qt-FiSY=nL8-x<%9sj*d|bJ`5AOV zor^{xxpxt~B0VC#65x1mSV_m7&y_#HaVj#@gW_(OTy =18<_bJrsrjR__RfdCFON?lvR4h$HXjGPAgR-~qyZ(hMo zL&t| L+mHI>oC}oZcHEHd-nlHdghT zjjzs5zOxqWgrUfnaV`*(zYB 59I?Ni?DSXRms*(h7Kup-qL@J>Q2&px5Kjphr1n~O8{?74!@4V} zEOp}~q%32;8g>ki_@TI|`Z2?3KUD|Lro-V@lfK-u+4 =P+~-23Q^j05mzST*r3OFx?SFiw?<7L9dtAZ z?M_j!4nvwI2m42>#+ZcLEZBvT51&Y=E!|!ide-5avcoS)1o>eQN)CP!$fCV3@i *Mz$xyZCZr;5glN(=^(~=k5a%-_33GT^n+9VsWlZ3t_x+ z7plMX%?Q#EaEZ26z7uIHp~56BJo@7#gF1M<8sIWVm~A{mvGOB7Cv31~YM47|yn%kh zIFe?Q?W02g?>Wg 7#kYAvi}V6_ ztp50!*9OA7D)1bXYT)Q&e7lcLWV9qkwTHX2PUFax`2@lwzZ>*hctB?B6KQ zn1JoO_qR^-hBu2rVGbwTm>26peAn z!9cbM+-~U$Xg!%zT5AopMIEc2XSP-6on((zx|1XJJp=?grDzi4kDjPb8(CsMC-J(z z=d$z$ErzwXJr; Pj`--{To}qcT<>`N z+Y}cfB+en}yd@t_94|c!7U%Li6&xIevQsa+mtypFHY~0Ue-^qk^z7CN;IA-8&O-eC zA+77){a+sF$XWDVIjwKwCcXzX{Eiw~%>eb3Ho7@2EOR1w$|&f6`#)I5QR7E%f@$RJ zIE&h!l+FbqA&P79l9+PRzU$_0Oc7=$lf914T9=zsGMhv|bJz>pY<#84V4*Br@xjtm zo1*jKese!DQM8e#%6c{7wZVFqy*$S3=n_ 4}TxAG*FCp@!aR7 z`Zd#MFMC{4TL<)`8Z;5qp@cUlgABiyn{zDq`gxm3phu8{@Nw)n?ZUq6%^7o`N)Mx= znM|4nR^9UJD3>S=_uKI{-@!0nei$x8YhPmWsBRhd_4^{esJgw|(N)`+Vdi>*;(GnV zMZBl2)_jANTqO|@Wipw@o%OP?ia38;?VdZBe;@eD@|kv8pq!oBwC_~3!kT_M=(HU~ zR-)mw(3Uk2D@P0mefEnd$`Dn_{=3Q~H;KHiDxP$-%~B@P2pqh4rrj EX(^@8c}K+15HM>P#LnU}H8_A^A)E9*!pvvXy7wLZ#apa!+R)qo*@PSa z`DvLqg4H??pq{3Asqfmv`r|XJ%&SnBQjtXEVO>5mULGNWF+^+Hb6Zzc EDc($vA?#f&sW(J=$>u_) zjanVDt)qP!YPDv>OTCcgvE>DI9S+Zko8=1=NgVnc5H_hNx&1Q9%>389Cwo^PkvI4) zHFvg~1o}K6jS&fcl@#d+>ze9a!Y9nx8SFXxEyML>=FP%4O5){rzM9uZz}r5}Y|{C9 zrFsd&j83Dm*JL!ox^sP^>%~|CuHlO-Zw8w`JWa=8x7i}_RgaOnlk`%nssumPzE`H* zV?4eEsEK;p4uls>>@ARN7j9d?Ssu{bWv8(+Kq_{aU-0)?t7NZ>agOQSp#?`g+T*id z3$R9Nf>*OMV%<3e|NL{CJ9j($_C76s+af{BQ(@;)q&{pGB-=>BdjyeLfaX2*hu2&L zzb*$+a~iVz$Y1qNfa)dd?+i?n)#cE!vgQ7vc)XpN$C1Hngu ?$SMk_yYpO)LCv=~cMb&L*B5zMH@tmf_IfMS_YK@n%0u`t*pqulDIB7P-Q@ zK2ivG`r(&7jp`x7`w?WeA35+ycO@D6ec9o;Mf7>)uB=-P7kE0>Kme#L7^XqM9`hZK zyunMeQot7pwKCopaOG>_bXj7}*t5#mDKDg}I0UzF^GnQT(MD4cc;$jjWd84Gk=lwg ziL;$!HnG$_H@U>DObMgs%<#Ut- B)+$aMIge^8lL zEY}+Cl5?!#h#X(8Fgc%OD9sw?Qn>%WLaqX+s`c9|C@2z2cSuNciW4-FQin$3h;&Iw z9k{@Oi-3USq0_^WP$Z?1k}l~Ik&;g3APo=ieg8La?wvPp&+Kol@3-pPGkeW9v-h`_ zj)pMZY>4a!iovJWY5M1}{$g7^RJV2YMLJQWfy3 =O#i!XISm*i#^P|F-&&ojcpo;vU zEU8Cd)!b2%BvGl>ALn&r_Le92P_!J3X!hk%$+afTo)7;Z?qlRn_Fg-F*`E=Dp4=Ky zwc75t!6AnO=`Yf_ENoR%nW$~U{x3H4#d6n?_{RaGS3j*5hiEI?HtKRQQ|CA2#}iC| z$arL#fV^eh)uyeyvU(@Hx0hPwL*{C9D+48C; W|1~^PSUE=^|luLSJ5G!=|I-#?CB+2s2GlI#*j?p|ZCgn(>EW)|aZC zeADDPpPAO56zPrPA8*fKXx>DSZ8&U%k#fxCP=5$lUP~m7wj7N@c2oOYwV!Y)vEAh? zEV;v%P&BQH)H#-oatieFp7-UwzfwIVuxQ=E>`}xtB-`=8aFRdAWUnVB#3QXQeTc8w zbgr&!py-R8`d-7)3)NzYH{Fvx^EJ;C6VM-m9;+8L9;Ke|%$~h$*u;U!ETDts(Q% zNHcG83u1B4)j)j_p*u)JR7533ElpSbV$Jj3xKp*2p6uN4*cd7r$B^KF<*+EZ(_1Yr zE$B_ZMwin%raAv5m;{)=w$|#X<+0Z+T`+yCFe_0^u2_55>CG>AfmEvlhzGhf9bO+* z&$i;RLvBy()zU_X#%TJCN$VU6U#za>9U&a-@AOa^xFOsmG=9nXc7)yo>3D_GCxTZ5 z7?-z`@23CCP B&y_KQ4Uj_wIvda{OpkAB{hM=niVF zZIt)j9j`NvxWW&^)!beV`OYw{8LbUBNcx4Jw<&(R`5|)lCMF7}+!)BfAH!T87-&4b zhwrhG8!C`I$k&TB`=zs7G1!a}P4b?;-gGgnDy{GP?9G#bLAMQq4mDe3f5;^2?N6h( zMm>_@!`nxLIsi>HpbiIG7`)-T=gsxvm3jP8w@8gtLhUDgNl*LYvkS``6tYk5DjhuP z+impmox3<^D8rDaNdi)fU$)8V@lxIyo-&VZXI>b(RM4Q;b^bt)bpU>mrOC*
tcH+El5E!!t WTb0^2KkY3@%zymh4wx(r!S`Tl74bhFjPKEWWSLAmz3DZv zGOqWT^T(pKzXzGYk7=*sFD&q_W`_>6o89cZYX`k*rAc=2PBhS=!R&MY%x07+5vP^A zM&>|dRpTp1R@_#0e5@do8=?O}5ngi0&n!UY&jc!o(~WB#ZX~h=+t|fzo)VqKG1QP< z32Xa%_U iS<(JQo%Hs7-ME(#ed zS~2Oll_Nyce^0^`iY9q)P;`4iZ?GtJJ&E icGEgl;}JCmx#(pPPeGj;>a*QEBXZ}+v7SsVnrK@FH5Ql5 z!3=1CMcn6bKmi}*QikXvv|eREVb&4@oUj}gvAnKqY) ?03=yV>B>fj@{dig<};Go!FYR@*<1%jpEft!A=tt zQ 0Wj=LARv!(PRp^J9G6elj4IqE9hGAQFww87|3P+2y5WC*SMZ6E7Dj#S1( zmCkfmy7OSu4P*_9m!dGDU|#FxT=C4>2+{feka1q z-yJoZ$EuB2h&?G1u$g>LY~xWa>$Wr7=Q5!Y>fvJTQkr0s!P!?=Ev{};o;b2b2vaNg zm0mg`pQjgoc}+q9vd*ZEy`Sxj5YoiZ=CR1MV6=S#2@d0?2(if7FX?^H*$GxTRV%eY z*+UIo8q4WlhX*5*%ig`?G@|H8(o|<4J&$T*vcp>hJx8V#v3ij)Z);tOx7_TZL-J)Z zBuN2+R^=11O9rR```EyY%C$=AehvGXglhkqR(6~5KQcDDDl3dQ)#j?}SJ*H%T*J-$ zHls0zEE!*qNuTfMgxuYBOr40GB%P^mijm==0@Cng$nS!6WeNvKmxBq_X}MHNmwg%o zMwKj!d6ZA_=1*OA #awNlQCIvgAyj1CO-zJ9V3yV<8rg;7D_V_>LC*@eNE zbw3d$^Um`h?ygSa$(4*lL1agJtLl5140DsOvx3|^a9WDxYY`RhNV~Up#_=v8hldZ% zGvL{^)wDLuoulmnqRz2_;U~ldgJ`>Bbb|Jm*MJMD4Xs*RfIsv3qm%eQk7QzM4vdtW zN0QwA6m2pyT)xVuV2XH=9kqGaV8u^eG+C`;@P_a(qLkzjh4~S#6T}Xx@H0xt37vB$ z)8<69$^5m3!zOl(bTWE37rB@9qvrKMM){%8!7+U+3u9NBm($Iq%otGmVDxD8U{tJ8 zk*UzY(pi#8hSC1nnD4oX2>RZc+r){6L{@S85K^d|;3PVO3H9F{wtv^8f-6vyY|Zh& ztF(HQj@HSBI26GzUb8-vm?>mUFlMdb*(5_sIv)6*jFaHuhRokUtT0xXElb1FW5-_5 zY#WtFl5hPnVt>If#&wasg{wZi=2?|`qbGbPY)~m)SvBAo<^#qDF-*O< zDMor^N~?UW;+ac0rUI>6SlY%B_FCZmQ7SFHPBcHJK5HW@trxsS4610nA9tzUE@G<) z{_)cP?&*Cu4f>f%J&V)h=I?HtMCuI9dMg>V(&!9^!Q%yv8}lMJB6bJ*Lb{BCVD$GR zMRP3$an<^2He)fp3&aMxf7FVwy`}NW7adEg*YVkqmwwA|kT0_rd!j2_3n4CK_Fz^` z8R6W+9NuMCYby*3YxAjprQ9;hbekmr5uFc&BzF#@)8%N^BW)vUm8!}lOcc&IXy4P# z5h`PLNAo3*#K_v2h-Z7arN4B)@=Q6|qJTu%j+!0$_@5HIJShf(!U4PFX$>2duKR=c z( ai&FM 3n7;z{m)FC43BV>(5%=Ak|@@qV&`(mG&GeLwFMWOe!~kUl`@ zgv3C_JfewlmSeWh$LHRhR|qn3HS8$%U=h!<%#kaE-_NDfNobCGf6lmaba@u^b71%j zo;cR`Os|Tz%CbS@5Bt3B||BAgl{9 zLoqQ`ae-tjjAg!R?q -z_xvDBz81KZtI5h_5RIuR|1c;5|X&p zE_gxoB%4_-Vs>wrC3u!y;`Z5->!>GrdIVkHkN1sfP11Fw^sH;w)SLqkTFF#7n9 zW^bpdAbRP~;&>W$>;Q~9t_zn-{408>rTt(_8B8^^9;-DH%dE5ZU>_n;GIDv-@o|JI zwhYPlpJP;CkrUU!*bmwo@K>L}H~T(~r7NJ_jIXLk!ov7R!aBQurM`B6LKJ{AL)1zW z-FG{zxw(1rl%K+=zrS(6evOA+j0g+Y@%hjBtSX)Q8=PP;80)I5NCY;;*6FgViJL-! z02CBv3WY*ZFu19>IF$3R8vsDTA^$T4fj}tul@tKryka@wVDR5s|IxmpSWZ1LE))>_ zm(dl4nu1`k-&!aj1n}F@6}<}Tug5NU6%>j<0mWf|!_fa0L&g7|!~Vkm3V^ND<75HC z5q}F1SBB!^DBxAAs^1g@5XTPcsbHXh|JBY4l{D~f20;7bFpx~ll_5njnYZgMWuhsW zF(EuZ;M!&v*ATq32lahhXz)#ewt~zaTqPYrJ+iXBw+_hKJy akDL_w^^PLh%yI<>UmL)Mm;QGgs|jeyYo-#}{Z3@ym-J`@q%u z)&^V}TJso61r%%Z>>Up@WuL}~#_Id3T?Xu!7>4Rxx@7WaWXb)^ededu$e*F=aMuRQ zucQOz&f`&Q$ipNmIZ)*3BvuEHJyUWJ;pJAF(REaGVB-~1+Z3qEtz_dRArY~Hk&~Nr zx=84J38wbPip;|B{HFCaraIN0u37x1pC9uXRtM{f^(IGG%O^VLw+H?igL01MXUiBh z(UI`Z9mpkk&W*s<*1tI`t*?Kp$|XF*DbS99 )+@;~IA3d94{v*R1ZarTBc8i>=WZ&i@syR{5M%lpP}-hclb}k9 zNf*f&waSv;m!kE1PhtPbLs+<74NDp${ &-&COrxi{}* z6Aw3RNj5X)ap@0Qxl$-5fw I7^D^KlC$47?DN@4Aod1%b30SW-+}F#Jo8jJ^zt8 z;V<>$v|M#|-lUunL-E{{L~Ij~lP9q?I>^?PpHpN_c{#g7ZWR4v$$gV7qcMc1q|6sb zi?SG1(2W*)CdI4BW!kBmCG?CgZ hsD(9P9*jWZY7;G-yVB{i~t z9H720r*Ow~mn=H;jiizNdddz8XM@%>mFwF}uMSFHv3CzmURhfs6Bz)Og;qkkTIfec zGRKT?6p58Wm4rUV-zMcHWq9*2tzb45kSE#JR2)w4sY)|>77<4B#Ab+Wx*W$)`Oweg zh{#mc;-2&l_LK4UFq^ b*i1{YWK(D%HE~0M bE?;!n)XJ7{#lO(brDA#5d7$C%)Jsj!Rdaca<}$}gFQ?@aM~lwqkHsr zd=Y`ZAJ@_zEn>c-`!#G69=}jIU>_&7B>=>(tW2nyLD=@k*R`w#@5MZ7QMTrbu%f;z z!}$?l_}zQqZF~sRsdK|~bUkxC5X5bzbVRpc)N%*^YwD%6-9w# PpG zz(7C*@ZYOLApV72W!FDf3 Ww8d&BhQg 2R|%<1_FrS3 B)sFxG delta 21449 zcmaglV{jnb+Bf`3GD#-R#Kt6<*!INE#I|j#C$??dwr$(C?RTGjKlgj~`EZ`6YSp#= z>v#31?y9cp?$xaWAlq#qQOp1%BO?!wy@Q>ht|hcfTD*!_#3>8n`YQ?tNU!S`A1WW% zQ({k$;RevJreAqeHF%k2^9)k$c5^!wQB?0$JeXxRX?VRfI$rxF2c+>&`%wye;_8(! z_g44a%Qfd#HtT7$PQL&j1@!^dIwor-kdqOE7DmhH3Ak@)$(Jr28GHczbzo$tOmjhC zCNE8=W(PeIXFLS4VYvemL6QA%8L<{0)DXyr`jpKMeWELsS_09ZX4H((8h=~)z4Yk& z=2+z~L2l@H0;#OC3!E-P{`3mmsoY)FPnG3DsX_RQQ5lq+)Eb6kFc+L7J9RF&oYZED zl81FRUzAuQgh#?Rl_3GFDC!{yJ|9LM?G-GiG;m|JDSZKBe5wGkUS78 zd*m$7l;c9cEe|AmWk<0_o(lYcUascm7bJmE0r& hzD_2k z#ae=h GG!eE>7KZ)j0$WfXouJE!k$8~dpfXZAYpctoWw9XEF8T* z7d&nBW#KET$9jDqfsbGdl1$z_#kDIh7 r9CJp?TfELvZO> ^<2#Olv8-dyN*rqa$72igz3sghSTYyd92Q|5J z7pib8I `#}h~tW&5_CNL=>qRYu^) z#DQ2I6^?9srz{>3jj+GN5CSa{cc4&`k2A$Qjx*Ffy+NyO*7S4|mZz56M3DT-#U2X^t`G z&kfpB-&AoxZdr=Q-B4T^ZuDwxqfZxS35@_VqxV~oqr%IBXxZLs&jNWmVT%Ii5NkAw zUg0lqtkeLRwCj;Tu(%3XnIv7_qYj8(zM=MTLPNuGyk7b&u0@d?+ImP)wBZ0Y<;u(L z8y<>PGO(gE#MIvqq&!g&V*JOVX1Br_ZhjxQX>(w}#|PyX_*nqG+i>R26-rEb`*IZj zw=vj0P5>6_$0S826{yplxhM{;?Uwgk`FGd0Z) uKAcStq2ZXKJ!4un`scmsU@R#}`&Q0OL791&{$n>E Ga9G?&` z$%}@7{_Pm WPLo92ZH=k)^O8T|cS@#O zg|)_>;hys&;9n}GjQjt9`xZu0+wbv~e5m^XIHV#l&UYH^Lp{3A!nZ=~yPQ)&uvVNJ z=neXYflJ9{jqfVwf^ fq=vPAYDfRM~5Q z&k?U8!Q97BdCqmW(1O-|s!KeM&1qKTN^i5+TTkDX5WZ&{Uu-Hn@Emv;TBw58A!2-q zhy)L_F jauaP;*K6?JFY5uRmW1Eqr{k{#ewA5&xP){Pn%r1|AWr;*YHDAh>O_8<*el z^Jd8f6d&L8uImJFX>V#)Qdrp9((wqmcb2yZkP#C5>>-1jGU>f2pdX8z><;NN-5-hR z+x@&WZ-96mgpxH1PrkZ3+QnxtM9i`KK2xW2R-{W*iN~cbBpPbTMrug~f5d5mwZj<^ z?N`k%ZyXmnZX)*W13BNk$CoJJ9JooQ_N(i^_aQW13KI(%pcu>2L{8gEVKM~f@`!hm zLNDL1xa|Zds>Dox7Z`87%B uQ4s4RK`qV)JmQJQ z@zYyA3^UVSQANReLtCvQ-z>N69I+Ow{owo2vq9kJob`WCcg;5oZlABQ&c^Oj*rBd7 z)(p)rGdnE}A>}7NKItmRCTs$V{0t0(+qK#St$P<#>f;whWo2j>BEti+H?|=)bUiMv ze%BsfVG+B;N+8xXNT9VZ$9R%So^dTnoSnyNn%66&NkrU@6L8K=?2Qe#sT{{8rzf$D z;}6Ta)8_B1k$;BAM6230mirl?qKa*DUBtZQ{80bC7uA^ 5Sfj@Of@C4+ z-zbkcemM?|JT_UL{TV#B)z47HwY|K8SZ841)Ge@Lkw3Q ~$k5tZci5x~1g=fm?P zjaqCb8ggt5(1>I|#n=n@B-o>8<+FOT9FnRhWYvH93E#X=>$B!Y8a24_eb2TrU@d@s zkbda&1Cp?D(FaF5KqdwtefS Pg)f#_*2=y{s07Qj_s)q8xdLb*4sLW_$VX)J{9Vl;3cYiT&A{Z-PGy z{JCyjzsc#8Q#P*PE9VM$9qrUdFxveWNlC~HW!;GF8ytNu!A;`nK BR4l(sfzvHX?GvG&|o^|3s1aMS=fo{;$QK z$e=rDTM^@Xdq)Uh&yOH|)D6YWmJLaIx8;YIHsy=#X7f)Z{JUS^`<*_eHY5(2C>T^> zUvStV8TQRFpl%KYlKG2UJt@yLdM;yOLEf<=&+j(jb>7!-soBThmb@R~ 1HL>thv;k5pS25)0lhHuGJQ5=f)Z zAVLf xF zuyauLIw15phU)AH3ijO^*UG@D4Q2!Cvi#BNrhL%UrubphO!#1^O|rqM>#~4<>k1Zi z6Nc7Ioj?<_*%@=Q=W&uY_DUTT!Oohv6n$z~66*&g^WrRcsPk#8vU5c$yEQjA#_er0 ztJg`(?ot9@#LkXWxiC(;=iBV`a%4Ge2)q0C=+4-BU?VELd6+xwH?IOFfeM2gD|XxO z0z(I|QT>Q9{a*mpzrvUQUPgi;+F`C32D=95bR?xrB;f}Bn*Vzl9E1$m=)aPUal^@M z$-+849ik`(`;wa#LuRt#tMAz4r9XrFi!xlGP>eOu$q)rM4#1>*CC+f!{3CaGa>nl7 zI3|8>#U!|wK#jv|Ra`yyzHiScZ5WBiekpr>vs(G}*yFTCh~)N$1)CL`(I`AgS!T=a zV=w8QD6q`>h^^+W=xNt@OBIJL_pb{wACpiP+vblZub?QNfF?`MC?EVRY?HoA;3c#B zHH4=O>1?hw6_`=*EEKJ}R#IDMMi4a@H3tS51z46Sm`j`p83Mg%;Oy@~fc_IAB~W zIo~=Nv(p2&tC9W$eJdyQqVVSG)r1r k4D>WZ8}r2-{apE+77LRm#-_I`2M&F%A~Da2kbp1kUl<|nWJ^A5=r9|e>hz1 zjl3s*yA9?h$b?F(C(`CoomkIgLbWDKG2L#iyV6i|p`qqVM9YwXnauY;oawRuC$sD| z*J&UgG$)a8l(1Q8Z5_aEAAtXB{%-|1*l#+fQv(Z7=b}oHS_nw3<_RITeJ#N1`w@lQ zFh*vo6L20(>ZZv-$0)B)O@%rSSq678j5k&BX_LLaO`q=_NCCrx!n<7cWOwcEeWx|* zF1_4fctF$|k2$@IeQz>%kXrIxpdV<{axi2$7rs5JP *7aUUxSKOH1sJk&RgQ*0DJ=wF<6^%q%k=U|BuBz;oqXFq`BL%M{9g;O#q$%> zlkJgyI3`kTAZa+8K9=5yVL w0w8{1AYBwaw)a4-v`jf@EP=EyYF=(McgeX zGaKCt(^C6xl2imT+xnUQsL+~rLs7E{sM+wtNSpjCW`mQ~ZTX?4P5B`I6ASlDOOgZ} zkV^A36XLr_PHYh> ;->+Y37@>N5kLmN@3fH4H cgN9dc5 z+QUx%8Hza~PQI(4jFiwz_8exdVpbyN?NpVM!ym0EYwIZ@1U>)*@lDpSSVblRqBSom zcWaq~QEV7ph{3vRd7lsOM79Z)-pZd=wp{jG@uA`$X4E=inOAcX&^#R-z2;W+D<#4`B0tYBBMgha(Ck#vO#^v^Pj>%QB4hegR zR}NT ls0o(Tlam?E$$meB3t; zW?V i z78I=)CscmdNBRp @teyNB1dat&NO?P$R z+Bll5?*5te>Nm^BsjhtTW=P<8>(+X9ZHC@3k~Jl6Gxs-NWlv#bDDNSHM4?;7s%6!( z3#z?udza8q*^NY6oEbcENS8=gM~JI0MKMCJGqZ$GsR8BUQ~rzGzOffgnuM=`+@ l)w(!nd*Gv<|oYqpIMB2wp@j3{0A__O^hyNs4hA$ z@66^XAMQ0yzs)uObph(uhxlr?u}O@3JH5+(aS=Mxlbpi*BDbtkuVW19#zF8#5*pK! zM;m#-4cF^1P*1~`N?>1s9%d}6DLab&d(Tge0pY9Bjhc|{`%u?X^YPSto_gXeSiBN# zK#njjvyG-?r1X&DPy;wE8>^f!{%|Eaz>%qb8y8QAk5+*-`W4#V{NpXLlq_t-fnsrA z_9&O$4dY>#_?;osAeu7k8z!Z5NIys^%E`2f3nAKPv|0-*^u5x3QWeE1_VuM6VY;i! zF4#IYdQhn1{&avW?Him^U|ci^D|La{<{F=eO#2$|ZfG8#4gYbWFmSMwX2j8bJ&N zKDdYQUolW{JBkbk$zLGHM)aSkkuy-^Z0OQV0^`){vtFgcO0@*{cii&FMmq}tZ`JL# zQmOi%DDXF!D`A}|4kyQjto75=-x|aY+Je}o*^wb3sa_DHdetBRjF{9$`Tp)!9#U6u zZuDkDn?~GCoVkx#2|Io~y8~k%AJ^rd8buV9@@=IC_P0iN{6-%{Fr}<%D1eH{=CqWC zllJRcMEZaWRd38}V}#pPeDv iR2vr%SHd$V=|(7AM;R)ZN>S4Ew1Qv}MzwF{ChZtjiCcYIw3p2lqTpt= zf4I4=OgTw-0jH(3pxD625-XiSOEmt+nMsXn(mGd+E7Ea%PcN2Qvs#e>_rdXuO6gd4 zlLWt#^cA%;kDiz5AaLMqDhVI!RzTm&WDFtvIs0z}{*MO!eSn7k8T@#1gWQuGe(8Sx zN{}Hp`5Jt?BrZ=u*&R~@Awuk^lehV#MWJwQ|E_=`618YBhO}HAmaEO%Mz5w_E6aEX zTR?#mMcQpudml%&o$dC;MvINa+jdLB_8>k^TnwYt8Cs^N*XP6}%@KQJAw35LK+|2C z?xK8JHoPz_(X!5cwh_SCQLZ)Bg6T R#^^q~D6T#PHd>u+JiTYNzC? zNwen|r^x#31F+vl-^>S5ILUjHH9w?{!F>C$>-a{Qug2n 9se3ee>f X zn`-Vf94H`y)^km+u0rc=cFL3xiL6ftTj)EAW@T=n!n3D|^{?n*Dp*Zqcfl4cI<^}$ z99=c_aR5iLkMIofwdZvv$Ku9KwZnZ6ynf?tb3OViSx4WR3b!KFyu4PY!s}f3+DX+V zM=`^Up17Q0UqsSX=`gKgjPx*1`jkI%iw!@z^idC#CR;WPsl&D(P1?T^bH*Qeip{zr zLV;P;8tHO3`n&j(pdIN @w8!bWL5QTC@ekM*0YVA2(loND!Y6`UE+AA{&{im)5B?? zHss|nqQm209=TH7wfBe1 }`8)}NM-?hkQq8QlCq-1%-H?{=D}+3p2c2A}0mZmksdn%6Zn@y(MAfdJ!# zOHaZmGK(a-y#5nQZ&JT^Mx#3~JHsu0(aKCd2*zVV?(?9VMJ;pVl~}-l?lxKESmiwc zHcQT}4SCp7Fo8JcbOCYXhm@(@S`qiYka|HotAo9DdPAL;)8ZPg_$IPDr_@O_17#E? zRa(-l)qL_7ckeH5=ifG?gIt9`re=q_FnXtUtR|kr{>)+A+zLOr61}y3_uBZzYwefG z+B1p0eHecAe;D{5-}BlC`p#@Ofv$PK$d%a}gfTvFraH+eghoVlQ=8;qs+-W-xH7O} zySnq~mY8DfRlf&zJ<|Oiigv+Ur8DhC81R!Olg109=*bMvajJ;1a5C++a<-67IJD1J zoRyBDE419`luyp;tSq0~w~QN1U0Xkh-fbWNF%$sTtNulW7Ss<~twMkX)2HXHLWLOm z4~$TuYP9KKp*EB^TQ*MKZz>Y8PFTn{yr{8=q}?SxL9G|msw?j=srHZGE%c o0W*! zsbEmb`~ZazKO#!sekg(pD`VkL7)(PQ=HkOgx8+KsHT%+$%nQr!ixoW)y~GBA!mk?! zkEWqZtq70B!=s-q#+2da{g{b;9Ox>Cpr;x-=`dyX-i|a?6W|NYUH?S?zx&K(rT8PG z@i!qqfzS@tg+&*e*sBI Xq0%Jvabc^x-Qj!w_m^PL7b B@rU zo{}za!|>L-VHH_2CJ~pMGt9&thJ&Fpl@Kvqui*2N6tfj6d3{9k8GSi&PwBL)8B(o6 z=I2x29l?BB8M=dRuUUV7o&@aYNmdL>06sYYWel#3CM3QohRhIS$to kB>w|=gNF;c&O2E0h_28)GXzjZU5 z iOPN>PWwIj&VGJ5PL=YS#iu-7C7zR#2UUewtK )PF2#&a zn+o_C_BWox5|6Y)W*R$xC>leSi_h=LQtbBpg1xTl=(Xjii5I(NaW@DD!@HP?=O4T= zuR|Pdj?jYY;vub46vUFT4ro12tb+~o%^_@L`l&a(pz2WVb1SO)ZEt6Q06$_yuz#XB zIc~*Z=r<*zDw60;e&ucbPEjF!ir0AFcO`WzxW?oLlZ^SFt@(qfmC1!e$BRyqkj8Dp zlGb3n?* JeGC$H*zecjZJ()Z|K_R(mvB>nFZAxTB zXNWGn&CCYLbSW#@jW1^b?CPTSEG82We`R)TCS%_Ni>WlYlv=)j;%-Xquz9PbeVLJ? zvbdsD&fS5+5n+nNfTo*-;IN4%c{@o=mpMa%Nt3+Fh=ERVx$yaQi~LMpp$w=yw4t5# zT3fQ#Dz}Z(!ra 4B3f;YIGavZeW4v2kP@P> oPLb;L3u+BFZQsJN|7(44W(yD1%>tIBRaLU!=5joI=o^)|5L8ggTa z(ej0n(|BU3%%h%wwK1#Zz9(Fww2?;&RmgKCICnD5ZNIOdanO7Rz>NG%AKDxhJyaj~ zocNrcU! Eg|+B%HL+cXQnpM(#MMo^I|;B8ZD@@k^-9P$ MmMLF-HzWWB(S;|-S`_tvoS+dD(P=jek4iRfFr!7;y2o3a#1-{ciBOSn5OU} zvt{bn_ VkN1g&@4>r94>$y5qomBn zd}5A?K)3O G!=wF21}kkCdIc-Z$FzzjDSvrQpuZbQMKiaiS1BpopxR&2H!SMCKtUz+ zQqF(uP<41Cr#G3af_UMrV9UJTFaIRk4kvn3+6b7;ku~Rkwlf)?!cn8&^XC4kn2mEM z-jhX?db{DKV{YBxaYR0$vXJ8-z1rd(1g@=2{*vi#B# |3pY*rf#4qX+0Gg3aHYVzsdS2~`{*3B7;)>PV0%keU3R9Xr zToHG0hu_2Fv_@~}1u++YugUyi3^%(j{*nyAD20BTxTt!%juv=c{3Z%m(KwbFK zX3ksEnF1>*`%?$pVmoZ|%rbkE5uAZ)= o=U$ARgpRR($}m!kqXr&^#epWg)lN zwheDUwdWu#p~%HQkKEoDpFe>r%$0V9e=V2wWx{0LMjH0z!6fuZOiuJSMB|q8odM59 zXArK)DcPeY{RHUS2HGOY)~m-EFsO@XcIP;?W+K*uH={up(|hzD^~qB}J6|Z+ja30? z_hdT}sq@3_HVC_?4QsU8s*;rv{Twb)s;?MN?_KXN0Kbm4o_qSq)^ zWRZPVur8@s+?a!#Ph A@_)TiDsU> W zrampCaPe}Cc}4)WsKi(eGsSZQS=eee@x?EW80jI9)~E{6)eDk!QRf-3wR0i2Se`uE zVS;y2U@!17S~AZ8Z1~*((e)4PPk|vBhA>!Cq9IWR=;mDv2DIZ1ml)07Pg6VI#69GP zizSZ_OAvB`vq?#qWp#=-?e8 3BmjxhuoyO;BD%n6rV5`=DPxeF8~B_@f+m;Nn- sPqexMJ#@9E!=H1m5Ae|D{d73t&@Wh~_G z9vP|qPypB)IHcJoI3_qhju~3?G@X>&Mh{nZBM>0iS=T)F(lMI-B0Z9DNB|<^p;OVq zO`^mh!yD#jJfcrzBAB9Z2d3bN{L~|@u(sjqaoZW$ -Hl$qSz_^r&~7%n z_C;#KJ-#~d2=CuOE%Wh!OK0-Iv<}xAAdo{Cxep(9oio(kfG;CZ;}B?rR1!@MNbF(X zFi1B;Gj+c6T7Fc{{M0DF5qlwf<)l?a-^XMN=Pxegjlt2a6Gx0Q^B2z-?u1G U#M i7xK3}^9-ZDSdVETf{ViID!Z%C<|CPaG4utnf=k_#qMSzc2}R~7W$v<2<@>r`P1 zH|R=tNPpx;7HJ;bkt6^7l(}^RawghsrLG-tsvKEK^#Jk*`NGMLi-3T70m`aRf{V#C zX6G5d#ZQ@c6xm1CAmYO_DAqAXlo4pz$w}LG>y3Iq{=iUfh9O4|^Hnz1=yww9MIW(- zx>-M?GAvGt^3>wY0cTDPr;Qhv(g =cIsJ7Z>1dyc_)JZK|d& z3EqG4;5NQ1(%Y>j+TZ!rfKMb)QCT+ e@j8U~%X{e86LGSDwgin_@{97nuaQ8t_C?9jMBCV;Y$;j-fE>_y}tlj8WezV$MK@ zV{qk|nIimFOvFRQ>)c$dJ-N#It{CX<@`j4qG%{BwcNc2hXKv1-KVDz~+cT#c5K(lp zQ=AjRY(Cfir_<~>g*k&cOJKFKp!X10$Kk^ppjiPS=r(4`yDu}6fDD$G0P{lNMZAK1 zQ!y4o;Ns+Jv^WaNP$|x=WZKz@5((hP^WPVN9?Gpm+)I$~<~oQWXk hM+6Ga%Z!kF$HAADpnEA`deM0Alu_<01eD1Pvx `Rtn*7z^=pav{U z?3G-T*J&+**Kg FAS!^TFxJ59k#u}sovgX4wM`4aj<1yKXQ+Ve{g%w^O6Cy3~ z=SmQ~_iM+UnxEY6ar>TG#OXXQzBi$Al{+r&HJRLfYga5Fj_GUnf`?sLyHMYOH>iR( ztnKg|`zw^rUMt;$FzGVls)**=N_ (+<~UAd1#J01B+os+-bqmc`H;|mQ0f^0~gw9zn})|f!Bj&ZP# zZ3SSi(fMK<+4RC%C3V9wHtGK5|3<1NU>nqE7>6;L3$zwPl>72LDf6-V=v=Sd))cfl zXW`fz#a=jV^MDl{9Suf-C)hm =Z(J)9ZNoLGC8z6w)LY#=y^5uPJv6z3%5 zT#t|01`jWt7w>8?_jVBM!=(!!KzP~(0l2mzA>Xe5{Hp vs zAA&ddyru(lorA#pcH}6nszC)H2d*&&_0M_%HmF@ Dwq^Ap9I(^F>wkX( z@K_`K;eJn9HVMpQTbbijF~cumL0W`^H4K2L?|xCn{ZIH)ZhO=)K*;ihVWqFY9)@Vr zJ%Ax)*9G~?@0B@v%-~aof`LiOBz!Rz(@JigA#)O^Xz|^AJjYp@M&^TaI~yEud!OV) zJB#7U^?FOOG$2|Aa2gC>mr7>Ll|6FPWINbpp*Vi}L l&3?wQRujd ?o9@O~J6~^ne>7`2KEkmnpgyip^^hat z)3q*XBAbLN4$Z3W+L-cIj7TNzhHp2Pu9_P5GJ~8M$Kr_4!WrD)lg)q^qTtffkt8^2 zE;wPS4oMd#eagPj3MReEUwVn3)FK @SoyG?EtNQghC&CVp~>np_0w1 zbQFQwqVNBA6wUen m2?Eef^6z!-DbYxzQzmxi)JEZ-PjDW|f$ z(4)^_Vfn_WTa|ho$q1ogXN_O|%grz;@2kNk(4jx09buXNs36U07-5%aSIK}Lp#Nem zPKRN^rin1t@aM8HoEfy%{gae#cO$trHF6xwi{7!sm#%c~R^;YsFjoj=y5;Oyqs4hh z*Q&VcmovQj62LebMbi3ngI(vqoR;gV&*wtl9sQhcf$cq$tGdQ5V0`+?`z?o|W52Hg z@GTG#`IdZ+{U-MTRTEPxQcmqhp;^pUEEF^ U#o92ayvUJs!9Z>NXW;sN9J@!)<(*-N-#I0# z)x}cf{m12CWgBoDD3YAsJT?+&8;VYQ8k@KppxEyD-a^1#>5 |D}ejepnyPXh`IMc#N zphc+1WOrBF#}knD%}M)cs?qCN?@df2MuY8HfMdL=8ZQ+JG(KNcT?HRen57hA`RHk> zdA7hZUj&|Ns|!9znsckJmv5vW!1gA8(d*%P(aN{$e|dbr%CqySA7p0#RrdRevN?YZ zDq0DIyD~>M2ofy|)yE7S(MC5de?%2+j|#>v0i1o*C(V#=qK3d&HL8|s=s)=Q?>>@U z!LGW_zEf*}g^NwoxBaK@fOYxGVeaH%0`BgD#t(bfg!o<(`S35fRhg7j%~IIj9DA|; z;86m;>&G|teD%M${HqeWZeiR9ngTe_g8XfYnu4UD4%%)Xx=$E >dD)El z298{IobP-29wfl&>xXuVQDaW$YESZ;=iQl(wq6vewEr&Fa+c`q1CX3mOpoPDg)3+! zByUOJFP61jNgD`was^&*eC}fA$bWgTRh`3Hth=olqX~W^9{)81`+q4MF-G6HLz-wo z>qJ7CBo}sgm}j= KLnj!*dHamO-lfzecL%1wqtXId}9 zbTSMMi`xo;$FP*t$F)$Z^x?6w8N6*CYm5b6zNJgSmC}&_*5C1vCA-3NLd0x`^Lp4? zf?KK@Oas$g^KCgFq3XB972XM4R>@&L528!*8xmaaXg7%AMA2%(>Up|ffCHbvz+^K! zi<__c9jCEGkAxNOz;*Hd*g8h)qW-j>B4vS;mg{5@bV7Fz0mTI;v3YNs=KBHH-B!nI zBaCER_l-+7t!tfjzu%~-)5bCdKTl<`4$f#~;1MD|>0B|nL`jG^g^U
Fk8 zmN%s}A#$zic6v-Dv%RX8q19CAZj9ZqlbG60OTc$_y8O_e>}-om<{am&l6^j&4M-jQ zy;^1V5|=8$xWX3mHlgHQ?R@EFwChe4L&DXOpS{?fl8pOcT_wuO>vsA6$Xy5QYFYVO z?_S~%s7rHK?0yq6x!L`q2QM2bdoJW>M2Qh3Zz +>HB|SHiHkfTuQl`-lGaq!O5~?GLm|w`i3& z{9T*fOToR; @*ZYnoz{8 z8|-@`X;} >U(;o$s?$tW z^&dP >z4b8~vZ2(~k!NoZTBBw-zdwk9wr~IHjE<1_KqTxd+^Fb2eWy(O zR1`jzN#LXCq%l;r{m*e4v0Ba-vQ`o5=cmxf&zR!0ns97-*3HDE39TZG>rn)J=9;!N zLZFus`PKuzwY*IJxq%sywajw9SiOcZYELw6rSWWGb)$I4Tms&U#FAyBbntMEr5}2= zkGoL5w&8k>XCRb~OT4tIwg4Tz?MbjFx(k5~l% 7^X?llA6;w7ybM3xVqLrb=Ky)NwNHo@l-&ak`2gZY&xx zK0a(C4_=hSIgwot7S!RpydR!b9Y}0(>_w}n@3A)vG$n8jWj70?!B}+?PfRZH7y=lh zPxhRN!+P93Z_pRkZ+FQV6KK?V#Prk$^#mK3KCZh=?6T!4gpt^8@0t;T?zo|ZQ89yp zd#1YVBc^CFO3uGez2>ESArNewDO1=M!k4LMe#6at&?9peES=;WDqn>KF7}OE<~uaO z KHkYzzK~5@1M8jOBLHj{hg6rF6 zWCXNucE2D`-YJg7+5}>esMbBvYptPmS@Qh#*Mh}^=`|we+mFj|dBY9EpxZ%khOrq; zH0`&0 {+&Z+F!mui1jZ>ja9hPHJ_Uv{QIf!~$ zrh6WL=_1;V%(2fMk$MFO+PF<&H75Pm6s2wJ5$D7ez{|U|bO%qEj;Bn*>6 sWG&$s=!f>anDLK7ND3 za>h`~TOls}LnI9YU;d*EE#5P4qZ+=|fF)T9gjd-@P-Ez&{;h&}@v#eyyxQpP=7fIs z=c6{yb@o7~#d^q7NBYf(w)LgjMvnb-UAATh>w*g-przE|c8a+l3*}i8o1f2Y5pO3D zeiQhV$G7NlKefRwO?mv+^^YE$DZcX#QA|++Uu0C*dS{s{Q5-QSk1;OW_jaj4o5*7} z{N|CPa&FRgqPk0Xczsp&{}?UC;ej%Ptcowd%)JSdY-kwViUc8KK2valnEtOM6}%fR zX1fRU>lraZ*g{?=Uk*}$;Z7-{hU*}Mla(n_&Mu{f?0Eugwh+H4^LvUExrBgS@fOHi zvbhHqr9@s05a+&4>ME`?TZVyv`y?hJT1@+ccsT?SDxm-$WL~$}S_y_774rw@fp{%o zN}kF=Hl^zhqa9(&p2|efe_1l|kPl7mHh{{#o06oJ>`Gb5m@3X#r!uMnu8k?sd{_P< ztmsJ?Pf`}Kh3#$qwd4Df@D}!Wze4t>=qj#2W zy;y9vZaeHtXD!#5uYF8*|L|H=8?|$dM` T4*!dbyO1{7Pc#ccMb JLMa!~PPLZV|DpB549#8jTw4{NV zi(;c@)ND-Wg388H0t{2$@{XXqfc Xv z+{C)mXaiM~vb43Q$D0NBWW8Iz`C#`dww1;i;J;-IX~t_xGZM)vdb+{D%=hOH4aK-M ziWI-VNv7Y81REh~Hqu%h-Ko0E$D*$mnD$DXC;1JhPes&f(Nd}t5qz6|imXQ#e~EjX zYtLK)5Y@lpPZhyjH{d#2>{uF4tUj#RIb?izIjSF?clP}5H7w1ZXx~UIj^L>Eau<7l z5?UPnv-KPOgzvsY>tXJ$=CD(fcHUN(8u*kI&}8;3%f66$w7G~6Ff(GE>VB10xM^LZ z+>Y%%Zg@Rp!yx^UbQi||eKPNQ@7xXpvzp@sh*r09B{r*X_85w?+xImT#gf)S6WKpf zblz+)QO@KERrTos!;qo8p}SHT?`1GqyEN8g{&w_9NIE*@QJWEa%)Y`Ko7xYa^cizo z8@nU5bZKmA0V}a}*E5hd&dF%<|J8AwK}~I4Tcn6o5m0)M(n%!=AVq0Xl@=rk7 86CL^=deKv025k>24_qzEE4px^}*Q99CVs2|?)z3=F}XXdQ7pJ%Og_ROC1XU)!% zY)y$vH>v23tAF0)Z7? pHRfBB8rzHlJyJL{WQGwX{N zP#&YhL6r@r4}(0s_@6j39k%fp3fFl)KU#d&JhHKs$f*4g#YHx`Y-;0krTmbNYELlD zQ$Wcaxtq31OIL?a=@J;8-->rGoNh@9V3^=hc?Gn#b&zqRt(9`9zT){uCR<6xy@gBP zDf0jlpyN52LSCq_^vB~PHFV$BTTp=~8@@K-FbzfstBlCVV#uWt5xbtG{Es7%rcUU~ z=kq`G-oNNnWsW_17M4;lBzF^ovRBrzTPpXHFRmgT6z96Hi_ksxht8CzcUvcRGs;;{ zy7SOhl M zpzXr*-X@oQ$Pcx)bET8n&5~9vl{+G1`Hl54Ex|QB9ic8u$_5|gDBE~@vX+ZgweC}6 zpn=m^%3Ao5m5aaRl0%5h?U}G*k_`~8e1*_T( }I;}fcYH@w1-aZbYf>_expg= z`?!MO>WIoUcd)TIew)NSa`)-X$L%A*1D~YxrB_WlO{MtBlWHHYI}eKEdGng>>}Q9I zsARfUB6h0}H(d~<71f-rZ&uCz=IrNXchy{Zqh%Cnv9eo!1twhYCDIMG;L1QH`M?<) zZRmDHy_3C`jTbkxSSvWVuXLSRon`k~z3z9z@{G*{l#k1urOmTD9gVKNo%f%YeD3Jc zsQW^8&rd3GekFw0o%;}LRm8N5(S*vjwVoh7e~roGIjykVdyv0hwh?iW^rQ_W7qtlj zPi@fjn86f;mJ^dR)@7vGhvb1KPQWEdH7KCYc)hl#{tnB4p|t>+!6EQt08a0bRcGrF z zSuhCLUpApIeR3z1Hi3y&MQjO%?< zI2PI%5%!QRz-&YVnCc*7crA1M)&e6mq!0mlEjyCYwjECJUVQY>XW%PI-4Ds!u{mr5 zDDRGvs+u)%Rxw*AEQBq93a^Umju kn^3gK1FF>2KTm)1$hL6b|+>ZIW0t=|K7KA>Aso4cV}ffWnGUz z*vOfRO2@2wtW7F{+aMd@QhU5P2DK{=kJL-N@1mg1pT`iysMMr5J@S6Ft(MafR+2&2 z>*<79ao7;FE`pAA4YNlNos3@HBUg8zvsNR&O3aCo2!IkG*o8UO1mn0JOvA~S<}{+8 zRhgVe2&|$)HiT5s#Xvf)yYpQM>H2fX^y`594x$L3NiM413^WK%Uo{Qc1mo^ vNZ79s>T1Q&z^KppCtIy~c}ZNfpGG)0Ag|N^~g!(XxlU>Z}oq zGTt_@8>G6c1byr(derwy$V;QolQe!6fz(wwlNPl@j>sj5UF2fWAora-KYQ5dKuPi^ z`G*~mi#H#FGTEzmMXNL|M6|RHxxBMJ#i%Q0UDR?1&W=t aouH(Az44JzHbnPY=Qdbpk$ZV~D>ieEiH9ovQ?Gdp^a2-cAdQRIYwjhVihKF`E z=BEWbmXcSBkEZ9+avtSqS<6-lvI84oPHC6CjDwgWU%Wm;zrlXg``{?S7j0JbM$k-u ziw}_uWb?&0gxi10!l&eZX;l_uR}@d^id7Fj8&&{1$<49`DGLW4yIrX|d-3q`A4D7n ztK4%T<=xG1PMtM )V#TZld% zG8C?9&8w&9F?y43eMtAeX4t0Ikc+(hq|t;f71$j-!9!0G(7WO&&YU$79$%EnM(?6I zAkL$2<0u`SwF(cpFd&vXmd#e}C@`eD;6zQw`!+)_ZRc)-m*UY$JRf9$Unwg~$39cB zGT=O!FI9J)tz)&%&HcrK(>allh2Fthm)<|I9#lR1)Sut^($ys1dR6)D9aHMnK}Q9B zwQL{ YBAL?9WYpcwlMciUHXg99f>%nqkIr26;oCVg6Xfp@9XN$Lh>_n z*kH<9a%47t;+L%0IriPC#+S>p7zGW7+<0ztczLO`{DaSZ`Pp^ZDxijh4WEHWSDPq5 zl&kv|bXQ4xBNU%K<2H=Hvj4rUa5QT_0oa8#s4c4+DZjMY;7nJI@?w~<5ha>)l$Ytr zr!U?#yxGftmGweHs!7^Lyj<`3WnHFp*pA&w^j9`X#zwwCVPBMQ9Kfu;FU7onzuvO> z$P=SpVnjjIT=2!0ptRrjMf0kJDjupdWghqz?3*9i*a3SRpBy?EVxteS PVG&7RMvySOD9Q%vP^Y8Ji{z#Zrp zIzMOXe4-`#==tlTcpT!LyY?x{ks?3g2dGIcyTB-5OR6XN6%XjDJa?2P`!4_f5z}c( z67xFc4dl6sMwdiPSwprR$C`=d$lDvcO%@Y(S%@c1F8n*nB5N{41t-#)>Y *y-r8O ~G-T1UwpnCEmu!+5HUSA+WzW$5oGQ z qZF!aB9@DTW4sf7?=un_V%* 7*#!^vAxS6b1|}cK1U2pOSt?dQYoCs+MhK-Z++ukYUq)2VVwc3^$Hb! zGi%B8wU@7AkV4O(nLuN4^+Y!|<&__f5*%Q4N;aTT?+(EgZP2<^f0kwVmIRC68pU&b z$3Jzj5GByn8XLIYSQrbeO?~%Wpj`3XsfwvLSSJNiHhp@BXSVGqJ(!H@GRg4w@y3?3 zy48V;MK}T5kp-vj?VQEc)R}$W)%8*jr>T(_PbcKpg68Nf|Dd$IEq$+|5ZtRG=__#b z2S2uZp2&g>Nvsl%7m;vd%mb15g_yG|P7i47VAQOQ6sRTQ2W|lIt{OTsLfl3Qtepu1 zwAwL=vX|Ggy%1tG> Kyl3k#lr3i=_?V7xeDh+-F`o7-@2?m54-vIV6oV3pmBR!6JKKuX0a7 z!WF?SHgVz2;lPUrHPoU?VKIrG55>Zo=||OAc)2_$PeRWOX^Vt&mV~{emule&%M`YY z_Q(X;F~^eQ%i1XsB_ag`ahB`~@yz%J)nJ{n^Y7j@9$T7u(b7y%1A?g5S=-5v+P*ja z4HC28&|HZ&p}D~2JV|~qS6eSed%<>G=ksNoYe7=pj6k&7W?`EMoYTl{ip cg`HE+tOgwLk5>S+;M`O09XUdZjOEbrMEXKzy~92_q?>AD_AyD`C36h5qOpisdx1 zSwB$kW1=2sP1X*2KnGNheBL)aS>m_a+gNEVs@3RnQ1f5^l9ZFO*;P809AuVq^j0tw z_ZY`Ol*an}dvJ5HWa2#>NBL9SIVxH_1P#R!%kfggX}kpSye0G`3`Nwnlr!`_=eMUb zV+-$BRbDAw{&=Y)qAcK2FuHfpY&p0H10)p24Dy-P;>XS8HpOS0icE|jb-Kq&^;?CJ zMo8ZV52`w+P#rGbs1JWw%}u*umx-O0d;qqg_}S|RZ69VwO9BODjh3%%1v1SYR(yVL z+|$9{v>f??xrp=8>ab9?-AWnD`i=Zn<)E<%m)#nj>6LM}(Q@Y~!B{Nj(+_?z&(I!5 z(kw%oK!P)l@Ll31 &F)Qgj$GmV{%17_9u8Qkgc@`JU-Y~_B-lC(ZZQ>HjDbr zwd?MLCQr Da4bZT_OW-bXcm34xpgg1E1;WM@~yDHG;4l`oZFkV7d$&3?oFv7%7*VL!IJ7# zLOEf?joDChD6wtsRKe2}vBns-JO@d?hp ySI#6Dq1;8IJc-q`@!PrW80W9;#H3)0bJR;5681|zqDdht8UF*IA1sOza8 z0hL-A0gV=+)Efuc-40I5t6k{D@gW31c?bLE&u4~HPRVP1rMM?J(;W9rRv0BAkHPMi z?LYTH9$W52T7tL=0tFTV|9pgQ2{HU-K*0zE?3WlAA`eCU7K20NiLL}OE-)N|`ehLU zfgoVST!I(?mIuRsSp-9nsNX6;!7#`#I4Bqe`Mo<7`nMPajza#zg+QR_pZS3Q{ checkUpQueueUpdateListener = this::handleGetCheckUpQueueUpdateResponse; private final ResponseListener checkUpQueueListener = this::handleGetCheckUpQueueResponse; - private final ResponseListener customerHistoryListener = this::handleGetCustomerHistoryResponse; + private final ResponseListener patientHistoryListener = this::handleGetPatientHistoryResponse; private final ResponseListener districtResponseListener = this::handleGetDistrictResponse; private final ResponseListener wardResponseListener = this::handleGetWardResponse; private final ResponseListener callPatientResponseListener = this::handleCallPatientResponse; @@ -264,7 +271,7 @@ public class CheckUpPage extends JPanel { ClientHandler.addResponseListener(GetCheckUpQueueResponse.class, checkUpQueueListener); ClientHandler.addResponseListener(GetCheckUpQueueUpdateResponse.class, checkUpQueueUpdateListener); - ClientHandler.addResponseListener(GetCustomerHistoryResponse.class, customerHistoryListener); + ClientHandler.addResponseListener(GetPatientHistoryResponse.class, patientHistoryListener); ClientHandler.addResponseListener(GetDistrictResponse.class, districtResponseListener); ClientHandler.addResponseListener(GetWardResponse.class, wardResponseListener); ClientHandler.addResponseListener(CallPatientResponse.class, callPatientResponseListener); @@ -1047,7 +1054,8 @@ public class CheckUpPage extends JPanel { public void mouseClicked(MouseEvent e) { if (e.getClickCount() == 1) { int selectedRow = historyTable.getSelectedRow(); - // if (selectedRow != -1) { System.out.println("Clicked on history row: " + selectedRow); } + // send getPatientInfoByCheckupIdReq + } } }); @@ -1436,9 +1444,9 @@ public class CheckUpPage extends JPanel { customerLastNameField.setText(selectedPatient.getCustomerLastName()); customerFirstNameField.setText(selectedPatient.getCustomerFirstName()); doctorComboBox.setSelectedItem(selectedPatient.getDoctorName()); - suggestionField.setText(selectedPatient.getSymptoms()); - diagnosisField.setText(selectedPatient.getDiagnosis()); - conclusionField.setText(""); // Clear conclusion field for now + suggestionField.setText(selectedPatient.getSuggestion()); // De nghi + diagnosisField.setText(selectedPatient.getDiagnosis()); // Chuan doan + conclusionField.setText(selectedPatient.getConclusion()); // Ket luan // Handle RTF notes content String notes = selectedPatient.getNotes(); @@ -1469,7 +1477,7 @@ public class CheckUpPage extends JPanel { genderComboBox.setSelectedItem(selectedPatient.getCustomerGender()); checkupTypeComboBox.setSelectedItem(selectedPatient.getCheckupType()); - NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetCustomerHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId()))); + NetworkUtil.sendPacket(ClientHandler.ctx.channel(), new GetPatientHistoryRequest(Integer.parseInt(selectedPatient.getCustomerId()))); String fullAddress = selectedPatient.getCustomerAddress(); String[] addressParts = fullAddress.split(", "); @@ -1578,9 +1586,13 @@ public class CheckUpPage extends JPanel { } } - private void handleGetCustomerHistoryResponse(GetCustomerHistoryResponse response) { - log.info("Received customer history"); + private void handleGetPatientHistoryResponse(GetPatientHistoryResponse response) { + log.info("Received patient history"); this.history = response.getHistory(); + + // Access PatientHistory objects if needed + List patientHistories = response.getPatientHistoryList(); + historyModel.setDataVector(this.history, new String[]{"Ngày Tháng","Mã khám bệnh", "Triệu chứng", "Chẩn đoán", "Ghi chú"}); } @@ -2400,6 +2412,29 @@ public class CheckUpPage extends JPanel { setIconImage(new ImageIcon("src/main/java/BsK/client/ui/assets/icon/database.png").getImage()); setSize(800, 600); setLayout(new BorderLayout()); + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (mainFrame != null) { + mainFrame.setState(java.awt.Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (mainFrame != null && mainFrame.getState() == java.awt.Frame.ICONIFIED) { + mainFrame.setState(java.awt.Frame.NORMAL); + } + } + + @Override + public void windowClosing(WindowEvent e) { + // Hide instead of closing to maintain state + setVisible(false); + } + }); String[] columns = {"Mã khám bệnh", "Ngày sinh", "Họ", "Tên", "Bác Sĩ", "Loại khám"}; queueTableModel = new DefaultTableModel(preprocessPatientDataForTable(patientQueue), columns) { @@ -2622,7 +2657,7 @@ public class CheckUpPage extends JPanel { String customerNumber = customerPhoneField.getText(); String customerWeight = customerWeightSpinner.getValue().toString(); String customerHeight = customerHeightSpinner.getValue().toString(); - String doctorName = (String) doctorComboBox.getSelectedItem(); + String doctorId = String.valueOf(doctorComboBox.getSelectedIndex() + 1); String suggestions = suggestionField.getText(); String diagnosis = diagnosisField.getText(); String conclusion = conclusionField.getText(); @@ -2642,7 +2677,7 @@ public class CheckUpPage extends JPanel { log.info("customerNumber: {}", customerNumber); log.info("customerWeight: {}", customerWeight); log.info("customerHeight: {}", customerHeight); - log.info("doctorName: {}", doctorName); + log.info("doctorId: {}", doctorId); log.info("suggestions: {}", suggestions); log.info("diagnosis: {}", diagnosis); log.info("conclusion: {}", conclusion); @@ -2653,7 +2688,7 @@ public class CheckUpPage extends JPanel { log.info("servicePrescription: {}", (Object) servicePrescription); log.info("=== End SaveCheckupRequest Data ==="); - // Create and send the save request + SaveCheckupRequest request = new SaveCheckupRequest( checkupId, checkupDate, @@ -2666,12 +2701,13 @@ public class CheckUpPage extends JPanel { customerNumber, customerWeight, customerHeight, - doctorName, - suggestions, - diagnosis, + doctorId, + suggestions, // De nghi + diagnosis, // Chuan doan rtfContent, // Send RTF content as notes status, checkupType, + conclusion, // Ket luan medicinePrescription, servicePrescription ); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java index d617537..01fcc30 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/MedicineDialog/MedicineDialog.java @@ -8,6 +8,7 @@ import javax.swing.table.TableColumnModel; import BsK.client.network.handler.ClientHandler; import BsK.client.network.handler.ResponseListener; +import BsK.common.entity.Medicine; import BsK.common.packet.req.GetMedInfoRequest; import BsK.common.packet.res.GetMedInfoResponse; import BsK.common.util.network.NetworkUtil; @@ -21,6 +22,8 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -47,6 +50,7 @@ public class MedicineDialog extends JDialog { private JTable selectedTable; private boolean isProgrammaticallySettingMedicineNameField = false; + private List medicines = new ArrayList<>(); private String[][] medicinePrescription; private static final Logger logger = LoggerFactory.getLogger(MedicineDialog.class); @@ -57,6 +61,11 @@ public class MedicineDialog extends JDialog { void getMedInfoHandler(GetMedInfoResponse response) { logger.info("Received medicine data"); medicineData = response.getMedInfo(); + + // Get Medicine objects from the response + medicines = response.getMedicines(); + + // Set the data vector with the raw string arrays for backward compatibility tableModel.setDataVector(medicineData, medcineColumns); // resize column width @@ -81,6 +90,27 @@ public class MedicineDialog extends JDialog { setSize(1100, 600); setLocationRelativeTo(parent); setResizable(true); + + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); // Initialize medicinePrescription as empty array medicinePrescription = new String[0][0]; @@ -325,12 +355,29 @@ public class MedicineDialog extends JDialog { JButton removeMedicineButton = new JButton("Xóa thuốc đã chọn"); removeButtonPanel.add(removeMedicineButton); + // Create titled panels for right side sections + JPanel availableMedicinesPanel = new JPanel(new BorderLayout()); + availableMedicinesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thuốc trong kho", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(63, 81, 181) + )); + availableMedicinesPanel.add(medicineTableScrollPane, BorderLayout.CENTER); + + JPanel chosenMedicinesPanel = new JPanel(new BorderLayout()); + chosenMedicinesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Thuốc đã chọn", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(220, 20, 60) + )); + chosenMedicinesPanel.setBackground(new Color(255, 240, 240)); // Light red background + selectedTableScrollPane.setBackground(new Color(255, 240, 240)); // Light red background + chosenMedicinesPanel.add(selectedTableScrollPane, BorderLayout.CENTER); + chosenMedicinesPanel.add(removeButtonPanel, BorderLayout.SOUTH); + JPanel rightPanel = new JPanel(new BorderLayout(0, 5)); - rightPanel.add(medicineTableScrollPane, BorderLayout.CENTER); - JPanel bottomRightsPanel = new JPanel(new BorderLayout(0,5)); - bottomRightsPanel.add(selectedTableScrollPane, BorderLayout.CENTER); - bottomRightsPanel.add(removeButtonPanel, BorderLayout.SOUTH); - rightPanel.add(bottomRightsPanel, BorderLayout.SOUTH); + rightPanel.add(availableMedicinesPanel, BorderLayout.CENTER); + rightPanel.add(chosenMedicinesPanel, BorderLayout.SOUTH); JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel); mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); @@ -439,12 +486,16 @@ public class MedicineDialog extends JDialog { medicineTable.clearSelection(); } else { List filteredData = new ArrayList<>(); + List filteredMedicines = new ArrayList<>(); String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); - for (String[] row : medicineData) { - if (row.length > 1 && row[1] != null && TextUtils.removeAccents(row[1].toLowerCase()).contains(lowerCaseFilterText)) { - filteredData.add(row); + + for (Medicine med : medicines) { + if (TextUtils.removeAccents(med.getName().toLowerCase()).contains(lowerCaseFilterText)) { + filteredMedicines.add(med); + filteredData.add(med.toStringArray()); } } + tableModel.setDataVector(filteredData.toArray(new String[0][0]), medcineColumns); resizeMedicineTableColumns(); if (!filteredData.isEmpty()) { @@ -534,18 +585,23 @@ public class MedicineDialog extends JDialog { currentPrice = tableModel.getValueAt(modelRow, 6).toString(); } else { String searchName = medicineNameField.getText().trim(); - String foundId = null, foundUnit = null, foundPrice = null, actualName = null; - for (String[] med : medicineData) { - if (TextUtils.removeAccents(med[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { - foundId = med[0]; actualName = med[1]; foundUnit = med[5]; foundPrice = med[6]; + Medicine foundMedicine = null; + + for (Medicine med : medicines) { + if (TextUtils.removeAccents(med.getName().toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { + foundMedicine = med; break; } } - if (foundId == null) { + + if (foundMedicine == null) { JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho thuốc: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE); return; } - medicineId = foundId; name = actualName; unit = foundUnit; currentPrice = foundPrice; + medicineId = foundMedicine.getId(); + name = foundMedicine.getName(); + unit = foundMedicine.getUnit(); + currentPrice = foundMedicine.getSellingPrice(); } int quantity = (Integer) quantitySpinner.getValue(); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java index 4439a53..1af05bf 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/PrintDialog/MedicineInvoice.java @@ -32,6 +32,8 @@ import java.util.ArrayList; import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.text.DecimalFormat; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; public class MedicineInvoice{ @@ -107,6 +109,27 @@ public class MedicineInvoice{ dialog.setResizable(true); dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); dialog.setLayout(new BorderLayout()); + + // Ensure modal behavior and proper parent relationship + dialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + dialog.setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + dialog.addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); JPanel buttonPanel = new JPanel(new GridLayout(1, 2)); JButton saveButton = new JButton("Save PDF"); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java index f618674..e12bf5a 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/ServiceDialog/ServiceDialog.java @@ -8,6 +8,7 @@ import javax.swing.table.TableColumnModel; import BsK.client.network.handler.ClientHandler; import BsK.client.network.handler.ResponseListener; +import BsK.common.entity.Service; import BsK.common.packet.req.GetSerInfoRequest; import BsK.common.packet.res.GetSerInfoResponse; import BsK.common.util.network.NetworkUtil; @@ -21,6 +22,8 @@ import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -41,6 +44,7 @@ public class ServiceDialog extends JDialog { private JTable selectedTable; private boolean isProgrammaticallySettingNameField = false; + private List services = new ArrayList<>(); private String[][] servicePrescription; private static final Logger logger = LoggerFactory.getLogger(ServiceDialog.class); @@ -51,6 +55,11 @@ public class ServiceDialog extends JDialog { void getSerInfoHandler(GetSerInfoResponse response) { logger.info("Received service data"); serviceData = response.getSerInfo(); + + // Get Service objects from the response + services = response.getServices(); + + // Set the data vector with the raw string arrays for backward compatibility tableModel.setDataVector(serviceData, serviceColumns); resizeServiceTableColumns(); } @@ -66,6 +75,27 @@ public class ServiceDialog extends JDialog { setSize(1100, 600); setLocationRelativeTo(parent); setResizable(true); + + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (parent != null) { + parent.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (parent != null && parent.getState() == Frame.ICONIFIED) { + parent.setState(Frame.NORMAL); + } + } + }); // Initialize servicePrescription as empty array servicePrescription = new String[0][0]; @@ -217,12 +247,29 @@ public class ServiceDialog extends JDialog { JButton removeServiceButton = new JButton("Xóa dịch vụ đã chọn"); removeServiceButtonPanel.add(removeServiceButton); + // Create titled panels for right side sections + JPanel availableServicesPanel = new JPanel(new BorderLayout()); + availableServicesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Dịch vụ có", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(63, 81, 181) + )); + availableServicesPanel.add(serviceTableScrollPane, BorderLayout.CENTER); + + JPanel chosenServicesPanel = new JPanel(new BorderLayout()); + chosenServicesPanel.setBorder(BorderFactory.createTitledBorder( + BorderFactory.createEtchedBorder(), "Dịch vụ đã chọn", + javax.swing.border.TitledBorder.LEADING, javax.swing.border.TitledBorder.TOP, + new Font("Arial", Font.BOLD, 14), new Color(220, 20, 60) + )); + chosenServicesPanel.setBackground(new Color(255, 240, 240)); // Light red background + selectedTableScrollPane.setBackground(new Color(255, 240, 240)); // Light red background + chosenServicesPanel.add(selectedTableScrollPane, BorderLayout.CENTER); + chosenServicesPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH); + JPanel rightPanel = new JPanel(new BorderLayout(0,5)); - rightPanel.add(serviceTableScrollPane, BorderLayout.CENTER); - JPanel bottomRightPanel = new JPanel(new BorderLayout(0,5)); - bottomRightPanel.add(selectedTableScrollPane, BorderLayout.CENTER); - bottomRightPanel.add(removeServiceButtonPanel, BorderLayout.SOUTH); - rightPanel.add(bottomRightPanel, BorderLayout.SOUTH); + rightPanel.add(availableServicesPanel, BorderLayout.CENTER); + rightPanel.add(chosenServicesPanel, BorderLayout.SOUTH); JScrollPane mainInputScrollPane = new JScrollPane(mainInputPanel); mainInputScrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED); @@ -333,30 +380,30 @@ public class ServiceDialog extends JDialog { private void filterServiceTable() { String filterText = serviceNameField.getText().trim(); if (serviceData == null) return; - if (filterText.isEmpty()) { tableModel.setDataVector(serviceData, serviceColumns); resizeServiceTableColumns(); serviceTable.clearSelection(); } else { List filteredData = new ArrayList<>(); + List filteredServices = new ArrayList<>(); String lowerCaseFilterText = TextUtils.removeAccents(filterText.toLowerCase()); - for (String[] row : serviceData) { - if (row.length > 1 && row[1] != null) { - String cellValue = TextUtils.removeAccents(row[1].toLowerCase()); - if (cellValue.contains(lowerCaseFilterText)) { - filteredData.add(row); - } + + for (Service service : services) { + if (TextUtils.removeAccents(service.getName().toLowerCase()).contains(lowerCaseFilterText)) { + filteredServices.add(service); + filteredData.add(service.toStringArray()); } } + tableModel.setDataVector(filteredData.toArray(new String[0][0]), serviceColumns); resizeServiceTableColumns(); - if (!filteredData.isEmpty()) { + if (!filteredData.isEmpty()) { serviceTable.setRowSelectionInterval(0,0); handleServiceTableRowSelection(0); } else { - clearInputFields(); - } + clearInputFields(); + } } } @@ -416,9 +463,7 @@ public class ServiceDialog extends JDialog { return; } - String serviceId; - String name; - String currentPrice; + String serviceId, name, currentPrice; if (selectedRowInServiceTable != -1) { int modelRow = serviceTable.convertRowIndexToModel(selectedRowInServiceTable); @@ -427,26 +472,22 @@ public class ServiceDialog extends JDialog { currentPrice = tableModel.getValueAt(modelRow, 2).toString(); } else { String searchName = serviceNameField.getText().trim(); - String foundId = null; - String foundPrice = null; - String actualName = null; - - for (String[] serv : serviceData) { - if (TextUtils.removeAccents(serv[1].toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { - foundId = serv[0]; - actualName = serv[1]; - foundPrice = serv[2]; + Service foundService = null; + + for (Service service : services) { + if (TextUtils.removeAccents(service.getName().toLowerCase()).equals(TextUtils.removeAccents(searchName.toLowerCase()))) { + foundService = service; break; } } - - if (foundId == null) { + + if (foundService == null) { JOptionPane.showMessageDialog(this, "Không tìm thấy ID cho dịch vụ: " + searchName + ". Vui lòng chọn từ bảng.", "Lỗi", JOptionPane.ERROR_MESSAGE); return; } - serviceId = foundId; - name = actualName; - currentPrice = foundPrice; + serviceId = foundService.getId(); + name = foundService.getName(); + currentPrice = foundService.getCost(); } int quantity = (Integer) quantitySpinner.getValue(); diff --git a/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java b/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java index 871f02b..2d01b35 100644 --- a/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java +++ b/src/main/java/BsK/client/ui/component/CheckUpPage/TemplateDialog/TemplateDialog.java @@ -14,6 +14,8 @@ import javax.swing.text.StyleConstants; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; +import java.awt.event.WindowAdapter; +import java.awt.event.WindowEvent; @Slf4j public class TemplateDialog extends JDialog { @@ -46,6 +48,27 @@ public class TemplateDialog extends JDialog { super(mainFrame, "Quản lý mẫu", true); this.mainFrame = mainFrame; + // Ensure modal behavior and proper parent relationship + setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + setAlwaysOnTop(false); // Don't force always on top, let modal behavior handle this + + // Add window listener to handle minimize/restore events with parent + addWindowListener(new WindowAdapter() { + @Override + public void windowIconified(WindowEvent e) { + if (mainFrame != null) { + mainFrame.setState(Frame.ICONIFIED); + } + } + + @Override + public void windowDeiconified(WindowEvent e) { + if (mainFrame != null && mainFrame.getState() == Frame.ICONIFIED) { + mainFrame.setState(Frame.NORMAL); + } + } + }); + initializeComponents(); setupLayout(); setupListeners(); diff --git a/src/main/java/BsK/common/entity/Medicine.java b/src/main/java/BsK/common/entity/Medicine.java new file mode 100644 index 0000000..bd6341f --- /dev/null +++ b/src/main/java/BsK/common/entity/Medicine.java @@ -0,0 +1,46 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Medicine + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Medicine { + private String id; + private String name; + private String company; + private String description; + private String quantity; + private String unit; + private String sellingPrice; + + /** + * Constructor to create Medicine from array data + * @param data Array of medicine data from backend + */ + public Medicine(String[] data) { + if (data.length < 7) { + throw new IllegalArgumentException("Medicine data array must contain at least 7 elements"); + } + this.id = data[0]; + this.name = data[1]; + this.company = data[2]; + this.description = data[3]; + this.quantity = data[4]; + this.unit = data[5]; + this.sellingPrice = data[6]; + } + + /** + * Convert the Medicine entity to a String array + * @return String array representation of Medicine + */ + public String[] toStringArray() { + return new String[]{id, name, company, description, quantity, unit, sellingPrice}; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/entity/Patient.java b/src/main/java/BsK/common/entity/Patient.java index 11f5f90..5152ee0 100644 --- a/src/main/java/BsK/common/entity/Patient.java +++ b/src/main/java/BsK/common/entity/Patient.java @@ -12,7 +12,7 @@ public class Patient { private String customerLastName; private String customerFirstName; private String doctorName; - private String symptoms; + private String suggestion; private String diagnosis; private String notes; private String status; @@ -24,6 +24,7 @@ public class Patient { private String customerGender; private String customerDob; private String checkupType; + private String conclusion; public Patient(String[][] array) { this.checkupId = array[0][0]; @@ -31,7 +32,7 @@ public class Patient { this.customerLastName = array[0][2]; this.customerFirstName = array[0][3]; this.doctorName = array[0][4]; - this.symptoms = array[0][5]; + this.suggestion = array[0][5]; this.diagnosis = array[0][6]; this.notes = array[0][7]; this.status = array[0][8]; @@ -43,6 +44,7 @@ public class Patient { this.customerGender = array[0][14]; this.customerDob = array[0][15]; this.checkupType = array[0][16]; + this.conclusion = array[0][17]; } // New constructor for a single patient data row @@ -55,7 +57,7 @@ public class Patient { this.customerLastName = data[2]; this.customerFirstName = data[3]; this.doctorName = data[4]; - this.symptoms = data[5]; + this.suggestion = data[5]; this.diagnosis = data[6]; this.notes = data[7]; this.status = data[8]; @@ -67,5 +69,6 @@ public class Patient { this.customerGender = data[14]; this.customerDob = data[15]; this.checkupType = data[16]; + this.conclusion = data[17]; } } diff --git a/src/main/java/BsK/common/entity/PatientHistory.java b/src/main/java/BsK/common/entity/PatientHistory.java new file mode 100644 index 0000000..c5e5826 --- /dev/null +++ b/src/main/java/BsK/common/entity/PatientHistory.java @@ -0,0 +1,44 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Patient History + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class PatientHistory { + private String checkupDate; + private String checkupId; + private String suggestion; + private String diagnosis; + private String prescriptionId; + private String notes; + + /** + * Constructor to create PatientHistory from array data + * @param data Array of patient history data from backend + */ + public PatientHistory(String[] data) { + if (data.length < 6) { + throw new IllegalArgumentException("PatientHistory data array must contain at least 6 elements"); + } + this.checkupDate = data[0]; + this.checkupId = data[1]; + this.suggestion = data[2]; + this.diagnosis = data[3]; + this.prescriptionId = data[4]; + this.notes = data[5]; + } + + /** + * Convert the PatientHistory entity to a String array + * @return String array representation of PatientHistory + */ + public String[] toStringArray() { + return new String[]{checkupDate, checkupId, suggestion, diagnosis, prescriptionId, notes}; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/entity/Service.java b/src/main/java/BsK/common/entity/Service.java new file mode 100644 index 0000000..9b88414 --- /dev/null +++ b/src/main/java/BsK/common/entity/Service.java @@ -0,0 +1,38 @@ +package BsK.common.entity; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * Entity class for Service + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class Service { + private String id; + private String name; + private String cost; + + /** + * Constructor to create Service from array data + * @param data Array of service data from backend + */ + public Service(String[] data) { + if (data.length < 3) { + throw new IllegalArgumentException("Service data array must contain at least 3 elements"); + } + this.id = data[0]; + this.name = data[1]; + this.cost = data[2]; + } + + /** + * Convert the Service entity to a String array + * @return String array representation of Service + */ + public String[] toStringArray() { + return new String[]{id, name, cost}; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java b/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java new file mode 100644 index 0000000..d1285be --- /dev/null +++ b/src/main/java/BsK/common/packet/req/GetPatientHistoryRequest.java @@ -0,0 +1,14 @@ +package BsK.common.packet.req; + +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + + +@Slf4j +@AllArgsConstructor +@Data +public class GetPatientHistoryRequest implements Packet { + int patientId; +} \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java index c3852b0..69b1637 100644 --- a/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java +++ b/src/main/java/BsK/common/packet/req/SaveCheckupRequest.java @@ -20,12 +20,13 @@ public class SaveCheckupRequest implements Packet { private String customerNumber; // Phone number private String customerWeight; private String customerHeight; - private String doctorName; // Or doctorId if preferred for backend processing - private String symptoms; + private String doctorId; // Or doctorId if preferred for backend processing + private String suggestions; private String diagnosis; private String notes; private String status; private String checkupType; + private String conclusion; private String[][] medicinePrescription; // Structure: [med_id, name, quantity, unit, dose_morning, dose_noon, dose_evening, unit_price, total_price, notes] private String[][] servicePrescription; // Structure: [ser_id, name, quantity, unit_price, total_price, notes] } \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java b/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java index c879912..effaa6a 100644 --- a/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java +++ b/src/main/java/BsK/common/packet/res/GetMedInfoResponse.java @@ -1,13 +1,51 @@ package BsK.common.packet.res; +import BsK.common.entity.Medicine; import BsK.common.packet.Packet; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.List; + @Slf4j @Data @AllArgsConstructor public class GetMedInfoResponse implements Packet { private String[][] medInfo; + private transient List medicines; + + public GetMedInfoResponse(String[][] medInfo) { + this.medInfo = medInfo; + this.medicines = convertToMedicineList(medInfo); + } + + /** + * Converts the raw string array data to a list of Medicine objects + * @param medInfo Raw medicine data as string arrays + * @return List of Medicine objects + */ + private List convertToMedicineList(String[][] medInfo) { + List result = new ArrayList<>(); + if (medInfo != null) { + for (String[] data : medInfo) { + if (data.length >= 7) { + result.add(new Medicine(data)); + } + } + } + return result; + } + + /** + * Returns the medicine list, converting from raw data if needed + * @return List of Medicine objects + */ + public List getMedicines() { + if (medicines == null && medInfo != null) { + medicines = convertToMedicineList(medInfo); + } + return medicines; + } } diff --git a/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java b/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java new file mode 100644 index 0000000..1f5f044 --- /dev/null +++ b/src/main/java/BsK/common/packet/res/GetPatientHistoryResponse.java @@ -0,0 +1,51 @@ +package BsK.common.packet.res; + +import BsK.common.entity.PatientHistory; +import BsK.common.packet.Packet; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.extern.slf4j.Slf4j; + +import java.util.ArrayList; +import java.util.List; + +@Data +@AllArgsConstructor +@Slf4j +public class GetPatientHistoryResponse implements Packet { + private String[][] history; + private transient List patientHistoryList; + + public GetPatientHistoryResponse(String[][] history) { + this.history = history; + this.patientHistoryList = convertToPatientHistoryList(history); + } + + /** + * Converts the raw string array data to a list of PatientHistory objects + * @param history Raw patient history data as string arrays + * @return List of PatientHistory objects + */ + private List convertToPatientHistoryList(String[][] history) { + List result = new ArrayList<>(); + if (history != null) { + for (String[] data : history) { + if (data.length >= 6) { + result.add(new PatientHistory(data)); + } + } + } + return result; + } + + /** + * Returns the patient history list, converting from raw data if needed + * @return List of PatientHistory objects + */ + public List getPatientHistoryList() { + if (patientHistoryList == null && history != null) { + patientHistoryList = convertToPatientHistoryList(history); + } + return patientHistoryList; + } +} \ No newline at end of file diff --git a/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java b/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java index 5276232..f834346 100644 --- a/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java +++ b/src/main/java/BsK/common/packet/res/GetSerInfoResponse.java @@ -1,13 +1,51 @@ package BsK.common.packet.res; +import BsK.common.entity.Service; import BsK.common.packet.Packet; import lombok.AllArgsConstructor; import lombok.Data; import lombok.extern.slf4j.Slf4j; +import java.util.ArrayList; +import java.util.List; + @Slf4j @Data @AllArgsConstructor public class GetSerInfoResponse implements Packet { private String[][] serInfo; + private transient List services; + + public GetSerInfoResponse(String[][] serInfo) { + this.serInfo = serInfo; + this.services = convertToServiceList(serInfo); + } + + /** + * Converts the raw string array data to a list of Service objects + * @param serInfo Raw service data as string arrays + * @return List of Service objects + */ + private List convertToServiceList(String[][] serInfo) { + List result = new ArrayList<>(); + if (serInfo != null) { + for (String[] data : serInfo) { + if (data.length >= 3) { + result.add(new Service(data)); + } + } + } + return result; + } + + /** + * Returns the service list, converting from raw data if needed + * @return List of Service objects + */ + public List getServices() { + if (services == null && serInfo != null) { + services = convertToServiceList(serInfo); + } + return services; + } } diff --git a/src/main/java/BsK/server/network/handler/ServerHandler.java b/src/main/java/BsK/server/network/handler/ServerHandler.java index 992d364..8fb40aa 100644 --- a/src/main/java/BsK/server/network/handler/ServerHandler.java +++ b/src/main/java/BsK/server/network/handler/ServerHandler.java @@ -19,6 +19,7 @@ import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler.Han import io.netty.handler.timeout.IdleState; import io.netty.handler.timeout.IdleStateEvent; import java.io.IOException; +import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; @@ -30,6 +31,9 @@ import java.util.HashMap; import lombok.extern.slf4j.Slf4j; +import BsK.common.entity.Medicine; +import BsK.common.entity.Service; +import BsK.common.entity.PatientHistory; import static BsK.server.Server.statement; @@ -94,8 +98,8 @@ public class ServerHandler extends SimpleChannelInboundHandler resultList = new ArrayList<>(); while (rs.next()) { @@ -280,11 +284,11 @@ public class ServerHandler extends SimpleChannelInboundHandler 0) { + + // Get next prescription_id + String getNextIdSql = "SELECT COALESCE(MAX(prescription_id), 0) + 1 as next_id FROM MedicineOrder"; + PreparedStatement nextIdStmt = conn.prepareStatement(getNextIdSql); + ResultSet rs = nextIdStmt.executeQuery(); + if (rs.next()) { + prescriptionId = rs.getString("next_id"); + } + rs.close(); + nextIdStmt.close(); + } + + // 3. Insert/Update Checkup + String checkupSql = """ + INSERT INTO Checkup ( + checkup_id, customer_id, doctor_id, checkup_date, + suggestion, diagnosis, prescription_id, notes, status, checkup_type, conclusion + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(checkup_id) DO UPDATE SET + suggestion = excluded.suggestion, + diagnosis = excluded.diagnosis, + prescription_id = excluded.prescription_id, + notes = excluded.notes, + status = excluded.status, + checkup_type = excluded.checkup_type, + conclusion = excluded.conclusion + """; + + PreparedStatement checkupStmt = conn.prepareStatement(checkupSql); + checkupStmt.setString(1, saveCheckupRequest.getCheckupId()); + checkupStmt.setString(2, saveCheckupRequest.getCustomerId()); + checkupStmt.setString(3, saveCheckupRequest.getDoctorId()); + checkupStmt.setString(4, saveCheckupRequest.getCheckupDate()); + checkupStmt.setString(5, saveCheckupRequest.getSuggestions()); + checkupStmt.setString(6, saveCheckupRequest.getDiagnosis()); + checkupStmt.setString(7, prescriptionId); + checkupStmt.setString(8, saveCheckupRequest.getNotes()); + checkupStmt.setString(9, saveCheckupRequest.getStatus()); + checkupStmt.setString(10, saveCheckupRequest.getCheckupType()); + checkupStmt.setString(11, saveCheckupRequest.getConclusion()); + checkupStmt.executeUpdate(); + log.info("Prescription ID generated or edited: {}", prescriptionId); + log.info("Checkup saved successfully"); + // 4. Handle Medicine Prescriptions + if (saveCheckupRequest.getMedicinePrescription() != null && + saveCheckupRequest.getMedicinePrescription().length > 0 && prescriptionId != null) { + + // Calculate total amount for medicine order + double totalMedicineAmount = 0; + for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) { + if (medicine.length > 8) { + try { + totalMedicineAmount += Double.parseDouble(medicine[8]); // total_price + } catch (NumberFormatException e) { + log.warn("Invalid medicine total price: {}", medicine[8]); + } + } + } + + // Insert/Update MedicineOrder + String medicineOrderSql = """ + INSERT INTO MedicineOrder ( + prescription_id, checkup_id, customer_id, + total_amount, payment_method, status, + payment_status, processed_by, notes, datetime + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_DATE) + ON CONFLICT(prescription_id, checkup_id) DO UPDATE SET + customer_id = excluded.customer_id, + total_amount = excluded.total_amount, + payment_method = excluded.payment_method, + status = excluded.status, + payment_status = excluded.payment_status, + processed_by = excluded.processed_by, + notes = excluded.notes, + datetime = CURRENT_DATE + """; + + PreparedStatement medicineOrderStmt = conn.prepareStatement(medicineOrderSql); + medicineOrderStmt.setString(1, prescriptionId); + medicineOrderStmt.setString(2, saveCheckupRequest.getCheckupId()); + medicineOrderStmt.setString(3, saveCheckupRequest.getCustomerId()); + medicineOrderStmt.setDouble(4, totalMedicineAmount); + medicineOrderStmt.setString(5, ""); // payment_method + medicineOrderStmt.setString(6, "Pending"); // status + medicineOrderStmt.setString(7, "Unpaid"); // payment_status + medicineOrderStmt.setString(8, ""); // processed_by + medicineOrderStmt.setString(9, ""); // notes + medicineOrderStmt.executeUpdate(); + log.info("MedicineOrder saved successfully"); + // Insert OrderItems + String orderItemSql = """ + INSERT INTO OrderItem ( + prescription_id, med_id, quantity_ordered, + dosage, duration, price_per_unit, total_price, checkup_id, notes + ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?) + ON CONFLICT(order_item_id) DO UPDATE SET + quantity_ordered = excluded.quantity_ordered, + dosage = excluded.dosage, + duration = excluded.duration, + price_per_unit = excluded.price_per_unit, + total_price = excluded.total_price, + checkup_id = excluded.checkup_id, + notes = excluded.notes + """; + log.info("OrderItem saved successfully"); + PreparedStatement orderItemStmt = conn.prepareStatement(orderItemSql); + + for (String[] medicine : saveCheckupRequest.getMedicinePrescription()) { + if (medicine.length >= 10) { + orderItemStmt.setString(1, prescriptionId); + orderItemStmt.setString(2, medicine[0]); // med_id + orderItemStmt.setString(3, medicine[2]); // quantity + + // Build dosage string from morning, noon, evening + String dosage = String.format("Sáng %s, Trưa %s, Chiều %s", + medicine[4], medicine[5], medicine[6]); + orderItemStmt.setString(4, dosage); + orderItemStmt.setString(5, ""); // duration - could be calculated or added to request + orderItemStmt.setString(6, medicine[7]); // unit_price + orderItemStmt.setString(7, medicine[8]); // total_price + orderItemStmt.setString(8, saveCheckupRequest.getCheckupId()); + orderItemStmt.setString(9, medicine[9]); // notes + orderItemStmt.addBatch(); + } + } + orderItemStmt.executeBatch(); + } + log.info("Medicine prescriptions saved successfully"); + // 5. Handle Service Prescriptions + if (saveCheckupRequest.getServicePrescription() != null && + saveCheckupRequest.getServicePrescription().length > 0) { + + String serviceSql = """ + INSERT INTO CheckupService ( + checkup_id, service_id, quantity, + total_cost, status, checkup_date + ) VALUES (?, ?, ?, ?, ?, CURRENT_DATE) + ON CONFLICT(order_id) DO UPDATE SET + quantity = excluded.quantity, + total_cost = excluded.total_cost, + status = excluded.status + """; + + PreparedStatement serviceStmt = conn.prepareStatement(serviceSql); + + for (String[] service : saveCheckupRequest.getServicePrescription()) { + if (service.length >= 6) { + serviceStmt.setString(1, saveCheckupRequest.getCheckupId()); + serviceStmt.setString(2, service[0]); // service_id + serviceStmt.setString(3, service[2]); // quantity + serviceStmt.setString(4, service[4]); // total_cost + serviceStmt.setString(5, "PENDING"); // status + + serviceStmt.addBatch(); + } + } + serviceStmt.executeBatch(); + } + log.info("Service prescriptions saved successfully"); + // Commit transaction + conn.commit(); + log.info("Successfully saved checkup {} with all related data", saveCheckupRequest.getCheckupId()); + + UserUtil.sendPacket(currentUser.getSessionId(), new AddCheckupResponse(true, "Checkup saved successfully")); + + } catch (Exception e) { + log.error("Error saving checkup transaction", e); + // Rollback transaction on error + if (conn != null) { + try { + conn.rollback(); + log.info("Transaction rolled back due to error"); + } catch (SQLException rollbackEx) { + log.error("Error rolling back transaction", rollbackEx); + } + } + UserUtil.sendPacket(currentUser.getSessionId(), new ErrorResponse(Error.SQL_EXCEPTION)); + } finally { + // Restore auto-commit + if (conn != null) { + try { + conn.setAutoCommit(true); + } catch (SQLException e) { + log.error("Error restoring auto-commit", e); + } + } + } + } } } diff --git a/src/main/resources/database/BSK.db b/src/main/resources/database/BSK.db index 4a0d49f5e31e7192343e2089153933dbd08d0b05..707351372273f085b600efd78033b6eacb99549a 100644 GIT binary patch delta 231542 zcmdSC2Y3`m*6%&l)jgv;B1l3+iJa*?Gvu6e&LEJG1SlXRGK`5f*d)tvoNd53;6ySQ zFqmY54LGM=du<%nIcKoV{r7a2)3>|t?(4nxc|O@x|LRma6}r0WRI7E>n`5s!+G|6t zD#Lv~R@dkACF38%zwH}a8fn9Qt>>&(97*spTwiMNa$@6M<;#L~`wZwY3`u-JJvn8W z&!6;@FSst{&E)YZnaRhKe%ez-{UFJn;7{?TRf~&LeA&KHJ|lT}-t6*$IYnd((Gc zrA7?XvP0pB+9!8L%dD;^9w;uz>R(cvU%9bnZOd+@=~