[#2518] Scripts de migration du RH en PHP vers le nouveau système RH
[auf_rh_dae.git] / project / rh / models.py
index 817a7e6..aea60a2 100644 (file)
@@ -1,6 +1,7 @@
 # -=- encoding: utf-8 -=-
 
 from datetime import date
+from decimal import Decimal
 
 from django.core.files.storage import FileSystemStorage
 from django.db import models
@@ -13,12 +14,12 @@ from validators import validate_date_passee
 from dae.managers import SecurityManager
 
 # Constantes
-REGIME_TRAVAIL_DEFAULT = 100.00
-REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT = 35.00
+REGIME_TRAVAIL_DEFAULT = Decimal('100.00')
+REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT = Decimal('35.00')
 
 
 # Upload de fichiers
-storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT, 
+storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
                             base_url=settings.PRIVE_MEDIA_URL)
 
 def poste_piece_dispatch(instance, filename):
@@ -37,11 +38,11 @@ def employe_piece_dispatch(instance, filename):
 class Commentaire(AUFMetadata):
     texte = models.TextField()
     owner = models.ForeignKey('auth.User', db_column='owner', related_name='+')
-    
+
     class Meta:
         abstract = True
         ordering = ['-date_creation']
-        
+
     def __unicode__(self):
         return u'%s' % (self.texte)
 
@@ -67,7 +68,7 @@ class PosteManager(SecurityManager):
         return super(PosteManager, self).get_query_set().select_related(*fkeys).all()
 
 class Poste_(AUFMetadata):
