From 80042faf0fc36e28dbdc396bed0390940270eabc Mon Sep 17 00:00:00 2001 From: admin Date: Tue, 30 Jun 2026 08:21:14 +0200 Subject: [PATCH] =?UTF-8?q?Saisie=20unifi=C3=A9e=20+=204=20vues=20dashboar?= =?UTF-8?q?d=20(mensuel,=20d=C3=A9clarations=20URSSAF/TVA)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- compta/__pycache__/models.cpython-310.pyc | Bin 12868 -> 11824 bytes compta/__pycache__/services.cpython-310.pyc | Bin 6388 -> 9595 bytes compta/__pycache__/urls.cpython-310.pyc | Bin 485 -> 659 bytes compta/__pycache__/views.cpython-310.pyc | Bin 3539 -> 4834 bytes .../__pycache__/views_saisie.cpython-310.pyc | Bin 3547 -> 5423 bytes ...tegorie_alter_depense_paiement_and_more.py | 34 +++ .../__pycache__/0001_initial.cpython-310.pyc | Bin 3966 -> 0 bytes ..._alter_entreprise_activite.cpython-310.pyc | Bin 1144 -> 0 bytes ..._depense_taux_tva_and_more.cpython-310.pyc | Bin 1613 -> 0 bytes ..._paiement_depense_paiement.cpython-310.pyc | Bin 1607 -> 0 bytes ...va_alter_depense_categorie.cpython-310.pyc | Bin 1793 -> 0 bytes .../__pycache__/__init__.cpython-310.pyc | Bin 170 -> 0 bytes compta/models.py | 126 +++----- compta/services.py | 282 ++++++++++++------ compta/templates/compta/base.html | 81 +++++ compta/templates/compta/dashboard.html | 141 ++------- compta/templates/compta/decl_tva.html | 67 +++++ compta/templates/compta/decl_urssaf.html | 43 +++ compta/templates/compta/mensuel.html | 29 ++ compta/templates/compta/saisie_paiement.html | 111 +++++-- compta/urls.py | 3 + compta/views.py | 110 +++++-- compta/views_saisie.py | 218 +++++++++----- config/__pycache__/__init__.cpython-310.pyc | Bin 159 -> 111 bytes config/__pycache__/asgi.cpython-310.pyc | Bin 0 -> 281 bytes config/__pycache__/settings.cpython-310.pyc | Bin 3237 -> 3189 bytes config/__pycache__/urls.cpython-310.pyc | Bin 601 -> 553 bytes config/__pycache__/wsgi.cpython-310.pyc | Bin 0 -> 281 bytes db.sqlite3 | 0 29 files changed, 808 insertions(+), 437 deletions(-) create mode 100644 compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py delete mode 100644 compta/migrations/__pycache__/0001_initial.cpython-310.pyc delete mode 100644 compta/migrations/__pycache__/0002_depense_activite_alter_entreprise_activite.cpython-310.pyc delete mode 100644 compta/migrations/__pycache__/0003_depense_regime_tva_depense_taux_tva_and_more.cpython-310.pyc delete mode 100644 compta/migrations/__pycache__/0004_paiement_depense_paiement.cpython-310.pyc delete mode 100644 compta/migrations/__pycache__/0005_categorie_entreprise_vue_tva_alter_depense_categorie.cpython-310.pyc delete mode 100644 compta/migrations/__pycache__/__init__.cpython-310.pyc create mode 100644 compta/templates/compta/base.html create mode 100644 compta/templates/compta/decl_tva.html create mode 100644 compta/templates/compta/decl_urssaf.html create mode 100644 compta/templates/compta/mensuel.html create mode 100644 config/__pycache__/asgi.cpython-310.pyc create mode 100644 config/__pycache__/wsgi.cpython-310.pyc delete mode 100644 db.sqlite3 diff --git a/compta/__pycache__/models.cpython-310.pyc b/compta/__pycache__/models.cpython-310.pyc index 0b5461e458b4b50bb3311a86f7622822e31ce225..42213d9a2cf58c7e491eb9b6a1160476bbd8cae1 100644 GIT binary patch delta 3783 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u4u7MDooy)=7 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 literal 6388 zcma)A%WvGq8Ru)cTx#{SY|D=%%+uQ1UMoqTu434h?K-KWq>*X_DuOM=8A;r5AD$r{ zTU`|{WS}lkAKK)YgM6zl?WHJs*B?=U$F`RM1&YF#B1q!?z9DzD+O>i1g2UlFzxkfO zZ(5_Hu7;oUugmqn9n-Xb&`1Bzz{f>A@~BTZtE+f%weVb+R7Mn*$Cd_tjtF7o?v5a z9Pdds!6xyZVpHr8-iO#UJB;@zE zi1%^!(vntrIr%pZ;@)Vjhqn^$t%Wl1BkukE-e=xBm%W8nqaFKI*S+jZEV<>dv?0)lh81eY;BT0-z32Ho!_Q5>H2gE+h$#(TRMn>0BHjRkKDTCL5K*yDG& z2)OXtQ6fX02tB$OBw z`%$R)?1B~Ed9aAyK`V-I?7dx1oI!(3gjxtv=*b54)2h=?;?~&+QV$u;nDv7VKZb~| zo(mi8mWUPSX%%#{1RPuio3nM-Kz1T%7VVHvY$}$}fuFWMiSfoM+B4fSa zg|v80otD$+Hg8Iv+LwI+38$k=P=h4y_B)p{HRGGsTy^gCfaZ69R5$S~;E_`(I$FCB z!%b}KJ9=z9(qW3mVye$oOhJw%b#5g-G+>jb*22}rWsp|5?MDgU)xIFQCh-p+}t*+ONjuxABtD`ghp{dBIn5hds z0Brk01hHl}pzZS>wIdm!ujp|giRQFWs zL`HU^d}~)7A*0$9MGO{)sW?K#3>C!OKB~r8CUaR+M4v*hV()}5PNIH*5RxxFijZ%i z5<|)zecRXpYafBpvB`{W1crzDmccAmxUY4LE7~^6-cDY`gezeXd%wti0?&Nx^1kVA zRa2+inYs-qDO9Ak5${4dlutOIawKh%%Mu>>Jcbozpp-Q5!W$T_IP$!<`C8zEni>qP)+Mt#AGP3`<%R10 zl!~QS3-5zAlV`_NH($AF_=lLfn=;jLCgb>bty=yyE*wKe|=r>^)B7Y+;;nk zH)+6tJ{1)0-@!7ENoSAfWnG*@<(XuPS@eR4bfhcq_%=)qjfxmSCl_a_7}#kH<7#lv zHS(QY&eM=NDoA2O+jZT-e5#1m&q_d>A63=5Y%3qPr+N85}?WU${w+se_-qjc35GUw_`w@k6 zI2f{`RA+{_WQlZG;S<)DYetqf;i zU-4Z`y+k`YD5ectx1KnLYJH{z#G4pev65?e5pNN+f}~CGh~n>gZ$I|V<`FllfSm&( znC%PW&*ZT9HueytjNs-pv4Zh#q^~LC6yKu}2Q^Fl0B!G3`*{?bFCLutECz}n(u}vL zIJjJ@6m@^&>onv76&Fz)VAFy4Mcz8@sc4KSMy~c~X0&=j+bQq@Px%G&1<)w>rgaz6C&$I zNU#|2{q;9I2v$K{7$f6%yg6XHy5GDVa#0~vz5ra`7jwK>f~3lra||sj)pT+Q3P6Lb zMk|zp3`}`ULD5?o&llvGGy5PGR2=31#jDgxUQoP31v&qH(@G2E?>2xfdeK0TZ%T`R za5CYMApbq6OQTt6$cNm-2uXOsF%c7<*aph45gBX;G2*yx+_QA?BXkF}b@xXUj^Uwm z$bX_BC95ND@0d_A?U4Z?XkfOE7@>b|K@^6lb*vq{>jj7d?e)U678fbjpCG{)bti9k z2HH#1o{hvPh;5+XMXM`r1Qy$KdjsQ(J0sN~7;sh7cLgcSP80V;N zuV%Q|e0^?C3BEFtj!+6zLrNw6wREH}7fg$)=XR7H?j?dXB!WE81JlX>zV53FQIyKjMoZ3m7lwSN8U72qncO>4Wf=o>Ogo-5;&{@SABk;xB)TmtA_o+7C zD-`A(XFT@9sE3x;(XAr&lraXpOaJNoG4vVTcw!lrG4ljr1PYA*V{69xZ#IrfLwCNk z9Pu-(a=`bH`O$u)=JG2PUEhOX4wZotX?0^mzYPD92Y%!O^#brApehCbEo)u-jRrM@ z!omL>*(!95onogDJLIQe67~aarxd%LLI?hcdLHT9@MMVBFZ^0tgC}G5eP^o}k7QnY z%ZbZ*trU;uH8&pXIGqxC@GUj&ffkRmB6IF*4>Y0zAY>vN+kuZ$R8NXJ>MqPdXCtVU z(TX)XBOO`|fp`4U=;RdQX~pkUGLeG{@gYX{P+0^FZ3a=E`6&Z%=W&mbB@&+zsdVU$%)bs}jkt&+ z!)tzxTruwNMCu~u<^i{INab9B$~?}pNy5GRDK@SWaIwUh(u?e#!^RyBZ!+by`nIE; zl$T9Szj-&~8XZ){>q@!roV0zOai*ni+9}gwc3L=Zw22j(%2A{)g@i06LTo}kPaqJE z@BtW_RRHQT*#tCGDU8#V#IMmL3E8{GgfXLAMp>VL_LL3Ra&;FOonadA#S`$*wmt#S zPha$RspUW76HFb_D6-ZCTA0$a?hOXr%aBKYXzmo4$t-wUJ+_rM1==N_NdEC5W$z_K zuRQrAqfd_wA+%a8l(<5R#AWKY=;mgK3es!k#0Ye4keL^<-0Sz5SyTLi)47Y+(^7-9 zG!MzEdx5`Th%)CXZSz%icOq*nll?>3zB*!yU!rNTaw09(YOEF1YH5*W*Mgab zPB6gZ^;VUwRvRtGBdL&TdVH|Cx`vxWbqKoZk0 zDNEG9#g8Ns4-~0lFHX!X&P)Z$Ko}GMCNc6&PGB@wEdu$jh!;fg0f}4OrA0Xfi6tee kMR~<58H$8J5<-*LGAhYP0GT`tJd8Yye9U}|$dKa)09g)NC;$Ke delta 139 zcmbQt`jlBapO=@50SIQlbIRJt$iVOz#6bp}K#l_t7Z**`wqj(Nn5HSroW+^KpTgeD zoX#4>l_C(#peZ=B{EZrmKM#$c;8A}C;SOi1b3ku5WnK5cWyE`_*Yyv4%lm224G%)g z2Wg=j9Yo#YK@shc7t&I$n|{3>6w{08`SzG3+*;IHtjmcPoc@lC9|hQ2NSg6-P`{~~|M@>}3v=GQI% z0%vH>tM%!9p-!`&5)IdcQuMfxX7yO; zR(9ACZM3jRegG4gKUNQnPgqBWaLZsFA?piY0p6RfR3Z-N;?BogSt8Cj7=&ILQI zj@U69xjN9HE^w>pbH=^TLI~P9GOLsOnBMoE&vgjO77yg^@>Qa4Lf zD@_!T6j>Wc(x36-G?hi-*7E5so)Dap!&E5a(*jczy?$3nfYA8&KKj{S!?Q=)Q{>B7 zds!33)?utftF0fA0u&wOx)F0(%|78X`CW7^S&HyBvy?`IEOl=2#+|++>za+=#eRwsv9Ecwof?xwkp!{anNjMGNNcpt>QKFR$4S5F7VyBAC2?G8Zh z#fkAh=qEi((|&+CyV+M?0@r)u)Oa7Y!8NmiU3}-Or|l&zOg(Q8s}!O67+Ux^JY&!X zJ7<4%KY_2cs~{18Q)dwzd<||Mvbr1nvl7T3VQ5KpWDJtTde+aue0u;* z0dwYQgt8n=0E&Ne85}`#xIO{LL_-VEC``JHFSCaVPD$2VPz~KD*UyTM;ahQks%H5X z4PA;QRru_5LMm=xhV`PkGU`x>yoLTHC6EGUUL?`@FO=X=9Os@6#|6a2h~gp?QnbqP z&%HB$(wJDn43Jl~14oIBdU(EX0&Z zk>)VHQMs%+0>PzdEU%%@1lYpll6KaltJSdySmy}J4o+E|$yf8BZX2T3obGWf+20e)9Hn3VSFa#II!-(94`0`zlM)lk3Uy`Sp0s)nF zSOEB<888HAu<3H<`ne6>qa**5A?{+}Lp)3FziEvSO^rcwmW)BsjgqT%|JT51n=L*? z2I1bw`4e)$=Tx%*zaxfPCbZdsGC$!}i%0s6wi>*>-9Cw>68fFKK6?Ev1ldX(AVX?x z>BpyHcs15KZlA!{_1oJ=Nh*4ASG@D;Gxq9m{dPX_^*fY5RYD-_lSF_2^3M%d`ru6P zP$tN~u8F2=5iU@@U4o+VTae{rtgz)x3GuIl*e9FlS-Lc#kLnbA=|u!mQiJ=t8imft z#pT`4J?qXW))3hc+dg&S&_ElKWAFtEJ0wClKH=%Y9Y>^|JI=>10d*7+gH91osZ$;W z;|eO3pi{+DLuS(RvSvxM$PM>^#lm`N!1DX=5^~~SFjh?Z$pE6VAB2Bu`gKQK{ z*rduLGvVzt!Bi9NwskVV;IMiJsbgDo6Q!WZ;cDLOwPSP$GJpy9bgcW@xOY)r?myTC z*}rYVw%w_*p5vGb5UV)jK>`z63rG{?@9be7Io6Njw(i3eC@=F9Q2f*ol&FA~B84d2 zgtiLGgiDA0O32GCA4 zrQMu|_bfDY`*~NoN5Zg&h8Z6rtzVMmZY36e?jCv2+H(em+JLl4W{2#eYu@qbXcc$+}Bx};5&(9MwNQnSxVNN zF5rUqVK`*se1mvfih+RZOH-rcu;D~=rcUj}4NaALW*g=U<{Rd1NIco_X|w=EQmOm_ z5%OmFYa-<7@;4yQnb$BS$-=TjWI18sTPrs(gi#o_^&;i13gQLXcg$b%1uR~`@?DAH`F$sv9IWccbfrQJg$0oy;U--n5N{q3ZSh)-3W zSxJ-Q9>T~pD3~Cn;@JeKoyoB^j-8Yyrre$%YYKK`keVwNg>)>w*5=2q#R!Z@Go7C&x*R>!W19l#2VLnHj_kOE05r8b)&6{tnaCb&y4% zRov^y&LINHrdP`$_j@|*MGJ2eQ2^8l;yfj3bDekM-f^}=iB%?tJMgTN9h3|iX;>;r zIn$IUlTPseMxHzClOA<~pw{kdl^55P0!>JTOsewF+9&AAqk4fpR$K4im*1hT4I(xj z*iwZ)AZ!6`3p+^#MedRm39ZXWy2OGytozw7{pcnR?5E-u{lB1YfJFEcqT-V05e?nQ biwcqNmP^cC!BfIhDY2;T7Qof;RBPF6|MVoEf6g<;|Fl3foGPhcci@KiW7~Sbp zE;60ANQQU#*BYxP2v47^HfUEc+G8zF8_lTCg07&hj7jG2A_p1BMK)qH$%0jNFdi4YhV|i0`8T7IvDCQH+OppvQXZh(g;B*a*<%%Iz)Gkrke0#xqMfpTQ9O1 z>DB~D_VBS+SYG19=!P^=NZpaLsYh}{bVa*8KQN_DY_@%9#daw+&!**}29pZ3sn|@b zYBmkC8ECWHdFg6rG1lRyV1J<4O1>+^C+0@npM;>9Oo4e4u!K_*UYBq_x$a&^SCU8W z;O1e***VzH(5D&Nn&jQ1cY6^Nja?`R%HIGxE@igy91!QjcLrAhq0Ij@bhZH`9^L;O z4E~+F)P7p&B7cZSLpbe1Y>as{fd=;(k8&2{QPl(5HTet3D|8eVYR9#0OY#&Oz>+&+ zDKo@w&|q&uWb7>o7m|7Z=-K)8k5(3fzKFusPQOJcU3hKd>SC)SQ?Bn-+Qmkp?id`C6 zlF~>lQii@#(^T(``+uyJZc}63a(%{muysf}$gZAeH=tv01MHcXR~Ft~-z%&K{ksbr zAFi#ftY*n4rQh~%OUv8@)9hn;P*%L+Iz!F$}~$M8cGDe9y3VOwKcV=3NzIkI9!cCZd)Ik0308N@qiPBp2f zdZvff)wDwN0EwwR?16xQAcr6kFmnmOb&h)pc2D6Ve?U%)IxUiug8&QcVX+AkE5BFW zLs4=v%`R5GdiCnn`>OXknVzm0_`Udl-`M<*lZNp-8XWyu7`%Zu9iiby+u)3wG3%La zvuCv}Mq?|sdzE%Y_wCr}xoubXE3wzBwyV1D#I;_%UGGh`r!aQ87f<(Q+B3b`_H3`w zZuI8bbG`ZYd~cz>&|7RT_KvlW^_JR8z+K=qUVp&a$N3bWeqgkh`68d?4M0}HDxc%? z4~$*bKEY>h8_i>x$qb`*JBZRK^s|1n9v$q5{%h!lelj@N7lDkDzP}dqyFnyEKaDo} zz-ZMj`R^os8T6%(@dmKgegD!mpC9ZG!hRZFOq>42gZ*wC$gmdzYmlUAv=)cWqe}sd zCM(y2uFSB*#ciO6O+VNUyM9+3>=zvH(C@|(Q92m;ao}$!G5C17=Ksy#yYKtGB-<{w z?uVHq!r}dvFT&nhl8LmK0ns}jTn9^^LRm)BRPLK$H|hnkYP{Xw4&sQvI|vIAG?}tF z4uk(*FHF;5BTUhsiIa_}-x1+f2Jvu^a75VWp->)%0CnZ3kg(CLs@hw9urUx(8Y<_8 zK8td{S)5u;-GpGn$;kV55NQsHAuUhkUGK_hJCdQQz87vpz3|RBvsf=DYDCy9e@s4A%x(Lj*dtC{N(gSAYCZwZkI2*ZL9mBYjJ zAd~xsSOwOfmbyuAAcI%7qwu4&lWNmz4Zg4J4iCGFz(~o%Om>8Nq$xXC2-_LrXNIIQV212GEMe?p7A>UEfEZ4*|IDrY^4B@Cm|O|rgJPIx~`W!kihRh6LM4?|_I1!*YGVWAdQ(IC!1xSeu$ zc})9G#hM@+00|+pG6Z>x1N&PQzmf})47)0$mDFyq_t_w3SI}2 zbUEa($2L7~^U5AOgxTELFuA*D%Ic^#Hb(U^B+I>@d59#{TlyS#;1d#t~h4oi2i7cwasTcuE9K6I#Nv$mXFhlR5t{z6NlY zV#DM|TA#+N&DyRMW=&o?WVx|vadyuV^U}@@Zc6y^F54xOWt$FW-I2$wG2`|g8&$Ca z&Y)qHF&i{xjZ`(70^BO$4&Ya~JDLW}mNO;P8-wbwB4=}p8@&3@<_fNgp|fEO-P}yy z$=y+d*T#$)xt-q8qq-hd_|!d9e5fJQ8sZ9>yKfpFdP7gnjppH+-WXTSR`7&>+H=>W zGw>Nc`@oP3n~YBTChb|xiKhLKiFfkq$Se74jA5$7T3!>Mp)XAY%*60}b3n#0)==k%C8WCJ_5wU!!rWjL45$&>udF3V@}-`{1c#&KiS7@flC z>@LY>UY;%`QkQ4IrL$}1GmwtO&++qn?r=f+dVV2ql$7T4S$<&;$wc+5;%Xxkg`ySIgpXW{Vm&*Q&=tB#z!}I(l^q0&2%jg$p zJkKwozf!_m=%0YCt;!4Xxg5IMWw0PQJP8S%1Y~8G-7{bj5A&7$*IWN)S%Je7yK1%QABa6r1;Bxsif$RncnMoIrHoM z_+&=ImrM9>@?{M-<@1{_l>E(>bN`*smTSOYeyW^3QO^EPe&We2zx*@o$iV&9$MPb- z!oTpq$XCe&pdGA+HJr&;ADZb>X;-iQJ-d?4{7k;MT&#pG+^ak^#p~s&f5^{3hhNMW z(SE7GL+{Iv+`mihs9!Deukf$#Rfaxs_Th!$S@~jVV?HeFEO%B6w6E#*YtV!__$TfH za#cfK*X=dketpH@Z|v1@UUTbPrJY^ZcJ>WuV|ACV?;B5BTJz0Y%KrAdx9{w%mU%;w zF?0rHiqyI*d$GQIaP^pjdpcMvH;&fAI1ZJerh7%A+L4J2;!i1-XLO_9KG>(L0O>7q zUCMW#+(E63V)1*F$WmJ7$N1Cq4)%SX`G;Ez3g6jTG&e&eoMk>7`UBi_z)Jmtf2GW` zpT(`GB0XKj#K&#C=`xxKmoZ``qpWKacAv8!nOo-V;!dGF3R#wT9f;_gt;>papS^Nw zS9#QxB%*6{x2LqlvaxgiS!rF3lWq{F*IG~TReig2c!MgZONpy&I$Ea1)kg`Wcms{H zQwT*>579G9*xq?I)&A8denvN||Eqt8RO#vl{|EamHTw)Zs)4O_D%|N4kszC7QC2_c zDQ_}cLbV8XUcG_qI*n2&ZBi5HT#Aa3B=hwa>7}qw^Ci`ms%>{B*@SYoJ2J@biv>E- z^fvMcDrE;I@ z7y2aQ6Q7qc>YPa>wPQA0sv37Du27GUsJRwDk%t!}AEKH2|34)Acai5XZmbv|;1=34 zIqquceFM|Cw6$YEsgYvwv&LV&x)Jun`vY<9$3&4_%lyHTv2*Sjb89`Z{>g&uc_3fk zOi;A=K?Wy|a72m*vG#mah?A1GwR7xd;HOEi2UtjQKxjz)i`fNG@PGU9FPc7i%Y{`I4X#uE@BpAVvw;3+3H}3zxu@`04~(Tt!b=8=+K{^++H_kmhuvEncJEacZdi zQ&vPS6bz8?b3r0gm5^#oovo``ov6cC5Zby~oC@MeM>2S&a?`Af>^>D9k)v8bO=Hha zB07k5z|h89)U~A-?u5E{(^Zt9oLn_f$|JbKO!Pg1=s{P6azQ9YbW;@(^ikwkG2$J` zNmXxMzw`FHx31rO3(-64DJxh@1$j~P991(Y;c#AELFlTH@xqzj0ET#h2)H<4h))%Y zp)5qeO_haK+|hGoTvM!5Ft1IaO;eVH9!&%dwyvuB%Ijcf<&{w&Rkg$5or){8+ZSk= zSDHbpY?ubB)xx|LixdO%SdCJjY+uB*CZgu)sz-baFrtJ-0 zlG=HU!rG!89Sa4vrcOLgff6|Wvr_?=k3CbLyvg)^(qy-qtIF+kc+%~3l#6c~^g&5m zSMim^qq{*O+7_A04Bv$1BrU2K@$sZE?$b{LTljo(w*^uOK1L-X6%Hh<8YNPCHxaU% z$+U1UvT`_F!b3QM_CsC%k~eEp*3O|VQCGi5ieB=x!sNA!l0J{$aiJR6sH4Qt7m)1M=bgDB2yvp6AkxqA})0xgp+Uzvb+0JY_*O^P_JM&nd zi+qmH@Fu8Be0sxZ%@;p|O5UAdJMwwt=Rusuk$=^X(?KSr z{{#Q{C`8btj(%G=&E2sK)!s@{si zI1Lije80aNBr(4|h(sXctlwhF;t-5qH;wWf3gm#NlWaTgcSW>QKt6{EM?`%d3FQI> zDU{C`$##_YZeNOMAmThy?wzq}bfQ*5xi>=@@5VAxwGX51IF0V^2CDWBj@-^f94U4! zG)c?xhu|8}F13@97rf*6hn}v+t6(SS3Z3R|R4v)5i zLLM1n8C+j4g;_d~!PVV3`Y7*?T+|*sR#uls;WRjMiawLAvL>5i=5uitXh>F?e9i!9 zVCc(PI9eJ#M%USArO~T!_R#!%jp%P&-e^<03Tt4a;#&s#0z+6ijT`? z{_)Os*%&s<=HT~yR!gXA34bc9V+q&`xvr)TOZO#eO;bNFYnp1x+1}h(4^OZBwe-gP z(4(9guTAN-f0WR8q9dRC6!v1q&bFNA^L*inQ8sSjy~+i;!GXCyi#yMiX8vV4H*E4n z;w`QGnZ}kh*2w>*u`?qK{+yAE{;j9>{vzG${u}#C^1^s^5$j9bUNOprp#$jBigC#B``QXl8{FeW z`!-gW_;>XDx}M)yG5Fe3PiyoeXt)ZUTBU_MT!yzA2Mo}gpV_3dyvVN{unpr>Z&(yM zx87P;)}7lMca?{nHqInSLFVA0u<%$^HHkN)B#D%v8fn&-L0@)dCh$DX2E`-VH5Q|6kah#n#4&;%YJsX@tmQ#J_eX+{k6Nv&SS2;4 zPULrEuH2IYkQlXiOAD_~L{}jArN>X{hq6shgA&TGk=O25@$?xtR*ZYdT{|X+fm)`x zg5`Ccb&!k9_BF*4jg6)itg71e?Wi9;8i*U;CxK)s^H)p8-lb7u>Q0J8`+AavL6YBS zpKK`3gL&ilC_xrS3Ft>CDpW%z#%~vw!0;i}ka!y_3I4PTj*om^oRpt-@jBRU_A=y6 zKPdb(i(xksmFR7#W0O+U-m|W?uYGrK@q@t6voxh5M<#~!++Qm$LyrIDZ~oHqu{q3y zi1IcwbQ{)?$YdS|M+)dpzC8{kBRNQbK^Yysv#c8H2H={$Zi`L+9NS} z#NB69LwZPQCHtefV{VM{KUD?P3~>S6a8VmSiiNMb#k&wR8Yp*JE z>Xj3IW%Fn=mZ((ZA07KT@`MjmSvmY6)@Y?g6WL6>NfQN&cned@(>s+r@%^a`RXd4Z zyL>3@hUygA4T>qqpA>ihi_iVyA_)E$e?!=iBp3$45y^3%LU7+N zsMejtz*^pFtC?5q(DH=`CRNQgdx*wdRe}VCH;v?jj4Nj=PSEp+bHq~Fj-;};Vu7=C zUa)1xgU6~C=h;>!(m;v@@ad|ns$fo_m9j<9-;PuziTcV32Ixt+pjxeJ>o@Pde|!Dr z2k*kIMLOyZD#2zhD3X+oV-bi|g57S2v=cp&g3^Pi6U_-lMEw}qMOE`64ADFZ7g*~I zH5ofagwyM%k18MehVd7C>W;HluDlcX&cYqd^;Gzg$^xTaJXLACO|P zVkxc|Z=$QA*QKh^o_ayWB4t5zF1|;aU!{ps&uJbJ*MTl#^@V;91EKt3ZaUPJ1HS4*$U1YSYwOt$(92ByZ^6=^B7icTdvnFy7j3b&pzSsl9Q zkI*9_z1p8TNUP5JU6d7@th=S`UATt2Ub~>4x8(?Gz{SsLx<}Jpn&>cP4YFL~ZBaC< zYWN_F;cFd4qj>lsalWk=qeo#Z_c8J%Or}X@bhP0B zJ@-tLIbR9lX)#@;T5O%-tJ~#S*zKw+I#25Bb@YyUS(mzbiJ%!hl4;(<1IXGG!Dw+u z!M*x2v5hA8K^vkn{7;aHTsSyTHOEZ(1N;XP7KkA-zYcLc(2Pe(uHUYH5BeQw3%y_% u>VLH-^$lLQxwe6>V(xWFDw601RbOob%V<^*bO^pC^G&Z}Ry{WT-v0rxfvThc diff --git a/compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py b/compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py new file mode 100644 index 0000000..0813f64 --- /dev/null +++ b/compta/migrations/0006_paiement_categorie_alter_depense_paiement_and_more.py @@ -0,0 +1,34 @@ +# Generated by Django 5.2.15 on 2026-06-29 22:49 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('compta', '0005_categorie_entreprise_vue_tva_alter_depense_categorie'), + ] + + operations = [ + migrations.AddField( + model_name='paiement', + name='categorie', + field=models.ForeignKey(blank=True, help_text='Surtout pour les mouvements neutres (remboursement TVA, virement…).', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='paiements', to='compta.categorie'), + ), + migrations.AlterField( + model_name='depense', + name='paiement', + field=models.ForeignKey(blank=True, help_text='Opération bancaire dont cette dépense fait partie.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='depenses', to='compta.paiement'), + ), + migrations.AlterField( + model_name='entreprise', + name='activite', + field=models.CharField(choices=[('vente', 'Vente de marchandises (BIC)'), ('service_bic', 'Prestation de services (BIC)'), ('service_bnc', 'Prestation de services / libéral (BNC)')], default='service_bnc', help_text='Activité par défaut des nouvelles factures.', max_length=20, verbose_name='Activité principale'), + ), + migrations.AlterField( + model_name='facture', + name='date_encaissement', + field=models.DateField(blank=True, help_text="Le CA micro est compté à l'encaissement.", null=True), + ), + ] diff --git a/compta/migrations/__pycache__/0001_initial.cpython-310.pyc b/compta/migrations/__pycache__/0001_initial.cpython-310.pyc deleted file mode 100644 index 665cab39f1efb15826a1fd4dcb2649c460d646f5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3966 zcmaJ^TX!5s5#E`dy=o;(wk*qbE)yWw3+Q5pa0wx?uEB{filrD3m!Ws7cX#Butb1mJ zwND(vtNwu>0#EoH{)Jxn0X#Kt@U%~yLm)s71itQ`T`9KA?%CO?>guZMufDFHi6N z94h^)KqX$WM!SV{g4GsTh>FrD_H8sQ6kG>Ogu>1%IxH4 z9>NX$5`k2>4!&!XwOi~Kb{&K$lTAfl*{hO;FG5rK(COGzfv!eXsvTO8>RPgBf*OZ5 zn|pbH;bG*1`llJC1;jy^1TRuHs~#6LeL_YVyiQ z)4wht-><+gvZJrk*JwfKyExV?<(lPOGf&rY&GlUK8ht(2ygt@^BiDR$tofGFbZhi& zdV{`0lU{;~P$IdwSw@%IdZR9zl^Sl9XHLQOnM`u;bYk6(f$FVl_`ZkRW%-;d# zuhI8%%s+=B-GtRyer|7eud;W3Z;sxj_vn_wpj)NebcgOfh40p>IewlcPgn72*8HEU zK9kS;<9sT#MfaYm_x7pz>}X^@$dNg%`uE4Eejm0oK=lXs{ZLnbSIq{ie*{+l7|0n` z{{&jYVT0#Swe8a=8*N^TG4@95!NYUdOh3ceaUCuYqn4?|Gl;M;B9pXDZ6ewMm-tg$ z>YR#8SFMajjOpnW`N(olGsI;1Kx^l)Zo{L%$oX34$ykNZ9_Kqp5yd%g%G9TaBp%y} z*WL2&JSAvcsjS@|b*UX$--uGd`@&DSw0+u?_RaOCBO5GBW3MeZOF792jE77dcn|o2 ztR-CdEbs?hHukw_#R>N!7IImP;&AwTbT;(FddMDm0gt+APnHs2@N_tJX1uS|X z&Onk?)=vF>KjqD$8rkPj$_JP4t3Q{xi;(ReiwL&uJF%R_aZWtVZ;fl=xt&;an4dC$ zrsm6v4!0j<%7YGcI)2)?n()jIyfz!E)n|%=TLJ6D5xHwCZs@l~>|XuTFMrpRQ^ry; zqQ!835vJQ}@(D_TPr3E3kd%Ez5iSV$ z@V`o$2;(SapkbP}5ocV569fB9TFcpaSgWOi?UbRLv;rWnL8^nrqs~?(6FV8qwBxX! zvc=;;@Te9?*jfQcS`EPi)$>eCv(s2F9lKlSp4AX;BqW2-WLfpkENwdIt+kej z!J+_3)mBHh&{uf0A0Kc-gAH|$xXOOyoM-Bxd&7h>V-;{D3-UQD>K=M=R|T$*irY(( zkgl!jdwQG6QWU3XxMhQ-tmcbN29tT^Mguvt$zV!XVT>o}azlB$sX{ewDfmWJm>`}& z*85ymA7&sJ4xeFBSz6cRG>*AP9(b!au-8jvRqtQyGg-;=kmW#mMAST;TtArBkyHa7 zX=b!0IiW&oYHRCVa;JGwRy|EK&y#h}!|MX@!hOT@kXoR68>EVld~n{%hUPEx$XP@818QtZ%^O`a;2K7xe7$Ec>o)pTo$D(d5ZE8vEK1<7YyUsk}1j3@k`j!^78UEFGp~(e;`!LSG_FxgT8TWd>z}GwWq9U zYtA}r&EtRCn!?xq#GbS*$8!GT{MDINHnUb87RO)vA9lmG%F5>7&d0{4u4hlf&y;n^ zvj1sMJg|8e?U!f9$Stlh$f|YhXpKrZZpo^Nixv((( z%UQkB52t7bgP5U#FTBM^C1z-fUEW2cb-IV?tjgPLhsBv}fTTc6$_ z0l}*_hESr2isz*V)@}pXeqN*K5ScOsm%AF`WDVZ>pFj_q=956g+Wt%wu zKIhL&g_L8|*5Qb$TCdvsqJV2T%VF^*4v;fG@c8*Q; zRG7*le0e~3uX%i*?s`fm>`DyVnlf$gb`!(yDYi(R|nM)KSR{oQD{hnGTiOIHZZ#usL Dsg+)6 diff --git a/compta/migrations/__pycache__/0003_depense_regime_tva_depense_taux_tva_and_more.cpython-310.pyc b/compta/migrations/__pycache__/0003_depense_regime_tva_depense_taux_tva_and_more.cpython-310.pyc deleted file mode 100644 index afc38400aa3258c5746641d799de1a58b9ad15ef..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1613 zcmZWp&2Ah;5T4nY**~uxhZsYgK>85Gj)-?Hb3q89*x@fou@D#zdl|L2%Ddyt^h|oX zE!#)J)$f3~AWIzb4D2&-=_8yhcmgB@)iblZ0Y;kY>aL%zud2GT)zz*;-!FgcpKP@p z=Qq1~`SWS>kp5zq2sq#-PU?G5ix))D8M)w$8sJXck%#_C z01fc4dD5EDeLuJmKoeTfzVxAUIhS`y-i8(IK<~K=tFQ()VEr;!XxxNXtOvLS8}KS@ zUV62pOTJ&LY~gkCdmFYEey!}zzx>`MjW_0gC&6e1-XzrbOy|iQBLQxXvou%S40m>R zb|b(XMS&3?tN1il#3Tx3ghENoW&D!Fv}#?`l{kr0jMOO)J@VKQW&U`A`aUJ-V7_Dh z!*BN3vE*X*BPUnvm~PQ>c7-8XwPU)2Q!p3zAJ<%}1=otl5?P;^EQz1#7)p26q8X=o zCe;OdT&`@I$%1|T@&9$DZKLZR5p7HNqJo(@UtjE zF;P>~J4Jbv5puy(3_X(Bt|)iLw`V3ejxhn#R$QOiEL=C}7US;dN3Dl zDBq?pl#f6HL8tZ*J{=x#(qQbX?_kfMx#zGNvR$H*o4q3n8q9|`$Hpf8U zA~#Pew=C!9vn$sB_1mAq(3iLEy*5aB$C47)GJ;dC-=kw2d})T1I(AI)h`1<_!s^#a zuRE41r3Ez^rj(j+pzqbjFuMXxFXD)TUBp$za1i!PJ1V;&icB|(s5^9m#P_1;ndXV@ z5W6d13!oUsR2z>RoAw^SC$@8>t*_}knsQn0O|PCum9*@p?w);!mAISbSkI^{N4`VG zCHeDoSYT1u(KY0d$yiLbiv%%0-+qcof^wJ&HT-;^ZL{jl59?()^6U_JcQFK(?|N}G zZ}Er=AgB@t`MC@z0-DcG@jj8Z)A0Pi{h;AC+zr?NtKv2n+}eU$UvO61{=M{b5H{^I uEXT1dk$2#Pi%B+sqcUw}vP#t@@``&vx106{RRKP9Z9q*Ty~et`>HPz`mg$!O diff --git a/compta/migrations/__pycache__/0004_paiement_depense_paiement.cpython-310.pyc b/compta/migrations/__pycache__/0004_paiement_depense_paiement.cpython-310.pyc deleted file mode 100644 index 3ce7cc6171b1bd1991ffa9b8f6ded1f701bb6276..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1607 zcmYjR&2r;J5SB)=EjxC8HX-aT3#;7)Haf%(1xKJZu@eeN0#ykYUsPjvN0Br7VI(g} zu5k4maDbzqf`i|H<6JrAvNsM;MJ+v&okS|Nx_i2(yT6{Eq}6H|^!)K=Z-iCD_)81- zADe_%^kr=#V1Ox%zDa*3SY-9B$nM*w@s$A<*uNNHqcy7hZ1pQ(o)})`hJ1`;kEuo! z29oDt5@)I!C4eFu>b3jNG6}Ef%l;&SMU*}RGcfxW+M^0Eu&_F+flY4(s!*HQXAB&y z{^tB{5Lc(wIy7JjniEEHi{u6@!wRex@>;R}0M-Kr8}JTnPAsx|m$XZ?iY83x{_#iOh>8}>J6P?oF>M)7YoU2cEc6L7X0aJ`K^d$yigns^-->PyxzAO|x20uz9 zD(8D1VuW$7SO}*FihbSlY}MrBJn@GTd5)l#QW-{EUilYzrRo{Vkc;pdRr58=?jd5{9LxkMzx|O&Uu{sd6IK+ z!$^12Bto{!fI|u|{8aEEX4HT3g)j*7OxYP#N;Vb1L70zq+k3MqvF;Whp7Y!*z>x^F zxJip$IhzviZlO*k%Zkc9`A~ggQJ?0T(iN&9Y<_O2%}wL&1_)RZVRxFvQhPgH%;iR`+vt+Dj15}Wn= zFcS)6$%S$f3Dk%a~?K50YK4%qw05^Qx*c5Inw6^>Y-d zpW|gtsg8w^&nOM~T$2|>sDef@#|0MhC8<=UDCXGPP>x@yeP1IG}+EgZNvTf+~!793+atKtn(+rTa+Ly1OUc-QK=(b^%H!=VbrXKmOtHP&JYi z=l9mskUdJNApZ_K8D^Qj&N>_t-O}w$Af{K_7kZFnCyMjVH+$~3TLRpf|L(5A4vqb1 z{sQ;gVP7cP>6O%1Z;ifehlpb^%YJKDn7P3k<|@6L=Cb+NWdE=RGi}rU&;Hw9Vb&&yIgB!~ F{s&@r_~ifq diff --git a/compta/migrations/__pycache__/0005_categorie_entreprise_vue_tva_alter_depense_categorie.cpython-310.pyc b/compta/migrations/__pycache__/0005_categorie_entreprise_vue_tva_alter_depense_categorie.cpython-310.pyc deleted file mode 100644 index 3973bf4b204f902d80b03f8c6f1f4d082edba1b8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1793 zcmY*aOK;mo5EfsuB+Is9r)eHd%0AMBO=1T~dntk2($ot;udtmJ~0`E1Q zY3v5J>ZD05SefLRRLwL8Ns&Cvq5^m@>Q zg^4?`p+&qFw29Z5*hDXCI&MJsCkvKf8CE6^aeKsV)2VZ?3TuXQ-f&!aXJV230#O}! zx2EDIT!c$x<1*Q}@}mRqO|%D#a22j;>+nAKu?-)P^$+2ri3=Yad6(|H4xdbHqf2|z zUDDp-@@Qq$9Q8)$;0D};VB+Z$vD;sTPYI=4s{Ql?9fGPE|^NkJ_ZB1xGz3J>r|wIzy#@#Gj) zcZ^~`CvzD~Q8`(jz6L_wWQrYz9J9C_s%DiXuPRhtC0UGtL*j+G9~IR~5%T2#vN3$v3_M^N^WL)9Cf$2UOk+oNV`oL{}oDiR!m%o1Z^YN8ks->-Y#mVDd-5Kj= zdY6ZN*cmFFB;Hm4p@GsHNTwKf52 z{^B!C^D)*$XD1h!#Mw90p-mXEEY6h;`-Ye+K>+0`i;g`k0-jq=Ss?l`h(HF6K#l_t7qb9~6oz01O-8?!3`HPe1o6vPzc{tHI5R)5 zSU)i(zbG>=UAH(VHMO8pH#;>aC$&gFH?KtB%Sl03Avr&{pd?WrO66vz7bTW}^~J|$ e=4F<|$LkeT-r}&y%}*)KNwou6Q_KV;SQr33 mouvement neutre (hors fiscal).""" + USAGE = [("depense", "Dépense"), ("vente", "Vente"), ("neutre", "Mouvement neutre")] nom = models.CharField(max_length=100, unique=True) usage = models.CharField(max_length=10, choices=USAGE, default="depense") exclure_calculs = models.BooleanField( @@ -62,21 +48,14 @@ class Categorie(models.Model): class Bareme(models.Model): - """Taux et seuils officiels, par activité et par année (paramétrable).""" activite = models.CharField(max_length=20, choices=Activite.choices) annee = models.PositiveIntegerField() - taux_urssaf = models.DecimalField( - "Taux cotisations URSSAF (%)", max_digits=5, decimal_places=2) - abattement_fiscal = models.DecimalField( - "Abattement forfaitaire (%)", max_digits=5, decimal_places=2) - taux_vl = models.DecimalField( - "Taux versement libératoire (%)", max_digits=4, decimal_places=2) - seuil_ca = models.DecimalField( - "Plafond CA micro (€)", max_digits=12, decimal_places=2) - seuil_tva_base = models.DecimalField( - "Seuil franchise TVA (€)", max_digits=12, decimal_places=2) - seuil_tva_majore = models.DecimalField( - "Seuil franchise TVA majoré (€)", max_digits=12, decimal_places=2) + taux_urssaf = models.DecimalField("Taux cotisations URSSAF (%)", max_digits=5, decimal_places=2) + abattement_fiscal = models.DecimalField("Abattement forfaitaire (%)", max_digits=5, decimal_places=2) + taux_vl = models.DecimalField("Taux versement libératoire (%)", max_digits=4, decimal_places=2) + seuil_ca = models.DecimalField("Plafond CA micro (€)", max_digits=12, decimal_places=2) + seuil_tva_base = models.DecimalField("Seuil franchise TVA (€)", max_digits=12, decimal_places=2) + seuil_tva_majore = models.DecimalField("Seuil franchise TVA majoré (€)", max_digits=12, decimal_places=2) class Meta: verbose_name = "Barème" @@ -95,19 +74,14 @@ class Entreprise(models.Model): activite = models.CharField( "Activité principale", max_length=20, choices=Activite.choices, default=Activite.SERVICE_BNC, - help_text="Activité par défaut des nouvelles factures. " - "Une entreprise peut avoir plusieurs activités : " - "chaque facture/dépense porte la sienne.") + help_text="Activité par défaut des nouvelles factures.") franchise_tva = models.BooleanField( "En franchise de TVA", default=False, help_text="Décochez si vous facturez et déclarez la TVA.") taux_tva_defaut = models.DecimalField( - "Taux de TVA par défaut (%)", max_digits=4, decimal_places=2, - default=Decimal("20.00")) - versement_liberatoire = models.BooleanField( - "Option versement libératoire", default=False) - num_tva_intracom = models.CharField( - "N° TVA intracommunautaire", max_length=20, blank=True) + "Taux de TVA par défaut (%)", max_digits=4, decimal_places=2, default=Decimal("20.00")) + versement_liberatoire = models.BooleanField("Option versement libératoire", default=False) + num_tva_intracom = models.CharField("N° TVA intracommunautaire", max_length=20, blank=True) vue_tva = models.CharField( "Mode d'affichage de la déclaration TVA", max_length=10, choices=VUE_TVA, default="simplifie") @@ -128,8 +102,7 @@ class Entreprise(models.Model): class Client(models.Model): - entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, - related_name="clients") + entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="clients") nom = models.CharField(max_length=200) email = models.EmailField(blank=True) siret = models.CharField(max_length=20, blank=True) @@ -146,25 +119,18 @@ class Client(models.Model): class Facture(models.Model): - entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, - related_name="factures") - client = models.ForeignKey(Client, on_delete=models.PROTECT, - related_name="factures") + entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="factures") + client = models.ForeignKey(Client, on_delete=models.PROTECT, related_name="factures") numero = models.CharField(max_length=50) activite = models.CharField(max_length=20, choices=Activite.choices) date_emission = models.DateField() date_encaissement = models.DateField( null=True, blank=True, - help_text="À renseigner une fois la facture payée. " - "Le CA micro est compté à l'encaissement.") - statut = models.CharField(max_length=12, choices=StatutFacture.choices, - default=StatutFacture.BROUILLON) - montant_ht = models.DecimalField(max_digits=12, decimal_places=2, - default=Decimal("0")) - montant_tva = models.DecimalField(max_digits=12, decimal_places=2, - default=Decimal("0")) - montant_ttc = models.DecimalField(max_digits=12, decimal_places=2, - default=Decimal("0")) + help_text="Le CA micro est compté à l'encaissement.") + statut = models.CharField(max_length=12, choices=StatutFacture.choices, default=StatutFacture.BROUILLON) + montant_ht = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal("0")) + montant_tva = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal("0")) + montant_ttc = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal("0")) notes = models.TextField(blank=True) cree_le = models.DateTimeField(auto_now_add=True) @@ -178,7 +144,6 @@ class Facture(models.Model): return f"{self.numero} — {self.client}" def recompute_totaux(self, save=True): - """Recalcule HT/TVA/TTC à partir des lignes.""" ht = Decimal("0") tva = Decimal("0") for ligne in self.lignes.all(): @@ -193,14 +158,11 @@ class Facture(models.Model): class LigneFacture(models.Model): - facture = models.ForeignKey(Facture, on_delete=models.CASCADE, - related_name="lignes") + facture = models.ForeignKey(Facture, on_delete=models.CASCADE, related_name="lignes") designation = models.CharField(max_length=255) - quantite = models.DecimalField(max_digits=12, decimal_places=3, - default=Decimal("1")) + quantite = models.DecimalField(max_digits=12, decimal_places=3, default=Decimal("1")) prix_unitaire_ht = models.DecimalField(max_digits=12, decimal_places=2) - taux_tva = models.DecimalField(max_digits=4, decimal_places=2, - default=Decimal("0")) + taux_tva = models.DecimalField(max_digits=4, decimal_places=2, default=Decimal("0")) class Meta: verbose_name = "Ligne de facture" @@ -215,15 +177,17 @@ class LigneFacture(models.Model): class Paiement(models.Model): - """Opération bancaire réelle, à éclater en une ou plusieurs dépenses - (ex. un débit Amazon regroupant plusieurs vendeurs/factures).""" + """Opération bancaire réelle, à éclater en une ou plusieurs dépenses.""" SENS = [("debit", "Débit (achat)"), ("credit", "Crédit (encaissement)")] - entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, - related_name="paiements") + entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="paiements") date = models.DateField() libelle = models.CharField("Libellé bancaire", max_length=255) montant_total = models.DecimalField(max_digits=12, decimal_places=2) sens = models.CharField(max_length=6, choices=SENS, default="debit") + categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL, + null=True, blank=True, related_name="paiements", + help_text="Surtout pour les mouvements neutres " + "(remboursement TVA, virement…).") cree_le = models.DateTimeField(auto_now_add=True) class Meta: @@ -249,32 +213,27 @@ class Paiement(models.Model): class Depense(models.Model): - entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, - related_name="depenses") - paiement = models.ForeignKey(Paiement, on_delete=models.SET_NULL, - null=True, blank=True, related_name="depenses", - help_text="Opération bancaire dont cette dépense " - "fait partie (achat groupé éclaté).") + entreprise = models.ForeignKey(Entreprise, on_delete=models.CASCADE, related_name="depenses") + paiement = models.ForeignKey(Paiement, on_delete=models.SET_NULL, null=True, blank=True, + related_name="depenses", + help_text="Opération bancaire dont cette dépense fait partie.") date = models.DateField() activite = models.CharField( max_length=20, choices=Activite.choices, blank=True, help_text="Activité concernée (facultatif, pour la ventilation).") - categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL, - null=True, blank=True, related_name="depenses") + categorie = models.ForeignKey(Categorie, on_delete=models.SET_NULL, null=True, blank=True, + related_name="depenses") libelle = models.CharField(max_length=255) montant_ttc = models.DecimalField( "Montant payé (€)", max_digits=12, decimal_places=2, help_text="Pour un achat UE/import, c'est le montant HT (sans TVA facturée).") montant_tva = models.DecimalField( - "TVA déductible facturée (€)", max_digits=12, decimal_places=2, - default=Decimal("0"), + "TVA déductible facturée (€)", max_digits=12, decimal_places=2, default=Decimal("0"), help_text="TVA payée au fournisseur (achats nationaux uniquement).") regime_tva = models.CharField( - "Régime de TVA", max_length=12, choices=RegimeTva.choices, - default=RegimeTva.NATIONALE) + "Régime de TVA", max_length=12, choices=RegimeTva.choices, default=RegimeTva.NATIONALE) taux_tva = models.DecimalField( - "Taux TVA autoliquidation (%)", max_digits=4, decimal_places=2, - default=Decimal("0"), + "Taux TVA autoliquidation (%)", max_digits=4, decimal_places=2, default=Decimal("0"), help_text="Taux français à auto-liquider pour les achats UE/import (ex. 20).") cree_le = models.DateTimeField(auto_now_add=True) @@ -288,33 +247,26 @@ class Depense(models.Model): @property def neutre(self): - """Mouvement neutre : exclu des calculs fiscaux.""" return bool(self.categorie and self.categorie.exclure_calculs) @property def autoliquidee(self): - """True si la TVA est auto-liquidée (achat UE ou import).""" return self.regime_tva in (RegimeTva.INTRACOM, RegimeTva.IMPORT) @property def montant_ht(self): - # En autoliquidation, le fournisseur ne facture pas de TVA : HT = montant payé. if self.autoliquidee: return self.montant_ttc.quantize(Decimal("0.01")) return (self.montant_ttc - self.montant_tva).quantize(Decimal("0.01")) @property def tva_autoliquidee(self): - """TVA française auto-liquidée (comptée en collectée ET en déductible).""" if self.autoliquidee and not self.neutre: - return (self.montant_ttc * self.taux_tva / Decimal("100")).quantize( - Decimal("0.01")) + return (self.montant_ttc * self.taux_tva / Decimal("100")).quantize(Decimal("0.01")) return Decimal("0.00") @property def tva_deductible_value(self): - """TVA récupérable : facturée (national) ou auto-liquidée (UE/import). - Nulle pour un mouvement neutre.""" if self.neutre: return Decimal("0.00") if self.autoliquidee: diff --git a/compta/services.py b/compta/services.py index d5bfff7..ebbb7e1 100644 --- a/compta/services.py +++ b/compta/services.py @@ -1,96 +1,110 @@ """ Logique fiscale — JB Compta. -Bases de calcul : CA HORS TAXES (HT) encaissé. -- Base des cotisations URSSAF et du versement libératoire (assujetti TVA). -- En franchise de TVA, HT = TTC : le calcul reste correct. - -Multi-activité : une entreprise peut exercer plusieurs activités (ex. Vente BIC -+ Prestation BIC). Taux URSSAF, abattements, taux VL et seuils diffèrent par -activité, donc tout est ventilé par activité puis totalisé. - -TVA : -- TVA collectée = TVA sur ventes encaissées + TVA auto-liquidée (achats UE/import). -- TVA déductible = TVA facturée sur achats nationaux + TVA auto-liquidée. -- L'autoliquidation s'ajoute aux deux côtés (effet net souvent nul), mais doit - apparaître sur la CA3 : on l'expose donc séparément. +Bases : CA HORS TAXES (HT) encaissé. +TVA collectée = TVA ventes + TVA auto-liquidée (UE/import). +TVA déductible = TVA achats nationaux + TVA auto-liquidée. Neutres exclus. """ from decimal import Decimal from django.db.models import Sum from django.db.models.functions import TruncMonth -from .models import Facture, Depense, Bareme, StatutFacture, Activite +from .models import (Facture, Depense, LigneFacture, Bareme, StatutFacture, + Activite, RegimeTva) CENT = Decimal("0.01") +MOIS_FR = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", + "Août", "Septembre", "Octobre", "Novembre", "Décembre"] -def _q(value): - return (value or Decimal("0")).quantize(CENT) +def _q(v): + return (v or Decimal("0")).quantize(CENT) -def factures_encaissees(entreprise, annee): - return Facture.objects.filter( - entreprise=entreprise, - statut=StatutFacture.ENCAISSEE, - date_encaissement__year=annee, - ) +# ---- Sélections de période --------------------------------------------- + +def factures_encaissees(entreprise, annee, mois=None): + qs = Facture.objects.filter(entreprise=entreprise, statut=StatutFacture.ENCAISSEE, + date_encaissement__year=annee) + if mois: + qs = qs.filter(date_encaissement__month=mois) + return qs -def depenses_annee(entreprise, annee): - return Depense.objects.filter(entreprise=entreprise, date__year=annee) +def depenses_periode(entreprise, annee, mois=None): + qs = Depense.objects.filter(entreprise=entreprise, date__year=annee) + if mois: + qs = qs.filter(date__month=mois) + return qs -def ca_ht_par_activite(entreprise, annee): - """Dict {activite: CA HT encaissé}.""" - rows = ( - factures_encaissees(entreprise, annee) - .values("activite") - .annotate(total=Sum("montant_ht")) - ) +# ---- CA ----------------------------------------------------------------- + +def ca_ht_par_activite(entreprise, annee, mois=None): + rows = factures_encaissees(entreprise, annee, mois).values("activite").annotate( + total=Sum("montant_ht")) return {r["activite"]: _q(r["total"]) for r in rows} -def ca_ht(entreprise, annee): - agg = factures_encaissees(entreprise, annee).aggregate(s=Sum("montant_ht")) - return _q(agg["s"]) +def ca_ht(entreprise, annee, mois=None): + return _q(factures_encaissees(entreprise, annee, mois).aggregate( + s=Sum("montant_ht"))["s"]) # ---- TVA ---------------------------------------------------------------- -def tva_sur_ventes(entreprise, annee): - agg = factures_encaissees(entreprise, annee).aggregate(s=Sum("montant_tva")) - return _q(agg["s"]) +def tva_sur_ventes(entreprise, annee, mois=None): + return _q(factures_encaissees(entreprise, annee, mois).aggregate( + s=Sum("montant_tva"))["s"]) -def tva_autoliquidee(entreprise, annee): - """TVA auto-liquidée sur les achats UE/import (intracom + import).""" - total = sum((d.tva_autoliquidee for d in depenses_annee(entreprise, annee)), +def tva_autoliquidee(entreprise, annee, mois=None): + total = sum((d.tva_autoliquidee for d in depenses_periode(entreprise, annee, mois)), Decimal("0")) return _q(total) -def tva_deductible(entreprise, annee): - """TVA récupérable : facturée (national) + auto-liquidée (UE/import).""" - total = sum((d.tva_deductible_value for d in depenses_annee(entreprise, annee)), +def tva_deductible(entreprise, annee, mois=None): + total = sum((d.tva_deductible_value for d in depenses_periode(entreprise, annee, mois)), Decimal("0")) return _q(total) -def tva_collectee(entreprise, annee): - """TVA due : sur ventes + auto-liquidée sur achats UE/import.""" - return _q(tva_sur_ventes(entreprise, annee) + tva_autoliquidee(entreprise, annee)) +def tva_collectee(entreprise, annee, mois=None): + return _q(tva_sur_ventes(entreprise, annee, mois) + + tva_autoliquidee(entreprise, annee, mois)) -# ---- CA mensuel --------------------------------------------------------- +def tva_par_taux(entreprise, annee, mois=None): + """Ventilation des ventes encaissées par taux de TVA : {taux: (base_ht, tva)}.""" + lignes = LigneFacture.objects.filter( + facture__in=factures_encaissees(entreprise, annee, mois)) + par_taux = {} + for l in lignes.select_related("facture"): + base = l.montant_ht + taux = l.taux_tva + b, t = par_taux.get(taux, (Decimal("0"), Decimal("0"))) + par_taux[taux] = (b + base, t + base * taux / Decimal("100")) + return {taux: (_q(b), _q(t)) for taux, (b, t) in sorted(par_taux.items())} + + +def intracom_periode(entreprise, annee, mois=None): + """Acquisitions UE/import auto-liquidées : (base_ht, tva).""" + base = Decimal("0") + tva = Decimal("0") + for d in depenses_periode(entreprise, annee, mois): + if d.autoliquidee and not d.neutre: + base += d.montant_ht + tva += d.tva_autoliquidee + return _q(base), _q(tva) + + +# ---- CA mensuel (graphe) ------------------------------------------------ def ca_ht_mensuel(entreprise, annee): - """Liste de 12 valeurs (index 0 = janvier) du CA HT encaissé (toutes activités).""" - rows = ( - factures_encaissees(entreprise, annee) - .annotate(mois=TruncMonth("date_encaissement")) - .values("mois") - .annotate(total=Sum("montant_ht")) - ) + rows = (factures_encaissees(entreprise, annee) + .annotate(mois=TruncMonth("date_encaissement")) + .values("mois").annotate(total=Sum("montant_ht"))) mensuel = [Decimal("0")] * 12 for r in rows: if r["mois"]: @@ -101,7 +115,6 @@ def ca_ht_mensuel(entreprise, annee): # ---- Détail par activité ------------------------------------------------ def detail_activite(activite, ca, bareme): - """Calculs fiscaux pour une activité donnée.""" label = dict(Activite.choices).get(activite, activite) d = {"activite": activite, "label": label, "ca_ht": ca, "bareme": bareme} if bareme: @@ -111,72 +124,147 @@ def detail_activite(activite, ca, bareme): "taux_vl": bareme.taux_vl, "versement_liberatoire": _q(ca * bareme.taux_vl / Decimal("100")), "abattement_fiscal": bareme.abattement_fiscal, - "revenu_imposable": _q( - ca * (Decimal("100") - bareme.abattement_fiscal) / Decimal("100")), + "revenu_imposable": _q(ca * (Decimal("100") - bareme.abattement_fiscal) / Decimal("100")), "seuil_ca": bareme.seuil_ca, - "pct_seuil_ca": (_q(ca / bareme.seuil_ca * Decimal("100")) - if bareme.seuil_ca else Decimal("0")), + "pct_seuil_ca": (_q(ca / bareme.seuil_ca * Decimal("100")) if bareme.seuil_ca else Decimal("0")), "seuil_tva_base": bareme.seuil_tva_base, "depasse_seuil_tva": ca > bareme.seuil_tva_base, "depasse_seuil_ca": ca > bareme.seuil_ca, }) else: - d.update({ - "taux_urssaf": None, "cotisations": None, - "taux_vl": None, "versement_liberatoire": None, - "abattement_fiscal": None, "revenu_imposable": None, - "seuil_ca": None, "pct_seuil_ca": None, - "seuil_tva_base": None, "depasse_seuil_tva": False, - "depasse_seuil_ca": False, - }) + for k in ("taux_urssaf", "cotisations", "taux_vl", "versement_liberatoire", + "abattement_fiscal", "revenu_imposable", "seuil_ca", "pct_seuil_ca", + "seuil_tva_base"): + d[k] = None + d["depasse_seuil_tva"] = d["depasse_seuil_ca"] = False return d -def synthese(entreprise, annee): - """Tableau de bord chiffré, ventilé par activité puis totalisé.""" - ca_par_act = ca_ht_par_activite(entreprise, annee) - - activites = set(ca_par_act) | {entreprise.activite} +def _lignes_activite(entreprise, annee, mois=None): + ca_par = ca_ht_par_activite(entreprise, annee, mois) + activites = set(ca_par) | {entreprise.activite} baremes = {b.activite: b for b in Bareme.objects.filter(annee=annee)} + return [detail_activite(a, ca_par.get(a, Decimal("0")), baremes.get(a)) + for a in sorted(activites)] - lignes = [] - for act in sorted(activites): - ca = ca_par_act.get(act, Decimal("0")) - lignes.append(detail_activite(act, ca, baremes.get(act))) - def somme(cle): - vals = [l[cle] for l in lignes if l.get(cle) is not None] - return _q(sum(vals, Decimal("0"))) if vals else None +def _somme(lignes, cle): + vals = [l[cle] for l in lignes if l.get(cle) is not None] + return _q(sum(vals, Decimal("0"))) if vals else None + +# ---- Vue globale (annuelle) -------------------------------------------- + +def synthese(entreprise, annee): + lignes = _lignes_activite(entreprise, annee) tva_ventes = tva_sur_ventes(entreprise, annee) tva_autoliq = tva_autoliquidee(entreprise, annee) tva_ded = tva_deductible(entreprise, annee) tva_col = _q(tva_ventes + tva_autoliq) - return { - "entreprise": entreprise, - "annee": annee, - "lignes_activite": lignes, + "entreprise": entreprise, "annee": annee, "lignes_activite": lignes, "bareme_manquant": any(l["bareme"] is None and l["ca_ht"] for l in lignes), "ca_ht": ca_ht(entreprise, annee), - "cotisations_urssaf": somme("cotisations"), - "versement_liberatoire": ( - somme("versement_liberatoire") if entreprise.versement_liberatoire else None), - "revenu_imposable": somme("revenu_imposable"), - "tva_sur_ventes": tva_ventes, - "tva_autoliquidee": tva_autoliq, - "tva_collectee": tva_col, - "tva_deductible": tva_ded, + "cotisations_urssaf": _somme(lignes, "cotisations"), + "versement_liberatoire": (_somme(lignes, "versement_liberatoire") + if entreprise.versement_liberatoire else None), + "revenu_imposable": _somme(lignes, "revenu_imposable"), + "tva_sur_ventes": tva_ventes, "tva_autoliquidee": tva_autoliq, + "tva_collectee": tva_col, "tva_deductible": tva_ded, "tva_a_reverser": _q(tva_col - tva_ded), "ca_mensuel": ca_ht_mensuel(entreprise, annee), } +# ---- Vue mensuelle ------------------------------------------------------ + +def tableau_mensuel(entreprise, annee): + baremes = {b.activite: b for b in Bareme.objects.filter(annee=annee)} + rows = [] + for m in range(1, 13): + ca_par = ca_ht_par_activite(entreprise, annee, m) + cot = Decimal("0") + vl = Decimal("0") + for act, ca in ca_par.items(): + b = baremes.get(act) + if b: + cot += ca * b.taux_urssaf / Decimal("100") + vl += ca * b.taux_vl / Decimal("100") + tva_col = tva_collectee(entreprise, annee, m) + tva_ded = tva_deductible(entreprise, annee, m) + rows.append({ + "mois": m, "nom": MOIS_FR[m - 1], + "ca_ht": ca_ht(entreprise, annee, m), + "cotisations": _q(cot), + "versement_liberatoire": _q(vl) if entreprise.versement_liberatoire else None, + "tva_collectee": tva_col, "tva_deductible": tva_ded, + "tva_a_reverser": _q(tva_col - tva_ded), + }) + return rows + + +# ---- Déclaration URSSAF (période) -------------------------------------- + +def declaration_urssaf(entreprise, annee, mois=None): + lignes = _lignes_activite(entreprise, annee, mois) + return { + "entreprise": entreprise, "annee": annee, "mois": mois, + "mois_nom": MOIS_FR[mois - 1] if mois else "Année entière", + "lignes": lignes, + "ca_ht": ca_ht(entreprise, annee, mois), + "cotisations": _somme(lignes, "cotisations"), + "versement_liberatoire": (_somme(lignes, "versement_liberatoire") + if entreprise.versement_liberatoire else None), + } + + +# ---- Déclaration TVA (période) ----------------------------------------- + +def declaration_tva(entreprise, annee, mois=None): + par_taux = tva_par_taux(entreprise, annee, mois) + intra_base, intra_tva = intracom_periode(entreprise, annee, mois) + tva_ventes = sum((t for _, t in par_taux.values()), Decimal("0")) + tva_ded = tva_deductible(entreprise, annee, mois) + tva_brute = _q(tva_ventes + intra_tva) + a_payer = _q(tva_brute - tva_ded) + + def couple(taux): + return par_taux.get(Decimal(taux), (Decimal("0.00"), Decimal("0.00"))) + + b20, t20 = couple("20.00") + b10, t10 = couple("10.00") + b55, t55 = couple("5.50") + base_taxable = _q(sum((b for b, _ in par_taux.values()), Decimal("0"))) + + return { + "entreprise": entreprise, "annee": annee, "mois": mois, + "mois_nom": MOIS_FR[mois - 1] if mois else "Année entière", + "par_taux": [{"taux": k, "base": v[0], "tva": v[1]} for k, v in par_taux.items()], + # Résumé simplifié + "tva_sur_ventes": _q(tva_ventes), + "tva_autoliquidee": intra_tva, + "tva_collectee": tva_brute, + "tva_deductible": tva_ded, + "tva_a_payer": a_payer, + "credit_tva": _q(-a_payer) if a_payer < 0 else Decimal("0.00"), + # Champs CA3 + "ca3": { + "l01_base_taxable": base_taxable, + "l08_base": b20, "l08_tva": t20, + "l9B_base": b10, "l9B_tva": t10, + "l09_base": b55, "l09_tva": t55, + "l03_intra_base": intra_base, + "l17_intra_tva": intra_tva, + "l16_tva_brute": tva_brute, + "l20_tva_deductible": tva_ded, + "l28_total_deductible": tva_ded, + "l32_a_payer": a_payer if a_payer > 0 else Decimal("0.00"), + "l25_credit": _q(-a_payer) if a_payer < 0 else Decimal("0.00"), + }, + } + + def annees_disponibles(entreprise): - qs = ( - Facture.objects.filter(entreprise=entreprise, - statut=StatutFacture.ENCAISSEE, - date_encaissement__isnull=False) - .dates("date_encaissement", "year") - ) + qs = Facture.objects.filter(entreprise=entreprise, statut=StatutFacture.ENCAISSEE, + date_encaissement__isnull=False).dates("date_encaissement", "year") return sorted({d.year for d in qs}, reverse=True) diff --git a/compta/templates/compta/base.html b/compta/templates/compta/base.html new file mode 100644 index 0000000..9bb265d --- /dev/null +++ b/compta/templates/compta/base.html @@ -0,0 +1,81 @@ + + + + + +JB Compta — {% block titre %}Tableau de bord{% endblock %} + + + + +{% if not entreprise %} +

