From dfd9fe44a4aa5b6dc2eaf4cebf4daa46558d97fb Mon Sep 17 00:00:00 2001 From: Oliver Date: Wed, 23 Jul 2025 20:16:00 +1000 Subject: [PATCH] [Refactor] BOM Validation (#10056) * Add "bom_validated" field to the Part model * Check bom validity of any assemblies when a part is changed * Improved update logic * Fixes for circular imports * Add additional info to BOM validation serializer * More intelligent caching * Refactor * Update API filter * Data migration to process existing BomItem entries * Add "BOM Valid" filter to part table * Add dashboard widget * Display BOM validation status * Tweak dashboard widget * Update BomTable * Allow locked BOM items to be validated * Adjust get_item_hash - preserve "some" backwards compatibility * Bump API version * Refactor app URL patterns * Fix import sequence * Tweak imports * Fix logging message * Fix error message * Update src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update exception handling * Try info level debug * Disable exchange rate update * Add registry ready flag * Add is_ready func * Cleaner init code * Protect against plugin access until ready * Fix dashboard widget filter * Adjust unit test * Fix receiver name * Only add plugin URLs if registry is ready * Cleanup code * Update playwright tests * Update docs * Revert changes to urls.py --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- .github/workflows/qc_checks.yaml | 2 +- docs/docs/assets/images/build/bom_invalid.png | Bin 0 -> 47201 bytes .../assets/images/build/bom_validated.png | Bin 0 -> 76362 bytes docs/docs/manufacturing/bom.md | 36 ++++ .../InvenTree/InvenTree/api_version.py | 5 +- src/backend/InvenTree/part/api.py | 53 +----- .../migrations/0140_part_bom_validated.py | 22 +++ .../migrations/0141_auto_20250722_0303.py | 80 +++++++++ src/backend/InvenTree/part/models.py | 162 ++++++++++++++---- src/backend/InvenTree/part/serializers.py | 38 ++++ src/backend/InvenTree/part/tasks.py | 87 +++++++--- src/backend/InvenTree/part/test_api.py | 87 +++++++++- .../dashboard/DashboardWidgetLibrary.tsx | 12 +- .../widgets/QueryCountDashboardWidget.tsx | 2 +- src/frontend/src/pages/part/PartDetail.tsx | 125 +++++++++++++- src/frontend/src/tables/bom/BomTable.tsx | 63 +++---- src/frontend/src/tables/part/PartTable.tsx | 6 + src/frontend/tests/pages/pui_part.spec.ts | 29 +++- 18 files changed, 641 insertions(+), 168 deletions(-) create mode 100644 docs/docs/assets/images/build/bom_invalid.png create mode 100644 docs/docs/assets/images/build/bom_validated.png create mode 100644 src/backend/InvenTree/part/migrations/0140_part_bom_validated.py create mode 100644 src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py diff --git a/.github/workflows/qc_checks.yaml b/.github/workflows/qc_checks.yaml index 604996e909..4d3959cd65 100644 --- a/.github/workflows/qc_checks.yaml +++ b/.github/workflows/qc_checks.yaml @@ -369,7 +369,7 @@ jobs: INVENTREE_DB_HOST: "127.0.0.1" INVENTREE_DB_PORT: 5432 INVENTREE_DEBUG: true - INVENTREE_LOG_LEVEL: WARNING + INVENTREE_LOG_LEVEL: INFO INVENTREE_CONSOLE_LOG: false INVENTREE_CACHE_HOST: localhost INVENTREE_PLUGINS_ENABLED: true diff --git a/docs/docs/assets/images/build/bom_invalid.png b/docs/docs/assets/images/build/bom_invalid.png new file mode 100644 index 0000000000000000000000000000000000000000..1ecc08f863cb1420632f85e5bdaa9e8d6a1e14d0 GIT binary patch literal 47201 zcmce;cT`hb^e)Od7DNw%6-9~)h=71f@4W;Bq)Uy8NR!?P0mXt!4^64k2?$89NmM#W zFQJ1{0)!ACB!PsGxA7d0=Xc+Gf800TxEbsLJA3c7*P3&#x#pVRH+gecUyJ!T`*8*a z24;}9x)B2d;}r&mKOP@D3VhQ=T#5vK{o!q-rOHs+%drBS9ClFAQ(<5zk2$gT;0SQe zS$oIibYEiCLc({(1@yTy*K zSk02z*C!&v?&hkx7S~uFd7E(k>iM_VCk96++=X|V6Bv$bpBo%CN)E9)A)@@0_ZaWh z3PG2rFJj;C(mWmN$-Q?T!|uSC0u{JpPIM|@)*aUGtUL5#q~(v+PrIcnLM^#sv`VaM zlbXM?0$u#)quxBfxjYOv_LAoCdiOkbWMrhMu&{q+s9@2r5%|G%|M}}qhNmxwRuYC* zOp47)5>ir}4_{e~y3=4SVrp()+|XbtlB|o|{r6r5*0-X~&Z??mr_Y>8eD}_gSycx% ziAyST;Qf7zdh?1;*LRN#bZS_v;u4ri{r9V0x2*V3LP;@9PVn#N9!vgy=I~~oed1Zg zV@$ul@;$Z8_Mg*R%B-jVy{LTW)QiLap0OHVX@Bw8trP!#!Qi~$AstEYQPG}_ajTWP zd*=6*>^UFyH}_~N+A;0(nWs;mUL*eT@5K+Az+g{?H(xk2h(0v9=ymGp?|0ngXJTUV zs<+l;HJUm5Q6#zB^1rY9a{9$~&%f<3JZ-2rd_{pPX3DL$`oOgQ`3#3m;%Z3$>G)I1 z)&Gq6a{e*Z|A=r#2r1!QFzI{ z$v;no+KJmns)ULjF z$S|BmLU9^VVgC{Q{d4{*9knw-x;$FZCK?(qLv4Ys9A#$D?G{B@x3;!g*1Dx5_qKFH z>zlv#pAc>Okh#BIupb!p^1p4E+S+m{*{O=$`=neu?U$;=5Qw(%cN)sqlLZE}zZnzi zxIR@MMCg(7-I!CsP1PI6U()W?)`)n^8R=dyIzI^#|bs#4kVR;V_5~WQFnbngj)xF~5+?^!c)zwvD?!tQb%I}F& zT&LYmney&iSsuuN<(k_D=9&GW9oQZjOdK%>yu|g|$&#rlYsk6Z{f;UGJ&!dtHKpL( zLxn&Mm<)(dJE#9xu{P}Z@#Di;YK*jXe^ezK2zH>!i(-$pVP>V)a85bDvEtv2IP&VT zzu2SB(o5&tV{~EH^im#)lBnB52EM>Bpj(~%q^qbmXZX8T#7{abb|sP?{(dwn7u5bD z_{SUm?FjIms(^riY~WU|(rZVIUt@J3;jz8Fy?~6&xb2$%xP7wId|OllwzRS8$@p!E z=eJwe{+)@+!Llo|c~M#sRV7U(R3X&x_o5u<5g(0P2x>VN^7b<9#kHUuO%B!vSffE;Mk}ahhX0l&| zR4!^`T|}L|0JZC-MiW0jI|R!`LYp(w95SJ1_4V~ZdzyYb zxtet=1}a{HMfD1zzG1bZ@-@UqBO z>W-JW4vb@3^ZMqim;~{K*wd&@DZcI0oR;F*yGFgXeA6aZ9m=%!x*H@7U@qNfhbDfg z0Ud4hx#cwy^0yu8iqxyuPI*N@{j|BE+i|>b7j!8pyRx^Sv4}Vg2Mc1J(I`DXls!oC zKZYooRB~Vef|9#ag<6YqKn~OQ9Aow|THxh48Acl6(jZJiYn#^Yzmku~Z|HwN)EjF? zn}6!YMF&sI@1rbMZ zJT=3s<3U-en}7{bxeT&htea_qHq*p^wFiG3Ekb04vpmtxP{VgufB$1XIrpT_&cszd z+xMbclTQR%kj1{QPE-0!e@LNq;A}duDY;NS|8ed$iBuC{{AG1Nj(4UeNt_ojk$N(A z8N@E`7>l^>rWZ`AbKBj#GPIp|OTqEZnM{T3BSHqNEdnmRKQuxG(BJLcKqpIf(E?B- z>qX&PQf;ivL#G~x9NO7E0U;Kg3n%U>&R=4Dl~POF95tqpUbq4W?6WxXo;$+3#dgGf z_KP4h(!V&gVRJ_K;Gj5mGeJfyAr8~0diGr3=YN}iAnMF2Dk5U6k!N)-QKW}z3_TQ{ z?`=bHV*E!|AI1gC6yfc)^8B^(vi#-Mw#bL2NBwtSAuP2IP1-wsN$e9u=&^tX%W6gY zON}9GBkaBJc>p{4s*Oq+)(1%i^J0$pPrAHZSl$-qo32xdu4(glS^70RZ|fnX`r1&g zN=KGjxS))T1#CNYR=wA}F@zShxU@9kpbI-MY?*7<9u0!MV&QfZjrP9sXZQ?Vr^`Bs zhBKzvu3y1XyUtmQE<1F7f6eOY{6cxb3eSnwvJ#2}U&R?{FEOO3(aE6t`u-oGiX z7INS&TkYQc_{aMsF+}D1YyGs_nbf1_hp=agiN6ekH8W{F#F)Rx00hf_tXL>gB0F9@ zT)0n0bu#Tneu|r*4|OPD;;q>vH>@xqn5usXC(Hkv4CKu zjq9@Z4i1yWEfPF;&R#8C&Ro3P>J+m-TNJ;~KJTWa#W~5Z4RhNyD-!9u9agXS{ZJgq zS1OVQHnWo4+M!`qa7x6{$Uk;=SPtpXfNevPOH280$k&O;^4d z*85||Blgk|T$}BGPEpOqBU07l8L!CsuI8Q-GP{1|ii%aO+XxX2E*l&)f=Jjue!O~V zcIOq}C%>JI2h*PIoCmQGA4n9n=T(V4BG8#xI?QoRwxC*#2Nmzk^rBjy$+{HqAA*=K z&-Vr736@x{*Os(>Cfar~)tA7cgzizA%1aHc(`BK1WhR~FFy4hLv$Q6Il|JJ4KHTZv zf9%6a;vCl_5XS&+e2~>j#EnGxq&DA;Cs0eFR_OV&aX7!OVPfwHf4+%Q$<_xB6=bD>gcH5GOl;X z)rLHy}xk6PbMMdM?cxk-41?6wkbDOrS54;S6E`E7Z$lf z6<5N)*6Av0@{aO-B2+vWc20}fAxcjDGcKQK6;@n5R*EW8n{ATsp%b*M^qpsuM>8t8 zpU8g)&sl+8Tr5VPeD69Ggq>~{6d^_%3xK=|126)JjV>+)`dbBQXWN-6p4&H3d%3#v zT$4o@wvf_3ayqF`$!9xqW)G9xKrROqX3goSViH2tOJJZz#s;O_AkBI#WpItnt`jg(ol6IvLE9z*`gq?h`(qd8n!dI{(cGF-nRxQRsA$RyNn2}!%RgLgCZYVg%~t zu_T$?X=>kc95}_B$0|nPCVvA?_Uti=jnZ{J_&(Q%lZZyeSeWmmL!xZ(@K~)7CG*U& zo%dnxiWpy=QK-M)2tIQyFLWRE2L(gF^ntoA+Xu#!_@8vatIP*8J`Agwug2{Up@fFk z5t>P5$AqdCgOQfcQ8VM$eY6Q|iQ`GfPG;;Ao{I{9>AK}_T}Zd`dw%?Uux@;4zm`p( z9x?fs(pd4)CKuCPf@(;l^WuRIyXAYR+c81G-IcPoP}O*;9-{$I9-g_XkKEYQ^s*|^ zA9%gMvBsHK3o=k;*n)O<1>49Qo)Xj}OLyalRA6nG_M3D)SO);VpBNka0oYzT_M}Jz z0s9m2M;)ws?{8}enU@Z)PdCztwNJ*g!?{DokVTK%&bl2xQ#I*-J$7qYWI{RDH?qT{ z`*5(4&%`oqxnyF#NgZ*!ICYAs;Mxh zcvO~)o|M_%P*4~y(G|+82KBRO`jJJ1&f%ot2*yJQUMTuQ_FzfIv{3^6YhDn(&wS+# zH<^Uq9Ic{>AhvyK)^|vOtD~(uW}23%>cBeg9@?X(RcvAu^tB?g2{YheNIxSXJ zz$<`DQ)foNQy(i1ir&5WkJo!Djh5_ueI?lN!TJsRy4@xhJ`nb#`)~&fmx*Wnsi%7u zncORrHb|#p*D-Ua!yALA50$@p^+Wr%tVE!`I=f)crC#r0j!{C|?N@R>!wEIjr$(A; z_2v#HPuP(YKFj@AN-V2&l&Ir<+a>N$UDt`Kfpg~8S>aqk+Pk~EvL16!_qUQ!h3>OR zlBu|HzIL22tHqW@_0k^P`oxU%xk9_5Q!PmoBj4Cxe;j}HqwBLI$KVi4dNH9-BFHWy z)Ga%I1}*a``J|KG8<`Qfe^!u3N7fns=>}}5><*f^ixdp~oGzC+TFYT*Aui39~vx%Jh z*f(?N+2!0j==;5WlpQQ3`3>uJ5dnp)S<#t1jO`4Znt6S=F4J`*LqKg81THhJ{2Vkm z!TWwm+NUp<7(|Tft0pU7#z_2HXnHiLygZ7Z5K|e#cPS6rE=&jQ-v@J}e;+X(re7R7`Q=>* z+Rzvk8f{s7dr5Z?>u&BiYd*sjCvmugml{-(&8+S=6Eea55hk_acz$R4x+v|d)Z<(W zpUH{}ks(a>S>M&zxlH;x#S77hv_6;ER$nLB7&SQYPE3K(o$%6}xaDm*vwKCV)#vFBD81c`?ku8kB5OiH?X1OI~imXT`GZZ#4KxqU^!|YDdYL9wJIIw z`PBPCFr;r_g*-^AX#V`CX73o@gWnN#2993sv5Gc96w8WVp@@w93BjdCNd~a!x-E*o}CYjP|b-P*2b-d#h zG$`0|C~=)yF;UU`NAlq|EAe4B2#h9wID{+FZb2hSO;ALkZn~S35%4|#h z!8FVJU5%>bSyygGv8kFz!eXx5&0p>I2g6axS#n4HM-mSO&%3>{$PBHiZn8q3RUm37 zjo#RKb#2zaT-N`XHO2;_zta4bCyDZF`nD9C^*&;jp=*MS_A$j(^DPb}K|>j@rFTp2 z;OijvF%ey0lT>B?2Jyl~Ml|Iz$SaicQNBGgBhoJ;J`>~+-Q^uziRkd4e6W5QqkRym z@BxMfi2_7c@#f{K42Do-tD62vK7(3YBPEWqE3BG9+-T~#GFZa&5U3sjI( zCmV&3`n|y*eEJ@w^y5l5P4sGe~Ia} z|CvFp)wGsTukIX+R+MRV`vhE&z0ZyMT)$p$Ay$pWCb$ey_l5USZ!ByASc#=vYMRUn(8a_fNiO4qSfsYHy+N;8?mL zop(HxWEvinmi(c+a!o~hJWT6ZX7h_5*(W7!sMRL+`HX+YHImI;_U5TRgcxK$A_k9M zKlE(#5NcG)G}-!*{;k!Ut*3J)Gc;Z+FX*AGm^tG8Gm(MyaJF2FcU+0U9vYsdG?RaD z_@lYa3rKGs^ckC#DL*Cj8z#5yf4nCZ;vTO!VN^QlzLu+xJ7o7Tq%O~zY%rVuYtKVA z5FHpCVKfb?D7CxsYKFfvYp<5H+Hi||d0k6#xdrZQY>K^|^%zq!Q8G)fzr&Zu|0iK) z%(ZCgF1eJ%Rq{)wEoC)toKnW!VQF5=mBv8!E6Tvheen-Sb!@bG=H2xq2jZ72c6Ak* z#9hPr?XYdodTxtK8OW>4*fw{Se(6TKV6~}N*MQ%Lg(_yE-_oZdG;^{8-5F%`)jsrf zs%`AmV;7aal3$C5tdI3(foA2}#ZlKD{CV%n=%vmf7GJX%!fBcc>N9F4lfwx3n@HgH zJAUJlAj>J;$PCx-*i!N6S^rX5-%oiDnZ57$&rA(iE&&NCtXz>UKTqK$LDpTR+12}2 z1=YPDwIBi#t>n{hA4Y1+ghhAuT5{|xlD^nHUy|ch5bwRORl82h+IyiJF}tU# z^sYu_wt)(!j85ZSHo<;Wp}jR^#7K$D+fe3Tq7i3IuErODpdpt*6G3~%QFM=r{R*-x zL?VLaBsAL-%RLf$5_$u~J~D5%nC6c_SU&gmLE17Uvm-bZ!s*3gE~1IlN_w?<<PaMjDQb~vei<5M#Ba4qZ;z!xe(-FD=4<2cDmn?JnfqiDx>y6KXcne7@C#2 z&NZmP=I3IcAQMTi2&x_cBF3S2lf7cX*mK`sNUJ6oP(m6sXGeRux7UUhEyTEx>Qc7T z0-p@NL}jcq;r~0J5iD7&BdHK?}_X zoSkWX-||ab=%6coADQo#kz?*((tofkhE2P#z9}JyZ=sY%tAxFM*Y4Ewt{j9gRIxT- zqvV*Tz<+(S=$w&i(L!m0%aDQ3V?8!gI@zzjwkHXr{A@~Y2_8hVktLA-GA+Aw&6E?y z%lB7+EfX!HysV;A20(KG%+d+ZCinbkI=<4of zQXOSf(f(yaPj`F?Bhb#5`iLQ`xN-Zv@TXx$xJlBuZ`a_L) zD-an3URR?hVLD_3qupY8gpH%4&ASISvA^+)%f~DX{s$_wwdK|JAGDLz>c3Z8&i?KR z1H&zi-^c~S|BCMZpMC6Pj0yk)pF4L>M_0EJt#SbXOhK?gNSeFo-{36s+@n^fba~SU znFq7+a-?L5@yNC6M5y0$K~YiuV;0>1bk|XyUr4_j=Ogb+W$U@)KE#bhdap#OhGM<1@uW#z06!V%De z`SJ7^JGIg$FfcIq@D(tWXc~1%O20Xr1--S{%?F}Uh{lG7aR_I1ySB)IO!}S!b?vCi z->Q7tHZ;bff6zjuhFxfqh^S~xjL%1N8{b3`S!6QE(0v;y=Eks?3-PO zm194y9orq$6*(kVK4(!}25YI=-TB-10R3t&fAI69rEy9A+9PYgw1QUn4#4RH zBOszt`=+qrMt^qz{vHD4b&5xG8AuS<_PPCnogL+W_c{9VBLz+wFLl618}=z!iTj@! zK!XrGqQHL^T05$6CH!A4{tv4?ko@3*x|By*OY_?FoF#QxB&@YY`J`)bV`54O3 z|7l|Xe>kOtX1mz#MI(SoFgQ54=F6%%z7B=uZgXKKBd!+t)tdxTA`kRi*eR2)pI4=0 z^iVbIrzc@}dd+*I-_Unuct)|YS3VJfs|3{ZwC;!Y%HvE^{__1BoS?u!8HFpUI!#9} z^L66h zHWo8yx$^B4#2$Eh?&I9Xg$1vA6ClZ0RV5DLd|_Av$>bG4wC|F#Uq@$3+h?BhpU4=R zQ2aR^|L>iD*z4VNw^WZsw?MIyZC9o-Dpt(pRYHf@LE7e?j8Vh5!wBf3lCG8!;5H5Z zwxOr*yiL1owocS5*4Y$|2|G{@>C~<9@r7VH`9d1inZRH^2YS7i57KNM(QQiG_ub;y zON9yp?I~COT52x0S*LO(K+xSr*bJQ=ij|k`);kWHeJ$s75x(3GAZ|>X))M^g60z_t zMZlCh*`+yVLFV3l@BxdV|Hd!=(d#SZnZ>Q(T?3hklXrhe?TctxCL!LwBz*PMsSB#- zEwD}v6)}a{mxVoc%Cin_)E+rFtRkW;8rv?R=&nsuq}6NdC1_34vMa64ihvTAqHbNm zW(l;v_$UvxgpfFg2j&-H?UqS?`j>p=@Yd-=`5|_)Z4d8saLPhYPWOx%e=>9_`2bx%9wzMVEo z{RMF^Tx@@d0Gy6^|LUH8q|$;ZQ)dkxXx{-KC7P2~vY&qB#^w zdD=e?VIU;<@{kerYEJmJv}^a@94_u8^_#wGCqgbem)U2WSM(M9Mo@t&JO zn@5gZUM#V)!`JyQjN7OB1|$k-TbxJ~?Zjau|vLK-3|YxV2oOFNMn^?>7lsx-UOH3j6ZSk zv47%^Dvyl$mj~!0WKHAM>KPcXfqfxLAl+;Gf)HqnWA=$?q=43vgHF?~7~jlZ*dEgx z!J5EB5v^V*4DO2qoccbE&3ZYV8aqB=pxWu`ssvpKjr=V^o`Jhut9)!wwGx;18m zx(}aS-wCA{j~o3R>3(=g;1Eu*^o>jFBxM&$TSA@(v`5~5{9LX{_-2bjZ<$>2u^k%W?Gp@}1nY8HA<~#FpDf#53;eIvoJ?V&juE-hR z!$L_^g~^(Izc_1TMoME_@kBDk6gg9rzt!yhr28oOQB)HPAckr+*4w>6T*WD*l%+sD zx!nIfN0Ty=(@x_SG)R+Q%=BMa_T-eaT?doKHoZ7yf~>=1IEL6DEX&KAUiDL?n0L$= zpTzQHOjY#5Sgr5{AL;Fq?lZigP@2Ks(W^1$moPiI#Ub-SKNq9k+av9HV&Mzg>rX`A zWcXY3b>c{{cUuVo&-J*G3!Ir7V)UI?>u2N;)v5&?%(-W2))}Aej;Ud^7PpU_BnKV} zZrpz;-@d$2m#^tGGYs)q_ltXMfz&SzwCm9W_4Px`|`wet4tbe?jud)tpHn;r4Xm z%CUH7+qZ7oxUCQ^g{t}9XaC>uKuDn zr_~(uE1itKrH*OOBKr*4ru)N$qqfg?j4O_Y!IATK!zUNFNymwKaJ_?h6ht06T^G5q zk*2)SS8K3D+rE^4m{9Yr-T!``R8O%$_4<(`Kj+)_jU=krY{(=U&q=twdf?0oIm2Q- zOHI8hIFK>1`6pj;h36{6qCN-D-tdlVu5D6-3q;YG25Wacf->C2)oQI8XmQ4tjmU{m zjwAHa(J6C%D>k4NV3(mUGeL~EP8{s)f2NJmP>QX@KarVWt^x>h^6jw-q(YNr} zFS6j+GCj^VS8>mb(1=a7MfR0XqUVYSAfNPu^fwuMRHM``F2^~}XqsKVV7Y?*a_jNrr{%Y8Jx7bf*{pl%fl93H66 z`8cf-CpFXAjMVQ(j4IqtW1Cq8?E@3KM-Gzjl9 zv(j$73E~F2;at3)N$+=r#YcTvE4C4V7@8FIrp58{@$&;a0S)oyyNa95W|lX3Oe;fS zKQcx7#Q%)P_(!w0{$xyC)|ZAvCULy+!UtIt?R~YqL1ruKv=P@U8nY9HHLkH3E!xUV zfUmU6K-XHCbMH~Q&bJWA@(I8yWdBV?_vVc!_Xnm<4BYddHY!i?NfX2z$$~5$X)D~$ zXG{Fpo@mtWy<*TwiJr;~JmibCBQcWQVhKwxy+*Hoa6B(N*J#8=4TwZ4V~-q}Ia`s{ z{`#-Ma)Zm|NlGI{PVC}0C_im7uRrtLDRUSs^44j-6`#aYcN20CfkX22G%|~RRK=AJ zu9u|bI_9xBp94P9;d#7{}2fP+|Vj}#OTY7ZI$}&5Pq4{i8nXQM=Orb2{6TtRyOB&`6X0ivhvn7yr_Q~3y`~I zt_R;A$b^|5pS7zA(kA)D_;gnqXO^|c^ zgHjQLz&Azcm;~!YFI#yF9jCo46Caglr+ZXdqm2^-2o7G9Fpq1|-}({)(nUY9y;9IS!QkD9k2skUT&443b#0&HfR)2=W4N*Cotlu_iXLM*7NVI+-ME1h z@kZwajn~Tdt@Em}#lF+%)8c{FLJ-gAyWKOj#IGc!aR>CoGFL4&{G#33-&=oQpa1S` zMnyN-S@<6HB;+)oGs<46*KNgk;UUKBoPD}n$!ToS@nQr}=Yzit z4wgyj|FLP{$`Q?$xD2q6!L(I#S$q>gJaFq#x~ysA-1PX>TLU|Xa#!ypA__H^GU7Ws zH{+s-8=f&1M>$-5c-re`^l*bq$ zUzgf1B-5W90;5*0dc})ntJQ~0XzQk4P>;qUdK`R}T$Tu-RvK;9 z>(t!__kMhT_%dKn9;2_Z8L?P&8JZ<@M-70EJC*QZCq()qM3IG)ZjC)gwF2Ug4Laz- zb%q6kf+GcFjnx61yg*k5kjUUdx4+bY^I$_iq0n7`T4%7@+%Yilo6@#k9j*2{-0 z#bP!>7iF4kGv4+sE_>8-aF!u zR}{IUT00xk2oM-c5s*tOF)yWK*ErcKiUj*BZ?PjY;z4gV2Tp8d$rK#)j|=2hn^3zH znNO=3R8qLvsOnIJ)BQQY`Xl-BI|ml!ydLYDf0b|-@bt(!EfU&#L9Sxj{%?_)y=}a_ zU;qS2Y>mombR=n~AM);$rl)ze*%bexz5(;7#k3An7D_8#>dPzxXc^kD zbp+Y=92*-pKh+J!%giBt_dsP$frNy#w2xg~I*S;L%hI>^H4^DiSbXH=hlx&+?T$OE z{DBZ7$!a<@yBbKN3xww$E)d62OSYHFtf&RM#zqOO<|V(YTdL2Qf6vfQm$e81HwBbu z1g#3B%Lf|4Ol)jQX7`q)f+#Bmrl$6}7ZY;{ePy(Fkv&nOoW=G0kA&Ru9i#BJfHwjH zZFLbPccu@P*ZBBvHms@o*t1Tf21W^q>j{&>Jijrm^N^I@~WAtn6*siW7HU7%-t^j#qTf*1_q4xX1@ zOjS9>a|JG(;^XCYr0>&$zQ1Iva-Ea}4Hg>a9Q;x3g4TyEc)Yk^y>Vk6OS~6Isgmc4 z;S~_LrDD^Edjdzp?I3wU5cMuAaK2Azl)#lQJE)4TF>s|BXmFURWP1myDcXBi(BM)x z-?bJrQO}QE1%K9I2l@*aW^nBMA$e@P#?=b3@%4nDpr8>dgtkXe+?hKhY*C@E08~<( z`l7`RzCW<&RlGA~1jO(1KTJrwT#3n-;7_ayj{X@Rm8Z*?6A1PEoYQ*5p(?OZZWTT^ zJ7S3%;RCHs2p~%d{cf{{y@6<#V2Tat>MyA?X#X!IEjhaf`~}DU%b>w>yY{^mGk*QF z!h>v@)NF9&J_U`UV9Cn^YT)3_m*;QX`>;9RPQ^$iRZLR>vsZ9dy0*A!dGf{bWQb;(lu%{>3M1}!^FV3`A5a5y}vn&^vV*K99Ry(VP8K=uu4J9TM zi+vexpEt4s^(mwjb>BWS+>pe1%Zd*K6-!GYcpU%Z?;H>C{-=-gJ0Yq;@?1bc!b*8N z7lH8{w4$P78w;jeh5{85;_fr@)4_zE6fuYBkgfR4@3&kvN^H2QQ4*J{H6aDfj5A+5zt;zkqAZQ;z9BzLf8W@f9M{Fx>| z>e+973jUz^)yj_u(V8a*d(6K-%JYsDmluGPK23>yFgJm^j}f5af!+jE8{Hi$Ffi}S zP@oZb1#aCkG?3qX0dS^_=uKuNmMZ`WX^T;qB{Qx;08qWk}9k%1v9r!y=TsFd2I zH`#Ze2TEU7-~|~*rlyI}+)AYfq(O5qv>IUB*jtnW^?<)UJGl!eF$5N?=OXnBrvc(q zmr-qgRmtym*)`*jTsd>*3?JyswL5nU^wUjDP4j?Sp6dzr`P?UW+=!)e&95l{e7$slEUWxY~I{hddjKauWJa^HxCf&{8DD845VW zl$#I2c%1;ZB@0wC0pP~=&U{R9UENqFOhQ;#@4!dviXw7>QNSxI24{_L^Yd#>*1DtX zJm+s<|Mdh6{dFnw#{@tJEJ>hri~ro&vmM_(v|*zqRs{u_A(p@(D~5fgIVb}$A-RAr z+%;|h+{AOs%EwVb+shB&m-^Cr>!gVfTB-X#SFnT5UJjA4$n9i%vP}~OrOWvr*a0tb zeR@&}1pqa$?GbbEF2)x(VG7hB!E%Mlng*3n_1aE}XKSA+77W1?%s`~^f5)tBl5*qt z688H}eCD1yYHv~0)W*hDfE*w%>-powfe)qalTpdMP@qVsfzT^g4OGO1a#kKV@CM?j z6`%X%9;x+5@;Z04`(;Xuhe>Ipo>J*UvKnugm`FCi>lCvGAE|U4)cfEjs)vAzQ3=-x zaUvFNa7oH-I~jw)6#L_6C_vGXFtElhEIa}Y0#MKhkgNw{E7TTxy2ds2fYd(?WZz{0 z)B+YCPi&l}>4QMqOTWnW3=HfC$_xMNVm2U>RiKJ5k@rA}lrJnHbE2pNrO$o-gO88T zX>}Mb8?;?`u=>`xPOdI=#s>lAel&%vm~ZcM0E+;_eCDI1oF{8s4@`xI{X1W;%&U}z4C2y;4evGH=kfCN1M>?PDo8=xJOcumaHNq)#>rW@%;HzPq5RlT~t18I2YM1 zm37qJoY~jS**C`dE4k>}z~Qp2YY$yfhFtz76Z#i=_Gr8_5*9#WOcr3gzdXUV3Z_$% zZ;OfHS{s&9T&jR-v!5%jvvUzw(DD_)s`UJ40t1TNmbM)%bj@F&eC+?POt!G;^SDD{DBH$8yVMQ+gmAt#B zUA|D|7eZ#j_9O!Xt0hpQ14ZukxH}V;dQv1^Q+NdcHvWP4M$u{R#Fe3T zBXSd=cgUp-k$(VTIS=JW@j7iU4;)Zc0sXqd|AyBXK;!ijh7S(MO`Ri1s1LHb-|e&k zdU1|tO}FKqfu051*i3d>HM_(5o8@WLtCJqCA~SsUk6-q40&`aQ!NmOQB;UVg7508%aAwQJ6!A1#2YxuViiQh^gQW+~Nu6etJ; zfT^m0&4)l>(QErN?EoZ;9oHfm6Af$hQgKH-vrUXNzck$xU`)ntV)CxYDBuq6y7?yc z7cTqbdFWKjO*?+eF6?c^u~FC2j2E{qI(%Dxc0LFdS>bsEcAP6oOyX<6!}syxJPygW zVD@bNeZdla?rcK~(w|h4o`UdJd*^$~Co^43g)bTFBQ5L196`hXJ&-5#k-rea^ni5{1gN^&_r{J3d8d>4!E z&)DmpNGdNtmu$+vtdnHXlFhiE>Q#0NSBcOvV|LYvBlUGDjixL%jQ7Ujn--{}m&P)D zm6~H_S1Sd`?D+i`?5Mtwk~K`{_1etF&$5U$*ZvUN*`oA-m*eim$8bE$zHCkDpD=p) z&`&m6R8PhTpZ&E-X2o81vhMK#QdmsST(QH9rXE@RIkD*o9$fX6+hIX6L8_@S?aZi- zWwED9ck0W466&Z+DD%hHL|KrCIspCJ)n)Pv8DZYUmk^GgZebTUyOUHt7lH(`h&&mESLtylhN=^mC`FPnZV z@3IP|rs7VoX~_pAuWsDSEhl9+2&U!Dz*&j|eoWTTwiGUNW=RQ=91Wv$IqYWrzpc7} zBU;BUvgq{nl`G8M7#uD@J9nI^XTSem)V0edFXX#XZ?x^%grjI}YK?Vmp5l%7M)M+F z!G_7>TocKPN(Poj&F>;#R#u632d_P^4pOaidB{0GV6$11|7Zex;b<=J0*C`ozy^-IToBSKRmz`Rc6vlR(EzDZA9iJ2hb7UCSCak0FKb^^yb^ z*$md>T%*Sj9QQ&gmQi9Qs5VK{2<)=?#-}tS*05ab(lx;r7yslXMPcExF}wPic4)}#jurY=+=w#KHMUJTFpyF)AuEJYVs4_O}_RNxq-RmlCf$3?m{7M z|Hxvye>Jk?inLl7G!PlY|A_O*QSl2Wt-)1C6q=bo8CrHg}BrbicJ$hbC zbTFG^M7qW-)*Jtw3$k)Umn=CSP?xRN1E%x>qiFw|{8*8Ck``#E9Fy!pyL+~T4-_rAURCEKHkC6Vo3doD-g!;sgE{R$HH zn$zusio-=NPaHHRjIP>0I?_-uQKof$)b2u-6x&}($IW)1U$8TO$9H_oUG-3o~JjmD4VZT$efQ}|7;-|E?+P@mlYQh&~G^*yeXG5T!b8fO8!wTyAF=O-EDU zi~EP8-aK)ay3#_iz3;9y&}2Z-824MPW-hy1_RJ$wAUibEx&9ZJ{ke~$gQ>zdRfp#E zEqF*T#p-dHBL)k?q-Tufcdx;FR=*unX08{K{&162`sIsCPT@rs4|65g)h}Ye>&HHQ zdd8)j(r`V9^h9K4(SQY;SsFBuQY1Mjv00w`UCb!#RYH34MVXlo9(PwiUq72vDE;VZ zX8?muQ|sQw2nTPGu=i8p)9xQuc2mFRVovb2P^d)1SALEWaGMlkx(#Vh#M@P1!sSe~ z%w<;1F-g;8?1d@>ay3&R@jP4V(s-}#gEw!5!B=ly^L8&S7Xv(qKtKw4!qWau8hb_f zz|s$!u49|*?9$HKxVAT(3M0$k+4LyQ1ArmPe8TI&P2&t7~aKe7)*vzN_I<=(HBX)RIx}3lX(E_ikPA*{o za9ELevVFVB+oTW;x&c`E)tnNCz(bd(rQ~DN6hE6PrPA0x}PlQh(jT@=;`FCqoR%oP|H+=tgUc)zl@sS1u7@K1$4D;c8ld;e ztd?a}T(2)kd@6om^S#%&&=vGdyyiV}(fM9&s0DKFWW=@D^6bR{1^s>dk+&aXltnzCP>{`TQQ_O&tT4%eKs5AWY~MZ zFZ%;7pum?U==SiCn0>m_OxPE$_n2|)jSFjWe`2i@cw)Sk7|SLrm?x2wwWoT5R7cPGu}@WU&UgihC|H1k^kCsS8BO^jzlTb@d+A}(HEpT~92g?#<> z>i|{xp<|=gK@F_km-z1W>%Jz)`+Ue#55Z`E(nz@AA~Coxk`eWGg$H&_C2gqJurfwj z#{0eU)2b76i@_TeBR$y{>UR^mMc+(#>+++;CD5!wdsqCWMV8&89z+-2+_zz_C9(I- zS-UIvkT}(WT+OkB}@Ii8eKofXMS`&x3Zs_-}De2S>s5w*PLLSgtuM8P0D^kiVQ((fq*B zCM;{^TkcyL_)Iu1@_Lm^bid#|HF&Dun@8=-=KNXrF+%0&!fbl?S&vIiOSMN24l|^+ zkL{mg650w7vwRihHVYPF1vdON(%ND>ju=r%AD5N*&h}(~5U|x>FI%ZJ~C{$l-6y zz+|{%p683RYx5hnG~*QIaVRrne8VC*`0@>*GzGyi>p_7qEqjPFLEwO8{jej*JKt9V z%!%(kUA&loTvSBNxKo5sqD`2UYf;e40YelW*>%P)va!cW0ovn zHp2DWILOARd8I{bU>x%#y{-v9Gh1s>uZu`lbXL}F|GvLStafAlYNM}&iQuy-!HS1( z%`18eGQD()>@BHvN<_rGk!0lDu4I@oHo7(xti*{vF>ZfQ2AF`~x+qhH2)>=lG_1Nm zswIklI}3}f_pLb87})#}Tf?@P7F#3G)%JR}c}=ouKs=2NM@F_77q~DI33?yVuZuS* z)l;b%^DdRY#3&p?7~wYue}&$8QT*(CWDw`M-H}nl0R9{Sn3uHSzeS?GFBChi8q}Fv zBd)xP_DZW=?*Gtqq5q7-rGLdNy{a-gxzWSVTFP9b@#tN<1aQ}G=jl@Rr~g~{mmUsm zfCpDSwT3@&FWrs{xk&bw|6i{+ja@chDRh*YeyoNbz8+{g&*aUyZ4XdDX2Uy5N6rlh z#cKWkhiY)<|DmA+B)TOS&+{mm9OU_y%D(ahR}1NqrG}f!Rh-33C<^MB+L7&gO0s7m zW9of!=6a%Uprhq9Y|_O78KZayTxx+Q0>S_+PIN{$xW|{P7VluQ;GEol)ygq9-^i@b zTrn^(D(Tpt8;x4DC87kA(o>9=Z#rJC?#b^EBYSl)Z3*6;gynEl5BgqTI?wgz zO6b;LUUc!^r9D3gudUkn$hcKydml;IBYFM>f042OtABt7kYcP}-P_8FqpE@1Mnc3{ zF3z!&z3TUmd#aFaL3)j&6R>M@yxNBEsy_XaJZjje-b7^k%$OVM0U&A@Vu&iDXGci_ zyjRe-z&k0`A-BDfmiIJPMl*A@R`3h0F=jQyFUh;kj)N&M&A4fri^BqFy}7m4Z74|vcbko(cE$F)8AGUU3ik?;R4n`YS31j zj=ttsiHyC0fx0~O*_ojjl!ZT37XbwOm^*J$dB0% zxj383$bwB*YAdY%)ap?hdC*rq!8V>*Bf&I=r5)Yr4s==C3fw2Y&bEeF@WLfF(&lXc z(a<`<;85_{?*+pS+0F4H)AxIUahHd;jS)=dxc0As=?2ZUyr*;eTlC6VCZx_vzX>#R z;P?3AhiP=frd*fu%{_K`vFSpWj$N0X6*mj`R+H~;-;&S3xyB7vBOWn$65LhB6r!CH z84ULllNmgc#5DJY6wO+)oTvRx89R`{+#bh#OgXI1Nww2>GmuEPrftIVp#Kpo8Ozg! zF?EF&zwm^*@9eK`Y!)j6nR9M2HA7OD+U5fD1~_-n4PUde)#ofneVDt|0{5Baif{XN}V49@%WK#yf8NDar&IUyzlgb5e4xa zX5aOIY)xLa3Nwtz=o=J&!8GghaF>700|jSTH)ogU+-mW2Nx!}3cmEs8Z-`I9XKz)p z>Uzl9$26cB@^OP-9InQRYi(GJ3^f+~M5P->5heAWH^JZ4`PM8{a4FPscR3&mfB&3V zs1ceOcg`XqnYUuwAat96=)C~7m9aK|u!Nw6xgvKANp|f(;%bdxM16dL`9xivNWSBA zuY+ws^rKz=rMjgaZUJP%{)pPGn?t}*j5hjxY+rNtX6im38`_9u!`rHZu1}f$zOvhz z*^Y@v|2TPY|Gpy3DR7|F0b{c`Q?Gv__!WWb2)W_NXESs9B4okPB*D}EKBt0GA95@k z`bL_$r0yGKaM^U?xt-#-+oRMxv?#wZn8Rv?HPWxJ{qVzrk4ftiwy{~YA2l?Mq9Du9 z95b9;%MGZL{=FXJ)s5#I_eM~uU2;DDz%VPv5-OQt6lvvsO*vLo~#wSnFx^SMrZ{Nt`V zbbh670t?8w8AZ!1k*0lehouMG3%LLdfk)k+3YUW8^lHiSVs%>B+Q8`T755^2 zuSlY)6qiNd2R{qJo0x^Fk+)j{@o$fF*D1T}?S<%XuQx3S?NG54l0 ztOV$|F=hdBUS54JWNlNCPB`B&oJhE~ASb=c?^S$sS#xAw2<;48)qA{PX(V{DQQ2fQ zx45S=^srcJrf{Qz=Nn{<-Fs)n@0YQfmDRF^(N;x6XbwL`)(EqtU%uDxQ@)p1caixF zYaZ5kU{csl48ahh&!;hTy?q=((|ntfQZ6?me~EcM!>qxU2}o440PQX{}~q@X}0YZzo;07tnTQ9$Sq%fN+e5BpXK6bo7;Bs>4@y z%X}fP8W!3eicPlUPSbwJ2|;^@;Dl-ml*2OIOd`#)uf!*=ZY+db1F^zgrNQX2Eb=(C zeLm3YEZ`i^OlwPP4SM}3nl#G4e2=Gkv>uK96+W|Wmg`NoHWFhW{d zi9g6FUV_f)k^n1$bvg>*2-U^LxSh+|Te_AhX`Us9n2-pbx7R!v-^c(gW-X9~0gP&# z@G-y#qXBL;?Btocx;mz=<=90Pov&E~{vFjWoNc9#^v+oo8u{{~k^Ks)%xt<9RW(?# zdCipg(;m|3l^jCo2yZ*E^?Jv{qiwd>P7xAE9Y-`f+@)fm*y*OS>Wd8w(=~2;ZSQlU ziF6hH?HUu47*^t6B~Bfr^iXr6J9#aXSQbzNc5$F~9OKHW8Id4+``v$SlW>XONj$ec zZgehSC@71HhACw&x5h#|9frGBH21uxi-%@{lfvq^NC7YPkFB|Pv_V3Gl``Xtv}A!* zlzV~P;%817>(y#}yl!1z`Y=y>f3?l5sdAl?lS1>$yw8n=8rxI!<7wvXi1$6D#mJEf za0zYu%YaYz=Y~*_Kx(E3@)Be2uk9LlM?pRVii}0tS#qPFtzX-X2v{_##K+!{dXX9@ zX?-&5{k9@RTo$WdH*3%B5qDO%)6c|D!4xl=*=OCBV3>&g{LdDP zy_OjdYjHCamDP55z>!oqm>e1FNF-1kv}daiNsF~vBKMj1q7C=F656uty-p-#?U^Tr z{F$7<>$Yl~^I1o-koHCXHJMX|nyH@ng&J82oHu)CAm4Xml>uS6f%tjND$1uL31~XZ zAq|-dRCJOjqhvD9rML|unj%#dzH>>sT_2+iJKzJ^ql_XNMa1Q zs#+`kY-Rf~EZIpvylU{_4vQv#HA$|U{i#ip4iM2C!GT1|JU|l^_^}NrUwwcuOwi%( z%I@v&WuajEf+C0bNBl)X7K>e)&TNeKtMHS}FYZdA`m#F=x6h?MTG7)W+LYSwXR?jN z;wRUV=*mJjAGL0k^Bz&W+4I$j+wz%#AA3*PhIe*pFPIrwyGu(sLErwuGkp7EIt87D|7QD`Dux@uPg#vfL>hUr~` zVa_V;|CwCLn%hx!EM~s-YClsq1*DxaR)Ev~6$nRFin?C0G5#nDPVR8VXW>j%oLIgfA~`P}pD%89@wyJfRo zslu3VCW{%P7IicDOU(d*Hj?w&=MT}FUNl?uiM-$|elf$$7Lf-~p(%@JtpBkh9k`D{9sPNko;)GlGV+}~Q(G^0dB?Q&}(<*O0+{zJn$;EC8fyZV^0 zL0EKW7rQ(t^03E=6_hL2Wg3n$I#`{y*?~IPAX_fb8?AkOyddnNHpkP??OjT!G{*z$ zqGKQ}%U-lE%m^x^$l6*iQ|UM{?c7aoO(THsIFyO{uHHn&0vh54+F)k*P6Yvfkmc8b zvV+7ZIofqs3$_pcipZ&Uo8&4$L4Hz&Lzh- z`4dSgJva1HD*+0C6(OS!JR;l0z6byA8^Cgr<2?|8&%h`5x_ki|EVnCjtxWL*@^RME zj_N%_FB;9ab<&Ib5EiLGDRS2dph`e%dlzoIPJn)pV?6Yi2hws~u0MI_enjDL+)=4z zm2r7zFp-&a83as$9^L=V|LVGchLiGAlK^ENkZV=k4go|i5u=F~BA+N^LwH3S4SUSKzA59aZy^onXUj#jXDkm(t4wcwuKuyh?Azd~4ly>RQ>5btCY)>Z zFK&tz?g-ZmXQ@y*6cG(deC8TY-(396g6GNHtMvuAOTp4s6i$<{zRFUt?}=c#GP@Tg zT7$T9pSmFnwbbPKR($yNH(-uBEOzPaG$|Q6aT&Y=TZt^z^TSTlG8EkaEV`+j@HB|w zU%x3S7?pOq_)WqlM6=*pD8oj?-QOT~<_M`25WVHCq%Q2yt zqI8crCh`yR6s(e<_z1@`=xE^X3-PD)9ZYK%PF3s831Fm@A1U(+YXk)p~YU6kMF2hZwqSMXPYilt9?d zCvZzzkV6D}tg#cUBpbYMy^o8j1S$4tyv(gveUJMd*?!y!mq% z$>hE086T_E;rUg8Q1soLTUrj!lPv_2;g^9qo6Pi0g_}tP!qJzRZ_8Dzop~F$9w9|u z=MhdCd5h@&!t`H>rAj>5htZkAyP{c_C_y`Kem%F63}|fvkyIDIv~x$4Medc>7#2IR z<}xxDDQXtUL1b5skdsGUJJ_)j@Ynnys~SS+i?=?od9*CYj00&6?Wlz5FXxO zREEBfIZ2iau^ODaKkUiR{~=<`?(B%T%LoUYI6!7rcE*H6<%P3_)~W3C`f=L~Kj^CX zP2HmEB{GLAu6v=67v$5aAaTPSI&y$Lh37-67MB%R9p|n}c(A>3tx)n6`f-Kct zy||jY`QF)p)}sO-a4jurb^bw)O5L2BFM`FR)DeRG@zv&Wk6P3f7*a6#vHfXFY?s+u z2Jdn5cFA?UkT=vm;zU0xS*;hbd}5tVAu0^x$ZdwPp437{IzRCJ7YAv zJ?U+v?S(`e=;7)GvJ~nd`+~Z;i_?$E$)0h|W~QILWyW+O0P@UT`na8GJ%@TREJZrGB%*L-iK?I$KisGZ2Sf|`^o6~HX`*%AW|qb|Q`z)61Md>2yuCIA z@aar&4t|oTKuKG%4sxTTklD#PhES0I#oJ&<~72Ub+q^|m#5MKj*q}5wu zsgDhl^~8gDXqG3;c<}i>&;}0O4u)RcwZTXOnGF{+I)sW|mL=~9kfld;=eo=dCUvjv z+e;1G<(!KvxI)A%y$K@W?%kKv9qe?{0J=x!DECiJwfdHggG^y`m_g@Oeq*y&qTt)P zn`v%eLl)&uEJvJY@a;-t)pn5!b<(c6R_u0fjO&t$A`AMkE@H74rQET1sJR1m;_%5^ zKeMhS&Q=qEwOq^1aWwo$n{pN@&Pm3Fy%G)-ndMItW(+`;8x|&IH;LK>xsFGu1Uo5jxTU z`)DJJ)odU-m6K23b2V)=g-dOLXr_h?=Gp`S5uBnYc7t==RgKZRm zDGB`3SnQ~C;Jp1g)70WKmO&H{JE^lJX14sy`sTG@p_8wc)GWJBz|tcNyMmnL*Pu`U zb1%E*!3q(Zd-s)R2P+h2!GQ&KN+Ol{2NZ8?jFty-Zg^3l4OnVcAKMvYZ+-({dZTV5 zs8d#Y(YVh@C&%b>ZBA1+rMM>(6((G6F#U-NTgup-$|QyAuZf;Mz+iWCHaL`i5{)!I z|FKq3wS+6BmQ}M)iFuT3=Y1Mc@{#t@S z6^8&KTawrekigwlC2274CY#?@9YqG#3Do~0*UXZ$(0+7W9elEt1=?L6f%-_~{X^m0 zTgRxVUmIwZ0sVh4b+031i`5HucSn3?i^f1%BrN3nb!9b)!&2#YI}QsfKnluq30EFT zg?S7FWDb$?`ABF87}%W;Apo)aZ>~oJKqBU`Pq(L+*UsFdQxk2u{k*@nd(!hrCeImF za_ct%87OAo%>)4|PaIcPP!1r3B$)wR>&#k!AOuo&!SSp_ONC%QAij0#P7!h*Do*+z zZ;~k+n3&~ur<`5J|8uSDcroBuAmzN194g+we_T1Wl))%V1!O1B1o`2V3y9{9A452>!3UwUc{R@Ig^|!z|0V z56*(5ogS?LIUuHpK%DxY(26!jtJ1b8Mtl^j!GA;&Z2m`_M)Ch96bEiV`!*0-LE1!I zdGoS-k@d|owx$Z=TlJav#i+;ke_>c{X1Ht)e*KjXOj&r*JZV?_M?lJ-PIsXF?=-@n zACrFj|B;aL-w)AvM>*#$otU0z+?tr3ccb~f1bK`(*5kYTjQ>ASFc_D?R{_Famae*(dH)t&wgF- zE-~2$57J&ZA95JOwufW)o@fb1;BiM!tTH5T|Ky01iI)NJa?h*(&OCXm{?w!Y7vPJK zVN<2~IpfRyH5F*nSr^&Q3fE4AjV3~lonnlSM2`J;5c;~NIfh&%-ipqCK4mI~xJ7yc zb}6{Q-BaB#40j#g_+#sp`78g-6C^d3?rO}p$q!=!b@5jdwud$pV8p&~mP-G4Xvl6R zs?>X7eIT>3+9e{^UuywVxxzDQQF=)i&pVWq4I14+D`&Z)S91>&e|dvn$AuD-{@I&k zZw3GBn0(in>b#&It}I|PjAED!#PHrxbvtRSLEg^+9{<5I5RfcmZ@m_ZaIRTyO7i)lt~tb-{p+~9pNkM`1Qe%7IT{00&ho((+a zG%JFSa=bF$0jA;-(fu@H=ZtUH!h@+f1Hf{Wrf|s+U;A!&nuSdM*h5hE&|PzY&eXX{ z+Avu6Kep&3iN~$-{~<2e9Nu~J(&>!KpjOgS^vO0MYX=!tP~&l0(3rgyu&JMgy)wt` zqqdU0>oU5yRs8y3b2#~+y$#}{Xm16tcFnbjBo@|tN71m!H01;(9 z8lOBx5|YHq`Yr#}alKE3EZ#<#-m}Mnhv#ASyGEP-Hx>!CQK(W$z@2kvXT2Kt zQ*TCkr3l?nhA)An)k+)sm^Q_-4Mwblj~WC#a~Qohza<_C&vn?2+$qst@%_p1Ggq4O zHY2TpT8Ztc=*tRZ*p&xcxS#3w)aOgxVU-$P1=WkWV=5IjZ}HnErPc$beQp&ICcE(2 zojKaFPpwgP$!(cj!{6upUIcZ$!%WPb`( zF+175t@&uh;>0@%s~taU!OUT7G%en9=1ZW#bJ)$Y^zhJQA*|aa8Zhor)tYiBn9z9F ztova++i)Moz%->wX?o?c@lQ!@5gf_4aIWj^zhj4Z!#+5kWdqJjQB`*hFjc*2RuVWP z<(!aStk*%y%`K&xnUExH?xP_Pr!I^4?hnssTKtyDtR~$O(|gCD)2sB!if`sXc!8d1 z@zRj6&28s!C*4d-JDoDLa_NIID`8jKx-`>%cdOc7I-55VrEC znxKCtyXcMMc2<@Z{-nI+5p5o25WO@5W1F}^;n>GM)dV@+naG4BVCSLr=Y53Foo56M zC-rfAStq@BIP|^!Ou{kq=X`2HlBHo@*>2dkyQVt6QTc+b)>0CrvG%IUoK(hlaQ7X8 zfWp#qHv_I&>&#GV$6`Pd)FYMj`ifi3fHKp~Z>N{Pl5tQA`0ssOrw=$bjb28VO1*A4%ttpi9Y)iYAQ_6E4(^J4YZI)a_go-GV{LO z?J|Ih=;l^AfWTU*hJ2HPBz~OiEr^8V)ik+YN^UP`qs;ru_M7HI{n=RJuZa54Sh)&b zux@Y#4Q@@S92%xQ_jg$&C!?ElW^OyP_$~LWat=?4R*75R33#e;1DN-D5TzelCk^2R zYN@fci-DUrWM%x~J=sr-R+m%CaBN3O!CS{%qSk|w79rCyi@qL>@%UPu7f&j0(EOY( zvXjH|EAr?;5AZJ_Bga*FmA73CKGi0wEI+ zQ?%2y53wtGrf!tT4cQwnhP?Hvh={SuO%KOrW}#omN+C3)2o%6kLJUbgX>4*gNr^aQ z*$e-McFg_>?UWqO3xp>{8D}9vdL%VRVtg&2ReWl{o2mhOO0H{ zpB5WgrdC^~U0JyesjtQIo*NuxVA@CP05!zhU2|m<2{->^aI0%4@?PgJz`x@I98yDC z{ZweU2U4>!4qCi(=rzYFAL9J_2B)-7zG-q^M#1+4f$PERR*>Al5dJ{d3QPY3MLS!f z-jhQt;Ej@rJqwrx0Ry0kmO~iinq~noL$xDx4)RiKKe{lz2Bs62ZKCjY4njfp>m_g)8IZ5`5de^YUsp5U6&K6pvIj3U z9T2kdgP%HcwK{GtLI-saeP9FiIX*+*mM)*hVqH)5cNc#GTMWY-i9JWHuZxAp*&l=q zB+KG-zs1xr*@R;b{3%`&oC?LJj2_566L++PGGr1Mm|p9OZBJc}FaI;!Rr|p#^B$KS z7`F#?TiMe##tx`-F(s02<12sLvpB4C5i-&mFdJZ>$84T_N|qRdWmf02y(1wnCIIp> zp8H@~{NLsS4*x^!OqiY_aCh2GEtBb=hTrAcJ-zCB)F$uNQ`p{ixhs=K{QLf9K6+

zcU|Z4kS(7F&?f!~?~@3@x-!9fi1CoagmV+biw8&76;TFq!%Qd6Sp=Jh0)ltVP`fPv z+4Y0wgRMB*m)fzY<`q%EqeD{47X7l;8>idp!9DtYaE7dq8JJ}x<2pDaZ=A^6>ORRC z+jN{gmC2cl&_KUoMU~94Lo9KZkMeudr8tvIOv~cU0C$V3mP3xjuw$O3k7V?(+}6nt zz=>>b#rDUCJQi(_Cv5q~bot;!gGY8H0I0+ux;M?hJfcaVgFO0#dTy0uI2uR=QyjDR zu5@s_be2-vsA!XVNy&==)~S_-DTsKOe?KO*NOd7!i2YK5lYOYZ_!^b^Gw-0#ys!ek zuWF=QB;U;ZNk{4{w@HkCsh?wM^eZg7wC=b_b^H&Yr1CZWRr4eul>GhJWu3pJJkDOj z7*wW@5&t8BMMb3O=AoRZVq-Bu7*C`akq7j7wsI~I^byhgBEDA3Uk472QP#~X@@u)T z88nhcgPDKKLZ4lE&UMLIZ+ZP8;-wJ5*qy?FW55nqAz%*S-MB&0kXVm#{y+9dPkk0e&@4@ZA;wq0^4C)o{=5OcjD_f10|{yv;1vomntS7UP@5q{f zb6jFzetcIIeF513l=Ww!7rJmu=u~jRER-618FNCNF7}flTbR6#`C5VVOWBUL8#;TBj-^kpvNnI-u_U67g-r!{XWY#-Gcr{lO4E&4p`pfZ|WGp@(s@op#@N zNQM{~`Y5))@W#YWygr}-#P{wDUsocN2ILXoSQO`dP1j#w)-%3a_4zhyEPP3cXV35E zr7Wi{)YwCpb>iQ)g&wJ)ebKf87h|6E5jIso@yIqY-kS+xrSss!z>hwoMiRsH~scK zPo6lyoLg`YUh@3t*}jMpA6xOpxjM&2i45o6Wv(Q>-E<|*El4Ae{_2mpJ0S-HZKj4F z%2m7UBdKq(6zM_mZ<~84C1$D1vhMUW37fPoGwT6cVd$x`f-z~|g%&$neW@C5+IZW1 zeNeKu^16EI8S_7na}YBmQtU41ueWPp_R%)q!lM|V6dwVg!(E?nJ-iFaB@qxv+%HHY zmnFZXu5TWf`3_~7yh`P^rm5bjYWAv=wI1n}$o;{T7V4b_waf)P9lBW+(t%xxgWS@o zsXkQs5v1NuPNir@(dQ5d(Pni;qR2SP;bf5##fQuvhV+0EQ{XgBBF z(Z~Tl=CNT1WswiTbbZZ3kuuLQ(3>3PAE}$t z!=Y?3Hr1Qkyq((xtjH(45h3(UTNUn6YWl$X32S%Q)=b4bq5O5*t!mP)kC5L<%T@AS z)E5YyDE;~jUlC*UJV;etUf6hdoHNmKH$AiZy!0RZ(&IZUUTm$Vrjtp$o8A8hE5V|- z#BfO~7 zMk=LMXsR2P45qD$JYeRwOrBL=#BtRVvjSv#0gw0{`{0+4R+ntZ*lCnMo#u6;?^D08%uldKZS+bRHGA}2ibxA zisca=8a2hm47F&&$M%D=rI=<-1n{%nbH_O^mO(Mox(9E@{4l7eJnUN$JfKmDwN0Is zKgE6a&*+SeY+))xZoxw3i9Z-V--8$-BeU9S_~y>%&5~(x0Nqr2>tmJ*_CI(zZrCNn z(aI!iplAE?(VCbSOCNYWmbc;l6CR^Orc5A|f->eJv)lW`7EhzMTei^^4F@$dy1x09 z)rKOT6bp~H>{Hs;tBFe8Rt7fOKjzz>Pq3(;Nb2=V&@-< z<}EA6Jh-`)KXxc;q_gWvNp=f3)q^yIff7xXWZyy=sz>4jx0xT)Y^8Vvw1TukohEPQ zdaRCDkt2(!9Gt#yHEAT}j3~L^w8?T=f+k7?rGJ#e-~v5J=;*wYqpBMV{F@0(=ehRl zQKQ^QRfJ#$9DWX4jQ|*ut`S_LmCgcAnHefXvvRn^ny_K5zE<~Z>w4tooTIDB{B}Ks zGPRD00B?UoX-BqN+WR=tk)o|XQT=QRFHOa0H3JoWcfO7p9E-fJox|+ zUNF+}x&I7Y?y=HnXidF_^w}=tH^$E*y7C7d z&Ke=lSV`t2jjpF-5kYt%a?emmb(tBUxUS=0Fy4wt>p-%WDw6DRf4D$*S;6F`+UyF- zKxehW1H!Rkp$k;TxZrurnluw7Xn@=)ec${iSm2J+kFdI-H7CwnH8zG;Yr!M21HKtg zdZYaLUoQc>$M(6u>7nOoZ^kVA11aUau(08;uUl!buz*aAkLOVZ%-C;hUHKDb1`rZL zLaG#k$g!5#2qn6!Zy6r{fQEs{$aXrA+smzDiVBWJD*)aG(=U7uWNOJ%a9%L!yY;*B zE@CnC5IFV2jkWxgxVRLb2P$7hxZKUJX4PrjDi=CvWv6&2!E*Jn$}QCYrHiivTRu1d zw|~e4)>Y9)Afq)pN{FMyx6Pn)-lxKq*|RCcvX|cvFzp*JDgr40#jNK;I^Dd5IwHWE}uCIn>(sjkbr0V`={DKWY0rO^?&CX?a7UnTZU|97?ge;=5ndHJ7dz<^4e1=ENAn3$a0VMT{t z@m&gYotyWzgTJp^F-R_-TQCyr9>8d8RvA${4X^8f10JJ*r0ZK3E`Dbmwa5H-wCvBh z#IKQ5wql+j)Ld`nH~Li4*x?f z4rD%GdZLD|UptnMlaU@e4FsCt^p#hACwXXXP?8_wQX_y|rs`^ZHGE&+)xJ_ULZFt> z_&05gd4Zi!uNk)bN#L3O=IdH|h9z>HWfe{xg~!S@Ws;x$3w2Eagi2}|bghdWR+`xD z+HKtg_Z3{`vXcdIYGh}4_-g>LB`Q*>0$R?XdZl4ODd>yGOR`Z12208B`rwN1DtLjJKt9i ziMP1_^h{FaF}4qR(V_^H_PsxZ;w!~U^3;;tqAP}+F4rs+#jjG6Ghx1^i?L|SbA>&b zvA6sbkc#l{!z;t`=0tzo*fx{V=RP4*{|82vZz&aysV-epKH0JiIJiDTKW5e{7WE=6 z|I2K4fE{$-p{xxfroEshXjTrN)Vlet(}c-?h1F${?X757((Ct`qBTQ22Emdh0L)Ts z6nXdqdOL|*{rI9BB~W#9-Ty3i5W`~t?Z%GyHAagw2#~o7&Bn}AU?2N{Dl*(F9ZL4R zlMJzoF0z#PKlL}6<6}U6cf~SAJ9fMzu>`8eN%fo5^K52kSV|@gP?n2^&KH zw;Nl@yi`eP0SL8w5c(s#_rTI$+b1$9_m6v&EkIJ%hMau z>jhW@?QyJOOAWeGhOXZVf?1Jet1B$zvuG`dd)D~Arj1^x)2ddfleuU>H-+%Fmj10Z z+RjvueOG67if+1TMDNxC)<9Czq^!KZS-9vW;X1bZ-N3MJ-Y+@tTG8xJV4q=P2Rf?5 zPk`YNH*H0zHOGp2trhRtB6}@Ij+2UnbG?iwua@0c$E`P&jr$zzXeO)GzM&CTqVq`G z+&KzpHJvN1HSVH{-jV^&+QRWI^x405?VOTpy&JH&+?5)ZPqODnt9=@>aqsAlz*JuB z7Yd!MEl1mAQXI?%WD-tki8se5_WQcIlu`JP^}&oA?xUj0^@f zwhYw^9PZR*8J*U{uv=>rgsD#)nBLJqp;6*j^u3^^^%eD5+U?gGZrVWYD*B_^YIox` zq{R%!ZXa?qV@=Qwd+O@ti`_W&C0wPjfjJk4i%276i#pPTPhs>jqY;%M7l|%m2IX~2h_*YE99!^fn4S7f=4G> zrzga~$C$vi69vRM_~6-~N-^hpe)no%u%VS52UD8FWRM!X(7h}rXZ3f9gu0OD9#L&9 z*iL-OkF-j6$V7oC!7)D=c*Y(3oB8;+3*>@y6{oYRuP}gy_ry9)9BFL%v7ZfRMYEN7+T%B{k*E8ZDADnjzE67L z(4IgwQ~N;^sdm2`ecYJDtDfKi)T>5C)xwEQJgX?alvX>l+8NnBMq+Eyd?0hUXyvsemdm{ zxzOxxbFgls#Eu{TDy| zC*#VcN-frTAxY7hO(;=P8SK(Ag{&KS{rnu!c|;{wtNzezy^$PO?{3NtYP6jQpr(>b zav~&kiK6Rb!|MuP5-gPHcx<)z+rXZdsh;_)9eM!vMOnu5X$LifBx-@&!7=TEtWJr^ zM>-*VLZh*0F4nH0#ITxQmN95hs81EL>P*9GAmN*_d2&Kl} z;7=xzgR`Yj2=PLfzt?^j)}I~CdPXhep!)%GA}IRRPg{O5*#~`@$R4OT=8z1(xgH#C z2uz#{`KJ4rtc30&xuh}^jN3&mTGk}to+dQz+O*{#kUBpSi8{7)(?X`mnyI@KDJZvC z6Xxi?nyGdMpoO7EC);!S(5?3~lbiDr(PYl*36HklHxi{mAD17S;g@vcsp<-!E>L}U ztrAI zi18nXSI1>JSxBQh=yBt{p35)!B-d|h^$Z&<>Wl0MEL3I7YPUOFGYJaORT?Hw>uaUM=x*3Aof~GfNOe^)lDb#|5FhCw^54 zkj4F%yS6+cV)3Z)v|tfT*gpwSh>RJu(aX9FyQkGl+9-8xIwSxImP^rH@IP{z1>i5h z_jm~}-!5??)VS2{oNlaNzcQ!1)AmbV^Gb6&l%)yBOMWU`S5F>L^WKi-cG`Zw1vlN% zp2iLHDshjaSqnYEA1PDOJ>ht_t%}1G%my{pK*j(||AW|8KLIlV&X|e4_@AZ|zl!u- zxEU1Sw_gfSB|C=mJAzKXy}Gi#LD{h^OWn~e3XTa^nQN#i)ZnSd9UL?Y>#hkYU zVAkPg$?XUg#>#R#Pm0Qo{F;$F+T~1793zr<+A)Ox?M>S zLq*Ss3&4U1mX(JH>xQZOOwgV~It&lmAcJj`cshdm*6pM(1nMdjHN?E3FNZ`p1H}R9 zi(dn$$u7FZi zPhs?cntEUbWjZY3Xrb@idAaf+Eju1e^FrvW>3InaCmOTV*B?h<2DgTp7mc#_~MZ#^!l6n#`@yIhwof69PNVO)I=LmQq`>glcEWpzIQqr4V>SZIgMBQTG{p$IseVIKq!1T z_)YB~GweWETLib&1&LdFq#5 z=j{wbx5lsqMCpHS7o{)mb4o0#GnlMyBIjPp`{Hjs9SI}98zt5absid?MY7lj z@0m(!wjM3f;JgH=q6$EnPA|JiEM&p56t!?c6<>P3&Q|A12~Q z&X9Q|Ri`17L(tqEcm7LB4>zTLCP(P}*`h7^n9Jx-Um{;nxRlIC%R-CkRLNLe%3usg z<9};6Cd=C;84bm~>6!Y3#l^i7+_MiZQv=$QiLTKCRxUNYrgfCz6S$0_=;75F*<3AP zx9LC0&%?x?UTFyI?F}&dpU0JRrnIv_atyf{$wQLj&o6M`W*)ksa0Ox(FHia#W9(98 z4^?OA`7oceExieXMaJbZ`jRe=-$iR469*d$ZSZf0XP29~pU%ak+g~)FrQIR9+XeJj zpIIHB2%~JSOy-?^S8dt!0RIzZ)>>n(Ucj#xI-{;sarH$L2j1Z(Ui!}n`(i@c`F|QXgCGF@_IxaMMHbjINU%!wY6VTe&#|d8; z$g`pLxY4B)oae~kV%jdX0pHMY@(DM*eEI{OEA$ryydlX2V1W7F$s8RPTUwu9+kC7W z5vJ$yn}xAhx)HwgX>ii%ykL7ck$EK9h{GIb|XYofKn6U$XE9XjW` zA&um?G>a#tl_~^v#zhvLyIG-$H75(5Gc2xB>>pP-2QHFs0PZftX}xhZNzl`!S?`7mIcV3hTwXEhs)Ai>2mi2ivOH{^lI;yN@A`7fY=#=lNJ$*vd@ zwZB0|HZXPky&Nc(-e|uI+7;0aIS)p%=+(0o?JIx^&42>p@ls_zXDu+Nmclx9LP(YE zA{A&cgiL5s#Tm%u{`(>Mb1I^82yvV3H>KynPj1H_eS%cmxd;k?eb<>O&qYHcJ}&>e2@bs85N!0G>Q{lbY9 zFcsiwco_*VilUf!#&WL0LFh<>;%HHtzo{%9z(d#n3Lx(EW)^t5zEju$*}faWx(UET z(+E`I3pW|mT2KqRKN+s8@ucr2QlYt)iW;jk_!TSd$;Emfu6z!)>oXK``2!{OYy1^y z_Uf8Nb>?-Sbe7{%Qc;$1+oFG~+5yfBB>hx+I90*Uz#gz`f-%pRr;I!rrL#@tKDUna zS$q6fUuDYo2Za4%lJh+1`Evi`t;*_tXlIqJ`lkjkb@Mdcfi9W0AUfL+lE}I;MK+b_ zcf;cJ`w|QA-tw&+e?Q}{Cj+d5Myx=)$M966sMIkE9z59IJ(#yGTxR^Fn%t2PG(CTC zul)w8W&Q4dF)RKr^B?O}&S?)U{cVQm`xGFk^F{5SKwzQ&;6WJhoeZ=F*lL!X1Ym}@ zBq=X6lez!QVssfP;WTCi(ifhR>S7N*U=ktW0(bK(IQ0Oi#jaTtQY-bv7@!VJyH=*v z{}#`K-~X#c<3cU~U;z=Jaa;f2pERLL|3V^1c0e>&+Bc`p#y?YC%lU8;CufN$+>8<0 z&H21oslf-NMp$|^QT~&wOYSz5wG@acF0yhUV*&ml`F3TBIfe<+-a5j0Z>5;|Upg-m z{6qH@$c=u`=f!X^LLEFlb?hH64`DVK-M5fA{XzRIrmf`EKK?x`DA>y`b!sCOE7Jo_}TAY zIM_Vbfkoq>bp4KtV?qQ~!LxaAZ!TMP36+@B7x#~~Z+>7fRE|h8tnCFxL0ftKvu8pt zH1sad#WmROX8uevxBqrng9S9)ElA>Jk-&(I35Qb@K%X7OLlinT#uDqeUnw;U| zbV)z6((t_I-Hv`(gV`>R8SjofJB_OzR`D9(dHW0~f4&bvy$j0N=(5Ofv{rIG(EFvf8WwDzhx@}Y%1W3clUNKz0L02Fs zXg*kY#mFqJQBzZsn&UToet}Myr!ybp(7UuZS5T&zAq@WhfVv6%dCawSbg=mCiA~)d zO%L^Zu(=Sg-`pOfYEqCEJpWc}QXaPeC~ZxG8xEM>EKH*-J1Jn~3Oq|56uVoI1|WMU zFQ{AELguT!ZGQrAQp4WsFH$@)7Plr8@Kr?dxaF5Z7eJE@DkmR$PPJ7{O2kZ`Uz8rD znF2q&u`t%ad@wQdgB5|7=tHk6bmhIuGV8ghfZXMOQy1I3(ReH$x>(zdX%y|d(1m&+ zT{pcJsu0CBmfy1gNX|muXns|)hspB`1G2_u^t6DVW<3F ztwF3f?e^vR2x|=I*d`7jnAnawIfsee(9_^0p;E-nSL@h~HQhH433}e=dwI9-^w$0O zb{OtH(BL(I)BbF9UIfc46jDi3yDrf%=-Hx9rh3v=xelw#syMPK1GHzjl-;3@rr&C=c% zT0bsK`?9ithi(Cfue1x8L|BRyww|%gGc#(`+}*yc7kS!vMA1o1#%(9wM9TJ^=WqLl&n;WBEDA z;6`Y9dN466I`kWhdG@W@hulydj5$Zs9K6QE(8M+RIv1^_eBPmnP!@LcJ)I%B4y@K} zGO@fhV9PxBsj&{Sd&^R;H$&8_*LWY&$L!Depp-vRvuV+$+G^hHW@Hp(SY^|H)$H=E z=KC+H&s`Er`9uglm#Vc?z1JvVk@8pGq$#({E0RpkzNp8@ieKp5eo1n#vwVyf(mnRo z*7Lq&tH-;pQ7Yi}cCMD^V;5sFlcvz|z^mQI<`&F)`rv&W%;m|KfAy}}1i6==csHaM zyBpdPkHPH3)#!k(nj;k-?7qc1s6sd zyJVZznna7F^0&p^wr1lig+h>tP3+l?_Yuhpx+$GnCzLKVlxqd~$@@<#V?@`#O?&9R z^U^U#rIfFGx?8h~ZPT!u^V+ zaW>`5*wRJAe&UXFD5tJgCnQUF_1j6Ob(wc|^`k%bG73Jp2DbYU``p{xbOD4Q%;wr8n5eug#ymbXi9J@29H7 zvZ3XM?C%ga%l1bnPiI1?W_X-EbLV8Uj}}JO1B4FgDnk-y^I!U|qUBpew|o80ak;>n z{9{_P8@`#_(_N98%m&_5KkXS)B2Zn0BWB4w%Z+G*0(&8D&93`B_uZ$OpjClIIJ`B+7(2|sWhkQ4wmozJdzV$ z4*v8hyi|I4&<@Sjvvcd%NGy-g%eO{4I}hew?L{O8-8*2RND&5RICITkGnz1Jy_e+L zeU4@VQHhT$aLwHm;tsj;rc#@7mC2}vXCUys?KuvE(UT|jiJ#1qD;%E>c~O}zjJ3;N zSVyRZ_FCk+HW;RjrD?g{dG@8m6FMKZPFEr1mDxZi=f&|uXbQ$Uo!Q^&r>J{|IYyU` zckyaj<&9kO_CXO^CZ6-|qMEcRn(bCGJPd@~kB&Cc{W1Z%kiyTI-vtCpOlK$#?=IaXdH-^_lMErX3LTAq$EvAdJ zp>2vUoqC?+6y}%NyViuUj;`5j+)b0c={-JFeKLsB+dwOJ^A5I0r-4W3+?~ZEPC+O6 z&-opx58kBg{<>rMn=^cOCi-$dU34EpQ^75S-?)6GqLQ&R?!aQ&N(=()M@EtRO4_IC zD3+Mcus>wnI_dd@GvaCIYw2yg;$2u9qB72bfy>#-Jk_DJ^?qi5r$NO`e8HP!L}^9^ z%louNuv%t>_IxpGKWpCgGt^1-`;yVB)Fg0V#X*tzf?HKC=6^djsE8^js_HY% z9?Q6}y~S)U>xfL}JN4erEkJAN^)n3*=UKrWFRwYI5A+34x(503o~P5pR3si<>t70kB%+^gU72qp~+ za`QuK1RBJQUQkiQzXbVI67(X3y{{2sp78a}&P^c+wTE8$kF{XxLzYj{9Gz=O?x@fs zTaTdyd{dA3#G;%2bm}rH=o^Z;KV7Kki@X(ln{Elr!YKEgyVv~t!Mi(?7mNimEO&+_$gjLrGQ(kg(Mrm%u&zasKCy{01urr@@moU z3OBCb`uH1%u*S9gEx!29$)GDVu^~jw=Q}KlJ01jsfZ2lO1ZgmphR1@4@VrmfLkp?O>;~XP)!; zQRdsRDcXOGS#Fl5TOzV;b#Vi@nZY zK7^T9I>o1^rk3>Fj$x5QEByRIXO4SZC*k@zJ2my{Do^Me?9@GZj;uuvL{r1;SQ3o4-&BbJ5N3jWJu*9swoR%XhDoWgWkPC9BOpz?a zFAm1yfUx~CP~)j`r+)pT^H#NVEUWg@m9Hnh^u`yc3|YpUgJhN|&hM)7b3OZgdwz05 z>@IOpsMMC7VTZ%vNeDzPS^lVbqc4P5AtE9I(5u>$nuDT!vUz|pY*qW<_P5qoLw2!p zs2t^pmG*efR4Ff%3mlGVpg6X$Jbw_bmD#ZFDdMN4Tu=rMz;LoazOkMj+pkH+Imml+KL-Y>h? zQt7p*6GFeg?h!I+gA*4J&{FvEp6=%%`JaA&ex0bAx;h|W+yRt?X0D>@YLjP_bfZ9r zgn^kEGg552`to{a92bE4tN>Fq_V%uvgLD9533dhOn3)|#>U1_L}@cI0C;2wH8q>fqc5k9zDFqFNyM!-RyYU=o(<*@@F=0IANR)hM|t*Q4h{mN?I#N^!S+ zEEb?pM$HJutLS<8I z17qm_5Ox-J%pSmx>?_7A0dOM3f0P``$iCxjlMV5zDKU;bhelhm-$k%|&yTyEgEYz= z^yG8sm4z!ujV8G#$OS#yHcU59VwL{<`!PJYvKL~&WpF>fMiD7I0T+w-;ATHS)no&j z$^_}~`DKkF5PV27@7LosPR9SG;zZe*x2(mOE0Qrd7Q`9~9 zq68hR8na%tkqrL%NbYd0f~>&h{mV*qgSRifx+<=TFtWC0d2lIP90cJCAo-uDHzL~u zr4s5`;rk@5?gfKeEVK(4)deN9Xdr63S0x}F@UaKl|K7pSve9Dpd=FwPP&gI0r`YdT zri0FiU~~!$MICz+xtQf2-$CHJMf-RGW@BVzgv@9mj5}sI|M>m|c(w%d*&7c5ei3Z= zQLpqZ*NzN|{BY_~+k&$anGW|A7jF6WdTT*V`U+duxZk%?_2i^T$T;Zs#a%zdC;I!i zTMOI%!6wl!gLvZlQaF?*{e%b)j~bJNtDb?u&Vwqz_cAs$U4`2mrjm8w;H7nH*k)oq zP(ksKX1Qx?lRr(MMHkN&gCx0lVJ?h@Xh7}`;Q&nhhE2L;n^B= z&T8CbUacCJvfA%T_0#Bl&E4RYU`9%@GDW|WZnjJ7<~AEkazA@z zWGTLKP0sKInS(|C9RD8DRJna=$q72hO8TKb4P*rC zL10hH$bdi(K(N4q0CF+33FRW~jzpQCTwM-NK^y_OsJwHMh^Ex5a27*6h?R(kA9) zeJ#hVMW^beIUa*P&PRDayjyx&Q|ulEg%I;BGKg-Iul~d$L%@Ya3;QoeO0|Dm0^vyc zyz4&r#}(&7y+EwZVq&^F{CPP4bc(e1lm=n>rC~;Ag>*3rh4NGX6STk%9C(KP&$Ux{ z^C|r?c?t@vNc#U6dWtC=|4m0z^dtZO{>(Kz!cr&&q+Dd0+p2`qlbz2Gp@^V0C%9qS zZ7Hlb#ZkaPVs>;OS|Wc*Yug4C*zsT15zyB;CWAcGkI6G9@=Rld6^tZ!Errd@bq2v#4OL30v zxe6A{;Y60JcEsU_8`3JB`bY!!#?H?-?}r%M*lc$3{JGimx6iN-2+*{|O+FBi<~LXG z4Ms;0ck?CI{mqP7v-HU`hsaaJSme7)69QP`<@_&l14;J4%i2d0Jy43 zg8g_MZH=o$TCS`9gb_jVXe=vh@QrTF@=87N>H#!_me`Ig7T54l4av4LCh2(_iQWFQ z28t(NzMwC!tTYNa7|N;)Ah^IUaztZ)u3Ua)!+(fvHs0Fh$O!pnm?12cd+e~eAJSTN z)gRRyVRR-5GC?ZCARL<0VN#;^j zS6vN>*%)XhxyR2G2bi9aj~U$3#Tk(VPOnDAt`PcA{Itg3Ee47pr^0}7w)Bin{;rXw%FRm-QHZYf!H{(z zVIQ=wYIU@)2n>^!Ra9H~G_6U9MPezVgaK_{X+LqxW(6&b?Oy_K#JsOn%VG^tKST?F zpGQvZHQh3nus}y*R{yzWCyTp{mH3;SDd;;sh&=x}IZH$Vs^JRYW)8~jP2~X{Oz0pG z!0_H|+H(Pc_NrV!Wn^SzNdt%AxRFn55vj@zd9JZSozo{QJeH;8_KAq49r)B$5&y#Q z*e0bDZF^(%dkpm=zM-eD(+tOXWp#~;>1CxqfbZon2Q20tm5;l6Hpu1i%b;(K@T6go z+_&qJW}?!vt4BYy*dh;h*GuAJ8Ot8L{hfn{c4;`cKXo-UEvy$5^(NhUh%O=t=7jZ1 zd4=4a40?nCLoUO<`0=U$8ZCvpT766u2a#oG*#QR);}NuKaeyDv#lUy~t+5}hXyN)~ zjYS&${gqzUk>Y~c{*IN4(nE6ym)_Nu79Fb>4~u5jM_;Dp;`dE$eX>bruj+5zN$86) zT4T7J^nmzOCm5fZYxR&n&?`2QG;3~E`$$J$KZEJKR7f#<%XdKrsV#%YG5n3qOs)4I zy1QE$xsJ$BPruf6p5Fbc6|BC)2WcxL#=oHbOf9AnYHH0` z5bVe_tmk$6#Ht-bV7AF_eeork!sgSDM|tzy<9ult3(v9+FMZ3);&)Mu*0J=_?Ar*| z7oiz<9aUeJ-$_~E#RW9)?vEMqO9q7{8nWkcsBfV#|JbCr4pLLX1g`eQQ+zS3!yGr| zmk^BfJllCGtD;V7O3S*mM{0{-z==gg?@d}M!%Nk5TwOC}UBK(C(O+44Jw(b3EJjt- zsLA$i^HcM*Hs0~Vl^@q21o7*_reA1^T;=$A=dgBM#h|$cX!>~HmTIUIrg1Bqs9$;lmCc0aj~e{hlZk7Hq7|U z{{CXtVF+#+m8R`xQZo}!e2!T#w z<1d5C3GENsZISnroz(_R2ou~Y4y6}R?!&GjFF#YyU+czG#wXQLb;@4RGp}|EcWmCb zc-Gla{ebh=R6ls2dm16`;oFEd)i(a{^=MSBIXli1tkL1|_nkq~g^ZoA2YdG~Pfi7A z?V3_jW@%>Tsu;sbxZP{Yn%35Zj3WYx8y2Q-il2&{w>-Wy^nw3*bZo4twsthvvT(?L zRF%|=Y41G*&P~=?~^q)Bm9Q<>nq3ohJxWc!oJ>zdJ{g;`x7pg_#S>T(rwQ33)VQ; zqQE=cV^C64RbBqoB{*7hR2Y)PZP6Zmz1-Xnu32knd#$3+Wp878bXxfAb$OD~<_0NS zEBRtp$UV8oH80A?=Ok#R;8tOFy;>iqnpsJ_;b zmB&^Y>68LV+g8{?-d0|(`^Qp^&#{x+DT)5j&5`iEThk9R(fhO_5AXhuz3 zI4o|Dm)SOYN>FwSET@IsygQ>&5#!FP$<)THLy$GK$;#8ufu4Fl zByNRb39Dc%D~A&Cb1S+I*0-fje{;7Z+a!{LT>|uU+)vgVNA8xsVmG*^eZH z96^0&MmOM?@#E)NaWV1bWT>QH$6){Pu-)x^7=m3{;Wb`-+KCDM{2*Af_Lf0Jta{f` z3_f_8QaWJ6PPNJ3{ASgW{pw}p67h)Pq7TxQGraw^mm1ld~e^Jc@+y6E6C&YjM zo)_yl?g$-|mCv``GjheXGhYurS#fj;7D=anyxKGC9KUq}s&es4QYWNLXFsuIKF#Zr zjY+?)P3HcrbNk)TT%l(!`m)c39y@{GYbIHdW!cG)&cKa2b)d*x<6O6bF6M&vDUcee zsi{jMmMY}s{59>=W6Mf6D&OX*FAXIWloU6F_1sUmcjam!F;!|SQ2L<*#)S~p+InuV z|K17lLTNR>VeQdG{fpvGpDA87`oAuXj2g=KxX#NHT!@ofo($NQsjGKxf91*KzWU{` z%pL|!>>RE6CRDI*A-AKt>v{K|1lRk`oPR1a|CgPelaIG57X8)OPxp>oVRO>t%#x_t z!}l3edStKs7*9=^%Cvj5kaRM%3-&oIZadlGh>Up!EuJRO7n^u%7vU@gG^c}s;+Gb} zq|_2L_X2n{T97Y6^6FMAjwZ1MeOtK1=U%oFF|r&E9b4Ib+2N8tp?f{troX77fbc0) z^lGZKw%S+vBaZp&0du2uGv%f3s^bAaDk0V-3?o~qa>7d&km}uiui4F3XH6IQM*^da z9=f2-V|XoU4S!9TQc#?cN$P}EB`Okjdtj+w59P+hJj-tRpiOwy(^3u(HWuK{I*pwo zCl0zJ4U^3N!lolLAv(G^Hpaq{zP#>yWQ=H}(i`!*1IWj60f-hNF^+9Ap{ z=c-m4Bxj1SF->NR*v3MygghUEf)Vd>{fp|cL)lCbE+6JB2w6jy1^kD(>S$6zNE@ci zdWJJR7APb8Ysn;OR6)-yf1Xn~IO)ux;iJy)3OOPU2k;i%k=Ahr=`|0n(d`e6L@91Q zrr-Nj==Y=&{{#sabPvzuFauhsi)KKwW_H*wrHF$u;>3b2zK_YjtgcURC)<k>y1Ht&byUDaM^{^3skxSf_=;{j9@SW_fj$MLcB1ELvH&89<+jIe}sDz zMh0VpxvbPGR^NBsf7Cwj`Ky0=nzr11D35IBAjKoMiBrX#5mn=xYvUDHZ`wWwX-;L( zp>NQl%YJl zs$l8H4qJGYR&bW^S)Do-Gp;=!-9l2|=39wfDo_F}FAvT%8K{Med7&mq z;1;7-BqHRhq)$0ZmYA+h)9zn&ja~|Bk143wwl5WQ-iKp`AGW>jYOKtTVim2_oagzR z*gqJ(Cx-+VHzT9Kn8S=}+&sTYN3R1+IPNaL9QoA;6`=yl*lvSwC1x%Y_uO{Ib5Ld# z!?mN%ZZB@AJESLyEu4lcXZ`J~<+Y%p9-}c?4P71e4{rW)+WF%dUAFzhSuKx7c5yL{ zSrX2BE0@44_jh}g%j3U~iQl(>lx(wE?*7D`-gj9?M<<4B`DkyJyyfFTIcYV7f(?OI zCRt~g9RFIvX@_(}4h*4#xvEiOkAFPq(x>MO|Jb^$&WS7HGmRcd$0AqaCl_3trta3! z)Z|jJ4om~Finyp6P(z8=VyfuDLr#M4Yacv8HDADFWtc%zaC^#U^eZifT|Vo{V%TP* zvpCldb6!%P=Z;dFJjb0EeNoQN^P>F|dv2hN39-SKcUp3PM!Bd~Re{7en@-ERm$zNM zhp8JXV(q~m=FJk%-r<`!Juyh|l`U#D)9n=HyK~u#B|axUvc7?m$lUa|0?9C76|#&> z$xpllhgZmK#90TM9V~G!hmbtra7i!X?N?G34uNaj1e<2-qcH{kv~OnO#08(#=LGl z0bPuhtWwV{)ABbkFsSriHXuEY^=RA*-`$ul24wTDFAtm+do!d1cT2dIXNl{xMtANw z$(`hYHDBRd;D=rDtl&}(r#Y^HkDi;CUSo~1$!A2|fAs8p%MJUJ)=Ry-F4i{joS4@x zR0^`3-M|<{x{iT?3{XDt*#cHPo94bV#e$ zuMjvcAIl)6S?T&0D=W5~08uFJM7*M2!)7&C?LYdbDEm2k4AMtC81%v3-i3E&vuy$U z-WFNSaFDy_y-afKxi@cg0ej=`{k3sa>5ZT)az%2!@A8cq&nWzcmS@U<0P z)hE=b(y`ejwV3#xiT+cvq?pxw+?(4gHO)y2eS6(USC%d}e=ngE^Fb$OK)_QmBY5v@ zeNwS~u^8bdp`oj0L5L7|3p%k^KE?W`rs>?wG=j4?G-)!1egHLbim3?NiV5{MBxO$QTc6E!3`!$2j=A(9&kVlkBo|{*oZlE>9T15VFIk$eILE)uv61{ zSH2y`>0d|fs6n|WPDzS9ZBRebdlvL=_}5jN8+>qq zZaJKSMBf!0{mL*H&G?w@$zNdyIEaq%{c#!w#_-7dC*7m1zcfAMEckHo>dq_R`=RpR zmfA8y0jRbUmEcLb`A95(YpcWk?CGBL!Rz2r;aiL237Lm}_{L|HNKwc9@d^wiQ=|ku zWEiai4{8xz+)ewaDrCc2qCWdLF1W^@U}cj4%j!B(8$-si2K_9W-_zA@WFG5-hO z@B3AM_pWuV;;9FS<%m>5|Kp+sNl%%*k2!sj-fo|PK zy9fNHhS1vyctf$5mk4R`@?7U_3plUuQx_^@PRkPxx4NnsUQ1l;Ku?- z@%CZui#EJp?^cu(;{18>VW}`d_nXWa%W?nwHBN3I$o^x%pO>VEya@yKpBLuq^sGNe z1aN*;BN{2i$EW&p6m=(2aQo6F(nM5xJm0BqR!~a(&nr_D1!W*i*PK+1pA_o^%*r|> zsQTyM#WO{@6ETC^<;&Q>ntVC^MhZHA&w}d_Az?tjQJ$)alxl1-cVfQ=(_ddNZzzGM zCSM8qYk3g~{NIL#7j1N^ru*Cf8cwZfR5fi*t`_vy%6{JSh5j{cI1TsjA(B4-XYoA! zzjYHwos+q%t+~4Szt<}K^lvRj+{+r0|4W#}(W26S3BvU41Mtf~hh)BB_@6g1wB;qm zvez{v#X{vy4E6TD^(7;tKk8+eTipo)HBI575+?PBZ0wSXDfqmvx^V1mqaF`^xr+i; ziwian9W_%lVUQ>ITWUvv^iO9i`!!}(?l>aeILJrVRFA0?C^=#?{&7QUYD{S`_~ph2 zwZ9~umu2`o7+-wGF4)B`BQ`bC`>LpEBP%OQR62q`?XP8Pq#J7bdpc${R%*|-{iRn) zABF#E(;IR3!DVG-F378scwrBIo_TkUSZz-}o;l|y*g}U7d!gXvg%%&!|FBUUll}Bn z3a`r+%c|K|z?hojDRY;V2nk6kDcz%uQ6}}ukQ#fq-Et_8UF&^%o1;@|1Ou#lP0Hp&Eq_=HTEU;xu^(7>`kZLW$>XSLQ$y&#}nuMRh@9(KGeW%Y#r2lgdcG+fk`$FXe_wJ~ws)8;r z=8@SR7ss$?ctK%dbhNa-X=#*OP67%!vRul!*$$CQU==3V^mF-s#Z4(!`TFqf5}lIo zBRWL7u8;Qc(QlJ%r2Tnw$&ArV(cYycy%djQRnV(Y;`WMhE%U-#%@PzI;s>yP7eo!b zFA5|nDX9bNiwD&o36g<7)+(>DlZ5!-^f@M`9Z;`9%F1!` zZS02+7iO*?b1t2NXJ11#VnnHl@>Qk+9yLbf+3?Cs_DN<#Ja}B*#C0@L>%@xl~Rn{@RGIWS*5@E{BVNsu#S`X9{Tg+1c6S1R%_LN(Fj5a}BU= za==^!L7vDf$U!Fmg8r7YJx>7wq1?OK5l${s-!8hdg3$#`85|-L&#H%Jn_1Eqt(J{- zIMcY_5ob6(A`p+ko}}3aXupo<%eM=q$vEg-FN3!KpQ<8kD0L%J_Od5QB|RM$%97$H1~D~ek3BO_N%ne!3@tCpCmkq_iBW1CD+xy|T31=ZftUTf z72yuD2DA_XKe3xENUYf!>(_AIq+@Pye0(BU+N)78mG8nmhg@Qzqer#|_a=dxH-{@j zH0F?(Ae}`UBQ`m(tuSOXY0p{NbL+Qb%v?0YGd^gGIC8h$MV@xnqN?t1+JOd4#*(S% zweL1d6G;F@8AH#|I=C8k)f-!TXe4&<88G0JAoaKKW6px!i$|3-P}J(<3D}sZCNMna*WA{OtLg%Fy3R6d%0S80sF$g z@329IWgP1pgJ%^bvU-GNJU!7kEl}-v<|&qPu`?BF`K!uDou6^;Kj$Z@N)S^P+f})%IA0aSTbCvyb;xA|+shS{-^bZf z?Lp36uafy>pkbp^3jS=Jz+pq!fr2F@O_W4hR$JP#5SG~36}wY2VfokNxl9SJ zr8VCI!F+QIL<4*Tf`sVZw%E?6wWq!WoOMaN=$G- zsLv2SQR{#KYlExpFWQ68#`Ezte$xMZQhj;AqRf+LGwQpTuQ8DjBUiEb4ZUr4l{u8E z+M#r4FZ1D4B^JBf*AMo~vZf?U*B8?c^UDI)J2|8$jY-3mm&&viI2zt;J9$~Fih4`i zw(b>&f^#m76q-w4`l9j*JS)|HM;o(gMS86s5@V6JKC5N4(mM(CJgPvjz`(|ahY27+ zI&<|d`Pxl$8h1Ycp%eQ3vUYwHP@YgRcKc^mOoY<*GVa6vp!s@2#QT5P~x6FlY1}j zQm}WxQDfKlqL9@7Gw^YCm3({i89H-bSAsP>rmC!3|6I7R`hcgf+8*1dYn7X=!Lxye|6`4wg~&$?bGBSvZDYSu`sO8ZMP04k-CJ_(GAY`*UF@PT%+YF zGM3*j+&5I!QJq3zCOYDX4cXtSoJug(-z%N3xt?=457&@DMr>BQ?9*u>b($oEJ_K8z zr*9n#1<#*`S`xX;2(l#yOSpF~Wi|0_hMYo!d#qTPW|vdO6O>%d+oaSQ~iTxd25lIqBOuO6rmGhATDIv*Ay1vAh)TSXpa{Cx)3aCpzhS<{SHM@0uZJ(Iw&L_zet(BCP zE|i(e71_QB7s&o#i_~_~(ujbEB~4Xf9-6!}#rh(6mbg(g(?r&U&+*KZqF1D(^ki_j zR382AODxYR`L2;%iMWGdsu2xC!PW`0{JQ*&`(U45HFbXUSg$Q2XU{XS%DVlUh73%F z`tgcgkqearF|4L-7Q0wHTz-|R?w*x1qWrVDN$TltUN09L)09dNh;Unn<&7?}jO)TM zFDr{a$If5+NF6v;_X?_Z^;WT24d5Sd@%V?%p>Q_?!9`20(w~Ccz?OI9%-sa|Pk0CI zRbIB{!-I1zS?yQhiCjg|b>!E9Svxz1gN?V^aM2SXuwrlqdFRd_NApxW>4T@G1h?N> zu;|X5PtKGu2h!HXCgyST$uO5&OqS9zDi_kQvVI0a^+=PK7dw0X<5FhLTAS-LxLf&C z0lO76z~g`^+;>qxMfLlyg(^x(yT2Vp1#*Ot@WsW9lAPGCkkWP5bhw4&>@Sj!5E>F^ z40FLUAuNSYR99674IPU0{cY>)A9BC7S{0>;dG|Bp}vePTF+%Lf!D}oj9VFOI{tFy#e*PHL)tmr zl=fTNh5hh#T;((qHBT-dX~=dd@?H|bX3FuEpZm!?2zk-HG4-IZ_A4K1cr>BXVohP; z-eO2o7Fq6r96T6#xY7Wx`+?k2fi%FO{HPqv@P?ueYV(xPTsg#-;vp9cX1+Q;ihAdJ z(y3eB4BJUygK?`qU10Og9uTJHC?V$;OG|irvA&?r8y^qbt8*MQj!3z!S~R*Y-+~Iw z#)fUTw%oew%dMqxoR?l{o7fv$>1=w>?fNM;BZ2H@KXR`2Q&kF|KhC%VCI7d2Ifwto z44hoAv&vgm>O6rz%;`&uc0nUNEIRtf+@L9m^1C`-i!bE8-2AU^J)szDuRAw zNF0o7d!eubr#aYQs&yfqJfqW&qZ7(hZo`4p7rI9~{kPs=%lWryJ|cghx$IrNx`D!>m>|h`m~H>^f^U2>Uqe(G*JiDZ5*@8^^|*jw z2_~{W-JJsJxl7N|RDu0rw-_|%{*C@e#DH-7Jo56P9J8&{_Ef1VtF_3cg~W{4irP~* ztGm@JN%(K73m*T7__!sYB3{Rxt|7QoSIX5KdVAV#JOB%E(dfRnt=*?lj*0q^oVY6o zDNOL8f~3@Wp(GTs?Pd2`?)=DXKqTktJ1lvC*+U=+cVJU4Wwk``4-*jHd@T32){^8! zvcTiyXoD0D@QqEJUwf9mwnHnwqi4@c!aM8GLy1`9B>g}SsqaI*5aH=xpQkMtG<2=H z=x?{GsJX%`Q7e!s%F629u9_dX*lCgz7r%4&?%f%Wli6~UAKu@;i<5gCz7xLO6$30_ z5Wp$;R-+ZF&o}bUHjA2yRhR^?4o4m^Wd*VVX??jY(^K!%N~n#O9k+-jms6C6aJX`w zU=h?!7BAd(-Ew(=`eaz06WyRE89bYAkZPqHQeRu2^`-rmh$Xj^oLYlpylU}hWt|+$ zO`Y@O02`JaFA=`mRf^_49ed>}r_(isV{v#z_7{j-TCRXJE>DCJE(QcuGHg!sW-SSD z_ob5+w|L}5Nxi@%d2+_gN80Cgk2UmdTNjyj3Jbva=-|Y=9QIqeh_k5*hSZ#-liH@! zV-_uyTL1XqO8fCPlDLy$7DU3`moe5}@5MjY84Oa~y|kZ_tFLpV7^~k$SpE^v7RB%Q zl~L*vzk7j$Xx=}T<*B|c{ULoD)y}F{nC81v!J!k7&dv;7o3#-WD@9>~Q3@N+%_^;C zd)9tr+{eJ6;oykE7PQ!ZIuobN z#7`J2QS8)u9$_EW{mhHWkXJ!M;HcBpQ#eB(A!?y9fw{ zAsc-Pm*usfL!)wx$|7)M^;gW}p{kvI#!kB(s9wA6RsNIv!7<*V>UZ4`^1_Cv&Bx@l zi4ZlHr5G{|5tw?4mq%<}%J0KCXHs>Sg34#sPzCBKTQB%Dp-Yb=`N*_?)oKZOl*_l1 zkk{4+uEJgGKez}HMPZ!lLys1s6=WAjm9eZPR{`^MJi=lL_SAi*z3@5tTDbFE*XU=t`<`OHE;{jG|M-4+2l)O=e@p zA%!VF_G@UAWXeRu&y|r&@+Ub#KGNEq^ZJ}UrST_ZtbR2ErH|Eo347kSTZ~jo@WfV3 zmD?zZav04)h|#khjA0%*v7@^won?JzkS48u7dtbSTF0=_G*dg^bu zQBsT#{45N4=oa~6o0&(>Y&gN)Wc?8dNiWy9hM1ilySB#>UQJC+zGfX1fZN#IPi>~k zP4!b=X6_x}$k(Wb{D``1-e_LlTPm+$1H(yh*~9wzY&c6Hz%39p+!;bqi!724H#}b7 zAjKY5U)|hS5|(o?09|3WteWqTImwaOXmOm(7kaQ?qZeD1nTN=spUgQ>D{BuYH41&1 z8DSL0?^r0EVc2d>@={rdc3QmT$qK)yq+9LVS2xo>Kjy6Zc3AS9qwO^iF|*T#5Gk91 zsCBmMH3e(qW%0Sv>dR+G=WPYrMX>r)6N)(v2W}qCk(7Yq=a7aIVf(O=KY+?miNK6c zqo|G9-xsemJrwNwK0&EFnH1Qei8~7ga_E!Sd6*db8prEubKi!PDSbJNI0%T|bM`A} zxQ*8`85m8wY^}Db9BhMb!cA+7F6$xRsgU%*R3vv-fppJQPT= z=M>+W%lzT(trP8?iUkJwnDiP8KG<#=A52ujowvuU`V|vN0TLiInoVb>u~T-bL6v4M zu^lt2>}>Gju3r#-VJ&6(rRUildz3inX2BQJ)mPdC(#~;tg}y~&c&rAO!iA^s9l^sx zss}+D1;UFRd3v*60_JW=MML3R--9n+;@~J9zgOZui>^#3*KDgji=#+7XgFKcWv+8X ze?q@QQ`8JQ;pJ1;gUYE_F~pj8S}a8-j-XWQo79yu4Dj~~3pUF<=C13kOyabrH*bXa z){d^aC2FibrkHPi`J~aXby+cE5R0H8H+g7i)QJ@Y^A` zZCi%>)i^e6l#}pAN^wQD4>c7uWOoR=3dUcZCc$Ff#Oys-xn!D7T|+}ZAUD~@(SQyr z&RK5*X-M_r>mBB|SBCzIOZu=f*H=*Mk9Y53n^=9*6%1ST!rHR_3TLijH4>jLZK@x? zI|QVOMax@L`A(LigkI)u5SKGAm?2SBw_pMFs^i5`NS2s@@_5~V9r{CZ{N{r3)TI(R zBbdzj!CNZ1`h9esnIt02Im&4cJDNmq&{%X8s>b&9M=KawE z>Iue-3Q-Hg&ZHr$tXn(ao}lE%%n0KRV*GmJTOISmP?AE6m(Hrv{m_0NXXn1|Ly;niVfzE(M}_72Mt<>rrJ`EdJ^0nuezTiqrl2Z7NFnSoX;dI_8L z8p~6Nop$Z_3;yii1|qM0tN8Pb`GdU(G7#E*G$XAdf8LySA_V^v0gcY>WUB=z*0{23 zUBd`w2!iuxv7g@!9kqJJs}#|;*lbkx?(N&>4&{wq_-xY+5D`%Flz{qH;O`LCwbyz6 z{c*I_^sBe<0dacTc=~ZkSyN`@R6_`(T>U5JAA7Lwif_!7^8|UL-=KeUd@?WJyv1D> zn12WVXz2035Jj&6F6k%A-&9p0Lh-g*IpuzsKwlI|Lp`l@F$Mp7mf5yv<+!=G|03GH z;N&T@uQQvf| zPeUUwUnvjZ)@pxqYngUqX9oo5;F1sl|e)gnT$!i@>_8&&yO`K05%mSZy?Sav6v1>KJ4yq@jp`@8DcL=L#vyW zbug%Qi3HY3N^7o#0$dX*?q9UTdO`N-|1}{=eQ2z9S~H-J%rJ2YKORZzd%th>3jq7y zP^SM6dDqRPwnZI)lKS4+`Iwv>>$#B;9SOHZ8h_nmQqni6!X7id4|Bo)D>EzK!S5xU zHC^@C;e1_OF2NvWYrLgRwcK>F@0a!}Ru&eSTJc5v0jYY|Lg-yDo$X`P3@g+UjZESr zBU}4<=KFy2gv-Tt70TLNgMfOa1)Flg&j6e!&R0j{0EaH|^1sCZlB>V0j#CyBM~%B1 zp$m|Y7W@!IA3&*XGS>mjXo(6F`O!0A_5*mz8oJDY@_%0uu0Lti)wr1flHr7ouT3LG zGDsiqNq@VY25$lsTIlj`S|8^4f(1-RsZVfR?fiPDzE^Ovs4Kvx;)c7<6~p{7ET6vm zju9PPm%X5(Nd7JxtDN;2IIMdU!p72~@0Sf03Ot=-B(%%lRt5_s{Fg0WaZU%emAE)@ zjvpF0(cq+5rlm`ffhMvgC~N&MlAD@Z4=`nkeu%WRMge$_*?RcraOvycyEfAF0UeX~7774@P%l=&s5+$)oYKvv$FUBiXK1Lma+g;P{rNHFK#d9G4cEXH zh7|a=f{f#`Ei-k@$Bwgpj#w$OHES}%>7O^8nk2$t*@*xmb(X+mtU;=ojQHl zTt!N>`0%Tm*0wva?PrOxymYbP-Wrd>W=C9g`q&-)*LuVm_1@AG{7+I0+j_@J%nVd@ z*7=H_(Ei|z{dWsXZvUzo=?u!gP8g#R>(y=Z+Y9%)^qDL(uIq&jB=^$BYP7Z!Xltxa z$0%!ce-C^t1l1H6mI|VHsWxMvR4dvLk=Juu*cip0uCoQ8xeCBHG?$EK5gn`b__L7Y zzF*)akp;80g&JK1E(B73zb`QcjfjYKvbgV)OW*5HA$Zcgvbxoi0CZgW8hTYdYPw8oa3rnYG9merln9Tep}o!_vZRF*6+ISxTC< z*9y+!X*4>)dMi=dQJDhUs+5gmcG*$wjlR|IBUwN|SIO&iLmw&Q^#>IF z5A&Wm5}CqEGlt6CNHf8Cx+nPqRyrflQ46cGPcC(mQilj7rQeJP5ME?t*vn@c+<3(N zFcy;`bGETyjq170Vv^4+X-mMuP`Ww#&3)Ol%QZ?Q+vJEIjmOY}fj8@+4`uS0jFo#_z@FV#FUuV}L z@x`CHfq0k{FpEn73v>p2v&&vP!K~q+&ZhE|ETvG-lTAMPq!z^K^gDn*%eis>nND1*L#0wq|D*1x6YKw9 z!Io9~yoUth%gbnsi4Sc?95-1FPVQsi%ZTIU6GLY%MeXbkNM^x2O`qjG{!RP%Dt$k_ zE!DkU6O11LR8rjCX_Q_s)P$+s?57H#z)tdsg35dESt}j_o(E2K$*o@={YuPkd>g44 zP>D6`6^odYS^TEB!mJ@V7tA7m*w4djXD7H342<|ywifzEACpOw_~Q6KX>r#NHa{qH zSgUBzcp9i zx>7g;``i$0g9zpIa zw1$OS-h(0((mH7_b}=gO!5oJRC+~k#$t!-qO1Qy^@xkURMN4n&BLV^>qeh=)*3^7` zg+)FagaEZFK!++lrh;8)(aw*0YU zQKR_yCO9}+tat{sJkrzeh`+=c`i2MXk_6OsKOi(~Ez!D|LfE2k8~5S!iLBU1PW@ae z)L{sLTfeNWLK;Y2p3QH;K#;~46e+*omSCktSDqh_z(WGmrwD@a)$T0;CW9Nfx35dgcnr&GNnfEI{JgUEj4zc%cEG#1# zS7RHT_V+zRFek}&J71vM(%)r0K)k(mn4PC02h!ZaJP0-L=^%{-KPf z>Xf>mb3ncEzQt|l-DwNzL);ZC#@hl;oXP@bon|IyJKC{Pl}L1IAL^%1!OdYli2_=K zqn5}HB$-wxhX7J0x6LS}zwL(8UXvB;ZU{U$zuhEClY&zZg44+u7a!aPiT_Fwcy{ni~D`CdMpt>!-Mm)BG1LHfDcd(Py&=~t<^kt=>eJ|8Sy+z@!iiHh z5AX4U zPNN9QnwORcP*{p-uQ>R_4=Q3=!KQDE1By+-`+gtnP9JfBHK}@#(LZb2vkJdwyMsv; z%3a;ZT(Upeg0z(l=HO+x6u%Mr&6RsMEQ!>3A}%o)s0X!KfiHLUwnfATf*JD!Lj>I? z1kFQLTHBHfC=+=gl^+}`JT2x(dpbU0n9L=8FzTm#+9l`~WjNU9aIeU*UJfvn`=2!v z+R&Ra?PPm7*5=PO4p#21ij?0mX>Vo@w~?NN%&n%UT9dbAGJHP7JB}aUW^rKBE`lE~ zl!OF{Nx1t#9cPD!@v2>|Au=~|*tn??CMO2s^$yar*{CXNXq!{5%50H&kmczLa*W># z8?rd*frg36)~6^WL88`(G-O~YVuF2#;%lX7k>@8|RgZS`5d_(&kgD{2>1|3S_%6ir z(|vk#czAwpd3Dy>MrzYOes0_IUuo>MynDMaikH{m&8$ZDa)6>`OvMJsLsV{GI{ujI zQWxiT?6fnPj5~X<9I}`xAj#?XnY<;rWHpN_$$k{u^kw2w6R!ytf?e5WuyT-ZEd?P` z?l6AzO`*{$70f5&?h931-Q-p5@#q5%e3ET9K8t+gNx1_r>BlBNAgo(RDvkBPs{nHSXp*(YAZyz53&RHSAeT0Yi0&oBy4ST=#0VdF^9J@R9 z?UAe{MRRMQF09*1#HxbL-WCTRUBl)Tr8aVfbaS!iz=x0{fA4N=Fo(R0{m!!ivxw&U z6zq8Vo6rY@M;?vY;wgK(;tMqL{$&ZhJZFoho*U((=&Mc;F6bvohF_$o!(K|><|7xC zM#WOPzQ%cZtoJI3y-Tp>#p#3PdUY11;m~hX?`h7BpeMZJPm0YS?-QT~V~z-_@hVZ3 za~94QW7t2PSj65@%IIJnQ8rmOM+%Mk+b5swCKwAX3)h{g6sE3g6)1&X+>NpF0Rm-i zP&TQW5;*-~#4F>$oeR~A1G-AYO#dJWP;BQ1zO2?ocSU?GkXYaP?T5SoSWkMHvzy%#+{q&oZ(j3)htM(@;y8o7u4r6Zi) zr~^X}K1hGuO&{oSX{(nlOKY~JLj5Y7&b{lQXi>HI=YxSK)ygd1{EpOVii~%m@+_VM zQ+;A2aJ#Rktb;qRbtmVrPmgy8d2N8>CxDac*bjs~u)bRF zsA}u4g)=6h7{DmDIOK}LqArF>K7id11U{b$iQvZihDN}88Xe7Io|8P)_7gb$#LZ?r z5_mjg2SaBPsk(HMQ|4?d!YE7BLNQ2 ztoQ`i_(Ruj+E0g@5;n2ScTyZ8?FGA^Vd+v26KX>CW0Iyzv7Jc8@4aNrxVU{&-qrlF zU~s8Jp-^|NI}mTqiq|mBrKEyog*{ReC5h33@j8j#3E>N)|1qG^q&Bd6SqifC7^~X4 zW=!@$6qf{H6!483d#IDU4+oX0#Xyk!<@ps~>)K7YYSgWVL(>s#?7dY@GgWI^-0U*y}vKG&JxxD++MZVAG}^}J+zIqxpk?S~m{WS}G9 zfcC%xB4m0WlQzvz`o=?58_S6f2!t0i7E%K4i`uFormI9hFooTBqLTtr>6avu8b`zt z;JScvLZH3i;J=}#(fs{K--v!Z+Tog9&7Ybm4sWR&OF)E7MVjyM-o za?y^Mn-56xI)+|a{GiBZKo?HdY~$~&a)8tjdz48?_ijum7;IFI<}_TwJI7V9R|}rb zg29YR zr*|%99a;ULeyg0X3KG@VCi$8@%*iu~|A8A4xFxKZD?Kir&X)K@Qg~&W^b_B>Aa9;I z%9D125@wa>1AXxZGI7txs%=-^BnLSnH0)A6pqJxX89J-4A1D`PpVsX>ovbICP7&f| z=SbhOPl=}$O!~lWi53)2Uk$MqWv>2EoL5NgKEXVD1i*>_vAsX$1hZc7 zh$`X-S3VU+NMrn}(7;{ya9h=QtaQVcuXQA9%|lsvQRX3oN*G)FXjPmiJRMn8vWu%f zvg_Pb+AKG^6=(0^MCd01)mz~X%7$3}w-ftcy?BB0;|JI2dQcae)rpod@5BwRk@puDeK5*gfoTM3On?&iU%Luoqw@=O549Dz& z*YPL>lr)(~(x2E~-OKp+@v4TL3|L#U)jaPtZ-oa4onFa{sXs@H1$IF3y5_X+Io%wo zS&D{newK^7>jGmwZ4FEcl;1nk3`-(Q!u6!~GPnQe!VXIRNTo3a6O=`>v`nG-l}gCO z_xsJ7wd+Y&==2PoIwBHw{0=4 zygYhn=o}qGJRvBb6z)F=D;dQ;3I)?KKE@@K>>!8B70H^+M((o0m-oVNqcSivr@t&S z;tUSWPD}eNf_?2f3)qOJ>U)hPE0@W|6{pb+YVLAkM~}%!U3w4yWc(8RYtIy8#=+YX zq;FiZ0~EDmSr@i+Qd4uArcLxuD4h5!(HH7Jotx91cc*HXuEo-38e1Q^2)uW=Q>+8- zKS+SpUd7b}>PWL1bMKgH)&B}zoZPgu6J@0UE&1u{WS(KRA~>=@ok2=2=k#KS;d&nV z6iELbh@1zsp3LX$wqY}z?@I?YT|pyLJ---?(7lFiHCGyAA75RRPzY~fFa~?Ln`1tF z_<@$ruX(KCzTi5|EdjuRnk$RC9#Lm%i--=2qz*~5I695GZdZJY4ZTa`dVMyqN9lG% zT`f&;1J4Ma8QfBaVBk3|6i>Oy4C}rzwg!sXz`c3&&5F7LX;dp(;=7g*1iC zRD7T?Xxzs%|LE-fPr!^kKELpL^^4@2bs(YT>^%R7iADm_vy1aT};CZ@}>)26KSvO|0`N-s^oCD)YwT4*7 zyW`HJ{#G+1Sp;r<7dUgFR=$4QSzN{y6wPT)w}mQ4Y1x-VAE|fn*R>WHkoZ;DSGeQh z?6FW6JGr)YF+Qa+nN=fIRlQSgDz;VwPsscE4QOBk7_eOQqtW3su(`^oY>5HtVD8&U5(a`7=m>E0)N zSad8*E6q5lFFx*e%%HVu=+hH&zL)Mz8rDdeJhR5aBjc94rK7!w0(_$5e;Nc-g7rnA z1Ym_NAx#iBL67Y40WKujRU`Sg!e#XruYRE0aM=`v!-M0e4V2p zOWl}J*8BO}Cnze`ReMK4(?e3+@9pp-E^vjOBaRwYajg>UE@+lq- z^9q~z6W4t_Vk5u055CQL=htal@-gfwxIHC4&~iuXHMe#4jXycx;&?FU;tym1Pk;kv z$w0F7ak;5MIECEJ;gduEUX7gK3jnc>M4oTrdhVjUL|(D9Z;rjI^$-BkjAHo8DJIkH ze-1NgBXaba*4m;hHJ^&}is+WI69X>cB7q6K5pk+z0UrBOdIZcgupbhEp1UuoJ42|J zV2Sse5d5w8Feq>GXvAD{nu07 z93yA2FF-49{pnIL1|jQ*!$Su|^)eRFy^Q)?MTHP3S|`iR&AszARVr8Ph)4}_wJcY; za~=Z__ygOCpyyJ#p-gE=o03}tQaL8d8_eZL>7KXGXcxz?@9r!9T>a-Mp zDw^DX`~DWXLeSHSwsHY?)JR}A-N3@%I+fAZ%}`94AhVrTJS<<-m=+hcs4{8!6P{C~ z18>CA0k-vMID9F;I_7gVAAm`^Iw`#R0RDHq(QC7ys?>Q;PZU71G5p(FT7A)7*3$i- zGBe-P=?Ed}OX3^Rl*p_cH8yeC9n$5tn3dAe0bLDl`nNCXKbNFvM7lgtGAFkslcg(c z>xOmX@lOretrE~OxfFehk}KYAjov`Mc17g9bdR!HE-mvWgeWYZ*)5N1&r=%?zGgOa z#4oEA(MkFB6a#rT3-0jdA7rBEXw;gED?j#4FxNBgX0m+(fOx;>^KC)_0ffK$_5WGI zRE0U;4I_LaCMtT<;0>sNmYqFvcvwEg>)OSrFYbks60~k^sXh1%E`nTdWRaiV;3zLw zJ2<_Dhl0ul(zz_lI+D z8eAN2gFgBD-&DHD%3>8Yo}Opu#mQD7B#qcvGOWeaCA~(uxay z8|^+8tiH_k@%w)5=Mr*qTY*V42wxaU(CS&G(ZuH>r!!xEM^S8 zE$up6ZX`y>>Uf0rGL6CFkU&%o;Ak2e0=j$Y`nErr;E593v{yxhwn@r{wh8O^ zD(r=@P0=;?ppI-j0Q8PHHy-RS;C1Glse;2qO;>k*FzVe$KOiL;Rz5Yspv4r+21>d9 zVLi=aMSJh*Vcb09ATTgcw=;|kgIq8UXcv0?_;DM3s(ULRJn8mxe=M?7pvS6d27nT+CO`b7}LMa`qYG} zil`^|TEY|?8W0fV(^SHrd)&;4?*6LG#h zT?Gf)PKss`x(cXzL=`bNg*!#woIp{}-hb+hB-`wwqj_(?5~0c*ua0nW0o+p}n+2zT zc!%v1&DA?!;SX_eemkbF!9h4sqQ9(-JSPC%bckP`?%la}j~(ch0hG6ZqsFY=m_%Y- zj{~%~`4VV#1>rH8jAVzJ=8nR*@GBV^!W7@*;yo)DZ2UEIfMxIiJbF_B)i!K@MuYV3 zUEtg0bkdY}RoS|t~$)9&U zGisvAO9Xg+i$*(GSU3ep61-pd+>57IRCW2RLQa`*W@#GF{}AZs8~i+s=RoILnec|p zME8Ay@j^Vm4Q2j5o?7iKY2-AGqr^XTVx)sJ1ymjtA2@wKA19H1UEokx1kUe5tsNg; zcLbJy`V>b1JVgdm{ARG^w!Dcdc45=Ct5Tr`;GR3lAfJN9OZ9-2S%Jc@H@qzPS)(O z0-Jw~=O1eQS`gKsZbn=!+Kc;&?U~F+L$Uva>(Jzk%vt#?R$eOFsD7(M|)RV{dX+%}X@dIwdR z!QCNN;{T)UEx@W;-*#V2QV@|Y0SQ4`Qo*m!cHMj-JIGWbh);)5pgQB|G)^K4oqVJotoxe}lZHFi6MtMDw`fp!n;&I%# zejN+<4igIqBKjNFu@=a{*@qa(%;U?NdZS>P=SnyN?XA=zi7%wps*0#At@-Hb?X>^$ z!O%-#^oE+r$)GH!E#M+~O$Q(9dLF18?a$7>2akU|za<5DCmS{S-1@uO?GH#n##wOP zBU4f~;NFWdYP*2s+68OIe%f{Ud5T|bHNpf`NQpkL2^wDXEx?JLPR#K5Qvk5d|1}f) z?}ocTwrq2JhmP8-Ei~(tc0xp*p1w{?l;TClGi4zPS|Hx71wp$(4DntL-?V0+M||0ts`k;5;4%lS1+eK0A2sVHo&Sg3pR|>jho5AHdo|=4zHxZ_I*6 zO*FrsNAyu@Q{g@ybyr)(FEYtfG!DPxgV?vGzsS+2i8CXM7XHiXF?lyIn^~7V?ak%X zoJ(;y&}o0SqsVzV(>&w5PyvG!gtlz(L^a;!`Kc&$?b?zA&?lY<{%?0aJe-+;iDTH=*-cK^0m5nfe6P=bDZ#iF;eTtTKmA7I#UZ6^?BlsnE$h#k zIPC7s#__?PH$TJs2_N+G);-336e_!uI@2L&#fTA<6EQgaxe(Eio;oc0_e;{tr)7R` z2Z+T~-&n-v1JDYp3tYp-+sC25p;JV6;ecWVF!k{NdV-!;Sxt@AInN8 z4sxOXh?n5uGJKP#gv1{O=?D8hxLAl?ndSg|#@)k-pxP_1j9hsJr)iNya1brsTO#DO zq!^fQBeb2c^dq2@9E(xAXV-M=CNq$HX%&Iw`}Zi9wWBVaJeuC3VZ~95*@ap0%`q;) z;`V!@ABggATVLgSgD5`?3X~K45OC>LX88O2TSYImxD4`K4V33Z#ELNMjZ$X=R$WGa z?mq8@sd|O2jqW~=*-BgH8~Qc8#HT}iP~UKBI9-+9VyZLVkhq+uCUxZ^Lc*8O&%@0f zse^E;K{!s8!UEhaWIrM{YlClK;@_pB>h>iNlU7wF0i-596O(^kok&|aZC{%ACHBwB z8ha?G38NZ3`{NCsPo1Y6j=$_#@xRQ!~O>lPM`R|*}YMG$Evq37|u39%KGmMAlg z%lRI(YnSH&I%vdGyKU#&fkJK<9o^wRcWYN!iN)ne=Zn}?@*i5SZ-!kY;B$nDvf&2w ze9)!F094S;N=sSbg78arWlAU2lND8nQL*&&l=34@hXWb#>zr=R@cmqAwxifO* z=%5xZXnt&?i<#HIuwe-uY5TYzKKu@ox~^IIOX(0M1J#{v&D6ptbt4;bM-8J>TzX@T zt$|*_f|ebGKo!zo#oBX7c6_1rj$+y>buc#wGfkz+Rfe|B0ujz9AthLD$u!kN3{m>g z*zDIdD=j)et$Yj~nAO4TU{@Y!Kc^J1TG&C-GK+{Lm%@-`o@Y+}c;wfgy?z~pu$ri} zguH^sU>jH+E6w~j3`=Q(3Mic6ROt?VG&1~_8Botbvp&%$hOOhO&%51yQ>YLu(Zk`q z-q8pmrU+hprW#z5h$FC;Idxr&q#}(?h=t= zN7$9RUj&R!W8!k`Vx9XpXc2rG^JicG#-QUC&m8ZV^)gq{6>0sTCa6m7(u|{Uc{?!*Ii}XuCx`c|hr9TcZ}3gg<}otFjx^aPYD$e(FoD8M z+otK3#bkBlBjp^7yJTeT(0y1+vK9nxVbQfzmp?&|L3JE1ei;vJnix=a7vzlv)eBHb zAXQmM0S$E1De)JtV--hK-=Fo7?4C5R{pl7+~)eYVZvbmiXOFHR2` zkN1bLEl~Kf4o{A8#2|4X%jzKU^wH#nrTJZvlrUcxA?N=*U=D6xRIx@!M_tBI5!!`otei)kH_7 zdFLD#6tl?5X1*Wi+0}Kug^pH?;&taA2kl7T_5hmZ(=pfrhvuv4D8~DR>d)tx)7}7L z^Q+ZlAaGu+dtcm=kXTZ=_8uGTk}7O}<#aX97rj77|F<6rs0#38LX5@X4l+omI%4c( zFDOAQ<@?TjP~AK}y*~l-dVT>`{R;hyUP0fV%8HD$encf-FW$d}bdfM0{Yj}boo|Ub zYW#=as^l%EmNb-NxbfKL*UCmvPL`>I0izcrr2%@bIeLpzjj8+>&u_1LrB!)4Bq7d= z6j!-ixR%7O^`JSeV-_v4N4%SgOWX-BUWy_Op7br-*6GZLa8Pe1Pa82Mc~+eAq(#eU zo+cpH4}_7ko2<^=Qa`1fPbQhKx@;`xwKm`Vvrl^TLvFty`#QcinK; zI>c-EwxDZDl6y^Y)Wy!yTG*H?Z`A^?9_7+1u~nL4&fd~ISpsUcLT<0(gSNkWVf&Y( zg5+>>@=0WCh1DLvD@tISo435BEgIx~JzeK@{@;red|Dpt+5 zH+wHc@L+298g4L&| zFg~$90Nk=k$FTd~pfvIxFE7PHi zt1{wYUV0~S-=2Qp4L=1{I4K5bE9$zn!9wl4aOv26LZAvPTQeA5=WCC}l= zQuEN~h6Fi{jS-9P@mpW!sNoiSyQ2nj?^7xM-I%D&H9@X!0>=`!FGc=nHl_k_vOJ1_}J|U$Rm@LZc zF8UdZpL<~9GM!2MM=J!Y9ygXse>G6Pd-Kma)4;@H&PivD7DG+rB&U`_+%8X_aPR6C z!DZ)oM#ofb7g;sgV`>HTxK7blmz{u&33Gom5) zgjmaX=L&sOYNqO?1KY_iO?}tOV^({k@zQNq1vzAH_wI8eotj#Q+F}cxhOlM&!~V!p zVj5iLy^}o=rrUq_IR2A8a$cZ}E)v$TsZuR~^cw z4cnaxaa|AH;D#`bTc~%Ay_tOn37tFBYE^8c_A66XG2)k=a3sphb+ z(zf@K1ytf+U5M24q52Ht_4ZkkICtQ^5$Lu6hhQV`9@iVEytjo(!TY zMk5CX)H5?JFSD)d&3-L%L z(nBK0GrAl@L{W?N?2BEO!{eGIJ}?6@?@-=yhergdtT>#WeQ1qMyicLXHV4aN5rKvy zWN@GPz+Sw{VxkXgs#F@lzcp1IL{;Z@w(Lc{Dks{v$v()?o?5&1?`gtU1mjT}zWrr` zjxJNhE3U}anVsFzLe0UiFJ=EVP%GVW`sbhtoFo6A2TfihoKbKpI7%>X2Sq)L%b6vP z_{G;h){WT^q~Lu)d=|;zbwd^)1)nT`LvWDIi|gs6+RvclqnHCrWYvtq@Wm04s3irr z{jxB^2pp;0em}_om?b!bV$<@6l&}>)+;7Ik1}9V&vDBaZmWdYsq)vxEC#ZGecGmfg zwZHNQK{+sEYXk|Wl9!09uTL-3ul=s%go~dS{Cp&!I^OnKOcU#tLyS< z61-}>j`hph!kuqN5{QTJ2_@8#>OK%>{OsdAlfAOn_vmsCQ>N0Q>F{g|KqNmu@D}4^ z$QnNUWI1tfxQ4gM^YpIUPOIM?D(U0xxmTC-w68?BF$RWmht`9bO%8{Ze~BED4JhZ_ z<#T#WST!wke1IZyY9;ndcIRjkb-y=U%J8X3Qy&|fyI2M)RkIZEsGWp797q{L=zjVq zgv2YQlo%5IF!sh#!#^C#C`fecdAQmMy~k}@s@;;}MJ)*3!#>Eh^1KYt>0KKa*z+Rw z?y~b2a<8Bh3f4p%EVQwbMn1u_1RFEh|HpI?-G;{H?3JPc!TKJmnk%0%B+b zaBA8X_Q!Io$mRx$%vDC`bdW-ui^U?m7CBOax?Z~4^PX-r#re8YNXmaSD!XpmuV3Gb z+ua08+*=^2KyI^vwUPUU+OIFYFJErj_eJg<&l@VH$Q&N;7%Ij?K%RPGs5%D z!lv%2z^e;aKBY8#~QyRe+v@`P)cZ{Gg##e*s?kxMr^#;Na%eR5kCJ88`_`Cg>0 z_9%^ zZL!n)lXUsYi^z0?>9g`E^yst{X1H)nF>Gc1Y~_tAlHBR&{b^+}Sr`2&Wf-c?YN*pY zWNzN2o_C$C*a(+mNGXfML{3&Hbyag)5h=N_B8Y=qkNnsT8VttC(o*{AKvyQZzu+v= z{!DK%GIEJgntI^>vi&Q7qz37iy8=1c(#oS>k>&3|pgQJ#&CVUUe_k}xRIaY6bXg-` zdhUu)bZw}z0goPp8I*82Z=2vfyrI*S>8hz7?@Bm`wQ{mQmTAxG_#728%&cS02kDL! zQ7>eLNFa?9xWN`(a?G2=sVRjx1gU7~GN%-wK4RYG#9PqL!wOdozqT|xFf228kIF*V z?+Le?nCRh3vW!l@BjdqqlvWS;B$580rxYdu(JjFE@Sh($v2ajnW~$^An~wS){Z|cF zS{ZBAQJky44ok>?_5bwSGL@rhZ3Rip-p>~X!U)L8R`AH>i)chOUnX9X>`3+HB56{T z^Kfh7Sz@bh1^=_308D4!xB7Q~rpp~tpLc0%!!~}G|M43gaW^RFaOx_3{r55|p+G-x zT0b-V?%;?))E@)D`Ly1w~a=8%88AVqz)qyiezT>KB z;+fpLo1(SJW~uKOkb@Q9VD=vN_etE!{mUtRrR=d1l7ch;5#DijaVT$eYKot(WsKY- zH`VoNw|&`{dq=T2Qi;?-=e6hH!KlkS63x@6YJcBt9#Hdz=Dwzka;f98Rpn;;PyJ6n z+>X9^B8a+G@;B6LNE|G}u8OA2T&|`_1r^&fC=$bkiHPrlT`YT zzR>t7n~~n`*fX+Lz=0S@U(;^0PPz2)*E{xEc?lRT8m zoK8mvuvZ96^0*pe!odmd9zlOb`|kbxbfdnrTL6{<_@Hpp4{e+=1sSE(lzW%t)}f1C z3iENYKmgv4S0w_totRFk&*7&}ej8J=kWps;WchyG8OO<^M+^1a*GFCVASJvE8P05e z?qZjt$n1RSu}i|qB+eHDp{y&xoRAf2&~l>WVh0n*>r_q%S4SY^L5^IDiP&#L3JeR~ zyx1EvX(gX-Kmua>)2)YQ%{L7`^geWIx(?xGKxxdNbqfR%GTon8Ei77*_>wM8=7nT? ztjz)6Z}`;zIXOGh4?eq%PqlI&qM3ri%mmjeDmuES6t?Wnb9nyiR!nT+pdL@gxCje#OHQ zi*cT#nS+b*I_@+!aAeGT3SIc7RwR@Vf77{5zH)oQBxb=dYdmSvR^+(F>paWt2yIf!O$A@l{iP*Y0J*KNr0T%6w zFBxDY#4(Z<$e~1_bu?yg-&1h?=RGoMpnKm;_26lmswK1=)qq5CeUz^3ukUYPO?wKw zjbLwMTzpK2T|-iX^lnwwMeSq?jv57Qd6tT_<_!7S3&zU9SROVgJwDf$04+sdi||M^x4B2(=2m?c+pmHiq%90F9WppL=y7SeuoZvIcEm_W z*K~0{RPXNWTvKe%3-bAnX0WlbHyhC}VPa~a;!$#jT#&vLf;QjI)KURl1J>7(4}z69=Odz` z{%|NNC>**zUP}b{>{!EvjnwK2RwHk9BWmcvQ;QS3r_9 zWMK)1E;)cZjU`{6J$r0Ola($w*HDjknB8(0GZQwZZ#T`9Ggwuu@hj0#GC8{0lhR$3t zkz!!+*U3%SWyqUNOcbzARU4~!SXBxaMn<GZ^Nw_H^1B=R&I<$v)P&TXSTB%gUo zA+ANe5q=)#kCvkS0N;3WJO0uKkvT917a&y|Gdbt>EndFsjr^roBM!b?^PCNr^|Xj{ z0=PwX3)02QBd3GveK9~W9cBkr^x%8wVWNfKBO=nMFpEL%yY4Nyo+l1Xs(i1>PC=ux zcXoDkbByWPv;p=n5Ee-p8F_ML336{ZOHSHEyJbJXT zfVQ05VH*hv!3%d=;(Va(cC@*%7tFBd1bDl#izDdW7Yi=ml0BkZPdf2v(Z%3=+iLTR z!)T$#uh!Pc$jG;Iq{2v?dD6G=9TO#GWl=Z+;{pGJkB85ngE+gkj0vlGq(pEvzeqCp zZzrjVTNmQVGW0wm3wP#ER@Q$AorDUwaPh~wXJpn0v3^0*)3bp3L7}fN*WHGz)YWx; zsyyg{c(^z?zf1Y}jJ;KAW(xjHPV4y#^VY4p$T*~IO-;!0miGy4jO)^Kw_`Fbp~F{0 zqr0O+KXRlqs!oL7A5Y)Y;_=LV;!@EEAAg2XW1Eb8QARlKxplv3npX^`B+Lup-nSFY zRb4(9e=H_8T~HxwXIF+YSCW+UM+$3iId(%=tJyYr1-jtkJ{-fZq+!W3ZsM8lc&yzzC^OhYPeqK`uhSu}%Wh)o)^ zTD3N%*l>(@m3NWe8^ihFdS{xCNse}2%gOBv!82Sh& z9}9mex&Muuk+Jf2@6*CG{!sVK%oDXVF>uW9oC9&Ws|(QyQ-$POn~iC`;^#l*BO^mh zrV=Gu0(b#+=3=w*CXtkMZz7hRO3()5BR~y#P5K|)zC-aVFXbsLC}_^chD%FU<~aR( zU>Jj>%{&+$waVKUA@0J}AtWS{9Mc+`{?ZgkH+8nV9o;=0bCII%WF;=#Yk&l&1~(YSCz`Ve!a7%Fb0(^o<(F@m8vSf z)JYli%f}H2XJrsL1esvxkhDaHC(C~eyMck^(*Y?%@#Amy9_<~`9SNdIm<9&bysTev zd3hg!ezv<6hv$nICF|oY6Tzf=y@S~ci;LGHqN7`0GpF2qrV8Mjh6{ehV)dpu5_T-1 zGmF8@OpOHKe7Ca~Iy+eo0=f}!Xw=O9=raNK<;Nh`ZfSX;L{K137639pZ^EiyxF=KV z2-A9v?LB7k)&KLSIklrU&jo zJXDCN-}N;kB#PE*1lWr^8c-r6fO>0V68C~9~?YOt=12Jl%Zz6 zj$`*&8KCZ=-DYM`S;jA|zmU-z0$3Rg%wkL9ZTHl%PNDwAQPu47ejJW4)YHvXDmqePU^J>@2_J!wqau5jMkXzf|Gi!~^6?Bq6$`aNWWU(1j_N7uHP1Mmy3@{7*S+ z1Jw#ur@Oo6Th2WiI36#I_lYyJaD?f`X}|b1wYej)zSNT>3b&n~A2=T?#GfZ+Z-WyR zoK+X~AkzY|jP(7`H7z6byC%A6p?&6Ua1EWRFAZEymH}*U_@+G^7nj3#M5A|PH^FTV zKS=V%TVO%wRPw~sOO6V_2*{UguR_?Hy4gcQ=HNBN(QGz}BCcg>xFi0vwM&3upWKo! zpy+)<9J*2aB|Q9y@?OG?`9!f;E^YI+KE8(P=_4}4;y%j08$g*;eauUxoS~oUib*2i zT%D6sZ2rifrw8cQ($e2ivz?@R79+GL5KVOuY#~j#ulnU3_0T zfD2Ph{C(U{ZNcN7BJ60;`SlUx96}$0xq;D2vCfXRj8AqKm&PTsQpvru&CiYU zc2L=Eo5dcEuNTYERf|c$yRdX@YRL|R%n*0K4=4Fy$RI7WqG0SBJKqYH-rDkmT{Ci7qA1h|AtfpQYIZc#GPQ*h(K z1L4G^O`6WmKc&f39OtOU zz`6rfdU#_hB9>!uB7>ek=Ii!!t>evdlWR>)H1p?V#cZg!n#XW6sZnec*L^E z?87D#Vat4FKB~A8DJM_kO;67Ow~ct@?zMlUtsH|go*lu#!i3S*v)do0D z<+mKCc2wVKdk}`B%aEVJUGL4+Fq#yeA}b^F}2PnxRY#>+%e_rqDP1AAI( z>Yf?UYWeeV6vx&RY7LdC~3p7 zmZ~G;!-vDiE&69p%>6&KjC)rnd}DiHJ<&;zrEIwmTLN6SYL-{Xsk&X$)3pgnB%3Cz zqp!BSqtDiVEP!6!xN=T}sF%n%5pxu;IZ2J8*z0Mqbg$YdUje7MpwX6=@&>V z>}CkR3;xYpSleB0yN7eWw+Wrbjr@`wNt$#J_r@ATW5?8~u3hTo>r9Q3H)bUOwE*R) zO+N2p)c}`8#XaDI{1uL#6tq<%HC~>UlzblNL4ohx@MUCW!F;NYEqi$tvPMIASR{kg zsEl*)q4-<)gnJP_W+vD1k7HgS>^(zl;rYPvxYX6 zeHa_;S&Ruq@xJ^Z(@zTP;DUM`6rL4o%xkc{Ozw-Sn#!%ll0<-7n=hg|qXo;6_EnskL`?GzJ zS9$M$3z!&K>No)MM~ThciyVa%5UMn$+W<|H1tV;ud3yz0bokx&O)kN#F8d*QfsqxI zu@obeg52RCZ(Cya@EPk3_RKM1S5d^$$<-{KeAj0E*|nA7h9Xy`Y!JYxS9IbvT^G$; z>&#C|Td~GbRXZ^ipOu@7@UF4Iv(Eakwx(%w|NiXXs0d#W0tsBB_3Iz%wrr+2g==jU z9@EkN6IKWd`a=JJsm)WIOZYcJ1nQ-5IyzcVWPZAV85J8VsjZ!iinFJumqNRMo|5kH zp^&_cj9t}ZSvk4F-MF35$mxPnZA(41gI1S|(OEXi#>f7unK7T!LUnYgGcyO?6<4+W z{O=%%zd6_14Bo`XiNRr}fDwP+U3sSd!;)$GhBI`%gI#jE4Djopc8d)B)Y!Uc(ktC8 zrRnW2o1s_Gvh&L(cEBm|c!S&aX1ok78H1{K5G<1NUVA&N|6<&0l)u@vK@!_3Y%0-^Ljh8*jr~QDQZ0lU? zo>-O5KC{-W3il);v@UvKmJ34B0)Aj=ol{j#A+Xn`Q&x87dsNI~M$9a0+1bI6VOu7- z_&56Rv7s@Yy#U>UTurI(n3XVv5p87ZeHtbyVAkxV`Zy_}=~u|KesvxV)deT3%wHPL zqkL2f+P&xRfG{HRzi6sC352F1K6ru>5TMbU;P`j6<*f|S`<|zdTCa}0K}2WZ21*ag zd=Mxp_X{BEujTucB21VZ@vQx1nFz{Z;fAP+Dam2aBZ$~9wO_u604#Jz$1jm{E=XAd zx#1A;IH#r~(f*nA-Xuccd2XuAvm}0%e+d3dGUQ`zhKIMmWnhpD6X)Vw;$!Vd&rBB+TP?5w*! zhHtW3&*3~yYY2VV!ZRxO(fITZQiMwr^N1(8YVBt1<><^sX_`o^>-QBbfMiQy=G)y- z(Z3R~NbkD0*KnX;S!}NMDhwNn1g^5CDraA|LvV(x(_gOJ%h~p65tv zE_~Q(RM+^A*|*LtLi;*>8ylBnzIe0B)3$&`4Jw+zBhF7}MGI(&c@nzAJxXwj=-6hw z-Fggb>9CwPCyFf!dWIg*hss3bu{U0xn#;(@U}0m=l_F^}q4~kAH!(5o!Kut1m3N!D!7$JcBvUEhZwYXc}Qtv>dy!U;PPzt zrBuZ-vbKi*XSr_20~!n6$w6Qcdb|6M1WqhR_O>qp%juRk)E@+4$E6z+0iw*vIwt1e zTe5O^P}u5ZHZGm#**z%jOV9oe6#lg!e)W1k>-DehM2Yoi#liKw+(6I=$cT=DV#5NH zteMVh(O4G~GtjBr2jL7wjDe&vB-Ff5f>`3rTq}KvI=)!&YhsSRTuOhWD4SZmCcf35 zjhMzk{q4KP5%2gFjLcC*8L|G5fo6E}`X;+1+42!zYSxUSpqA*N-ZtC1?kn%T)1Ez7 zb_5RkaGS)QN;YygY8`#Zw9c>px5+>rVPhAXwqPo^cI8ougLy}$AT)2XB) zvFMH>`fW^Xj3l4>m48C_*>~0=kkjB{9+kdW+$en3u?gbZvtees4g(Sx3*fW~M7>02 zRE9&;hb%$?!<*O)0nySF^7*CNPc)dztrVo++!eQ;)-bP%`1U}-MCa*+uD~sD%#uGu zwec;our@ob+|B+kLWnXr^@77M6csj#q+wW24`>ICuEN*RCB3QgP^MV@InOO%SJ2m_ zkkIp#=;#wy2Ki%Sm*+%(?FtIdpF_1z|D)WylSPz}+XJe7yrvxg);{svkRxPoB@?Q9 zelVv!bUL(vviKnL2#DruhS&H*NqrzQ2MQ;Kj3y75ZsRqU$F{*hO(I`u1VT2XVec&- zxz*K45$A%gMNMD*S6(lxp;AI2x2XWV`SFaG86`rs_VH zd2P=Dh1$TCup5`>1mURM3RcRdy%XIUg|9s*Fb>_*%ttAfCY<8v;tPM*idu{{;|mXF zXK44SsUHpKOg5V@P7JInx4kGZ2SRQhvT$#E)V(u!oCjA*?FC~L=1zs_+b6=fC7KB-h z;ZuFvkJDq(XiSCj_n>?umpysl(fB<18MjmnM8*LvVo7BG?x>JG2=NO9S$-@a(yuvO zJO@NJvy7K~8VtdMX0dGZI+ZX!`T8-I9eZs!Xf{<8TWCw98D8x($VL=YeW04Li3neL zq#WpaZks3$d~6`+WPg7ZqTwL36`{biw7LO3WWkb76T9iSDK{I$eD5x?kuKDxfkOpUC!;PxxFhG5RO&UyGf+WdlS~H z8sSkMwK$?)RW-D-#{Bq(n!53yQkuRE8z(lc5kS{vx2!hKs@;}5TIZ?IyG{_ zpS{a=aH~pvvX{7uS-}{As(q#!^DlF1evcmJgzjQdrKrr8hR85*XP=||(ag^5#Sy~L za0qt}W`=}mqt^@VDn>?c8bWYRedVc5nt06EqEVD6%#M0bza+zm$%*E&Ie0BPfhw+z zWnL@`e_vBqCSzIYRhTivh&Fga>Eb)Jj*wMr4wLrHb{pkF{W1OME!=j{pDe^CN`AgD zoCjrR9*rA!cyI9GsKT>s4$S^#Cmv1xz?AyH^!01J+O3ZufbQo%_{8jVJ$B9Pbx7ja z)^7={zHowg(51P6pcH|=advc_>ni}(LGXktb#Mc-*hv;yos+C*_VcpN%@ z`;2CaJEFe#){362?hYHZkT}J6)jAY3xz$0gjnPbJE~gE`*wGT=IbiWJnX)F#WzT!a zvcVVaNHeb_jQGPx(?__u!&T5rPI6|#SmRK=T(djSV)2F-Lcq0Nf8t)e zKo@Vf{7f}5o<8T#c9nu{+N3W}Y=O7&A1wlSXCW;DDp-y;5ewN_5ohOn>cGkP7eEbV zaQ};OAQO}2Kw8N{ZjKT#)Zh2I6lyg`DS`hN)JMq3lsjH+;A|;DI0jiCQY3BC#QYAo z6oIeNZ^nh;t@HHjeLw79AoS$NE!@BSX?McQ{g(uUj$8Gwrrp@)d@+ffP8R)AA^z0u z?pXSbYwTJ+Z2|vI+chl{sSTWhYD~)7`|Omwc6h;E2=$cyfpWi0hx=%8kB9Lx2*LcR|bLN{8tCpKa71By9TDu{t>qV+Mu-z>B`;~f9`Z%*j9`it#) z;Vz-b!J3f#;+d`EI1GkZ*Hq^4*eK$SMgg_p@e%8hI$T{s(@Eeb_aDxCh(P!|+By*? zl^W!!MW~g&i_L!i$A-34ry0xt*K^JB_X*If@aOam`HAERlZ}+*Yvf7eCh&LI_U1|q zGwiZG$eSzUDS|v>+LgP0sX9&lyUJOQ?Cz7o#68GB93v?3rFVc$cYie(f=Q z^cjFG-?Cge+uM=<2msIn=6+(dJLBRD&JZk@yRtG~r$OLkQL;4?&LDuQW6k}DaXpcb z0m3uZe#gvuU0^{8rttdMz(|Im+B2&RW<{nChZexw`xQve3xv==|7A(T9zrZB2O?zl zM=$+%bG7ReRmgjkxSu{PRY3h#@X=oS9VHh9cW@#^uTy~0di(Za>@KwC(?$AfY41&1 zSN~Wrt}6X--2Y}C*ZH!bb9BR!Oqtoi@W5hA^`ioSgC{08)&_q6TIT?cqqtVDS`o)h zbSk@+NU(tA9@&xY$pf_&wS#`Ay&UTPJlv^nB9V8N7qg-?W+U$^ujuVWKmY}&ap^NSqz zCE?AbUOcv@nNL!)pRk5OGXn7SyV}1!6ivyvx(Ela7p5zKbGRR47?2b{YzZo$n2;#tSuSDA(vZZggr&0gCNz^B{GPllY5|3DnzxClWU z769VFBw*T&)e5XV(#-%6!5EUp-l+@BLi^R73_TaGz(60!{tFp}Os+0(rnxdf&(M2> zlll{}tbZ+}vSa*|(*3RuyH*=Mj~?pTR^0&3k+%lt0LWD;n+ zO~w4f&YGLP{PR^QLziZinBH@frw)gn4%=g>Dl+a<<(BGjqj~8DmFFwQg8cBas>jYCop#jSt^mYg2y}MvcQBp7_JaaMEzTI?{iK;gL1D z1|zm8cZjq`dg>G#Htn4$r0Np|BNdg}hD4coB*%PwT&zn}K1xI-OXlC>auhf&U;cAk zZZ3ZUt$2VpU3wpGiiqT&ZeCrPGtph6WAV|flDT)QIqD`ZM@6vZ2>(N~K(Yt&!^4Mg z*^0`3KYtQG5;LXMsnOAFy@_MqniiTj*rmUx%_a~k-xB?v`NZ6mXu6~Ag}+u);Ne{Q(eiVy=cdx_dbVt*11)LiEe)s z^IYfVv;LhGYn=KPEq;|pgm5mn_<7BdN{C)fHuxg@q4QNlFK_RgR~Chm51Cj`zpy>o zrJU*i?z;EnRBL819)dC%RM#A;+++jqM;Oq@$LX%l{*lnW6^-%hxrL5yT#MvWbE^Sd%AH&KtEwGS z)_-~tjLI|70%%*&c!R_o=Hkc$qA6l@YTFR0!KiS(nn!4A58pwwAVSw(^pk<2?gVG1 z+}d8S?uK^m%8;YvzNJ!WeWB^y;Zd|>!gS+Ff9(eB9mr@dHYW0ugdKG zwmQrfk<0}wP1CvAX|4!Y%e2+dPi?!DN>5Q8_4s86p{u;!q{V89OK%i(Qu7ZZc;o&OW=zIA;rDxa(#&UmY_t+1s@(#vL%;ll& z7O~c6v(?S-|G1zLIeM2$mS=~L>~;-X&sU@l2d>q-FT6|4Frui z0>Lq_q2b4~8dr)w4~WKixcVdQqR?4;<{g{2>*xF5(xhI?bSCBss(fD#WsiQRzR3}1 zwz^#U&a^|qOqzWaZ?CK>?_tGriH_0Oppg^PHhbp+d3)KuwR1e;lXtEu>83diJU4KT z-F7Aax?>QFOr2Ns+6t2XUIPKETnLQ;{@%xq?(k0rbp*(h8$Dufr4W`J*@^sa4fiz<2AaO?JMXMm0)F~4>|TI1ws?xg$;6+O7%V1F@w z7ubeW!}hz_+q!62rSof18_;*>31d~4K~qzsLnEB54_~Q>i51VVKJ8<98gvr&qvJK( z&Rf{V*oT3S3{F2BH&0j}*lL0}dYU478F=q~>EL-S8CrI+yWz~sU%x__LdOdkF0q&} zXtH=fRU9uqhXCL#!X5l2=dHL2+TWGyJOyroy7-h&bgpq(om_R`x@l ziQ(EgCUCaoT}%&q|E`)tDUep+^g5}ht^BUV&!0ptLL3_BMvZ%SsvaFZTvSzlFa+@T zGPF-km()H}a5qBbeyaR;ySMKLdRX7{b!C3eO044|*Ro z%U>F%$5q=dv8-<}drv!g$FXr>K0fe{lhKN+F6;~J4|}H z*q5;k4FP?mQW*TzS$&ORijv}0#E#Rx4gA?1Pl%1MSKBUY4i^PQ=dHdR1X}*u=RvO+ zMqAcjzI_iu+cIz1-U1*D$|1))QR=P6dJ^`|&o>%HwW1xCF^ml=Y>4I z&&gvybZ>JfiMK9>={k-^Aa*C}IcLhUXAQq#GbhS*nY%# zp7PWRYX(=($MIjiAJ^Ey0ce9eO;4oi@?`~H-d*x+GS_d3P*G`B^bl_=PHz`0aSl;J z9g>}n_UF$YErf;U)F5H22&ho8vrdZT+(`?ITUI&9LNImj zeKJKgZ0IzN%kw3zR7;O+g#$4A5&wnPlL}7g_Z*2tRja5beLGBw^OuRmTdZ>WtgNhw z`8V9w8%(}f|CjCh-$adz_Wt^g2l`^|+%3-AD;ua8M2wGt@94gebNBb&7E-bqDeaJC|z0PfB)-Wq9z#oDD`-{g2dN1Eqv06uYO{5op{<4?MK& zV*|sj%nrkVu@hB{;SV%@AKKUKfMtfXdhh60IUFBMPF?Mbog2gup<0W1-FWmOyaa(5 zZDslAhS!2FfzDjz)uB>L?ME0WXXi@H1sd#bj^SVU*2=q*bm47tWj9nN{epgs}W%mo>puUn5R*3h`4;B^aG0n5UzLjy}m#j@36e&wvg=4GccLUg3$Y zlUs&OaQ}Oyb}o4C=49vgFX;ydx29)g+`4hYmc6_{%w@#br0_eHkRf365Qk0&Xt?mF zV12-N4@J)Avfa}8P39{!?K`2B z7#ZolmFQsuHh+K@=H?dl{{4_sVO9Zf%PfHT z4`47F1gJ<$0FXpUL4h(JQ;7kj7VI|HVUc)~o}Rl9Et$1jZq+XWvO*Un+m(*GiJF}b zelHlj%*&BcQ9rAf)GEm9A|#jLp8ZK#qwpzj&L?FTv=&;ES2xy)@2|GJ%rPlVG0?`8 zy#g;f{cKOa1#PKW!#r^nFMwA(9F`crNE?+CvTlQL4Ig;dVanh9>%27sQ_Ub9g{lL9#}czog0p3^w3U>VOPZU_Y=NG5?ebr3d$rL0!zI6)yMAKs+uL-lpc=2-4RD$hJG%aB zaDC}gQD0Dq;tSLoZ&OqBOFxRQv*lRxxYc%8UQp$djfF*^xr58FXF|R9W_??k-3*MG z*?zh_kbIku*(M|a6dv)D?R9WGqUJ(GFl28i`&D#sb!_;rJ9X!uWTJ6{7um&~Hp6Jn zuWxlmYI#eO8IE@&n!lEI&HtfHUH(%8j9O1TyNQcSDJQRp$S|ELv0g_*Y@jwKQ;xW3J^~D@OjT;xdz*q~?>QjkE2i!`9cX zF^vy0+^j#;!o0V=yh4-1b!{Pz<;awKa!Sl+Qt5GUrlSWYA&-5^%K39(UsoS1Bb?hZ=6OTNi!K!JRfHFWbG^v}z!x;S6yEbnFr44dsajGi1u=MEpPG=3cg!-k8* z`q$>3vM+m%Gh-YX1>has*x(gWG+2Hr40Z!xi+YCmZ#05)DI*L=q zElA}RYhL?}kIjY}aidCMI}M`2p{vlAF7uSWYx`maE@nr1a!AK#4!vWfV41Fw37yrJ ztb&78kB4ub%LT8yr+@}oQ!p!?AeU$KJfPEN^T1Dr`m|09dIlHHXq@@6EB&=7L`} zQ_y>A7YU!$aVIWPC41m8oHF{Ob@j#4j&k7{XakAbvV8Fe-o37*4I_8_47%g8@u+=U zT%kW9AE*BDo?L4V9+}#{yj#zLY+$AkSb9FQNOk)KcE@ek2^5KE^q{FLpXw&guJ>fA zq>;t)V%M$P)y`a^IW-RYAY-E}kJ8P->5+lQVmkQCQe+5|@$i>&K;3NLj?L5`j6CM3 zO-{RKv|M0LDlFqw#pv&Sxotm?z~?ap*-8DFe)cD5<*4*;=@T>-WG(1jK-XIxW|D&B z?A|w4b}=`mM<8%5cWrYGJ@A&?FXb-m;U>mgcTdVU)+ik&S5tRCl~ZMRv~_peAyaP4 zxvwU9wZK5F$|VArZp*d?t8C+}AwbbhJNR}2OoU@7#|ekaxz6Ia-fov|f=^{DmQE zI`%JKxpVl|zfMTeX~9CxPIgQJK4ouL%~DmNJf>G^+>hR-cUK#)T#gEuEvMphtHdDA z1zCH%eNf45xT(G5HkpVE-Ta1G*%gzx@M2SC*ZM8gKEOJybYR_FR*m*Kh?Z6BQ72Nb1_p5k6FEeObA*rL0OybAcUdw)DrSFWuimMVWE^nm_EF82Bo< z-wdn~YuVgw$XCw{B#pK0+H6{O@w6V0(uLB%A&h78R6H!FA`6WQ$qC3k4VbY5`ZlDH zO7n5%b%H&ibrc9zW|b4$;wO-!c;e+kP3prAXFPe9-N~fCe4^CE?-A_}@p5wlt^ig# zvw5TIsTS@>0(k}S&zgl65Kijb)@amcdOWJ7&hJsDRSFU{H!oYxbhb-{z}j+3xju5`5LQTdFIQzjp8#QT@%ke5YgG-47L|= za4IX}yn>O4^OgJN1vqCg1;yJN8i2xbe2DSk?V3@TXB7b_wqq9D&2DQRy@)N@}rR(0CSKB%C4krsVLiuP_l8!(BJRUdNH&f zKM1~ZO*2f5RE0x>5kGw&CGn+7^$CSqQt3;TB0$O!y z>+VL^cR>xt2N;Ttsz+K4hYx z=!z>wq_uzhmF=gqx(g_h5FHI2($GW{y6cj>EQ!b@hv%@5IOJ0e7 z96Zs1@vf_7AH5^&PFIAnB)_{mW>MuLOsqx%jABlZPYS2vVFZpts4ny*z`v`PYT=&- zp!-FEnhS{hd`A)ir>?513#1yJ5|vs2B?Y8+M+}Yx&s|dJRvOjl^Z3(z2b`&22v)#o zN5Fjp;iRgm7@Z5^tySVOR3{eAY5*ZWc<%y0>)on+&l*>$e|x1z$xO-DPs zvfFMehh?gecD?9l%=PwWCd_T8Mz7Oz0_e&I6DAS%TH}H5lO+o`i>pIs)C@0tt}U09 zU}Hqd_3TU%!aMHv5=e$t6fe(s*)EF(0Tuheb}kPj-AJ6hEL9`Zr5UZDnaN(q97p%d z-0Qqez>M(MMkitZq;_g<$?(Y?B@CebV6coMqtX=Im~o1-s)CY%R@dRf zv*QED_OW|IDNbl^GZO9k=vFphlDYNFcAuTQog-myx#)A1;#~M3-RBQZE*>T-Dv?|- z2+3i7(pjgYu{(hZHBP#(r5_T~rY*BB>R|;7Uv_cvXhdGUw%}an+XpJKs_XZ7+2~n% z;{C150M>95mB1%)jqfZ4soZ|XUaWie(*X?C`Sd61-THEEfpIW1BKg@KwHR^8ovJnj zjdL=vMJVI5R)UG*>IMbm5ar0E@VDfw`qjX)AGsK_4J`PU$z#ch}S7zTKE1d zk`)}?c^b+3HW_9eN~_VRuKcOa1v`^ioca4_5QH{e;YIK^lbOPhMfHZ0EJCby4A;Kk z0?4#D8M_t)#Ik6|*Ih0S?*k?Y-}RtKHX?db(7?wad+40Y{WQ{}HEsobh%y11K3qCn zi>@!5<;hru(xwfc0Ff~07t2;JydJ(IO5Va`fjYf`WS4io zG=SA3DXJWFrgGqeJ23_YH40cKBv)ot*zMA~B$itZRGeLLDotH+4p7MNKx>)23RHLs zui)>mMDNJ=Br0SZ=V$xzVx~8UIqM!cdl1kK;`_6`hGx;b?Q_)_9sEd{U&Fc!DdYUB z@AIpQwVZ&%fk^ZhlaS!Qf4^HEtmQIRR9>N-0C=cBa%rsPajB^&tnT)oy}S57I>S$; zw4X?QN<$0Q&~7xE5D!%hgjNqyhZHy4=1O;!_ZLhrmFY|s2v~6VAf9Qk7oB-g0D^h@ zG%_PrrypY{eSHpEO+k}R)G{tt?&A~1vKr?RH#5L=qc?eMi1v1Ckc#$N8FR!}!O2lO zoQ#K=+>Vm=W)0EF=vmq_7~K4$x=n|Cn2nVws1Po9!OD*9IkH~7(?(bdS(Ph%?mqZ^ zdVM2j22KmYO7~0s@SUBJGU9ib6GA~|eH_hNaP_W>)yEu~kO;Vn&<*@CFP`CeV>yKLK8tP}Cs6^140-|H|&yLDhxq@IlNKQR34AFYW&4c@f&OS2990g45Baic7zVA5~(D#D(Tf$#)4 z$RGP`6g#QGDDM1ojk>RnfcLZAG&dO8Fv_D7mD4NX9sg7;^Aq9T$lusH?2*#w*dgA6 zoj@p6Po>}mHGqX#$kokqtFdlX5^ylI_L++g`@|8ZPHfU(n-;Ty9n;OG;D@Cd!xd90 z8NB6tf)9;Tnt^)BX6EgngBWAz@hdih%9^1uKb-N9Yk;yQLrP(tS zU89t4#mR`Z70(8*u7`=mNPP;1fhQt$zRi%5($dkbMP(OpH4=dOBZQsoy*l1xNA2xz z4M(3im{9^dT*A?-vfWqNa*&wO+>|j`EI&WCXndF(qYjF4GdyTvT0DEiWJWq?umF?_ z0(6mS<#6H6j3KgWZC#uAw#DH#wOsS+No*Olbn0H9@djuYNlC%x+liD#df9D?QH0q zC@o#F3+c%FozO+S(m49a?l;yHpL-p2I-x=0M0XIYzeL`s^U}>=WD-3-uJ1kUSpAH( zBvtRA_aaFjI=}rCx+fzwYva?grDTyxx@D0{9A#5j4q1#Q9b3#*3oui>oG%#i9~JZ3 zmEU|LKPM;^V;o~yA3R#L-Y51ZlOxZe*ZiQWz<4*?__yC|*-!+lh93v{aC4nNC3YhwFfx?(M^-hduXZ}&^y_vW{) zcXZe*-v@SQBQL-$Qj@WIhWmZ8T>*1mpLmgof3@)op>2DoZ}-yNS&EYO*uu-o+#7da z@@O=MaId=Fy>cFYWdAi`U z%)BfkCLj3zRgggy*{>VEJc5OpO!Z{99lC~5Ft`_J#ip#fkOQg({Jk7-Cg{9}vV>%V zdbw9a5++f_0_6yST(2|Z`mlx)CbW{5M@PogV)yWL2C6>2>B_bW_bZHN2AB;uIue|}gG=n)>OUDLap@vA?XLEV7wP*L{&@3wjPJu2)*<7`8+6_+4;(F>@k9}59p6r z>Pv&PWn#XpYd{E$isF7sqv-&`1r4aKWuAu`p&H-DZcdL!Y*)>s3FZySau--XBt2=E z`)K3z43<=O<8 ztXkr0og|*|r5VR>iB6z}4|{G9Uo3o;#G&x5j?~~1P?*1tgzIw`x72dgGk3n)xs7hY zEc$rhcI!L$NwgW13i1x&$^OkVViZPOs z$S$J!aat!2ZAKHgUtbpL3yv_Q(CxDYH#F`*(#@q*zJao!HwC9jAN4h%TLYNwJIRq+ zBPDh|BTd~eERZ(Tr28(2+Y|0T9=uwX;v-VVih-pOv`2JfWd?z^Ww*=MwiU;kaNT1U zXC?_|V02z!SL~3&Qu>62+opiqM6>B`^YZ4yq*!J|fsPn89I<^BEO!o&12^*%4O0sR z9Zb=aH)qt##{`ikD9s;eza-|hJPL|)r^iG$#K_vw>~ptKFZ8!P?T#IPqJxuZQ+@Oj zuu`&g6fC<%*83lrH`U`q1IwWMfOmL`G=*(cNIgVi2lf^`%qb)%%uoTuou{w{(}5JT zF`ulXkQFS;Z?m^tt`Bw-R4(1n?2uT7)ZFi+d%72oz0WWgi6DjZuCOt(BBn{RyJ3vr z7*rU_E3+vGzEnFwp0GrM-#0jE-DuYM9K^QaX2(1`8gTX0fi}-8@T4E`!K-J8`_Imh zj)ON>!{?6m-|UsIY;Tp4O^Q2062N6f(uJMGLnWITEpMXVM=vBUQUliVm(UC^y9d8fgsb6ugfadK6@ z@wDUI2>1uHjx*^;LySdc5gP-a1=|dlm7Vg-m2kg28YGBLJ9FCLk=%?Rs8(|FS7aA=UNaIDMZoXRli{&t-fc`M# zCms8_J1LXPjpipnhUyW2$59OUl|{xb#`!P+--h@9-9;lu@FPFHWa5H`Zhm4@tIUbFVo1-@k zjxh*_P#c=7rS%K+w}vs;ie~94qO`5NDl7CNT0DNNnl%Jwa5)7QBlpmEERbgX zS|HP-^pj-J;E%E4%^o$fGxUjW{A9b|@YleW&bqZ9yK+=Pk;4-XF__M-&54V9{lF7$ z?P2%S#Ykjwk7ht`E4gmf#(_I(3ghdM zBWm|G%%iZXDa@B{ouRmNE|mQ3!e3qKQZ{`J>(W~$nfUH~r92`!Jf`{V?6O+|9e?!k z#HXLcTrRV@p8F~={!o{9YYcx}QKhppMMd7W+Sl`isA{zvLiSdl?v z=|fF%^(B9sV*}*_ut({{;0mfHuM6Q-_|0)u=|%*RApjgBEe#>2N*`##OH4p}0&oV+$QP}6Ho=Ik_W zoHL%#OagXyQn&T#n{aSIi`&Prr0lehh4k_v<m?=PkgDSv@97-VhJ|c5PBnNUB`}(|mLuU9Y93ddrwj zS9IQW9V{343GZ1g5hKsbZ}_oY?c)7qCnpc`3IsW;qoJlypo8HQTj`CRNlsW#b#@EQ z?Ub*g0v7D<3tIB;r=}&mx=%LzzX^hi-z3tRc#QHHM5a(Z#Hz*yk1ow$Z#Bi61ly|v zdrfl2-@^$=30_47I@#L8Chz>bYGiUWB_pt@5`b$Nsoa+p8Lrg()&PPXMA&s5rjg2# zp-g0uTEE5&mnH}*1aqDTwBj#(yo;Mh*SD87^#rn)wCAVCH!7`)DwuS&v*;Yw&lw=V z`!iSYPp{n6ZLz{iJgoO@cd9C>e|Ai_+me&lDpn#jUTzT@5gPniBqe@w2?I7as<5hb z2r`YkZIinV8_=()-NYm>7>@=js+eiCFS|GXSj8mcM3yrDnvTH3otHodB;0w>#Heyo ze{D))KHwOA6m}Sf!~h^HD*1T&?2~A5_m#DX3ahrGJ52FYd%M+!&N`>Vdi=-B!Zz?{ z24Qv23I~-Mao0q8W#8vBI+#gz%<5PW15}s~G8r$1@qoj!&MfVtGfC6l9BrAvN34OI zMyL?Xbm5ZbsKYeI&UDqPVGK+v@Ljcc+vw2I7&Rl09=d039QRPHD_+&%jSuWcvUxa- z!URy4@)Y_3r`jM5YJWj$EbyP%@CYvTAMv(z>3?Hr?g_Q&KG1-Tt7s&~g^u7;*Ap;S z9wX=!9X>fZ0|)4`tJ?4wh`G+gDhsL~`z~fAv(8s5hnMx$+W*Ma@HbpQwW6_SlEAaH z`hazARf|t11>y3%!q9aiU|9TzJo2a+85ois`CeDyHYMGEKuTHc0BB93$sX^s;pefW zdD`{7bEG2NjsMqh*m=zG_A9DqyE9WAo74hayioUo?Nouiz(db1{j(YrGk?bv-C`Xc z{hv(7zvsi}UfwkzV05Vk2*M#%!4Icq;C2K`J4)b4e0mCX@)eS>a9LmcN9Lz1@a)U{ zH|FL?sc-!>AnmA;xg}%Jk4fi4Jevo6?iiBzJVPZLC)+Pd{f}Px_52BdvHR=#m49pg zT@~$FiaVTdNDfS=K(W*7WzyKkcY1fj66h&jUQ&78{WPYAmWG!mwSTFGb}jDz5$lo1 zzDu$T|KPP#W3M)BfRy?s-&YFYY)1glMwF(i?vGrh*?9oejL)pSZ(2ZoA#yfQffK{r z+4G;_YyaJ#SOXS6=YQ`N`Kv>~gJLv*E%@j9`~j$f{m%;u;e5cq{O?y+c>($V`%;Yh z|Ms=Mc_6Gx44?68hB$k8m;gbhXV0Fk<&DZHg_6Y;y}qt{Yn}q<|0Ae1&@}Uh3;4nV&x}eSpDOH<6V@4wbct@FZoOzOJCBG3-Lv z*OzCpIYE0`MHLdS;%)E#N9eB(fE8qPa3tLEd$_wiwTWwEgVdg(Wa@dgZ_Cb++(Y^M3+S%H}Ltlj;s$#J3Rs zk2|Kt&vjk_@dEhDe=cj)pK`?pm=C^Wx=)X~Xsv0ZK1z03QSgB5CVMrftV0d=QY;C`< zqXMl7U8QG)6ZI!!79#yS$V*|rx>)6U%1N{5p2c{Lzyn+f$yCDqKzZLM`j z`u+Mwmqjn5R{PMz`AZsbVCWQnsN3^I@;~572frMD@$CH9M2UbNzIBDee*XM<$_Ypb zOCn5t^ z!J6`NEk;I0($w7XeKisg{3)1nZ`Dey3*4~>L7E2XAhSSyU()Pe2e%ry$~xTH#bvj3 z?noP;{ul#r-X$2TFbklv?<_7{7bxAc=XX5OSW1Png;zkgKNg-)4uI^{BQknbQ`EzUSro^=Z7~|8$Q~btK5Wc21^GK ziU8JU!sm0eJPHa{iFHos`xc=GOD*J$RxULl6mA5x9H5uY1F~giTic*yqFQ2iEZ$-c z8c}KJL=(BNQ@YL(l2Xt+|IS3`KI@CozW9Le3C7OsX9yqBj5%=){(h48e)}qqtH5tg zPUatmI8969qOFqMkrqHACa%;fMO(pwv=M+<>{<8uXT!TD{e-X1S5g}PBwNz!$!S^% zz%(TqOmqdnhxk*}{q|Bnml}|I@?o0GhM9>eIWw~i1_MyRKvLNbN}+P@$ zNRyb3WefMs<_M|C_3Q9TdFz?l}3NWMMS?) zYE)GI4^5||Al&COxS0UNwl{9f9HDUdSY17lxK@p#CM4}WeAYC5^~Sp`v+2By;Iq>2 z-RpR@lDZNKD(0Mo8+e7=k_)mp!xp*47VULHQK!a>lGCK;N&Bn@@atF^8Bcwx;z4hK zeEoEqapHicR(Je)BeXITFlVHp5gm|z`NsVhCknRRR{>^TXP`!W(EJqwprKK6ML1&>xOKSciiR#%Pm}Fk-yI3Vjku<`yf=-G+vkzuTXW|3=RYzQdXSzJ;T)K^rBtJZFyy4&qSbj>Q=S%farf;z~B{f!2| zy{C?g2IBW7EP+CQROjA~edeM#2h_iOfh11zOs`w_TXuei*o)b$Z>wfJ_z zX4k;_ImA}TB+QNdx-7z}m(N+%=slcjXkrRd(id%@q)pH6|M`xWvU1BIgQrJ+unZY;v`9;Q#y*AH?ni*k^Cf0Kgc3N&rk~>dH}xXuL>Kcf^CuZL&2eEK zh3?b5W~)y>|1#-uG3>V3_(dRx>UEAjLdrXzE>&HBF(xBcV|jvmo6iEw3HMM#Xw*Br zDZZ;=lx>(i?I*mfmsPp+xWk}KTEVh6W4XA5Psgj6%e%QGS>mUB%FR?0JExG^Ie(j} z8?N^w5BI*z49lW}L25=yaqwTm26y=<0@%54Z8W;_qe9~hGPjN2jMv8tpMvaDOZSQn zixu#jCzHhmwmkxiI(79{QPKu7>P>_Ab_d{IHL0+*@9F9roAFLod5>2$ris-i7En*kPxOb;v$zC zJ;XD-!F3|{`}d^E>3nPiSN-#hvsu}RSNc=vF`se~#D{l!XUcMkp9-CAIuzO!b86*5tBey^^-ZVT+%Uo>&j zMqihD6J_k^NurtwF}nPPf=0z&4f7LIWz_bGSL*K>m6&2?>VG*eIo#yEEB4W(0cdxZ!(Z`4xY{%B)pcUMzx+4#G|fpp{}P>_QvEC+&n9yTj5-u~i||3EUD| zt`|ne(yW(Ex-nX{?itDL?S(IV4_)fWbOwl5Ep<+)gK)lZ2_h=ry7zx zvnde1)Z&K^Iol6ZY=)Aj=3xCx!+scxLX2gw+oN;+t22Iyh&SrLI%?F>u16JwCLN6c zIA~S{1qK_Yy6H#g*Wb~7#;c~TKh`E${LBI?Qo3_%^PNekpTmx?&yA4aq@Dc`Jo|^V zb}8g;yFcs+K^vR(xzQ=)lL*Bn+Up4k&N4#lu`yRidGy6223O)pI4l5Pa9m zYE}z*pPYn-P94q!GvzlqrQ5LEB#Cl7*3Vjl(`Sf?i=b}SsEVWRnYiPD z-9SI7j(I@+>>auZjV|bAA+^z(P`=cBW1gN4mWK-PV57h>otJNOe-EIj15Sn+m#uAZ zY~{k0tx?IYCx5CPb%(r*rxkWQxmE$wgEG$41IRWWdhS|;BatvQ(l8%<0#;q1y%P!t+c`{HW);i;nj%T z?rZ*QBCyoZ^B4i&$#=?vW}4J*FZvi_7B^K9V3WHD|It2#=+CD6zfLZ?DKNx3FhwQa zLi)x-^QYX`oj&k+2l*_z?H3K8boCTVj0vs|Bqjd-d0Z_v%N811v3qdPu!V&OH!K}E zpc>+2KqQK7`zpoRC`z>-!t!s(DIPe?+WI{+)9LE~&t zskF4T0D3p0Fv2>r^{ub7+Dod$PMgf<>Sgp1wT;DmsM#@vai;vh23i@E1(VS-Rf`(m zwADR$zc@m}XyjU9o40|_$(kFVS_&V`Tqs1Gv#iQZaj~m+dp^yu$Su>U3k>iCbEdKk zsP^8aR`MDjMi8lN)c8c5?TZXW`Vzh^6;JyL+`qN4X~KI2#oISqH8TIf*#*(VR!fu0 zS!fXT?t^w_Rs7?9z7($)=>x?LiGtZEs+|MTVDprqBd_Z#5UcD$$zY&ngNRrt=Pmcz z5ZH4e)%9&{y)bw%(eC@Ud~H3SkT!KjUmF|*Xk~o1n2>hK)QsJVJxFJZ7VKaeE=%5~jMI8ZS4p$e?sxxxO`qme?D~4elqHcnBnjJ=0MB~6fq;DOS*|bFu{591frq@ z-3vC}>zC)HELDka)nZriR!52Vtr!Z0AmSP(5TaE7z8Zb^Y6*YkU_O=YE&^qhJ3-G4 z?nbG8EUOIJ^44k*HnuugSW183R$~{!b7f+1cs9~bQ88lf^Du7AF+VYpZpIg_ za(zStm_ws5Dj+b*6G|ajMGw$!LLH9%5_eD~@G&f3`i#d65JkHxZdz7KTwk_GO;HB5+{$ohW-gwtsv`Tjw!RhDv@fEED9%`n+ z*3G~{^E5+TV#YU#6Bl-j4QpGx_{xjkKOzc7^Vz93sIdhf(8Pay_DNVt0Oo*~10Q@Q zU@tq{vc@;yqbqWS@z>t9;MvT9psXp4ij7_@ukG zhrb$=?bdYAzq36fUr4RC^*a74y+MfEiWq>rs;31#lj|%) z!fog04*~shB$x`}y1cPj2>Hw+`u5V$00aH~}x1akhs6mtjNM0B3M<}&>OKE}9xdX~h|{Qt%l-<>~_oTc^|+jgU3?F@YO6_|Db-V3t*@5f7?U*o@3)JX-B}!0VE|V0brR zsgxopR3yX3L!0qKF!MMV;V&O_o5Ie+N5?ABIb_^FAeH7xI&ik7NmbN#cnwK}#DzBx z-4R}F+D(q4`nsnPA(Oue!Br7A=RfT^2n__@Fvl>3uz$}QbxhOx%<#g>d{#lb zux@7 zC->{4KM>hkldkU%DEXSBsT={LdH#NIIM&!G`18j0EFk)Ks~yMS^!qB>S=%L$nPbY` z%j`24J2{Cn#Io~lLd~rjxTq6Ki9hol?hoVbVVADoy#neG#^ z>>!Xe)_^oacr?!jRou)L0h46gQrlf1kws=&`hC{odjn0nP1%djnDtva?Ah{DH|M`H z+Dv=?@{Iey*(^GqgCIx4+p|OcI)Q zUoqjS&r$7Nre3r34>+;`Gj_U06c4z^4{*p&4e!TUZ(Ky599QVZHIHaJrF)-E&Q8U? z<(>-E<}+fFpt<|}0`|}>{q{atloB=gXrsB*>2 z1mta{xKZ=$O)Gfl*WvF{zkS;C*f%9SG47xL zoVuda)ZTOA(p@Ts-cmCkQo>Fb2`4VoXJCsa1K_D=1BlO#0CMKP&u@FvKJoH9X(-i1 zzMoEyJ|q`#l@@hQHoFmD8TR^jqC)5M9HfPEO*h#5tvuH*=Y_bv@jgth;?weBfOZk; zY?s!IlzAax^fjOxEkYZULdkt|k>v`+=se}p{`$Yic8%}wV@0TmHBuRz~%@kpBQWJE=t@k2C+Ar z)Zm)XapZ=J;?Gx%%^Hr2V(^@XQ*fibns;W3t$R-tFJHZ5#hCKE8(SF3* zBi6X3bbC_vxz|i{K47Z>cn9|BD-mEcuNJLcNF>WRYb!-WB?gnHrX5{vD@|==Ln?34 zwsG2NO+7!9*ZRwG5|eFEcI)-(4?JGbg}Gv`J<0xKWg21gXk)f(*XK2Z3A?YaZby9H41jH}mg#t+jBxceYJ*a1H^WhFJWic?=1 zz(}+-i3~&H%W4O_)x5Pn3U?Y=S$SANgbbUv7o@Ga$nW}nj3&7KnjuDkUND@#2Bad< zaEf3QSRuJ5sra7X$ot#U1k5zi8LDM#Bgi{o2N%6V7Lo1WD&#}mqW7F2u3y9Um3q_e zm}Nu3WTMDyCBQ~7F9*il9sARL*QAR%axEe_zj3D>LN0;o>Jt2$+buS;gZ2jT6>~Fw z#dx@R7#47kde%2)Rq2ShrsPv%8K8^QsrZ_Hh+yvt$KB4QNg&q}GsswEdza-Z+|4#K z5-M*(ap&pPnS%8SKl;_TRpK9ao|KSX$8nl=sG4)j#j0 zJ6fPe{-HTR!;kYl!pKwJ6Qn9v4-d>YdaJVZbSAao^~Sl1Z_)Jl9r#JxY#^33%Wur= z)SV|9xcwYmN^t~GMpSX~5)Wr3f?Z@^G@0zQ*I*UWrPl9}e}&@Ly@Ok4<$$;y;aKKk zeY0myBGNxeP=j0{JvsF|rmCTaEn7Xse){(9rX_u$x<+O$8(P-HSt#a3=$M&ue_v&Y zQdCq8>VYpM0Bfi3vY#^io7Bl@w_{F`f`R7kT1el4&*!TX@=B(Qf6*`f9Yi>iyRCs< zs1G(`fJ&4Pm>do>PO`^DJ9Rw`+j}6KOiA8%b9TN`zFoz3k(O=b)Z+?xhb@snoWU1g z-bgwR`^$Y@Qo;w_$(T##lmqGIH)zj`0IoObGeSGK-<)fU(#JdPY1eHWVKgB*|m zHAUZiOqGr}0XQdn@LOcE!}Fq=WG|F@KYRMxchm?#`=~|JYXEh1K#i(aC!{}D1Rh*X zDKOJ`UkdOuL02N_D73{h6#yr)@DDo=s=Uwo8x?9_c&g411_Qg5@>2$zZzqSSlNEle zgQ$+}luE~;T&2W(m&$hI)9Ri$EI>q1l%N0fFJD$MkSz^A_~Agy%Gb!PbY59x*#62>t?A}tb zbJ*S8g$D$C&DveXk9koeiIxPvGZfss18_hG09v5r10QXx*P`-F$j(n7$FJcks<(Ft z2pN?Wxu-LOtU4g(4X#6gQ3H_6$O#2sCX89bB2)4MX338tXQ6ZQO{jtYONi!@&VP>C zIogETH2pHY8n$Npx~5Y;G$TFz2|tiJ{S65BbUpsBLvsJOu+n#;)4xLXK>0Kxvh z`f>Wepnn=y{pWgn^Pk{7@Xr4py*y==`RDro`)mIhH9(z7IE^H7egh)VP{T&qoqsp* zYD&j*7U*eC!~d%8J%F0*zP4X%C?IxJkfwrk5b0e(L3;093=pbx={7*Af^-C=gc6#R z&

}N+*=ikrH~55?Ux{-}vbBf8TSyGw*!!&73(IN0EW#F8khl?X|A!w=7A{zB%xZ zp83dEC;q%&WTTyMv*LeMaO5cY{BKQn0DzfR)Em3B0huf^;EzxU4AqkVQ+0l+#yMx^ zuX8HSJ!km%7qMw!qN%l_>lQ$@()srLnMWaSHG1AYek2nz+jZOOVfsg;L60 zARL)|7i0jOfO+4;HEj;#QiTrD^ zK4i$8ntDn@n2HtOoxKf#Ooj&4mg$Q!fx+mXU%5XEAXRstCOw}Zff!<&ivv*E3i?ec zdo;p#Wsni<(||?jhaqj>Ws^%re?Q9SE1=+`tYbK9Hq>l~O54Oqzua{M)6kE(V3qE| zJ0Zz;A28|ZyDau+dMp;IEsfUbgC2(SmI87Ol?I&0|4J^tYUubygBEXUpl^$LM^YZ} zKh6$X7xXR;6ayET@&MDRyJyo8h`DhY+P+CfMvslKu=zgwF{Hqlhq`?YKI7n5fJ9b) zzS70;i=foGRa%;^@wY5D&IDAq*NnYX;cz%;vr?MmvkPx22km3$Di`v*gDH77zFk(< zX*_ma4_x}mrZ6mM&y`Vxm4W2Ga= z06d5%&kzG){5_AN=)UdX&f!^R#w**w|J+Dm{=5J<>JTsvT0&!teJYZ?H+5w(uo%OP z=4QE??TMgx9*a;0I4C&|mf9v5`Ru?z5lA)yD)JyQGp(WT7r}yH6{ab?isxji*FgF4 z#rTgo=VtI{{24PS4gt7Gb9;LqwC+N{r zZ7pO$l-MhSD$}ZT>8OI@knC*DnkP|(^j*S#$u}Fa-~FP)Bo4|Ue9Jz*z&A?46juvK zs8GG}GiZ)l-vWRxa!{EvzxKdGR!vhg3T!zeV`40lt7GHs`m92$!9TqrBqmI-p1{bfoc?ELtYyiYDush#H2r|BRsSiOES z4F#Kcb>qrVx$XkTz|%N>4Y|U?LMwJ-3ld)IK3grbMe=xM^FOoleU&!`mIH%>R_;C% z-$+{4^Fae(MkXe<+qdgQ7Qw<&q;SIInR|1!$5)w-!y)kLggko|4h~I#$H0O{f%rYA zjs<(J>Ys^98ES<|hg-4}&WeR)S-IfSOj`~Voy>M9*mMr<++CAyFlT=+P|Plm{XU!3 z-V}K}St}taqA0QZ6sO2SVx#d7;BWr@`lSsN7*k=nvp zr7%=#Tdd3}J6Pjc23^Cpk3#BfTDdCj?iHZ&3)Sbv*1q4~{Bi8;F{ka>XM$7C!o3(w z>MMl23t_2jAw*zQ#oN1vbcL;Fygq=@%{Mm5y={HVDEgjydSeZ0^d{c|e-1aJI$80( zsMMvwpKa%@QDv&}_eS2b0~en)=vj1i$)ZGbD&d2alBB06mQ83@!z1qt)h)bM) z?(nLwH56m8;@HBA{#X$SE7j0qe8V;D5awT3&8m{_{YHN@*Hh61utS6A2WyrywC zh&sG$);EWM0zv&CY;6M*#Kj6mHUNf>?kpnAJt1Qk;IN2TI+ta96dt)@spX;8QbdmI zp@x~us>MYYz%}Gn#q;h(1say)x#c@6Tn8YZih@gy;$@hvW+A+S?-ZK`CD5)ab~EIU zW==%5>;Ab_XS8o#)1wt~eCX%rw+n*ST-{>xxj2iYF3_~IW?vT#`d>qjD8QOQlY^#) z#^dGBj-b+UG<75;<%6IX)L$jKK_?xXg1gs|QFLRvm%7Iblv_czHK9>tVjVYRozZ1% zKI6FCEd#R{OOW=BNrE-DpslVOfn`a2`?IX)p%cAj*m-fAi0%QbR*<*FIZm-K@Zf^> z;?@qMVa{FYM+Umy&dyJ52`%7}+?PL^_jq%eHWvAuf|}!9SnY!~8;{?3_3^< zp{%7U`L0YeHiuQ2WLj3PdkR#;CEs~g=g7IUsuc;9=eBEC-PX$JQsEZIm`%gZ49JSylF%-(5sWj?Hwo}`xAjj+nb3%ejmNlC%&?{u|RejQm!U*)8tmQqS_ry}_Xv>K_G zYC9?m3a?3-<^H!gTrvX?@Y7FdGwbNj96X+yASIXLk{(^(H@+1* zyI0)t8ARWJ8Y2bhF;D13Ef+aB89|`YMJ3@=ZP!|@N;b{a3Fa`D?O&$eE1@EIz#Ro^ z!q*!Ho4vrYrska&ZP#ySi;pe8PgxkkYFyfiIj;V$o38;ghL8c%s>g1P0$|EytQFm{ z7O;uQP}o8%ne7Po>SWxNRQq#@z8Zqb?3$r-IDlgc*}Ln=?t8l;@TmL8w!0k2V8dLB<-pHm+MHT8I1V}HyNVjo`NPdR9C~ntl4rOl4g8}8 zI~i~*QoEk`#{V{Bco;af@j;UxzwKc9o0GS*JXag8LDM@FT;yowsaaVep$P+oVTuvX zn*$aLb*Jexz^o0r-cLE7YZ!t`FvBL?f{T;R0wb@)ws~xAoD*?4D=<)@^YOchA~_(RTI^>< z^)g{qvNB?wQ6fPP4P>56R>#PzKhbli%zj0s1Or&6azNa<0En=ixESMn5Jnk!ZZ39l zlntlu6Sfv!o~G^8wMb-?rQ$IU_Sx-Do(Icu=R`x0%f_tgh{r-6G#l{S4QGM*yJoGK zZ8K&T!2oLu`oP-v!?-N07naa-K|onIBcF*wbYG=!Ned;4)45TSotxvByL_qGM>w9{ zaK<)_Jwye70_8r%G-F0M0`eFmUpE%*QE?<*LWIwu_nUT$0gB{y=9T{4aB^SkZclWG ze`p~W2O~=PD&Hx&>shte=oRu5S8`oVeOhUjGlju6mR={pXKmjnizT+zO^iO+Z@70K zP`memFbUgVXvQcDhGP1q%a>80`(4e>bONL%%xr9w76X}7D;t|0&{?~8a1aSqCqM)T zuGT?10IEEC^k|L%&Id}q6681Z)W%5>NJ*j99qC`Qs__|9sliQ$qw&Tf+x1ytwzew?GHt?@4!K6heb} zudr_Bk}*QsFf*0uB^!>=>rk26m7!MobEXar&;&qcp95+|Z}P8T)L^8i3Qj@Rm0 zVEmZ8y^bYuWVT6KEgQ==PV_;UoA$ zkga^tE0>K1`|2#Jb%D=*ap4At!Zzpp4dSLO}D0efr4*{vk#aoS0L%~}A2Z)3FxJ!Id*B2i2qV&67W&{A5L&dmq0=A|5JbI6O-(Ivo>No&D5wgl za-2a#9YQUn2Bl5$jW_IPv9$*%=#dAv?LlcN5NJWANJ2_$xSRo}ZA+=5QMehYafD{1 z%B>i9Dky_FMx`TfH4SINiZmV!I=aO@UE0#~DWhUwS{e94H9^+ES(R)q~+bXB#}`4$$g;X$h<&#V4L z8xIa%I$kP6LI#8v;w;?;TQ09AB6TLsM<;HutM^<{=wr0Lq_Kz&{cILqV($8L%)`_9 zwQFpFkjCf}(%Pbu2G`g9tI+@r@K{dQuI8PfYav{yZg0d80##&-n#H)X2WrYWiOo=#2x3ecenv%}s@SR+6kOxBwo;yk( zv!G4MWhSK_&qoF%u*i++ur3c+S+RS0A0+F`dpuGVIr5l5BTTWb z>BP}>-}TQ9nPJ$s7gaZ79uTo<3!^reXae7HCt-U8oe4{cH*x2E`%^gdsXb;A+uiSO zTS1$?X0y_(w{-_G_0P@`z4)b>Ht50PI`2$418(6j&C&-^g+ZvVUb^(wC>Bxn?lry; zIhN(KKY@d-C$)b`kMgH~mtRiX_>*WY_CNp!I8hdn%hK6=wAhn=w_0Ea?u<6!D1r8_ zqTY-%ybdTWcVo1)ytbY8Vrod@&`oP#&;uk4-51l*0J>mF$8XbP37_7_q#8m*%O^`| zjJK=X^SV+vfBL+z zb3HlD=*+o*$d8L)%jLHx^Jq3__~#*~;D;9o15u6{QARZdgl_Ks3zt#v*`bw`+Jjps zVwiLRW-7d5h|yL%e~Irzey02A#=Vuw9qX4fzB_$aJrCsKV+n;N=%#jej-hYLls6Xm z*KM$0+yKv2Stoi~Jrt2)QUnumE(1Rt-+cGy*YzP2!-e+T@v9LIr^PcYX z>->4ZJ-ET12S0*&K9(RgBPPD(E! z{uKo>xaPt72}MlMH3RrF1A+1r^( zGIOU5{5maHB2>NC3;bzZzB?_IKzdtkiUT`Ec=9PCC99>S#(Fcrwjw3V!4uQo4vQA!$$Il$>X9i?AG0Bk!!3FU~3V0UR2D zgI~MBnVwZSa2eN1I~!Ei8aT(7w@@J;G;hC3a!$iNi2o^7_cq}bt>sQ1+*NmlmK_XI z95n?Yt}o4@jeP}p&fFDPLbj~YEY%QPY}i9RJ0VdWb`Z<4y z=1MQL+^ETW<53ba%=miY+EUSygwtK~NV0p%#b)!QI9JvA28$|>mWfEX9k{D;PlSzJ zIkmj=#eNkdSz>KC+PX2w2^PS^P$r5k*K^^hDq+?NbScPm_uaDeJt^p4Qz;sK2}(Nw zWU6qP?EtZ+T(l{ao#i8ip}X!iQttDau-;<2*F2jHF|Iq@km{` zY4>1hpgm2zNzFo*vt@PZK!0h;-PyJhA3IRUTt~pn%H?gw+j-rkJ!Z!MvZrUR>Ba$? z3r;%I`nC7RVGi2;Hx47j&uJkpONJ^-%ke#*&F*G3AZxdcWVK)QR5z}z@HH-o6Vq+U z>*XdFH~fAba4k-reQs^Fklsjek$vkEHe6h$T1ZJ&(&XcW^7IM|dtKx_S$2V<#{Zi| zhlq?Z0Yg#AY?sQJ>|My2Al7~F;A=UVGwvE!x1Ync+4UG7rE5&OTeCf~*XDwfYad=~ zH2lQnb8ty9(J>s-MJ04%%)oALx9+^pC#a?bA^#P1NQjOxFjfG3IOYCpISej&rBjAZ zF4@OQ)ZQ-I$m2&!9FJ$;3Nvw_gIi+P%8TSafh0OIk+yx#{+6ae9|ee)hNE7%hRKc6 znigl!#gK}2+ zUj2E`L%BwBHp7uwAeFlSl&$w0ScWup2eSC$>du^2)O={Z(pwa*r7ysj7`#%7&SSr3 zhw|o9pHcMQ$GY357Zs^SRqUU?4mMakc?E@HPEvQud3~gG>^EI%14eDukK)7uA2QRs zLWxR@ZI3%qEZTEx@|=CKU1=PXG7qKaC%(;9a^kEE`K>d+_6kM`x{D(QcrLpbHuuJh z@u=QT13xFF+V2-l^dhNZA^#i*vM=ublA7EZu#nG8x>K|2fAQ()R?rznrYg|+&Qgf_ zIa&^A!M(fOBasGF#JokzB8Ejkv+X zKgxENedf|EAN$bvZPN++A+FYslQsGn>JmJl1Pq*z2#VM1B}Ar#r?bN{9^;Q$?>f)B z`Z~Lxt#WZ=XH>#ov45+VHn^(;%QeUj*6Fx=+mc`IEZ%-dzf}9n((7X%Ajk<-NP@&7 zBI3mUQrti&V{+B^v@~AF(h7u)eS2C`0=v22@W@ELa;jGE3MRFmbC>gYWw$2WiT?ZX zs5?#1dOmO-e^G~e2CdRhDA>|apK3W-Jlef@bTar)?wxfWdoi#S76-s90<6>Q=gW<1 z4yyn%N?GJ;11o>2QpIMz*zS+aC+f_~$A0MqfYKKq00Cnb6S6M6 zfhv%mM`5Ysx$6-Y$+q3;vYh*S78iGY!-Vm(bc%R0ZgwcSIN6YN_09eTYgQ*KkLw)Y zTVXulQNf(i1|(T-|6T~dVtJCYJL8-~6`$Kc5;?4KB&%8H)3 zX?mtidD99}_*x_X2Cqby-~pRJq^=a~jp$^%z1t5Qy}|X(NWM~WQHgC&ciOhWc{F!J z`L^Wq0Xk_3F)>ze;_b-;&kgZPW{&e4F*Q*g4sz?{W}AKc22)MH!V6P+vkrzXkaJsj3FS=0=lnwy~4!JgPXaN~o;T{QA22!VhJ65d9{tl?CtgLEoO_C4$s>KQwM%Il<#^k+ASLG|CTmf&b_ zvfvNDizi(-R|llTg#0~O z+0?<0vblvYX}BK#w5>L-u%_aW(D5u<@+_%2VzJvarFU(y5(93!U9*U;(>;3+mB!RV-y7YLB;jg7>QB+c!e8l&f0z z?C9IrR%Y+zbhyE`R#cLaJdIoIhjp3zBDSAk8{r+o9{C~9?at!Yt7_Q|0bjVJl?F}8 zSRv9h%tf3Rh zvunbGUw@y|#ExhV_F-?`Cyb=#*TSj@dyZS4F@pU^`!zDb1~D`pM7> zLh?AuD&kLPwL+qmxW5+Sm+6?^V7GNB)54=i7u%Zq2L@ph zDbmKbrRj2Ch8o7b`VwBlDluu{yG>m$1}pK8!Znq6#_pfaNnV;SUh*uj@!G-@!shsCHvwU>vfg^@p_oKiUqTp8KYb$q5dCx7X|>t=Kzrus zR($>t#Zn>fF|7qShc;tlA!KEx$meo1ZmkfsBJ@6E$K}i+G`1ZR&w{QUsk9Y>?#XuCb?0vq)inYy{sug`58_?!_Z2m>^pWuiR^aE#kMF*n z{4IId*wQgb5Z6Vqd5&ItDhM)Vxw!5vzg9(`{gI|GD#AcC;oU5_i_Fe`7adLXvk(|~ zmsuyf0`9#gQev%{?>GUv4FT${#Y#ZH?;aA8S0P4zySQ2PIa;3%fQtwJC|e|RkBSQh z_gc3(wWMeG^A^K=0p}EUraEBPJpC#yR^n2w+-Ab6}BfX9M4hUnRai zbf*O52h{8k?vHb?E{X2=&>ycq_FsC}{THc>w7U*bO<&hIro8(JFFy6%1J7U)jtuRW zu;=~x1jMTxjGSt`~z(e|CpiS02+pNe81tx$d`u%z%ASqAu z3|lV&w(}+C(bCdG_5XC=c;b|Ue3&;_sW&ai4U5}x2ldAAtG0K^lirPkLTG-wzFjIfh$!TKwBBoOJo;~%fsGti7R;5uU#5T*OtcrD zpIx}DmUmPZ!ahB}@igf-c5ieLT%(PWsa7>}xwOr`J}Y}Ow>*(Qr*!5#&_1cCf2xQU zXJ?G!=#GcB+>aw85fnjBG~+}7Xy-$(CA_lAE7w>$IV!4RPS2W3PyM}}o^I8-T0rYdAt9kn)%%@%w4&^?fntX?ge*Jo^>F6S z-*eTTFMW6%_|URLRn(J1#?U zq0N8K?Yq)f%iDHS#tV&Hy8ZY4U)a5Iw%qBGq06oueEZ`nhh93D_nbUv=<``&@|oZU zsLmTE@Y||^zDT3bC&^p3Kgn_wzCkc%10&uyHi^e9MlL>HTvBx!QL%kZni0rA>VS#Y zD9OKXfB!x7_M@IF?uWiWHmlb-VCc2l06HyUmq)6A*7@OP=_Lg6K|-^??Bm!PMaFEk z`ivsG*Bwxgug zX8~>%fPJk&L2z^6bX+o;YYAuc$9OwavRY9A*aMLUJ@kB6SO&)5-+m0U04{mQ^e8z5 zJlaR_#3p<$v3-5k;C!+;N6kuuSbhRpd};p~u+dTteafV9_&Yx7pZYmlvdBsh20AuC z_F5Nco;*N7a0BQy41jRvv#1?t!fEI+huGaLtIY%DyP<2`_vKLel7R{Q_504*{B1e+ z@SeJ?$U( z9M1~We@1+UR7_Klz`Z!R6jQYm7o-XS*RB6~zUd`GqD$8mR&dO&KQI^v&@5NC7TD3+A-BnZ@ zKe&YONCw%E-!J-)eqNR=ZgKr9z}Ujd!qVl`A(jaq^3gRx$Eml=RV(7ZIT~AIzGLXO z2RYxlqXd8(SMr?!!S+~6W-!uIIBhD&p6&w)&!Q9lk)NXi4#c#Bq`MrMf2;>;D`J(S^{jw@Ma4+sGaox8anx zZ_l_mJI{hDRGD|gfjkFyl#j~~n03wUTrsyyVz(x4M;@ocvI+pTHpXNe`Al zwRj=`kxog{=fnJ(+4E&yn0(m$J-_eMf8X)G1h~?1F)>WC9{65u7ogAtUXJ`ups(aJ z{d$s-k#Vk>t)ORnbrKTX%VI!r8T$T^8nMoiNJ2+@o<<`vIVIKTjpGSQYF9ST1Ua=E z8j;imUfm1YGnu+cTe6$1xCXu~W+BClytKXGTlgpIf3E=j8|c%4R6#RG%bkz|O1X#G zkO1?0_-zoR9!-}}H88??y-mOxKsz=coqc6vo71dr@CcGYfj{>&Xg?{p5LJ&SfzDuj zqSR-PXc8X?_z;=340!W=zErCnWA-y&z!G4MoHWX>55F0uoh^{?2Y*IHl+yD54Ce~? z28y&tkBKI5i$eShux#wcOiWHjB_`%|cun(E0?nk$55|~&i~7%+d3)PWZR52O`+oaM z(x(k4AEaqGEbdqg$++aK{u~!?`}=_x&c066ChQ=sgNq=mN%3s*Z%eaCi>*|^-c+I(A&4~A zZ(#7^gJk~(z-}>tzy91O$zBr+3e(;{ye{zV7oE_y^UurV@&A{a^8a55-~Ue>WMn-c z&McnzznNm*f=32U*jxfWozG#U-{+j?A&GQr9M4kPi(|e2m|TjN(kj8%&yuJA&a!9x z>)KgC8$wB@l4_viz)u8m4W4#e0RQyG^I5akw}ak_iiyI#_s*WV^KayM0ZA;9b0)j9 z2#9dN{2*orcc1ONxep%Q!t+K>- zuWO*{zT1is&=n5JkS79xBGWaNly3;zfz9xgY>2(`h=kR_Rz0>FK@0Us{^ylqbrgWz z5n4J?-D2HzK-z9avKiwtNrEz>#=;t;;Du4~oO@XC}JF`v8BDd_k3P}4oISXsFB`UC6G`86baU49M&=t`b)sq)_tp~p|dQdeC)4V1C_PcKAi z8>s@6llQax0|NtF|vhQC8nHk@!$sgZJ1Ww|$YC*m$9286c z?icR92i%$9z(5Rzw!!%2`%1u@CY}xCj0})PhpYP6tB$n%d~h%O^7Iv*HkBZ|dA4MW zXXmZ1vauH0O&W+?wC=UKQ`9kCY$MVATr&o)4k;2IHqhZ_tQ&^oTcu&X(^P zLg*xnT4K1i2(bWG#xMs~tI`V3x}jU#y60r{uH{{lNA>irz+|m}*{>?TiFtDVNCs+w zX2oSc{*m!1|DWNu%2_4nPEv}CT>>(=2KeAv!kGl%CVH zPTQ&}slJ98nW+W1c;~F8r#?;osWzA%d=|hoB3e+!ezM-r3;rE#o8VgR%_;jLJ(nwF zAW6G-|0IB@(n*9e|G7ow6IT|GwN{HMR$z{!yTe$j>Km%Ok-t{^gAcZ(cXUNHa!3C^uGLb zDi_T*;wlF%TFc6Ez;FYNtv0((Oe1shF<=qa=FlyA2(@z6%GDX?SQ54y7KBvlzkyX? zHu$t$y{@Df&npXQO3*uP_VYjMUPmy1y#qv@gS-zw`sm!JYcU6`3Z>OrhZ<}W;<}HT z)uS?9U@qth=#(^~%7^pLj?`b0Gms{GL|Di03OTYal5GXH9dpI6=8e%(jv>gtwJ0r=U3>v4{hHT-_ zt`qNMhx=dZ`J@P|VeyO4v(+5IN5!RorN~x$6pHZ@8RG^1$h0iLg{mYaA32_H0d#h{ zW2Ttkli8*pyzVT0NUGNUbAUu=-N29qlU_yi44CmMf4(B)1+w3kU-vVV5|KRZf>J;; zT6FdhNa3yBM{j`H1ijA&!FVzy9o%z<^9~~*;!lIHJCCFrb3e)^uQ^B^=s!6OaHiptzC!ak-`rjjv zxz0=1+yAxj-Or+0%uUVCW+~bIH(VgP;PdZKfCSk88_d@c%r-IJCT`$wunX%Q9?k^~ zk_nbS(~oSz`0NjZb<4wl^vRtye_f!0xgVXTPbRuF3KD+Wp-PfetQSZKKR*QpQl-ph z0l%J)=8`p*jz77x&P{kRjriM+g%I9YAXsH>>I{Lo)_;nspf0`t`d^P4I&{OSWTcCmJe zmh97H(u1c7*Yk&3ZGMMKAn*QXP&(s4H7~&1ghh5H*;~3m|3hhM()Gw+iV@P%GWc?r z36s;&^9|PLu(td?|Km&dzkyGFp`smsC*ZasVsl&&zSv(OU$8W0PbazvT!#Ip>oQQN zUU6o2u&Xf5)PRRR$7v4*Y)Qf6sn5yG+!i;k(f86`(8$|+-d#j<4zb$yPi9dnTiAGY zdcoA7Oj07s*8(|lCysW^{j2bU5iUGzeOp$YLST+RN?LnotMn4Efzxr(jX@s&a zPVQ!VZ1F~w8$d?Q-}=+7TfkFdoFCXj!KzRc7M?x=mW8b%!z1Zw0N0D(H{rS!Br!L* zJXuMxo3b1;=xSO)Wi+zjvnQ#I{$=J}@p+VgJjS`!ExyL;Io=f350n#4?EuwDl0y0+_6~xq}%@W>ZKITX;K5Fj8D)ty+&G)lQcq_v|V5H2zKLBNL7> ziOQ37ijS)SvyIxwa*}lF?{oM<|9vzgLL*;4<@_a|bP1d?H+i{HkiD4l>BGIqO%LLw zp@P>LEFSYHAjMxVL#J{1jFZ+>mPuC`4~={+yNKR z`<)ePHTUCZl~4L~(pU<|*rA60ZV?ah+xqcNO>IL2*gYsidDC-r;x@?)HoyaIdJ{~1 zhJ!1$ER&tAhR9nuxaOzUf9T$4NO}hiE$E)I-CpQETfC4T4v?66jFwC5p1au_&SOuJ zK3ehSM)}ET_N9fsN4OFU@0PWG<-4`+kq_Q+(RjX5YcHSdEwQ1oP;OoH1kGx5L8pv|&&;S|Gx~JzwZli7UnhGb&%`sUDt_oua@=YV9r4`bb)_CML({<-rawBZh;2n& z>)UVJNv=Jt;@Dl~QMURK!MMAT8N;@-K7+?99vdjNJ!g19T1F;HDH9L&Yr0VW6y=DM zbNarwjXPya*gPd>U&=+~;^MN~tc_0X&Cy;s5?Ln-!_Mhm0n>qkx?IX*X|0m(!d=HF z*~`JXeM?AwrM3pW$nU3n3oo3|`8_zio`ksDM+N=3F{^4_@y150>*d?qSL9iyNSMdv zE9F|F(FS6f?gRcfuBM@l)6QJ7&4>}FcRrDYdP zE#{x5HCY#J!d+Am*dzy1H1>>@p%h>zJuk#d|ICgS#<~n1% z^Pdgw+mx2+VeT8>?97{{RIq4w%H2X=727(9!Q0CG=vOQ}bX4iJB*F9c84Y>jpUH&q6NOkSUEGv7)D&3(Nj6?HwnB2;`ze_5v+ zWg~R#>Zr}XyWpAWY!ZZDoXupb<+omlZkh~tQ&{>ZU1Y+k8+cWF7M2#h_U@`oMUP~5 zzt^pwK26g)b69M0a`|yxK*7B!QUN8V^~9hrX2M7i5F7m>!G)dIdL=m&E#{C%{MvZ6 zF8x(=mM5mxO%2>|g0@NTnM^X@lbg;V*{bJ%4~y2*zFsOnOpM-*EMI$4=eVOeV_~Xs zkt~x2r;=$_}jPU~e3TyRyjs6bJ zU*wuyvY8~>Ss!)_Hcjsm50k4QUd5|=poCdmEYeN%%aMc{wTk?twfP@keQzLK6xGFF z=Sa*T$-&Vd)OSoNQg_)?Gnz8FDKw46R3|5U?O8~yQ@YSSe;_5bV!fW|n^rqQ)sq>+ zh3z<8%Bz@=RT^?qKfcew64XTcPt^RV(cm8#4ME0|IK>1YR$8eue>I3(I8|n}jQNWOBF* z#mv{#v$l&`=vGwjCaZiAqNiXD=8Ca*v8#(z&t**6)9{q^7ok9{$(4L#eQCLxs268O ztJ(6s<*{ZwPXDtVddD2>a=+R5*8{uP z6Mhak5E^E5X8Eg;yqjRUcR|y&=n`oKCw_{FjE`+E#L?DrnS^0Vevs6)X|lXC zNkht-_L!N`ue<2O1K6#0H~oDpwpuJHn=KPXtKewfBMp*c-K7zA!MAW*cE|7~<~jY1 zKMJ-@v{V_dFw+d(!rytFuX>`rhWfUeW6_p-#1#c(TCJl3!)0eW9^AfC0K&59!BVK( zt3Y$Mm6P@Gtzvk%umR3A$CJ2NWF}5zcbR~(r;SEAbjNlG9HjBie?~7@7MPF8X9B;^ zyfznY&&z8`*gy)lR=ZDkp4$LSL}PY;X`Xxx=iJ`T`rhQ}Mu#V}=?rt5F4nF2EQ!~J zh0-PgH28bkCUFxT@buRao9dT8`t{m^jbK1WW47{{2`x+3Jv6ZSo*9^jKUG)x2z zUr=AZQ%qKl6rc0_gYNk9!#9|86WkKUWa=o&)Qy!N#3@qMbUpa8XaIb;`pj zQM1Ru(jD|j$jQl(U%(b%L2AU#wTfE8$AHf3*UJHet6@zPA<}K;CR%^FGhZfF$LTsO zrzRfd={uzjpt8;;?xoGguy?fyjGDMA&T`#OE3(zc`XEOa<6W*D+`ARll&0Lv#aGo> z?z?h|-23@}4I0@uVvy-wmCu*Zq?6Ur3ePVNj3?t*0f=$v!a%lGTJH_j;vqzFu{z`f zYMn0H-SvPnnnnfK#pC(c|6XEtPP&8DVDatQ$bsstVaINZObVUoJ=jx?+qcuq*K>0U zjP+c$qs3fhCHA+vgvdCt;Ddd4G7+R+pgXCx0oiJtP@9WZVU|=2VC~OA=E}y98(_6B zbR-NETcE&%pD6CF2YBcnAWAi8GDRbOUd{b`<79#m0zMnU+H&4Hx zBfA3|a^3k5BKPiv49|}>wG=<9;~7`ho?m&q9qBxrV{1V{^Ok-7)1S>O5s`LB>KP8V z#r_oFq>rhUAS@Q|c7^#&QJEc?Yr*w~&Ky+YJ`Z>|?5!F-;$Z2nxlrE}_nl=rgxTy- z6snuFYIiU2{Gc51y4*=gJ@(&y4UeYU3e^uO2&WtE;o%p>0Jkvzpg!RI0!YzD@Qx=X zKon;;^g4|!;4GjmT|17~QhA%}lglq@-LhvHyn%fvqUhf(dn!s*q~$*n)8u@VdcxXz zeOPQxadGk9zW)t7sD28O!hod{GJN91)j`pYVGh(Q&9Us?v+eH}QBj3V$5yx3(ARKB z-9~FXrVWz`4^CG|7tAPk-_~DRO0wb*%fL6N{|hu&0@lVAPK!{fe<1o&2Jvtg?;I%~(!v9rC|om(TBe zo?6Zsz*%Gg#s&l7C1f!mwE=Z|fO3|D#TH@5U%s$jG?4W>E;sdh{;h}_IJl(ylIV0Z zuE$^+KJ{$8#AZnA-t6%0ZuSN}#7kHAs-ecD+hCRI&MU?EL=I%-Dx_cf6_Rt|OEXq* zoV6vQkyyTYE2m>XaM^%#{K->#q>a0MYx`L8-#4 zKk%A|lK0%-6v3Bj<$YY2j=@RSPSmLC?!0mB*u>OUE7L@g3F!oy)2y#G z^_u2}esq~^V?^n{13}OOi;98In1L*I?3X#wvg)2eHVdVX3pcJ8OY(k9GQIHk33!o{ zdPk}SXF|g?YR+w+BM_$YQe9GfTX79B&V=&AQbR5*grQXz$(Gw6GEnyp^n) zD6Lzf+vb~IN>sW6XyW~dpZ|LVZcx8#htj!^mqyv_wJN`ho!`(aHMET3hm2?zimNK2 zx)N|^|4+T;n3)VolHs~F*8bpIwD!0|vVT@&BBE(SMZY7w+N8QJ0q z)nHByrijx0;UXGB9vwMHTQ$$!yJYl0h^-?py@b?t(dlo6@XOw6KY)dVs2AGm$8Z{A zz}^@2q=B@UfTX0Q?JMpO&jEN;ec-`<&j6;)g{dDOgk2Y7HP}y8fsHWM!EQ ztH3BvWVu{QOiU~y9Z*v(!Iq~IsEknNZB$t9-WHZ=a5t2lDm~W)hLp}GMly_`?RF3I<(mGEi`JO9r_?-rqM`=+DX@38 zLx6MF2T(nROHkGp<$LI(oxmUlSXv%Hhi70L4P%LEf%sQWDCIGR6x?%DBku3cjsXsi zer}GZ-n6^QYH17@Wi!5iZ?7cT@=ERP?9^Gx#5?iwg;;M1QdW3b_R!_77w+inq@gz{ z2qEnSjf^^7&2FTG2|GF28<0 zfYuu@Vg?J%0-#{u>H1x^|Hwun)w@#|KfnJE(Se4=^XED)=KpHNh*}x{O&Ajqt^XI* qV)cJXSQh-pI}^3C{h$A}U0>mMYmAmU+h))MBKt_`VZNlvtN#l@;dGq< literal 0 HcmV?d00001 diff --git a/docs/docs/manufacturing/bom.md b/docs/docs/manufacturing/bom.md index dcbc135d33..aa74434c6e 100644 --- a/docs/docs/manufacturing/bom.md +++ b/docs/docs/manufacturing/bom.md @@ -114,6 +114,42 @@ Select a part in the list and click on "Add Substitute" button to confirm. Multi-level (hierarchical) BOMs are natively supported by InvenTree. A Bill of Materials (BOM) can contain sub-assemblies which themselves have a defined BOM. This can continue for an unlimited number of levels. +## BOM Validation + +InvenTree maintains a "validated" flag for each assembled part. When set, this flag indicates that the production requirements for this part have been validated, and that the BOM has not been changed since the last validation. + +A BOM "checksum" is stored against each part, which is a hash of the BOM line items associated with that part. This checksum is used to determine whether the BOM has changed since the last validation. Whenever a BOM line item is created, adjusted or deleted, any assemblies which are associated with that BOM must be validated to ensure that the BOM is still valid. + +### BOM Checksum + +The following BOM item fields are used when calculating the BOM checksum: + +- *Assembly ID* - The unique identifier of the assembly associated with the BOM line item. +- *Component ID* - The unique identifier of the component part associated with the BOM line item. +- *Reference* - The reference field of the BOM line item. +- *Quantity* - The quantity of the component part required for the assembly. +- *Attrition* - The attrition percentage of the BOM line item. +- *Setup Quantity* - The setup quantity of the BOM line item. +- *Rounding Multiple* - The rounding multiple of the BOM line item. +- *Consumable* - Whether the BOM line item is consumable. +- *Inherited* - Whether the BOM line item is inherited. +- *Optional* - Whether the BOM line item is optional. +- *Allow Variants* - Whether the BOM line item allows variants. + +If any of these fields are changed, the BOM checksum is recalculated, and any assemblies associated with the BOM are marked as "not validated". + +The user must then manually revalidate the BOM for the assembly/ + +### BOM Validation Status + +To view the "validation" status of an assembled part, navigate to the "Bill of Materials" tab of the part detail page. The validation status is displayed at the top of the BOM table: + +{{ image("build/bom_validated.png", "BOM Validation Status") }} + +If the BOM requires revalidation, the status will be displayed as "Not Validated". Additionally the "Validate BOM' button will be displayed at the top of the BOM table, allowing the user to revalidate the BOM. + +{{ image("build/bom_invalid.png", "BOM Not Validated") }} + ## Required Quantity Calculation When a new [Build Order](./build.md) is created, the required production quantity of each component part is calculated based on the BOM line items defined for the assembly being built. To calculate the required production quantity of a component part, the following considerations are made: diff --git a/src/backend/InvenTree/InvenTree/api_version.py b/src/backend/InvenTree/InvenTree/api_version.py index 30d71099b9..67d5757464 100644 --- a/src/backend/InvenTree/InvenTree/api_version.py +++ b/src/backend/InvenTree/InvenTree/api_version.py @@ -1,12 +1,15 @@ """InvenTree API version information.""" # InvenTree API version -INVENTREE_API_VERSION = 371 +INVENTREE_API_VERSION = 372 """Increment this API version number whenever there is a significant change to the API that any clients need to know about.""" INVENTREE_API_TEXT = """ +v372 -> 2025-07-19 : https://github.com/inventree/InvenTree/pull/10056 + - Adds BOM validation information to the Part API + v371 -> 2025-07-18 : https://github.com/inventree/InvenTree/pull/10042 - Adds "setup_quantity" and "attrition" fields to BomItem API endpoints - Remove "overage" field from BomItem API endpoints diff --git a/src/backend/InvenTree/part/api.py b/src/backend/InvenTree/part/api.py index 81f74c971a..73c50b6957 100644 --- a/src/backend/InvenTree/part/api.py +++ b/src/backend/InvenTree/part/api.py @@ -11,7 +11,6 @@ from django_filters.rest_framework import DjangoFilterBackend from drf_spectacular.types import OpenApiTypes from drf_spectacular.utils import extend_schema_field from rest_framework import serializers -from rest_framework.exceptions import ValidationError from rest_framework.response import Response import InvenTree.permissions @@ -617,32 +616,8 @@ class PartCopyBOM(CreateAPI): class PartValidateBOM(RetrieveUpdateAPI): """API endpoint for 'validating' the BOM for a given Part.""" - class BOMValidateSerializer(serializers.ModelSerializer): - """Simple serializer class for validating a single BomItem instance.""" - - class Meta: - """Metaclass defines serializer fields.""" - - model = Part - fields = ['checksum', 'valid'] - - checksum = serializers.CharField(read_only=True, source='bom_checksum') - - valid = serializers.BooleanField( - write_only=True, - default=False, - label=_('Valid'), - help_text=_('Validate entire Bill of Materials'), - ) - - def validate_valid(self, valid): - """Check that the 'valid' input was flagged.""" - if not valid: - raise ValidationError(_('This option must be selected')) - queryset = Part.objects.all() - - serializer_class = BOMValidateSerializer + serializer_class = part_serializers.PartBomValidateSerializer def update(self, request, *args, **kwargs): """Validate the referenced BomItem instance.""" @@ -656,9 +631,14 @@ class PartValidateBOM(RetrieveUpdateAPI): serializer = self.get_serializer(part, data=data, partial=partial) serializer.is_valid(raise_exception=True) - part.validate_bom(request.user) + valid = str2bool(serializer.validated_data.get('valid', False)) - return Response({'checksum': part.bom_checksum}) + part.validate_bom(request.user, valid=valid) + + # Re-serialize the response + serializer = self.get_serializer(part, many=False) + + return Response(serializer.data) class PartFilter(rest_filters.FilterSet): @@ -883,24 +863,9 @@ class PartFilter(rest_filters.FilterSet): ) bom_valid = rest_filters.BooleanFilter( - label=_('BOM Valid'), method='filter_bom_valid' + label=_('BOM Valid'), field_name='bom_validated' ) - def filter_bom_valid(self, queryset, name, value): - """Filter by whether the BOM for the part is valid or not.""" - # Limit queryset to active assemblies - queryset = queryset.filter(active=True, assembly=True).distinct() - - # Iterate through the queryset - # TODO: We should cache BOM checksums to make this process more efficient - pks = [] - - for item in queryset: - if item.is_bom_valid() == value: - pks.append(item.pk) - - return queryset.filter(pk__in=pks) - starred = rest_filters.BooleanFilter(label='Starred', method='filter_starred') def filter_starred(self, queryset, name, value): diff --git a/src/backend/InvenTree/part/migrations/0140_part_bom_validated.py b/src/backend/InvenTree/part/migrations/0140_part_bom_validated.py new file mode 100644 index 0000000000..7accbafacc --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0140_part_bom_validated.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.23 on 2025-07-22 01:57 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("part", "0139_remove_bomitem_overage"), + ] + + operations = [ + migrations.AddField( + model_name="part", + name="bom_validated", + field=models.BooleanField( + default=False, + help_text="Is the BOM for this part valid?", + verbose_name="BOM Validated", + ), + ), + ] diff --git a/src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py b/src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py new file mode 100644 index 0000000000..1b0496dd27 --- /dev/null +++ b/src/backend/InvenTree/part/migrations/0141_auto_20250722_0303.py @@ -0,0 +1,80 @@ +# Generated by Django 4.2.23 on 2025-07-22 03:03 + +from django.db import migrations + +def cache_bom_valid(apps, schema_editor): + """Calculate and cache the BOM validity for all parts. + + Procedure: + + - Find all parts which have linked BOM item(s) + - Limit to parts which have a stored BOM checksum + - For each such part, calculate and update the BOM "validity" + """ + + from InvenTree.tasks import offload_task + from part.tasks import check_bom_valid + + Part = apps.get_model('part', 'Part') + BomItem = apps.get_model('part', 'BomItem') + + # Fetch all BomItem objects + bom_items = BomItem.objects.exclude(part=None).prefetch_related('part').distinct() + + parts_to_update = set() + + for item in bom_items: + + # Parts associated with this BomItem + parts = [] + + if item.inherited: + # Find all inherited assemblies for this BomItem + parts = list( + Part.objects.filter( + tree_id=item.part.tree_id, + lft__gte=item.part.lft, + rght__lte=item.part.rght + ) + ) + else: + parts = [item.part] + + for part in parts: + # Part has already been observed - skip + if part in parts_to_update: + continue + + # Part has no BOM checksum - skip + if not part.bom_checksum: + continue + + # Part has not already been validated + if not part.bom_checked_date: + continue + + parts_to_update.add(part) + + if len(parts_to_update) > 0: + print(f"\nScheduling {len(parts_to_update)} parts to update BOM validity.") + + for part in parts_to_update: + # Offload task to recalculate the BOM checksum for this part + # The background worker will process these when the server restarts + offload_task( + check_bom_valid, + part.pk, + force_async=True, + group='part' + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("part", "0140_part_bom_validated"), + ] + + operations = [ + migrations.RunPython(cache_bom_valid, migrations.RunPython.noop), + ] diff --git a/src/backend/InvenTree/part/models.py b/src/backend/InvenTree/part/models.py index 186b251356..08981548a1 100644 --- a/src/backend/InvenTree/part/models.py +++ b/src/backend/InvenTree/part/models.py @@ -453,6 +453,12 @@ class Part( creation_user: User who added this part to the database responsible_owner: Owner (either user or group) which is responsible for this part (optional) last_stocktake: Date at which last stocktake was performed for this Part + + BOM (Bill of Materials) related attributes: + bom_checksum: Checksum for the BOM of this part + bom_validated: Boolean field indicating if the BOM is valid (checksum matches) + bom_checked_by: User who last checked the BOM for this part + bom_checked_date: Date when the BOM was last checked """ NODE_PARENT_KEY = 'variant_of' @@ -1265,6 +1271,12 @@ class Part( help_text=_('Is this a virtual part, such as a software product or license?'), ) + bom_validated = models.BooleanField( + default=False, + verbose_name=_('BOM Validated'), + help_text=_('Is the BOM for this part valid?'), + ) + bom_checksum = models.CharField( max_length=128, blank=True, @@ -1942,31 +1954,50 @@ class Part( result_hash = hashlib.md5(str(self.id).encode()) # List *all* BOM items (including inherited ones!) - bom_items = self.get_bom_items().all().prefetch_related('sub_part') + bom_items = self.get_bom_items().all().prefetch_related('part', 'sub_part') for item in bom_items: result_hash.update(str(item.get_item_hash()).encode()) return str(result_hash.digest()) - def is_bom_valid(self): - """Check if the BOM is 'valid' - if the calculated checksum matches the stored value.""" - return self.get_bom_hash() == self.bom_checksum or not self.has_bom + def is_bom_valid(self) -> bool: + """Check if the BOM is 'valid'. + + To be "valid", the part must: + - Have a stored "bom_checksum" value + - The stored "bom_checksum" must match the calculated checksum. + + Returns: + bool: True if the BOM is valid, False otherwise + """ + if not self.bom_checksum or not self.bom_checked_date: + # If there is no BOM checksum, then the BOM is not valid + return False + + return self.get_bom_hash() == self.bom_checksum @transaction.atomic - def validate_bom(self, user): + def validate_bom(self, user, valid: bool = True): """Validate the BOM (mark the BOM as validated by the given User. + Arguments: + user: User who is validating the BOM + valid: If True, mark the BOM as valid (default=True) + - Calculates and stores the hash for the BOM - Saves the current date and the checking user """ # Validate each line item, ignoring inherited ones - bom_items = self.get_bom_items(include_inherited=False) + bom_items = self.get_bom_items(include_inherited=False).prefetch_related( + 'part', 'sub_part' + ) for item in bom_items: - item.validate_hash() + item.validate_hash(valid=valid) - self.bom_checksum = self.get_bom_hash() + self.bom_validated = valid + self.bom_checksum = self.get_bom_hash() if valid else '' self.bom_checked_by = user self.bom_checked_date = InvenTree.helpers.current_date() @@ -4252,6 +4283,7 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel): rounding_multiple: Rounding quantity when calculating the required quantity for a build note: Note field for this BOM item checksum: Validation checksum for the particular BOM line item + validated: Boolean field indicating if this BOM item is valid (checksum matches) inherited: This BomItem can be inherited by the BOMs of variant parts allow_variants: Stock for part variants can be substituted for this BomItem """ @@ -4329,26 +4361,61 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel): def delete(self): """Check if this item can be deleted.""" + import part.tasks as part_tasks + self.check_part_lock(self.part) + + assemblies = self.get_assemblies() super().delete() + for assembly in assemblies: + # Offload task to update the checksum for this assembly + InvenTree.tasks.offload_task( + part_tasks.check_bom_valid, assembly.pk, group='part' + ) + def save(self, *args, **kwargs): """Enforce 'clean' operation when saving a BomItem instance.""" + import part.tasks as part_tasks + self.clean() - self.check_part_lock(self.part) + check_lock = kwargs.pop('check_lock', True) + + if check_lock: + self.check_part_lock(self.part) + + db_instance = self.get_db_instance() # Check if the part was changed deltas = self.get_field_deltas() if 'part' in deltas and (old_part := deltas['part'].get('old', None)): - self.check_part_lock(old_part) + if check_lock: + self.check_part_lock(old_part) # Update the 'validated' field based on checksum calculation self.validated = self.is_line_valid super().save(*args, **kwargs) + # Do we need to recalculate the BOM hash for assemblies? + if not db_instance or any(f in deltas for f in self.hash_fields()): + # If this is a new BomItem, or if any of the fields used to calculate the hash have changed, + # then we need to recalculate the BOM checksum for all assemblies which use this BomItem + + assemblies = set() + + if db_instance: + # Find all assemblies which use this BomItem *after* we save + assemblies.update(db_instance.get_assemblies()) + + for assembly in assemblies: + # Offload task to update the checksum for this assembly + InvenTree.tasks.offload_task( + part_tasks.check_bom_valid, assembly.pk, group='part' + ) + def check_part_lock(self, assembly): """When editing or deleting a BOM item, check if the assembly is locked. @@ -4490,39 +4557,57 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel): help_text=_('Stock items for variant parts can be used for this BOM item'), ) - def get_item_hash(self): - """Calculate the checksum hash of this BOM line item. + def hash_fields(self) -> list[str]: + """Return a list of fields to be used for hashing this BOM item. - The hash is calculated from the following fields: - - part.pk - - sub_part.pk - - quantity - - reference - - optional - - inherited - - consumable - - allow_variants + These fields are used to calculate the checksum hash of this BOM item. """ + return [ + 'part_id', + 'sub_part_id', + 'quantity', + 'setup_quantity', + 'attrition', + 'rounding_multiple', + 'reference', + 'optional', + 'inherited', + 'consumable', + 'allow_variants', + ] + + def get_item_hash(self) -> str: + """Calculate the checksum hash of this BOM line item.""" # Seed the hash with the ID of this BOM item result_hash = hashlib.md5(b'') - # The following components are used to calculate the checksum - components = [ - self.part.pk, - self.sub_part.pk, - normalize(self.quantity), - self.setup_quantity, - self.attrition, - self.rounding_multiple, - self.reference, - self.optional, - self.inherited, - self.consumable, - self.allow_variants, - ] + for field in self.hash_fields(): + # Get the value of the field + value = getattr(self, field, None) - for component in components: - result_hash.update(str(component).encode()) + # If the value is None, use an empty string + if value is None: + value = '' + + # Normalize decimal values to ensure consistent representation + # These values are only included if they are non-zero + # This is to provide some backwards compatibility from before these fields were addede + if value is not None and field in [ + 'quantity', + 'attrition', + 'setup_quantity', + 'rounding_multiple', + ]: + try: + value = normalize(value) + + if not value or value <= 0: + continue + except Exception: + pass + + # Update the hash with the string representation of the value + result_hash.update(str(value).encode()) return str(result_hash.digest()) @@ -4537,7 +4622,8 @@ class BomItem(InvenTree.models.MetadataMixin, InvenTree.models.InvenTreeModel): else: self.checksum = '' - self.save() + # Save the BOM item (bypass lock check) + self.save(check_lock=False) @property def is_line_valid(self): diff --git a/src/backend/InvenTree/part/serializers.py b/src/backend/InvenTree/part/serializers.py index 6ac50521bf..963199a9d3 100644 --- a/src/backend/InvenTree/part/serializers.py +++ b/src/backend/InvenTree/part/serializers.py @@ -1210,6 +1210,44 @@ class PartSerializer( return self.instance +class PartBomValidateSerializer(InvenTree.serializers.InvenTreeModelSerializer): + """Serializer for Part BOM information.""" + + class Meta: + """Metaclass options.""" + + model = Part + fields = [ + 'pk', + 'bom_validated', + 'bom_checksum', + 'bom_checked_by', + 'bom_checked_by_detail', + 'bom_checked_date', + 'valid', + ] + + read_only_fields = [ + 'bom_validated', + 'bom_checksum', + 'bom_checked_by', + 'bom_checked_by_detail', + 'bom_checked_date', + ] + + valid = serializers.BooleanField( + write_only=True, + default=False, + required=False, + label=_('Valid'), + help_text=_('Validate entire Bill of Materials'), + ) + + bom_checked_by_detail = UserSerializer( + source='bom_checked_by', many=False, read_only=True, allow_null=True + ) + + class PartRequirementsSerializer(InvenTree.serializers.InvenTreeModelSerializer): """Serializer for Part requirements.""" diff --git a/src/backend/InvenTree/part/tasks.py b/src/backend/InvenTree/part/tasks.py index 15349e92e1..9a09d7f98b 100644 --- a/src/backend/InvenTree/part/tasks.py +++ b/src/backend/InvenTree/part/tasks.py @@ -3,6 +3,7 @@ from datetime import datetime, timedelta from django.core.exceptions import ValidationError +from django.db.models import Model from django.utils.translation import gettext_lazy as _ import structlog @@ -10,16 +11,12 @@ from opentelemetry import trace import common.currency import common.notifications -import company.models import InvenTree.helpers_model -import InvenTree.tasks -import part.models as part_models -import part.stocktake -import stock.models as stock_models from common.settings import get_global_setting from InvenTree.tasks import ( ScheduledTask, check_daily_holdoff, + offload_task, record_task_success, scheduled_task, ) @@ -29,7 +26,7 @@ logger = structlog.get_logger('inventree') @tracer.start_as_current_span('notify_low_stock') -def notify_low_stock(part: part_models.Part): +def notify_low_stock(part: Model): """Notify interested users that a part is 'low stock'. Rules: @@ -135,9 +132,11 @@ def notify_low_stock_if_required(part_id: int): If true, notify the users who have subscribed to the part """ + from part.models import Part + try: - part = part_models.Part.objects.get(pk=part_id) - except part_models.Part.DoesNotExist: + part = Part.objects.get(pk=part_id) + except Part.DoesNotExist: logger.warning( 'notify_low_stock_if_required: Part with ID %s does not exist', part_id ) @@ -148,7 +147,7 @@ def notify_low_stock_if_required(part_id: int): for p in parts: if p.is_part_low_on_stock(): - InvenTree.tasks.offload_task(notify_low_stock, p, group='notification') + offload_task(notify_low_stock, p, group='notification') @tracer.start_as_current_span('check_stale_stock') @@ -163,6 +162,8 @@ def check_stale_stock(): to notifications for the respective parts. Each user receives one consolidated email containing all their stale stock items. """ + from stock.models import StockItem + # Check if stock expiry functionality is enabled if not get_global_setting('STOCK_ENABLE_EXPIRY', False, cache=False): logger.info('Stock expiry functionality is not enabled - exiting') @@ -179,8 +180,8 @@ def check_stale_stock(): stale_threshold = today + timedelta(days=stale_days) # Find stock items that are stale (expiry date within STOCK_STALE_DAYS) - stale_stock_items = stock_models.StockItem.objects.filter( - stock_models.StockItem.IN_STOCK_FILTER, # Only in-stock items + stale_stock_items = StockItem.objects.filter( + StockItem.IN_STOCK_FILTER, # Only in-stock items expiry_date__isnull=False, # Must have an expiry date expiry_date__lt=stale_threshold, # Expiry date is within stale threshold ).select_related('part', 'location') # Optimize queries @@ -192,7 +193,7 @@ def check_stale_stock(): logger.info('Found %s stale stock items', stale_stock_items.count()) # Group stale stock items by user subscriptions - user_stale_items: dict[stock_models.StockItem, list[stock_models.StockItem]] = {} + user_stale_items: dict[StockItem, list[StockItem]] = {} for stock_item in stale_stock_items: # Get all subscribers for this part @@ -206,9 +207,7 @@ def check_stale_stock(): # Send one consolidated notification per user for user, items in user_stale_items.items(): try: - InvenTree.tasks.offload_task( - notify_stale_stock, user, items, group='notification' - ) + offload_task(notify_stale_stock, user, items, group='notification') except Exception as e: logger.error( 'Error scheduling stale stock notification for user %s: %s', @@ -222,7 +221,7 @@ def check_stale_stock(): @tracer.start_as_current_span('update_part_pricing') -def update_part_pricing(pricing: part_models.PartPricing, counter: int = 0): +def update_part_pricing(pricing: Model, counter: int = 0): """Update cached pricing data for the specified PartPricing instance. Arguments: @@ -251,6 +250,8 @@ def check_missing_pricing(limit=250): Arguments: limit: Maximum number of parts to process at once """ + from part.models import Part, PartPricing + # Find any parts which have 'old' pricing information days = int(get_global_setting('PRICING_UPDATE_DAYS', 30)) @@ -259,7 +260,7 @@ def check_missing_pricing(limit=250): return # Find parts for which pricing information has never been updated - results = part_models.PartPricing.objects.filter(updated=None)[:limit] + results = PartPricing.objects.filter(updated=None)[:limit] if results.count() > 0: logger.info('Found %s parts with empty pricing', results.count()) @@ -269,7 +270,7 @@ def check_missing_pricing(limit=250): stale_date = datetime.now().date() - timedelta(days=days) - results = part_models.PartPricing.objects.filter(updated__lte=stale_date)[:limit] + results = PartPricing.objects.filter(updated__lte=stale_date)[:limit] if results.count() > 0: logger.info('Found %s stale pricing entries', results.count()) @@ -279,7 +280,7 @@ def check_missing_pricing(limit=250): # Find any pricing data which is in the wrong currency currency = common.currency.currency_code_default() - results = part_models.PartPricing.objects.exclude(currency=currency) + results = PartPricing.objects.exclude(currency=currency) if results.count() > 0: logger.info('Found %s pricing entries in the wrong currency', results.count()) @@ -288,7 +289,7 @@ def check_missing_pricing(limit=250): pp.schedule_for_update() # Find any parts which do not have pricing information - results = part_models.Part.objects.filter(pricing_data=None)[:limit] + results = Part.objects.filter(pricing_data=None)[:limit] if results.count() > 0: logger.info('Found %s parts without pricing', results.count()) @@ -309,12 +310,15 @@ def scheduled_stocktake_reports(): - Delete 'old' stocktake report files after the specified period - Generate new reports at the specified period """ + import part.stocktake + from part.models import PartStocktakeReport + # First let's delete any old stocktake reports delete_n_days = int( get_global_setting('STOCKTAKE_DELETE_REPORT_DAYS', 30, cache=False) ) threshold = datetime.now() - timedelta(days=delete_n_days) - old_reports = part_models.PartStocktakeReport.objects.filter(date__lt=threshold) + old_reports = PartStocktakeReport.objects.filter(date__lt=threshold) if old_reports.count() > 0: logger.info('Deleting %s stale stocktake reports', old_reports.count()) @@ -349,12 +353,14 @@ def rebuild_parameters(template_id): This function is called when a base template is changed, which may cause the base unit to be adjusted. """ + from part.models import PartParameter, PartParameterTemplate + try: - template = part_models.PartParameterTemplate.objects.get(pk=template_id) - except part_models.PartParameterTemplate.DoesNotExist: + template = PartParameterTemplate.objects.get(pk=template_id) + except PartParameterTemplate.DoesNotExist: return - parameters = part_models.PartParameter.objects.filter(template=template) + parameters = PartParameter.objects.filter(template=template) n = 0 @@ -373,18 +379,21 @@ def rebuild_parameters(template_id): @tracer.start_as_current_span('rebuild_supplier_parts') -def rebuild_supplier_parts(part_id): +def rebuild_supplier_parts(part_id: int): """Rebuild all SupplierPart objects for a given part. This function is called when a bart part is changed, which may cause the native units of any supplier parts to be updated """ + from company.models import SupplierPart + from part.models import Part + try: - prt = part_models.Part.objects.get(pk=part_id) - except part_models.Part.DoesNotExist: + prt = Part.objects.get(pk=part_id) + except Part.DoesNotExist: return - supplier_parts = company.models.SupplierPart.objects.filter(part=prt) + supplier_parts = SupplierPart.objects.filter(part=prt) n = supplier_parts.count() @@ -398,3 +407,25 @@ def rebuild_supplier_parts(part_id): if n > 0: logger.info("Rebuilt %s supplier parts for part '%s'", n, prt.name) + + +@tracer.start_as_current_span('check_bom_valid') +def check_bom_valid(part_id: int): + """Recalculate the BOM checksum for all assemblies which include the specified Part. + + Arguments: + part_id: The ID of the part for which to recalculate the BOM checksum. + """ + from part.models import Part + + try: + part = Part.objects.get(pk=part_id) + except Part.DoesNotExist: + logger.warning('check_bom_valid: Part with ID %s does not exist', part_id) + return + + valid = part.is_bom_valid() + + if valid != part.bom_validated: + part.bom_validated = valid + part.save() diff --git a/src/backend/InvenTree/part/test_api.py b/src/backend/InvenTree/part/test_api.py index 1cca5dd9cf..bea0a1ee62 100644 --- a/src/backend/InvenTree/part/test_api.py +++ b/src/backend/InvenTree/part/test_api.py @@ -911,20 +911,89 @@ class PartAPITest(PartAPITestBase): """Test the 'bom_valid' Part API filter.""" url = reverse('api-part-list') - n = Part.objects.filter(active=True, assembly=True).count() + # Create a new assembly + assembly = Part.objects.create( + name='Test Assembly', + description='A test assembly with a valid BOM', + category=PartCategory.objects.first(), + assembly=True, + active=True, + ) + + sub_part = Part.objects.create( + name='Sub Part', + description='A sub part for the assembly', + category=PartCategory.objects.first(), + component=True, + assembly=False, + active=True, + ) + + assembly.refresh_from_db() + sub_part.refresh_from_db() + + # Link the sub part to the assembly via a BOM + bom_item = BomItem.objects.create(part=assembly, sub_part=sub_part, quantity=10) + + filters = {'active': True, 'assembly': True, 'bom_valid': True} # Initially, there are no parts with a valid BOM - response = self.get(url, {'bom_valid': False}, expected_code=200) - n1 = len(response.data) + response = self.get(url, filters) - for item in response.data: - self.assertTrue(item['assembly']) - self.assertTrue(item['active']) + self.assertEqual(len(response.data), 0) - response = self.get(url, {'bom_valid': True}, expected_code=200) - n2 = len(response.data) + # Validate the BOM assembly + assembly.validate_bom(self.user) - self.assertEqual(n1 + n2, n) + response = self.get(url, filters) + + self.assertEqual(len(response.data), 1) + self.assertEqual(response.data[0]['pk'], assembly.pk) + + # Adjust the 'quantity' of the BOM item to make it invalid + bom_item.quantity = 15 + bom_item.save() + + response = self.get(url, filters) + self.assertEqual(len(response.data), 0) + + # Adjust it back again - should be valid again + bom_item.quantity = 10 + bom_item.save() + + response = self.get(url, filters) + self.assertEqual(len(response.data), 1) + + # Test the BOM validation API endpoint + bom_url = reverse('api-part-bom-validate', kwargs={'pk': assembly.pk}) + data = self.get(bom_url, expected_code=200).data + + self.assertEqual(data['bom_validated'], True) + self.assertEqual(data['bom_checked_by'], self.user.pk) + self.assertEqual(data['bom_checked_by_detail']['username'], self.user.username) + self.assertIsNotNone(data['bom_checked_date']) + + # Now, let's try to validate and invalidate the assembly BOM via the API + bom_item.quantity = 99 + bom_item.save() + + data = self.get(bom_url, expected_code=200).data + self.assertEqual(data['bom_validated'], False) + + self.patch(bom_url, {'valid': True}, expected_code=200) + data = self.get(bom_url, expected_code=200).data + self.assertEqual(data['bom_validated'], True) + + assembly.refresh_from_db() + self.assertTrue(assembly.bom_validated) + + # And, we can also invalidate the BOM via the API + self.patch(bom_url, {'valid': False}, expected_code=200) + data = self.get(bom_url, expected_code=200).data + self.assertEqual(data['bom_validated'], False) + + assembly.refresh_from_db() + self.assertFalse(assembly.bom_validated) def test_filter_by_starred(self): """Test by 'starred' filter.""" diff --git a/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx b/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx index 4054aa7feb..aa2c414cdd 100644 --- a/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx +++ b/src/frontend/src/components/dashboard/DashboardWidgetLibrary.tsx @@ -33,8 +33,18 @@ export function BuiltinQueryCountWidgets(): DashboardWidgetProps[] { modelType: ModelType.partcategory, params: { starred: true } }), + QueryCountDashboardWidget({ + label: 'invalid-bom', + title: t`Invalid BOMs`, + description: t`Assemblies requiring bill of materials validation`, + modelType: ModelType.part, + params: { + active: true, // Only show active parts + assembly: true, // Only show parts which are assemblies + bom_valid: false // Only show parts with invalid BOMs + } + }), // TODO: 'latest parts' - // TODO: 'BOM waiting validation' // TODO: 'recently updated stock' QueryCountDashboardWidget({ title: t`Low Stock`, diff --git a/src/frontend/src/components/dashboard/widgets/QueryCountDashboardWidget.tsx b/src/frontend/src/components/dashboard/widgets/QueryCountDashboardWidget.tsx index e0da07a1f5..af38a5ce88 100644 --- a/src/frontend/src/components/dashboard/widgets/QueryCountDashboardWidget.tsx +++ b/src/frontend/src/components/dashboard/widgets/QueryCountDashboardWidget.tsx @@ -95,7 +95,7 @@ function QueryCountWidget({ }, [query.isFetching, query.isError, query.data]); return ( - + } title={t`Validate BOM`}> + {t`Do you want to validate the bill of materials for this assembly?`} + + ), + successMessage: t`BOM validated`, + onFormSuccess: () => { + bomInformationQuery.refetch(); + } + }); + + // Display information about the "validation" state of the BOM for this assembly + const bomValidIcon: ReactNode = useMemo(() => { + if (bomInformationQuery.isFetching) { + return ; + } + + let icon: ReactNode; + let color: MantineColor; + let title = ''; + let description = ''; + + if (bomInformation?.bom_validated) { + color = 'green'; + icon = ; + title = t`BOM Validated`; + description = t`The Bill of Materials for this part has been validated`; + } else if (bomInformation?.bom_checked_date) { + color = 'yellow'; + icon = ; + title = t`BOM Not Validated`; + description = t`The Bill of Materials for this part has previously been checked, but requires revalidation`; + } else { + color = 'red'; + icon = ; + title = t`BOM Not Validated`; + description = t`The Bill of Materials for this part has not yet been validated`; + } + + return ( + + {!bomInformation.bom_validated && ( + } + color='green' + tooltip={t`Validate BOM`} + onClick={validateBom.open} + /> + )} + + + + {icon} + + + + + + {description} + {bomInformation?.bom_checked_date && ( + + {t`Validated On`}: {bomInformation.bom_checked_date} + + )} + {bomInformation?.bom_checked_by_detail && ( + + {t`Validated By`}: + + + )} + + + + + + ); + }, [bomInformation, bomInformationQuery.isFetching]); + // Part data panels (recalculate when part data changes) const partPanels: PanelType[] = useMemo(() => { return [ @@ -712,6 +825,7 @@ export default function PartDetail() { { name: 'bom', label: t`Bill of Materials`, + controls: bomValidIcon, icon: , hidden: !part.assembly, content: part?.pk ? ( @@ -818,7 +932,15 @@ export default function PartDetail() { model_id: part?.pk }) ]; - }, [id, part, user, globalSettings, userSettings, detailsPanel]); + }, [ + id, + part, + user, + bomValidIcon, + globalSettings, + userSettings, + detailsPanel + ]); const breadcrumbs = useMemo(() => { return [ @@ -1065,6 +1187,7 @@ export default function PartDetail() { <> {editPart.modal} {deletePart.modal} + {validateBom.modal} {duplicatePart.modal} {orderPartsWizard.wizard} {findBySerialNumber.modal} diff --git a/src/frontend/src/tables/bom/BomTable.tsx b/src/frontend/src/tables/bom/BomTable.tsx index 3d68353330..d2e4c40a4e 100644 --- a/src/frontend/src/tables/bom/BomTable.tsx +++ b/src/frontend/src/tables/bom/BomTable.tsx @@ -1,9 +1,10 @@ import { t } from '@lingui/core/macro'; -import { Alert, Group, Stack, Text } from '@mantine/core'; +import { ActionIcon, Alert, Group, Stack, Text, Tooltip } from '@mantine/core'; import { showNotification } from '@mantine/notifications'; import { IconArrowRight, IconCircleCheck, + IconExclamationCircle, IconFileArrowLeft, IconLock, IconSwitch3 @@ -34,7 +35,6 @@ import { formatDecimal, formatPriceRange } from '../../defaults/formatters'; import { bomItemFields, useEditBomSubstitutesForm } from '../../forms/BomForms'; import { dataImporterSessionFields } from '../../forms/ImporterForms'; import { - useApiFormModal, useCreateApiFormModal, useDeleteApiFormModal, useEditApiFormModal @@ -105,17 +105,26 @@ export function BomTable({ return ( part && ( - - } - extra={extra} - title={t`Part Information`} - /> + + + } + extra={extra} + title={t`Part Information`} + /> + {!record.validated && ( + + + + + + )} + ) ); } @@ -499,26 +508,6 @@ export function BomTable({ } }); - const validateBom = useApiFormModal({ - url: ApiEndpoints.bom_validate, - method: 'PUT', - fields: { - valid: { - hidden: true, - value: true - } - }, - title: t`Validate BOM`, - pk: partId, - preFormContent: ( - } title={t`Validate BOM`}> - {t`Do you want to validate the bill of materials for this assembly?`} - - ), - successMessage: t`BOM validated`, - onFormSuccess: () => table.refreshTable() - }); - const validateBomItem = useCallback((record: any) => { const url = apiUrl(ApiEndpoints.bom_item_validate, record.pk); @@ -608,13 +597,6 @@ export function BomTable({ icon={} onClick={() => importBomItem.open()} />, -