diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 2aa9d24..2289660 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ diff --git a/core/templates/core/index.html b/core/templates/core/index.html index a9b9a95..5f1d45c 100644 --- a/core/templates/core/index.html +++ b/core/templates/core/index.html @@ -30,8 +30,8 @@
{% if is_admin_user %} -
-
+
+

Outstanding Payments

@@ -45,8 +45,8 @@
-
-
+
+

Paid This Month

@@ -59,8 +59,8 @@
-
-
+
+

Active Loans

@@ -74,6 +74,36 @@ View loans →
+ +
+
+
+
+

Outstanding by Project

+
+ +
+
+
+ {% if outstanding_project_costs %} +
    + {% for p in outstanding_project_costs %} +
  • + {{ p.name }} + R {{ p.cost|intcomma }} +
  • + {% endfor %} +
+ {% else %} +

No outstanding costs.

+ {% endif %} +
+
+ +
+
{% else %}
@@ -395,4 +425,4 @@ document.addEventListener('DOMContentLoaded', function() { }); {% endif %} -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/core/templates/core/payroll_dashboard.html b/core/templates/core/payroll_dashboard.html index d5191d4..961a44c 100644 --- a/core/templates/core/payroll_dashboard.html +++ b/core/templates/core/payroll_dashboard.html @@ -20,8 +20,8 @@
- -
+ +
Outstanding Payments
@@ -32,7 +32,7 @@
-
+
Recent Payments (2 Months)
@@ -42,10 +42,31 @@
- -
+ +
+
+
Outstanding by Project
+
+ {% if outstanding_project_costs %} +
    + {% for p in outstanding_project_costs %} +
  • + {{ p.name }} + R {{ p.cost|intcomma }} +
  • + {% endfor %} +
+ {% else %} +

No outstanding project costs.

+ {% endif %} +
+
+
+ + +
-
Project Costs (Active)
+
Project Costs (History)
{% if project_costs %}
    diff --git a/core/views.py b/core/views.py index 91158f9..e6d4f07 100644 --- a/core/views.py +++ b/core/views.py @@ -104,6 +104,32 @@ def home(request): all_projects = Project.objects.all().order_by('name') if user_is_admin else [] all_teams = Team.objects.all().prefetch_related('workers').order_by('name') if user_is_admin else [] + # Outstanding Project Costs (Admin only) - Added for Dashboard visibility + outstanding_project_costs = [] + if user_is_admin: + for project in all_projects: + outstanding_cost = 0 + + # Unpaid WorkLogs + unpaid_logs = project.logs.filter(paid_in__isnull=True).prefetch_related('workers') + for log in unpaid_logs: + for worker in log.workers.all(): + outstanding_cost += worker.day_rate + + # Unpaid Adjustments linked to this project + project_adjustments = PayrollAdjustment.objects.filter( + work_log__project=project, + payroll_record__isnull=True + ) + for adj in project_adjustments: + if adj.type in ['BONUS', 'OVERTIME', 'LOAN']: + outstanding_cost += adj.amount + elif adj.type in ['DEDUCTION', 'LOAN_REPAYMENT']: + outstanding_cost -= adj.amount + + if outstanding_cost > 0: + outstanding_project_costs.append({'name': project.name, 'cost': outstanding_cost}) + context = { "is_admin_user": user_is_admin, "workers_count": workers_count, @@ -116,6 +142,7 @@ def home(request): "recent_payments_total": recent_payments_total, "active_loans_count": active_loans_count, "active_loans_total": active_loans_total, + "outstanding_project_costs": outstanding_project_costs, # This week "week_worker_days": week_worker_days, "week_projects": week_projects, @@ -668,7 +695,7 @@ def toggle_resource_status(request, model_type, pk): return JsonResponse({ 'success': True, 'is_active': obj.is_active, - 'message': f"{obj.name} is now {'Active' if obj.is_active else 'Inactive'}." + 'message': f"{obj.name} is now {'Active' if obj.is_active else 'Inactive'}." }) return redirect('home') @@ -750,9 +777,11 @@ def payroll_dashboard(request): # Analytics: Project Costs (Active Projects) project_costs = [] + outstanding_project_costs = [] active_projects = Project.objects.filter(is_active=True) for project in active_projects: + # 1. Total Historical Cost cost = 0 logs = project.logs.all() for log in logs: @@ -760,7 +789,30 @@ def payroll_dashboard(request): cost += worker.day_rate if cost > 0: project_costs.append({'name': project.name, 'cost': cost}) - + + # 2. Outstanding Cost (Unpaid) + outstanding_cost = 0 + + # Unpaid WorkLogs + unpaid_logs = project.logs.filter(paid_in__isnull=True).prefetch_related('workers') + for log in unpaid_logs: + for worker in log.workers.all(): + outstanding_cost += worker.day_rate + + # Unpaid Adjustments linked to this project + project_adjustments = PayrollAdjustment.objects.filter( + work_log__project=project, + payroll_record__isnull=True + ) + for adj in project_adjustments: + if adj.type in ['BONUS', 'OVERTIME', 'LOAN']: + outstanding_cost += adj.amount + elif adj.type in ['DEDUCTION', 'LOAN_REPAYMENT']: + outstanding_cost -= adj.amount + + if outstanding_cost > 0: + outstanding_project_costs.append({'name': project.name, 'cost': outstanding_cost}) + # Analytics: Previous 2 months payments two_months_ago = timezone.now().date() - timedelta(days=60) recent_payments_total = PayrollRecord.objects.filter(date__gte=two_months_ago).aggregate(total=Sum('amount'))['total'] or 0 @@ -790,7 +842,7 @@ def payroll_dashboard(request): chart_labels = [] # e.g. ["Sep 2025", "Oct 2025", ...] chart_totals = [] # total payroll paid per month - # For per-project chart: {project_name: [month0_cost, month1_cost, ...]} + # For per-project chart: {project_name: [month0_cost, month1_cost, ...]}} all_project_names = list(Project.objects.values_list('name', flat=True).order_by('name')) project_monthly = {name: [] for name in all_project_names} @@ -843,6 +895,7 @@ def payroll_dashboard(request): 'paid_records': paid_records, 'outstanding_total': outstanding_total, 'project_costs': project_costs, + 'outstanding_project_costs': outstanding_project_costs, 'recent_payments_total': recent_payments_total, 'active_tab': status_filter, 'all_workers': all_workers,