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@TzLnOGfHdkWZ7W9YQZJwJyKU>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&$y9Yz$FZ?U?
z;t4a^`96*PFGN97dUX-5Q-O222CfUZWS>MoHMF7eZAoMs$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$o0ye9rQYFl=B)cl)NV)7-ohLbma
zEog{d6Ek4ULSp_9pS7j0$j&d@rB%;eATbd@AYQ+>c4R;`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
+
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 7837f8c73e..03b93c0cb2 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 f2ea1d88bc..a810665997 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 2a8e4344af..34e3af6102 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 0000000000..e69de29bb2
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 0000000000..cd7d8a4085
--- /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 0000000000..226a07ca6f
--- /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 0000000000..26e08d119d
--- /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 0000000000..3b3bf94f37
--- /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 7fa232e6f7..4bcbcc4b6f 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 0000000000..e69de29bb2
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 0000000000..2600a10fe0
--- /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 0000000000..53024bdac1
--- /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 0000000000..13db3f9272
--- /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 0000000000..ae9088486f
--- /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 0000000000..a77301e0e5
--- /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
From 5b05335e89a581ddb74176effd956ba9084fef7c Mon Sep 17 00:00:00 2001
From: Rohit Waghchaure
Date: Thu, 7 Sep 2017 15:14:09 +0530
Subject: [PATCH 8/9] added patch and set default fields after completion of
setup wizard
---
erpnext/patches.txt | 1 +
.../v8_9/set_default_fields_in_variant_settings.py | 13 +++++++++++++
erpnext/setup/setup_wizard/setup_wizard.py | 7 +++++++
.../item_variant_settings/item_variant_settings.js | 5 ++++-
.../item_variant_settings.json | 8 ++++----
.../item_variant_settings/item_variant_settings.py | 13 ++++++++++++-
.../test_item_variant_settings.py | 9 +++++++++
7 files changed, 50 insertions(+), 6 deletions(-)
create mode 100644 erpnext/patches/v8_9/set_default_fields_in_variant_settings.py
create mode 100644 erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index f961a5b81e..855358e716 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -446,3 +446,4 @@ erpnext.patches.v8_9.set_print_zero_amount_taxes
erpnext.patches.v8_9.set_default_customer_group
erpnext.patches.v8_9.remove_employee_from_salary_structure_parent
erpnext.patches.v8_9.delete_gst_doctypes_for_outside_india_accounts
+erpnext.patches.v8_9.set_default_fields_in_variant_settings
\ No newline at end of file
diff --git a/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py b/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py
new file mode 100644
index 0000000000..a550d093fa
--- /dev/null
+++ b/erpnext/patches/v8_9/set_default_fields_in_variant_settings.py
@@ -0,0 +1,13 @@
+# Copyright (c) 2017, Frappe and Contributors
+# License: GNU General Public License v3. See license.txt
+
+from __future__ import unicode_literals
+import frappe
+
+def execute():
+ frappe.reload_doc('stock', 'doctype', 'item_variant_settings')
+ frappe.reload_doc('stock', 'doctype', 'variant_field')
+
+ doc = frappe.get_doc('Item Variant Settings')
+ doc.set_default_fields()
+ doc.save()
\ No newline at end of file
diff --git a/erpnext/setup/setup_wizard/setup_wizard.py b/erpnext/setup/setup_wizard/setup_wizard.py
index d3e4a084f5..bf9221784c 100644
--- a/erpnext/setup/setup_wizard/setup_wizard.py
+++ b/erpnext/setup/setup_wizard/setup_wizard.py
@@ -33,6 +33,7 @@ def setup_complete(args=None):
create_feed_and_todo()
create_email_digest()
create_letter_head(args)
+ set_no_copy_fields_in_variant_settings()
if args.get('domain').lower() == 'education':
create_academic_year()
@@ -354,6 +355,12 @@ def create_letter_head(args):
fileurl = save_file(filename, content, "Letter Head", _("Standard"), decode=True).file_url
frappe.db.set_value("Letter Head", _("Standard"), "content", "" % fileurl)
+def set_no_copy_fields_in_variant_settings():
+ # set no copy fields of an item doctype to item variant settings
+ doc = frappe.get_doc('Item Variant Settings')
+ doc.set_default_fields()
+ doc.save()
+
def create_logo(args):
if args.get("attach_logo"):
attach_logo = args.get("attach_logo").split(",")
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
index cd7d8a4085..77ccfd0cc0 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
@@ -4,9 +4,12 @@
frappe.ui.form.on('Item Variant Settings', {
setup: function(frm) {
const allow_fields = [];
+ const exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website", "standard_rate"];
+
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) {
+ if(!in_list(['HTML', 'Section Break', 'Column Break', 'Button'], d.fieldtype)
+ && !d.no_copy && !in_list(exclude_fields, d.fieldname)) {
allow_fields.push(d.fieldname);
}
});
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
index 226a07ca6f..a29137c762 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.json
@@ -18,7 +18,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "fieldname": "variant_fields",
+ "fieldname": "copy_fields_to_variant",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -27,7 +27,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Variant Fields",
+ "label": "Copy Fields to Variant",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -84,8 +84,8 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
- "modified": "2017-08-29 16:38:49.467749",
- "modified_by": "Administrator",
+ "modified": "2017-09-11 12:05:16.288601",
+ "modified_by": "rohit@erpnext.com",
"module": "Stock",
"name": "Item Variant Settings",
"name_case": "",
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
index 26e08d119d..1cc7c21520 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -3,7 +3,18 @@
# For license information, please see license.txt
from __future__ import unicode_literals
+import frappe
from frappe.model.document import Document
class ItemVariantSettings(Document):
- pass
+ def set_default_fields(self):
+ self.fields = []
+ fields = frappe.get_meta('Item').fields
+ exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website", "standard_rate"]
+
+ for d in fields:
+ if not d.no_copy and d.fieldname not in exclude_fields and \
+ d.fieldtype not in ['HTML', 'Section Break', 'Column Break', 'Button']:
+ self.append('fields', {
+ 'field_name': d.fieldname
+ })
\ No newline at end of file
diff --git a/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.py
new file mode 100644
index 0000000000..9a800c07fc
--- /dev/null
+++ b/erpnext/stock/doctype/item_variant_settings/test_item_variant_settings.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 TestItemVariantSettings(unittest.TestCase):
+ pass
From 2851dfad9974c9a49feee8f05a4adb6971c5cceb Mon Sep 17 00:00:00 2001
From: Nabin Hait
Date: Thu, 21 Sep 2017 15:41:15 +0530
Subject: [PATCH 9/9] Set fields in Item Variant Settings which should be
copied from template to variant
---
.../doctype/item_variant_settings/item_variant_settings.js | 5 +++--
.../doctype/item_variant_settings/item_variant_settings.py | 7 +++++--
2 files changed, 8 insertions(+), 4 deletions(-)
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
index 77ccfd0cc0..f3404cc78b 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js
@@ -4,11 +4,12 @@
frappe.ui.form.on('Item Variant Settings', {
setup: function(frm) {
const allow_fields = [];
- const exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website", "standard_rate"];
+ const exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website",
+ "opening_stock", "variant_of", "valuation_rate", "variant_based_on"];
frappe.model.with_doctype('Item', () => {
frappe.get_meta('Item').fields.forEach(d => {
- if(!in_list(['HTML', 'Section Break', 'Column Break', 'Button'], d.fieldtype)
+ if(!in_list(['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only'], d.fieldtype)
&& !d.no_copy && !in_list(exclude_fields, d.fieldname)) {
allow_fields.push(d.fieldname);
}
diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
index 1cc7c21520..80462d1ab8 100644
--- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
+++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.py
@@ -10,11 +10,14 @@ class ItemVariantSettings(Document):
def set_default_fields(self):
self.fields = []
fields = frappe.get_meta('Item').fields
- exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website", "standard_rate"]
+ exclude_fields = ["item_code", "item_name", "show_in_website", "show_variant_in_website",
+ "standard_rate", "opening_stock", "image", "description",
+ "variant_of", "valuation_rate", "description", "variant_based_on",
+ "website_image", "thumbnail", "website_specifiations", "web_long_description"]
for d in fields:
if not d.no_copy and d.fieldname not in exclude_fields and \
- d.fieldtype not in ['HTML', 'Section Break', 'Column Break', 'Button']:
+ d.fieldtype not in ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only']:
self.append('fields', {
'field_name': d.fieldname
})
\ No newline at end of file