merge rh et dev
authorOlivier Larchevêque <olivier.larcheveque@auf.org>
Fri, 6 May 2011 19:49:55 +0000 (15:49 -0400)
committerOlivier Larchevêque <olivier.larcheveque@auf.org>
Fri, 6 May 2011 19:49:55 +0000 (15:49 -0400)
1  2 
project/dae/models.py
project/rh/models.py
project/rh_v1/models.py
project/settings.py

@@@ -4,21 -5,28 +5,21 @@@ from django.conf import setting
  from django.core.files.storage import FileSystemStorage
  from django.db import models
  import reversion
- from workflow import PosteWorkflow
+ from workflow import PosteWorkflow, DossierWorkflow
+ from managers import DossierManager, PosteManager
  import datamaster_modeles.models as ref
  from rh_v1 import models as rh
- import settings
  
 -STATUT_RESIDENCE_CHOICES = (
 -    ('local', 'Local'),
 -    ('expat', 'Expatrié'),
 -)
  
 -POSTE_APPEL_CHOICES = (
 -    ('interne', 'Interne'),
 -    ('externe', 'Externe'),
 -)
 +# Constantes
 +HELP_TEXT_DATE = "format: aaaa-mm-jj"
 +REGIME_TRAVAIL_DEFAULT=100.00
 +REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT=35.00
  
 -POSTE_STATUT_CHOICES = (
 -    ('MAD', 'Mise à disposition'),
 -    ('DET', 'Détachement'),
 -)
  
  # Upload de fichiers
 -storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT, base_url=settings.PRIVE_MEDIA_URL)
 +storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT, 
 +                            base_url=settings.PRIVE_MEDIA_URL)
  
  def poste_piece_dispatch(instance, filename):
      path = "poste/%s/%s" % (instance.poste_id, filename)
