diff --git a/core/__pycache__/models.cpython-311.pyc b/core/__pycache__/models.cpython-311.pyc index d899904..34dac78 100644 Binary files a/core/__pycache__/models.cpython-311.pyc and b/core/__pycache__/models.cpython-311.pyc differ diff --git a/core/management/commands/__pycache__/update_permission_names.cpython-311.pyc b/core/management/commands/__pycache__/update_permission_names.cpython-311.pyc new file mode 100644 index 0000000..6bdad8d Binary files /dev/null and b/core/management/commands/__pycache__/update_permission_names.cpython-311.pyc differ diff --git a/core/management/commands/update_permission_names.py b/core/management/commands/update_permission_names.py new file mode 100644 index 0000000..033d9f6 --- /dev/null +++ b/core/management/commands/update_permission_names.py @@ -0,0 +1,103 @@ +from django.core.management.base import BaseCommand +from django.contrib.auth.models import Permission +from django.contrib.contenttypes.models import ContentType + +class Command(BaseCommand): + help = 'Renames permissions to be more user-friendly' + + def handle(self, *args, **options): + # Dictionary mapping (app_label, model_name, codename) -> New Name + # Or just (codename) -> New Name if unique enough, but (app, model) is safer. + + # We can also map just the model's permissions generically if we want. + + renames = { + # Core App - Business Logic + ('core', 'project', 'add_project'): 'Manage: Create New Project', + ('core', 'project', 'change_project'): 'Manage: Edit Project Details', + ('core', 'project', 'delete_project'): 'Manage: Delete Project', + ('core', 'project', 'view_project'): 'Manage: View Projects (Dashboard Access)', + + ('core', 'worker', 'add_worker'): 'Manage: Add New Worker', + ('core', 'worker', 'change_worker'): 'Manage: Edit Worker Details', + ('core', 'worker', 'delete_worker'): 'Manage: Delete Worker', + ('core', 'worker', 'view_worker'): 'Manage: View Worker Profiles', + + ('core', 'team', 'add_team'): 'Manage: Create Team', + ('core', 'team', 'change_team'): 'Manage: Edit Team Structure', + ('core', 'team', 'delete_team'): 'Manage: Delete Team', + ('core', 'team', 'view_team'): 'Manage: View Teams', + + ('core', 'worklog', 'add_worklog'): 'Log Work: Add Attendance Entry', + ('core', 'worklog', 'change_worklog'): 'Log Work: Edit Attendance Entry', + ('core', 'worklog', 'delete_worklog'): 'Log Work: Delete Attendance Entry', + ('core', 'worklog', 'view_worklog'): 'Log Work: View Attendance History', + + ('core', 'payrollrecord', 'add_payrollrecord'): 'Payroll: Generate Payment Record', + ('core', 'payrollrecord', 'change_payrollrecord'): 'Payroll: Edit Payment Record', + ('core', 'payrollrecord', 'delete_payrollrecord'): 'Payroll: Delete Payment Record', + ('core', 'payrollrecord', 'view_payrollrecord'): 'Payroll: View Payment History', + + ('core', 'loan', 'add_loan'): 'Loans: Create New Loan', + ('core', 'loan', 'change_loan'): 'Loans: Edit Loan Details', + ('core', 'loan', 'delete_loan'): 'Loans: Delete Loan', + ('core', 'loan', 'view_loan'): 'Loans: View Loan Registry', + + ('core', 'expensereceipt', 'add_expensereceipt'): 'Receipts: Add Expense Receipt', + ('core', 'expensereceipt', 'change_expensereceipt'): 'Receipts: Edit Expense Receipt', + ('core', 'expensereceipt', 'delete_expensereceipt'): 'Receipts: Delete Expense Receipt', + ('core', 'expensereceipt', 'view_expensereceipt'): 'Receipts: View Expense History', + + # Auth App + ('auth', 'user', 'add_user'): 'Admin: Create User Accounts', + ('auth', 'user', 'change_user'): 'Admin: Edit User Accounts', + ('auth', 'user', 'delete_user'): 'Admin: Delete User Accounts', + ('auth', 'user', 'view_user'): 'Admin: View User Accounts', + + ('auth', 'group', 'add_group'): 'Admin: Create Permission Groups', + ('auth', 'group', 'change_group'): 'Admin: Edit Permission Groups', + ('auth', 'group', 'delete_group'): 'Admin: Delete Permission Groups', + ('auth', 'group', 'view_group'): 'Admin: View Permission Groups', + + # System / Technical (Marking as Technical to help user ignore them) + ('sessions', 'session', 'add_session'): 'System (Tech): Add Session', + ('sessions', 'session', 'change_session'): 'System (Tech): Manage Sessions', + ('sessions', 'session', 'delete_session'): 'System (Tech): Clear Sessions', + ('sessions', 'session', 'view_session'): 'System (Tech): View Sessions', + + ('admin', 'logentry', 'add_logentry'): 'System (Tech): Add Admin Log', + ('admin', 'logentry', 'change_logentry'): 'System (Tech): Edit Admin Log', + ('admin', 'logentry', 'delete_logentry'): 'System (Tech): Clear Admin Log', + ('admin', 'logentry', 'view_logentry'): 'System (Tech): View Admin Log', + + ('contenttypes', 'contenttype', 'add_contenttype'): 'System (Tech): Add Content Type', + ('contenttypes', 'contenttype', 'change_contenttype'): 'System (Tech): Edit Content Type', + ('contenttypes', 'contenttype', 'delete_contenttype'): 'System (Tech): Delete Content Type', + ('contenttypes', 'contenttype', 'view_contenttype'): 'System (Tech): View Content Types', + } + + count = 0 + for (app_label, model, codename), new_name in renames.items(): + try: + # We need to find the content type first to be safe, or filter by codename + content_type__app_label + # but direct filter is easiest if unique. + # However, codenames can be shared across models (though rare for standard CRUD). + # Safer to lookup ContentType. + + ct = ContentType.objects.filter(app_label=app_label, model=model).first() + if not ct: + self.stdout.write(self.style.WARNING(f"Model {app_label}.{model} not found, skipping.")) + continue + + perm = Permission.objects.filter(content_type=ct, codename=codename).first() + if perm: + perm.name = new_name + perm.save() + self.stdout.write(f"Renamed {codename} -> {new_name}") + count += 1 + else: + self.stdout.write(self.style.WARNING(f"Permission {codename} for {app_label}.{model} not found.")) + except Exception as e: + self.stdout.write(self.style.ERROR(f"Error renaming {codename}: {e}")) + + self.stdout.write(self.style.SUCCESS(f"Successfully updated {count} permission names.")) diff --git a/core/migrations/0010_alter_expenselineitem_options_and_more.py b/core/migrations/0010_alter_expenselineitem_options_and_more.py new file mode 100644 index 0000000..e03777e --- /dev/null +++ b/core/migrations/0010_alter_expenselineitem_options_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 5.2.7 on 2026-02-04 20:45 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0009_worker_date_of_employment_worker_id_photo_and_more'), + ] + + operations = [ + migrations.AlterModelOptions( + name='expenselineitem', + options={'verbose_name': 'Expense Line Item', 'verbose_name_plural': 'Expense Line Items'}, + ), + migrations.AlterModelOptions( + name='expensereceipt', + options={'verbose_name': 'Expense Receipt', 'verbose_name_plural': 'Expense Receipts'}, + ), + migrations.AlterModelOptions( + name='loan', + options={'verbose_name': 'Loan', 'verbose_name_plural': 'Loans'}, + ), + migrations.AlterModelOptions( + name='payrolladjustment', + options={'verbose_name': 'Payroll Adjustment', 'verbose_name_plural': 'Payroll Adjustments'}, + ), + migrations.AlterModelOptions( + name='payrollrecord', + options={'verbose_name': 'Payroll Record', 'verbose_name_plural': 'Payroll Records'}, + ), + migrations.AlterModelOptions( + name='project', + options={'verbose_name': 'Project', 'verbose_name_plural': 'Projects'}, + ), + migrations.AlterModelOptions( + name='team', + options={'verbose_name': 'Team', 'verbose_name_plural': 'Teams'}, + ), + migrations.AlterModelOptions( + name='userprofile', + options={'verbose_name': 'User Profile', 'verbose_name_plural': 'User Profiles'}, + ), + migrations.AlterModelOptions( + name='worker', + options={'verbose_name': 'Worker', 'verbose_name_plural': 'Workers'}, + ), + migrations.AlterModelOptions( + name='worklog', + options={'verbose_name': 'Work Log / Attendance', 'verbose_name_plural': 'Work Logs / Attendance'}, + ), + ] diff --git a/core/migrations/__pycache__/0010_alter_expenselineitem_options_and_more.cpython-311.pyc b/core/migrations/__pycache__/0010_alter_expenselineitem_options_and_more.cpython-311.pyc new file mode 100644 index 0000000..c0da053 Binary files /dev/null and b/core/migrations/__pycache__/0010_alter_expenselineitem_options_and_more.cpython-311.pyc differ diff --git a/core/models.py b/core/models.py index c43ef4b..43ea234 100644 --- a/core/models.py +++ b/core/models.py @@ -9,6 +9,10 @@ class UserProfile(models.Model): pin = models.CharField(max_length=4, help_text="4-digit PIN for login") is_admin = models.BooleanField(default=False) + class Meta: + verbose_name = "User Profile" + verbose_name_plural = "User Profiles" + def __str__(self): return f"{self.user.username}'s profile" @@ -19,6 +23,10 @@ class Project(models.Model): created_at = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=True) + class Meta: + verbose_name = "Project" + verbose_name_plural = "Projects" + def __str__(self): return self.name @@ -37,6 +45,10 @@ class Worker(models.Model): created_at = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=True) + class Meta: + verbose_name = "Worker" + verbose_name_plural = "Workers" + @property def day_rate(self): return self.monthly_salary / Decimal('20.0') @@ -56,6 +68,10 @@ class Team(models.Model): created_at = models.DateTimeField(auto_now_add=True) is_active = models.BooleanField(default=True) + class Meta: + verbose_name = "Team" + verbose_name_plural = "Teams" + def __str__(self): return self.name @@ -66,6 +82,10 @@ class WorkLog(models.Model): supervisor = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, blank=True) notes = models.TextField(blank=True) + class Meta: + verbose_name = "Work Log / Attendance" + verbose_name_plural = "Work Logs / Attendance" + def __str__(self): return f"{self.date} - {self.project.name}" @@ -76,6 +96,10 @@ class PayrollRecord(models.Model): work_logs = models.ManyToManyField(WorkLog, related_name='paid_in') created_at = models.DateTimeField(auto_now_add=True) + class Meta: + verbose_name = "Payroll Record" + verbose_name_plural = "Payroll Records" + def __str__(self): return f"Payment to {self.worker.name} on {self.date}" @@ -87,6 +111,10 @@ class Loan(models.Model): reason = models.TextField(blank=True) is_active = models.BooleanField(default=True) + class Meta: + verbose_name = "Loan" + verbose_name_plural = "Loans" + def save(self, *args, **kwargs): if not self.pk: # On creation self.balance = self.amount @@ -112,6 +140,10 @@ class PayrollAdjustment(models.Model): description = models.CharField(max_length=255) type = models.CharField(max_length=20, choices=ADJUSTMENT_TYPES, default='DEDUCTION') + class Meta: + verbose_name = "Payroll Adjustment" + verbose_name_plural = "Payroll Adjustments" + def __str__(self): return f"{self.get_type_display()} - {self.amount} for {self.worker.name}" @@ -142,6 +174,10 @@ class ExpenseReceipt(models.Model): created_at = models.DateTimeField(auto_now_add=True) + class Meta: + verbose_name = "Expense Receipt" + verbose_name_plural = "Expense Receipts" + def __str__(self): return f"Receipt from {self.vendor} - {self.date}" @@ -150,5 +186,9 @@ class ExpenseLineItem(models.Model): product = models.CharField(max_length=255, verbose_name="Product/Item") amount = models.DecimalField(max_digits=10, decimal_places=2) + class Meta: + verbose_name = "Expense Line Item" + verbose_name_plural = "Expense Line Items" + def __str__(self): return f"{self.product} - {self.amount}" \ No newline at end of file