Augmenté la taille des FileField
[auf_rh_dae.git] / project / rh / models.py
index 1f605bc..3c7a538 100644 (file)
@@ -10,6 +10,7 @@ from auf.django.emploi.models import \
 from auf.django.references import models as ref
 from django.contrib.auth.models import User
 from django.core.files.storage import FileSystemStorage
+from django.core.exceptions import MultipleObjectsReturned
 from django.db import models
 from django.db.models import Q
 from django.db.models.signals import post_save, pre_save
@@ -31,6 +32,8 @@ from project.rh.managers import (
     )
 
 
+TWOPLACES = Decimal('0.01')
+
 from project.rh.validators import validate_date_passee
 
 # import pour relocaliser le modèle selon la convention (models.py pour
@@ -124,14 +127,17 @@ class DevisableMixin(object):
                 .order_by('-annee')
         return taux[0].taux
 
-    def montant_euros(self):
+    def montant_euros_float(self):
         try:
             taux = self.taux_devise()
         except Exception, e:
             return e
         if not taux:
             return None
-        return int(round(float(self.montant) * float(taux), 2))
+        return float(self.montant) * float(taux)
+
+    def montant_euros(self):
+        return int(round(self.montant_euros_float(), 2))
 
 
 class Commentaire(models.Model):
@@ -422,7 +428,8 @@ class PostePiece_(models.Model):
     """
     nom = models.CharField(u"Nom", max_length=255)
     fichier = models.FileField(
-        u"Fichier", upload_to=poste_piece_dispatch, storage=storage_prive
+        u"Fichier", upload_to=poste_piece_dispatch, storage=storage_prive,
+        max_length=255
     )
 
     class Meta:
@@ -549,6 +556,18 @@ class Employe(models.Model):
     def __unicode__(self):
         return u'%s %s [%s]' % (self.nom.upper(), self.prenom, self.id)
 
+    def get_latest_dossier_ordered_by_date_fin_and_principal(self):
+        res = self.rh_dossiers.order_by(
+            '-principal', 'date_fin')
+
+        # Retourne en le premier du queryset si la date de fin est None
+        # Sinon, retourne le plus récent selon la date de fin.
+        first = res[0]
+        if first.date_fin == None:
+            return first
+        else:
+            return res.order_by('-principal', '-date_fin')[0]
+
     def civilite(self):
         civilite = u''
         if self.genre.upper() == u'M':
@@ -586,17 +605,50 @@ class Employe(models.Model):
         q = search.get_q_temporel(self.rh_dossiers)
         return self.rh_dossiers.filter(q)
 
-    def dossier_principal(self):
+    def dossier_principal_pour_annee(self):
+        return self.dossier_principal(pour_annee=True)
+
+    def dossier_principal(self, pour_annee=False):
         """
         Retourne le dossier principal (ou le plus ancien si il y en a
         plusieurs)
