From 23f7726fb962cc2fdc0f1128b6b31384fbea5806 Mon Sep 17 00:00:00 2001 From: Flatlogic Bot Date: Fri, 13 Feb 2026 09:33:50 +0000 Subject: [PATCH] Autosave: 20260213-093348 --- core/__pycache__/views.cpython-311.pyc | Bin 56030 -> 57677 bytes core/templates/core/payroll_dashboard.html | 40 +++++++++++- core/templates/core/work_log_list.html | 67 ++++++++++++++++++++- core/views.py | 28 +++++++++ 4 files changed, 131 insertions(+), 4 deletions(-) diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index b622d85f7d8ecff9a50860239949e2590297a514..2aa9d2481c2ee85c907b1f79ec21d3a3741044ec 100644 GIT binary patch delta 5138 zcmZ`d4OCm_b?*~MNC+V?NazQUzz8D{0t5y_Y>1728fXA$%)`dKHcfUVU z-#DLP8sMaSd<uoZmP6Z% z#qdIDHk@tM*O+{(l2UM0>qP;7L^L0~N$&8!0Nz4$`Vur6eDUmGx+E90r!g71CTFV#l zYLCrf5iO#buUd<0;tJ6+s^d6mg@Ff;u0cI$0c(dw&B`jjk4)g>vu zeuNX!M2nV_EPMk>mg`%L|G&3Xf$LrPH?~6O;p-SR~ry-NWA{jl`b89gbWm+NdwNEosU- zrf*A4MzV|c@LJko$Xu6OES2DyP%4&S*n6avh?j{g5pTtiWeKM%F}QDC(jnr8MO>Fi zUMzx{dyOv9Ho%?7)jTh+h?MxXQ|YPEaYNd~(p%a7+k_r!Hf1OsVo|u2x&^v7X)?;C zRbqL#jke6SF4bD9!1X)C3VHpjFwMhKG2e<^Ud(IEaI)9jC^->tTdE7bAt^LA)9q3v z`B>6PWaBI5*DvwJO6=^)0p@2Ik$kF`$%^?6OVnQUi1PL&4pLQ1)VLuuo-7!?b0mXs zZ-ELkaqt`Asm%`E`lOsMo!$hwQr_w+Q5$MXaY%v_HTDH4YzWk(IbS>k%miYYUDNU+p#`%?Eo><6lp-l=MO?tqk zbNpAt!t*$no~PL)f4;^!?-<>D_fY=<`+>Jf|*tVG5bH*4ugd|zIFy6*JOsg`S2 z_f@O=!hxu@Y0lcTC>E+9r^;+wgGRS9DUeubS+;Uk)DHQWhv{pHfEa(+b0y)+N3d_MI`D-WM*I8jYGO=ggIh0t~qGj*r+0*wW#FhdYdQpOK?K68M|| zy~e^f34B3-L*K%agvqsJNP>O92yb=NC>VpRtsB_i!uGAVF&!M=I%$vT10iqUV2=>; z4)^r-4+mqqMF!s!>Vd*-FS7rFtJ^Bn6fK64LD4y5D@4De1$_abzlL_%I~*AB4i5!G z?!nNWVdo~e+Iij+v+%wi|3JXa_qz84cy#W*A3Gp46z~UyPsp3P$L|k>dP4oegOsrb z_J8$>4B;Z-uRz-NQf7hj?WNk4v?gjWXEoINER}e};bQ3+GAI~GG)4v+2)st%6oFC_ zl)$;|R=1wibNT{%dWZb|yjSSQwE3V=KpG|jiCl2qLCgiB+qW<~n7i&_pF^mtoK?e& zuK>Pr!VF_Z6};Z1*LI>2VXOYcgdl3^n6q>o z(a*~*_QF@NqoSsDbEb7ibdmU7Hx(6cTWwP<;Xu^dIA?8q+gvnd33o=#o;kB8Zq3%K z;v9mpEpdC~)15i`oWGz--A!O8f!$ypc$9q=!UIBK8ZpuddRtk+IqN|&bD`UV9xtJ5480uzq@X?T|tTt(uID39eS{~xn z6Br}10jhj9RWs2B=<-#uJ`jC&RU0wBfN%TOs~$j9rHH)e3#gO<#1BQf_f@B{&)|5- zrF;k(MMiv1pYp*7NC5w6-t@uOpt5ib)$^ zhnI-*aD#jad9^2Jh14LX;}}zj`FLN(3u=gm{Syx{RG4UhFD43_SEY_U-ZQ0_R)_Xs z+*=x$|Bxl0p@*BtcNtw2pqhXcqX;GfO$>%6 z%UC0vn6$B{;cq6lvB%)%WLd{Eq&I~irtiVCdZgdGR|xp}`$9p1wm>*Sa(X*r*`Wgg z?;e`xL2u7MK%S9}WupNr#5B&;oOhd-U0;BKt`w zfbzs*9%t0xe{duTi{CI z-cxT?SgP<4yJl{_YHps-wayz$=8d*^24`tm;LZVzBQ>6Z{Cs?6;dd`+NMXhV* ztZU-796B)(jBWX(G;;Y{hceXPz=WOYLMwDVyN|WN&z=pk%g}iCVKxFUo_)zSO-1@4 zfqx}%i0W~eu)~Du;duC{@;{OOdc=J0nT)#ahz^l5;hT9Ow4VFVMO^_ zRS*Atp@NM;_Hz@e#0dwdpWEpC6H##t^FDccCqm0f=t22|%FN%O*MGz#Echn;G-QhG z_`x5Q@;(SaF~?!|3-_^lc=?6Lwe;@DS1jQmbj=j7U%=o@t9G2YACm6>z|2g$@^?u8 zCi3abrF2CagI9lCk>84(U^;5p?e?+c%((gEa#jMRFYU@9vD|Hm`TlD-_R`MPb;P8z zKXEBg3nJm~6OfPX4TSxgl>R_qlE_vtzFeODDbg!2TJSpZ{s1NCoRQ$mEzGfp^!x~x z4W5{0IMCxy)a5f2RY$J=eN2Iuf*)SAY3ar%+(h8ag@czYuyC;xC2g-%C^>9VPh|L& z!z!%I$NyYW{sn2N60=3#898%#-pG^eb#iqN+C46wDoIMHoZ`_|NcdH_nf!Cw{p}irY368(fYIsc!bZ3I+75<&F`8oXR zjZ^9-M4e?qBaC0lgU4s{ZFHHAsfYY{iSqaL3$#fw^+-?1E2pT3pUl>+yF&IWXf<@7 z7giF$e^+qA8zh(|5G8PucvlId|9c7lLf{wRj+Ut?tdI@6qJ_>pT$%7oqTeJSZ}Wc; zc8$P1ff#|e;7qhACx^5NOi(eLX>@ag&%1IXA4i+gw7sNwgThot8h-gSTh~bp8uS89 zc!8$6KqrtuucSclg+R?LP)7;W9s+H=K-CsnZ$X4YsX0PR$rHJ#jgN^bfu@KR#qt(G#^wSzQJe}_ zW~Mo_yu}f%65}@KEs4l6MmcAGBl?zty-e}d`YILn&?Yl@8z(_+S+)}LU}Kz`%ve+9 z|C%&DP;xi=SR!GOE|1Wn^?**!<{8o)9xo;Ec&|00k@ddnz>)|j2%>FU>>w`gOvizw zmdbjr=h>otI@2=XLfoS1T3nh8aXdKJ%xpPO>)4cy?Zh{Q!_gSRrmi52Sw zNzL^-LDq|vsEIBGdJR{>%jw0dpA;~%!Rv@Pb9S&5*^|Ur9 zjZQ+%g87qJ)1j@8B1DChCK;mJ6jEVw!+ zGdI6OkPCP|rdlqWkuKzBq#H0zAuVDlX3O>IGHnf0fo)uh5x6z9|v8;jl)x6OH*m;$|EZS%0Y!7m;LO~_ zJhAv#L+lXyu${@}To;%MC+BAUngK?YtvqMAo)+}Mz{Y46?~Dk|i(;el0-nN?12qvn zwz`H0%~PUP^YRIcUGB}gZDXAvw?^e6n+eXl3>7(IBdtl`!H5G6SrD6e&IoqmqtuO+ zXNh2&6B%pyvdkAh4{F~r$f<**B31D5& zqQcL*1+&=G@Z5rWri1YXgK^Rt)Wd;$o@f7r#S5(o6fA^+;g1VXnzQkU_ZixIB#(1L zkF=((tEZ!_tJ5EFtPXT{*&1Q-q7kTC{0O|Z*vvBF)5XQvXDHiQf;{wLkqxo7P=c?wyG?X&^h+`sBvFv`_MYwsI(;6o zP4ZxEA{<=O!i@0QlI842n6=cRv7sq6?YBX{UJD18rsht-NfJvOOrK0|9wBG zQpXL+Y1(lCp}%E3F9<8r)6(Zr@>YWR1ouF2&2}~e?&y+=exU;XL+~_$pN?>0p+vbP z0d4y%Fw|Yp+~M(g{;*bRxD)jfqZriILz;!|PB9d}(Bl(3eI3$wD6?smxesYXkH0;< ze#%<4fTO!LLxZkQg&v zR!J?$+UycFCyA~xDTAO0e(A1c#Zci}Im1d0FOpR8r(GG|Nac}85}ixw3j!O->;(P9 zR={gMv$mS}boks?!R`ZnPoZ`)32wslo+hmu$!}S3Yfq0h&O_qh_q|nW_C1tstV~*m zf(s*62aR z8?b_x!|{O%V>!3Rc3H*8CfmWh-H?e|(HeWo;QD|u8|B;{E0-(b{FW+p1+Ron+uK3hV>ya%_>rC50b6$DmoHMjmE`mV~2{3rUt%xcp7T~ z%dW%B4{z;iX4S#;K?`F^Fm-oTRzEIQC{c6=JZ)`V0gtj{;kZo?{}@b%=XPhSwlnx? z@D5f0+DC3^l_TMVjNN(AYERTTrbiA&1vvKrv%=_}HkJw{LodR$Atx(>g1v`uCNAt< zrZ1yyVj;-Enk5HOxdJ%$l@`@W97UlNf508+^|!6+q(US&nNcH^hQPnCmC0~nU#WI4 z`R#+x_nEYtiL8bsD9}De-G) z=mfM+ox!2~e~Hr>O2!3a+}-TDV1#*3TnRq@+rnVoQ(NN`?nOtN zMyiAJ&#c2g8P{PyI}L{qZ)ROkI&#WH0$!Ak^NsgzAb4G@(CH%E5r{*HVjo(TUT zHSFDA9(?|YFWxl=g`Gf_cM@DB^Javl(i|cUBy2?rb1P@p4oi<)*?N$VZ_|d4FvOpj z?fRAcp#-<+RhoRb2&EiqyM7(U1NV_E7&?)vdmkkSsjBxGR%HlYIPr6wj;2WhDqsV= z@weYIJCvW=t$UKB=SbQM=T8~f>u~v0gKh&!uaWc$6rOI@eTMuP1=OpQfCHzGXjH%8 zs-LxH)uGO>LEFN@!v0$w7rbXJEDrXbU6xL2N$EkeDBdOb@$Bz#gDpB|GEI<@(H8`R zWK;(O=PcSUk^fE|Jb7*nD{Cj6h%lk+h7!Aa+`jN^e2uD%VAF+=id~1iQM1-d4%eV* zv+-RF*;M?@ISnTI~gjun>-qZku6TF0AOHs~)^a-lM3m4e9))T(+qiMcn z02i(70-U*6qq~Ww4xCH>V=DM~)D^V9m7GZPJnd2m6JW(93)=)+FRjW@cCIQEk5{ax zb8SF!z|Bhy*=LkQi^H-u=^T}G9_GKhpE=-@cOB*~Mc?Ve^XY5%NOZM?64tu|ZAx89 z;JoZ&FN6DXPU$P;I!f>=!4M7UHKL*;d>x*;TsluV{tORaD5(oC{kG14r`sP&^5cJj zySt~?7m&25l-?lUw+NK=eUqp$f{O%i6TAb-@8u_{$(mqwu=YKVTDOc;Hz>U&`1F-0 z*bMqvklX~yeMNVar0i@ui4tvmi8iZ5n@6HuA<@E1G_jHz`aZ~KdBHz@P^&VnP_voi o0^xPFpi19XFs@QWTnnFmxIE9RV$Ez^P(HT}!op>6|3}~a7n&a0F8}}l diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html index fedc41e..d5191d4 100644 --- a/core/templates/core/payroll_dashboard.html +++ b/core/templates/core/payroll_dashboard.html @@ -65,7 +65,7 @@ -
+
Monthly Payroll Totals
@@ -83,6 +83,18 @@
+ + +
+
+
+
Overtime History
+
+ +
+
+
+
@@ -343,9 +362,51 @@ .cal-day--selected { background-color: rgba(13, 110, 253, 0.08) !important; box-shadow: inset 0 0 0 2px var(--bs-primary); } -{% if view_mode == 'calendar' %} -{% endif %} {% endblock %} \ No newline at end of file diff --git a/core/views.py b/core/views.py index cb7ca5d..91158f9 100644 --- a/core/views.py +++ b/core/views.py @@ -359,6 +359,21 @@ def work_log_list(request): total_amount = 0 combined_records = [] + # Prepare Chart Data (Overtime) - Admin only + ot_chart_labels = [] + ot_chart_data = [] + + if user_is_admin: + from django.db.models.functions import TruncMonth + ot_stats = adjustments.filter(type='OVERTIME') \ + .annotate(month=TruncMonth('date')) \ + .values('month') \ + .annotate(total=Sum('amount')) \ + .order_by('month') + + ot_chart_labels = [s['month'].strftime('%b %Y') for s in ot_stats] + ot_chart_data = [float(s['total']) for s in ot_stats] + # Process Logs for log in logs: record = { @@ -431,6 +446,8 @@ def work_log_list(request): 'selected_payment_status': payment_status, 'target_worker': target_worker, 'view_mode': view_mode, + 'ot_chart_labels': json.dumps(ot_chart_labels), + 'ot_chart_data': json.dumps(ot_chart_data), } if view_mode == 'calendar': @@ -777,6 +794,8 @@ def payroll_dashboard(request): all_project_names = list(Project.objects.values_list('name', flat=True).order_by('name')) project_monthly = {name: [] for name in all_project_names} + ot_history_totals = [] # Overtime history + for year, month in chart_months: chart_labels.append(f"{calendar.month_abbr[month]} {year}") @@ -789,6 +808,14 @@ def payroll_dashboard(request): date__gte=month_start, date__lte=month_end ).aggregate(total=Sum('amount'))['total'] or 0 chart_totals.append(float(month_paid)) + + # Overtime paid this month + ot_month_total = PayrollAdjustment.objects.filter( + type='OVERTIME', + date__gte=month_start, + date__lte=month_end + ).aggregate(total=Sum('amount'))['total'] or 0 + ot_history_totals.append(float(ot_month_total)) # Per-project labour cost this month (from work logs × day rates) month_logs = WorkLog.objects.filter( @@ -826,6 +853,7 @@ def payroll_dashboard(request): 'chart_totals_json': json.dumps(chart_totals), 'project_chart_json': json.dumps(project_chart_data), 'overtime_data_json': json.dumps(all_ot_data), + 'ot_history_json': json.dumps(ot_history_totals), } return render(request, 'core/payroll_dashboard.html', context)