Aucune entreprise

+

Crée une entreprise dans l'administration, + ou saisis une opération.

+{% else %} + + +

{{ entreprise.nom }}

+
{{ entreprise.get_activite_display }} (principale) · + {% if entreprise.franchise_tva %}Franchise de TVA{% else %}Assujetti TVA{% endif %} + {% if entreprise.versement_liberatoire %}· Versement libératoire{% endif %} + · Exercice {{ annee }}
+ +
+ + + {% block extrafilters %}{% endblock %} +
+ + {% block content %}{% endblock %} +{% endif %} + +{% block scripts %}{% endblock %} + + diff --git a/compta/templates/compta/dashboard.html b/compta/templates/compta/dashboard.html index 6694271..f076149 100644 --- a/compta/templates/compta/dashboard.html +++ b/compta/templates/compta/dashboard.html @@ -1,86 +1,21 @@ - - - - - -JB Compta — Tableau de bord - - - - -{% if not entreprise %} -

Aucune entreprise

-

Crée une entreprise et des factures dans - l'administration, puis reviens ici.

-{% else %} -

{{ entreprise.nom }}

-
{{ entreprise.get_activite_display }} (principale) · - {% if entreprise.franchise_tva %}Franchise de TVA{% else %}Assujetti TVA{% endif %} - {% if entreprise.versement_liberatoire %}· Versement libératoire{% endif %} - · Exercice {{ annee }}
- -
- - - - -
+{% extends "compta/base.html" %} +{% block titre %}Global{% endblock %} +{% block content %} {% if bareme_manquant %}
- Une activité avec du CA n'a pas de barème {{ annee }}. Ajoute-le dans - Barèmes. + Une activité avec du CA n'a pas de barème {{ annee }}. + Ajoute-le dans Barèmes.
{% endif %} -
CA HT encaissé (total)
{{ ca_ht }} €
-
Cotisations URSSAF (total)
+
Cotisations URSSAF
{{ cotisations_urssaf|default:"—" }} €
@@ -91,93 +26,55 @@
TVA à reverser
{{ tva_a_reverser }} €
{{ tva_collectee }} collectée − {{ tva_deductible }} déductible
- {% if tva_autoliquidee %}
dont {{ tva_autoliquidee }} € auto-liquidée (UE/import)
{% endif %} + {% if tva_autoliquidee %}
dont {{ tva_autoliquidee }} € auto-liquidée
{% endif %}
-