+
+        Si pour_annee == True, retourne le ou les dossiers principaux
+        pour l'annee en cours, sinon, le ou les dossiers principaux
+        pour la journee en cours.
+
+        TODO: (Refactoring possible): Utiliser meme logique dans
+        dae/templatetags/dae.py
         """
-        try:
-            dossier = self.rh_dossiers \
-                    .filter(principal=True).order_by('date_debut')[0]
-        except IndexError, Dossier.DoesNotExist:
-            dossier = None
-        return dossier
+        
+        today = date.today()
+        if pour_annee:
+            year = today.year
+            year_start = date(year, 1, 1)
+            year_end = date(year, 12, 31)
+            
+            try:
+                dossier = self.rh_dossiers.filter(
+                    (Q(date_debut__lte=year_end, date_fin__isnull=True) |
+                     Q(date_debut__isnull=True, date_fin__gte=year_start) |
+                     Q(date_debut__lte=year_end, date_fin__gte=year_start) |
+                     Q(date_debut__isnull=True, date_fin__isnull=True)) &
+                    Q(principal=True)).order_by('date_debut')[0]
+            except IndexError, Dossier.DoesNotExist:
+                dossier = None
+            return dossier
+        else:
+            try:
+                dossier = self.rh_dossiers.filter(
+                    (Q(date_debut__lte=today, date_fin__isnull=True) |
+                     Q(date_debut__isnull=True, date_fin__gte=today) |
+                     Q(date_debut__lte=today, date_fin__gte=today) |
+                     Q(date_debut__isnull=True, date_fin__isnull=True)) &
+                    Q(principal=True)).order_by('date_debut')[0]
+            except IndexError, Dossier.DoesNotExist:
+                dossier = None
+            return dossier
+                
 
     def postes_encours(self):
         postes_encours = set()
@@ -643,7 +695,8 @@ class EmployePiece(models.Model):
     )
     nom = models.CharField(max_length=255)
     fichier = models.FileField(
-        u"fichier", upload_to=employe_piece_dispatch, storage=storage_prive
+        u"fichier", upload_to=employe_piece_dispatch, storage=storage_prive,
+        max_length=255
     )
 
     class Meta:
@@ -812,12 +865,15 @@ class Dossier_(DateActiviteMixin, models.Model, DevisableMixin,):
 
     # Meta-data:
     est_cadre = models.BooleanField(
-        u"Est un câdre?",
+        u"Est un cadre?",
         default=False,
         )
 
     # Comptes
-    # TODO?
+    compte_compta = models.CharField(max_length=10, default='aucun',
+                                    verbose_name=u'Compte comptabilité',
+                                    choices=COMPTE_COMPTA_CHOICES)
+    compte_courriel = models.BooleanField()
 
     class Meta:
         abstract = True
@@ -1098,6 +1154,12 @@ class RHDossierClassementRecord(models.Model):
         blank=True,
         db_index=True
     )
+    commentaire = models.CharField(
+        max_length=2048,
+        blank=True,
+        null=True,
+        default='',
+        )
 
     def __unicode__(self):
         return self.classement.__unicode__()
@@ -1114,56 +1176,78 @@ class RHDossierClassementRecord(models.Model):
                           using,
                           **kw):
 
-        # Attempt to pull previous classement from history.
+        today = date.today()
+        previous_record = None
         previous_classement = None
         has_changed = False
 
-        if not created:
-            has_changed = (
-                instance.classement.id !=
-                instance.before_save.classement.id
-                )
-            try:
-                previous_record = cls.objects.get(
+        # Premièrement, pour les nouvelles instances:
+        if created:
+            if not instance.classement:
+                return
+            else:
+                cls.objects.create(
+                    date_debut=instance.date_debut,
+                    classement=instance.classement,
                     dossier=instance,
-                    classement=instance.before_save.classement,
-                    date_fin=None,
                     )
-            except cls.DoesNotExist:
-                previous_record = None
-            else:
-                previous_classement = previous_record.classement
+                return
 
+        # Deuxièmement, pour les instances existantes:
 
-        if created or (not created and not previous_classement):
-            cls.objects.create(
-                date_debut=instance.date_debut,
-                classement=instance.classement,
+        # Détermine si:
+        # 1. Est-ce que le classement a changé?
+        # 2. Est-ce qu'une historique de classement existe déjà
+        try:
+            previous_record = cls.objects.get(
                 dossier=instance,
+                classement=instance.before_save.classement,
+                date_fin=None,
                 )
-        elif has_changed and previous_classement:
-            change_date = datetime.date.today()
-            previous_record.date_fin = change_date
-            previous_record.save()
-            cls.objects.create(
-                date_debut=change_date,
-                classement=instance.classement,
+        except cls.DoesNotExist:
+            if instance.before_save.classement:
+                # Il était censé avoir une historique de classement
+                # donc on le créé.
+                previous_record = cls.objects.create(
+                    date_debut=instance.before_save.date_debut,
+                    classement=instance.before_save.classement,
+                    dossier=instance,
+                    )
+                previous_classement = instance.before_save.classement
+        except MultipleObjectsReturned:
+            qs = cls.objects.filter(
                 dossier=instance,
+                classement=instance.before_save.classement,
+                date_fin=None,
                 )
-            
-        # elif not created:
-        #     cmp_inst = self.before_save
+            latest = qs.latest('date_debut')
+            qs.exclude(id=latest.id).update(date_fin=today)
+            previous_record = latest
+            previous_classement = latest.classement
+        else:
+            previous_classement = previous_record.classement
 
-        #     # Classement has changed!
-        #     if (cmp_inst.classement.id !=
-        #         instance.classement.id):
+        has_changed = (
+            instance.classement !=
+            previous_classement
+            )
 
+        # Cas aucun changement:
+        if not has_changed:
+            return
+
+        else:
+            # Classement a changé
+            if previous_record:
+                previous_record.date_fin = today
+                previous_record.save()
                 
-                
-                
-            
-            
-        
+            if instance.classement:
+                cls.objects.create(
+                    date_debut=today,
+                    classement=instance.classement,
+                    dossier=instance,
+                    )
 
 
 class DossierPiece_(models.Model):
@@ -1173,7 +1257,8 @@ class DossierPiece_(models.Model):
     """
     nom = models.CharField(max_length=255)
     fichier = models.FileField(
-        upload_to=dossier_piece_dispatch, storage=storage_prive
+        upload_to=dossier_piece_dispatch, storage=storage_prive,
+        max_length=255
     )
 
     class Meta:
