merge master
[auf_rh_dae.git] / project / rh / models.py
index 820f50b..c05e28d 100644 (file)
@@ -1,13 +1,12 @@
 # -=- encoding: utf-8 -=-
 
 # -=- encoding: utf-8 -=-
 
-import datetime
-
 from django.core.files.storage import FileSystemStorage
 from django.db import models
 from django.core.files.storage import FileSystemStorage
 from django.db import models
-import settings
-
+from django.conf import settings
+from auf.django.metadata.models import AUFMetadata
+from auf.django.metadata.managers import NoDeleteManager
 import datamaster_modeles.models as ref
 import datamaster_modeles.models as ref
-
+from validators import validate_date_passee
 
 # Constantes
 HELP_TEXT_DATE = "format: aaaa-mm-jj"
 
 # Constantes
 HELP_TEXT_DATE = "format: aaaa-mm-jj"
@@ -27,31 +26,8 @@ def dossier_piece_dispatch(instance, filename):
     path = "dossier/%s/%s" % (instance.dossier_id, filename)
     return path
 
     path = "dossier/%s/%s" % (instance.dossier_id, filename)
     return path
 
-# Abstracts
-class Metadata(models.Model):
-    """Méta-données AUF.
-    Metadata.actif = flag remplaçant la suppression.
-    supprime == True : objet réputé supprimé.
-    """
-    actif = models.BooleanField(default=True)
-    supprime = models.BooleanField(default=False)
-    date_creation = models.DateField(auto_now_add=True)
-    user_creation = models.ForeignKey('auth.User', 
-                            db_column='user_creation', related_name='+',
-                            null=True, blank=True)
-    date_modification = models.DateField(auto_now=True)
-    user_modification = models.ForeignKey('auth.User', 
-                            db_column='user_modification', related_name='+',
-                            null=True, blank=True)
-    date_desactivation = models.DateField(null=True, blank=True)
-    user_desactivation = models.ForeignKey('auth.User', 
-                            db_column='user_desactivation', related_name='+',
-                            null=True, blank=True)
-    
-    class Meta:
-        abstract = True
 
 
-class Commentaire(Metadata):
+class Commentaire(AUFMetadata):
     texte = models.TextField()
     owner = models.ForeignKey('auth.User', db_column='owner', related_name='+')
     
     texte = models.TextField()
     owner = models.ForeignKey('auth.User', db_column='owner', related_name='+')
     
@@ -70,11 +46,18 @@ POSTE_APPEL_CHOICES = (
     ('externe', 'Externe'),
 )
 
     ('externe', 'Externe'),
 )
 
-class Poste_(Metadata):
+class PosteManager(NoDeleteManager):
+    def get_query_set(self):
+        return super(PosteManager, self).get_query_set().select_related('implantation')
+
+class Poste_(AUFMetadata):
     """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.
     """
     """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.
     """
+
+    objects = PosteManager()
+
     # Identification
     nom = models.CharField(max_length=255, 
                             verbose_name="Titre du poste", )
     # Identification
     nom = models.CharField(max_length=255, 
                             verbose_name="Titre du poste", )
@@ -86,34 +69,34 @@ class Poste_(Metadata):
     type_poste = models.ForeignKey('TypePoste', db_column='type_poste',
                             related_name='+',
                             null=True)
     type_poste = models.ForeignKey('TypePoste', db_column='type_poste',
                             related_name='+',
                             null=True)
-    service = models.ForeignKey('Service', db_column='service', 
+    service = models.ForeignKey('Service', db_column='service', null=True,
                             related_name='+',
                             verbose_name="Direction/Service/Pôle support",
                             default=1)  # default = Rectorat
     responsable = models.ForeignKey('Poste', db_column='responsable', 
                             related_name='+',
                             verbose_name="Direction/Service/Pôle support",
                             default=1)  # default = Rectorat
     responsable = models.ForeignKey('Poste', db_column='responsable', 
-                            related_name='+',
+                            related_name='+', null=True,
                             verbose_name="Poste du responsable",
                             default=149)    # default = Recteur
                                 
     # Contrat
     regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
                             verbose_name="Poste du responsable",
                             default=149)    # default = Recteur
                                 
     # Contrat
     regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=REGIME_TRAVAIL_DEFAULT, 