Ventilation par activité

- - - - - - + {% for l in lignes_activite %} - + {% endfor %} - - - - - - - - +
ActivitéCA HT% plafondURSSAFImpôt (VL)Revenu imposable
ActivitéCA HT% plafondURSSAFImpôt (VL)Revenu imposable
{{ l.label }} {{ l.ca_ht }} € - {% if l.pct_seuil_ca is not None %}{{ l.pct_seuil_ca }} %{% else %}—{% endif %} - {% if l.pct_seuil_ca is not None %}{{ l.pct_seuil_ca }} %{% else %}—{% endif %} {% if l.cotisations is not None %}{{ l.cotisations }} €{% else %}—{% endif %} {% if l.versement_liberatoire is not None %}{{ l.versement_liberatoire }} €{% else %}—{% endif %} {% if l.revenu_imposable is not None %}{{ l.revenu_imposable }} €{% else %}—{% endif %}
Total{{ ca_ht }} €{{ cotisations_urssaf|default:"—" }} €{{ versement_liberatoire|default:"—" }} €{{ revenu_imposable|default:"—" }} €
Total{{ ca_ht }} €{{ cotisations_urssaf|default:"—" }} €{{ versement_liberatoire|default:"—" }} €{{ revenu_imposable|default:"—" }} €
- -

