From 9e4e9597d1993c854afa20f7e1bfac874b6aca19 Mon Sep 17 00:00:00 2001 From: Skia Date: Wed, 21 Sep 2016 14:09:16 +0200 Subject: [PATCH] Add the merge user function --- core/templates/core/user_detail.jinja | 1 - core/templates/core/user_tools.jinja | 1 + counter/models.py | 8 + locale/fr/LC_MESSAGES/django.mo | Bin 34943 -> 35271 bytes locale/fr/LC_MESSAGES/django.po | 218 ++++++++++++---------- rootplace/__init__.py | 0 rootplace/admin.py | 3 + rootplace/migrations/__init__.py | 0 rootplace/models.py | 3 + rootplace/templates/rootplace/merge.jinja | 14 ++ rootplace/tests.py | 3 + rootplace/urls.py | 10 + rootplace/views.py | 85 +++++++++ sith/settings.py | 1 + sith/urls.py | 1 + 15 files changed, 249 insertions(+), 99 deletions(-) create mode 100644 rootplace/__init__.py create mode 100644 rootplace/admin.py create mode 100644 rootplace/migrations/__init__.py create mode 100644 rootplace/models.py create mode 100644 rootplace/templates/rootplace/merge.jinja create mode 100644 rootplace/tests.py create mode 100644 rootplace/urls.py create mode 100644 rootplace/views.py diff --git a/core/templates/core/user_detail.jinja b/core/templates/core/user_detail.jinja index f9696be0..7ab6ebe5 100644 --- a/core/templates/core/user_detail.jinja +++ b/core/templates/core/user_detail.jinja @@ -6,7 +6,6 @@ {% endblock %} {% block content %} -
diff --git a/core/templates/core/user_tools.jinja b/core/templates/core/user_tools.jinja index 587b78d9..d1e64128 100644 --- a/core/templates/core/user_tools.jinja +++ b/core/templates/core/user_tools.jinja @@ -12,6 +12,7 @@
    {% if user.is_root %}
  • {% trans %}Groups{% endtrans %}
  • +
  • {% trans %}Merge users{% endtrans %}
  • {% endif %} {% if user.is_in_group(settings.SITH_MAIN_BOARD_GROUP) or user.is_root %}
  • {% trans %}Subscriptions{% endtrans %}
  • diff --git a/counter/models.py b/counter/models.py index b553bac9..13554c9e 100644 --- a/counter/models.py +++ b/counter/models.py @@ -44,6 +44,14 @@ class Customer(models.Model): raise ValidationError(_("Not enough money")) super(Customer, self).save(*args, **kwargs) + def recompute_amount(self): + self.amount = 0 + for r in self.refillings.all(): + self.amount += r.amount + for s in self.buyings.filter(payment_method="SITH_ACCOUNT"): + self.amount -= s.quantity * s.unit_price + self.save() + class ProductType(models.Model): """ This describes a product type diff --git a/locale/fr/LC_MESSAGES/django.mo b/locale/fr/LC_MESSAGES/django.mo index 7e9c8b53ac2b4ab83a2ef1ff5c114d72cf8077be..0cbe6f47eb5ba5b672d1d6c369ee743a088c3f74 100644 GIT binary patch delta 12997 zcmZ|V37C#$-^cN57=vNP%$Q;9H)9yP86nG9#vr@NT9e(xjIm@0iJM5)?4`(7n5598 zh>$&+EJ=#UlC6k}lI{Ke?(09t<9&|zy^iPabDsa}yw2@f?s<;qc{C^2{`tAQXTtm! zIvgExIZjDj5au|gayw4JXk{H|LagJI$B9@BS7RjZ#3J|u7R8%b9P`Dw^(c&>-oQ-4 zI@AZCA1=iJ$MHJLDdZ!u#@vAUsc*r&xZCoF%;OkL{wMP{RJ(h2K6fp*zk*nqd<0g) zCRh#AFa{U-@?OXJl0qvIzoI&-{e$)OJ@;6g6WHhGKivih834n1O2l7V6e4M}Pbr z3*rvcLcT_A{b>xt!c-!#0(#Z3Aq5T43bnP#sF|ms1{i``=}6SAnur=`I_mn_mS2L} zsa2?**kI>3qXyiI>hBb40q5&*|5dn2LKi$l4IEI{?Klv1K~dCzrLibRp&qsdsPkP> zE9-%Jh=*Y~&O$xBD^Xj%A2s1@)Ixu(%l@l_t0c6-o92DgO7lJGo)1M$AQIIUa`rrZZ6;%tftWk=2)C zVd|?;18qV6EAx)D8qRaQh8K?T`nxbCs>$6wC4aIc+Ft2Zo?JoPgTuxu}8O zLk+wRHS-OY--6w!e~Hgwv4-w-FJg7-)36F|MosV{Y9d!rJNhT)(fj|eC7gJ7fV`+3 z2|;an8Po;QsE(?dv8bJ?gPLGH)WqYl1a?H-(sb0qUbgeIP}eO-uU7Of1>M7Ss2OfX zZQ(968#RHGs0p0K7`%wup`b?YKoO|xqEYQ@qT1Ir8=6f~Z%>Ox?EhE_ok?h=N3a;4 zu=-7`LH!QunW)g%Z5M;uq3WoK$D{89hPubesQ!kdCNLT`p>e2(b~38{qQ+jgqqQV7 zz!ucjY(=eXJ8Fmap*lK@CGj|F%Wq*(yp8HOsEJ!Ii<($HvpK52B-F&7LEV~OUJ7bB z6!r9uMKzd;8u)e8KyRWZuo$(H<*13SL3O+qHGz*&{p>^C^KVi8T(tUi)OZh3d2e7- z_nsF7_PR(^%ru?wi{oCJ5gP~;YR zoe~r@lgg--)k3YPK59YzGwM$0rP=8drG}J_fquNbDJ;d`-3wa0itbB-Sw+;1U zbOgio{$H{N_sqa%Zb!vXuTuroQ(e#UiOA1`lZ1S0oM~7Z_n>y_0%`)cP!qg|>L+)i zTQ7o|XlV>(e5VQpT^Ns%*ao%I{-_xbMs+k1d*U?I4qUPP4fLme$MW}3*ZVbhCmx8} z$q3ZM%38h(`riLK6tq>1k!zg}my?^T8WxK7HTKA zptkxLYJq1l0DnjIbEgISuL19o(1rdjT?0`Q3c+ZMM6Iw9>Q*ITF6@OGpf9T9p{Nhg zinxba-JZh_6L9J+><=?{M)Hk7a>IiCO zC#?QE=B0ic^Wy_EZyR^VLQz{@2GwtQDp4(_jI8%(4=7PVtLu``~;ide0q`(JpvpjQveG74$97dv6i zBwk6JjOq9(>Pr{Jld3OWPppaKuo8ZRx@9LZ7%yNU`~!8bbJ3}GGC!&wirT5jPVB!D zWl3lUVz3}qvxfDt0QIIAgzZpUnT+~|_dz|Bqfpn)v-8VP?N_1t%`!Km+I?a5gPqua z&FnA<`Mr788eX>gU#J=1M?GYD*iUs_2sOcQ)PNq;t*MUMk@j}}8LOw5!!U&NW4#o# zqM4`;=AkCC95rAjYM_m%fj>ocyc4y>hwS_*^JnuHR6o~H*ZpDj`&Rew;y%3IU<%rj zXjI2>s0-s!6Kjr|KpPChj;Id%p;kWF9Ao)cQ7fB`TEKEtKkuXZU5}yo8Pbo}`HF&8 z{w->tbM6V}66)EwiR$2Q)PN6A6VBh&ok$VX#676?RZtIM9I9PYtG7p8-^1#u=zIT1 zQ_ujDP&1x}n&~`W1AhHb9j!7yMs58LRQtoI_9s#O{EV8wB|CrH@()ov=ikk}E*Smw z{uia7nU_R$5QX7b3Dt2Uvju9W+M@=34)vW#L9Hwm^$8z|n&4d2j;_E6T#Z`DHq?%N zgjQe3K^{o4?Z;1LXbj70h zg5{@K{aq|Ve(SUBzZx7Pk%-q&f8o?fb|(;vT4`O>PBlYKq_v%Y+Rk^k^ZijfI0&_) z<53Hljp}D1s-G2D5i`A3ID)lF{DV(m_2=9lk$%X3oay|d3om0yyn%(WKo9qsD1-WQ zyBg}@Yl)gbSJZU_P!k`5x`i)V-8+dwaS}67JFyydkG5kZ9zYMCLv@ggzd&_95UXQ2 zYQ@d4CU!+_?Q5ukmttvLW%WI%_J@#%-0Pg7kU-*3)PQw*xfAMuT0t6?z_F+mzlnMV zK0pny9yPH~(Km6_!*>An7GzugC)CbdMzz0#A&l=lq~IY@sJGieP1FkNqdG`J4b%rU z@Icf~rK38UjCy!yqE@yLb&E1l=l7x(mW@$(33Yv7AKo0scgj&v!#=1EQc+k--6WgEt*9A*R$knJ% z>;}}zwqrTmj~={+epn#Ioj?%gq8^61F#!EIKE7T`G*=r|;p@+l- z)VF#CR>03t6F7s~5x?i%od`p1Z8U1&IMhn(peB%j8Q2<=a69TD3>m=f#!486-o6xc z!9vs)zk|BR8&Es2)!bwGY}78N@xIml2D%gUM@=9fdax|!)BB%5 zK{IcInm{+qi)k2u!>vBXoQV0zPeX0xJj=g_8h9O6$Bn3k{ET|qf3fF+HdiM&r9!2er~4Q62q^`S2fXgnnu6GtmU|Q%^uGq%CS8y->GwfaS-b+RZ`V z@BiBrbT5}-MO=sF@i^)sxr=(}f(N-<9D?e&Flr(ttX>}VDX)r}a7(MVL-jKd^?Hu8 z`rGJx|JPGchg(rwz8m%W95gSXCYUST-Qq&1dN}IgD{DrhuCIi87HV3(7W%%ZsGUhd zJq!KQ+5dtR#*xqmWIAf=m!e*u^{DT{4$FUIo;7cxuJar0wvRyFqG&T7b-oM6V=8LG zYcL8w7|i`IMd2_BJxtfJ06xNkSYU|z5JjR^7KPfW%BTs(p$14mUDp-0^?gwb8Hl>~ zW6as8>sFyAy2(o+m_jyI#?y8p?@)JQWl&oekGhAQ%~U%-9yRb1GZWiV{{;2k`wes7 zhEAw0e+2_^F=|}zYCEwBwX$ze_vi%bVY-ST=>LNI@I;^i=Lv>W?tL-v5Rf?w?LAP+Qp>)p0tigGs0Xr(1mvYNy^rt#Fx{Y59#< zjr{vE2n8>k)q%Y1;^fq*gk{!<7Y<9^vE6c49bcLSyhVoQcIS@n!dx^hB-rHPlYdL-pg`LP1-! z9d!%#nEO$$(Lq$hZ%_k$Yxy6MMLK6N0lSZLpOwX^dwmO=VEOUxf3Zn17o$E+yN)jW@9lCQWqfQ?WbstEh)A;T1kgn2xGvqMn&! zs1Mmi)GfV@dG!AKPjc^hpc#hR+Ty4cN12sT9mHBa0o73}v%BR7pavX*rEvo4nObJ9 zLEWnL==Ss{*?iy-hw^0*$h`O%OWOt(BsDVnL@=>UPVo(!m zfKOsO)H5{&eZT+nDX8ORsD_!S0X|1P8;4Mzf3BOm}dI)OZNc6`l zW=+(N)<#{|@KyF-D@!Dy33Nbp)EzxI05#B5RJ#S}n<(a|o{8%4W7KQ81#@FI>a{+K z8t4kDpPQ(K-A7F}Ex2H;z$g)T?k`_-rgdb22~;TLvdpPl#?b-_=l4lbD2Fp~N$)OCSV-FAgh z6Do$vS43@bP1M9{o6XHGNWWeug@W!?I%*}8%z3CSUxC_@Rj4id9JR&AP`Bn6)OCNO z1`3$wPNWEG0p(FEu8g`Bbx{+03WN0ix1*4kL^9^XeyEPq&GB}Aj=2c6Bg;`MUSn=T zO?Wr@VFUg1*ikzd0~1%trE;zk{>J#uoW~leIE9!@gBOYIL_Xrbk8dq;0@H}Dw28qG z2DnZ6J<3aoW<-DL)lrAOu;JEUBied@rt*pMC32%Izn^+j%2TXP z)p>=`Z@@`%L$ETT<1OlM5Q8YUC*GwTK%AwVO)Mgwp#Gmj`_J*f#rLm24OSES`OG=K zr*NIz2BJ9Sp9wu|I{0JAX+eBPlw+4VVJ_4$jrQG%-GrX0s;DCt3u95QHEcv8KT*zi znkSb09O@m2)8zgoz9wRcHwYbPId{}8IzOSlkb&fyIfVI&asQ~BO-`SKEQA6#6MVo zTwOxH1$l5E=bxawok*mvVtnzm~x@6`B3IPnLOpTtpXd<>^k zKS*37J|lv;NRuCEZF*x}>T`+9ln)bH&VKYGQYq^=PekfMj;FAjTXc5PrfJUm--h#}brb$sRGY$NwNafJAS+~>F#^%qPR${!IqM^y&7pHnxh znD5f473W`|o)?P{f0FYKBT>?N*h=|`m7}m8@f^7}M9%RJh2mVCb3CN*iJkY5|A+Eb ztDiPEBYBf(MbxkkcPUT2MI;ix5ak)5CAJ|N6C-HfjNDV0 zml!~94Y8ci5loCFvWRk=e;Nzmn?zU2-)XfMh$rY|0L~+sfhDP5pj-p@VSi#h`R8yn zK7KT(Ueyve%rfZ5wK|@$_HW~-Zp!y>E$ZHv?BwysPN?V_`^ZCL0u6=|+bAcZj@v{D$_srp ze%&k|LcOw0^e*M`_=KI^;WH~B_H=Nml;HzXoOb%xGh)>dMILFExFqHCs^DM?wzKfYKD%NrQ z9LM8CllW6n!K{h_l zahybwL@I`%I{FrKVjAYcQu7_C~iWJ8Xh3g0LM^Udl@zJ`=|*$My>P(YGMKOrh$S{*N36%i=%d`9BL=3+W7WnUhf~nu9vO2sMG_s18=4CcYKb(O%R;d&JJ4 zMcwmjs2%wSH36ThZa@C01qXUaH1kl*htYPTu9;}IMz!mNd2s-0f~ly1XQA%(0@Q%3 zQ489FTEMqfK7bndC~AV9GbFnAH&7k?i|ROgHFu_YP#xq)tsv6MQ5ZtG9BQDt$bXy` z{G%LB!``?Zb$xhsx4)vO{!1eD9;YISwy=uX2=#$TLTz~m5!KO` z<}TFE>_JU%A8G<=7>;L9xAY-uVOeUq=R;A~MWaUpmLbtStc03j0%{AJnn|b`C!;3N z7o%|iYKP{d23mr;ZX>GwPE`Bv%me0+sJG{rTI~N=k_%L5r5$U#U$Snf@+ho;<5ADV zCad3!+Myk&iKn6W1BSZCmr>V0LruV`<4(vI_0Z-(wU4aB{;Q){Dl|Y{)YjBTt*jAh zhgzaK>VO5Y3u?Uh4D*P$l1&-@wH-&xc{`Uh%)cRVB-;32BR7pQ?UCAc&8 zLro|U)j=-QK>1J|l||j#YN&piTDdK10=-c6gHgA3G^#!ob;~^OTg5WeJ=tL8U8s)J zPy?Q}@=dHr`Crt;%GPynSq0S2B%-eCgBoxoYKJGHCNc;0Y%D?+-b1>%6`|lyqYcmV= z^e*$BaGdqXXWRJ-`7}6pu`rgd@9tD0Y62Zl6YPQN=S?e*Lrrut>S3FU+ToR$54WH{ z<2#2)G~*Mfj{b+;@GfcxS~hU&+o7I~E~xq*sGaJIn)qPUPL4%QY?{^2MYUUu+No8@ zf1K_7!=sL#k!XfaLwA5+%tSE^Gh=?MFN9iQQS`;isAr)D>Yffjt#BA>0uxaanu%HP zBh&&HqbBrOL-t=48?9oyxd(MYI!57P)IEHFdbs=>xf95b+R6yjR#!!>up#lFh>*jM0r%X9Y*5_D=)*+l=q=JxP?VAIMKbQl~EHO zgIei3SPWCKIIclW@GyGc|LY`vR6Ma0FR=jS@TP7D@u-#6v~m(=rTjVuU{7-(YR5*R zwtNbz-x=tA`mMau&acO8djG#5(UyFTT1h(UlX?^NR`@h?SCAKVYpSCLZh_^o1L^}a z6?Of5%!WHrJFwr*|A?B{8PozUdgcCKB~iz>aV9#=-Fr6+BPp&#e>{Lu_%o`bho}Lb zqPF~{>C?h}YkX1d0?Z(*55fM_hhc5K|EVPUz64#2ylTvbL2Qp?0c;mD{6speyFU6ssSE*(r}`!~G8=nM8%Qat`X7y#VzP zevY~@-OisxwLgdI__}!;)$YEPU!o?Kg=NbeW*$_#f>tiomi^a^W2n$WRUOrF1Jn!? zQ3EESZcR_rj-=Z8*;fA8T!pzfzZtcleW>fxQ4=|Z8t)=%oNFEu4g43X<4341){Rwz zTxO_Q0M$_h>bjCvjgdRDC$=*(rfs?{UhKXuw$1jBBGBHbc!k z33Wj->LKilYB$2lsi^DcS$Qeyx{as_?nF&^4{D<6cK!%@|NfuzCj8k&ZT&;k1zFm= z7X+d@3PnvI9Cg05)yJVGT+Qn1qqep=YT~U?*LTD`*bOz2VN&n^SQ2ejDyrkTs6V?O zqgJ*Q_03+7n&1zp0ZwBmo=5G-J=DtoLk%3z!M$aK0W(O}G(yl(Zu8J}jsK z`=Vw%4AtRitDlS-c#hT2N3~my>Swi`-;7$wc5{!NPe-*shU)j%4)*u|Pb#$Hn^=oq z9(S+=<+7dJ4^tb=Pk9LHyD$^OaJAL%vGN(rOa0$g&f3}iVn!g{I=xU6=!075fX?i{ zwrUg=x+flMFwGjghdRFqwS~)3Z^1THM+ZH9jrk|>wMO4t*la3OZV z?>r>BuplMUK~C)u0ZR$3)bMM`1;riQ3xTsDV$Qehr_q@>5iM{c_b)9)k5S8Z}^l z)PyFZ7O)J%_5N=r(TWeFo`K&{16)C^>@W1r9QE+MK)nS%-QD_N)Xo${wJ(DjAPyt3 z0qXkRs09o{T|X857~feyqJfv7wrT~cqg|+{cpqwIhp-@CwDZqUEAvTqf1re;uCIf& zu>-1~1*q$nqF%o(sQUfr{r7)Ak!WU@P&0jm+JTp-75nvYAF}MIm4=y7sFhSU8=@wV zgnB3kq8`%GsD31h4?k}D`sP@Oq^QgD!Dr#X5uqZz7>2V*Dh+ggoA^|mldgz0VF*7zrO{gVm#qF)! z$I5S^?(KNg(?7@RS79XO?Wj-nX^g^Ks0oC4db?W^kJ^dG==~I<2JVYmX@ArNM&dBk zb*=FM>LF~{hyV72-7pqELbX4H>i0P67GFi}z~83lsh#lY>rNyPeQ8h_wS~n|J5e69 zV2qXHQ4_3&nm{d##P+BGMxrJ@0X2bHm=%|yAFgrB9%qxC*nzrm4{9sZt^O=(;6G3w zrfaB`gudZE?UAT*Rm_U@QSF=R`Tw`x}p$a5x6w zNYqLuq6Yd9bx#*t{ZipXt5)C}h zT#C&puSdP#|6qAc9OQ2KD9lEAE(YOZtKWcH*#XooI*fW|enTzf8R{9yKG=P>qEQp_ z#E@uf8X<3l(*m^vJ5UedZq!z$na44J@^4nYVLmo94{<-Wxlq>?Mopv)w#G)N{ud(8 zl*d_W728p-+c&6t{4Hu?`>+HawDKLSN%;|KBC$i=Ur04kJJ}xfu%@7{AB`Gtf|aMD zcIrL!*ZV);oACG9POQOlH24Db$L1pD#;2$bvk!B>bio)*ISzATBh-Z2qjoMCHK8|A z*9}KaWE`s9B#hGgKZ`{7VjJp$gQ(ZMaQ5W7ot?Vx98Ss1CeF$@-9;Q;Lt!|9k z`V>sUL8ytQVJSR=ap*Ig{f{DvCn_~Y9@{D<$XCyVK zC_d4B=#o(n$qdYei&6J*BWmTlQTO_U$! z4w7i#qo^5PHm{>*dK=a5A$ngs)C96lwynfS$`Po65>V}$qj#bhKsg!JUq94qIAk*S zKQqY`D)b&tM-8+b)zMni%D%)bn1)*6Aym7g7>Z|51KdIF$P-k**`~My2BF%AqIRYb z>UAzNg{M<1PoP3u(iSyf3TmdqP%|Badf#WG+O0;t-`i0grJ+`Q4mI(s<~`KFFHi&f zPIa#jL0wCN;lLB`=iA$^Z(_DWKgu0Z z<8(I%VrIs7#*$>>#0&kyj&%Hxav2|fh@ie;SFiwOeIwLiE6NF!8yaKrneTH?^Q$vnD#5K!R)-FX74GA69 zIJXwN6J-ex<#I$I{pLg+i7wuMwxLerJAou$Sc9kLeboK@%F5rM_xZrl#4*lY)WsYn zC~IpsVF9d-lZmoKIOT1ahtN@+$@t?uB1z*fP^}$Rt;(6i*dB!m3E-5 zWBE|>@~X6>BK0L0P_JtrYm;&AB*_QV@kiFlLz`#1M|TPR28bhGBdH8rNfPlL@e}co`uB)W$Sb0b zO5_r;mQOT(Ms%X?Z>)nK+c~kBctqVYTuJmIZ$ZS+ zPd&;HGI6J;Q}L3BqC!V~@_oc`^6j{o&~etqsm{d>sGCAWk$1+Lb}rJ|>X-B#@*F^ z`SwIXq64v*`oFM&o&O)+(v#kV%JsxpBIBq|lFiB!$=i~DnQ;=gP!1th6GtfzB1RJW z(;Q)|?n++Sav$31XpQ>6`tHW&lr_Hdr=3)uar7YxrT!RE)GB{57nz;SZ!p1f@fp`# zA|4XmDEA|>lk4~g#}kv?qQ@ERCe9al+8U{<3o(px5VJjm*D3G7mspj$U&wt>$361j zu_fxrPW`*&pO9a~z4(!xBXuf}Hzf+|{m(d(sR-c0SyWsiE>k{8j38Q2{?{7zz>?IJ zv+_#vGDOC)gQO!-!|INc?<3|=-i5CoT|HF3MHHd-KJh8BnmA8V2^H{)lVwabDnsr%9DAL;q4rx{uU)n6eV z6HAFzL|;3%0E_wW`mi%6z^p4G?GpN?Eo&)-xc zhLb~yFNt>6@F(hZbRc?qGgfSNp^dL~^Z|7r5zC3w#6LvFF@tt>DP+y4z?#&Zv9kXE z$Ifvo-XxA#Wi`mWF;z6PQ&jx zm!G^D`9yvH=Ti|%g^v1oiWAL={*?bBJ}1}F6Z7D7Z^`|iQ_op0CUJfO{y{7xe}~98 zs#*UVts)IWymfl?bJzclWu`8l&$jU~7lO8pOZY1LwqDJr\n" "Language-Team: AE info \n" @@ -17,8 +17,8 @@ msgstr "" "Plural-Forms: nplurals=2; plural=(n > 1);\n" #: accounting/models.py:36 accounting/models.py:55 accounting/models.py:82 -#: accounting/models.py:132 club/models.py:19 counter/models.py:52 -#: counter/models.py:77 counter/models.py:112 launderette/models.py:15 +#: accounting/models.py:132 club/models.py:19 counter/models.py:60 +#: counter/models.py:85 counter/models.py:120 launderette/models.py:15 #: launderette/models.py:60 launderette/models.py:85 msgid "name" msgstr "nom" @@ -64,7 +64,7 @@ msgid "account number" msgstr "numero de compte" #: accounting/models.py:58 accounting/models.py:83 club/models.py:145 -#: counter/models.py:86 counter/models.py:113 +#: counter/models.py:94 counter/models.py:121 msgid "club" msgstr "club" @@ -85,12 +85,12 @@ msgstr "Compte club" msgid "%(club_account)s on %(bank_account)s" msgstr "%(club_account)s sur %(bank_account)s" -#: accounting/models.py:130 club/models.py:146 counter/models.py:322 +#: accounting/models.py:130 club/models.py:146 counter/models.py:330 #: launderette/models.py:122 msgid "start date" msgstr "date de début" -#: accounting/models.py:131 club/models.py:147 counter/models.py:323 +#: accounting/models.py:131 club/models.py:147 counter/models.py:331 msgid "end date" msgstr "date de fin" @@ -103,7 +103,7 @@ msgid "club account" msgstr "compte club" #: accounting/models.py:135 accounting/models.py:178 counter/models.py:25 -#: counter/models.py:210 +#: counter/models.py:218 msgid "amount" msgstr "montant" @@ -124,16 +124,16 @@ msgid "journal" msgstr "classeur" #: accounting/models.py:179 core/models.py:461 core/models.py:739 -#: counter/models.py:213 counter/models.py:256 counter/models.py:339 +#: counter/models.py:221 counter/models.py:264 counter/models.py:347 #: eboutic/models.py:15 eboutic/models.py:48 msgid "date" msgstr "date" -#: accounting/models.py:180 counter/models.py:340 +#: accounting/models.py:180 counter/models.py:348 msgid "comment" msgstr "commentaire" -#: accounting/models.py:181 counter/models.py:214 counter/models.py:257 +#: accounting/models.py:181 counter/models.py:222 counter/models.py:265 #: subscription/models.py:57 msgid "payment method" msgstr "méthode de paiement" @@ -182,7 +182,7 @@ msgstr "Compte" msgid "Company" msgstr "Entreprise" -#: accounting/models.py:190 sith/settings.py:278 +#: accounting/models.py:190 sith/settings.py:279 msgid "Other" msgstr "Autre" @@ -224,7 +224,7 @@ msgstr "" "Vous devez fournir soit un type comptable simplifié ou un type comptable " "standard" -#: accounting/models.py:277 counter/models.py:81 +#: accounting/models.py:277 counter/models.py:89 msgid "code" msgstr "code" @@ -232,7 +232,7 @@ msgstr "code" msgid "An accounting type code contains only numbers" msgstr "Un code comptable ne contient que des numéros" -#: accounting/models.py:282 accounting/models.py:308 counter/models.py:248 +#: accounting/models.py:282 accounting/models.py:308 counter/models.py:256 msgid "label" msgstr "intitulé" @@ -272,7 +272,7 @@ msgstr "Liste des types comptable" #: accounting/templates/accounting/journal_details.jinja:9 #: accounting/templates/accounting/operation_edit.jinja:9 #: accounting/templates/accounting/simplifiedaccountingtype_list.jinja:9 -#: core/templates/core/user_tools.jinja:40 +#: core/templates/core/user_tools.jinja:41 msgid "Accounting" msgstr "Comptabilité" @@ -291,7 +291,7 @@ msgstr "Il n'y a pas de types comptable dans ce site web." #: accounting/templates/accounting/bank_account_details.jinja:4 #: accounting/templates/accounting/bank_account_details.jinja:13 -#: core/templates/core/user_tools.jinja:47 +#: core/templates/core/user_tools.jinja:48 msgid "Bank account: " msgstr "Compte en banque : " @@ -494,7 +494,7 @@ msgstr "Éditer l'opération" #: core/templates/core/create.jinja:12 core/templates/core/edit.jinja:12 #: core/templates/core/file_edit.jinja:8 core/templates/core/page_prop.jinja:8 #: core/templates/core/pagerev_edit.jinja:24 -#: core/templates/core/user_godfathers.jinja:25 +#: core/templates/core/user_godfathers.jinja:27 #: counter/templates/counter/cash_register_summary.jinja:22 #: subscription/templates/subscription/subscription.jinja:23 msgid "Save" @@ -545,7 +545,7 @@ msgstr "Vous ne pouvez pas faire de boucles dans les clubs" msgid "A club with that unix_name already exists" msgstr "Un club avec ce nom UNIX existe déjà." -#: club/models.py:144 counter/models.py:320 counter/models.py:337 +#: club/models.py:144 counter/models.py:328 counter/models.py:345 #: eboutic/models.py:14 eboutic/models.py:47 launderette/models.py:89 #: launderette/models.py:126 msgid "user" @@ -555,8 +555,8 @@ msgstr "nom d'utilisateur" msgid "role" msgstr "rôle" -#: club/models.py:150 core/models.py:32 counter/models.py:53 -#: counter/models.py:78 +#: club/models.py:150 core/models.py:32 counter/models.py:61 +#: counter/models.py:86 msgid "description" msgstr "description" @@ -693,7 +693,7 @@ msgid "Payment method" msgstr "Méthode de paiement" #: club/templates/club/club_tools.jinja:4 -#: core/templates/core/user_tools.jinja:59 +#: core/templates/core/user_tools.jinja:60 msgid "Club tools" msgstr "Outils club" @@ -1632,16 +1632,16 @@ msgstr "Filière : " msgid "Subscribed until %(subscription_end)s" msgstr "Cotisant jusqu'au %(subscription_end)s" -#: core/templates/core/user_detail.jinja:60 +#: core/templates/core/user_detail.jinja:61 #: core/templates/core/user_edit.jinja:39 msgid "Account number: " msgstr "Numero de compte : " -#: core/templates/core/user_detail.jinja:66 +#: core/templates/core/user_detail.jinja:68 msgid "Not subscribed" msgstr "Non cotisant" -#: core/templates/core/user_detail.jinja:68 +#: core/templates/core/user_detail.jinja:70 #: subscription/templates/subscription/subscription.jinja:4 #: subscription/templates/subscription/subscription.jinja:8 msgid "New subscription" @@ -1692,6 +1692,7 @@ msgid "Change user password" msgstr "Changer le mot de passe" #: core/templates/core/user_godfathers.jinja:5 +#, python-format msgid "%(user_name)s's godfathers" msgstr "Parrains de %(user_name)s" @@ -1699,7 +1700,7 @@ msgstr "Parrains de %(user_name)s" msgid "Godfathers" msgstr "Parrains" -#: core/templates/core/user_godfathers.jinja:15 +#: core/templates/core/user_godfathers.jinja:16 msgid "Godchildren" msgstr "Fillots" @@ -1772,11 +1773,18 @@ msgstr "Gestion des types de produit" msgid "Cash register summaries" msgstr "Relevés de caisse" -#: core/templates/core/user_tools.jinja:43 +#: core/templates/core/user_tools.jinja:35 core/views/user.py:145 +#: counter/templates/counter/counter_list.jinja:18 +#: counter/templates/counter/counter_list.jinja:33 +#: counter/templates/counter/counter_list.jinja:48 +msgid "Stats" +msgstr "Stats" + +#: core/templates/core/user_tools.jinja:44 msgid "General accounting" msgstr "Comptabilité générale" -#: core/templates/core/user_tools.jinja:52 +#: core/templates/core/user_tools.jinja:53 msgid "Club account: " msgstr "Compte club : " @@ -1838,12 +1846,6 @@ msgstr "Fillot" msgid "Select user" msgstr "Choisir un utilisateur" -#: core/views/user.py:145 counter/templates/counter/counter_list.jinja:18 -#: counter/templates/counter/counter_list.jinja:33 -#: counter/templates/counter/counter_list.jinja:48 -msgid "Stats" -msgstr "Stats" - #: core/views/user.py:272 msgid "User already has a profile picture" msgstr "L'utilisateur a déjà une photo de profil" @@ -1864,146 +1866,146 @@ msgstr "clients" msgid "Not enough money" msgstr "Solde insuffisant" -#: counter/models.py:57 counter/models.py:79 +#: counter/models.py:65 counter/models.py:87 msgid "product type" msgstr "type du produit" -#: counter/models.py:82 +#: counter/models.py:90 msgid "purchase price" msgstr "prix d'achat" -#: counter/models.py:83 +#: counter/models.py:91 msgid "selling price" msgstr "prix de vente" -#: counter/models.py:84 +#: counter/models.py:92 msgid "special selling price" msgstr "prix de vente spécial" -#: counter/models.py:85 +#: counter/models.py:93 msgid "icon" msgstr "icône" -#: counter/models.py:87 +#: counter/models.py:95 msgid "limit age" msgstr "âge limite" -#: counter/models.py:88 +#: counter/models.py:96 msgid "tray price" msgstr "prix plateau" -#: counter/models.py:89 +#: counter/models.py:97 msgid "parent product" msgstr "produit parent" -#: counter/models.py:91 +#: counter/models.py:99 msgid "buying groups" msgstr "groupe d'achat" -#: counter/models.py:92 +#: counter/models.py:100 msgid "archived" msgstr "archivé" -#: counter/models.py:95 +#: counter/models.py:103 msgid "product" msgstr "produit" -#: counter/models.py:114 +#: counter/models.py:122 msgid "products" msgstr "produits" -#: counter/models.py:115 +#: counter/models.py:123 msgid "counter type" msgstr "type de comptoir" -#: counter/models.py:117 +#: counter/models.py:125 msgid "Bar" msgstr "Bar" -#: counter/models.py:117 +#: counter/models.py:125 msgid "Office" msgstr "Bureau" -#: counter/models.py:117 counter/templates/counter/counter_list.jinja:11 +#: counter/models.py:125 counter/templates/counter/counter_list.jinja:11 #: eboutic/templates/eboutic/eboutic_main.jinja:4 #: eboutic/templates/eboutic/eboutic_main.jinja:24 #: eboutic/templates/eboutic/eboutic_makecommand.jinja:8 #: eboutic/templates/eboutic/eboutic_payment_result.jinja:4 -#: sith/settings.py:277 sith/settings.py:285 +#: sith/settings.py:278 sith/settings.py:286 msgid "Eboutic" msgstr "Eboutic" -#: counter/models.py:118 +#: counter/models.py:126 msgid "sellers" msgstr "vendeurs" -#: counter/models.py:123 counter/models.py:321 counter/models.py:338 +#: counter/models.py:131 counter/models.py:329 counter/models.py:346 #: launderette/models.py:16 msgid "counter" msgstr "comptoir" -#: counter/models.py:216 +#: counter/models.py:224 msgid "bank" msgstr "banque" -#: counter/models.py:218 counter/models.py:259 +#: counter/models.py:226 counter/models.py:267 msgid "is validated" msgstr "est validé" -#: counter/models.py:221 +#: counter/models.py:229 msgid "refilling" msgstr "rechargement" -#: counter/models.py:252 eboutic/models.py:103 +#: counter/models.py:260 eboutic/models.py:103 msgid "unit price" msgstr "prix unitaire" -#: counter/models.py:253 counter/models.py:371 eboutic/models.py:104 +#: counter/models.py:261 counter/models.py:379 eboutic/models.py:104 msgid "quantity" msgstr "quantité" -#: counter/models.py:258 +#: counter/models.py:266 msgid "Sith account" msgstr "Compte utilisateur" -#: counter/models.py:258 sith/settings.py:270 sith/settings.py:275 -#: sith/settings.py:297 +#: counter/models.py:266 sith/settings.py:271 sith/settings.py:276 +#: sith/settings.py:298 msgid "Credit card" msgstr "Carte bancaire" -#: counter/models.py:262 +#: counter/models.py:270 msgid "selling" msgstr "vente" -#: counter/models.py:324 +#: counter/models.py:332 msgid "last activity date" msgstr "dernière activité" -#: counter/models.py:327 +#: counter/models.py:335 msgid "permanency" msgstr "permanence" -#: counter/models.py:341 +#: counter/models.py:349 msgid "emptied" msgstr "coffre vidée" -#: counter/models.py:344 +#: counter/models.py:352 msgid "cash register summary" msgstr "relevé de caisse" -#: counter/models.py:369 +#: counter/models.py:377 msgid "cash summary" msgstr "relevé" -#: counter/models.py:370 +#: counter/models.py:378 msgid "value" msgstr "valeur" -#: counter/models.py:372 +#: counter/models.py:380 msgid "check" msgstr "chèque" -#: counter/models.py:375 +#: counter/models.py:383 msgid "cash register summary item" msgstr "élément de relevé de caisse" @@ -2416,12 +2418,12 @@ msgid "Washing and drying" msgstr "Lavage et séchage" #: launderette/templates/launderette/launderette_book.jinja:27 -#: sith/settings.py:411 +#: sith/settings.py:412 msgid "Washing" msgstr "Lavage" #: launderette/templates/launderette/launderette_book.jinja:31 -#: sith/settings.py:411 +#: sith/settings.py:412 msgid "Drying" msgstr "Séchage" @@ -2472,115 +2474,135 @@ msgstr "L'utilisateur n'a pas réservé de créneau" msgid "Token not found" msgstr "Jeton non trouvé" -#: sith/settings.py:164 +#: rootplace/templates/rootplace/merge.jinja:4 +msgid "Merge users" +msgstr "Fusionner deux utilisateurs" + +#: rootplace/templates/rootplace/merge.jinja:8 +msgid "Merge two users" +msgstr "Fusionner deux utilisateurs" + +#: rootplace/templates/rootplace/merge.jinja:12 +msgid "Merge" +msgstr "Fusion" + +#: rootplace/views.py:63 +msgid "User that will be kept" +msgstr "Utilisateur qui sera conservé" + +#: rootplace/views.py:64 +msgid "User that will be deleted" +msgstr "Utilisateur qui sera supprimé" + +#: sith/settings.py:165 msgid "English" msgstr "Anglais" -#: sith/settings.py:165 +#: sith/settings.py:166 msgid "French" msgstr "Français" -#: sith/settings.py:267 sith/settings.py:274 sith/settings.py:295 +#: sith/settings.py:268 sith/settings.py:275 sith/settings.py:296 msgid "Check" msgstr "Chèque" -#: sith/settings.py:268 sith/settings.py:276 sith/settings.py:296 +#: sith/settings.py:269 sith/settings.py:277 sith/settings.py:297 msgid "Cash" msgstr "Espèces" -#: sith/settings.py:269 +#: sith/settings.py:270 msgid "Transfert" msgstr "Virement" -#: sith/settings.py:282 +#: sith/settings.py:283 msgid "Belfort" msgstr "Belfort" -#: sith/settings.py:283 +#: sith/settings.py:284 msgid "Sevenans" msgstr "Sevenans" -#: sith/settings.py:284 +#: sith/settings.py:285 msgid "Montbéliard" msgstr "Montbéliard" -#: sith/settings.py:324 +#: sith/settings.py:325 msgid "One semester" msgstr "Un semestre, 15 €" -#: sith/settings.py:329 +#: sith/settings.py:330 msgid "Two semesters" msgstr "Deux semestres, 28 €" -#: sith/settings.py:334 +#: sith/settings.py:335 msgid "Common core cursus" msgstr "Cursus tronc commun, 45 €" -#: sith/settings.py:339 +#: sith/settings.py:340 msgid "Branch cursus" msgstr "Cursus branche, 45 €" -#: sith/settings.py:344 +#: sith/settings.py:345 msgid "Alternating cursus" msgstr "Cursus alternant, 30 €" -#: sith/settings.py:349 +#: sith/settings.py:350 msgid "Honorary member" msgstr "Membre honoraire, 0 €" -#: sith/settings.py:354 +#: sith/settings.py:355 msgid "Assidu member" msgstr "Membre d'Assidu, 0 €" -#: sith/settings.py:359 +#: sith/settings.py:360 msgid "Amicale/DOCEO member" msgstr "Membre de l'Amicale/DOCEO, 0 €" -#: sith/settings.py:364 +#: sith/settings.py:365 msgid "UT network member" msgstr "Cotisant du réseau UT, 0 €" -#: sith/settings.py:369 +#: sith/settings.py:370 msgid "CROUS member" msgstr "Membres du CROUS, 0 €" -#: sith/settings.py:374 +#: sith/settings.py:375 msgid "Sbarro/ESTA member" msgstr "Membre de Sbarro ou de l'ESTA, 15 €" -#: sith/settings.py:382 +#: sith/settings.py:383 msgid "President" msgstr "Président" -#: sith/settings.py:383 +#: sith/settings.py:384 msgid "Vice-President" msgstr "Vice-Président" -#: sith/settings.py:384 +#: sith/settings.py:385 msgid "Treasurer" msgstr "Trésorier" -#: sith/settings.py:385 +#: sith/settings.py:386 msgid "Communication supervisor" msgstr "Responsable com" -#: sith/settings.py:386 +#: sith/settings.py:387 msgid "Secretary" msgstr "Secrétaire" -#: sith/settings.py:387 +#: sith/settings.py:388 msgid "IT supervisor" msgstr "Responsable info" -#: sith/settings.py:388 +#: sith/settings.py:389 msgid "Board member" msgstr "Membre du bureau" -#: sith/settings.py:389 +#: sith/settings.py:390 msgid "Active member" msgstr "Membre actif" -#: sith/settings.py:390 +#: sith/settings.py:391 msgid "Curious" msgstr "Curieux" diff --git a/rootplace/__init__.py b/rootplace/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rootplace/admin.py b/rootplace/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/rootplace/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/rootplace/migrations/__init__.py b/rootplace/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/rootplace/models.py b/rootplace/models.py new file mode 100644 index 00000000..71a83623 --- /dev/null +++ b/rootplace/models.py @@ -0,0 +1,3 @@ +from django.db import models + +# Create your models here. diff --git a/rootplace/templates/rootplace/merge.jinja b/rootplace/templates/rootplace/merge.jinja new file mode 100644 index 00000000..23084e73 --- /dev/null +++ b/rootplace/templates/rootplace/merge.jinja @@ -0,0 +1,14 @@ +{% extends "core/base.jinja" %} + +{% block title %} +{% trans %}Merge users{% endtrans %} +{% endblock %} + +{% block content %} +

    {% trans %}Merge two users{% endtrans %}

    +
    + {% csrf_token %} + {{ form.as_p() }} +

    +
    +{% endblock %} diff --git a/rootplace/tests.py b/rootplace/tests.py new file mode 100644 index 00000000..7ce503c2 --- /dev/null +++ b/rootplace/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/rootplace/urls.py b/rootplace/urls.py new file mode 100644 index 00000000..19b6aca6 --- /dev/null +++ b/rootplace/urls.py @@ -0,0 +1,10 @@ +from django.conf.urls import url, include + +from rootplace.views import * + +urlpatterns = [ + url(r'^merge$', MergeUsersView.as_view(), name='merge'), +] + + + diff --git a/rootplace/views.py b/rootplace/views.py new file mode 100644 index 00000000..6a5259ed --- /dev/null +++ b/rootplace/views.py @@ -0,0 +1,85 @@ +from django.shortcuts import render +from django.utils.translation import ugettext as _ +from django.views.generic.edit import FormView +from django.core.urlresolvers import reverse +from django import forms +from django.core.exceptions import PermissionDenied + +from ajax_select.fields import AutoCompleteSelectField + +from core.models import User +from subscription.models import Subscriber +from counter.models import Customer + +def merge_users(u1, u2): + u1.nick_name = u1.nick_name or u2.nick_name + u1.date_of_birth = u1.date_of_birth or u2.date_of_birth + u1.home = u1.home or u2.home + u1.sex = u1.sex or u2.sex + u1.tshirt_size = u1.tshirt_size or u2.tshirt_size + u1.role = u1.role or u2.role + u1.department = u1.department or u2.department + u1.dpt_option = u1.dpt_option or u2.dpt_option + u1.semester = u1.semester or u2.semester + u1.quote = u1.quote or u2.quote + u1.school = u1.school or u2.school + u1.promo = u1.promo or u2.promo + u1.forum_signature = u1.forum_signature or u2.forum_signature + u1.second_email = u1.second_email or u2.second_email + u1.phone = u1.phone or u2.phone + u1.parent_phone = u1.parent_phone or u2.parent_phone + u1.address = u1.address or u2.address + u1.parent_address = u1.parent_address or u2.parent_address + u1.save() + for u in u2.godfathers.all(): + u1.godfathers.add(u) + u1.save() + for i in u2.invoices.all(): + for f in i._meta.local_fields: # I have sadly not found anything better :/ + if f.name == "date": + f.auto_now = False + u1.invoices.add(i) + u1.save() + s1 = Subscriber.objects.filter(id=u1.id).first() + s2 = Subscriber.objects.filter(id=u2.id).first() + for s in s2.subscriptions.all(): + s1.subscriptions.add(s) + s1.save() + c1 = Customer.objects.filter(user__id=u1.id).first() + c2 = Customer.objects.filter(user__id=u2.id).first() + if c1 and c2: + for r in c2.refillings.all(): + c1.refillings.add(r) + c1.save() + for s in c2.buyings.all(): + c1.buyings.add(s) + c1.save() + elif c2 and not c1: + c2.user = u1 + c1 = c2 + c1.save() + c1.recompute_amount() + u2.delete() + return u1 + +class MergeForm(forms.Form): + user1 = AutoCompleteSelectField('users', label=_("User that will be kept"), help_text=None, required=True) + user2 = AutoCompleteSelectField('users', label=_("User that will be deleted"), help_text=None, required=True) + +class MergeUsersView(FormView): + template_name = "rootplace/merge.jinja" + form_class = MergeForm + + def dispatch(self, request, *arg, **kwargs): + res = super(MergeUsersView, self).dispatch(request, *arg, **kwargs) + if request.user.is_root: + return res + raise PermissionDenied + + def form_valid(self, form): + self.final_user = merge_users(form.cleaned_data['user1'], form.cleaned_data['user2']) + return super(MergeUsersView, self).form_valid(form) + + def get_success_url(self): + return reverse('core:user_profile', kwargs={'user_id': self.final_user.id}) + diff --git a/sith/settings.py b/sith/settings.py index 2f773bf1..ae7a1271 100644 --- a/sith/settings.py +++ b/sith/settings.py @@ -54,6 +54,7 @@ INSTALLED_APPS = ( 'eboutic', 'launderette', 'api', + 'rootplace', ) MIDDLEWARE_CLASSES = ( diff --git a/sith/urls.py b/sith/urls.py index c93f36c9..97ac0af3 100644 --- a/sith/urls.py +++ b/sith/urls.py @@ -29,6 +29,7 @@ handler404 = "core.views.not_found" urlpatterns = [ url(r'^', include('core.urls', namespace="core", app_name="core")), + url(r'^rootplace/', include('rootplace.urls', namespace="rootplace", app_name="rootplace")), url(r'^subscription/', include('subscription.urls', namespace="subscription", app_name="subscription")), url(r'^club/', include('club.urls', namespace="club", app_name="club")), url(r'^counter/', include('counter.urls', namespace="counter", app_name="counter")),