-    """Un Poste est un emploi (job) à combler dans une implantation. 
+    """Un Poste est un emploi (job) à combler dans une implantation.
     Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
     Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
     """
@@ -75,29 +76,29 @@ class Poste_(AUFMetadata):
     objects = PosteManager()
 
     # Identification
-    nom = models.CharField(max_length=255, 
+    nom = models.CharField(max_length=255,
                             verbose_name = u"Titre du poste", )
     nom_feminin = models.CharField(max_length=255,
                             verbose_name = u"Titre du poste (au féminin)",
                             null=True)
-    implantation = models.ForeignKey(ref.Implantation, 
+    implantation = models.ForeignKey(ref.Implantation,
                             db_column='implantation', related_name='+')
     type_poste = models.ForeignKey('TypePoste', db_column='type_poste',
                             related_name='+',
                             null=True)
-    service = models.ForeignKey('Service', db_column='service', null=True,
+    service = models.ForeignKey('Service', db_column='service',
                             related_name='+',
                             verbose_name = u"Direction/Service/Pôle support",
                             default=1)  # default = Rectorat
-    responsable = models.ForeignKey('Poste', db_column='responsable', 
-                            related_name='+', null=True,
+    responsable = models.ForeignKey('Poste', db_column='responsable',
+                            related_name='+',
                             verbose_name = u"Poste du responsable",
                             default=149)    # default = Recteur
-                                
+
     # Contrat
     regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
                             default=REGIME_TRAVAIL_DEFAULT, null=True,
-                            verbose_name = u"Temps de travail", 
+                            verbose_name = u"Temps de travail",
                             help_text="% du temps complet")
     regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
                             decimal_places=2, null=True,
@@ -105,9 +106,9 @@ class Poste_(AUFMetadata):
                             verbose_name = u"Nb. heures par semaine")
 
     # Recrutement
-    local = models.NullBooleanField(verbose_name = u"Local", default=True, 
+    local = models.NullBooleanField(verbose_name = u"Local", default=True,
                             null=True, blank=True)
-    expatrie = models.NullBooleanField(verbose_name = u"Expatrié", default=False, 
+    expatrie = models.NullBooleanField(verbose_name = u"Expatrié", default=False,
                             null=True, blank=True)
     mise_a_disposition = models.NullBooleanField(
                             verbose_name = u"Mise à disposition",
@@ -118,17 +119,17 @@ class Poste_(AUFMetadata):
                             default='interne')
 
     # Rémunération
-    classement_min = models.ForeignKey('Classement', 
+    classement_min = models.ForeignKey('Classement',
                             db_column='classement_min', related_name='+',
                             null=True, blank=True)
-    classement_max = models.ForeignKey('Classement', 
+    classement_max = models.ForeignKey('Classement',
                             db_column='classement_max', related_name='+',
                             null=True, blank=True)
-    valeur_point_min = models.ForeignKey('ValeurPoint', 
-                            db_column='valeur_point_min', related_name='+', 
+    valeur_point_min = models.ForeignKey('ValeurPoint',
+                            db_column='valeur_point_min', related_name='+',
                             null=True, blank=True)
-    valeur_point_max = models.ForeignKey('ValeurPoint', 
-                            db_column='valeur_point_max', related_name='+', 
+    valeur_point_max = models.ForeignKey('ValeurPoint',
+                            db_column='valeur_point_max', related_name='+',
                             null=True, blank=True)
     devise_min = models.ForeignKey('Devise', db_column='devise_min', null=True,
                             related_name='+', default=5)
@@ -149,7 +150,7 @@ class Poste_(AUFMetadata):
 
     # Comparatifs de rémunération
     devise_comparaison = models.ForeignKey('Devise', null=True,
-                            db_column='devise_comparaison', 
+                            db_column='devise_comparaison',
                             related_name='+',
                             default=5)
     comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
@@ -190,12 +191,12 @@ class Poste_(AUFMetadata):
         verbose_name_plural = u"Postes"
 
     def __unicode__(self):
-        representation = u'%s - %s [%s]' % (self.implantation, self.nom, 
+        representation = u'%s - %s [%s]' % (self.implantation, self.nom,
                             self.id)
         if self.is_vacant():
             representation = representation + u' (VACANT)'
         return representation
-        
+
     def is_vacant(self):
         vacant = True
         if self.occupe_par():
@@ -219,10 +220,6 @@ class Poste(Poste_):
     __doc__ = Poste_.__doc__
 
 
-class Poste(Poste_):
-    __doc__ = Poste_.__doc__
-
-
 POSTE_FINANCEMENT_CHOICES = (
     ('A', 'A - Frais de personnel'),
     ('B', 'B - Projet(s)-Titre(s)'),
@@ -234,7 +231,7 @@ class PosteFinancement_(models.Model):
     """Pour un Poste, structure d'informations décrivant comment on prévoit
     financer ce Poste.
     """
-    poste = models.ForeignKey('Poste', db_column='poste', 
+    poste = models.ForeignKey('Poste', db_column='poste',
                             related_name='%(app_label)s_financements')
     type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
     pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
@@ -245,7 +242,7 @@ class PosteFinancement_(models.Model):
     class Meta:
         abstract = True
         ordering = ['type']
-        
+
     def __unicode__(self):
         return u'%s : %s %' % (self.type, self.pourcentage)
 
@@ -258,16 +255,16 @@ class PostePiece(models.Model):
     """Documents relatifs au Poste.
     Ex.: Description de poste
     """
-    poste = models.ForeignKey('Poste', db_column='poste', 
+    poste = models.ForeignKey('Poste', db_column='poste',
                             related_name='pieces')
     nom = models.CharField(verbose_name = u"Nom", max_length=255)
-    fichier = models.FileField(verbose_name = u"Fichier", 
-                            upload_to=poste_piece_dispatch, 
+    fichier = models.FileField(verbose_name = u"Fichier",
+                            upload_to=poste_piece_dispatch,
                             storage=storage_prive)
 
     class Meta:
         ordering = ['nom']
-        
+
     def __unicode__(self):
         return u'%s' % (self.nom)
 
@@ -309,40 +306,41 @@ SITUATION_CHOICES = (
 )
 
 class Employe(AUFMetadata):
-    """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de 
+    """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
     Dossiers qu'il occupe ou a occupé de Postes.
-    
-    Cette classe aurait pu avantageusement s'appeler Personne car la notion 
+
+    Cette classe aurait pu avantageusement s'appeler Personne car la notion
     d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
     """
     # Identification
     nom = models.CharField(max_length=255)
     prenom = models.CharField(max_length=255, verbose_name = u"Prénom")