TVA

-
- - - - - - -
TVA collectée sur ventes{{ tva_sur_ventes }} €
TVA auto-liquidée (UE / import){{ tva_autoliquidee }} €
TVA collectée totale (due){{ tva_collectee }} €
TVA déductible{{ tva_deductible }} €
TVA à reverser{{ tva_a_reverser }} €
-
-
-
CA HT mensuel (toutes activités)
+
CA HT mensuel
-
À provisionner (année)
- - - - - -
PosteMontant
URSSAF{{ cotisations_urssaf|default:"—" }} €
Impôt (VL){{ versement_liberatoire|default:"—" }} €
TVA{{ tva_a_reverser }} €
-
-
Exports CSV
+
Exports CSV
+
+{% endblock %} +{% block scripts %} -{% endif %} - - +{% endblock %} diff --git a/compta/templates/compta/decl_tva.html b/compta/templates/compta/decl_tva.html new file mode 100644 index 0000000..96189d2 --- /dev/null +++ b/compta/templates/compta/decl_tva.html @@ -0,0 +1,67 @@ +{% extends "compta/base.html" %} +{% block titre %}Déclaration TVA{% endblock %} + +{% block extrafilters %} + +{% endblock %} + +{% block content %} +

Déclaration TVA — {{ decl.mois_nom }} {{ annee }}

