- TrainingRecord model with completion_mode (online/offline/blended) and CompletionStatus state machine - TrainerSignoff model with decision, notes, trainer FK, and audit timestamp - SignoffEvidence model for uploaded proof files - services.py state transitions: - mark_in_progress: not_started → in_progress - mark_online_passed: online → completed; blended → pending_signoff - submit_trainer_signoff: offline/blended approved → completed; rejected → in_progress - InvalidTransitionError on illegal state moves - IsTrainer permission class based on training:signoff capability - API: record detail, start, mark-online-passed, trainer-signoff, pending-signoff list - 20 unit + integration tests covering all mode paths, invalid transitions, and access control - Blended completion requires both online pass AND trainer approval Co-Authored-By: Paperclip <noreply@paperclip.ing>
30 lines
863 B
Python
30 lines
863 B
Python
from django.contrib import admin
|
|
|
|
from .models import SignoffEvidence, TrainerSignoff, TrainingRecord
|
|
|
|
|
|
class TrainerSignoffInline(admin.TabularInline):
|
|
model = TrainerSignoff
|
|
extra = 0
|
|
readonly_fields = ("signed_off_at",)
|
|
|
|
|
|
class SignoffEvidenceInline(admin.TabularInline):
|
|
model = SignoffEvidence
|
|
extra = 0
|
|
readonly_fields = ("uploaded_at",)
|
|
|
|
|
|
@admin.register(TrainingRecord)
|
|
class TrainingRecordAdmin(admin.ModelAdmin):
|
|
list_display = ("id", "enrollment", "completion_mode", "status", "completed_at")
|
|
list_filter = ("completion_mode", "status")
|
|
inlines = [TrainerSignoffInline, SignoffEvidenceInline]
|
|
|
|
|
|
@admin.register(TrainerSignoff)
|
|
class TrainerSignoffAdmin(admin.ModelAdmin):
|
|
list_display = ("training_record", "trainer", "decision", "signed_off_at")
|
|
list_filter = ("decision",)
|
|
raw_id_fields = ("trainer",)
|