From 0e28fccb34f95cf301c7b2014f6d228d169b549c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Tue, 29 Aug 2017 18:15:57 +0530 Subject: [PATCH] [Enahance] Update variants fields defined in the Item Varianst Settings, if template updated --- erpnext/config/stock.py | 5 + erpnext/controllers/item_variant.py | 4 +- .../controllers/tests/test_item_variant.py | 3 + .../img/stock/item_variants_settings.png | Bin 0 -> 46358 bytes .../manual/en/stock/item/item-variants.md | 9 +- erpnext/stock/doctype/item/item.js | 6 + erpnext/stock/doctype/item/item.py | 16 ++ erpnext/stock/doctype/item/test_item.py | 38 +++++ .../doctype/item_variant_settings/__init__.py | 0 .../item_variant_settings.js | 18 +++ .../item_variant_settings.json | 143 ++++++++++++++++++ .../item_variant_settings.py | 9 ++ .../test_item_variant_settings.js | 23 +++ .../doctype/stock_entry/test_stock_entry.py | 14 ++ .../stock/doctype/variant_field/__init__.py | 0 .../variant_field/test_variant_field.js | 23 +++ .../variant_field/test_variant_field.py | 9 ++ .../doctype/variant_field/variant_field.js | 8 + .../doctype/variant_field/variant_field.json | 72 +++++++++ .../doctype/variant_field/variant_field.py | 9 ++ 20 files changed, 406 insertions(+), 3 deletions(-) create mode 100644 erpnext/docs/assets/img/stock/item_variants_settings.png create mode 100644 erpnext/stock/doctype/item_variant_settings/__init__.py create mode 100644 erpnext/stock/doctype/item_variant_settings/item_variant_settings.js create mode 100644 erpnext/stock/doctype/item_variant_settings/item_variant_settings.json create mode 100644 erpnext/stock/doctype/item_variant_settings/item_variant_settings.py create mode 100644 erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js create mode 100644 erpnext/stock/doctype/variant_field/__init__.py create mode 100644 erpnext/stock/doctype/variant_field/test_variant_field.js create mode 100644 erpnext/stock/doctype/variant_field/test_variant_field.py create mode 100644 erpnext/stock/doctype/variant_field/variant_field.js create mode 100644 erpnext/stock/doctype/variant_field/variant_field.json create mode 100644 erpnext/stock/doctype/variant_field/variant_field.py diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index a98c40e091d..d6b18fdec04 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -105,6 +105,11 @@ def get_data(): "name": "Pricing Rule", "description": _("Rules for applying pricing and discount.") }, + { + "type": "doctype", + "name": "Item Variant Settings", + "description": _("Item Variant Settings."), + }, ] }, diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py index 967c1339f1c..ff11eb258de 100644 --- a/erpnext/controllers/item_variant.py +++ b/erpnext/controllers/item_variant.py @@ -180,10 +180,10 @@ def copy_attributes_to_variant(item, variant): # don't copy manufacturer values if based on part no exclude_fields += ['manufacturer', 'manufacturer_part_no'] + allow_fields = [d.field_name for d in frappe.get_all("Variant Field", fields = ['field_name'])] for field in item.meta.fields: # "Table" is part of `no_value_field` but we shouldn't ignore tables - if (field.fieldtype == 'Table' or field.fieldtype not in no_value_fields) \ - and (not field.no_copy) and field.fieldname not in exclude_fields: + if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields: if variant.get(field.fieldname) != item.get(field.fieldname): variant.set(field.fieldname, item.get(field.fieldname)) variant.variant_of = item.name diff --git a/erpnext/controllers/tests/test_item_variant.py b/erpnext/controllers/tests/test_item_variant.py index 9fc45d234c2..34d63603b1e 100644 --- a/erpnext/controllers/tests/test_item_variant.py +++ b/erpnext/controllers/tests/test_item_variant.py @@ -4,6 +4,7 @@ import frappe import json import unittest +from erpnext.stock.doctype.item.test_item import set_item_variant_settings from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code # python 3 compatibility stuff @@ -54,5 +55,7 @@ def make_item_variant(): class TestItemVariant(unittest.TestCase): def test_tables_in_template_copied_to_variant(self): + fields = [{'field_name': 'quality_parameters'}] + set_item_variant_settings(fields) variant = make_item_variant() self.assertNotEqual(variant.get("quality_parameters"), []) diff --git a/erpnext/docs/assets/img/stock/item_variants_settings.png b/erpnext/docs/assets/img/stock/item_variants_settings.png new file mode 100644 index 0000000000000000000000000000000000000000..82b909fb066ed9b133cb7150cec0bfbd566b6109 GIT binary patch literal 46358 zcmeFYRa{(K(g#YA5Ii9Pf&~k~Avld&a2ls^cZY`H5Foe{B)GdYPH+eiEV#RCjtExIw5h#g;PK1ttfPf_}C9aHsfV_)<@XYST z^QSv}M>OFG2ybaD#l#e)#l*-Jo$SplZA=jmGy)Uc(RkEWW*!A$i~U6A=ksY z>4$^XndQ4O&u6O!)PY}-vn-yyz(l9gCbl@|z} zu}4IlfR5#Ty%&7M<8eiFXN_XY!Lgh8nL2Ce@p>hE`ae2C`Zd#<08NrPnH}n7t;WE*8`Gu=}H;&7TaS5``HR7*D zA1K`*%{cu?2v=O<=B@??C-@m+l_3kh*QXD@UQO^zX-fCU9zNqaW;flH(HI2f$wx=t z2N>?#mk))}x>EUP6vSHgk%Y+&Y>x)tnB)RigUGqT7|0V;*P)Q!`Um`XS7_X|cjZFR zcO?P82@Rh2IeEr7%fF2HO&tbUTmeG46RBexY+rfDH`;%XJ+wcNz^;EdB*R-HEHOVv zy>lZ03RZl2U{Hcki@AFaGBXOcxG#*HD)V$UYe9 zV2#iOmdYL>o>YHnzmOM$Ft~DiLOp_j1Wk7jvJaG)y{|gN5ks^wf47R`^WJ!n^x?!v z>U;hnHa7>+cSO=G3^x%TdCrE8eHJmkl4e9va!iXlTSp0t7_WfiS!H0?Do0dp9 zodn44{-f=m=NHN|$Pr`h?-P#rslUv8w(hPE6uJ&s73SdcV9~;|4ddsGr4Lt^D7~-R z$iJCjP{%0^=j`4${`7d}AgO^$o`C7~*nB`H`&&|uY#V8t+6?oY=sS6NM&I{_x6k3E z$pYVJug>o7L{{9++P|M8vVRPk-FtkOPHZG_E{V$YwNhQ!I(O>T!wXjq_7qa7tgYBs zi)_xADmwD+XFoi931bhsWu! z-`1KPQ14&(pmTR{ej%{)w_anuUc-MNMti*wds4^^wY7T)AzKmR9mgAIABR4cyN=sG z-#om0ZkQ3=RnPSm=pglGZf1cd0)zpG6>yLAU!-Y`fTAp7GIk66MVAk7sztmOiqGwpE zuiP**{Pa$6TSSbEalM{{4PKRry!;vv@)D{0`Khpf49|NEIx$ZQfxb7@L22ZmK4NtL zpV_&Ji1E+U1DHiYSx&oLs;C0~8KT~!*1G~6NboP}N*}|}dqUPl(X#Ut2WXk%(FXAA z$hIPRzlKRr=L}~#DSav;F0z`Wnq;4poqW5`ZuRW>N9y00=bgBfY-LE=9mACbazVyx zsywL7{tRm#Cgu7a#wUy|{=aevM_Vk=-V0)leYSADpm1iqeRE6MgsB^8C)O_9E;*9z zYZ&qt7{McfXZ+Q%+s&AgRr))L9No5H0qLC5x9^`A@dqRDe$i8SPM|@91^fj9nL5-B zQ>(mZ9S;z(gN?gFHzpnNd2pFAlfE{8Y4)@JdAaeyanMbMA4d-#67Bj6b#3Dma1Fhd z@DlPtZZ(XINec_ z=j7+~7zE+?;WV=7vedFdgSZT+r5{RZtm&=spt93yTzXsrT)y>~^+ffrK65m`ZN~F_ ze@A`w`AQR80-F=tHA*##kEAP&7RU;;9;Qm`Nz+Zk&{@=Zu0x?i@I$N7tTDNfvl0HI z<_B@3x!YT}tF!AflT(gUuXWS)sdcQ?p)K2u^o@;8_DzhP=ASb|4fV2aNlO*ix@yf5 z*J$?x_j>o5_e}R-M1DjwL`f8S!i~bO zc)Hz8Z0mm#^`cs<@d7~OZDjX#$jV9)E; zdP@1rh{{rm=7_GhzOTtEHfb@5+yv1*qcbnYEXFR@A4ea5HcmZmZ?bC|VY+U*Z|c(* z*55R^-H+Y(YU^Svs()(0JBc6(7T^Bub3*@7k{7qDp`fz#8T<>#Wq0 zg>p%Vd7-P4y{d0gTZ2#FLznccEWaLHgq;{p>B3!ws|&bfd#P3n+ARhj(3W1^xEz|+Jve?Bf#~p%r1Jf zg$*p8D|zAmvT=i}K5Ot`n2ZAd5WpJ`KejH*^oKeZ=V#ZOcl2b2f_P$jS5SFMTt3)tG(LQJx>5n_A-B`KPd0J}i{i@5d zHa49$wcV20+L{4C3PD-)KN-yIek|^`7JJszE9aca;DOcVnH(7IrwbP<8c>hFFIqO1 zdNrJ7thV0YVZu~Ov-gYk8;LSgsN97yw|2Dg83`l^kO*cHm6PTY2i4<2yy~Rth0~NXj?hNp9OBD%IiJ=g%*91aidg9d z2;xU&B|SZa`ybB{#?TO+zmKL;Jl@oA$H27BCq3!j{eS{ zHy@B7bjs*LI>eZc+)5+Hy1`TPR`!kcYd)-CBy+SGg$#j~P_L1jJ>)VJ1q^}L&vKlM z^wxJ=U1b=hL#4F)yZhDqT2+PV251jKK(%6Z@AlxLik2BQs!!+j)7qph6*(Er51Ij5 zT}sA%;vJJ6zeo6&)-$#kN==E)v;lk=cf!FFQ3=42w3Xvju;zUG=a)ZlrEKhQkF?%R zXP~z7w@M3gwRzsV!6_cAz7%u}ilB80QX~e)_qSwUkJ=jstVO;ujZF?V&PB{gk9te0 z7OfPf#$SM2=(6~JnWB@O^`JX6uk0IjzpF}$O7r-0g;qWRmu=bG=o$>d@Ev?Mf@*c} z=uWbetNzCyyBV&GLJe7G$|`F$gGivD@Lyr(GTX9i)^essTk|uz^py4{OD!sMpi4N{ z5@};I7fkdxvz!@H4DnW-(OS7*73qQpt3__f_Y9##S8FS4N7vufE%2)!7#wQ2vt88| zo}boCS4fT!H`8E)z$KoXLa2k)vw9jiRx!vGw78lqsH!5kn!@-gvz-ipQk|Iq;*% zcPD}b4yAFy?#hIze93wOxgemPk_;SN6?T*;ZjLf1ZX{9B|7s#|u50a2Q)T z_98zssE0BshieylHurjuctNO^u?5H5RNHENS|2W-ZTY#2>~)?}Y>F|P3@K)e)>S60 zaZ|-nE@4AINlRjotnm)ucfFDI#PqabJ?c6gzFt6^3yr0@q9M$jl;@JC$bYBM?6H5k zH^#0%K`_j0%9wVS3`n}9_T<`u-fUsi5~!W|pUGl0u|yIK>twW?RqVPr>i2&ujm*cI z;T>$#*(sg%rFz_k(*M%lZuk~EVZGI6QYR^|XX`7l?dR3i8B9YYn@W^IOr~oOSRk6f zpL+-6Y0X`gKAn(FH_r%ko#Rj1m2ky@E$+!f&!G(-+1y#MajQ{V+IzKi=(jFb`xs*@oYVnbww&~s}xtTSwrE$&Q zX`;Dhiaz+bO?`vF+wN4T3r3h*@&5QEZHVMNDm*XF@I+40as1%GvHdw#u zs9>j#^B53|}+< zZ%P|Mer_X$ZVQt|!-_7PT%FwH+~8gr|_<2t<9{WXeBk_mr-I z5$6*zR?1>Zv_RA3`0QxDiUHrqj8P7KRi@<}9y(Il$8@(y4 z^Va@%u^y6eK_^OE-u>{K=pMY%^2?PQQoB+Eyk~vlXL5HFQt8jgbD#IKlOc47B6P&P z_)?B8oL+`Z8z6&llX>mwD=f7hD(M6?>p&9ss()8oRc6#u&-X9z0lIngK3jn^oU#{LygMFNm1c028y2dAzZ6E!pdCNY zR1+e7#CXOaa?}YAzUap?JX=$X#SONXJO+#tzxIk?xBDo_Fm}`W*rfaU=HtO4aiu7BSrG(M8BCle)0;^1Dmi7U!y13TcM|hzf zFp7jtj%?!h{38-G`O9v9Ohv?fVVv*C#4p+3qY#SeDI%&O3-}9WjnW#%(}hJeg;;&^ z4geUwzNXbdQOKGbo8IMc=6n9L$T-U2oxyY!(JbY!>rS&JP-6r1FIMW&Adonq;x)?vq3|!sj#w;nI{_sPd#VGqi5b_MLioc7P zNscZ>!WPh{$UKSZEy|kftYlcEJo!-$mAEt1fxX2~T}(0;ItG2qBu8)df+0*GinQ~v z2XF24+Tb1`5dG7$XP-`}TJk3Iy@c~cFGDP(@1;l;x+Ev$prXxEu8Dg74*;>3RIybN z(y4n);5QiDvfNNYF7D5^*sMT_Va|$IM_L)+7C*0o=DZtCluASf|CPWtw8uAdFLUt; zX3Oc;=m}}m$aNXcMXw6^oBd;rT9ixC6H3D8nOsQDD&~voWcgH*G$G?1b0Nb&MlN#P zEA!Lq%7!Y_YrKAqd~BET2NSE#_Fl`CIwSLC?s`a{ola^gK#c<*i$ zs)AT%sH?oQ{=UnBx!2eun|I&!)b>ea13uNn@y}i^!ES|)Z>K6ndtViHM|#cLeWo7q zIYB4zi|CWgCZVKl2+Mbt7R?Jy9i1?Z6wS|?N19`r#TwKVL1kAEy0WbbZ+&hp7A^gS z%B3GS7!O<5&|e>FEgRgut>I#=na=ga&w#Sp<$k?g?#QtL@UmOq}burhyS`8V$;QvN^g@+w-onc8TGTiTl1fuF_@WM$*z z;{ON1|2+E7kbff8{0}KR*Iy|A^yFWZ{49TH@F$J_3D-aFKH(*Z&d>61>;=(9I{~~1 z2*L=`;v%YU&kmN*yws)|A0Ju1roF@sdN2I`B{?lJo>5t&n+TH_N{#^|Q`DPB|9ghv z>9DDJqx>YR9;>mET7n*o^)DJWFEPe*R0A2fRpk^z3|^2MKBqv7K^j}B8SxUu{rQH? zYq}I*e%v~(XMG403>Cn!KAhU*lcl8if`Isf{J+0C2wz!vNND9NA-@zxc=kVECof)R zZ3O%`(LWe<5WXQ7c71+oN%X&DPplA~y`KLqOP&Bki%rT0NRrJX~)^JqvsmY{%Dci?^ z=>nch3AGn4JKxm0*7mz50zDZx{zi(lyf3fQz+}uq{aX7wdlh}T0nR{&qi-a11Js=5 zlHm94aCLgHEP?sUymMb7=-A=8YqIPAKxYwwINaCxD}}!Kv-zfuH^wjkHiGtn7ykKH z;SBu=)o=eh@TF)L>&UaRgZXsJ@=ME2o!#Kyd#>IoiNy1;i7NRoN6tmcG=F151Vqj! zQ-H*1!~WLB4!p=OM$@s?_Qn6kz`|KUh(lHyQARH~y!_6T50`6oo3G;g@V~`U>d5S- zmE~Q35Ar?Kq4=L+(^&nU<7F|VIYw8cs;zb%6eQ27BoKN2vQg0O+DhegKRF!0p8Q_G zjSo}@X`kke*Rn`%Nbr~N>zf^yNpisTw41Nr6y^kuSaiRZO?YX$RyLe_d4(}kQo@|R zUZ7y>ax|oManB+Dd$_!1dDle#2h1muWU>70UQ1wq`Yw986cuCx`YF=D_Dg;&kJxkC z3daKTfBXCPO)Ou8?a=Fy>ge zu*%i$ZeX*->V4?BOG!z~#B3qEo=df}yM1vFg(aMK592OOn)e(Li$@srs^RzR(U?fy zxGRsskLrxd!LW%SoJ9O#vPjJM$&Pe4gI1|l}QXW$*UID810qjKN1vgMIlDe~Tm*>)sX3O>rRlF6x((0A+V13k>!f|FIutZ-?@9HTS zuAF=xizz#`lXQrQFStKxfUo_R73XQOp`{bZ>i+E5WOj}7bNyEz1t(6v_p{XbdyNFX#Sw2- z+Bav}_ngcgfUdvMf!?|o_u)jTYC{+6#&vsx_JqpJ^d<}Knl3&O&w)PT#luCP8f2vj z@^2K@NdMxMMH7fs_%Yy55>~#B#fdwcmQs)5*7@zwm2`-_&|&_t-|ppRngvInZqR&CI)P8mwoJew&uxk z{MQclTBT_$ZOT>fsAik@>N3X|lb4M89lfof8wRl2kBR-d`$WBn`?atuk`EZP#KwG6N;~+ z)-^r2TJ`nT3pJXJ3^Nw6Hf8EGZehZmv~mt&%k4ZiFfVVqg(vHD2E%Ly1fr|rLC?v_ zIx81?v#?y@O_2-Z0>FlDK}YJUP@cw;Di`~^0lt!P(8l+E7-|4`xGLnWX@tA@w=;8RT&no|x&5|) zj-o}Df^+t`Kd5eCgqvW5^A{cG(%p^AaaB`#T-%7~tn~2ir#l@y+l5?RzH}(;VK^`E zN>wy)rTpZ4_Ro{v@7Md$0=2Ru5_aWbsirLkxPrXU5~8hh)I&KnL;Ki}_q-eN!gcmT zb*WMFibenA%HFB3O=X0XY2;o0QA<k-2;z^DRcLfn&&j&E(o%rX$M4^CI`{ zY4R*|RmniPqA$qa_+WkSu5Y>M%@T<6wPkCaV^4v_!eW=bk6wLs0m3uHdDqH`pEdY` z?ofSrB`NTr*U~~iRmaW}HsF5S^EK&bhCG4tC?f03{%NlyLq9s_ z$Ux~nUTp>6lRUc=XzFt!0ntZ|kLnMx`ZQA+a5~~|2?d060KY3l;^MB0U-75=R(_fH z$qgtAIB)vKT&OBw2a%a)O}|u6(*zZsf!q{X@6vHN>W@RveWi5w1+p@6&t_V)*&?!l4;V$H zDoWzsS>3?|v|D$m^AbM|JiuqC#3lMMX8zNFklK0PLXD7l!tya*e1z_y#gR z(5Z&JlQwbA=;nGeL{ae|!{E81Q+7;x39<}^lIE}m1gv-{P| z{W+BO3!*(aiZk^%Pp7`ENMYdyE^p_M-X4P<&5joo$O6u(T|n^y0d{#8m)5sN?GuwT z9+zanE<1Hebj#c)B1coVgOd1eO*>qd07&j|^Zf^bSzQm8E1I_B4BLzDOuI4nVgU|| zMS-w}47rF__mvAzUtvNdgzy}yIxe?)d=l*vk8-t8e@F$tT|M2 z_)+7wFtSM7USDeg&|Cgu?;4rt{sk{cMo7S>J=`5*n{MH8&|D?eV5oLkuViE#T-aYXkpY730JOGU=S(JyVUg-VaDTOAM%pARmn*29oqllq*^Hrk zIIu~ecN$SD9Tj@2eJ5>A0PWNq^pD(PnGTA3B}3#8ew3)&wHPyIJ=RYo#MtGZV3+?Z z7*#O<&#gg+4$0ZIIqBl}a7N z(@U@i@#3)sCfd8WYWSsHN^U4bFQ?GHwk@(v{RX;Ykpdq(FI@Hxwlt7zUgr{+Drb~N zQQIy=@y~hymwLGRTQ(maMU%Levf0*N{uB}_ZM^E8&Ro9V(ZZ6yj3Uv!3YM(v%Uo+| zke$eLq_Rn=oD($6#Npd4XQ_?pk$HSV$uC>eIKjoS`ZGkgptuGfRpQO|NP0m_Y6j@| z#LZ&1@!-#ASYbH9Y?U=+x&j8Yb1tY7yiA%YX`wa8n~A8+&`M5%Z02>-52ic+%EoP6 zuNY4sDzrM1)Q7eJ!)rxXCPofVr88&g3m!I+=c@|^4TK)3u9oY&3w++yM-Dnix#GsM z-*NKO5-7Kn42^N?K>JfJ_fri&Nvya<-n$zT7FrgDM}DcXAH0Rzw_rI}(az&h<+bS^ zYC7XcNBe&8ew5ksy?vTapq>5ni)56|+s{c&?-!y`C}(!uqWK`@Oa?Fv8jBr;&&qv2 za37VHqbdH_LWgOnp`Y&>DS>kh8nZLscr7P&jhPg;7noMc2q|*ClH zUY27r&K;uXRYVI`{=kX6{skU-OQd^8!L`|QUnsgca@YB zS?C4#a;&30lFr(VoYo*Ly2CjKe4f{@l)ZYmo3Z;MvE);Jyo}PUcB7sq(aQ7b(~SJ+ zqVN~i+D$zsE#8sy4HD86PQBMh)mi1o;HMgH=Uw4$r-yL{ioO78Js-c@7iC+b%QyO? zu5%Zf)5BEz+-9}6d$?QJO*AL&10H> zaodhefL+55m7B`)?GQd<9r)EQmf4qedm zEOw3uJ&Y9I%USsTOu1^35K)VM^OXj1Rj2g@cQ49n~ZO&Ha8OR z)2w|De$wbtDkm-73c&Y9SoT?LLC~~PvZ^)q3`2_Ll=1~n8|ZL93j@?^$8tH=3G&#` z#xtwO+9qA8=AG);wtqd50pXGwvi&Mf-A@*Bj=E>X$Ft+S7bbVtWVZN;OWvjN(Ek3X znZ}&L`1zHwU+*bM+u-mE3cdOK?hiaJwJC{dz%v0F_!6R%cd#rsy!&7@cL7vr@c3^Kfbp)bRyJ3SGrf05t_;uo^zDgTs#ROAXQ~I*zkj+0AOY z!r_3+bh0{NO#)xKzP`TJWrnEvi(8x(cDoSYiDjw;XJ8-1p_!~ClZI!9A2__k@v8wUUoAq+1Vk(ao2WP$a&sVpXFaeZ>fjX z6q|A*rQuL9paPWkWF7%8h+a%`Z5_mXSTe*A`;o3De7|Rq>|1JfTqVG1*s1MdeC8He zYhJFFgm1dwJXL1iG7-yk0^sXzUMZr;Sk+l&H}*XWUVcN=eHj&ZF?o!=2O*^egRY}z ztIkTHc|t7SHsm}z0&nawY_8c4y$9coIBUY5Yl~BEo_r0Kxbp|M1#nq`?~ww;WUjf5 z<2{s$Kv5DJ2oAqWkLixXhpq^2?7XYp+5|O?gu++GJBG7eC-hN||8fErK77%A*($R` zs#R{2iuT#qjK!MceKr2KoHF2eW`>V$>ea$2Tz`M-j%)&w=%Zr0qf`Gq=ixMQ`j|if@QT<+N4$m1^44*_%)+9(Z7ZlFzr!{JsVMuwU zC6%V#5Bco{Pu0GOGY#j`v2S{a9G+-cqoT8#&9FmEV>Qq6cfZ>l;> z$8I6cKp4Az;NFW>!3&SO0uRkIp1{l$IJ{d z`INM6N3kJqrrwM3$Rvq>PNn5Ll1t<|A!@H)C?`*5?$-i&w9Q@r zDSHRw)pu`ZGZ!pduy2|J7b&ECstLPqOlZH899PTm^oV}gaQBRx9I>5NSHQ?ydTC>A zo_$faby?TKz-WIz(&CP4b1xf?Of1bkjqhEFW|C=JZ;<9)uG7HpcGNwSXI@tEFdPk@ zMg07DZT&H`JO$%rR%xS}lr#nnS;B$JF0z3rD*9Y7AVyT;03|AYRiekTg3LF*^sE2q zs2Vp`3oWan=khtsVsX*H;6sekMkG)M-Sf=!k|x8;)69MToAymOLFtJ>-V&QNYPvBNFe2I2&jM< zDwLtT$6Bp?JsF%I4b}5=Ke`?>j@IswP1F*?Iqa9k3^BAOQ;@L!oa7WPM|+<>1%$yb zo&%o!*kU*@v2AEV(5s_)Ct1L7`JokNcJfuQ-A(Nn<+E`-=Z3uL4NUuTJiY#LLV2f3 zl=;reqLYh8tw^jw&%@`z*YkW*3^wGe;>x?j5k1CAK!1_$@?k~qHo&BcYh)LY6gf|O zKPu6C!&^Icrl(Nff{kxpJLWj-Xda(lbC@?nRzr4db}}{GzTh)lN#;*_A?`PUbW*0vM0Az?>_Qz9-?Rq+G=kt*k(sriZtxL zx*bJy&OTKuv<2;OL_Q8uX>qNOb*PTEIV8e4M75jBMuj9SdFqc6d^Gwt7l2 z^X6z9x2`r6)Dq7LPFj6M3@ZHlPLH|95z8UblB<0c^7<(C>1rmKJ}w2f>3B{SV!(vXq!?w+<+8)8 zsC|uE_-kQ#W$~7AUfFQ28+5XJVKRFn>k*mKgUyp7wGyTut6pAVVqs=FvmiqFVJ4C2 zHzvV2Ta2(Dxm1VA!BsL%wP!fGs$0QGoTLmwiYMY+l%lOyR`V-PoZfP7WQ?;J&Fd{O*R(tdP+jmVK`zaR& z=^089H(X_anBSk*TT>2he*dl-88*TLvG#EK!ZSKF6KqKs+N;4<9={Xj^^HX(0=9>$ z_gca+Hj|^Y^-9v7Qse=H0qqQ(9!v9;l%Vj$o!RyGjVb)6pYQ_TfHSm{%yJc_|FSp! z;YTrHd2wlsbtt4k zH|BZy@H4Hl;~N-wb&(W*+o6$v%fK@Xo3}gOJ=&UWZI&)Zl})&ul#7&uc(^$Q$(tS} z!5s|ZlWO`pyYK8~#G9L2`4-skvF$*5J)bCTSYwouugZ93k?cvCmbeCI0$aI^4m?`t zTr1i5nl4WLQMB;S7CXPnv6maY)x09JpXsg9m8qj~67;qg()aFcIJrM5uIp5$X-X(6 zYi8rv2u%}4nRwSMIdL$DI8`WqTvGXvU~}kdCT36r6t z+cM1R=qE~wIt9;;&LhMQ(-oaBlJw8B?~l9(QKN7d`e+>{78_3~I4Cc~?sjT(3=a`K zk?i|2L17tz-VAu-Rt>k(abJ4+YjA;;+3t(N${p99VpEL(3R}L^)2c9Zfj6XulfnyJ zbsQX!pasjWU=VF`wjZ8k#GLAHX)ukoN&bj>4GNV+^h_6AY^h9Uhxca%lM<8IEs;g7 zX$-pH)M(!yrQg* zvNFl4!Z{)cg43U~_LYsnpk($DkWp^!pLeH{@lzE)QGaEq?Z8P?Gs!$y;^tA0MRrf) zfp|y7;6tU(>dukYZ)?i!sAl_1g+~pm;06QzLv~)Lyi4EZ_+0;%VNI*_g1tmn*A?`U z<#Q8W7ByYpECk~JnWF}X@@+Mfv+oz}Jk`ImFZP)Zl7m8G-Ix~XmoMr*C-u^0*VR;; zwv@YRU8?rquJ0_@=zgk?iyE-)-<{>DJA6x^@w~}R#qV>{5AWXBNWBW%^H$9Qg^(lM z74T|~fD1A99)bSEXgqp5&lTU+EZ&0utFX!ukoBEcx@jil_5N#`KL;5IhzS@63<9d|5>$YB z-U9d81nhc8K55dQ*i)tHIpyQ)uKQ|93fktxreBU~EtOdjFM$Tl1z1FdgnmA*6B8^C zK)7QGTC~pv(ZGN&H7q^IJ4BJ8evnoM+T>gk~Fx zvT9c5l2fZZy#5n|&38+mK2ZW7jc0UQt z#g_}|D1ouQ0+y*l^7Pkf3^iyPz(;AlXA+~Q(!yC|81egXN&ATaS=(DMXR+(HuE(t( zh2aQdSpkpcUtFcb@)eIoG#am0UdvcS9#~=+(Aj9=$)Rkz_m6`7ELG;y|AflQEYbnn zIs;kH#z&5e*>!n0NGXI}f7w4H70$AIaqwW2yzFaU?8I%Oo-F`z0EJ#Ma499}a0qM8 zV5-=!)Xp@GX?ytnN1K0K33t6B7oKrAq6YmXKKYk$ia03yYsSdXY&Knqztmm-72Ri{ zKeaBXbvV5Ji)H?mDD|6v`BZ2p9;p5AG5kOV#$9wGW{Fv zBGD&x@II3KJ^oxzNK6H>+5Q{2KkKC!_@oZK0Z+vL>G$Wigl+#o0?Ty%U!eZgg8wfX zc9fC3J&S8T`|m-1StOqI)N4Wy7^XmeSyYG!92H=y(-t@sSo$vjEEo~Je6f}-3w}Ij znic&wTz}FgYZ~PV99{qx<^ML1ydZ!1)CEFNYk=`rz5mI_PmAEm)X+_ikiSJv@yQ~i z{!D-CBv!gl56izzkpHdsi99)p2uA&%&&L0jyvXGb4*~)M{%pd3OOE`c3(Ehm=>Hcj z+RLI??+dv)T!B#a`qG>Kc@jUl=cJ>bnM;eWGtM*Q{czzMkjH5co|WS%`I3W(QPvEx zU`nC8wKjCxG2o4{BsWtgq|u#Prrqf4b82o_BG|V=s6DN8yhC$2^15QlWhe%7#NGN) zQ1W=sYstIk#!q=_N^QEW`RKt=BI{rtwmjZ^l+i@x(wyPx>$&Q-D=`2IwiF*x?-WzJ zCSnd?31tdO~sk5e%mx2MqTN2Rh|EJvWsPo9b{vF4dq+AYxQHi-LHj3yO* z71vD$NYP;@=2w$u?$;gE&7YUM>W3-bc61U=3Jm*x`L8}PKM&@oWi(?&xG7pV{Z$B$ z_TC}LVsGDtz6L=zzR=jS&gzijlgsfITD9~--TFDM!f***DU&b(W)^^|i&rNmo1wXP z_X|q447$+FeVU6_bwzN+{_+63(}9%x?v%Q2lFEKxluJs(txe?a7M2Mq9V~-nYD;wK zq$?oI>RomVwQTKpdKocWvutqQ^%X@YKwrfWeukxQJ@%`(rVsSM}>!wZ!*q0>!p^Xp|@$Nf=3>Caj|1mIRPLklc?BY=iFhL(sLkY*$HVL zgk4{dr{oPlq<=c}@Xc|UVoeB-E`Liq^FGnB?a8j41M*+j;jDM)s_^uxcY2t*6033; z@xFKE*p1PkOj9LOpJdv3q@f_& zgcKUOrFChb_?|DgzVs;ux;#iMuQYBXhhYb1pbI zrlP+|TOiMn@4Qzc8+0L50;h+5_^)6mYZznlF+@0>rXV2lQwJdy$Z6F3_o~h(9GdSt zX)_-sCaCFhOJzOVG|W|$Vfz!J4fC-5z4UF(+eSf##GHZfi~jbOAN z;ao8%w85(D*WrBS!cUtaZH74*V||R#=k+QmZyt}XdtKz>-8O^g^JTqHfZQ$M$DZ!q zV$VWxY8q-<>SDG0PQk)`VE{vFTv}H5cj3uH9<8gZ2{J{wMz`-r&F(Q3w?3}cG=cbV{Kci|Tkle1Tr2&&@kpan55r#BK-&_TYh>CN z;krNiK#_v!hx5y>;&a*3&y90+FMZzYj$B}SG&qzB>ZO;Kb1@za=q;mAam}7@M$P&( zpW&7-7|4>=hSgDkJulm#SI`0GWx~{@I~~K3)N>}1suhXI);1u3Z(yq3+s)xn!^Pbp zUCHv4kZ8`cbo;?NbYS7fRN0o&K`;Mm#IJB68Zm;qJGCA{VB=LnVrl0NC0pM3_z-=y zWoJK~bew0oURt9>QUqakhJfunq@0ztJ7WVjub*f!RM08|6VPYbwBh=%7;!)QkK=QK zJ?N@ilGFWWJ4QRUOj<#Hdh}wTpg-_UY4}qIU7bb0R-^Kr_3EciHCCt*h}bhT&9Ypr zfb?-%rQJ;q!}eFsq=XQ>hlA&zjrPVdhd?3CpmzlmB=DIL$66%3*>{%2JHXDx95@2 z)%_W8mVS2yf;?ql;E|EbHGyxV7yu%IG=W!xp6~CcKoxmz;WDt^62=q9kb3h$_waXg z40YzOj?~!V88|-HUp;v87sdXmBOx4-yw<8%>+@*`Z2z)&I3if>RrjrS+ zX84u)X3A?i3gt6x@!!>A9>4VbuZZVTI_nwAt_&wE(J`gEe=S-i(L0&x&ZrpQcD^hW z7O64_T2XGV*`Fw{YCF1-dtEr9wBN_^|Iqg3;ZW}V{}F9M5>A9lvQyc2EmCCPVl0&y z+t|jC8B3CcP%+6i_H_(dhKwy0LUv;?)?^(!GlMbaH=XBs&Z%?y{`vc7uKT)X=Dt76 zdwsp%ug}fs3)vo2HZ?^C&yFfWiEJ!gXnLy+P41R(w0%!$FijS+vYxkFskxh#3QgTT zzg*)>R7CfM7nUh-RoqbThdz?dF20BIQ_5bDK2C2-d9lTUcb#cea0B&M>LLocQG*KQ z?%-EaZ+c{vP+M;y@O)+q10ru#-%Df{?io&#T+BpJpAAx<71UyR^DKgLt($GQ%BO3$ z6acoE?^Zt7no@<8nwGA}K=R|);dZH1^GmWT$1LKBGoLS9aJ?~mmc_uj z9PS-*C9u>kz(36{g?}$1f>h{&X}@#pAIrpg{YRwC`!V;FOwi3lP0WzAu)b`CNXNH> zh^_rknsh{QY*8H=l^3@lMt+u?y!$yCsu@yB2Vp{mS_z2DR7qc>BJDVXo|Kj2w%{&5 z{WSE~joJmo;hV^%utd3l1;5-XLgD53;KI;CmDutw9L1!bwilmVc2;NSuY$KvHdB|#O{8n)oohcSz(Nc?I_o*Gc=l|Jmwvf7#C{GXHB{ zImt0eaa#-X`pR&jDGLHa&Tpa-YIuH7o$(pI1y}EoTX`b__iTmb z18*UHd`1V zuj%dcChj5-G)b{;olpO(w2|xX5&d>Ax5X{y-C+>tR?EjZELQbID*%DB<`S*u@HEd0 z;yzLZ(RVml!C|Y?F(@_U5Pi)IUW6(widPz+WX^hUzZm_#miF*b>xCpu9r7?wWm|E+{$guU4n|Ow)D_86ovDge_3D)Jx$FEo!>`fFAfat~ui(S%)r)EmgvL{$7E(Qjfcz{# z{1b3@XIf#tJ)*D{uS|)6h?X_xHC`GSHeUUTUs8rnW2=Ual5W*XzK?&Yp%pLiN9kPN zXlc%|#3ixMdZ?p;fU&a!gIyKu)y{F79BVnZ(!j6Ok95-9Woc6{$*u$zvDSZ_Gqx}cmJOBo%I{*l7Ch8i;9m9K;Y)PsDlg%H-weh zA#t|`wG8cwt#=vkA(+o*FJIjpuUIAyIi?iT?gSteX)Y%*U#V%*Tr4xs0f~sWDvlRJJ3eJ_a(`>NFpA zIwv@|8|RLG!kOM=JtzH&?N{K8uHPTpj>^3iD-oma>Vg&B$bFj5z0X+WliZ7@B z5o8nA674r)q^j4f3Tj_@8zCnyKa9w&zGF%w2ulG}^ud;FJ#V8KIWl+$ZoL{g>D8<% zadpWkk~5mfM6X7W(7uCb?FXu>RIaK1!~PsOYXoHGz4N#WzAvP&o0c89{vWEd}y*(KeWl0ixeG1 z$@FZM=$fi(8 zZZ(rb_uVT^Z?>}EBB(~v|Kv^sMwO1 z=LGETdnlp=M1IoPf19M9YKlu)?(QE2;bci zY=Ijiy)6fEL>BkPITz%=ph=Fq0nVP!!q{gU>@M_I&!=+9LdN1e2i_N^i*DxW$Q9^e zuVNPo$Yo@<`mZ?zJ-SvJ`vFD!V)zP)yJNZH(q(1N9MWia+u;Gj>QTy45~TNX)#Dq> z#4gb0nT~dI?ey4eK?}n&EGL&6mA#k^dS=daE8*CgDsvn5GspNScU`C}CMk0xdqd=6 zZE)gwBI-?jT^DkR`0%<4QTU=NKG>JCl7aXund?d+FZs4U91PH5L54Xm0(E!pn%Z9 zlu(SA(b#AmX4nB5uEl@Kc|}FOY{k^vkpDuK-oJ(Rf2i1p^&>arK;l5x#kLD`f%yU_ z|7}d98L}8+2*F@d({mVPvs4&HF>3>-f`at9a=x9>RoENDr8Oo@uG;rHNVX%jjW;HODIip(j?h1D*DBL9z^(kk zjSCqY35ugV>{6PcqnQTEVqE8Y9xid2#YZs?a}XHz3H+(29+LX+JLU7(EOP)e1Xt z&)O~QeT)s$&v%tO|L4zA^#A*_AA9o`nD(Emo?CP@NxqM-+wpg>?7$B|7c#BU!}2dp zq6^$jaXJ$5YW$Hj-|rUC;|H|LM-H|7=OcfNK+fdH&TO@ShyOLAA3O5{y&ikdzQy>v z8^5A+;oNc*5gq@OnLoAc2adeh5|qL7yBo)t(QhnNziy-O@0ED0HTDDBR$Mt)|3h0N0he8HqCwd43(+g}Uato}QnF@+Hy|3u6&=RO`Lu{`_04 z0`k|!Y|n|ji~Mzey{`Qb!sjvZC$uKNGqs~3o7`N2B`QjDsKy%B*q^~eklekmT(K%wfp9ossj62bST$Lv-Q{CIVc#}@<&?BlHsDYi!KHN~ z|4P37zD9u0qpN?a%U>T#9HHWkacQ?4Q^V+^8MyVK-cXo3HTp%QsqTaMx2UxW^2$(F z8g~XBY~&hp@^ZnNHDS)KTCXBu7|W^4V)LPei?5RQ;p(ysL~l9SZ7eEe(cXUL4X0A{ z^T^)jgCQBds&L-x*wGu5h5bApC*M_~Au&+7 zinj4>jS@1qvo3O6-7{sj zkd}jU1s5;}7lveRRAVBpvXKM8G%46;!9diMQa{1+uPN=TfFDjf7 ze3Y)9_T=dC^F0|%4aoPqT45NBDeUrLzm=Xv2taBGZB96*-~zmBgSs1FZ++vdN%g92 z4l=?n#{&@x<~}1aDmeH`pSf0zg2IFtD$<1Oc>BUy5Yu6@pT89^`vGCDrnJoq!C^^Ai20HYURFL8c6 zSE}K$?bR+?(b?AYb07*731#v}R}4L)7p(D_^>gZ3L3)OlI*W?0z~R+>A3C~wldA{a z0CtEfX5E{jCAl$CfMq7O#z9Kx^b{ZJ=?UZ3fmq55pHN zH$u%c`>xczN$CY`r~l_?X0l@Q#j85pz4_z11j#f(BaCq2XV}mp6xA2|E-3 zs1cpkeD0c?lo0H$7&2EdGSYobU8@*wHLvsY6XqeE9s` zEXQdsO;e@j7Tb_}y%8@Tz&#LnOGfHdkWZ7W9YQZJwJyKU>OiTS46SKYUN~q z_tyR#g=_i zn0$5eE~CQvj!1s4Rms=ZHwyb0Z1fxZGfx5?`|^Bt4ten-tv5hT%$5>ppF;)wLfFdD z%SG!>xObh&0}x$`dHf-a=VQK zYwPClZf4iYJ+{|#qj16Xs<6;O0r%Wj)6WVkX5i0K@k{a5#hNQ00uV8?kr?9cjrt9r zl093W*j74p^u;_Z;pD~)bQ$IsoVgk(5;bM}*x1-sW`O_tN%YpTuyu{l-Hi70=1NeEFta&>nb&F-P&AKLSdVh^3Mmw~6QTSx*B`TvvC0SyT9)d| zr*pabMn+|%v=XO=?RFaI_O?g+po~RSmGeG@5B)tYSA`tmP46H#>l?-t*=H$8cJ#Ir zYj$Bi<%a^gwlol9N8i)C8=Q{b#w?bvj8r@2zWMGsC=o)l@wb2FPH&f*@+1>h^#&IP zukN=dQ3RHQKNl9%K#2xs3H;*z=iOgp4rL1U`p z<*SCS>3!M@Qllyxoqj0kayPg5g6P*$Pk3;;$o+<)C*YYyukjt;9wF-h!QdinI;v-H z+Yi$&n7W1i#K#EHKYLNbcRT!V$myD?Rt63Dewl~%yGmkSMm#A>uK-V4nHMQq*f z@>A8cu%y0kWZsrU8=6?H1KO3}89M5|q3z7&a*qNa-u^_NcBa?bGm>W&b37YAaCAST zfVLyxi4aNZnf`_8~!wUF6R-;6_v}4Ld<$<449SE)PcqnxQu;1 z56uxG;)`QV%nNAu+x9l)qB@2TqQ010k@dJ{l-pT;mgxV=ww5iovk?OZTf==T`p$rS zQY6&duSCPvHU}}G*H=@hFe2HS6t$Z?4jQ)^t4%|3T<>#FuzsZ?IaY!SP=>Y#TjNjl zNAKF@ZRN}==wn0hqohDZyur#WYgbKlW!1gLZ{2$RFK?|L?Ck(ObCG4)XkHpe3P)?2 zw`=c%)BIt5gHy{;k6FNYn8K)Zo?fy?!+>SpUIs`>LR>d3-db-5k1lj>gqr`sU^y%J za*hO-k?Pe@6`9!+K!EFIfD%Fhx#M^rBZI`FJ>fn73=Dq+-~+~?y6K&EIuz@mYWEw% zMf?_@4>fXuH0O^RfNU$eZoa?G-=KJ>n$6I(^t(k=S@`+Ep!J%4HHR{i=xxOz@bojA zmpN=8n~`c*t$vVgMgTI60}Rjadty^hDK(U*2`VQyihe)s8 zmrnm9b8!7~lqYsHI14_|uwl-rUU7f)k&Ct=V+Xm%Y)(-*HhwsUSe&Y_LMUvDV4i0{$N{3q6i_+KHZo?0+F*2vc4BhPp zFDlH12tyis63uhWQ&9D@91WKP?gQWbYBc{dA)~|0ytf{&ytD|u&rq7@&FxMH4Z<|a z9l{X-6baOB-5q}SgyE4rbEwP|fEerVMil^VS%F~})70ldV}4ZPaxQOQKC)fGBT+U= zD$ntf^K3%x_pBSJeVkwY4oRo#?G1na;oTbM?c#emRHoOaKnd2U=(EjHHQbRKsA=wUoE65_Nv$(i2mqS>^tghK?6z zTwQvt>q*8>rA~5W%Xe6`pT6<7Z|+)NwT06^>$N|UqYSoVaxV^~)f&0EpL;%72!uTW z$AT+J>R8!X3Dns8>$u~DLjGYl^oWIv?Q+dgY2t3MCQ%mzx?0n0ZGAUqXZ2B-t8tYH zL7oaSe+EfG<#1lBcx>B!72~>k4X_TWn5|zvLzcQxHc_zg;{4b1L2Q1M92Or58d{Otb;&Sp!{?DMT&c;619ri; za&xP;uYqAV(>i!ukj3T_)!ps#ZXPVx-iu3kImPmOuU3akwqw;U8W>}JXVR-TWwL@@04UH}0Tq zWz$9^u$ON3V0$Fm8Nu)pcivtbI)e4-ay;D4Hr}VTcz%G}dDb;|5F&c-k!xw}$S+~) zpEnMDCe`C*;SCxhMZ{8s@qnRv11If&D5di0nUs~=Y|o+yMeyeJ}{`zf^!H5L9qBWswy9spzL zNJW~*{Y9OOS4ZF7@FOa!{aCm$PUOF^6s;A^@;6Fb+3B=Z(HvPBdgFj>2>U$4??x!Z zO&{TNPmRRb-;B^}_QxZ4U|Q_In>ue9ed;#@H*0+V=_BdC4waSBdc5*=;vn>gwf(ON z`|GObIWw=*;}RR6TEkvj$YQzRzhSo@f62d|F-5Nd`pA3GnemdF;`(3517ADkCd0C^ z^4k|@;HeMITa|(cRs6f;qB@6!^S_3+|G^(KGLOo=KVaLd!}6USER|e;w^;wSt?A^F- zYJ+gt8Sa;Be&QFPI;QK4)6Z@h%Qg2OcD}0!TK#FAXZNmncZ;j*ItH36v z5ew^u4)E_FjUk!e_l>O`l`H1(P0<)U#qvUoXsu4x((D%Q+Q(aW*Q$oZdhBM)V(16# zPpDd-7G{Rdmciu6(I#9C>wGC}DGv0u=^XD*l@gjdnzf)O$HpSeE5&s>EM|&^1YE1< zoAC0@Pr$X=@or_$WFq~- z=>JRSA2G6uY61HV=b${{Ul8kj3+iorsvW&q=DED_9@*2obI#xTZ1htvHGxGqRoDmn z7hH7dwVxQ9^@7=1nI3+B>9y~~6-_%s`-DeZ{HuYXb+S&jy7k2=@+K!NTDS--3pM9I z#afOIV}c^`Xx(mH>=FDFJE$k4ug+2Lzp$jg0d#t%&w5eIvd*WhoJw{AhL-#9`scV* zX!aB&fZ>-O7CVh66u&v#SPtx>UhUuDaoc#o09`reGWlnr$7+SOLyp*J*H@m~FXulK zB6{v_jK5pIYWtWU>Ppv0N!$brdzZPS_1HdHdOp|i%~$qQ-cKN1^EEvVp?B#0s2S_W zhBjr=yx*5RZ_)bz{}m@?wao}+njdXXTISzT*di4s%k|x?8cwucwkb;S2sGJ2a?i(y zgyd2}s)xA!|Hw;@CDa9F;vD6=rV2a3|Izfq_<(VH+cK8Fh!sZ7iv zHFz#^G?1M`*ytYi#3@l+9&5^0Of4Nwt9CCVof7P+5sNI64JjVQ!z&&Za(BLU3=w=S zf8~*J{{4>J607U-PC7cm!xOK+nTKG%TUO1lM5i4Xb8(Ip|HtII=$%75T`&_%&ulz` zElHZUSmLIY%vZ}dG_6^)pbc%fGIps^4wJo_zvfSFaXh3qJPaFJ?QCALua z(529GJ*j`_AUT%5Jj9`AWGSCsmCi35j$bj^4~fO*z;+9~rX+StX;u%Bw*)O*<1{d^ zZ?+1|nkM0!>3yFY1qKVI_=&R}t3$^IL2|RRW$j)eIgv6YFSGFT$*KYaZ@-pcNe#{m ze97B6X(2|uBaMksEPe%!yK!4cSRZlUQ)E%!wrG7}!ui{PHZNr zs!d=IwC#nzqZfL?;GB_}Sq=(NJveWz>N}s2las>>&ovr5;cTslF^;Z(?p5eWcsXg~ z+|T`X^ZU*daHf^Xi$mjX4Au0b($>3De7$Fvp-%BJMSLzo%PJ9xYOm_=Uk}OPLF|r2 zrnV~|888jK=sH~W1C=#i{st(BtQT}kwFNQ0S0Uq`>Z}a@K`CRvAm%=$HIT-{t%-$i zP}WlZ4^#apysmt>o5m4(XzwXgOxIBaywcKQqNJVX6TY(DW-9J`=+x@?gV1_I`So!B z?;iM;eZQdW(=uwgs**~$D%%`C{C=fKx_L0=U7B9pUBq9Sk`Ko=1a&I9xPvROiXe)%~AT%YZzdaL_ycS(6*K~+@JAH9lhJGFxf+a1EbK z+;wapu;?gtKZ6DCMq@6talaCkG%HaTk`}JduIvFgCTSMTxivKcY+n{X0G9Iv7QLDy z8LvmhReo(#Gs}ExnnZ5$3GF4=y2IRhR zQriKbM?;WTq^q?C_dMk&&B{LMJcZ$<4cU$cA6DR@8?gbKZ!~*xB}taIONYRVOIH3o z6vki#M*M~A=HU^O;7dkO0+*v974>>l^R?TK{Y=t@6b+kt(0xLn;XTjjyuE!^$D1L% zf2{M2G@fqUM~#fj8u8f$HE-I~4$$h1q^`5P`-zftfKh-UI}cpLR+ZsRh~B(7E+Xl9XFNHU)onm%yTd3;sUYfiEfO!hK#2+*5zX*Ur=M#u^e zs!Aygv9L_)2Wf-T7HTafru`9uUmyI!!}!RQ`r%Ad&864S+J5?Jti&p^?-{zJocz)$ zV`S}GogIs73FWO0x|hG9z=MRedMvsNA&c%D_7n97M}CX1Ei{c5*TW)@#~G79o8p1~ zg;AjIHVLR3%SFCii>}EMw42I$8t8?*srXwR?Ep~}bq=!;>OW9RyQ@y^I48b8&jr8| z`BNI8NR5ELqZo5ITSt9&3s&2*{DEH$5o-|(vG~fSA1k z0WLq{fgZyDq^D*wFKaz0rzsw0LEVcM9x;urY0o^;+o}vd&7_*a?|7?=Y0w)0LOD;q zGqy1}vycKJJuV-bE%Fuh>|gcuYSi|TR1{b9^S!38FduJoIn>y_H6+c0YjyW#v-E0v z@%ZL)jpvGjI6>3MpZrLtTQcZ&B0f%$+DKYq)+kyWr_gv_AHVleDwOG9)8hwOHcV5m zo>;S)g=Tr>ICG&>U0t+MBUxqu=JadIxpvOjCqtUgwFinsxlbKMdpUEktDU{faKYwa zsMbeets^gw_R>68+n$~{dGc&^Qs5u~{&gINs+V5vY+Kl~aVveKPwA|opBg5XvTj*< zTDw<2U-MdVX{TZOZrq095R4!>BT#LB&W~2ZSnY8;_}8j_laSV9=|>1*p{r*fGbKw% zZ@l;8SIPX;_3ZFW-m|FZ?zw||x#E`dS+du%up!^qe2_K3-3d8KA={*?fa}?M)}o~L zs@&NTIE)bBcwui|TZ-2%BkvWkt5~&ESM*o<6H~zfd)UFPrd|*eqiu!@K@VF4$%xAe z-fDEQsXO_tD4@7XO)<&}jD2y3_|ra~HL_%Gx3RLYRMl(zT}FK3hyuN=-$c4`n*HB7 z?_(`3ddZQcpj8|Ak0|ZWTRqXIm*(H88@K!pD?4FC#~>INy?d>GlUs$}f2S2%XNTz2F=jlcquS52{9ne-AWYwo3;~|BUyG3cnfd?6 zsb(@@0V>h-HVC`3!XQ}MssqUXlB1pl$Ul_y)5;a_Fa~NC^28~N9=iQlT2w$NOe`y(?9xUGLp8pwKXcC^>i|}qpH`}{&^nr!D8K>~wZ`Pdu7d=s# zzU=LxQ&)booAe`ufAIM({$KP&ANtC|4}Nm|&4$s72tW9I`u`U_afaSGB60Xx$M5#! z$iP$jekT84^u%^Lk<*gxc;Zj%^!Iib9HmRA{QpHyOrWb@$(cW-f6edyI|d_8`bu;G zYK#H`bl~=n#uW!YvWB;Js}`D~d#>pJv;XOb0GSz_t@LST%pb_h-9$SRZVQtdwW-H& z^XL%A9hKFm=NrX%b>8ZTuOWi!yc=3LY~gy7MET?y0a-_RAYzGG0|YtuhEh45!DHU zx&#;}eQRINFb-r7lJO0T$v0B>wcJ5p13t6um8AFL8&rD?S(sVuLZ=xf%9@X+o%RSfsJm2`3-zo2x(p^G9k99e z%!kJoJj1Oiaa`^GEAJksnl6jUo8=>?2gcg{on1p!`RpGCrb0*1Y?&!zlQ!8y9T3WL z5LR|D`#QjMYvE0QlXkv1T`ia%Kc))SRnL3gPFr3M-&()6Qdn`o_t#^g-z$Y?BpbmS zgW^faIu8?=bJ$h}2A^~AdWG4Fj^+?ul#jVg+&%~uL2yeZz@hS%7+LjQK&+%x?G8(N&4L-pV_9@5lkHs6F4VsFxJJj;1kcE> z50TJc_+De~Uc7!e*wikM8>Lw^Wx3<#mh*XB@yqGXH=KgbIv&p(Hq}+Dm()^v=W=F^ zrLYn)IkUs({u0kchNwHTw9bm0YZ6tOC zh;XkYwLP%nmX*PJ+W{#g_17fscYI=1a_9X%9YEs(;Lq9RP9F$gkyNdnDC~GM^vqv(Ah)BUQV|l9H)6P1@CR`T zoQ-K!kY3~vJTg$FmFc%Ul>2FXBMjUiFuQ29;%$p*1Y++sRDQT< zuC*cS9OWb0wP+KUQ53JU{^`&{!%|fg$fOxESB!gVwF$qJwW)n*{sM(CdyxigR+o}5 zc~L~rYX+JA+S*nCV}#78t~Pg^@-X(PGIwAo`dZBC;pM)H10;AdsDxw`f4td_`s9RG z)_bUzTX$U*vS#L>&1D<+0yLAN&H11yV(Sd;n5AO1u^Rwd1R6=Gx|$+grj=C>TPb>b zCa7V|zk=`DaTe2uIYBI%=FXR!Pc+N<+d0pDe@d9Wn&>)MCuO}=+Bc;~>AF>m?z>4kr)Gf#q(uyXmom9y&3wc}~{!dO(ez1wWWtH$1G{@zQ76FjNE* zX&1b3UFwUHq`XV!{p-Ow-t2bvXUyJ-osAnTgV-c z^-Aq{IdRC#ygHv!YsbKnLi0%#SMH_- z#|8KnC+T$521E)#)oxQmM(vXX8<7D{{r+odm^s=h=N6XL5xrv{2MBqW>8LWeHevo) z#80}w^i<2qDUY-Q8DzCcm9F11X>J*LDs1xM2veu(d~ET=MgKAvyEojyRoYb|VV!q7 zUZakoe8n4MZ^>7*0rN=@cSpS3zV--%RCcjYy@Lhf0w7B4YZ0bUX+nHg#gVSm% zuf0D6@r>1+N2*##4h$sGEX-Ded3Qc>@WL#!R4JlDV~k-Jm=<4X9h3)|2YYobI1#?+ z4Sdj$NCsMhuOGvydm7?uv#EMBQqDxf=<@2=g1wL8oil^;V`}!r(RlS6Ma4APF<#odyMfHtjY<&LJAZP)0 z-&bFNR`9wnEzxUMl=xYoes9r6s-oe>aI~_eGy)kRV&x^B70!kbHR}6lxfNVap)v2J zP5%sLJ?8OZ^e~u2W!>;9IiHD161>2GiMOu3+^P`jT=+_puds}?IS)!qd6fAY2SVoT z)hrHW+uW~biXj>tp{#aWmcEP`;M(@tp;KpcxCy=SvolbXBmzJ>3 z?LGeX455PXPHTsEJYn|&Uyp_M& z`s2J8f~6@i)*kvRCop6Pzi;Ek2Cww3!cJ&~gssjC+*cmNz*lmMcvDxu#v{`O?lXZw znVm(Z0DTG}+9LQ_9_w=P(~A-Cv=iO**GJiD7P3y_+2+6kiGM$TgbBB!X)5LLnA~>}UUkJWbwer+ngW~CD@&v(R6SoF$JbA~ z%)k!CB%h+5lN4_~{8P%%bkWG}B^^z=r@2tkXvO^Pky<2l8@MkF+-H#Me+j$kdaHNg zlO|!QTZy``;7%tt+ZppmDvoA-))0YQSbchpuR0;bbl_&%A?KyjWu;EM(1|bK{B#A_ zD>Yap+i#yy`ZyMjRp!64=xZPC>C3LdljvI^-~>oWXMyRJSaN785Ir4}P8BZUoG}_Ok>gtc5Gfe54t^bNc zP+c~2vtHV+*U(hbfA&13k7-n&eg8g=RF*v;{~3D96v%ci20I#X&i3pDg4?5vE%_5_ z3qp(vg8u9DV{wR!ngeeE(v~YF$0u|lhB*)Lca4y|@#ODLF#?)cYiFdGj9ONKGf)A9 z>2!57UZJdR^uOL(d&J`0%-WX40 z#h)TjL)Gj9=cO&p$pEbUMLT~RG1%&;in@Go$x4kHfOEE_0`m=9gvPU|dLUH=>I~^T zM_!E%#)!CX^U`oKmuHp(bNd~SM<_Gj-P4=kRmxP3*EsWCzp7#v39wfg5 zKc~7!hC-BN%8uS>b8u%gJ*)KQ{h>VddOFOc#rclhC587)8z&;3u?yy&(+s}#DJ{KK zL(&dvrGEWbd_EQeB}ZQ6UK`2IM?BJH?=C-JMG#0To}uFEfjOY@G$Ru4Ph=9Py& zxftSo_h`_{fNXU$K8mu>JJ;nU@viftdsz4oK%UkHK<$4@USA|Ym51B?1WR^kpiou( zZD1Ez0${niW{>?rTtGe>t+wl?Xm@HP3zWqmbygC0_U1E49 z7!s%UQ~tg9{xRoUAtlRJK@;Ix+$2B`E~!Uv(miq@G@){N@l8*e1=M<>Dh~g;`Gt+V zC*F7&1fKTkUw6-ac*gXhjSjjtXVioNKE(@(swvQb#SN94r9fsQ-<>s|x{tELbTK|{ zIPJY3waFr5Om_Y?x%V$jIuu8=}4fusA2@l0zfkfM1c;|hWbw>E?|4D z;w6zd=}RLLt?e*J^8AiPpTK(T$v#G5-kpX`SgM#Fo zFS=ql{{c6CAi&6%lMZz`kL;R2s#p*GlL&oez@I@OvS5|Q_=o0Cw)cNFK?_A^%^=N# zd4H4}{)ycctkThqMM~p=mbkxKRQ?Bo(Fu-7`nRs|^p?N(;qMzP*8b@K*_U~|{yW@c zposph^-R9^zaB}iC+td}NM*oWYa^hhbO>_J%XVhsW2;34n^`KKJO4NApNE5$IuXc# zaS-DT+B6zddQ@;x@{8!yocYMN9qTpHN&cWo7EpqrOLWOqx$UlW=Js7h?;pr*RkZvrA|O~ttMAu&M)k#|3V*H`YcSg zkEOxQBDzv0cc28pcBym8Dinx5LfP{09*!+2_o(c|z>>N0z61|(!R1@wTCgFf{i}jO z@5Pj!UUGj!F6O3AflC|!GBG)|h*c9_@E>5-I%5~#=}sw0jvJSrtXG|>Xihq9If2)gj#QU*5*;d zz2q~pQgFF(JLVZNQn`Y$>u;&}`-;)v?0cu4>8=M|9wPofgSf(z@=5@PQ9-9*y7&# z^u2M`F39q@ei=np4mRsatbN_#?2qc56S21T>U`6m*NJ;tom$mlkWQoO3wNfh?kjb8 z$byDZ{+>}5dAXwf6EZYZ?VI_$^u`BGHEOJj{dy>pC+>T`j)Y4@2&$yMoHMF7eZAoMs$qr*n&jfecqN-1OXHx=^ zS?t<<`f7SIR0f!{XIAvbcpZ0!ge!VGR;JOq zG$|w~e|t2;=Bo9$i~SQ2ek>;kRNA-uq^vwasv#rbQf$oSpDHRwiA>wUCW(kc zLCT3*JxudHxc%!adqQa843<%H=Z^H(M}!bbEwZ3?O=|+c^#NdZ-@(If@hc6P*!ZRQ zEHH1Rpg}ie`CN?jvE7+;QLm01kopB%eh)o6Q@I_WjA-M;AL`Skn1b;3K;QS@e472< zLB;RHaD_nhQpysu?>8owcpp3u4-wTXYy-@=fIF?|-5i)+ntUI1-I6*vH1R&7)PU+q z6V~7;Pzd#?+BsSn$S@aj)|c>^BGkci{_x*cI<$zxYyXqdgrRuVh38gf#`+0IUivPQYcwDiXOLNO2fhkfKLbBv_+@GZY&g*ct>|1mwjC7&Skpxuq4)Zh? zwDl*7x;56jm5B1+vCYCoKLLv;s06NRj;1n)K&?H8Yp0#mNB|bbSy4qy4DF3>(5^$+ z&VHEgD15EHp`i-IGve5xiL0AySxD@z&&mm!Grlc6l6mBJ{-%?KN#}7_?QNFVY4_mE z#ZYkXQrZbsP(RW+f8e-qkoPUB;7(@N(#BQ%*ZZW`#{*7!48CU)ai2qXo}CrnSUEiU z*sd?75trs*Z|w1;)25=*9jhQgx!y@5O8JGv+Eod^tMCuNq9>?Or|pH|Yh}Gv9)q{f zX{T4Y6Td!8F~70<@o7WPWKzw3MP9@C6_OYxAN;%mNp5%Baec5O%;*A4L_U)U zAFOs5tlo5|#PvI2(spJ(Wg|09&8%=tqrwU3%CD6Q_!2txuWtX{XSCYslArvf5t>ob z8948&FKZUlA;TRKMDI!V$VXS zB-H>p3s9M^`0R$nlQHSgH2_0`e?d<=_im_*PZD1_yOPt*IZa(RZ z`yRj2Vi)7IoT;b9g|rWZVuXweY*qah-jVUgFbT-(HLVcyh(EA!O^XFj+D${LFTJ1J zx+OB(r0eBKT-0*nOfuO10kcD4*3!-U=4Qsi$o0yec4R;`bngs**<`B7c{=+kB~f*c z3iEea?VWB{kw(|&j6RTm`zl{PWTK=b+rBSDgC8|^W2~Rbr5>2Wi%W%)=TQ@P(5Jz$ zfyMT&sP{e*#F_PX^@!Os}8|IjnL)*xSY$A@x(NL&Elgn!VCL_-QtQ45q|T!0BgRI zM1i;GJ;{88z3LU4w+A)95fj;_6uIj4qX&%4lG>%FR&)?KyvKjB+~rzElfS-H> zPYHg)wjjyhKf&KJ>UAE?f?Y(<0orP2%CnKM72QfKXP#qIh_rm7;5HN4ukR?0C{nfG zzFXLuC0}e<*QZcA_=#*_j;SYunx^CBK0N`~-yAK>kFYC*+Xoh_hN##Zh+Zbh5-# z)2d76Rm*3?J1VvQMI`7);gE^*BjWq783Q)U;g!m^>8oCErk&hKU-Gy1cos}-fUK_O z8SufbH5wFG?Ms>zs4kfU3R?vOkVviDEcU%mO5L2rN{KI@{vE5+$y2{#qlWYY2sC); z=Tg+4*seM>F=m0yNJ7rJeXNuBKp!r0JCvqqyXxteJYVdu`8F#*zp+FtXiv{3&TYot zvUWL+(s?%}lJvZ#SvjAuwbSxx1L0ed;8rn?VciSOQzJcg+8=bv4p1Zz!y}|BPauPzqfy{=JPSnJokOw*L~gJ>w7J?SD0sYz3rLyB7bXQ5(if%Iwo~G zcxG1;&vke@KW|-m?S`XTsHA5qud*f$%#wYE%Poy3WDRYJAv$zzQ z*>WSx*v*O?dOXLq-|>6qn zy-e{R^O4Z=q4Q#_KJJ1oz<@GQfvBcN7eYO^>{;?wPZ$cCDuRTXXy zuiT!~t@O}~K;FgkXHWG6LRFDWcX8wI>I5c8F4^&5-^qX~AA4N8N&bUVSFecmmSmU}wI#<#KiTZWqio@Pl=3=i$ZT^7Dl@x`e3CTi>{Wv%&z% zaB))R!esy+SWvg9`bWn}S-3Nmkc|q9;;B()(;?Gyz`}xO#;@BSDF}2}@UtNAyAq!w(l0 z)!vM2avS;h*-S=ZJ^1Mg+Y)hOqtnu?W>Z_{#g?~ky5yyh{Ydij4F zifEBrXc!ohUNX~pHkh@gMx9;3cI8Fa&wpF~P92$8`cPTZ2wq7`8Fo!H37jdc4grVY zu5@u>UAw&yDG4i&=9|T^D{riX6clt7T?XchBQU;Qq?blwTfha8LDGyTIKh-%Z6yg@ z{C`5RT5xN~3=*L_d2s_^1<{N~o!ESkS`%8@`J*;s@KW`#oM5x*9G16x(@MG{%27<{ z%d6906!Vf*JdNqUX6kMzKSbkY7fzx-7$dG)49cGRHSN zb>w@p7^;oXkM9lBdr)OtSP8dDSc=7edxEIE6f4>yvI|rhNCC*QJF=;bpCiX*C)HKm zPTw+h^Ixi~-MGt4z*UPHK)BccXL;gPqW4WR-o zyJ@Q)6o+NK*klo=%O9TKox+vVN@rgvp5MxwwKEm^KY+^+4ItCM{9iv|+_?k(0k%Lc zE0Fm$B#^OZ6ZASkAoJ^0${E|;Fz)YXnJxf$n0Bf8m+pFhBVyG*x0Bqmvfr8SK+yjJ zaXLL9@2OV#yyEV3yuZiHejR|P_r@USxpttCpT7bN-A;1zeD;J@j;5oShKA>x)|Ja9 zJA8?M0r~$pZN2c8R;9f4`Vu~uYTSBd$@E9jUF-exJ+a(O$iNSZR_8r{e4^O&bu|9l zhv$6Hp5JKeO-BmH_HA*oj1ddUh*73n@7GbRM^oh^BD$J6}oV8r53Ri9iNB@QvZpj7C_yWIvenPEq7=(E^EU%wn-O!c;whnnaul zD*uc3`Ah?lydAY!{tqeBx`fl;44LSFnx)&ivSP`ot~W`OH~tTQ3Kuv-VO@cXQ!a51YKI#pB0EKBR>y zVg5-7!Zaq-9ZpLVSj`RN6;ZsgQqJep{#~GDNqcHNZpI-3J^ty#xfZ3$YYFXdl3wn_ zqCd5OmLcyte>3^A;|p2g{(y^3f`L}D&R!c6azeV;j$I zI5`dLDPo{uaf}=FP?3sgXj#ER&Ws-;%w2qMz?gw~X5Fx-xfjsm|V;6$}qv6hJ zpS}(J3mo8jx#BIR^F0tMthDf}!?82BVG6uWk6zg$YM}Ztknx_^WTv0;gYr!Pe&?GNYA#mSIg!x#&efQ76IUO#jSh8OzC zK&fO5_sKWzCY}(c8RHWJO25^zEO*)jRen_l^Upl|Y!U9K4fq(661)~9x|`1R;vC!V z?w(`>VDL=^uUrCpAG}g6l5&w?F1Zv{F228TH}$h%0Sq80c=ZYI9&`HpHicCII6Z(B zCFsH#jr_zMCh@q+i>QUhcVa8X61n37O5 z6=f-?VP!{9VZWYZ05l|5MddCo5!xG8(_4uM5M=%sn_Ssqxh%9UD`0#-B@F_iSVUlt3PF%;!oz`hvA zaaGG1Td_yN=G;N~4Z>{WllBmIfwi7>;}iNCVkHwI0VB}+CwJ=v%zv9i@c;hmxF8)& zs%RVyb!Dd~zPXX$ZyBiB3rXV=A4cXsEbS`fTSbB=NTCq}gC!=8T*-D7 z<<3Qgp7*@2=IT~(b@FUkjCF9WlJY~0mK@}c5T;16^LfGND7=Y6@+M|UMIr(6L+0tp zPNctD-OS97q_p~*i+ROV`ruCAnCR(fR7pcdJ2<;6KDDg6$|Kj4)%W5zbx;o#Dzt}t z0!INgE>wPU*nd#Y=WdHbV|%fEp7hqTX8TE(Rx37fA{EIvs|Z7Lj*~_Zn<)Vspwdw# zvrV&#_12q{6R#v}l+7YsA&J*zn9Z)Bx$vt%(ETLp`5WZIoEb zM6P>utR#+3x@V8O8b2N%7=e(L_?&X3=TM|_OF`f=ik%~bU)qidcj%`U3$XJ8Xm-tj zDq}>C_c2HmQ_R`moFrCn{Yi4$NS%Eb?;P@lX0YEUZF3#U+{rl#`LZT8E+rf^4WARV zcA>?-d40hr?M@|Qi`J&W5$^sN`9Z;SqX4=4S6o7?Q0}RW3q!uHbrUw^t8}T zt9^m0JblM8YMr{zS^HQlmYOE_$4&e#PbK8$gbw(7s*oe8{OZcDJsMPtX>_b2MJ@oL zT1AnMB%rGlqM~WoJTE{(<}Z`|iN%YsMdjtBtY_qmhYp)JA{`YQ>W4}=+TG0db;Pcc zz8bD}cX@eJ9w|&MT%$l+nTF3Co{D{0<67>Tdz3?1VtHO?!*%vrd*DowM$Kw~#G)zU zW`=OZbhelKcdPHbpwZPdU+{1@vO@XcmHOAy~k~)yb z;Q4PJgU0P*H@0QVzd!&3fAV}hLSo0ln>U+A~2 zfQ_kYF1)krPG){jNm%5@DQF>HtYw>=r-F1T(qV;J4G>^m^Ym-uV*Trb(3BSE%*tA@ zg?)z~@}4ijPb%S8GaYl-$()+>%dmfHM$YzKII@p)jemXW#~B|3^rkSgyzd3I$#~VQ%{YJHF3ZHKNURu#)UOg}ljMJ;BT6wLFo?@HuoU^=gx|2Mz`akmEi7JoO3 zsC{HMBTIw0KK`r!zMp;6n&9M{P;qqM{!3P}SxBR+XaJO>Bg=OwARA(`C+g1@v$V6qAgmG&YWASeufxrUKqm!ZY0+aT|R zc}=3m(#F3WTv%}w8JI_LQv1F;aB^v{5Kz)a@8+%}(I1nd`zcgi$gkL&p$g?Z79`~s2ETWZwkKCXYaVBmq`hPa7& zzPV2gzF6(xGirY6$zx{vIwdmp=Q|1?mzx)K(&%7rnQzlrVzsv+zix^!0= z+ve)9`FJ|M;eF4Vc!QE!%+2GKdpb4^e0&WN#W(=;GJ)1G&Uv?M;au;k62kr&xtd8C{zu3++RB9In%KBQSHY5f(zPtDT4+t<$CuFPWQ-z{T*bK#< zJ4m{FeLP;GXLaP%Rov4L(EV%%0Ud@&oKtu1lcTyDI@*sF^-%KQ)LWN}i;j`bUC=<2 zwLbvvsdlAYPs_klMg>c}Glr)>urwoJNp!dn8&ztbQIS+BuA4XqL+K@^aR*z-Z}c5V zSIC{YzfnSyj94sgN0f?B-f?W@lu%%S+4U0I1gvyLOoC56-R({?3RecZU~?SR z6N1rosTs(Xm|B}WO}x}Xcgy{t;as!lRXG<|72TsJz>90-bshcW1fOhUTc1a08tbXc z$o^NhtroWP$&%QqwZxN1Enl*1uX*yVAlmA#rKIuZj77ro=UWq(tcf9#BD25SFc@Bt zT~PX)p7U4$bBxuo{9=LqIjINuR-B6+kS!m2z?wtQx8c~y%uiPnuOtmrn@EK?OuKot zAcsc@H;lJS4{nqm2;)To(V;T5IVR0*{m%j1O8pC69ocak3vKetjQpg^q8s?79$I(h zhMGz)S1+S$M~1UE2!uPMZ^F+?F1FEF57{FTPmd40gm}F4qKlLZD=a^c5h7Wi?frbd z!Q$lmgkrPXcUJqg)%UY{(mx@b?}MOwOao5{{XQ8WkPGI&2K0sI96WvH-Pmz|#|A}k zh`sb=^UP$%K!KmF&GY=XFW;|!&UF>W^dwaE{Nv0|-cD+Z-IY z;IRlLfLE!KA7&pcC%5M>9tf7r=q#4}CM8f`VU_NJBw26O3NiLFeprUfYfC z2aT2n1oAm(v*g9^mxTaSJL;g_Ci-g zOq+xu$HTqOXP@ivI$RvpJXJDJDZ5fLmU7*k*&|E(mN~bnn7jb{g>7Q4TO!T&qxY+n zUwlQP4sCT!O7O&G=`eyyrh&e+tW@#-`@8dz|KThEqJ(%A2JdgcNPg}V-gP$qG&!O` zRKn~9`|jxVPlClH0{{`uZxPP_|LFfBCMIN#D*q^7n?hbrT>DU=BX6_FDYfhQaTFi7 zUb1V$6^x01zWHt5J#8hwK`hI6T6`K z>J@Uc_c=1fAi!N^ziXRnH&6g|xOXD#VRt&@Zg{DkEJ$I_2 zB(7l}#pyWbZ{On&Rn)+wDvl0RpD}s0CX@*Mh}@^ z0wr!;f$Y)UjmaGbKk%M$$@Z4_?_#E*s}p7C5vTWZC{K3bB5}JIM?UnISt<*t{KM^G zRitG8vVUp{IP#goujBi%72#*Lk&78e54THis;S+Z(Md-uF)3AOVeRm7PR~7aa%%k! z$}IPE`jL%=Bh6&O3hlC?)9pP0%v-s9tQ#o@-K$lcz^&rR=a!tX@ByggUIInmhC z?L7AT8X2H|3<#CN6SikPKF_u&k~_q48mFFpzLhZ&_#0jHW1cy>F#i=+`~X9?+wUs_ zj7R$*pk?@%VV`%qSU+76RSsaCc@wWNw5TYo{?c&&Z->K(--*sUd^phfj+Lpul_%9C zwnx)`uhn5%-dot^9}rFo)Nd`FZ9cgmzUiI45#4fVGw*?*#dvQ z8p$K$zR%CKW6$xYwJyA|kVV`Pru=EJ zft`f_dMKK{j<~m{2u!K~gPtdk(AaY=L?vdn=wFoyJk=e0=0XGfX -The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1" \ No newline at end of file +The naming of the variant will be the name (ID) of the template Item with a number suffix. e.g. "ITEM000" will have variant "ITEM000-1" + +### Update Variants Based on Template +To update the value in the variants items from the template item, select the respective fields first in the Item Variant Settings page. After that system will update the value of that fields in the variants if that values has been changed in the template item. + +To set the fields Goto Stock > Item Variant Settings +Item Variant Settings diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 7837f8c73e8..03b93c0cb20 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -97,6 +97,12 @@ frappe.ui.form.on("Item", { } frappe.set_route('Form', 'Item', new_item.name); }); + + if(frm.doc.has_variants) { + frm.add_custom_button(__("Item Variant Settings"), function() { + frappe.set_route("Form", "Item Variant Settings"); + }, __("View")); + } }, validate: function(frm){ diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index f2ea1d88bca..a810665997b 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -100,6 +100,7 @@ class Item(WebsiteGenerator): def on_update(self): invalidate_cache_for_item(self) self.validate_name_with_item_group() + self.update_variants() self.update_item_price() self.update_template_item() @@ -607,9 +608,24 @@ class Item(WebsiteGenerator): if not template_item.show_in_website: template_item.show_in_website = 1 + template_item.flags.dont_update_variants = True template_item.flags.ignore_permissions = True template_item.save() + def update_variants(self): + if self.flags.dont_update_variants: + return + if self.has_variants: + updated = [] + variants = frappe.db.get_all("Item", fields=["item_code"], filters={"variant_of": self.name }) + for d in variants: + variant = frappe.get_doc("Item", d) + copy_attributes_to_variant(self, variant) + variant.save() + updated.append(d.item_code) + if updated: + frappe.msgprint(_("Item Variants {0} updated").format(", ".join(updated))) + def validate_has_variants(self): if not self.has_variants and frappe.db.get_value("Item", self.name, "has_variants"): if frappe.db.exists("Item", {"variant_of": self.name}): diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 2a8e4344afd..34e3af6102f 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -119,6 +119,37 @@ class TestItem(unittest.TestCase): variant.item_code = "_Test Variant Item-L-duplicate" self.assertRaises(ItemVariantExistsError, variant.save) + def test_copy_fields_from_template_to_variants(self): + fields = [{'field_name': 'item_group'}, {'field_name': 'is_stock_item'}] + allow_fields = [d.get('field_name') for d in fields] + set_item_variant_settings(fields) + + if not frappe.db.get_value('Item Attribute Value', + {'parent': 'Test Size', 'attribute_value': 'Extra Large'}, 'name'): + item_attribute = frappe.get_doc('Item Attribute', 'Test Size') + item_attribute.append('item_attribute_values', { + 'attribute_value' : 'Extra Large', + 'abbr': 'XL' + }) + item_attribute.save() + + variant = create_variant("_Test Variant Item", {"Test Size": "Extra Large"}) + variant.item_code = "_Test Variant Item-XL" + variant.item_name = "_Test Variant Item-XL" + variant.save() + + template = frappe.get_doc('Item', '_Test Variant Item') + template.item_group = "_Test Item Group D" + template.save() + + variant = frappe.get_doc('Item', '_Test Variant Item-XL') + for fieldname in allow_fields: + self.assertEquals(template.get(fieldname), variant.get(fieldname)) + + template = frappe.get_doc('Item', '_Test Variant Item') + template.item_group = "_Test Item Group Desktops" + template.save() + def test_make_item_variant_with_numeric_values(self): # cleanup for d in frappe.db.get_all('Item', filters={'variant_of': @@ -194,6 +225,9 @@ class TestItem(unittest.TestCase): {"item_code": "Test Item for Merging 2", "warehouse": "_Test Warehouse 1 - _TC"})) def test_item_variant_by_manufacturer(self): + fields = [{'field_name': 'description'}, {'field_name': 'variant_based_on'}] + set_item_variant_settings(fields) + if frappe.db.exists('Item', '_Test Variant Mfg'): frappe.delete_doc('Item', '_Test Variant Mfg') if frappe.db.exists('Item', '_Test Variant Mfg-1'): @@ -227,6 +261,10 @@ class TestItem(unittest.TestCase): self.assertEquals(variant.manufacturer, 'MSG1') self.assertEquals(variant.manufacturer_part_no, '007') +def set_item_variant_settings(fields): + doc = frappe.get_doc('Item Variant Settings') + doc.set('fields', fields) + doc.save() def make_item_variant(): if not frappe.db.exists("Item", "_Test Variant Item-S"): diff --git a/erpnext/stock/doctype/item_variant_settings/__init__.py b/erpnext/stock/doctype/item_variant_settings/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js new file mode 100644 index 00000000000..cd7d8a4085b --- /dev/null +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js @@ -0,0 +1,18 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Item Variant Settings', { + setup: function(frm) { + const allow_fields = []; + frappe.model.with_doctype('Item', () => { + frappe.get_meta('Item').fields.forEach(d => { + if(!in_list(['HTML', 'Section Break', 'Column Break', 'Button'], d.fieldtype) && !d.no_copy) { + allow_fields.push(d.fieldname); + } + }); + + const child = frappe.meta.get_docfield("Variant Field", "field_name", frm.doc.name); + child.options = allow_fields; + }); + } +}); diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json new file mode 100644 index 00000000000..226a07ca6f5 --- /dev/null +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json @@ -0,0 +1,143 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-08-29 16:38:31.173830", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "variant_fields", + "fieldtype": "Section Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Variant Fields", + "length": 0, + "no_copy": 0, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "fields", + "fieldtype": "Table", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_standard_filter": 0, + "label": "Fields", + "length": 0, + "no_copy": 0, + "options": "Variant Field", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 1, + "istable": 0, + "max_attachments": 0, + "modified": "2017-08-29 16:38:49.467749", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Variant Settings", + "name_case": "", + "owner": "Administrator", + "permissions": [ + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "System Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + }, + { + "amend": 0, + "apply_user_permissions": 0, + "cancel": 0, + "create": 1, + "delete": 0, + "email": 1, + "export": 0, + "if_owner": 0, + "import": 0, + "permlevel": 0, + "print": 1, + "read": 1, + "report": 0, + "role": "Item Manager", + "set_user_permissions": 0, + "share": 1, + "submit": 0, + "write": 1 + } + ], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py new file mode 100644 index 00000000000..26e08d119dc --- /dev/null +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class ItemVariantSettings(Document): + pass diff --git a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js new file mode 100644 index 00000000000..3b3bf94f375 --- /dev/null +++ b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Item Variant Settings", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Item Variant Settings + () => frappe.tests.make('Item Variant Settings', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 7fa232e6f79..4bcbcc4b6f4 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -11,6 +11,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \ from erpnext.stock.doctype.stock_ledger_entry.stock_ledger_entry import StockFreezeError from erpnext.stock.stock_ledger import get_previous_sle from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation +from erpnext.stock.doctype.item.test_item import set_item_variant_settings from frappe.tests.test_permissions import set_user_permission_doctypes from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.accounts.doctype.account.test_account import get_inventory_account @@ -79,6 +80,19 @@ class TestStockEntry(unittest.TestCase): self._test_auto_material_request("_Test Item", material_request_type="Transfer") def test_auto_material_request_for_variant(self): + fields = [{'field_name': 'reorder_levels'}] + set_item_variant_settings(fields) + template = frappe.get_doc("Item", "_Test Variant Item") + + if not template.reorder_levels: + template.append('reorder_levels', { + "material_request_type": "Purchase", + "warehouse": "_Test Warehouse - _TC", + "warehouse_reorder_level": 20, + "warehouse_reorder_qty": 20 + }) + + template.save() self._test_auto_material_request("_Test Variant Item-S") def test_auto_material_request_for_warehouse_group(self): diff --git a/erpnext/stock/doctype/variant_field/__init__.py b/erpnext/stock/doctype/variant_field/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/stock/doctype/variant_field/test_variant_field.js b/erpnext/stock/doctype/variant_field/test_variant_field.js new file mode 100644 index 00000000000..2600a10fe0d --- /dev/null +++ b/erpnext/stock/doctype/variant_field/test_variant_field.js @@ -0,0 +1,23 @@ +/* eslint-disable */ +// rename this file from _test_[name] to test_[name] to activate +// and remove above this line + +QUnit.test("test: Variant Field", function (assert) { + let done = assert.async(); + + // number of asserts + assert.expect(1); + + frappe.run_serially([ + // insert a new Variant Field + () => frappe.tests.make('Variant Field', [ + // values to be set + {key: 'value'} + ]), + () => { + assert.equal(cur_frm.doc.key, 'value'); + }, + () => done() + ]); + +}); diff --git a/erpnext/stock/doctype/variant_field/test_variant_field.py b/erpnext/stock/doctype/variant_field/test_variant_field.py new file mode 100644 index 00000000000..53024bdac15 --- /dev/null +++ b/erpnext/stock/doctype/variant_field/test_variant_field.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import unittest + +class TestVariantField(unittest.TestCase): + pass diff --git a/erpnext/stock/doctype/variant_field/variant_field.js b/erpnext/stock/doctype/variant_field/variant_field.js new file mode 100644 index 00000000000..13db3f9272d --- /dev/null +++ b/erpnext/stock/doctype/variant_field/variant_field.js @@ -0,0 +1,8 @@ +// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Variant Field', { + refresh: function() { + + } +}); diff --git a/erpnext/stock/doctype/variant_field/variant_field.json b/erpnext/stock/doctype/variant_field/variant_field.json new file mode 100644 index 00000000000..ae9088486f4 --- /dev/null +++ b/erpnext/stock/doctype/variant_field/variant_field.json @@ -0,0 +1,72 @@ +{ + "allow_copy": 0, + "allow_guest_to_view": 0, + "allow_import": 0, + "allow_rename": 0, + "beta": 0, + "creation": "2017-08-29 16:33:33.978574", + "custom": 0, + "docstatus": 0, + "doctype": "DocType", + "document_type": "", + "editable_grid": 1, + "engine": "InnoDB", + "fields": [ + { + "allow_bulk_edit": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "field_name", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_global_search": 0, + "in_list_view": 1, + "in_standard_filter": 0, + "label": "Field Name", + "length": 0, + "no_copy": 0, + "options": "", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "remember_last_selected_value": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + } + ], + "has_web_view": 0, + "hide_heading": 0, + "hide_toolbar": 0, + "idx": 0, + "image_view": 0, + "in_create": 0, + "is_submittable": 0, + "issingle": 0, + "istable": 1, + "max_attachments": 0, + "modified": "2017-08-29 17:19:20.353197", + "modified_by": "Administrator", + "module": "Stock", + "name": "Variant Field", + "name_case": "", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "read_only": 0, + "read_only_onload": 0, + "show_name_in_global_search": 0, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1, + "track_seen": 0 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/variant_field/variant_field.py b/erpnext/stock/doctype/variant_field/variant_field.py new file mode 100644 index 00000000000..a77301e0e54 --- /dev/null +++ b/erpnext/stock/doctype/variant_field/variant_field.py @@ -0,0 +1,9 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +from frappe.model.document import Document + +class VariantField(Document): + pass