@@@ -29,32 -37,11 +30,13 @@@ def dossier_piece_dispatch(instance, fi
      return path
  
  
 -class PostePiece(models.Model):
 -    poste = models.ForeignKey("Poste")
 -    nom = models.CharField(verbose_name="Nom", max_length=255)
 -    fichier = models.FileField(verbose_name="Fichier", upload_to=poste_piece_dispatch, storage=storage_prive)
 +### POSTE
 +
 +POSTE_APPEL_CHOICES = (
 +    ('interne', 'Interne'),
 +    ('externe', 'Externe'),
 +)
  
- class PosteManager(models.Manager):
-     """
-     Chargement de tous les objets FK existants sur chaque QuerySet.
-     """
-     def get_query_set(self):
-         fkeys = (
-             'id_rh',
-             'responsable',
-             'implantation',
-             'type_poste',
-             'service',
-             'classement_min',
-             'classement_max',
-             'valeur_point_min',
-             'valeur_point_max',
-         )
-         return super(PosteManager, self).get_query_set() \
-                                         .select_related(*fkeys).all()
  
  class Poste(PosteWorkflow, models.Model):
      # Modèle existant
              implantation_devise = 5 # EUR
          return implantation_devise
  
+     #####################
+     # Classement de poste
+     #####################
+     def get_couts_minimum(self):
+         return (float)(self.salaire_min + self.indemn_min + self.autre_min)
+     def get_taux_minimum(self):
+         try:
+             return rh.TauxChange.objects.filter(implantation=self.implantation, devise=self.devise_min)[0].taux
+         except:
+             return 1
+     def get_couts_minimum_euros(self):
+         return self.get_couts_minimum() * self.get_taux_minimum()
+     def get_couts_maximum(self):
+         return (float)(self.salaire_max + self.indemn_max + self.autre_max)
+     def get_taux_maximum(self):
+         try:
+             return rh.TauxChange.objects.filter(implantation=self.implantation, devise=self.devise_max)[0].taux
+         except:
+             return 1
+     def get_couts_maximum_euros(self):
+         return self.get_couts_maximum() * self.get_taux_maximum()
+     ######################
+     # Comparaison de poste
+     ######################
+     def est_comparable(self):
+         """
+         Si on a au moins une valeur de saisie dans les comparaisons, alors le poste
+         est comparable.
+         """
+         if self.comp_universite_min is None and \
+            self.comp_fonctionpub_min is None and \
+            self.comp_locale_min is None and \
+            self.comp_ong_min is None and \
+            self.comp_autre_min is None and \
+            self.comp_universite_max is None and \
+            self.comp_fonctionpub_max is None and \
+            self.comp_locale_max is None and \
+            self.comp_ong_max is None and \
+            self.comp_autre_max is None:
+             return False
+         else:
+             return True
+     
+     def get_taux_comparaison(self):
+         try:
+             return rh.TauxChange.objects.filter(implantation=self.implantation, devise=self.devise_comparaison)[0].taux
+         except:
+             return 1
+     def get_comp_universite_min_euros(self):
+         return (float)(self.comp_universite_min) * self.get_taux_comparaison()
+     def get_comp_fonctionpub_min_euros(self):
+         return (float)(self.comp_fonctionpub_min) * self.get_taux_comparaison()
+     def get_comp_locale_min_euros(self):
+         return (float)(self.comp_locale_min) * self.get_taux_comparaison()
+     def get_comp_ong_min_euros(self):
+         return (float)(self.comp_ong_min) * self.get_taux_comparaison()
+     def get_comp_autre_min_euros(self):
+         return (float)(self.comp_autre_min) * self.get_taux_comparaison()
+     def get_comp_universite_max_euros(self):
+         return (float)(self.comp_universite_max) * self.get_taux_comparaison()
+     def get_comp_fonctionpub_max_euros(self):
+         return (float)(self.comp_fonctionpub_max) * self.get_taux_comparaison()
+     def get_comp_locale_max_euros(self):
+         return (float)(self.comp_locale_max) * self.get_taux_comparaison()
+     def get_comp_ong_max_euros(self):
+         return (float)(self.comp_ong_max) * self.get_taux_comparaison()
+     def get_comp_autre_max_euros(self):
+         return (float)(self.comp_autre_max) * self.get_taux_comparaison()
      def __unicode__(self):
          """
 -        Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
 +        Cette fonction est consommatrice SQL car elle cherche les dossiers 
 +        qui ont été liés à celui-ci.
          """
          complement_nom_poste = self.get_complement_nom()
          if complement_nom_poste is None:
@@@ -267,19 -324,9 +325,24 @@@ class PosteFinancement(models.Model)
      class Meta:
          ordering = ['type']
  
+     def __unicode__(self):
+         return u"%s %s %s" % (self.get_type_display(), self.pourcentage, self.commentaire)
++
 +class PostePiece(models.Model):
 +    """Documents relatifs au Poste
 +    Ex.: Description de poste
 +    """
 +    poste = models.ForeignKey("Poste")
 +    nom = models.CharField(verbose_name="Nom", max_length=255)
 +    fichier = models.FileField(verbose_name="Fichier", 
 +                            upload_to=poste_piece_dispatch, 
 +                            storage=storage_prive)
 +
 +### EMPLOYÉ/PERSONNE
 +
 +# TODO : migration pour m -> M, f -> F
++
  GENRE_CHOICES = (
      ('m', 'Homme'),
      ('f', 'Femme'),
@@@ -311,7 -353,13 +374,7 @@@ COMPTE_COMPTA_CHOICES = 
      ('aucun', 'Aucun'),
  )
  
- class Dossier(models.Model):
 -class DossierPiece(models.Model):
 -    dossier = models.ForeignKey("Dossier")
 -    nom = models.CharField(verbose_name="Nom", max_length=255)
 -    fichier = models.FileField(verbose_name="Fichier", upload_to=dossier_piece_dispatch, storage=storage_prive)
 -
 -
+ class Dossier(DossierWorkflow, models.Model):
  
      # Modèle existant
      employe = models.ForeignKey('Employe', related_name='+', editable=False)
  if not reversion.is_registered(Dossier):
      reversion.register(Dossier)
  
 +class DossierPiece(models.Model):
 +    """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
 +    Ex.: Lettre de motivation.
 +    """
 +    dossier = models.ForeignKey("Dossier")
 +    nom = models.CharField(verbose_name="Nom", max_length=255)
 +    fichier = models.FileField(verbose_name="Fichier", 
 +                            upload_to=dossier_piece_dispatch, 
 +                            storage=storage_prive)
 +
 +
+ 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, 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(rh.Devise, default=5, related_name='+', null=True, blank=True)
+     montant_euros = models.IntegerField(null=True)
++
 +### RÉMUNÉRATION
 +
  class Remuneration(models.Model):
      # Identification
      dossier = models.ForeignKey('Dossier', db_column='dossier')
@@@ -326,283 -86,60 +326,283 @@@ LIEN_PARENTE_CHOICES = 
      ('Fils', 'Fils'),
  )
  
 -class AyantDroit(models.Model):
 -    #Identification
 -    id = models.IntegerField(primary_key=True)
 +class AyantDroit(Metadata):
 +    """Personne en relation avec un Employe.
 +    """
 +    # Identification
      nom = models.CharField(max_length=255)
      prenom = models.CharField(max_length=255)
 -    #Relation
 -    employe = models.ForeignKey('Employe', db_column='employe', related_name='ayants_droit')
 -    lien_parente = models.CharField(max_length=10, choices=LIEN_PARENTE_CHOICES, null=True, blank=True)
 -    #Méta
 -    commentaire = models.TextField(null=True, blank=True) 
 -    actif = models.BooleanField()
 +    # TODO : nom_affichage doit être obligatoire, pas nom et prenom
 +    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', 
 +                            db_column='nationalite',
 +                            related_name='ayantdroits_nationalite')
 +    date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
 +                            null=True, blank=True)
 +    genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
 +    
 +    # Relation
 +    employe = models.ForeignKey('Employe', db_column='employe', 
 +                            related_name='ayantdroits')
 +    lien_parente = models.CharField(max_length=10, 
 +                            choices=LIEN_PARENTE_CHOICES,
 +                            null=True, blank=True)
 +
 +    class Meta:
 +        ordering = ['nom_affichage']
 +    def __unicode__(self):
 +        # TODO : gérer nom d'affichage
 +        return u'%s %s' % (self.prenom, self.nom.upper())
 +
 +class AyantDroitCommentaire(Commentaire):
 +    ayant_droit = models.ForeignKey("AyantDroit", db_column='ayant_droit',
 +                            related_name='+')
 +
 +
 +### DOSSIER
 +
 +STATUT_RESIDENCE_CHOICES = (
 +    ('local', 'Local'),
 +    ('expat', 'Expatrié'),
 +)
 +
 +COMPTE_COMPTA_CHOICES = (
 +    ('coda', 'CODA'),
 +    ('scs', 'SCS'),
 +    ('aucun', 'Aucun'),
 +)
 +
 +class Dossier(Metadata):
 +    """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', 
 +                            related_name='+')
 +    poste = models.ForeignKey('Poste', db_column='poste', 
 +                            related_name='+', editable=False)
 +    statut = models.ForeignKey('Statut', related_name='+', default=3)
 +    organisme_bstg = models.ForeignKey('OrganismeBstg', 
 +                            db_column='organisme_bstg',
 +                            related_name='+',
 +                            verbose_name=u"Organisme", 
 +                            help_text=u"Si détaché (DET) ou \
 +                                    mis à disposition (MAD), \
 +                                    préciser l'organisme.",
 +                            null=True, blank=True)
 +                          
 +    # Recrutement
 +    remplacement = models.BooleanField(default=False)
 +    statut_residence = models.CharField(max_length=10, default='local', 
 +                            verbose_name=u"Statut",
 +                            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, 
 +                            decimal_places=2,
 +                            default=REGIME_TRAVAIL_DEFAULT,
 +                            verbose_name=u"Régime de travail",
 +                            help_text=u"% du temps complet")
 +    regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
 +                            decimal_places=2, 
 +                            default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
 +                            verbose_name=u"Nb. heures par semaine")
 +
 +    # Occupation du Poste par cet Employe (anciennement "mandat")
 +    date_debut = models.DateField(verbose_name=u"Date de début d'occupation \
 +                            de poste",
 +                            help_text=HELP_TEXT_DATE)
 +    date_fin = models.DateField(verbose_name=u"Date de fin d'occupation \
 +                            de poste",
 +                            help_text=HELP_TEXT_DATE,
 +                            null=True, blank=True)
      
 +    # Comptes
 +    # TODO?
 +    
 +    class Meta:
 +        ordering = ['poste__nom', 'employe__nom_affichage']
 +        
 +    def __unicode__(self):
 +        return u'%s - %s' % (self.poste.nom, self.employe)
 +
 +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', 
 +                            related_name='+')
 +    nom = models.CharField(verbose_name=u"Nom", max_length=255)
 +    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', 
 +                            related_name='+')
 +
 +
 +### RÉMUNÉRATION
      
 -class Remuneration(models.Model):
 -    #Identification
 -    id = models.IntegerField(primary_key=True)
 +class RemunerationMixin(Metadata):
 +    # Identification
      dossier = models.ForeignKey('Dossier', db_column='dossier')
 -    type = models.ForeignKey('TypeRemuneration', db_column='type')
 -    type_revalorisation = models.ForeignKey('TypeRevalorisation', db_column='type_revalorisation')
 -    montant = models.FloatField()
 -    devise = models.ForeignKey('Devise', to_field='code', db_column='devise')
 -    date_effective = models.DateField()
 -    pourcentage = models.IntegerField()
 -    #Méta
 -    date_creation = models.DateField(auto_now_add=True)
 -    user_creation = models.IntegerField() #User ou employé
 -    desactivation = models.BooleanField() #
 -    date_desactivation = models.DateField()
 -    user_desactivation = models.IntegerField() #User ou employé
 -    annule = models.BooleanField()
 -    date_annule = models.DateField()
 -    user_annule = models.IntegerField() #User ou employé
 +    type = models.ForeignKey('TypeRemuneration', db_column='type', 
 +                            related_name='+')
 +    type_revalorisation = models.ForeignKey('TypeRevalorisation', 
 +                            db_column='type_revalorisation', 
 +                            related_name='+',
 +                            null=True, blank=True)
 +    montant = models.FloatField(null=True, blank=True,
 +                            default=0)
 +                            # Annuel (12 mois, 52 semaines, 364 jours?)
-     devise = models.ForeignKey('Devise', to_field='code',
++    devise = models.ForeignKey('Devise', to_field='id',
 +                            db_column='devise', related_name='+',
 +                            default=5)
 +    # commentaire = precision
 +    commentaire = models.CharField(max_length=255, null=True, blank=True)
 +    # date_debut = anciennement date_effectif
 +    date_debut = models.DateField(help_text=HELP_TEXT_DATE, 
 +                            null=True, blank=True)
 +    date_fin = models.DateField(help_text=HELP_TEXT_DATE,
 +                            null=True, blank=True)
      
 -class FamilleEmploi(models.Model):
 -    #Identification
 -    id = models.IntegerField(primary_key=True)
 +    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.    
 +    """
 +
 +    def montant_mois(self):
 +        return round(self.montant / 12, 2)
 +
 +    def taux_devise(self):
 +        return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
 +
 +    def montant_euro(self):
 +        return round(float(self.montant) / float(self.taux_devise()), 2)
 +
 +    def montant_euro_mois(self):
 +        return round(self.montant_euro() / 12, 2)
 +    
 +    def __unicode__(self):
 +        try:
 +            devise = self.devise.code
 +        except:
 +            devise = "???"
 +        return "%s %s" % (self.montant, devise)
 +
 +
 +### CONTRATS
 +        
 +class Contrat(Metadata):
 +    """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.
 +    """
 +    dossier = models.ForeignKey('Dossier', db_column='dossier', 
 +                            related_name='+')
 +    type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat', 
 +                            related_name='+')
 +    date_debut = models.DateField(help_text=HELP_TEXT_DATE)
 +    date_fin = models.DateField(help_text=HELP_TEXT_DATE,
 +                            null=True, blank=True)
 +
 +    class Meta:
 +        ordering = ['dossier__employe__nom_affichage']
 +        
 +    def __unicode__(self):
 +        return u'%s - %s' % (self.dossier.employe.nom_affichage, self.id)
 +        
 +# TODO? class ContratPiece(models.Model):
 +    
 +
 +### ÉVÉNEMENTS
 +
 +class Evenement(Metadata):
 +    """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', 
 +                            related_name='+')
 +    nom = models.CharField(max_length=255)
 +    date_debut = models.DateField(help_text=HELP_TEXT_DATE)
 +    date_fin = models.DateField(help_text=HELP_TEXT_DATE, 
 +                            null=True, blank=True)
 +    class Meta:
 +        ordering = ['nom']
 +                            
 +    def __unicode__(self):
 +        return u'%s' % (self.nom)
 +    
 +class EvenementRemuneration(RemunerationMixin):
 +    """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.
 +    """
 +    evenement = models.ForeignKey("Evenement", db_column='evenement',
 +                            related_name='+')
 +
 +
 +### RÉFÉRENCES RH 
 +
 +class FamilleEmploi(Metadata):
 +    """Catégorie utilisée dans la gestion des Postes.
 +    Catégorie supérieure à TypePoste.
 +    """
      nom = models.CharField(max_length=255)
 -    #Méta
 -    actif = models.BooleanField()
 +    
 +    def __unicode__(self):
 +        return u'%s' % (self.nom)
  
 -class TypePoste(models.Model):
 -    #Identification
 -    id = models.IntegerField(primary_key=True)
 +class TypePoste(Metadata):
 +    """Catégorie de Poste.
 +    """
      nom = models.CharField(max_length=255)
      nom_feminin = models.CharField(max_length=255)
 -    description = models.CharField(max_length=255)
 -    is_responsable = models.BooleanField()
 -    famille_emploi = models.ForeignKey('FamilleEmploi', db_column='famille_emploi')
 -    #Méta
 -    date_modification = models.DateField(auto_now=True)
 -    actif = models.BooleanField()
 +    
 +    is_responsable = models.BooleanField(default=False)
 +    famille_emploi = models.ForeignKey('FamilleEmploi', 
 +                            db_column='famille_emploi',
 +                            related_name='+')
  
 +    class Meta:
 +        ordering = ['nom']
 +        
      def __unicode__(self):
 -        return u'%s' % self.nom
 +        # TODO : gérer nom féminin
 +        return u'%s' % (self.nom)
  
  
  TYPE_PAIEMENT_CHOICES = (
@@@ -199,15 -168,12 +195,14 @@@ class Remuneration(models.Model)
      id = models.IntegerField(primary_key=True)
      dossier = models.ForeignKey('Dossier', db_column='dossier')
      type = models.ForeignKey('TypeRemuneration', db_column='type')
 -    type_revalorisation = models.ForeignKey('TypeRevalorisation', db_column='type_revalorisation', null=True, blank=True)
 +    type_revalorisation = models.ForeignKey('TypeRevalorisation', 
 +                            db_column='type_revalorisation', 
 +                            null=True, blank=True)
      montant = models.FloatField(null=True, blank=True)
-     devise = models.ForeignKey('Devise', to_field='code', db_column='devise', 
-                             null=True, blank=True)
+     devise = models.ForeignKey('Devise', to_field='id', db_column='devise', null=True, blank=True)
      date_effective = models.DateField(null=True, blank=True)
      pourcentage = models.IntegerField(null=True, blank=True)
 -    #Méta
 +    # Méta
      date_creation = models.DateField(auto_now_add=True)
      user_creation = models.IntegerField(null=True, blank=True) #User ou employé
      desactivation = models.NullBooleanField(null=True, blank=True) #
              devise = self.devise.code
          except:
              devise = "???"
-         return "%s %s" % (self.montant, devise)
+         return "%s %s (%s EUR - %s)" % (self.montant, devise, self.en_euros(), self.get_taux_historique(), )
+     def get_taux_historique(self):
+         tauxchange = TauxChange.objects.filter(devise=self.devise, annee=self.date_creation.year)[0]
+         return tauxchange
+     def en_euros(self):
+         return int(self.montant * self.get_taux_historique().taux)
  
  class FamilleEmploi(models.Model):
 -    #Identification
 +    # Identification
      id = models.IntegerField(primary_key=True)
      nom = models.CharField(max_length=255)
 -    #Méta
 +    # Méta
      actif = models.BooleanField()
  
  class TypePoste(models.Model):
@@@ -397,16 -367,17 +401,19 @@@ class Classement(models.Model)
          ordering = ['type','echelon','degre','coefficient']
  
  class TauxChange(models.Model):
 -    #Identification
 +    # Identification
      id = models.IntegerField(primary_key=True)
 -    devise = models.ForeignKey('Devise', db_column='devise')
 +    devise = models.ForeignKey('Devise', to_field='code', db_column='devise')
      annee = models.IntegerField()
      taux = models.FloatField()
 -    #Relations
 -    implantation = models.ForeignKey('datamaster_modeles.Implantation', db_column='implantation')
 +    # Relations
 +    implantation = models.ForeignKey('datamaster_modeles.Implantation', 
 +                            db_column='implantation',
 +                            related_name='taux_change')
  
+     def __unicode__(self):
+         return u"%s %s : %s" % (self.devise, self.annee, self.taux)
  class ValeurPointManager(models.Manager):
      """
      Manager qui travaille uniquement sur les valeurs du point de l'année en cours.
@@@ -64,7 -65,8 +65,8 @@@ INSTALLED_APPS = 
      'ajax_select',
      'south',
      'reversion',
 +    'project.rh', 
+     'auf.django.workflow',
 -    #'project.rh', 
      'project.rh_v1',
      'project.dae',
  )