+                            default=REGIME_TRAVAIL_DEFAULT, null=True,
                             verbose_name="Temps de travail", 
                             help_text="% du temps complet")
     regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
                             verbose_name="Temps de travail", 
                             help_text="% du temps complet")
     regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
-                            decimal_places=2,
+                            decimal_places=2, null=True,
                             default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
                             verbose_name="Nb. heures par semaine")
 
     # Recrutement
                             default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
                             verbose_name="Nb. heures par semaine")
 
     # Recrutement
-    local = models.BooleanField(verbose_name="Local", default=True, 
-                            blank=True)
-    expatrie = models.BooleanField(verbose_name="Expatrié", default=False, 
-                            blank=True)
-    mise_a_disposition = models.BooleanField(
+    local = models.NullBooleanField(verbose_name="Local", default=True, 
+                            null=True, blank=True)
+    expatrie = models.NullBooleanField(verbose_name="Expatrié", default=False, 
+                            null=True, blank=True)
+    mise_a_disposition = models.NullBooleanField(
                             verbose_name="Mise à disposition",
                             verbose_name="Mise à disposition",
-                            default=False)
-    appel = models.CharField(max_length=10, 
+                            null=True, default=False)
+    appel = models.CharField(max_length=10, null=True,
                             verbose_name="Appel à candidature",
                             choices=POSTE_APPEL_CHOICES,
                             default='interne')
                             verbose_name="Appel à candidature",
                             choices=POSTE_APPEL_CHOICES,
                             default='interne')
@@ -131,25 +114,25 @@ class Poste_(Metadata):
     valeur_point_max = models.ForeignKey('ValeurPoint', 
                             db_column='valeur_point_max', related_name='+', 
                             null=True, blank=True)
     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', 
+    devise_min = models.ForeignKey('Devise', db_column='devise_min', null=True,
                             related_name='+', default=5)
                             related_name='+', default=5)
-    devise_max = models.ForeignKey('Devise', db_column='devise_max', 
+    devise_max = models.ForeignKey('Devise', db_column='devise_max', null=True,
                             related_name='+', default=5)
     salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
                             related_name='+', default=5)
     salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=0)
+                            null=True, default=0)
     salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
     salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=0)
+                            null=True, default=0)
     indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
     indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=0)
+                            null=True, default=0)
     indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
     indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=0)
+                            null=True, default=0)
     autre_min = models.DecimalField(max_digits=12, decimal_places=2,
     autre_min = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=0)
+                            null=True, default=0)
     autre_max = models.DecimalField(max_digits=12, decimal_places=2,
     autre_max = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=0)
+                            null=True, default=0)
 
     # Comparatifs de rémunération
 
     # Comparatifs de rémunération
-    devise_comparaison = models.ForeignKey('Devise', 
+    devise_comparaison = models.ForeignKey('Devise', null=True,
                             db_column='devise_comparaison', 
                             related_name='+',
                             default=5)
                             db_column='devise_comparaison', 
                             related_name='+',
                             default=5)
@@ -180,7 +163,8 @@ class Poste_(Metadata):
     # Autres Metadata
     date_validation = models.DateTimeField(null=True, blank=True)   # de dae
     date_debut = models.DateField(verbose_name="Date de début",
     # Autres Metadata
     date_validation = models.DateTimeField(null=True, blank=True)   # de dae
     date_debut = models.DateField(verbose_name="Date de début",
-                            help_text=HELP_TEXT_DATE)
+                            help_text=HELP_TEXT_DATE,
+                            null=True, blank=True)
     date_fin = models.DateField(verbose_name="Date de fin",
                             help_text=HELP_TEXT_DATE,
                             null=True, blank=True)
     date_fin = models.DateField(verbose_name="Date de fin",
                             help_text=HELP_TEXT_DATE,
                             null=True, blank=True)
@@ -265,11 +249,20 @@ class PosteComparaison(models.Model):
     De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
     """
     poste = models.ForeignKey('Poste', related_name='comparaisons_internes')
     De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
     """
     poste = models.ForeignKey('Poste', related_name='comparaisons_internes')
-    implantation = models.ForeignKey(ref.Implantation, null=True, blank=True)
+    implantation = models.ForeignKey(ref.Implantation, null=True, blank=True, related_name="+")
     nom = models.CharField(verbose_name="Poste", max_length=255, null=True, blank=True)
     montant = models.IntegerField(null=True)
     devise = models.ForeignKey("Devise", default=5, related_name='+', null=True, blank=True)
     nom = models.CharField(verbose_name="Poste", max_length=255, null=True, blank=True)
     montant = models.IntegerField(null=True)
     devise = models.ForeignKey("Devise", default=5, related_name='+', null=True, blank=True)
