diff --git a/core/__pycache__/admin.cpython-311.pyc b/core/__pycache__/admin.cpython-311.pyc index cb46a21..fe8bccf 100644 Binary files a/core/__pycache__/admin.cpython-311.pyc and b/core/__pycache__/admin.cpython-311.pyc differ diff --git a/core/__pycache__/forms.cpython-311.pyc b/core/__pycache__/forms.cpython-311.pyc new file mode 100644 index 0000000..ac2da0c Binary files /dev/null and b/core/__pycache__/forms.cpython-311.pyc differ diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index 5c3c750..b221b6f 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/__pycache__/urls.cpython-311.pyc b/core/__pycache__/urls.cpython-311.pyc index 5a69659..d422696 100644 Binary files a/core/__pycache__/urls.cpython-311.pyc and b/core/__pycache__/urls.cpython-311.pyc differ diff --git a/core/__pycache__/views.cpython-311.pyc b/core/__pycache__/views.cpython-311.pyc index 26ee8f4..fca06b7 100644 Binary files a/core/__pycache__/views.cpython-311.pyc and b/core/__pycache__/views.cpython-311.pyc differ 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 0000000..c1364c3 Binary files /dev/null and b/core/migrations/__pycache__/0002_project_supervisors_team_supervisor_and_more.cpython-311.pyc differ diff --git a/core/migrations/__pycache__/0003_project_is_active_team_is_active_worker_is_active.cpython-311.pyc b/core/migrations/__pycache__/0003_project_is_active_team_is_active_worker_is_active.cpython-311.pyc new file mode 100644 index 0000000..a4f5b7a Binary files /dev/null and b/core/migrations/__pycache__/0003_project_is_active_team_is_active_worker_is_active.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index 524f06e..6ddeb3f 100644 --- a/core/models.py +++ b/core/models.py @@ -1,11 +1,22 @@ from django.db import models from django.core.validators import MinValueValidator from decimal import Decimal +from django.contrib.auth.models import User + +class UserProfile(models.Model): + user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile') + pin = models.CharField(max_length=4, help_text="4-digit PIN for login") + is_admin = models.BooleanField(default=False) + + def __str__(self): + return f"{self.user.username}'s profile" class Project(models.Model): name = models.CharField(max_length=200) description = models.TextField(blank=True) + supervisors = models.ManyToManyField(User, related_name='assigned_projects', blank=True) created_at = models.DateTimeField(auto_now_add=True) + is_active = models.BooleanField(default=True) def __str__(self): return self.name @@ -16,6 +27,7 @@ class Worker(models.Model): phone_no = models.CharField(max_length=20, verbose_name="Phone Number") monthly_salary = models.DecimalField(max_digits=10, decimal_places=2, validators=[MinValueValidator(Decimal('0.00'))]) created_at = models.DateTimeField(auto_now_add=True) + is_active = models.BooleanField(default=True) @property def day_rate(self): @@ -27,7 +39,9 @@ class Worker(models.Model): class Team(models.Model): name = models.CharField(max_length=200) workers = models.ManyToManyField(Worker, related_name='teams') + supervisor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True, related_name='managed_teams') created_at = models.DateTimeField(auto_now_add=True) + is_active = models.BooleanField(default=True) def __str__(self): return self.name @@ -36,6 +50,7 @@ class WorkLog(models.Model): date = models.DateField() project = models.ForeignKey(Project, on_delete=models.CASCADE, related_name='logs') workers = models.ManyToManyField(Worker, related_name='work_logs') + supervisor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) notes = models.TextField(blank=True) def __str__(self): diff --git a/core/templates/base.html b/core/templates/base.html index a19c620..3393d4f 100644 --- a/core/templates/base.html +++ b/core/templates/base.html @@ -10,28 +10,45 @@ {% load static %} + + + {% block head %}{% endblock %}
-