Ver 14.06 Show outstanding per proj
This commit is contained in:
parent
23f7726fb9
commit
a66f75fe32
Binary file not shown.
@ -30,8 +30,8 @@
|
||||
<div class="row g-4 mb-4">
|
||||
{% if is_admin_user %}
|
||||
<!-- Admin: Outstanding Payments -->
|
||||
<div class="col-md-4">
|
||||
<div class="card stat-card p-4 border-start border-4 border-warning">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card p-4 border-start border-4 border-warning h-100">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<p class="text-uppercase small fw-bold mb-1 opacity-75">Outstanding Payments</p>
|
||||
@ -45,8 +45,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Admin: Paid This Month -->
|
||||
<div class="col-md-4">
|
||||
<div class="card stat-card p-4 border-start border-4 border-success">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card p-4 border-start border-4 border-success h-100">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<p class="text-uppercase small fw-bold mb-1 opacity-75">Paid This Month</p>
|
||||
@ -59,8 +59,8 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- Admin: Active Loans -->
|
||||
<div class="col-md-4">
|
||||
<div class="card stat-card p-4 border-start border-4 border-danger">
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card p-4 border-start border-4 border-danger h-100">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<div>
|
||||
<p class="text-uppercase small fw-bold mb-1 opacity-75">Active Loans</p>
|
||||
@ -74,6 +74,36 @@
|
||||
<a href="{% url 'payroll_dashboard' %}?status=loans" class="small text-muted mt-1 d-block stretched-link">View loans →</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Admin: Outstanding Project Costs (New) -->
|
||||
<div class="col-xl-3 col-md-6">
|
||||
<div class="card stat-card border-start border-4 border-info h-100">
|
||||
<div class="card-body p-4 pb-0">
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<p class="text-uppercase small fw-bold mb-0 opacity-75">Outstanding by Project</p>
|
||||
<div class="p-2 bg-info bg-opacity-10 rounded-circle text-info">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M3 3v18h18"/><path d="M18.7 8l-5.1 5.2-2.8-2.7L7 14.3"/></svg>
|
||||
</div>
|
||||
</div>
|
||||
<div class="overflow-auto" style="max-height: 100px;">
|
||||
{% if outstanding_project_costs %}
|
||||
<ul class="list-unstyled mb-0 small">
|
||||
{% for p in outstanding_project_costs %}
|
||||
<li class="d-flex justify-content-between py-1 border-bottom border-light">
|
||||
<span class="text-truncate me-2" style="max-width: 120px;" title="{{ p.name }}">{{ p.name }}</span>
|
||||
<span class="fw-bold">R {{ p.cost|intcomma }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted small">No outstanding costs.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-transparent border-0 pt-0 pb-3">
|
||||
<a href="{% url 'payroll_dashboard' %}" class="small text-muted stretched-link">View payroll →</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<!-- Non-admin: Active Projects -->
|
||||
<div class="col-md-4">
|
||||
@ -395,4 +425,4 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
});
|
||||
</script>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
@ -20,8 +20,8 @@
|
||||
|
||||
<!-- Analytics Section -->
|
||||
<div class="row mb-5">
|
||||
<!-- Outstanding Payments -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<!-- Outstanding Payments (Global) -->
|
||||
<div class="col-xl-3 col-md-6 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100 bg-warning bg-opacity-10">
|
||||
<div class="card-body">
|
||||
<h6 class="text-uppercase text-muted fw-bold small">Outstanding Payments</h6>
|
||||
@ -32,7 +32,7 @@
|
||||
</div>
|
||||
|
||||
<!-- Recent Payments -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="col-xl-3 col-md-6 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100 bg-success bg-opacity-10">
|
||||
<div class="card-body">
|
||||
<h6 class="text-uppercase text-muted fw-bold small">Recent Payments (2 Months)</h6>
|
||||
@ -42,10 +42,31 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Costs -->
|
||||
<div class="col-md-4 mb-3">
|
||||
<!-- Outstanding Project Costs -->
|
||||
<div class="col-xl-3 col-md-6 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100 bg-danger bg-opacity-10">
|
||||
<div class="card-header bg-transparent border-0 fw-bold small text-uppercase text-muted pb-0">Outstanding by Project</div>
|
||||
<div class="card-body overflow-auto pt-2" style="max-height: 150px;">
|
||||
{% if outstanding_project_costs %}
|
||||
<ul class="list-group list-group-flush bg-transparent">
|
||||
{% for p in outstanding_project_costs %}
|
||||
<li class="list-group-item d-flex justify-content-between align-items-center px-0 py-2 bg-transparent border-bottom border-danger border-opacity-25">
|
||||
<span>{{ p.name }}</span>
|
||||
<span class="fw-bold text-dark">R {{ p.cost|intcomma }}</span>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% else %}
|
||||
<p class="text-muted small mb-0">No outstanding project costs.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Project Costs (Total History) -->
|
||||
<div class="col-xl-3 col-md-6 mb-3">
|
||||
<div class="card border-0 shadow-sm h-100">
|
||||
<div class="card-header bg-white fw-bold small text-uppercase text-muted">Project Costs (Active)</div>
|
||||
<div class="card-header bg-white fw-bold small text-uppercase text-muted">Project Costs (History)</div>
|
||||
<div class="card-body overflow-auto" style="max-height: 150px;">
|
||||
{% if project_costs %}
|
||||
<ul class="list-group list-group-flush">
|
||||
|
||||
@ -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,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user