""" HR app admin configuration. """ from django.contrib import admin from django.utils.html import format_html from django.urls import reverse from django.utils.safestring import mark_safe from decimal import Decimal from datetime import date from .models import ( Employee, Department, Schedule, ScheduleAssignment, TimeEntry, PerformanceReview, TrainingRecord ) class ScheduleInline(admin.TabularInline): """ Inline admin for employee schedules. """ model = Schedule extra = 0 fields = [ 'name', 'schedule_type', 'effective_date', 'end_date', 'is_active' ] readonly_fields = ['schedule_id'] class TimeEntryInline(admin.TabularInline): """ Inline admin for employee time entries. """ model = TimeEntry extra = 0 fields = [ 'work_date', 'clock_in_time', 'clock_out_time', 'total_hours', 'entry_type', 'status' ] readonly_fields = ['entry_id', 'total_hours'] class PerformanceReviewInline(admin.TabularInline): """ Inline admin for employee performance reviews. """ model = PerformanceReview extra = 0 fields = [ 'review_date', 'review_type', 'overall_rating', 'status' ] readonly_fields = ['review_id'] class TrainingRecordInline(admin.TabularInline): """ Inline admin for employee training records. """ model = TrainingRecord extra = 0 fields = [ 'training_name', 'training_date', 'completion_date', 'status', 'passed' ] readonly_fields = ['record_id'] @admin.register(Employee) class EmployeeAdmin(admin.ModelAdmin): """ Admin interface for employees. """ list_display = [ 'employee_number', 'get_full_name', 'job_title', 'department', 'employment_type', 'employment_status', 'hire_date', 'years_of_service_display', 'license_status_display' ] list_filter = [ 'tenant', 'department', 'employment_type', 'employment_status', 'gender', 'hire_date' ] search_fields = [ 'employee_number', 'first_name', 'last_name', 'email', 'job_title' ] readonly_fields = [ 'employee_id', 'age', 'years_of_service', 'is_license_expired', 'full_address', 'created_at', 'updated_at' ] fieldsets = [ ('Employee Information', { 'fields': [ 'employee_id', 'tenant', 'employee_number', 'user' ] }), ('Personal Information', { 'fields': [ 'first_name', 'last_name', 'middle_name', 'preferred_name' ] }), ('Contact Information', { 'fields': [ 'email', 'phone', 'mobile_phone' ] }), ('Address Information', { 'fields': [ 'address_line_1', 'address_line_2', 'city', 'state', 'postal_code', 'country' ], 'classes': ['collapse'] }), ('Personal Details', { 'fields': [ 'date_of_birth', 'gender', 'marital_status' ] }), ('Employment Information', { 'fields': [ 'department', 'job_title', 'employment_type', 'employment_status' ] }), ('Employment Dates', { 'fields': [ 'hire_date', 'termination_date' ] }), ('Supervisor Information', { 'fields': [ 'supervisor' ] }), ('Work Schedule Information', { 'fields': [ 'standard_hours_per_week', 'fte_percentage' ] }), ('Compensation Information', { 'fields': [ 'hourly_rate', 'annual_salary' ] }), ('Professional Information', { 'fields': [ 'license_number', 'license_expiry_date', 'certifications' ] }), ('Emergency Contact', { 'fields': [ 'emergency_contact_name', 'emergency_contact_relationship', 'emergency_contact_phone' ], 'classes': ['collapse'] }), ('Calculated Fields', { 'fields': [ 'age', 'years_of_service', 'is_license_expired', 'full_address' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [ScheduleInline, TimeEntryInline, PerformanceReviewInline, TrainingRecordInline] date_hierarchy = 'hire_date' def years_of_service_display(self, obj): """Display years of service.""" years = obj.years_of_service if years: return f"{years:.1f} years" return "0 years" years_of_service_display.short_description = 'Years of Service' def license_status_display(self, obj): """Display license status with color coding.""" if not obj.license_number: return format_html('No License') if obj.is_license_expired: return format_html('⚠️ Expired') if obj.license_expiry_date: days_to_expiry = (obj.license_expiry_date - date.today()).days if days_to_expiry <= 30: return format_html('⚠️ Expires Soon') return format_html('✓ Valid') license_status_display.short_description = 'License Status' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.select_related('department', 'supervisor') @admin.register(Department) class DepartmentAdmin(admin.ModelAdmin): """ Admin interface for departments. """ list_display = [ 'code', 'name', 'department_type', 'department_head', 'employee_count_display', 'total_fte_display', 'is_active' ] list_filter = [ 'tenant', 'department_type', 'is_active' ] search_fields = [ 'code', 'name', 'description' ] readonly_fields = [ 'department_id', 'employee_count', 'total_fte', 'created_at', 'updated_at' ] fieldsets = [ ('Department Information', { 'fields': [ 'department_id', 'tenant', 'code', 'name', 'description' ] }), ('Department Type', { 'fields': [ 'department_type' ] }), ('Hierarchy', { 'fields': [ 'parent_department' ] }), ('Management', { 'fields': [ 'department_head' ] }), ('Budget Information', { 'fields': [ 'annual_budget', 'cost_center' ] }), ('Location Information', { 'fields': [ 'location' ] }), ('Department Status', { 'fields': [ 'is_active' ] }), ('Summary Information', { 'fields': [ 'employee_count', 'total_fte' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] def employee_count_display(self, obj): """Display employee count.""" return obj.employee_count employee_count_display.short_description = 'Employees' def total_fte_display(self, obj): """Display total FTE.""" return f"{obj.total_fte:.1f}" total_fte_display.short_description = 'Total FTE' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(tenant=request.user.tenant) return qs.select_related('department_head') class ScheduleAssignmentInline(admin.TabularInline): """ Inline admin for schedule assignments. """ model = ScheduleAssignment extra = 0 fields = [ 'assignment_date', 'start_time', 'end_time', 'shift_type', 'status' ] readonly_fields = ['assignment_id', 'total_hours'] @admin.register(Schedule) class ScheduleAdmin(admin.ModelAdmin): """ Admin interface for schedules. """ list_display = [ 'employee_name', 'name', 'schedule_type', 'effective_date', 'end_date', 'is_current_display', 'is_active' ] list_filter = [ 'schedule_type', 'effective_date', 'is_active' ] search_fields = [ 'employee__first_name', 'employee__last_name', 'employee__employee_number', 'name' ] readonly_fields = [ 'schedule_id', 'tenant', 'is_current', 'created_at', 'updated_at' ] fieldsets = [ ('Schedule Information', { 'fields': [ 'schedule_id', 'employee', 'name', 'description' ] }), ('Schedule Type', { 'fields': [ 'schedule_type' ] }), ('Schedule Dates', { 'fields': [ 'effective_date', 'end_date' ] }), ('Schedule Pattern', { 'fields': [ 'schedule_pattern' ] }), ('Schedule Status', { 'fields': [ 'is_active' ] }), ('Approval Information', { 'fields': [ 'approved_by', 'approval_date' ] }), ('Status Information', { 'fields': [ 'is_current' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] inlines = [ScheduleAssignmentInline] date_hierarchy = 'effective_date' def employee_name(self, obj): """Display employee name.""" return obj.employee.get_full_name() employee_name.short_description = 'Employee' def is_current_display(self, obj): """Display current status with icon.""" if obj.is_current: return format_html('✓ Current') return format_html('Not Current') is_current_display.short_description = 'Current' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(employee__tenant=request.user.tenant) return qs.select_related('employee', 'approved_by') @admin.register(ScheduleAssignment) class ScheduleAssignmentAdmin(admin.ModelAdmin): """ Admin interface for schedule assignments. """ list_display = [ 'employee_name', 'assignment_date', 'start_time', 'end_time', 'shift_type', 'total_hours_display', 'status' ] list_filter = [ 'shift_type', 'status', 'assignment_date' ] search_fields = [ 'schedule__employee__first_name', 'schedule__employee__last_name', 'schedule__employee__employee_number' ] readonly_fields = [ 'assignment_id', 'tenant', 'employee', 'total_hours', 'created_at', 'updated_at' ] fieldsets = [ ('Assignment Information', { 'fields': [ 'assignment_id', 'schedule' ] }), ('Date and Time', { 'fields': [ 'assignment_date', 'start_time', 'end_time' ] }), ('Shift Information', { 'fields': [ 'shift_type' ] }), ('Location Information', { 'fields': [ 'department', 'location' ] }), ('Assignment Status', { 'fields': [ 'status' ] }), ('Break Information', { 'fields': [ 'break_minutes', 'lunch_minutes' ] }), ('Calculated Information', { 'fields': [ 'total_hours' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'tenant', 'employee' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at' ], 'classes': ['collapse'] }) ] date_hierarchy = 'assignment_date' def employee_name(self, obj): """Display employee name.""" return obj.employee.get_full_name() employee_name.short_description = 'Employee' def total_hours_display(self, obj): """Display total hours.""" return f"{obj.total_hours:.2f}" total_hours_display.short_description = 'Hours' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(schedule__employee__tenant=request.user.tenant) return qs.select_related('schedule__employee', 'department') @admin.register(TimeEntry) class TimeEntryAdmin(admin.ModelAdmin): """ Admin interface for time entries. """ list_display = [ 'employee_name', 'work_date', 'clock_in_time', 'clock_out_time', 'total_hours', 'entry_type', 'status', 'approval_status_display' ] list_filter = [ 'entry_type', 'status', 'work_date' ] search_fields = [ 'employee__first_name', 'employee__last_name', 'employee__employee_number' ] readonly_fields = [ 'entry_id', 'tenant', 'regular_hours', 'overtime_hours', 'total_hours', 'is_approved', 'created_at', 'updated_at' ] fieldsets = [ ('Time Entry Information', { 'fields': [ 'entry_id', 'employee' ] }), ('Date and Time', { 'fields': [ 'work_date', 'clock_in_time', 'clock_out_time' ] }), ('Break Times', { 'fields': [ 'break_start_time', 'break_end_time', 'lunch_start_time', 'lunch_end_time' ] }), ('Hours Information', { 'fields': [ 'regular_hours', 'overtime_hours', 'total_hours' ] }), ('Entry Type', { 'fields': [ 'entry_type' ] }), ('Department and Location', { 'fields': [ 'department', 'location' ] }), ('Approval Information', { 'fields': [ 'approved_by', 'approval_date' ] }), ('Entry Status', { 'fields': [ 'status' ] }), ('Status Information', { 'fields': [ 'is_approved' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at' ], 'classes': ['collapse'] }) ] date_hierarchy = 'work_date' def employee_name(self, obj): """Display employee name.""" return obj.employee.get_full_name() employee_name.short_description = 'Employee' def approval_status_display(self, obj): """Display approval status with icon.""" if obj.is_approved: return format_html('✓ Approved') elif obj.status == 'REJECTED': return format_html('✗ Rejected') elif obj.status == 'SUBMITTED': return format_html('⏳ Pending') return format_html('Draft') approval_status_display.short_description = 'Approval' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(employee__tenant=request.user.tenant) return qs.select_related('employee', 'department', 'approved_by') @admin.register(PerformanceReview) class PerformanceReviewAdmin(admin.ModelAdmin): """ Admin interface for performance reviews. """ list_display = [ 'employee_name', 'review_date', 'review_type', 'overall_rating', 'status', 'is_overdue_display' ] list_filter = [ 'review_type', 'status', 'overall_rating', 'review_date' ] search_fields = [ 'employee__first_name', 'employee__last_name', 'employee__employee_number' ] readonly_fields = [ 'review_id', 'tenant', 'is_overdue', 'created_at', 'updated_at' ] fieldsets = [ ('Review Information', { 'fields': [ 'review_id', 'employee' ] }), ('Review Period', { 'fields': [ 'review_period_start', 'review_period_end', 'review_date' ] }), ('Review Type', { 'fields': [ 'review_type' ] }), ('Reviewer Information', { 'fields': [ 'reviewer' ] }), ('Overall Rating', { 'fields': [ 'overall_rating' ] }), ('Competency Ratings', { 'fields': [ 'competency_ratings' ] }), ('Goals and Objectives', { 'fields': [ 'goals_achieved', 'goals_not_achieved', 'future_goals' ] }), ('Strengths and Areas for Improvement', { 'fields': [ 'strengths', 'areas_for_improvement' ] }), ('Development Plan', { 'fields': [ 'development_plan', 'training_recommendations' ] }), ('Employee Comments', { 'fields': [ 'employee_comments', 'employee_signature_date' ] }), ('Review Status', { 'fields': [ 'status' ] }), ('Status Information', { 'fields': [ 'is_overdue' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at' ], 'classes': ['collapse'] }) ] date_hierarchy = 'review_date' def employee_name(self, obj): """Display employee name.""" return obj.employee.get_full_name() employee_name.short_description = 'Employee' def is_overdue_display(self, obj): """Display overdue status with icon.""" if obj.is_overdue: return format_html('⚠️ Overdue') return format_html('✓ On Time') is_overdue_display.short_description = 'Status' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(employee__tenant=request.user.tenant) return qs.select_related('employee', 'reviewer') @admin.register(TrainingRecord) class TrainingRecordAdmin(admin.ModelAdmin): """ Admin interface for training records. """ list_display = [ 'employee_name', 'training_name', 'training_type', 'training_date', 'completion_date', 'status', 'passed', 'expiry_status_display' ] list_filter = [ 'training_type', 'status', 'passed', 'training_date' ] search_fields = [ 'employee__first_name', 'employee__last_name', 'employee__employee_number', 'training_name' ] readonly_fields = [ 'record_id', 'tenant', 'is_expired', 'days_to_expiry', 'is_due_for_renewal', 'created_at', 'updated_at' ] fieldsets = [ ('Training Information', { 'fields': [ 'record_id', 'employee', 'training_name', 'training_description' ] }), ('Training Type', { 'fields': [ 'training_type' ] }), ('Training Provider', { 'fields': [ 'training_provider', 'instructor' ] }), ('Training Dates', { 'fields': [ 'training_date', 'completion_date', 'expiry_date' ] }), ('Training Details', { 'fields': [ 'duration_hours', 'credits_earned' ] }), ('Training Status', { 'fields': [ 'status' ] }), ('Results', { 'fields': [ 'score', 'passed' ] }), ('Certification Information', { 'fields': [ 'certificate_number', 'certification_body' ] }), ('Cost Information', { 'fields': [ 'training_cost' ] }), ('Expiry Information', { 'fields': [ 'is_expired', 'days_to_expiry', 'is_due_for_renewal' ], 'classes': ['collapse'] }), ('Notes', { 'fields': [ 'notes' ], 'classes': ['collapse'] }), ('Related Information', { 'fields': [ 'tenant' ], 'classes': ['collapse'] }), ('Metadata', { 'fields': [ 'created_at', 'updated_at', 'created_by' ], 'classes': ['collapse'] }) ] date_hierarchy = 'training_date' def employee_name(self, obj): """Display employee name.""" return obj.employee.get_full_name() employee_name.short_description = 'Employee' def expiry_status_display(self, obj): """Display expiry status with color coding.""" if not obj.expiry_date: return format_html('No Expiry') if obj.is_expired: return format_html('⚠️ Expired') if obj.is_due_for_renewal: return format_html('⚠️ Due Soon') return format_html('✓ Valid') expiry_status_display.short_description = 'Expiry Status' def get_queryset(self, request): """Filter by user's tenant.""" qs = super().get_queryset(request) if hasattr(request.user, 'tenant'): qs = qs.filter(employee__tenant=request.user.tenant) return qs.select_related('employee', 'created_by') # Customize admin site admin.site.site_header = "Hospital Management System - HR" admin.site.site_title = "HR Admin" admin.site.index_title = "Human Resources Administration"