-    montant_euros = models.IntegerField(null=True)
+
+    def taux_devise(self):
+        liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.implantation)
+        if len(liste_taux) == 0:
+            raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.implantation))
+        else:
+            return liste_taux[0].taux
+
+    def montant_euros(self):
+        return round(float(self.montant) * float(self.taux_devise()), 2)
 
 
 class PosteCommentaire(Commentaire):
 
 
 class PosteCommentaire(Commentaire):
@@ -288,7 +281,7 @@ SITUATION_CHOICES = (
     ('M', 'Marié'),
 )
 
     ('M', 'Marié'),
 )
 
-class Employe(Metadata):
+class Employe(AUFMetadata):
     """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de 
     Dossiers qu'il occupe ou a occupé de Postes.
     
     """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de 
     Dossiers qu'il occupe ou a occupé de Postes.
     
@@ -307,6 +300,7 @@ class Employe(Metadata):
                             verbose_name="Nationalité")
     date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
                             verbose_name="Date de naissance",
                             verbose_name="Nationalité")
     date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
                             verbose_name="Date de naissance",
+                            validators=[validate_date_passee],
                             null=True, blank=True)
     genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
     
                             null=True, blank=True)
     genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
     
@@ -347,6 +341,14 @@ class Employe(Metadata):
         if not nom_affichage:
             nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
         return 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':
+            civilite = u'M.'
+        elif self.genre.upper() == u'F':
+            civilite = u'Mme'
+        return civilite
 
 class EmployePiece(models.Model):
     """Documents relatifs à un employé.
 
 class EmployePiece(models.Model):
     """Documents relatifs à un employé.
@@ -377,7 +379,7 @@ LIEN_PARENTE_CHOICES = (
     ('Fils', 'Fils'),
 )
 
     ('Fils', 'Fils'),
 )
 
-class AyantDroit(Metadata):
+class AyantDroit(AUFMetadata):
     """Personne en relation avec un Employe.
     """
     # Identification
     """Personne en relation avec un Employe.
     """
     # Identification
@@ -393,6 +395,7 @@ class AyantDroit(Metadata):
                             verbose_name="Nationalité")
     date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
                             verbose_name="Date de naissance",
                             verbose_name="Nationalité")
     date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
                             verbose_name="Date de naissance",
+                            validators=[validate_date_passee],
                             null=True, blank=True)
     genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
     
                             null=True, blank=True)
     genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
     
@@ -437,7 +440,7 @@ COMPTE_COMPTA_CHOICES = (
     ('aucun', 'Aucun'),
 )
 
     ('aucun', 'Aucun'),
 )
 