+
+ Mode : + Résumé · + Champs CA3 +  (réglage par défaut de l'entreprise : {{ entreprise.get_vue_tva_display }}) +
+ + {% if mode == 'ca3' %} +
+ + + + + + + + + + + + + + {% if decl.ca3.l25_credit %}{% endif %} + +
Ligne CA3LibelléBase HTTVA
01Ventes / prestations taxables{{ decl.ca3.l01_base_taxable }} €
08Taux normal 20 %{{ decl.ca3.l08_base }} €{{ decl.ca3.l08_tva }} €
9BTaux réduit 10 %{{ decl.ca3.l9B_base }} €{{ decl.ca3.l9B_tva }} €
09Taux réduit 5,5 %{{ decl.ca3.l09_base }} €{{ decl.ca3.l09_tva }} €
03Acquisitions intracommunautaires{{ decl.ca3.l03_intra_base }} €
17dont TVA sur acquisitions intracom{{ decl.ca3.l17_intra_tva }} €
16TVA brute due{{ decl.ca3.l16_tva_brute }} €
20TVA déductible (biens et services){{ decl.ca3.l20_tva_deductible }} €
28Total TVA déductible{{ decl.ca3.l28_total_deductible }} €
32TVA à payer{{ decl.ca3.l32_a_payer }} €
25Crédit de TVA{{ decl.ca3.l25_credit }} €
+
+
Numéros de lignes du formulaire 3310-CA3 (régime réel normal). À recouper + avec ta déclaration officielle — vérifie notamment la ventilation par taux.
+ {% else %} +
+
TVA collectée sur ventes
{{ decl.tva_sur_ventes }} €
+
TVA auto-liquidée (UE/import)
{{ decl.tva_autoliquidee }} €
+
TVA collectée totale
{{ decl.tva_collectee }} €
+
TVA déductible
{{ decl.tva_deductible }} €
+
+
{% if decl.credit_tva %}Crédit de TVA{% else %}TVA à payer{% endif %}
+
{% if decl.credit_tva %}{{ decl.credit_tva }}{% else %}{{ decl.tva_a_payer }}{% endif %} €
+
+
+ {% if decl.par_taux %} +

Détail par taux (ventes)

+
+ + + + {% for t in decl.par_taux %}{% endfor %} + +
TauxBase HTTVA
{{ t.taux }} %{{ t.base }} €{{ t.tva }} €
+
+ {% endif %} + {% endif %} +{% endblock %} diff --git a/compta/templates/compta/decl_urssaf.html b/compta/templates/compta/decl_urssaf.html new file mode 100644 index 0000000..ef53586 --- /dev/null +++ b/compta/templates/compta/decl_urssaf.html @@ -0,0 +1,43 @@ +{% extends "compta/base.html" %} +{% block titre %}Déclaration URSSAF{% endblock %} + +{% block extrafilters %} + +{% endblock %} + +{% block content %} +

Déclaration URSSAF — {{ decl.mois_nom }} {{ annee }}

+
À déclarer sur autoentrepreneur.urssaf.fr : le CA HT encaissé de la période, + par type d'activité. Les cotisations (et l'impôt si versement libératoire) en découlent.
+ +
+ + + + {% if decl.versement_liberatoire is not None %}{% endif %} + + + {% for l in decl.lignes %} + + + + + + {% if decl.versement_liberatoire is not None %} + + + {% endif %} + + {% endfor %} + + + {% if decl.versement_liberatoire is not None %}{% endif %} + + +
ActivitéCA HT à déclarerTaux URSSAFCotisationsTaux VLImpôt (VL)
{{ l.label }}{{ l.ca_ht }} €{% if l.taux_urssaf is not None %}{{ l.taux_urssaf }} %{% else %}—{% endif %}{% if l.cotisations is not None %}{{ l.cotisations }} €{% else %}—{% endif %}{% if l.taux_vl is not None %}{{ l.taux_vl }} %{% else %}—{% endif %}{% if l.versement_liberatoire is not None %}{{ l.versement_liberatoire }} €{% else %}—{% endif %}
Total{{ decl.ca_ht }} €{{ decl.cotisations|default:"—" }} €{{ decl.versement_liberatoire }} €
+
+{% endblock %} diff --git a/compta/templates/compta/mensuel.html b/compta/templates/compta/mensuel.html new file mode 100644 index 0000000..62df8c4 --- /dev/null +++ b/compta/templates/compta/mensuel.html @@ -0,0 +1,29 @@ +{% extends "compta/base.html" %} +{% block titre %}Mensuel{% endblock %} + +{% block content %} +

