from datetime import datetime from django.conf import settings from django.db import models class TimeEntry(models.Model): user = models.ForeignKey( settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='time_entries', ) # Même group_id = même pointage logique réparti sur plusieurs jours. # NULL pour les pointages classiques sur une seule journée. group_id = models.UUIDField( null=True, blank=True, db_index=True, ) start_date = models.DateField() start_time = models.TimeField() # Chaque ligne représente une seule journée. # Pour une entrée classique : # start_date = end_date # # Pour un pointage multi-jours : # une ligne par jour, toutes liées par group_id. end_date = models.DateField() end_time = models.TimeField( null=True, blank=True, ) created_at = models.DateTimeField( auto_now_add=True, ) updated_at = models.DateTimeField( auto_now=True, ) class Meta: ordering = ['-start_date', '-start_time'] constraints = [ models.UniqueConstraint( fields=[ 'user', 'start_date', 'start_time', ], name='unique_time_entry_start', ), ] def __str__(self): if self.end_time: return ( f"{self.user.username} - " f"{self.start_date} " f"({self.start_time} → {self.end_time})" ) return ( f"{self.user.username} - " f"{self.start_date} " f"({self.start_time} → en cours)" ) @property def duration(self): """ Durée de CETTE ligne. Chaque TimeEntry représente une seule journée. Les différentes journées d'un même pointage sont reliées par group_id. """ if not self.start_time or not self.end_time: return None end_date = self.end_date or self.start_date start = datetime.combine( self.start_date, self.start_time, ) end = datetime.combine( end_date, self.end_time, ) seconds = ( end - start ).total_seconds() if seconds < 0: return None return seconds / 3600 @property def day_of_week(self): return self.start_date.strftime('%A') @property def is_complete(self): return bool( self.start_time and self.end_time ) @property def is_incomplete(self): return not self.is_complete