242 lines
6.4 KiB
Python
242 lines
6.4 KiB
Python
from datetime import time as datetime_time
|
||
from uuid import uuid4
|
||
|
||
from django.contrib.auth import get_user_model
|
||
from django.db import transaction
|
||
from rest_framework import serializers
|
||
from users.serializers import UserSerializer
|
||
from .models import TimeEntry
|
||
|
||
|
||
User = get_user_model()
|
||
|
||
|
||
class TimeEntrySerializer(serializers.ModelSerializer):
|
||
user = UserSerializer(read_only=True)
|
||
|
||
is_complete = serializers.ReadOnlyField()
|
||
is_incomplete = serializers.ReadOnlyField()
|
||
|
||
class Meta:
|
||
model = TimeEntry
|
||
|
||
fields = [
|
||
'id',
|
||
'user',
|
||
'group_id',
|
||
'date',
|
||
'start_time',
|
||
'end_time',
|
||
'created_at',
|
||
'updated_at',
|
||
'duration',
|
||
'day_of_week',
|
||
'is_complete',
|
||
'is_incomplete',
|
||
]
|
||
|
||
read_only_fields = [
|
||
'id',
|
||
'user',
|
||
'created_at',
|
||
'updated_at',
|
||
'duration',
|
||
'day_of_week',
|
||
'is_complete',
|
||
'is_incomplete',
|
||
]
|
||
|
||
def create(self, validated_data):
|
||
validated_data['user'] = self.context['request'].user
|
||
return super().create(validated_data)
|
||
|
||
def update(self, instance, validated_data):
|
||
validated_data.pop('user', None)
|
||
return super().update(instance, validated_data)
|
||
|
||
|
||
class TimeEntryCreateSerializer(serializers.ModelSerializer):
|
||
user_id = serializers.PrimaryKeyRelatedField(
|
||
source='user',
|
||
queryset=User.objects.all(),
|
||
write_only=True,
|
||
required=False,
|
||
)
|
||
|
||
end_date = serializers.DateField(
|
||
write_only=True,
|
||
required=False,
|
||
)
|
||
|
||
class Meta:
|
||
model = TimeEntry
|
||
|
||
fields = [
|
||
'user_id',
|
||
'date',
|
||
'end_date',
|
||
'start_time',
|
||
'end_time',
|
||
]
|
||
|
||
def validate(self, attrs):
|
||
start_time = attrs.get('start_time')
|
||
end_time = attrs.get('end_time')
|
||
date = attrs.get('date')
|
||
end_date = attrs.get('end_date') or date
|
||
|
||
if not start_time and not end_time:
|
||
raise serializers.ValidationError(
|
||
'Une heure d’entrée ou de sortie est requise.'
|
||
)
|
||
|
||
if end_date < date:
|
||
raise serializers.ValidationError(
|
||
'La date de sortie doit être postérieure ou égale à la date d’entrée.'
|
||
)
|
||
|
||
# Une entrée et une sortie le même jour doivent être cohérentes.
|
||
if (
|
||
date == end_date
|
||
and start_time
|
||
and end_time
|
||
and end_time <= start_time
|
||
):
|
||
raise serializers.ValidationError(
|
||
'L’heure de sortie doit être après l’entrée.'
|
||
)
|
||
|
||
# Pour un pointage sur plusieurs jours, les deux extrémités
|
||
# doivent être connues.
|
||
if (
|
||
date != end_date
|
||
and (
|
||
not start_time
|
||
or not end_time
|
||
)
|
||
):
|
||
raise serializers.ValidationError(
|
||
'Un pointage sur plusieurs jours nécessite une heure d’entrée et une heure de sortie.'
|
||
)
|
||
|
||
attrs['end_date'] = end_date
|
||
|
||
return attrs
|
||
|
||
def create(self, validated_data):
|
||
user = validated_data.pop('user', None)
|
||
end_date = validated_data.pop('end_date')
|
||
|
||
if user is None:
|
||
user = self.context['request'].user
|
||
|
||
start_date = validated_data['date']
|
||
start_time = validated_data.get('start_time')
|
||
end_time = validated_data.get('end_time')
|
||
|
||
# Pointage classique sur une seule journée.
|
||
if start_date == end_date:
|
||
return TimeEntry.objects.create(
|
||
user=user,
|
||
date=start_date,
|
||
start_time=start_time,
|
||
end_time=end_time,
|
||
)
|
||
|
||
# Pointage multi-jours :
|
||
#
|
||
# Exemple :
|
||
# 20/09 14:00 -> 22/09 10:00
|
||
#
|
||
# 20/09 14:00 -> 23:59:59
|
||
# 21/09 00:00 -> 23:59:59
|
||
# 22/09 00:00 -> 10:00
|
||
group_id = uuid4()
|
||
|
||
created_entries = []
|
||
|
||
current_date = start_date
|
||
|
||
with transaction.atomic():
|
||
while current_date <= end_date:
|
||
if current_date == start_date:
|
||
entry_start = start_time
|
||
entry_end = datetime_time(
|
||
23,
|
||
59,
|
||
59,
|
||
)
|
||
|
||
elif current_date == end_date:
|
||
entry_start = datetime_time.min
|
||
entry_end = end_time
|
||
|
||
else:
|
||
entry_start = datetime_time.min
|
||
entry_end = datetime_time(
|
||
23,
|
||
59,
|
||
59,
|
||
)
|
||
|
||
created_entries.append(
|
||
TimeEntry.objects.create(
|
||
user=user,
|
||
group_id=group_id,
|
||
date=current_date,
|
||
start_time=entry_start,
|
||
end_time=entry_end,
|
||
)
|
||
)
|
||
|
||
from datetime import timedelta
|
||
|
||
current_date += timedelta(days=1)
|
||
|
||
return created_entries[0]
|
||
|
||
|
||
class TimeEntryUpdateSerializer(serializers.ModelSerializer):
|
||
class Meta:
|
||
model = TimeEntry
|
||
|
||
fields = [
|
||
'start_time',
|
||
'end_time',
|
||
]
|
||
|
||
def validate(self, attrs):
|
||
start_time = attrs.get(
|
||
'start_time',
|
||
self.instance.start_time,
|
||
)
|
||
|
||
end_time = attrs.get(
|
||
'end_time',
|
||
self.instance.end_time,
|
||
)
|
||
|
||
if not start_time and not end_time:
|
||
raise serializers.ValidationError(
|
||
'Une heure d’entrée ou de sortie est requise.'
|
||
)
|
||
|
||
if (
|
||
start_time
|
||
and end_time
|
||
and end_time <= start_time
|
||
):
|
||
raise serializers.ValidationError(
|
||
'L’heure de sortie doit être après l’entrée.'
|
||
)
|
||
|
||
return attrs
|
||
|
||
|
||
class WeeklySummarySerializer(serializers.Serializer):
|
||
user = serializers.CharField()
|
||
username = serializers.CharField()
|
||
total_hours = serializers.FloatField()
|
||
entries = serializers.ListField(
|
||
child=serializers.DictField(),
|
||
) |