Vue mensuelle {{ annee }}

+
+ + + + + + + + + {% for r in rows %} + + + + + + + + + + {% endfor %} + +
MoisCA HTCotisations URSSAFImpôt (VL)TVA collectéeTVA déductibleTVA à reverser
{{ r.nom }}{{ r.ca_ht }} €{{ r.cotisations }} €{% if r.versement_liberatoire is not None %}{{ r.versement_liberatoire }} €{% else %}—{% endif %}{{ r.tva_collectee }} €{{ r.tva_deductible }} €{{ r.tva_a_reverser }} €
+
+{% endblock %} diff --git a/compta/templates/compta/saisie_paiement.html b/compta/templates/compta/saisie_paiement.html index de3ba61..47659db 100644 --- a/compta/templates/compta/saisie_paiement.html +++ b/compta/templates/compta/saisie_paiement.html @@ -3,7 +3,7 @@ -JB Compta — Saisie d'un paiement +JB Compta — Saisie -

Saisie d'un paiement

-
Éclate une opération bancaire (ex. un débit Amazon groupé) en - plusieurs dépenses. · ← Tableau de bord · - Voir les dépenses
+

Saisie d'une opération

+
Montant négatif = dépense · positif = vente/encaissement. + · ← Tableau de bord
- {% if messages %} - {% for m in messages %} -
{{ m }}
- {% endfor %} - {% endif %} + {% if messages %}{% for m in messages %}
{{ m }}
{% endfor %}{% endif %}
{% csrf_token %} @@ -66,45 +67,85 @@
- +
- + + placeholder="-150 ou 168.40" oninput="toggle()">
-
+ + + + +
- +
@@ -114,7 +155,7 @@ @@ -137,20 +178,32 @@ diff --git a/compta/urls.py b/compta/urls.py index 7353247..17229a3 100644 --- a/compta/urls.py +++ b/compta/urls.py @@ -4,6 +4,9 @@ from . import views_saisie urlpatterns = [ path("dashboard/", views.dashboard, name="dashboard"), + path("dashboard/mensuel/", views.vue_mensuelle, name="vue_mensuelle"), + path("declaration/urssaf/", views.declaration_urssaf, name="declaration_urssaf"), + path("declaration/tva/", views.declaration_tva, name="declaration_tva"), path("saisie/", views_saisie.saisie_paiement, name="saisie_paiement"), path("export/ventes/", views.export_ventes, name="export_ventes"), path("export/tva/", views.export_tva, name="export_tva"), diff --git a/compta/views.py b/compta/views.py index 217d0bc..47b03d2 100644 --- a/compta/views.py +++ b/compta/views.py @@ -8,26 +8,40 @@ from django.shortcuts import render, get_object_or_404 from .models import Entreprise from . import services +MOIS = services.MOIS_FR -def _entreprise_courante(request): + +def _entreprise(request): eid = request.GET.get("entreprise") - qs = Entreprise.objects.all() if eid: return get_object_or_404(Entreprise, pk=eid) - return qs.first() + return Entreprise.objects.first() + + +def _annee(request): + return int(request.GET.get("annee", date.today().year)) + + +def _base_ctx(entreprise, annee, vue): + return { + "entreprise": entreprise, + "entreprises": Entreprise.objects.all(), + "annee": annee, + "annees": services.annees_disponibles(entreprise) or [annee], + "vue": vue, + "mois_liste": list(enumerate(MOIS, start=1)), + } @login_required def dashboard(request): - entreprise = _entreprise_courante(request) + entreprise = _entreprise(request) if entreprise is None: return render(request, "compta/dashboard.html", {"entreprise": None}) - - annee = int(request.GET.get("annee", date.today().year)) + annee = _annee(request) ctx = services.synthese(entreprise, annee) + ctx.update(_base_ctx(entreprise, annee, "global")) ctx.update({ - "entreprises": Entreprise.objects.all(), - "annees": services.annees_disponibles(entreprise) or [annee], "mois_labels": ["Jan", "Fév", "Mar", "Avr", "Mai", "Juin", "Juil", "Août", "Sep", "Oct", "Nov", "Déc"], "ca_mensuel_json": [float(v) for v in ctx["ca_mensuel"]], @@ -35,18 +49,59 @@ def dashboard(request): return render(request, "compta/dashboard.html", ctx) -def _csv_response(filename): +@login_required +def vue_mensuelle(request): + entreprise = _entreprise(request) + if entreprise is None: + return render(request, "compta/mensuel.html", {"entreprise": None}) + annee = _annee(request) + ctx = _base_ctx(entreprise, annee, "mensuel") + ctx["rows"] = services.tableau_mensuel(entreprise, annee) + return render(request, "compta/mensuel.html", ctx) + + +@login_required +def declaration_urssaf(request): + entreprise = _entreprise(request) + if entreprise is None: + return render(request, "compta/decl_urssaf.html", {"entreprise": None}) + annee = _annee(request) + mois = int(request.GET.get("mois", date.today().month)) + ctx = _base_ctx(entreprise, annee, "urssaf") + ctx["mois_sel"] = mois + ctx["decl"] = services.declaration_urssaf(entreprise, annee, mois) + return render(request, "compta/decl_urssaf.html", ctx) + + +@login_required +def declaration_tva(request): + entreprise = _entreprise(request) + if entreprise is None: + return render(request, "compta/decl_tva.html", {"entreprise": None}) + annee = _annee(request) + mois = int(request.GET.get("mois", date.today().month)) + mode = request.GET.get("mode") or entreprise.vue_tva + ctx = _base_ctx(entreprise, annee, "tva") + ctx["mois_sel"] = mois + ctx["mode"] = mode + ctx["decl"] = services.declaration_tva(entreprise, annee, mois) + return render(request, "compta/decl_tva.html", ctx) + + +# ---- Exports CSV -------------------------------------------------------- + +def _csv(filename): resp = HttpResponse(content_type="text/csv; charset=utf-8") resp["Content-Disposition"] = f'attachment; filename="{filename}"' - resp.write("") # BOM pour Excel + resp.write("") return resp @login_required def export_ventes(request): - entreprise = _entreprise_courante(request) - annee = int(request.GET.get("annee", date.today().year)) - resp = _csv_response(f"ventes_{annee}.csv") + entreprise = _entreprise(request) + annee = _annee(request) + resp = _csv(f"ventes_{annee}.csv") w = csv.writer(resp, delimiter=";") w.writerow(["Numero", "Client", "Activite", "Date emission", "Date encaissement", "Statut", "HT", "TVA", "TTC"]) @@ -59,13 +114,13 @@ def export_ventes(request): @login_required def export_tva(request): - entreprise = _entreprise_courante(request) - annee = int(request.GET.get("annee", date.today().year)) + entreprise = _entreprise(request) + annee = _annee(request) s = services.synthese(entreprise, annee) - resp = _csv_response(f"tva_{annee}.csv") + resp = _csv(f"tva_{annee}.csv") w = csv.writer(resp, delimiter=";") - w.writerow(["Annee", "TVA sur ventes", "TVA autoliquidee (UE/import)", - "TVA collectee totale", "TVA deductible", "TVA a reverser"]) + w.writerow(["Annee", "TVA sur ventes", "TVA autoliquidee", "TVA collectee", + "TVA deductible", "TVA a reverser"]) w.writerow([annee, s["tva_sur_ventes"], s["tva_autoliquidee"], s["tva_collectee"], s["tva_deductible"], s["tva_a_reverser"]]) return resp @@ -73,18 +128,17 @@ def export_tva(request): @login_required def export_urssaf(request): - entreprise = _entreprise_courante(request) - annee = int(request.GET.get("annee", date.today().year)) + entreprise = _entreprise(request) + annee = _annee(request) s = services.synthese(entreprise, annee) - resp = _csv_response(f"urssaf_{annee}.csv") + resp = _csv(f"urssaf_{annee}.csv") w = csv.writer(resp, delimiter=";") w.writerow(["Annee", "Activite", "CA HT", "Taux URSSAF", "Cotisations", - "Taux VL", "Versement liberatoire", "Abattement", - "Revenu imposable"]) + "Taux VL", "Versement liberatoire", "Abattement", "Revenu imposable"]) for l in s["lignes_activite"]: - w.writerow([annee, l["label"], l["ca_ht"], l["taux_urssaf"], - l["cotisations"], l["taux_vl"], l["versement_liberatoire"], - l["abattement_fiscal"], l["revenu_imposable"]]) - w.writerow([annee, "TOTAL", s["ca_ht"], "", s["cotisations_urssaf"], - "", s["versement_liberatoire"], "", s["revenu_imposable"]]) + w.writerow([annee, l["label"], l["ca_ht"], l["taux_urssaf"], l["cotisations"], + l["taux_vl"], l["versement_liberatoire"], l["abattement_fiscal"], + l["revenu_imposable"]]) + w.writerow([annee, "TOTAL", s["ca_ht"], "", s["cotisations_urssaf"], "", + s["versement_liberatoire"], "", s["revenu_imposable"]]) return resp diff --git a/compta/views_saisie.py b/compta/views_saisie.py index 272f0db..1b73ca7 100644 --- a/compta/views_saisie.py +++ b/compta/views_saisie.py @@ -1,5 +1,8 @@ """ -Page de saisie / import : éclater un paiement bancaire en plusieurs dépenses. +Saisie unifiée : une opération bancaire signée. +- Montant négatif -> dépense(s) (éclatement possible). +- Montant positif -> facture (vente) avec création de client à la volée, + OU mouvement neutre (ex. remboursement de TVA). """ from decimal import Decimal, InvalidOperation from datetime import date @@ -8,7 +11,10 @@ from django.contrib import messages from django.contrib.auth.decorators import login_required from django.shortcuts import render, redirect -from .models import Entreprise, Paiement, Depense, Activite, RegimeTva, Categorie +from .models import ( + Entreprise, Client, Facture, LigneFacture, Paiement, Depense, + Activite, RegimeTva, Categorie, +) def _dec(val, defaut="0"): @@ -18,85 +24,149 @@ def _dec(val, defaut="0"): return Decimal(defaut) -@login_required -def saisie_paiement(request): - entreprises = Entreprise.objects.all() - - if request.method == "POST": - entreprise = entreprises.filter(pk=request.POST.get("entreprise")).first() - d = request.POST.get("date") or date.today().isoformat() - libelle = (request.POST.get("libelle") or "").strip() - montant_total = _dec(request.POST.get("montant_total")) - - libelles = request.POST.getlist("l_libelle") - categories = request.POST.getlist("l_categorie") - activites = request.POST.getlist("l_activite") - regimes = request.POST.getlist("l_regime") - taux = request.POST.getlist("l_taux") - montants = request.POST.getlist("l_montant") - tvas = request.POST.getlist("l_tva") - - lignes = [] - for i in range(len(libelles)): - m = _dec(montants[i] if i < len(montants) else "0") - lib = (libelles[i] or "").strip() - if m == 0 and not lib: - continue - lignes.append({ - "libelle": lib or "(sans libellé)", - "categorie_id": categories[i] if i < len(categories) else "", - "activite": activites[i] if i < len(activites) else "", - "regime": regimes[i] if i < len(regimes) else RegimeTva.NATIONALE, - "taux": _dec(taux[i] if i < len(taux) else "0"), - "montant": m, - "tva": _dec(tvas[i] if i < len(tvas) else "0"), - }) - - somme = sum((l["montant"] for l in lignes), Decimal("0")) - erreurs = [] - if not entreprise: - erreurs.append("Sélectionne une entreprise.") - if not libelle: - erreurs.append("Renseigne le libellé du paiement.") - if not lignes: - erreurs.append("Ajoute au moins une ligne de dépense.") - if lignes and abs(somme - montant_total) >= Decimal("0.01"): - erreurs.append( - f"La somme des lignes ({somme} €) ne correspond pas au " - f"montant total ({montant_total} €). Écart : {somme - montant_total} €.") - - if erreurs: - for e in erreurs: - messages.error(request, e) - return render(request, "compta/saisie_paiement.html", - _context(entreprises, request.POST)) - - cats = {str(c.pk): c for c in Categorie.objects.all()} - paiement = Paiement.objects.create( - entreprise=entreprise, date=d, libelle=libelle, - montant_total=montant_total, sens="debit") - for l in lignes: - Depense.objects.create( - entreprise=entreprise, paiement=paiement, date=d, - libelle=l["libelle"], categorie=cats.get(l["categorie_id"]), - activite=l["activite"] or "", regime_tva=l["regime"], - taux_tva=l["taux"], montant_ttc=l["montant"], montant_tva=l["tva"]) - - messages.success( - request, - f"Paiement « {libelle} » enregistré et éclaté en {len(lignes)} dépense(s).") - return redirect("saisie_paiement") - - return render(request, "compta/saisie_paiement.html", _context(entreprises)) +def _numero_unique(entreprise, annee): + base = Facture.objects.filter( + entreprise=entreprise, numero__startswith=f"{annee}-").count() + 1 + while True: + numero = f"{annee}-{base:03d}" + if not Facture.objects.filter(entreprise=entreprise, numero=numero).exists(): + return numero + base += 1 def _context(entreprises, post=None): return { "entreprises": entreprises, + "clients": Client.objects.select_related("entreprise"), "activites": Activite.choices, "regimes": RegimeTva.choices, - "categories": Categorie.objects.filter( - actif=True).exclude(usage="vente"), + "categories_depense": Categorie.objects.filter(actif=True).exclude(usage="vente"), + "categories_vente": Categorie.objects.filter(actif=True, usage__in=["vente", "neutre"]), "today": date.today().isoformat(), "post": post, } + + +@login_required +def saisie_paiement(request): + entreprises = Entreprise.objects.all() + if request.method != "POST": + return render(request, "compta/saisie_paiement.html", _context(entreprises)) + + entreprise = entreprises.filter(pk=request.POST.get("entreprise")).first() + d = request.POST.get("date") or date.today().isoformat() + libelle = (request.POST.get("libelle") or "").strip() + montant_total = _dec(request.POST.get("montant_total")) + annee = int(d[:4]) + + erreurs = [] + if not entreprise: + erreurs.append("Sélectionne une entreprise.") + if not libelle: + erreurs.append("Renseigne le libellé du paiement.") + if montant_total == 0: + erreurs.append("Le montant ne peut pas être nul.") + + if erreurs: + for e in erreurs: + messages.error(request, e) + return render(request, "compta/saisie_paiement.html", + _context(entreprises, request.POST)) + + cats = {str(c.pk): c for c in Categorie.objects.all()} + + # --------------------------------------------------------------- + # CRÉDIT (montant positif) -> facture (vente) ou mouvement neutre + # --------------------------------------------------------------- + if montant_total > 0: + cat = cats.get(request.POST.get("v_categorie")) + if cat and cat.exclure_calculs: + # Mouvement neutre (ex. remboursement de TVA) : pas de CA. + Paiement.objects.create( + entreprise=entreprise, date=d, libelle=libelle, + montant_total=montant_total, sens="credit", categorie=cat) + messages.success(request, f"Mouvement neutre « {libelle} » enregistré.") + return redirect("saisie_paiement") + + # Vente -> facture encaissée + client = None + nouveau = (request.POST.get("v_client_nouveau") or "").strip() + if nouveau: + client, _ = Client.objects.get_or_create(entreprise=entreprise, nom=nouveau) + elif request.POST.get("v_client"): + client = Client.objects.filter( + entreprise=entreprise, pk=request.POST.get("v_client")).first() + if not client: + messages.error(request, "Choisis un client existant ou saisis-en un nouveau.") + return render(request, "compta/saisie_paiement.html", + _context(entreprises, request.POST)) + + activite = request.POST.get("v_activite") or entreprise.activite + taux = _dec(request.POST.get("v_taux"), "0") + designation = (request.POST.get("v_designation") or libelle).strip() + ttc = montant_total + ht = (ttc / (Decimal("1") + taux / Decimal("100"))).quantize(Decimal("0.01")) + + f = Facture.objects.create( + entreprise=entreprise, client=client, + numero=_numero_unique(entreprise, annee), activite=activite, + date_emission=d, date_encaissement=d, statut="encaissee") + LigneFacture.objects.create( + facture=f, designation=designation, quantite=Decimal("1"), + prix_unitaire_ht=ht, taux_tva=taux) + f.recompute_totaux() + messages.success(request, f"Vente « {libelle} » enregistrée (facture {f.numero}).") + return redirect("saisie_paiement") + + # --------------------------------------------------------------- + # DÉBIT (montant négatif) -> dépense(s), éclatement possible + # --------------------------------------------------------------- + total_abs = -montant_total + libelles = request.POST.getlist("l_libelle") + categories = request.POST.getlist("l_categorie") + activites = request.POST.getlist("l_activite") + regimes = request.POST.getlist("l_regime") + taux = request.POST.getlist("l_taux") + montants = request.POST.getlist("l_montant") + tvas = request.POST.getlist("l_tva") + + lignes = [] + for i in range(len(libelles)): + m = _dec(montants[i] if i < len(montants) else "0") + lib = (libelles[i] or "").strip() + if m == 0 and not lib: + continue + lignes.append({ + "libelle": lib or "(sans libellé)", + "categorie": cats.get(categories[i] if i < len(categories) else ""), + "activite": activites[i] if i < len(activites) else "", + "regime": regimes[i] if i < len(regimes) else RegimeTva.NATIONALE, + "taux": _dec(taux[i] if i < len(taux) else "0"), + "montant": m, + "tva": _dec(tvas[i] if i < len(tvas) else "0"), + }) + + somme = sum((l["montant"] for l in lignes), Decimal("0")) + if not lignes: + messages.error(request, "Ajoute au moins une ligne de dépense.") + return render(request, "compta/saisie_paiement.html", + _context(entreprises, request.POST)) + if abs(somme - total_abs) >= Decimal("0.01"): + messages.error(request, + f"La somme des lignes ({somme} €) ne correspond pas au montant " + f"({total_abs} €). Écart : {somme - total_abs} €.") + return render(request, "compta/saisie_paiement.html", + _context(entreprises, request.POST)) + + paiement = Paiement.objects.create( + entreprise=entreprise, date=d, libelle=libelle, + montant_total=total_abs, sens="debit") + for l in lignes: + Depense.objects.create( + entreprise=entreprise, paiement=paiement, date=d, + libelle=l["libelle"], categorie=l["categorie"], + activite=l["activite"] or "", regime_tva=l["regime"], + taux_tva=l["taux"], montant_ttc=l["montant"], montant_tva=l["tva"]) + messages.success(request, + f"Paiement « {libelle} » enregistré ({len(lignes)} dépense(s)).") + return redirect("saisie_paiement") diff --git a/config/__pycache__/__init__.cpython-310.pyc b/config/__pycache__/__init__.cpython-310.pyc index 7b12e9c4eb514c80b3bf150e2c6d7eb6cc4d7ecc..a49be8e2a26143958b5ea74d602ac7d4a3d00060 100644 GIT binary patch delta 10 RcmbQwm_NatQD|a?Isg)K11bOj delta 58 zcmd0w&p5%|&`G~IwYWGlKd)FnF(tn!GcR4YI43o=pi(zGH76&vNIy5PMBmFvL02I; NKewPHQGcR`IskV<6xRR% diff --git a/config/__pycache__/asgi.cpython-310.pyc b/config/__pycache__/asgi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3dd4768d8d3ff9b430f189c4f2c45c8de024edc3 GIT binary patch literal 281 zcmd1j<>g`k0`^-@SxbTRV-N=!umCv@KwPW?BvKes7;_k+Kr~YhV=hw^6C*<^Ll*M_ zmK5d`mW3c5Q!_&pYYJm9gC^@spbiE8U00iN)!e@reZmIho0cC7Jnox5Qk$ z9R1w=wVN;31(i#6G9G36KE5=hBP%uCPL zOU^G!)dLw;!~`_{7JF)5S!NN?I4+?2l+?7u(wvf8+(`DUWGLbUDg+b1_#kHMgY@ea eRNmsS$<0qG%}KRm1S%=!01_MwJdAuyT#NuncuHLW literal 0 HcmV?d00001 diff --git a/config/__pycache__/settings.cpython-310.pyc b/config/__pycache__/settings.cpython-310.pyc index fc794266293c6eeaf338a16eef12dbfbd5e5d9f9..918c024d6549cb2d8250e38aa9ced87f4c799912 100644 GIT binary patch delta 14 VcmZ1~`Bh?r1T&-1W>4lAP5>m01P%ZI delta 62 zcmew=u~c${1hb)&esOAXab|vAv3_DoeoT(;>`TKV*SLF{G!afblu{d)YO7X-R#tyoYW%y+`JNfFDC_E Qh2;F)f|5l2$zK>v0ohg-*Z=?k diff --git a/config/__pycache__/wsgi.cpython-310.pyc b/config/__pycache__/wsgi.cpython-310.pyc new file mode 100644 index 0000000000000000000000000000000000000000..79d3c970cc3c3845280997ddc199ba271bdf0371 GIT binary patch literal 281 zcmd1j<>g`k0=8RDSxbTRV-N=!umCv@KwPW?BvKes7;_k+Kr~YhV=hw^6C*<^Ll*M_ zmK5d`mW3c5Q!_&pYYJm9gC^@spbiE8U00<;Cfl@reZmIho0cC7Jnox5Qk$ z9R1w=wVN;31(i#6G9G36KE5=hBP%uCPL zOU^G!)dLw;!~`_{7JF)5S!NN?I4+?2l+?7u(wvf8+(`DUWGLbUDg+b1_#kHMgY@ea eRNmsS$<0qG%}KRm1S%=!01_MwJdAuyT#Nu=$x5C8 literal 0 HcmV?d00001 diff --git a/db.sqlite3 b/db.sqlite3 deleted file mode 100644 index e69de29..0000000