-class Dossier_(Metadata):
+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.
     """Le Dossier regroupe les informations relatives à l'occupation
     d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
     par un Employe.
@@ -448,11 +451,11 @@ class Dossier_(Metadata):
     """
     # Identification
     employe = models.ForeignKey('Employe', db_column='employe', 
     """
     # Identification
     employe = models.ForeignKey('Employe', db_column='employe', 
-                            related_name='+',
+                            related_name='dossiers',
                             verbose_name="Employé")
                             verbose_name="Employé")
-    poste = models.ForeignKey('Poste', db_column='poste', 
-                            related_name='+', editable=False)
-    statut = models.ForeignKey('Statut', related_name='+', default=3)
+    poste = models.ForeignKey('Poste', db_column='poste', related_name='+')
+    statut = models.ForeignKey('Statut', related_name='+', default=3,
+                            null=True)
     organisme_bstg = models.ForeignKey('OrganismeBstg', 
                             db_column='organisme_bstg',
                             related_name='+',
     organisme_bstg = models.ForeignKey('OrganismeBstg', 
                             db_column='organisme_bstg',
                             related_name='+',
@@ -465,20 +468,20 @@ class Dossier_(Metadata):
     # Recrutement
     remplacement = models.BooleanField(default=False)
     statut_residence = models.CharField(max_length=10, default='local', 
     # Recrutement
     remplacement = models.BooleanField(default=False)
     statut_residence = models.CharField(max_length=10, default='local', 
-                            verbose_name="Statut",
+                            verbose_name="Statut", null=True,
                             choices=STATUT_RESIDENCE_CHOICES)
    
     # Rémunération
     classement = models.ForeignKey('Classement', db_column='classement', 
                             related_name='+',
                             null=True, blank=True)
                             choices=STATUT_RESIDENCE_CHOICES)
    
     # Rémunération
     classement = models.ForeignKey('Classement', db_column='classement', 
                             related_name='+',
                             null=True, blank=True)
-    regime_travail = models.DecimalField(max_digits=12, 
+    regime_travail = models.DecimalField(max_digits=12, null=True,
                             decimal_places=2,
                             default=REGIME_TRAVAIL_DEFAULT,
                             verbose_name="Régime de travail",
                             help_text="% du temps complet")
     regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
                             decimal_places=2,
                             default=REGIME_TRAVAIL_DEFAULT,
                             verbose_name="Régime de travail",
                             help_text="% du temps complet")
     regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
-                            decimal_places=2, 
+                            decimal_places=2, null=True,
                             default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
                             verbose_name="Nb. heures par semaine")
 
                             default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
                             verbose_name="Nb. heures par semaine")
 
@@ -496,7 +499,7 @@ class Dossier_(Metadata):
     
     class Meta:
         abstract = True
     
     class Meta:
         abstract = True
-        ordering = ['employe__nom_affichage', 'employe__nom', 'poste__nom']
+        ordering = ['employe__nom', ]
         verbose_name = "Dossier"
         verbose_name_plural = "Dossiers"
         
         verbose_name = "Dossier"
         verbose_name_plural = "Dossiers"
         
@@ -511,11 +514,6 @@ class Dossier(Dossier_):
     __doc__ = Dossier_.__doc__
 
 
     __doc__ = Dossier_.__doc__
 
 
-
-class Dossier(Dossier_):
-    __doc__ = Dossier_.__doc__
-
-
 class DossierPiece(models.Model):
     """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
     Ex.: Lettre de motivation.
 class DossierPiece(models.Model):
     """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
     Ex.: Lettre de motivation.
@@ -537,10 +535,31 @@ class DossierCommentaire(Commentaire):
     dossier = models.ForeignKey('Dossier', db_column='dossier', 
                             related_name='+')
 
     dossier = models.ForeignKey('Dossier', db_column='dossier', 
                             related_name='+')
 
+class DossierComparaison(models.Model):
+    """
+    Photo d'une comparaison salariale au moment de l'embauche.
+    """
+    dossier = models.ForeignKey('Dossier', related_name='comparaisons')
+    implantation = models.ForeignKey(ref.Implantation, related_name="+", null=True, blank=True)
+    poste = models.CharField(max_length=255, null=True, blank=True)
+    personne = models.CharField(max_length=255, null=True, blank=True)
+    montant = models.IntegerField(null=True)
+    devise = models.ForeignKey('Devise', default=5, related_name='+', null=True, blank=True)
+
+    def taux_devise(self):
+        liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.dossier.poste.implantation)
+        if len(liste_taux) == 0:
+            raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.dossier.poste.implantation))
+        else:
+            return liste_taux[0].taux
+
+    def montant_euros(self):
+        return round(float(self.montant) * float(self.taux_devise()), 2)
+
 
 ### RÉMUNÉRATION
     
 
 ### RÉMUNÉRATION
     
-class RemunerationMixin(Metadata):
+class RemunerationMixin(AUFMetadata):
     # Identification
     dossier = models.ForeignKey('Dossier', db_column='dossier',
                         related_name='%(app_label)s_%(class)s_remunerations')
     # Identification
     dossier = models.ForeignKey('Dossier', db_column='dossier',
                         related_name='%(app_label)s_%(class)s_remunerations')
@@ -611,12 +630,20 @@ class Remuneration(Remuneration_):
 
 
 ### CONTRATS
 
 
 ### CONTRATS
+
+class ContratManager(NoDeleteManager):
+    def get_query_set(self):
+        return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste')
+
         
         
-class Contrat(Metadata):
+class Contrat(AUFMetadata):
     """Document juridique qui encadre la relation de travail d'un Employe
     pour un Poste particulier. Pour un Dossier (qui documente cette 
     relation de travail) plusieurs contrats peuvent être associés.
     """
     """Document juridique qui encadre la relation de travail d'un Employe
     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', 
                             related_name='+')
     type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat', 
     dossier = models.ForeignKey('Dossier', db_column='dossier', 
                             related_name='+')
     type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat', 