@@ -1276,6 +1361,64 @@ class Remuneration_(RemunerationMixin, DevisableMixin):
     """
     objects = RemunerationManager()
 
+    @staticmethod
+    def find_yearly_range(from_date, to_date, year):
+        today = date.today()
+        year = year or date.today().year
+        year_start = date(year, 1, 1)
+        year_end = date(year, 12, 31)
+
+        def constrain_to_year(*dates):
+            """
+            S'assure que les dates soient dans le range year_start a
+            year_end
+            """
+            return [min(max(year_start, d), year_end)
+                    for d in dates]
+
+        start_date = max(
+                from_date or year_start, year_start)
+        end_date = min(
+                to_date or year_end, year_end)
+        
+        start_date, end_date = constrain_to_year(start_date, end_date)
+
+        jours_annee = (year_end - year_start).days
+        jours_dates = (end_date - start_date).days
+        factor = Decimal(str(jours_dates)) / Decimal(str(jours_annee))
+        
+        return start_date, end_date, factor
+        
+
+    def montant_ajuste_euros(self, annee=None):
+        """
+        Le montant ajusté représente le montant annuel, ajusté sur la
+        période de temps travaillée, multipliée par le ratio de temps
+        travaillé (en rapport au temps plein).
+        """
+        date_debut, date_fin, factor = self.find_yearly_range(
+            self.date_debut,
+            self.date_fin,
+            annee,
+            )
+
+        montant_euros = Decimal(str(self.montant_euros_float()) or '0')
+        
+        if self.type.nature_remuneration != u'Accessoire':
+            dossier = getattr(self, 'dossier', None)
+            if not dossier:
+                """
+                Dans le cas d'un DossierComparaisonRemuneration, il
+                n'y a plus de reference au dossier.
+                """
+                regime_travail = REGIME_TRAVAIL_DEFAULT
+            else:
+                regime_travail = self.dossier.regime_travail
+            return (montant_euros * factor *
+                    regime_travail / 100)
+        else:
+            return montant_euros
+        
     def montant_mois(self):
         return round(self.montant / 12, 2)
 
@@ -1327,7 +1470,7 @@ class Contrat_(models.Model):
     )
     fichier = models.FileField(
         upload_to=contrat_dispatch, storage=storage_prive, null=True,
-        blank=True
+        blank=True, max_length=255
     )
 
     class Meta:
@@ -1420,11 +1563,11 @@ TYPE_PAIEMENT_CHOICES = (
 )
 
 NATURE_REMUNERATION_CHOICES = (
-    (u'Accessoire', u'Accessoire'),
+    (u'Traitement', u'Traitement'),
+    (u'Indemnité', u'Indemnités autres'),
     (u'Charges', u'Charges patronales'),
-    (u'Indemnité', u'Indemnité autre'),
+    (u'Accessoire', u'Accessoires'),
     (u'RAS', u'Rémunération autre source'),
-    (u'Traitement', u'Traitement'),
 )
 
 
@@ -1433,6 +1576,9 @@ class TypeRemuneration(Archivable):
     Catégorie de Remuneration.
     """
 
