38156-vm/core/views.py
Flatlogic Bot bcf2bc58dd ver 4
2026-02-03 20:11:39 +00:00

244 lines
8.8 KiB
Python

import os
import platform
import json
from django.shortcuts import render, redirect, get_object_or_404
from django.utils import timezone
from django.contrib.auth.decorators import login_required
from django.db.models import Sum, Q
from django.core.mail import send_mail
from django.conf import settings
from django.contrib import messages
from .models import Worker, Project, Team, WorkLog, PayrollRecord
from .forms import WorkLogForm
from datetime import timedelta
def home(request):
"""Render the landing screen with dashboard stats."""
workers_count = Worker.objects.count()
projects_count = Project.objects.count()
teams_count = Team.objects.count()
recent_logs = WorkLog.objects.order_by('-date')[:5]
# Analytics
# 1. Outstanding Payments
outstanding_total = 0
active_workers = Worker.objects.filter(is_active=True)
for worker in active_workers:
# Find unpaid logs for this worker
unpaid_logs_count = worker.work_logs.exclude(paid_in__worker=worker).count()
outstanding_total += unpaid_logs_count * worker.day_rate
# 2. Project Costs (Active Projects)
# Calculate sum of day_rates for all workers in all logs for each project
project_costs = []
active_projects = Project.objects.filter(is_active=True)
# Simple iteration for calculation (safer than complex annotations given properties)
for project in active_projects:
cost = 0
logs = project.logs.all()
for log in logs:
# We need to sum the day_rate of all workers in this log
# Optimization: prefetch workers if slow, but for now just iterate
for worker in log.workers.all():
cost += worker.day_rate
if cost > 0:
project_costs.append({'name': project.name, 'cost': cost})
# 3. 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
context = {
"workers_count": workers_count,
"projects_count": projects_count,
"teams_count": teams_count,
"recent_logs": recent_logs,
"current_time": timezone.now(),
"outstanding_total": outstanding_total,
"project_costs": project_costs,
"recent_payments_total": recent_payments_total,
}
return render(request, "core/index.html", context)
def log_attendance(request):
if request.method == 'POST':
form = WorkLogForm(request.POST, user=request.user)
if form.is_valid():
work_log = form.save(commit=False)
if request.user.is_authenticated:
work_log.supervisor = request.user
work_log.save()
form.save_m2m()
return redirect('home')
else:
form = WorkLogForm(user=request.user if request.user.is_authenticated else None)
# Build team workers map for frontend JS
teams_qs = Team.objects.filter(is_active=True)
if request.user.is_authenticated and not request.user.is_superuser:
teams_qs = teams_qs.filter(supervisor=request.user)
team_workers_map = {}
for team in teams_qs:
# Get active workers for the team
active_workers = team.workers.filter(is_active=True).values_list('id', flat=True)
team_workers_map[team.id] = list(active_workers)
context = {
'form': form,
'team_workers_json': json.dumps(team_workers_map)
}
return render(request, 'core/log_attendance.html', context)
def work_log_list(request):
logs = WorkLog.objects.all().order_by('-date')
return render(request, 'core/work_log_list.html', {'logs': logs})
def manage_resources(request):
"""View to manage active status of resources."""
workers = Worker.objects.all().order_by('name')
projects = Project.objects.all().order_by('name')
teams = Team.objects.all().order_by('name')
context = {
'workers': workers,
'projects': projects,
'teams': teams,
}
return render(request, 'core/manage_resources.html', context)
def toggle_resource_status(request, model_type, pk):
"""Toggle the is_active status of a resource."""
if request.method == 'POST':
model_map = {
'worker': Worker,
'project': Project,
'team': Team,
}
model_class = model_map.get(model_type)
if model_class:
obj = get_object_or_404(model_class, pk=pk)
obj.is_active = not obj.is_active
obj.save()
return redirect('manage_resources')
def payroll_dashboard(request):
"""Dashboard for payroll management with filtering."""
status_filter = request.GET.get('status', 'pending') # pending, paid, all
# Common Analytics
outstanding_total = 0
active_workers = Worker.objects.filter(is_active=True).order_by('name')
workers_data = [] # For pending payments
for worker in active_workers:
unpaid_logs = worker.work_logs.exclude(paid_in__worker=worker)
count = unpaid_logs.count()
amount = count * worker.day_rate
if count > 0:
outstanding_total += amount
if status_filter in ['pending', 'all']:
workers_data.append({
'worker': worker,
'unpaid_count': count,
'unpaid_amount': amount,
'logs': unpaid_logs
})
# Paid History
paid_records = []
if status_filter in ['paid', 'all']:
paid_records = PayrollRecord.objects.select_related('worker').order_by('-date', '-id')
# Analytics: Project Costs (Active Projects)
project_costs = []
active_projects = Project.objects.filter(is_active=True)
for project in active_projects:
cost = 0
logs = project.logs.all()
for log in logs:
for worker in log.workers.all():
cost += worker.day_rate
if cost > 0:
project_costs.append({'name': project.name, 'cost': 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
context = {
'workers_data': workers_data,
'paid_records': paid_records,
'outstanding_total': outstanding_total,
'project_costs': project_costs,
'recent_payments_total': recent_payments_total,
'active_tab': status_filter,
}
return render(request, 'core/payroll_dashboard.html', context)
def process_payment(request, worker_id):
"""Process payment for a worker, mark logs as paid, and email receipt."""
worker = get_object_or_404(Worker, pk=worker_id)
if request.method == 'POST':
# Find unpaid logs
unpaid_logs = worker.work_logs.exclude(paid_in__worker=worker)
count = unpaid_logs.count()
if count > 0:
amount = count * worker.day_rate
# Create Payroll Record
payroll_record = PayrollRecord.objects.create(
worker=worker,
amount=amount,
date=timezone.now().date()
)
# Link logs
payroll_record.work_logs.set(unpaid_logs)
payroll_record.save()
# Email Notification
subject = f"Payslip for {worker.name} - {payroll_record.date}"
message = (
f"Payslip Generated\n\n"
f"Record ID: #{payroll_record.id}\n"
f"Worker: {worker.name}\n"
f"ID Number: {worker.id_no}\n"
f"Date: {payroll_record.date}\n"
f"Amount Paid: R {payroll_record.amount}\n\n"
f"This is an automated notification from Fox Fitt Payroll."
)
recipient_list = ['foxfitt-ed9wc+expense@to.sparkreceipt.com']
try:
send_mail(subject, message, settings.DEFAULT_FROM_EMAIL, recipient_list)
messages.success(request, f"Payment of R {payroll_record.amount} processed for {worker.name}. Email sent to accounting.")
except Exception as e:
messages.warning(request, f"Payment processed, but email delivery failed: {str(e)}")
return redirect('payroll_dashboard')
return redirect('payroll_dashboard')
def payslip_detail(request, pk):
"""Show details of a payslip (Payment Record)."""
record = get_object_or_404(PayrollRecord, pk=pk)
# Get the logs included in this payment
logs = record.work_logs.all().order_by('date')
context = {
'record': record,
'logs': logs,
}
return render(request, 'core/payslip.html', context)