@@ -641,7 +668,7 @@ class Contrat(Metadata):
 
 ### ÉVÉNEMENTS
 
 
 ### ÉVÉNEMENTS
 
-class Evenement_(Metadata):
+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 
     (ex.: la Remuneration).
     """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).
@@ -707,7 +734,7 @@ class EvenementRemuneration(EvenementRemuneration_):
 
 ### RÉFÉRENCES RH 
 
 
 ### RÉFÉRENCES RH 
 
-class FamilleEmploi(Metadata):
+class FamilleEmploi(AUFMetadata):
     """Catégorie utilisée dans la gestion des Postes.
     Catégorie supérieure à TypePoste.
     """
     """Catégorie utilisée dans la gestion des Postes.
     Catégorie supérieure à TypePoste.
     """
@@ -721,7 +748,7 @@ class FamilleEmploi(Metadata):
     def __unicode__(self):
         return u'%s' % (self.nom)
 
     def __unicode__(self):
         return u'%s' % (self.nom)
 
-class TypePoste(Metadata):
+class TypePoste(AUFMetadata):
     """Catégorie de Poste.
     """
     nom = models.CharField(max_length=255)
     """Catégorie de Poste.
     """
     nom = models.CharField(max_length=255)
@@ -757,7 +784,7 @@ NATURE_REMUNERATION_CHOICES = (
     ('Traitement', 'Traitement'),
 )
 
     ('Traitement', 'Traitement'),
 )
 
-class TypeRemuneration(Metadata):
+class TypeRemuneration(AUFMetadata):
     """Catégorie de Remuneration.
     """
     nom = models.CharField(max_length=255)
     """Catégorie de Remuneration.
     """
     nom = models.CharField(max_length=255)
@@ -776,7 +803,7 @@ class TypeRemuneration(Metadata):
     def __unicode__(self):
         return u'%s' % (self.nom)
         
     def __unicode__(self):
         return u'%s' % (self.nom)
         
-class TypeRevalorisation(Metadata):
+class TypeRevalorisation(AUFMetadata):
     """Justification du changement de la Remuneration.
     (Actuellement utilisé dans aucun traitement informatique.)
     """
     """Justification du changement de la Remuneration.
     (Actuellement utilisé dans aucun traitement informatique.)
     """
@@ -790,7 +817,7 @@ class TypeRevalorisation(Metadata):
     def __unicode__(self):
         return u'%s' % (self.nom)
     
     def __unicode__(self):
         return u'%s' % (self.nom)
     
-class Service(Metadata):
+class Service(AUFMetadata):
     """Unité administrative où les Postes sont rattachés.
     """
     nom = models.CharField(max_length=255)
     """Unité administrative où les Postes sont rattachés.
     """
     nom = models.CharField(max_length=255)
@@ -809,7 +836,7 @@ TYPE_ORGANISME_CHOICES = (
     ('DET', 'Détachement'),
 )
 
     ('DET', 'Détachement'),
 )
 
-class OrganismeBstg(Metadata):
+class OrganismeBstg(AUFMetadata):
     """Organisation d'où provient un Employe mis à disposition (MAD) de 
     ou détaché (DET) à l'AUF à titre gratuit.
     
     """Organisation d'où provient un Employe mis à disposition (MAD) de 
     ou détaché (DET) à l'AUF à titre gratuit.
     
@@ -830,7 +857,7 @@ class OrganismeBstg(Metadata):
     def __unicode__(self):
         return u'%s (%s)' % (self.nom, self.get_type_display())
 
     def __unicode__(self):
         return u'%s (%s)' % (self.nom, self.get_type_display())
 
-class Statut(Metadata):
+class Statut(AUFMetadata):
     """Statut de l'Employe dans le cadre d'un Dossier particulier.
     """
     # Identification
     """Statut de l'Employe dans le cadre d'un Dossier particulier.
     """
     # Identification
@@ -857,7 +884,7 @@ TYPE_CLASSEMENT_CHOICES = (
 )
 
 
 )
 
 
-class Classement_(Metadata):
+class Classement_(AUFMetadata):
     """Éléments de classement de la 
     "Grille générique de classement hiérarchique".
     
     """Éléments de classement de la 
     "Grille générique de classement hiérarchique".
     
@@ -870,7 +897,8 @@ class Classement_(Metadata):
     type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
     echelon = models.IntegerField(verbose_name="Échelon")
     degre = models.IntegerField(verbose_name="Degré")
     type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
     echelon = models.IntegerField(verbose_name="Échelon")
     degre = models.IntegerField(verbose_name="Degré")
-    coefficient = models.FloatField(default=0, verbose_name="Coéfficient")
+    coefficient = models.FloatField(default=0, verbose_name="Coéfficient",
+                                    null=True)
     # Méta
     # annee # au lieu de date_debut et date_fin
     commentaire = models.TextField(null=True, blank=True)
     # Méta
     # annee # au lieu de date_debut et date_fin
     commentaire = models.TextField(null=True, blank=True)
@@ -889,7 +917,7 @@ class Classement(Classement_):
     __doc__ = Classement_.__doc__
 
 
     __doc__ = Classement_.__doc__
 
 
-class TauxChange_(Metadata):
+class TauxChange_(AUFMetadata):
     """Taux de change de la devise vers l'euro (EUR) 
     pour chaque année budgétaire.
     """
     """Taux de change de la devise vers l'euro (EUR) 
     pour chaque année budgétaire.
     """
@@ -912,16 +940,23 @@ class TauxChange_(Metadata):
 class TauxChange(TauxChange_):
     __doc__ = TauxChange_.__doc__
 
 class TauxChange(TauxChange_):
     __doc__ = TauxChange_.__doc__
 
+class ValeurPointManager(NoDeleteManager):
+    def get_query_set(self):
+        return super(ValeurPointManager, self).get_query_set().select_related('devise', 'implantation')
 
 
-class ValeurPoint_(Metadata):
+
+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 
     du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
 
     salaire de base = coefficient * valeur du point de l'Implantation du Poste
     """
     """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
     """
