- 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>
26 lines
876 B
Python
26 lines
876 B
Python
from rest_framework import serializers
|
|
|
|
from .models import SignoffDecision, TrainerSignoff, TrainingRecord
|
|
|
|
|
|
class TrainingRecordSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = TrainingRecord
|
|
fields = [
|
|
"id", "enrollment", "completion_mode", "status",
|
|
"online_passed_at", "completed_at", "created_at", "updated_at",
|
|
]
|
|
read_only_fields = fields
|
|
|
|
|
|
class TrainerSignoffSerializer(serializers.ModelSerializer):
|
|
class Meta:
|
|
model = TrainerSignoff
|
|
fields = ["id", "training_record", "trainer", "decision", "notes", "signed_off_at"]
|
|
read_only_fields = ["id", "trainer", "signed_off_at"]
|
|
|
|
|
|
class SignoffRequestSerializer(serializers.Serializer):
|
|
decision = serializers.ChoiceField(choices=SignoffDecision.choices)
|
|
notes = serializers.CharField(allow_blank=True, default="")
|