From bc608d7d1e98fb5897e8b5a59d58a3094d80ba4c Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Tue, 3 Feb 2026 17:24:51 +0000 Subject: [PATCH] v2 --- core/__pycache__/admin.cpython-311.pyc | Bin 1819 -> 2197 bytes core/__pycache__/forms.cpython-311.pyc | Bin 0 -> 3210 bytes core/__pycache__/models.cpython-311.pyc | Bin 3601 -> 5024 bytes core/__pycache__/urls.cpython-311.pyc | Bin 347 -> 541 bytes core/__pycache__/views.cpython-311.pyc | Bin 1294 -> 3549 bytes core/admin.py | 20 +-- core/forms.py | 42 +++++++ ...ct_supervisors_team_supervisor_and_more.py | 40 ++++++ ..._active_team_is_active_worker_is_active.py | 28 +++++ ...s_team_supervisor_and_more.cpython-311.pyc | Bin 0 -> 2364 bytes ...is_active_worker_is_active.cpython-311.pyc | Bin 0 -> 1109 bytes core/models.py | 15 +++ core/templates/base.html | 31 +++-- core/templates/core/index.html | 15 ++- core/templates/core/log_attendance.html | 119 ++++++++++++++++++ core/templates/core/work_log_list.html | 64 ++++++++++ core/urls.py | 7 +- core/views.py | 42 ++++++- 18 files changed, 397 insertions(+), 26 deletions(-) create mode 100644 core/__pycache__/forms.cpython-311.pyc create mode 100644 core/forms.py create mode 100644 core/migrations/0002_project_supervisors_team_supervisor_and_more.py create mode 100644 core/migrations/0003_project_is_active_team_is_active_worker_is_active.py create mode 100644 core/migrations/__pycache__/0002_project_supervisors_team_supervisor_and_more.cpython-311.pyc create mode 100644 core/migrations/__pycache__/0003_project_is_active_team_is_active_worker_is_active.cpython-311.pyc create mode 100644 core/templates/core/log_attendance.html create mode 100644 core/templates/core/work_log_list.html diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index cb46a21f138f50f2de98bcb29d2402ecc02037d6..fe8bccf7f56a5b75b2452ace3ead20504c9b6082 100644 GIT binary patch literal 2197 zcmbVN%Wm676rJHy*28w}D0Z4Ob<-#b7qw8dMK(o?X46F-p+H*{;D%r{6N@e>sxx9! zcO?}00PXSz4h;WDmNuY;F{=PsWhcX4Wz~C0iLogygz#}Rk2`11nYlx~s@2K_E_Um` z;JHr7TdYhkYf?CWO$m8P7-2Ld5p^jgOk;YeMY^j;hHFHoYetr9MYd~Gq7hE_N>dVU zAHe7%c(Y3*KM`iUAk2iQrD^K{%zXrJb{)Xa(X>L`a)D8w!)O7cy)a{?z-Y{2ECJ)v z!i?1dqdAAM42+e98EXYbYYt--7;6hNF0$!SJt7*^jQ>i-{H7`Oyj-g7P*YzXrd8+JrQOuGM%T>?&EAhigPc;l=-(!L7 zh5r5_IY%|`zif8mh&Pj`Nw=Rg`9Hj!^kdO{4)SHsZ}Vn57QC6xv)$Vl6&RC(0R_1Z zFv#yW>)V4Jyv6lt)O^P`pu6bUHJA>r4OPh&G&5`{1H~&dU|u&?rCulQax5)Jakt+I z_dV%{zSws(v59lu1ZX*?Si#CF!Zie3VpWse7wwMsEZ`xN0<~vC>sZ>D0-ivLWS5OS z9_*Z6Zw#K`oejeHk8oF=q)BEkQ?1Hv!TmmGp5KT3s>r0r#ovL9g=`(p_i_!=sn3Pi ziAC@)tb`vH{oFx62sL=+Er82@9PqRG@YtwlPi6_DKf=-yCVb{cPj^9;KR6Jd0qG%C zHYOAYL*jF2D2ly@Z3wtgGh!d1*n0)B7(HL?C(u*KE(+PDx#lFps*|%h(X0#xi?8fn zc7R0##*6y^EytRhNR{*)Wji45Vss&w9Bf0tB#~PHm)HIqu(S5{;OALSIHcvQsAab2 zcX#9MHtehjo(dh>l?A7ohf_z>g73lshtEU#35KTjvICnYcbXCD zN~ij+YJOk(F8gYHFTMgY{5Z*1fMcCfdP=sAuRf>bhvUVbH*{rCe{pA6Iia^l^!8Yz zv^gfTFib`p56-aT0^v+2+NXKEMYPqF#X6xIBf2rxG6)5+n=Jh)UcL5+@F{5fVs9=3trZwsEoJuucTw6rp~A z-1-fQs-E~JtrVncPn9_J)~Y@A#EhMUHjPBpwf*-##=o5zkNq>3OA~0n3P0T63L$@R zFla`pY~QoV&dAw^axQOLrelOb!}p1glLQ*rh%Bb zHZjYx?}(W@!aSH%>JIbW`Vr>Q)THqt5TCh_iMJxy;MJzo-gC#737lRD)~mzhL1&Qq z(>WMy5l$@1h@~)UsZ6mnrouLw%*4c3xQnSvt?u|->MpbEflM}PL7ii7!Vl`rn%A~N z$8$tmCYjT=h12AwDl&irb(lz zikQSrF!}Lz=#aQGkQ8*T_-LY!_b&Q~_BZe`g=G;rDDDDbhsp$Ijf=2i>S79=m`0gF znFWzkYp&Pi!mb6v{SGhN@kU)QqAkht5W4=PRZ$Hk3?MT5&w$0T_-(vWRmi7=E|_axs#}5Xp3@ z^X6(e_(;Xf$qBaM)YpThD&$GHpM(nY(ttweUMO@J2>g8t{1*!R)ybsb>rn3S;igy7 zDtb{TyL=vy<-Px%hxAq_zgg@W z`*glX=Z_VIE+3Pt(k=C-AD-aC8On)Dl)}(HNtDTmlGvv(TR=(?DWlST-kVuON)ahV zq}(3b4=LI{&G%>?DJ7(gN>}eqE+D0ZloC?rhW10s&=(Z}SEcU@ES%x$KVL4HMuqzq DIs}H4 diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ac2da0c5ee185c7064fec8dbf3b3debba5ac774d GIT binary patch literal 3210 zcmbVOO>7&-6`uX&@<){diK2-rseckV6dekXWj6^F%Y{`3iEY`4Wf&j|Z`RzAyz-Ju z%`PolpaUZfP_{^09aO}J9E2cHoR~m#%B_G8J@~jJ79cS|00TMsMnf(V_|!MMBqdsM z8f13&&CHvbH}8FK-n{*)uP=h2{CMzVLr@UaJ#d$35~yiiWp@%9c6hQVPR41 zOvTco+-a|OmFjR zIji6h%lsO!(j14_0F$y*!sM2X9jXw#R5A$GX%y#+&Mno{u40qQ%Z9#yogTb8_H0!_ zt!=chkQEoexnFQu36PVw9mm|Pi=Xq)Tkg;5NJF};3%k+r=X5;u9K!yXfAAPSut!K& zcJHEdpNrY39%fiQ8`yzW<{Wl30729>2h&ioeNaNn{z0*+RInUulQ_sGF(4_Piq5Sf zCQ-)eV}!LHvm=?}{WcYewQSF(VpLJpf|kdsN+YV8xAc<9+A&pKDrsggLRe%_2?iO^ z^9D9`n=oNCJOdiNS16X?*aq&T!>6v{-1V#0&7gm-WI9IC#8jEXHyw>&jfMhO*nEVAdVG$ljs3+R&UgG}lZVS}m=W@BMxgMozhj znOb7TOUyJAM^{T;;%F^#%u5_=#t*GtTD!72`|uC$7|fe>W@+;O$&Tl%af7SkSSs?!|3ya-Q zv%f2gduv}7s$#G9Wwh80KV3?86;Zo^2a;JF_+@>Er5)b$7vt?vIke8gXIFtMfUkyn z{k6Ws_U#3|Uc0T7r5D(vRF$8xC%mUU@(W}M)yU7titZ&#M;B39`~?_FJ#${5SI?Xm z?w5+qQn6%XG6!ifY}l%nbBr4pI#Ig>*VhfhwunuJ0ZkfhUv{c$=rBQ?H#G-p+0cPg zAPJZQ-N^PjxB?-ur245)w2D;n2iRq26bwgI2@4b&vaYg}YqLwubTA?8Mj`C<(HN-X zuVMO;{fy7znD*thg0_Hll}Wd$pqVC(-q6evwpG)xAwTIrjY7_WgeThAoTs8j7GR6( z%d948CMDXw!)&v*`{Gt^^>Hk+U!K{@02^_b?A~~Kvsq> z2Y(YR!)7|Rz62xohj1(XbO$y2A1s3YQt0B@Kh!Gef+m;R~qrLdVFki&W(@N;%|HL zw=0qF2NP>kjlt3S;OOR%J2+Y!Jnju1uPBeA{Vg;Qp1do1`%-@EQRb~i=43r{^5LAD zIa$km;AK9j%zS$0ZrF<*^kV5&1igH~8_6_ACh8*-wUJ40WU_LunH+5-C+f+G&37I+ zZgQfQJnJRTHj$xF55WiNTT@_sY(W+O9Q&rCm9aWm6nSP+(o35qL zcydx@U|Qhj8E`Qv3qUSY(~lmq8j_B?gXGIIDd z%b@--BgbI^6$@4YlY?yT1S2eq`2M7QZJ+0`X{u_9BQr2$vjlT8@H#sQgc0^vX8#8W zIJxsQqX#O-yy$^@lhvt4da9nDs-;hO=@Sp*?w<^oeB_d4k9^cZ=v{u6hw1`)PZ;m%_P|h5nrxw8MuKF@%$iMwsjM$* zg#|0XQTG2rZ2y0~XWxf}jDmnb6pN5&$`q`H-3^9L-$MWZ literal 0 HcmV?d00001 diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 5c3c7504c875e21e274ac2551d25121f584d2c69..b221b6f89a8889d337bde57394fc060ff72e63e7 100644 GIT binary patch literal 5024 zcmb_gO>7&-6<+@TEq6)EaufeVjQl4;4JFyMYZPvZ*s>BwHtpK9l&~%qEA2?6yxf&% zNn0X%APOIZ!Uv-WV5C6+6-Zqf*1?Ayatw0vaoO&H*pq>te6t~+a_W1tq$n;GIRQFa zKJLuCoqe47zW2@OU-5X9gYuu@zm#8xIqtt$>6AeGX6p^yJmM5i@u*zIQ}lT7-m7{m zzM_xa`&54=Pz#`rq?x5 zA?Ck5jfzk4Zw4NFVKU+46r4;Lg)JzyD8bFpV-I-1$0@~_628wBc_jirE^5ax)@xK> zD67N{Eju?)ymmy_B>1_cN^G7IRjv|6(&P$xiQ(1#d0-$?NUCypwqOfFP628 z*N#@?6-g!9Vzp#PN<>|js$``)^#tZeAq#R%t(2!yRPm`B{7xh42^%F?#q zR{*-Ny%4Q8Uyq(J((NGOZT=brZ->!hZ15JpLDU5UA5H(59V_V-GFF?bY1P^oSt0W^ zSgNsmI=yWy%kyMxUZ-TtS=H?FsvVLfqe>;IKCoj>vhBtg3~B5K)8JaE{a<9P)KOC$ zZ(rf@EetxJiQ8d`O_Lp(DO@%rS*esY`VKfh;pn^YMSq0`5yA`( zYTO3X*#2w9hSttlv7vSUv*5SF^UuxLY%?}%#bz6mt>}TZ<5u**x@?~M)QnCyqtjM& zx^bZ;4zJy{#Nmz8rZ~|QCoFNI@yRQ{Cou7f>o-mu7{cxh_yKx;iUADsuy{Mr8en+t zvck9Ncj4K^3KCHQif=RM;tr$_;03NCE8yYg1tbOVi0|+SyWS)ykba7y5(O;A>=3YK ziOg4DK8Hu1c&Gqh1%O~+PL;LWc2YKs@}dT`0lcE0k?~R%bSCwlFIIe8qtWt7iBewov&@ zf*ycodJxSaH26|Fgk~I!9VsAz5u=dPNwe>79KnyFh>>K1e* zk3HOPC67JJnaQ)w0vIp%E zXuz^{9G3AHquuX34A~npyb|cca*!dl>lISMn-P}|xLBb?6_7@P>JU}^qc@j*a9=SA`&etk)gl2q*HcMEP>+hIjsl_>D1E;`ec?KGsp zy)8{I!~sau|D4^J`G@q4)D&}OIL9FR^HU4W;@ih|34xKcF0|j?*2SafIDrO5%12R`3)LO81yU{c$DgpW2&ok3fnNfoNkKKmN?z` zq%}D5aK;)Od3OH$JLb$HUL_r`<-5(&J*#xDIkRHTte7`f&B1zeux<_38~LA~Ze9f9 zue_eXY0NmMU;DVAaQ`!>A=saSSXy%O5dP$zJQRWkEL;Bu^NaJ)w(61)=%wG4VZw~_ zolFOLrAJ=_pu)KK1S~&AJhM`CYiwY%Ph;7rV&@6Sevw}K6cb7maz;{#0f~6qUm$X& zn=$M_73v1aF{Z169k0lmya*!9nlpZiI{*;QnyNA#b`*Jce($NkZ=(s1adp&}QECTC zQhSqx<{(IhUps`mRRUTXor29O>?fwlsdZED?DPs;R_MZ%F_0M@+Q#NE~BO})fA>IVQP1}B#X^t(MlG9D{U28 z&zby4lOM78k;X;bdgd_4yN#3bG1h!Wup`QntS#zUWzIQX?r+!ejGNuDl|dBZS)Bc^ zf+?+iVB1xz((+su&LyR+b1ZgJP2dXt=fgQxve`zRVcCOCl}mkp44-q(y&qBneuydK z6xdfjkH^#E4w=1Ii_4k4SBpDn_FgS+)a<=p`GX!fe)gM($G`aXYixhh?M#F{uy5}- zYYPw5*Vz81+qo}}zWrux=E1Gk*#4&5`Ck8!>@{m=9-QkNH|&80>^Gelau|c#gDvs@ E08sT;3jhEB delta 1682 zcmah}&2Jk;6yHtu+aK$7Na7MBT+$D@1xKP$Wkec?5{mi}3luArETq*YJ58P1yJ6N* zaX?ChdWb|0HS-UE1Z|>Ls1j$6ph~@@QVy^Z5*H4=rE<7%;=QqrX>DMn{q4-}y?Jlu z_ulSTryJ?`y;#f;VBAZ6Uj9LD#8b?dl*a2P;se6_f-Bt?T$zZgO5@I`sd7pIO6^b@ zr!=7S4yAL-04ma=Qi>a05VA3r5hq5MF;`~{wkOREwkFw&?%)IYt|$oi;c1PM?n1hf z;i?co3)K&(ze<*CPalDKPYjK!^NC#{-b(F85ti~&#qn;!B@S?*AY6oPP1WRmJ$&Xkk?~EK+Cm&AkTlpte{=mv_T|Si9cahX_ zz7h94Cx{LNj;18CkEL_$OZEFpB<`BGg}cDs=Qf0r=_WvzglmB=$xylG)0>1+4NMec zJbe?OxS?N~%M{kDON3?zslgs<6AIpwo?^di(_)Neb??d$5CICF;r9qN*QwjosgVt{ z6*bo(UP4d2_m=RRIdovY_U*OZ#jove>?h{rzBWlyOpoPC32>km!V8fpZrzBtgSNHj zGK``(&I(zJ9>c}U2xAD?A~cPVWUIy-GdY~TijV;awKeLm5n8L$3FI&ZJ&w>V%s4I) zV(3BV<^(*|A-XDV4e2Nz-j0|B#QorMBo~l^Nv?9`iQNj z#ruWoE#4vl*DlM&>#Npz%!YvK2QF9ZbUZcuubpgg4RObE!x|QML9#LSYrM?jva!&s zTAZt9wpCNH8?O2*p;Yl#g6u%3`=0HR3aJtL7K99SJhqKSnum3fM%XVVIh%xe|Ni1l z3lTf&Wm!3KY7@^`$R_$J7wU6Ro|aOxn9{iDQ&L{>u8=zas6%yjW?^<_?maq(S>HxL zN$C{AD*&PF`85*s)pHu13`E=$e9F?+hwKY07v-`ckH488t*Lk9c^_`x`j7DVycB%@ z^Bw)CHdXcA^$MA$^ROSk-QW*_T~A)vJd_mi*rCu1+t+t0&yYXr8WU0U?S<{dosXU& mf7CUmdqeUx+vj(tx^drarB0~g;{l~ki9!W5|fJ+rJei&AJHFk zBoyRYD?1W8D|b(g(dg_Wv-5r5>^Jv?LJmaqx37B{0`MXqUy=SyRwI$z0tHGeu!%tk z&=ggyl&u(wjRxAPp%O>|BNUlJ)o1Na0eBMUBN8OdkY@fwvP`A9-$+L>G@3t#%G#@J zQXw%8OuzdoOhQG8vFq9_Ubo!NspxRH-uOY=Y`^0Duj>) zl%k(|0ejK^d#Qn%4V#nA$;niUusp-^9Lo#Lgv}_sHEl(^M-et=*qCD@sZL5aR}Y5~ U)@N9sV||&%Vk*qb#n*+2cZ=bVTmS$7 delta 230 zcmbQsa+}F%IWI340}xbw%g@XL(vLwL7+{4mKHC5p(-~42QW$d>av7r-85vTTQkZj? za+#x;Cwj@4r87pcq_77wXmY#+Y0+f7#Zr)1lJSxe%E`#jO})iX1TqgKmzS8E>Zi#% zS&UI`vK^yu3?EP&q_o%zNPJ*sWMsU-Aael~Jzx;OfQmk_F|cwrxO510gv?;Oz#@N< WMg9tl{0C+xex?R)5G>*W8V3Mi2QzX2 diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 26ee8f4fdda73b89ffac2ed04871e384ee4c2ada..fca06b7aa8459d0cfa7b050ed4a1889f3dbbd9f7 100644 GIT binary patch literal 3549 zcmbtW-A^0Y6~AMTKVXc<7!xoA)&v?bBCsN95*jwFRvVVxZOFEX6xM2G8P6&zkC1M)zyiBBBOs)uKN-C7yW3EtKN83fW|(Oki=zCqW0zz9Q$@9 zT<~^f-725pRZqgBdJ|rb%DA&W)t~UQHlOWKI}@GI_DJ5Wpav2FwJXu31`|P5Oo*)C zmkp`iiEe27AE888>Hyv&bpnq_++7qGUQ&0K<8H@`<(wpA$B$)6!E%~7-D@&QYO5J& zCpDbB^8S@L=lF@D$`7@i4DDc6TT^mLEPs;+4e2EZrsBNgy|3Y~!IJL_tWjl$zbB_u z$49$=t*wEgP;V}3SWQ!{26>_3hX1P-=-o%8(GTxTQ|_=4!j@5lp2OFRX3q{ssI5Bv zOkEd&O>5B4)O8cK^)>Al-4<^emJw|Ib6C}j=E@|OvYG%>|+{IEKnAp!+s4z zrY2XnF^#I@&S_hit}pI&d^8zlo#1XN9j1)N14n>4zO(e(P)8$fRq8OR5 zB9nGxvT(c7d;V#%)H_-1owRzV?B1!uawUB3=~O8^Rt%3>;fr?oV&U^jIQn#^6rL=G zCy%aK;mdaT^2tXhzd8BH4qq#LUiG4Z@$$e#x$gozmHya4;4i_0U{&yn-YNoC_~dmE z1;S6p&B3XoHA}c^3s+74>ML*xwUrg!HC9#w!1s}XGE{_-#C^@m3ICZiQv^|oW|2X= zIdbfJ4*f5hV>_I|9r6uZt?%~K#4^ZHFV2wUKIYqtV8_)uV}|Y(BOGY$H(W_yW5jU1 z+jhGT90DJjrPI)*@6&RvB_w&;y}OHYqo*R(j82QMzE)pzfa(yrwo(UxqB&Wo3{7rE zhcv^_kmNn~ozi!=&d@URcw2v0!<0T~xZiENRsPt2dKLu%2)RE-zyEXxpd=G)*n79F zvwj}709Qj7fUg52J5S}3myEoXFud)a7(C2Qi<;3q@V(Crj{KtD0k2FG_>5B-B?$+v z9UqCml?7l77``*W*tP8NU);HS4~{54ugiE58s4;~stRE>PX{^_J()@qWs{zOoqB#l z#+!<+VaKINRPU>7io{*;hO^D-ra+i%fMrJ4au53%pa2*qQ-lBtr*dhz27bMcSCz@S zCSkg3I7S(bh~tMTH&aTWp#*apl@W)&UIYRWMUtHV=&9gmb(H}o35`tbm@JWfabRGor4 zTnQN3f1^90w&kj+4Tq;aPOuiRMj-GY7|>}c)<8HG!{@0C3jP&z(h2oPP&x%v z_5~mP;n}&O?>*D^UPT;ucI{yPXze&_iZ?9rhArOM<;#Nj-3O09_;z7$0T8ldtlT%a zzx~7Zk>|;_)i-VTP46z1#dCXxElyPt*LAsk@zRgkCw@CLS`I|MyZ!j~w|DmL?A|E{ zLT^7RJ%i5{t)5Z4XVeUgR=dFL>ktYJp7{PAu;0IJ#_k{beheH2%)9qZam5l>Y;k3m zf9*w~$evLWFBHWKNB2zef+fz{;%s4wF2k^WW8i>i{^Uze=?fB| zCcPlk391+_Oa`ZLXDXRr#uRuS_OWVfq$*2sR&0>xa2<(z*o6YKDsiW>S=nl`PkYnZ_8 zq*jZy8mC996Q~R1iIUZ8SEAv17rpRcWp$dO#k3?tFj8>4(J?(b0c|u%1`Qg~(^o7H zonNm35xwoOT4am;8)Ck)ihbjs2OIgUd=q~HLQthhF9EB%IgTr%jsp9Ykyv2AG7<{x zS4JP2|NSYW5wra%qdBwvDWh5Qe?4V1Y_>mDKFD3Gq9%(CaUXJ3)WUDNxJBrBH)jj~ E5B8-S$^ZZW delta 441 zcmZWky-UMD6u--NViG&`109r3SrlAc#KqORhzPex+eAs5%auzC+Cf2_6*7*Wy-~B!w@7=vFTzcVs5O@gAbNxJh)ULz&**2(K6r&hti2U4` zV9;8oQJv^md3t70lbFDb*vu?y6Pr53p)PT$M?CPY3q6>nbkX$xOrJQ6Qjx@tj>v{|Q@bjw1TT{g^1ZiC4k z1+I!6#$oh9=Y$)guQw{bN&*F;f_ZVHx6C?FTfFOcmB^PGE2J~X+T|hRRJ8osp7i5h zJ3nG8!!peZZa}xRD2o#i>J5K(ay9M&@rH_O03Xf2j?u50 bpyl!JnutMA6Q_X-!P?9S@%jw?hwoiq=MQgR diff --git a/core/admin.py b/core/admin.py index 80b8c10..deee1d0 100644 --- a/core/admin.py +++ b/core/admin.py @@ -1,23 +1,27 @@ from django.contrib import admin -from .models import Worker, Project, Team, WorkLog +from .models import Worker, Project, Team, WorkLog, UserProfile + +@admin.register(UserProfile) +class UserProfileAdmin(admin.ModelAdmin): + list_display = ('user', 'pin', 'is_admin') @admin.register(Worker) class WorkerAdmin(admin.ModelAdmin): - list_display = ('name', 'id_no', 'phone_no', 'monthly_salary', 'day_rate') - search_fields = ('name', 'id_no', 'phone_no') + list_display = ('name', 'id_no', 'phone_no', 'monthly_salary') + search_fields = ('name', 'id_no') @admin.register(Project) class ProjectAdmin(admin.ModelAdmin): list_display = ('name', 'created_at') - search_fields = ('name',) + filter_horizontal = ('supervisors',) @admin.register(Team) class TeamAdmin(admin.ModelAdmin): - list_display = ('name', 'created_at') + list_display = ('name', 'supervisor', 'created_at') filter_horizontal = ('workers',) @admin.register(WorkLog) class WorkLogAdmin(admin.ModelAdmin): - list_display = ('date', 'project') - list_filter = ('date', 'project') - filter_horizontal = ('workers',) \ No newline at end of file + list_display = ('date', 'project', 'supervisor') + list_filter = ('date', 'project', 'supervisor') + filter_horizontal = ('workers',) diff --git a/core/forms.py b/core/forms.py new file mode 100644 index 0000000..3238eec --- /dev/null +++ b/core/forms.py @@ -0,0 +1,42 @@ +from django import forms +from .models import WorkLog, Project, Worker, Team + +class WorkLogForm(forms.ModelForm): + team = forms.ModelChoiceField(queryset=Team.objects.none(), required=False, empty_label="Select Team", widget=forms.Select(attrs={'class': 'form-control'})) + + class Meta: + model = WorkLog + fields = ['date', 'project', 'workers', 'notes'] + widgets = { + 'date': forms.DateInput(attrs={'type': 'date', 'class': 'form-control'}), + 'project': forms.Select(attrs={'class': 'form-control'}), + 'workers': forms.CheckboxSelectMultiple(), + 'notes': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}), + } + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user', None) + super().__init__(*args, **kwargs) + + # Base querysets with active filter + projects_qs = Project.objects.filter(is_active=True) + workers_qs = Worker.objects.filter(is_active=True) + teams_qs = Team.objects.filter(is_active=True) + + if user and not user.is_superuser: + # Filter projects and workers based on user assignment + self.fields['project'].queryset = projects_qs.filter(supervisors=user) + + # For workers, we might want to show workers from teams supervised by the user + # OR just all active workers if that's the business rule. + # The previous code filtered workers by managed teams. Let's keep that logic but respecting is_active. + managed_teams = user.managed_teams.all() + worker_ids = managed_teams.values_list('workers__id', flat=True).distinct() + self.fields['workers'].queryset = workers_qs.filter(id__in=worker_ids) + + # Filter teams + self.fields['team'].queryset = teams_qs.filter(supervisor=user) + else: + self.fields['project'].queryset = projects_qs + self.fields['workers'].queryset = workers_qs + self.fields['team'].queryset = teams_qs diff --git a/core/migrations/0002_project_supervisors_team_supervisor_and_more.py b/core/migrations/0002_project_supervisors_team_supervisor_and_more.py new file mode 100644 index 0000000..5c25dff --- /dev/null +++ b/core/migrations/0002_project_supervisors_team_supervisor_and_more.py @@ -0,0 +1,40 @@ +# Generated by Django 5.2.7 on 2026-02-03 15:59 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='supervisors', + field=models.ManyToManyField(blank=True, related_name='assigned_projects', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='team', + name='supervisor', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='managed_teams', to=settings.AUTH_USER_MODEL), + ), + migrations.AddField( + model_name='worklog', + name='supervisor', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), + ), + migrations.CreateModel( + name='UserProfile', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('pin', models.CharField(help_text='4-digit PIN for login', max_length=4)), + ('is_admin', models.BooleanField(default=False)), + ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)), + ], + ), + ] diff --git a/core/migrations/0003_project_is_active_team_is_active_worker_is_active.py b/core/migrations/0003_project_is_active_team_is_active_worker_is_active.py new file mode 100644 index 0000000..8791b5d --- /dev/null +++ b/core/migrations/0003_project_is_active_team_is_active_worker_is_active.py @@ -0,0 +1,28 @@ +# Generated by Django 5.2.7 on 2026-02-03 16:31 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0002_project_supervisors_team_supervisor_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='project', + name='is_active', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='team', + name='is_active', + field=models.BooleanField(default=True), + ), + migrations.AddField( + model_name='worker', + name='is_active', + field=models.BooleanField(default=True), + ), + ] diff --git a/core/migrations/__pycache__/0002_project_supervisors_team_supervisor_and_more.cpython-311.pyc b/core/migrations/__pycache__/0002_project_supervisors_team_supervisor_and_more.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c1364c3165a60b0c08f1e8c2ea5306246af2c016 GIT binary patch literal 2364 zcmbtVy>Ht_6elT)A|*MN^MUa6dgKdYyJW^g*9}{(4j+zjx<1^!c7G_Wm6DOnfi`2<)}%Up(pY2-p9T7d+**I ze;gQyb1;@BzSUPHj{A!-;fNkjUai682M%$FH@FfHZ@~~6ky3q>8Ssy7QW<52uf^zFnx?88C0 z7uz>_@6@>W;1%G}a_m9akKhuv<+`c68e!_6ALBwyoDpjMQVj zosZCH+0e}UG)b_bIiNDN2Bv~zfhz8wfIuSp@@Q4Z2BM;awFZr!IHv;*&D3hJoN4TQ zl(252qS-PGs#vB9CkZ>4uzn+~)o5(jBKHlemKS|fGRidBo;tBA;f%DSy;sG*y1O~eqcYAwT|VhaNK zC1&9AkpOisv%El94^qi5mA#VBQd#wZs49)CD%7-PV3;RW^+8KBSY1_>Fd%f;-qo5- ztqgjEo7hCytn3kXbbo%QCQL2He1^i8m}D0UKMGUtgHNQKWGT`Y*Upf8t&NH10JjY+DUnXq8*0 z(<G4uG$th=;ZlaT&?4-|jGSg2! z^fJ@W9QXRBTPk}c;%4l_jO}IYZcJ3-FF7DbjCA8COLLucu9G>(mY#cd{^yNf@BOyr z6*s-&*Iu#K<+xpbQGnMSVO=<4uL|Raknqx{AcFaSxCdna?%p*VaM0k3zRyM9=OPO; z1To)+`~l1METsBpdc@6Me6r(ZFFqH4Kkt@Q*CcM%KFr!))^<})Cq3$pO+S^qv1#|? z+wPZNxno<0V_V+XmYXVd(i3iO_USz@H~W0V&CMO==DggTo4VFXXWj8jPe;A+OV6cW z6z|GS@5){8%9gwRz}@)4>fPy4_SLiCy;ZxnH%<2r}h>`Bpct(1B zoBhbuaHsP!9S>T229;-mulG#Y-{dlw(ZsR7Vwu(8Tq(U3{t@(#PtZJ{F~7d^Ihehc zKMhXqRk8pq`=HwnkZy$Md1xqp%H<}5e8S%5Z<+YKHEy9U?IZKArK-6H}S!>5fT(ZLXZYT;b!G{H;0YwJ=S)B?t~~P z)8!2Uh&%v~5yTazEocx0ouf;Yie3AB350@(wLPAhoo{C68~gqIyamMiaqUg`RRQ3; zG@8|#Dw7qVYy%81j6niDm?)kCfdcLWth@zSB`{JO(2meP%`8vF+GF6fe#&h};Z_p% znV*L=&A6UWOyVqPkn(L#jmbBmYy$#3h=HeI*oU5~0HQ_Bp+spcJ$lrhVgS~5TRTvc z7f-!*_1};4M~3!)XE08mp|5%dHU%jbHy<@@2x_!|GQw|lyWQJpz-UB*9A(9Tu+1=| zEJHc*lVc9#rx+y?(w;_hGt7`5IkJHt5La^H9(^mWV5K)> zW!7fR$&p$2fo6{7!E2_AdX?(C&uEJ7P&g#R5r3K8+J8NWI8ZTZQpXrS4 zrL7wyd+7t-vwL6c-pKBiZOydyfe;5$97kL}HR8&@Ar=>GefU6L>_XGZnxk + + + {% block head %}{% endblock %} -