-    valeur = models.FloatField()
-    devise = models.ForeignKey('Devise', db_column='devise', 
+    
+    objects = ValeurPointManager()
+
+    valeur = models.FloatField(null=True)
+    devise = models.ForeignKey('Devise', db_column='devise', null=True,
                             related_name='+', default=5)
     implantation = models.ForeignKey(ref.Implantation, 
                             db_column='implantation',
                             related_name='+', default=5)
     implantation = models.ForeignKey(ref.Implantation, 
                             db_column='implantation',
@@ -930,9 +965,8 @@ class ValeurPoint_(Metadata):
     annee = models.IntegerField()
 
     class Meta:
     annee = models.IntegerField()
 
     class Meta:
-        ordering = ['annee', 'implantation__nom']
+        ordering = ['-annee', 'implantation__nom']
         abstract = True
         abstract = True
-        ordering = ['annee']
         verbose_name = "Valeur du point"
         verbose_name_plural = "Valeurs du point"
 
         verbose_name = "Valeur du point"
         verbose_name_plural = "Valeurs du point"
 
@@ -957,7 +991,7 @@ class ValeurPoint(ValeurPoint_):
     __doc__ = ValeurPoint_.__doc__
 
 
     __doc__ = ValeurPoint_.__doc__
 
 
-class Devise(Metadata):
+class Devise(AUFMetadata):
     """Devise monétaire.
     """
     code =  models.CharField(max_length=10, unique=True)
     """Devise monétaire.
     """
     code =  models.CharField(max_length=10, unique=True)
@@ -971,7 +1005,7 @@ class Devise(Metadata):
     def __unicode__(self):
         return u'%s - %s' % (self.code, self.nom)
 
     def __unicode__(self):
         return u'%s - %s' % (self.code, self.nom)
 
-class TypeContrat(Metadata):
+class TypeContrat(AUFMetadata):
     """Type de contrat.
     """
     nom = models.CharField(max_length=255)
     """Type de contrat.
     """
     nom = models.CharField(max_length=255)
@@ -988,7 +1022,7 @@ class TypeContrat(Metadata):
         
 ### AUTRES
 
         
 ### AUTRES
 
-class ResponsableImplantation(Metadata):
+class ResponsableImplantation(AUFMetadata):
     """Le responsable d'une implantation. 
     Anciennement géré sur le Dossier du responsable.
     """
     """Le responsable d'une implantation. 
     Anciennement géré sur le Dossier du responsable.
     """