+    objects = models.Manager()
+    sans_archives = ArchivableManager()
+
     nom = models.CharField(max_length=255)
     type_paiement = models.CharField(
         u"type de paiement", max_length=30, choices=TYPE_PAIEMENT_CHOICES
@@ -1558,12 +1704,12 @@ TYPE_CLASSEMENT_CHOICES = (
 )
 
 
-class ClassementManager(ArchivableManager):
+class ClassementManager(models.Manager):
     """
     Ordonner les spcéfiquement les classements.
     """
     def get_query_set(self):
-        qs = super(self.__class__, self).get_query_set()
+        qs = super(ClassementManager, self).get_query_set()
         qs = qs.extra(select={
             'ponderation': 'FIND_IN_SET(type,"SO,HG,S,T,P,C,D")'
         })
@@ -1571,6 +1717,11 @@ class ClassementManager(ArchivableManager):
         return qs.all()
 
 
+class ClassementArchivableManager(ClassementManager,
+                                  ArchivableManager):
+    pass
+
+
 class Classement_(Archivable):
     """
     Éléments de classement de la
@@ -1582,6 +1733,7 @@ class Classement_(Archivable):
     salaire de base = coefficient * valeur du point de l'Implantation du Poste
     """
     objects = ClassementManager()
+    sans_archives = ClassementArchivableManager()
 
     # Identification
     type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
@@ -1776,16 +1928,16 @@ reversion.register(UserProfile, format='xml')
 
 
 TYPES_CHANGEMENT = (
-    ('NO', 'Nouveau personnel'),
-    ('MO', 'Mouvement de personnel'),
-    ('DE', 'Départ de personnel'),
+    ('NO', 'Arrivée'),
+    ('MO', 'Mobilité'),
+    ('DE', 'Départ'),
     )
 
 
 class ChangementPersonnelNotifications(models.Model):
     class Meta:
-        verbose_name = u"Destinataire pour notices de changement de personnel"
-        verbose_name_plural = u"Destinataires pour notices de changement de personnel"
+        verbose_name = u"Destinataire pour notices de mouvement de personnel"
+        verbose_name_plural = u"Destinataires pour notices de mouvement de personnel"
 
     type = models.CharField(
         max_length=2,
@@ -1816,8 +1968,8 @@ class ChangementPersonnel(models.Model):
     """
 
     class Meta:
-        verbose_name = u"Notification de changement du personnel"
-        verbose_name_plural = u"Notifications de changement du personnel"
+        verbose_name = u"Mouvement de personnel"
+        verbose_name_plural = u"Mouvements de personnel"
 
     def __unicode__(self):
         return '%s: %s' % (self.dossier.__unicode__(),
@@ -1857,6 +2009,7 @@ class ChangementPersonnel(models.Model):
             id=instance.id)
         dd = instance.date_debut
         df = instance.date_fin
+        today = date.today()
        
         # Here, verify differences between the instance, before and
         # after the save.
@@ -1882,8 +2035,7 @@ class ChangementPersonnel(models.Model):
                 Q(date_fin__gte=dd - NEW_EMPLOYE_THRESHOLD)
                 )
 
-            # 1. If existe un Dossier récent, et c'est une nouvelle
-            # instance de Dossier:
+            # 1. If existe un Dossier récent
             if exists_recent_file_qs.count() > 0:
                 cls.create_changement(
                     instance,
@@ -1896,7 +2048,16 @@ class ChangementPersonnel(models.Model):
                     instance,
                     'NO',
                     )
-                
+
+        elif not df and not created and cls.objects.filter(
+            valide=True,
+            date_creation__gte=today - NEW_EMPLOYE_THRESHOLD,
+            type='DE',
+            ).count() > 0:
+            cls.create_changement(
+                instance,
+                'MO',
+                )
 
         # Date de fin a été modifiée:
         if df_has_changed:
@@ -1927,7 +2088,12 @@ class ChangementPersonnel(models.Model):
         )
 
     valide = models.BooleanField(default=True)
-    communique = models.BooleanField(default=False)
+    date_creation = models.DateTimeField(
+        auto_now_add=True)
+    communique = models.BooleanField(
+        u'Communiqué',
+        default=False,
+        )
     date_communication = models.DateTimeField(
                 null=True,
                 blank=True,