-    nom_affichage = models.CharField(max_length=255, 
+    nom_affichage = models.CharField(max_length=255,
                             verbose_name = u"Nom d'affichage",
                             null=True, blank=True)
-    nationalite = models.ForeignKey(ref.Pays, to_field='code', 
+    nationalite = models.ForeignKey(ref.Pays, to_field='code',
                             db_column='nationalite',
                             related_name='employes_nationalite',
-                            verbose_name = u"Nationalité")
+                            verbose_name = u"Nationalité",
+                            blank=True, null=True)
     date_naissance = models.DateField(verbose_name = u"Date de naissance",
                             validators=[validate_date_passee],
                             null=True, blank=True)
     genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
-    
+
     # Infos personnelles
-    situation_famille = models.CharField(max_length=1, 
+    situation_famille = models.CharField(max_length=1,
                             choices=SITUATION_CHOICES,
                             verbose_name = u"Situation familiale",
                             null=True, blank=True)
     date_entree = models.DateField(verbose_name = u"Date d'entrée à l'AUF",
                             null=True, blank=True)
-    
+
     # Coordonnées
-    tel_domicile = models.CharField(max_length=255, 
+    tel_domicile = models.CharField(max_length=255,
                             verbose_name = u"Tél. domicile",
                             null=True, blank=True)
-    tel_cellulaire = models.CharField(max_length=255, 
+    tel_cellulaire = models.CharField(max_length=255,
                             verbose_name = u"Tél. cellulaire",
                             null=True, blank=True)
     adresse = models.CharField(max_length=255, null=True, blank=True)
@@ -350,23 +348,23 @@ class Employe(AUFMetadata):
     province = models.CharField(max_length=255, null=True, blank=True)
     code_postal = models.CharField(max_length=255, null=True, blank=True)
     pays = models.ForeignKey(ref.Pays, to_field='code', db_column='pays',
-                            related_name='employes', 
+                            related_name='employes',
                             null=True, blank=True)
 
     class Meta:
         ordering = ['nom_affichage','nom','prenom']
         verbose_name = u"Employé"
         verbose_name_plural = u"Employés"
-        
+
     def __unicode__(self):
         return u'%s [%s]' % (self.get_nom(), self.id)
-        
+
     def get_nom(self):
         nom_affichage = self.nom_affichage
         if not nom_affichage:
             nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
         return nom_affichage
-        
+
     def civilite(self):
         civilite = u''
         if self.genre.upper() == u'M':
@@ -374,45 +372,45 @@ class Employe(AUFMetadata):
         elif self.genre.upper() == u'F':
             civilite = u'Mme'
         return civilite
-        
+
     def url_photo(self):
         """Retourne l'URL du service retournant la photo de l'Employe.
         Équivalent reverse url 'rh_photo' avec id en param.
         """
         from django.core.urlresolvers import reverse
         return reverse('rh_photo', kwargs={'id':self.id})
-               
+
     def dossiers_passes(self):
         today = date.today()
         dossiers_passes = self.dossiers.filter(date_fin__lt=today).order_by('-date_fin')
         for d in dossiers_passes:
             d.archive = True
         return dossiers_passes
-        
+
     def dossiers_futurs(self):
         today = date.today()
         return self.dossiers.filter(date_debut__gt=today).order_by('-date_fin')
-        
+
     def dossiers_encours(self):
         dossiers_p_f = self.dossiers_passes() | self.dossiers_futurs()
         ids_dossiers_p_f = [d.id for d in dossiers_p_f]
         dossiers_encours = self.dossiers.exclude(id__in=ids_dossiers_p_f).order_by('-date_fin')
-        
+
         # TODO : supprimer ce code quand related_name fonctionnera ou d.remuneration_set
         for d in dossiers_encours:
             d.remunerations = Remuneration.objects.filter(dossier=d.id).order_by('-id')
         return dossiers_encours
-        
+
     def postes_encours(self):
         postes_encours = set()
         for d in self.dossiers_encours():
             postes_encours.add(d.poste)
         return postes_encours
-        
+
     def poste_principal(self):
         """
         Retourne le Poste du premier Dossier créé parmi les Dossiers en cours.
-        Idée derrière : 
+        Idée derrière :
         si on ajout d'autre Dossiers, c'est pour des Postes secondaires.
         """
         poste = Poste.objects.none()
@@ -436,8 +434,8 @@ class EmployePiece(models.Model):
     """
     employe = models.ForeignKey('Employe', db_column='employe')
     nom = models.CharField(verbose_name="Nom", max_length=255)
-    fichier = models.FileField(verbose_name="Fichier", 
-                            upload_to=employe_piece_dispatch, 
+    fichier = models.FileField(verbose_name="Fichier",
+                            upload_to=employe_piece_dispatch,
                             storage=storage_prive)
 
     class Meta:
@@ -471,23 +469,24 @@ class AyantDroit(AUFMetadata):
     nom = models.CharField(max_length=255)
     prenom = models.CharField(max_length=255,
                             verbose_name = u"Prénom",)
-    nom_affichage = models.CharField(max_length=255, 
+    nom_affichage = models.CharField(max_length=255,
                             verbose_name = u"Nom d'affichage",
                             null=True, blank=True)
-    nationalite = models.ForeignKey(ref.Pays, to_field='code', 
+    nationalite = models.ForeignKey(ref.Pays, to_field='code',
                             db_column='nationalite',
                             related_name='ayantdroits_nationalite',
-                            verbose_name = u"Nationalité")
+                            verbose_name = u"Nationalité",
+                            null=True, blank=True)
     date_naissance = models.DateField(verbose_name = u"Date de naissance",
                             validators=[validate_date_passee],
                             null=True, blank=True)
     genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
-    
+
     # Relation
-    employe = models.ForeignKey('Employe', db_column='employe', 
+    employe = models.ForeignKey('Employe', db_column='employe',
                             related_name='ayantdroits',
                             verbose_name = u"Employé")
-    lien_parente = models.CharField(max_length=10, 
+    lien_parente = models.CharField(max_length=10,
                             choices=LIEN_PARENTE_CHOICES,
                             verbose_name = u"Lien de parenté",
                             null=True, blank=True)
@@ -496,10 +495,10 @@ class AyantDroit(AUFMetadata):
         ordering = ['nom_affichage']
         verbose_name = u"Ayant droit"
         verbose_name_plural = u"Ayants droit"
-        
+
     def __unicode__(self):
         return u'%s' % (self.get_nom())
-        
+
     def get_nom(self):
         nom_affichage = self.nom_affichage
         if not nom_affichage:
@@ -536,38 +535,38 @@ class Dossier_(AUFMetadata):
     """Le Dossier regroupe les informations relatives à l'occupation
     d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
     par un Employe.
-    
+
     Plusieurs Contrats peuvent être associés au Dossier.
     Une structure de Remuneration est rattachée au Dossier. Un Poste pour
     lequel aucun Dossier n'existe est un poste vacant.
     """
     # Identification
-    employe = models.ForeignKey('Employe', db_column='employe', 
+    employe = models.ForeignKey('Employe', db_column='employe',
                             related_name='dossiers',
                             verbose_name=u"Employé")
     # TODO: OneToOne ??
     poste = models.ForeignKey('Poste', db_column='poste', related_name='dossiers')
     statut = models.ForeignKey('Statut', related_name='+', default=3,
                             null=True)
-    organisme_bstg = models.ForeignKey('OrganismeBstg', 
+    organisme_bstg = models.ForeignKey('OrganismeBstg',
                             db_column='organisme_bstg',
                             related_name='+',
-                            verbose_name = u"Organisme", 
+                            verbose_name = u"Organisme",
                             help_text="Si détaché (DET) ou \
                                     mis à disposition (MAD), \
                                     préciser l'organisme.",
                             null=True, blank=True)
-                          
+
     # Recrutement
     remplacement = models.BooleanField(default=False)
     remplacement_de = models.ForeignKey('self', related_name='+',
                             null=True, blank=True)
-    statut_residence = models.CharField(max_length=10, default='local', 
+    statut_residence = models.CharField(max_length=10, default='local',
                             verbose_name = u"Statut", null=True,
                             choices=STATUT_RESIDENCE_CHOICES)
-   
+
     # Rémunération
-    classement = models.ForeignKey('Classement', db_column='classement', 
+    classement = models.ForeignKey('Classement', db_column='classement',
                             related_name='+',
                             null=True, blank=True)
     regime_travail = models.DecimalField(max_digits=12, null=True,
@@ -586,30 +585,30 @@ class Dossier_(AUFMetadata):
     date_fin = models.DateField(verbose_name = u"Date de fin d'occupation \
                             de poste",
                             null=True, blank=True)
-    
+
     # Comptes
     # TODO?
-    
+
     class Meta:
         abstract = True
         ordering = ['employe__nom', ]
         verbose_name = u"Dossier"
         verbose_name_plural = "Dossiers"
-        
+
     def salaire_theorique(self):
         annee = date.today().year
         coeff = self.classement.coefficient
         implantation = self.poste.implantation
         point = ValeurPoint.objects.get(implantation=implantation, annee=annee)
-        
+
         montant = coeff * point.valeur
         devise = point.devise
         return {'montant':montant, 'devise':devise}
-        
+
     def __unicode__(self):
         poste = self.poste.nom
         if self.employe.genre == 'F':
-            poste = self.poste.nom_feminin            
+            poste = self.poste.nom_feminin
         return u'%s - %s' % (self.employe, poste)
 
     prefix_implantation = "poste__implantation__region"
@@ -634,21 +633,21 @@ class DossierPiece(models.Model):
     """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
     Ex.: Lettre de motivation.
     """
-    dossier = models.ForeignKey('Dossier', db_column='dossier', 
+    dossier = models.ForeignKey('Dossier', db_column='dossier',
                             related_name='+')
     nom = models.CharField(verbose_name = u"Nom", max_length=255)
-    fichier = models.FileField(verbose_name = u"Fichier", 
-                            upload_to=dossier_piece_dispatch, 
+    fichier = models.FileField(verbose_name = u"Fichier",
+                            upload_to=dossier_piece_dispatch,
                             storage=storage_prive)
 
     class Meta:
         ordering = ['nom']
-        
+
     def __unicode__(self):
         return u'%s' % (self.nom)
 
 class DossierCommentaire(Commentaire):
-    dossier = models.ForeignKey('Dossier', db_column='dossier', 
+    dossier = models.ForeignKey('Dossier', db_column='dossier',
                             related_name='+')
 
 class DossierComparaison(models.Model):
@@ -674,16 +673,16 @@ class DossierComparaison(models.Model):
 
 
 ### RÉMUNÉRATION
-    
+
 class RemunerationMixin(AUFMetadata):
     # Identification
     dossier = models.ForeignKey('Dossier', db_column='dossier',
                         related_name='%(app_label)s_%(class)s_remunerations')
-    type = models.ForeignKey('TypeRemuneration', db_column='type', 
+    type = models.ForeignKey('TypeRemuneration', db_column='type',
                             related_name='+',
                             verbose_name = u"Type de rémunération")
-    type_revalorisation = models.ForeignKey('TypeRevalorisation', 
-                            db_column='type_revalorisation', 
+    type_revalorisation = models.ForeignKey('TypeRevalorisation',
+                            db_column='type_revalorisation',
                             related_name='+',
                             verbose_name = u"Type de revalorisation",
                             null=True, blank=True)
@@ -700,18 +699,18 @@ class RemunerationMixin(AUFMetadata):
                             null=True, blank=True)
     date_fin = models.DateField(verbose_name = u"Date de fin",
                             null=True, blank=True)
-    
-    class Meta: 
+
+    class Meta:
         abstract = True
         ordering = ['type__nom', '-date_fin']
-        
+
     def __unicode__(self):
         return u'%s %s (%s)' % (self.montant, self.devise.code, self.type.nom)
-    
+
 class Remuneration_(RemunerationMixin):
     """Structure de rémunération (données budgétaires) en situation normale
-    pour un Dossier. Si un Evenement existe, utiliser la structure de 
-    rémunération EvenementRemuneration de cet événement.    
+    pour un Dossier. Si un Evenement existe, utiliser la structure de
+    rémunération EvenementRemuneration de cet événement.
     """
 
     def montant_mois(self):
@@ -725,7 +724,7 @@ class Remuneration_(RemunerationMixin):
 
     def montant_euro_mois(self):
         return round(self.montant_euro() / 12, 2)
-    
+
     def __unicode__(self):
         try:
             devise = self.devise.code
@@ -749,18 +748,18 @@ class ContratManager(NoDeleteManager):
     def get_query_set(self):
         return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste')
 
-        
+
 class Contrat(AUFMetadata):
     """Document juridique qui encadre la relation de travail d'un Employe
-    pour un Poste particulier. Pour un Dossier (qui documente cette 
+    pour un Poste particulier. Pour un Dossier (qui documente cette
     relation de travail) plusieurs contrats peuvent être associés.
     """
 
     objects = ContratManager()
 
-    dossier = models.ForeignKey('Dossier', db_column='dossier', 
+    dossier = models.ForeignKey('Dossier', db_column='dossier',
                             related_name='contrats')
-    type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat', 
+    type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
                             related_name='+',
                             verbose_name = u"Type de contrat")
     date_debut = models.DateField(verbose_name = u"Date de début")
@@ -771,28 +770,28 @@ class Contrat(AUFMetadata):
         ordering = ['dossier__employe__nom_affichage']
         verbose_name = u"Contrat"
         verbose_name_plural = u"Contrats"
-        
+
     def __unicode__(self):
         return u'%s - %s' % (self.dossier, self.id)
-        
+
 # TODO? class ContratPiece(models.Model):
-    
+
 
 ### ÉVÉNEMENTS
 
 class Evenement_(AUFMetadata):
-    """Un Evenement sert à déclarer une situation temporaire (exceptionnelle) 
-    d'un Dossier qui vient altérer des informations normales liées à un Dossier 
+    """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
+    d'un Dossier qui vient altérer des informations normales liées à un Dossier
     (ex.: la Remuneration).
-    
+
     Ex.: congé de maternité, maladie...
-    
+
     Lors de ces situations exceptionnelles, l'Employe a un régime de travail
     différent et une rémunération en conséquence. On souhaite toutefois
     conserver le Dossier intact afin d'éviter une re-saisie des données lors
     du retour à la normale.
     """
-    dossier = models.ForeignKey('Dossier', db_column='dossier', 
+    dossier = models.ForeignKey('Dossier', db_column='dossier',
                             related_name='+')
     nom = models.CharField(max_length=255)
     date_debut = models.DateField(verbose_name = u"Date de début")
@@ -804,7 +803,7 @@ class Evenement_(AUFMetadata):
         ordering = ['nom']
         verbose_name = u"Évènement"
         verbose_name_plural = u"Évènements"
-                            
+
     def __unicode__(self):
         return u'%s' % (self.nom)
 
@@ -812,9 +811,9 @@ class Evenement_(AUFMetadata):
 class Evenement(Evenement_):
     __doc__ = Evenement_.__doc__
 
-    
+
 class EvenementRemuneration_(RemunerationMixin):
-    """Structure de rémunération liée à un Evenement qui remplace 
+    """Structure de rémunération liée à un Evenement qui remplace
     temporairement la Remuneration normale d'un Dossier, pour toute la durée
     de l'Evenement.
     """
@@ -842,19 +841,19 @@ class EvenementRemuneration(EvenementRemuneration_):
     __doc__ = EvenementRemuneration_.__doc__
 
 
-### RÉFÉRENCES RH 
+### RÉFÉRENCES RH
 
 class FamilleEmploi(AUFMetadata):
     """Catégorie utilisée dans la gestion des Postes.
     Catégorie supérieure à TypePoste.
     """
     nom = models.CharField(max_length=255)
-    
+
     class Meta:
         ordering = ['nom']
         verbose_name = u"Famille d'emploi"
         verbose_name_plural = u"Familles d'emploi"
-    
+
     def __unicode__(self):
         return u'%s' % (self.nom)
 
@@ -864,10 +863,10 @@ class TypePoste(AUFMetadata):
     nom = models.CharField(max_length=255)
     nom_feminin = models.CharField(max_length=255,
                             verbose_name = u"Nom féminin")
-    
+
     is_responsable = models.BooleanField(default=False,
                             verbose_name = u"Poste de responsabilité")
-    famille_emploi = models.ForeignKey('FamilleEmploi', 
+    famille_emploi = models.ForeignKey('FamilleEmploi',
                             db_column='famille_emploi',
                             related_name='+',
                             verbose_name = u"Famille d'emploi")
@@ -876,7 +875,7 @@ class TypePoste(AUFMetadata):
         ordering = ['nom']
         verbose_name = u"Type de poste"
         verbose_name_plural = u"Types de poste"
-        
+
     def __unicode__(self):
         return u'%s' % (self.nom)
 
@@ -898,13 +897,13 @@ class TypeRemuneration(AUFMetadata):
     """Catégorie de Remuneration.
     """
     nom = models.CharField(max_length=255)
-    type_paiement = models.CharField(max_length=30, 
+    type_paiement = models.CharField(max_length=30,
                             choices=TYPE_PAIEMENT_CHOICES,
                             verbose_name = u"Type de paiement")
-    nature_remuneration = models.CharField(max_length=30, 
+    nature_remuneration = models.CharField(max_length=30,
                             choices=NATURE_REMUNERATION_CHOICES,
                             verbose_name = u"Nature de la rémunération")
-                            
+
     class Meta:
         ordering = ['nom']
         verbose_name = u"Type de rémunération"
@@ -912,13 +911,13 @@ class TypeRemuneration(AUFMetadata):
 
     def __unicode__(self):
         return u'%s' % (self.nom)
-        
+
 class TypeRevalorisation(AUFMetadata):
     """Justification du changement de la Remuneration.
     (Actuellement utilisé dans aucun traitement informatique.)
     """
     nom = models.CharField(max_length=255)
-    
+
     class Meta:
         ordering = ['nom']
         verbose_name = u"Type de revalorisation"
@@ -926,12 +925,12 @@ class TypeRevalorisation(AUFMetadata):
 
     def __unicode__(self):
         return u'%s' % (self.nom)
-    
+
 class Service(AUFMetadata):
     """Unité administrative où les Postes sont rattachés.
     """
     nom = models.CharField(max_length=255)
-        
+
     class Meta:
         ordering = ['nom']
         verbose_name = u"Service"
@@ -947,14 +946,14 @@ TYPE_ORGANISME_CHOICES = (
 )
 
 class OrganismeBstg(AUFMetadata):
-    """Organisation d'où provient un Employe mis à disposition (MAD) de 
+    """Organisation d'où provient un Employe mis à disposition (MAD) de
     ou détaché (DET) à l'AUF à titre gratuit.
-    
+
     (BSTG = bien et service à titre gratuit.)
     """
     nom = models.CharField(max_length=255)
     type = models.CharField(max_length=10, choices=TYPE_ORGANISME_CHOICES)
-    pays = models.ForeignKey(ref.Pays, to_field='code', 
+    pays = models.ForeignKey(ref.Pays, to_field='code',
                             db_column='pays',
                             related_name='organismes_bstg',
                             null=True, blank=True)
@@ -983,7 +982,7 @@ class Statut(AUFMetadata):
         ordering = ['code']
         verbose_name = u"Statut d'employé"
         verbose_name_plural = u"Statuts d'employé"
-        
+
     def __unicode__(self):
         return u'%s : %s' % (self.code, self.nom)
 
@@ -1000,24 +999,24 @@ TYPE_CLASSEMENT_CHOICES = (
 
 
 class Classement_(AUFMetadata):
-    """Éléments de classement de la 
+    """Éléments de classement de la
     "Grille générique de classement hiérarchique".
-    
-    Utile pour connaître, pour un Dossier, le salaire de base théorique lié au 
+
+    Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
     classement dans la grille. Le classement donne le coefficient utilisé dans:
 
     salaire de base = coefficient * valeur du point de l'Implantation du Poste
     """
     # Identification
     type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
-    echelon = models.IntegerField(verbose_name = u"Échelon")
-    degre = models.IntegerField(verbose_name = u"Degré")
-    coefficient = models.FloatField(default=0, verbose_name = u"Coéfficient",
+    echelon = models.IntegerField(verbose_name=u"Échelon", blank=True, default=0)
+    degre = models.IntegerField(verbose_name=u"Degré", blank=True, default=0)
+    coefficient = models.FloatField(default=0, verbose_name=u"Coefficient",
                                     null=True)
     # Méta
     # annee # au lieu de date_debut et date_fin
     commentaire = models.TextField(null=True, blank=True)
-    
+
     class Meta:
         abstract = True
         ordering = ['type','echelon','degre','coefficient']
@@ -1033,7 +1032,7 @@ class Classement(Classement_):
 
 
 class TauxChange_(AUFMetadata):
-    """Taux de change de la devise vers l'euro (EUR) 
+    """Taux de change de la devise vers l'euro (EUR)
     pour chaque année budgétaire.
     """
     # Identification
@@ -1046,7 +1045,7 @@ class TauxChange_(AUFMetadata):
         ordering = ['-annee', 'devise__code']
         verbose_name = u"Taux de change"
         verbose_name_plural = u"Taux de change"
-    
+
     def __unicode__(self):
         return u'%s : %s € (%s)' % (self.devise, self.taux, self.annee)
 
@@ -1060,19 +1059,19 @@ class ValeurPointManager(NoDeleteManager):
 
 
 class ValeurPoint_(AUFMetadata):
-    """Utile pour connaître, pour un Dossier, le salaire de base théorique lié 
-    au classement dans la grille. La ValeurPoint s'obtient par l'implantation 
+    """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
+    au classement dans la grille. La ValeurPoint s'obtient par l'implantation
     du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
 
     salaire de base = coefficient * valeur du point de l'Implantation du Poste
     """
-    
+
     actuelles = ValeurPointManager()
 
     valeur = models.FloatField(null=True)
     devise = models.ForeignKey('Devise', db_column='devise', null=True,
                             related_name='+', default=5)
-    implantation = models.ForeignKey(ref.Implantation, 
+    implantation = models.ForeignKey(ref.Implantation,
                             db_column='implantation',
                             related_name='%(app_label)s_valeur_point')
     # Méta
@@ -1115,7 +1114,7 @@ class Devise(AUFMetadata):
         ordering = ['code']
         verbose_name = u"Devise"
         verbose_name_plural = u"Devises"
-        
+
     def __unicode__(self):
         return u'%s - %s' % (self.code, self.nom)
 
@@ -1132,24 +1131,24 @@ class TypeContrat(AUFMetadata):
 
     def __unicode__(self):
         return u'%s' % (self.nom)
-        
-        
+
+
 ### AUTRES
 
 class ResponsableImplantation(AUFMetadata):
-    """Le responsable d'une implantation. 
+    """Le responsable d'une implantation.
     Anciennement géré sur le Dossier du responsable.
     """
-    employe = models.ForeignKey('Employe', db_column='employe', 
+    employe = models.ForeignKey('Employe', db_column='employe',
                             related_name='+',
                             null=True, blank=True)
-    implantation = models.ForeignKey(ref.Implantation, 
-                            db_column='implantation', related_name='+', 
+    implantation = models.ForeignKey(ref.Implantation,
+                            db_column='implantation', related_name='+',
                             unique=True)
 
     def __unicode__(self):
         return u'%s : %s' % (self.implantation, self.employe)
-        
+
     class Meta:
         ordering = ['implantation__nom']
         verbose_name = "Responsable d'implantation"