merge qbe in rh : conflits réglés
[auf_rh_dae.git] / project / dae / models.py
index f5e65d5..3d4d30e 100644 (file)
@@ -6,8 +6,10 @@ from django.core.files.storage import FileSystemStorage
 from django.db import models
 import reversion
 from workflow import PosteWorkflow, DossierWorkflow
-from workflow import DOSSIER_ETAT_DRH_FINALISATION
-from managers import DossierManager, PosteManager
+from workflow import DOSSIER_ETAT_DRH_FINALISATION, DOSSIER_ETAT_REGION_FINALISATION, \
+                     DOSSIER_ETAT_FINALISE
+from managers import DossierManager, PosteManager, PosteComparaisonManager, \
+                     DossierComparaisonManager
 import datamaster_modeles.models as ref
 from rh_v1 import models as rh
 
@@ -17,18 +19,8 @@ HELP_TEXT_DATE = "format: aaaa-mm-jj"
 REGIME_TRAVAIL_DEFAULT=100.00
 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT=35.00
 
-
 # Upload de fichiers
-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)
-    return path
-
-def dossier_piece_dispatch(instance, filename):
-    path = "dossier/%s/%s" % (instance.dossier_id, filename)
-    return path
+UPLOAD_STORAGE = FileSystemStorage(settings.PRIVE_MEDIA_ROOT)
 
 
 ### POSTE
@@ -44,6 +36,9 @@ POSTE_ACTION = (
 )
 
 
+class DeviseException(Exception):
+  silent_variable_failure = True
+
 
 class Poste(PosteWorkflow, models.Model):
 
@@ -63,8 +58,8 @@ class Poste(PosteWorkflow, models.Model):
 
     # Contrat
     regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
-                            default=REGIME_TRAVAIL_DEFAULT, 
-                            verbose_name=u"Temps de travail", 
+                            default=REGIME_TRAVAIL_DEFAULT,
+                            verbose_name=u"Temps de travail",
                             help_text="% du temps complet")
     regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
                             decimal_places=2,
@@ -73,7 +68,7 @@ class Poste(PosteWorkflow, models.Model):
 
     # Recrutement
     local = models.BooleanField(verbose_name=u"Local", default=True, blank=True)
-    expatrie = models.BooleanField(verbose_name=u"Expatrié", default=False, 
+    expatrie = models.BooleanField(verbose_name=u"Expatrié", default=False,
                             blank=True)
     mise_a_disposition = models.BooleanField(verbose_name=u"Mise à disposition")
     appel = models.CharField(max_length=10, default='interne',
@@ -85,9 +80,9 @@ class Poste(PosteWorkflow, models.Model):
                             blank=True, null=True)
     classement_max = models.ForeignKey(rh.Classement, related_name='+',
                             blank=True, null=True)
-    valeur_point_min = models.ForeignKey(rh.ValeurPoint, related_name='+', 
+    valeur_point_min = models.ForeignKey(rh.ValeurPoint, related_name='+',
                             blank=True, null=True)
-    valeur_point_max = models.ForeignKey(rh.ValeurPoint, related_name='+', 
+    valeur_point_max = models.ForeignKey(rh.ValeurPoint, related_name='+',
                             blank=True, null=True)
     devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
     devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
@@ -146,9 +141,9 @@ class Poste(PosteWorkflow, models.Model):
 
     def _get_key(self):
         """
-        Les vues sont montées selon une clef spéciale 
+        Les vues sont montées selon une clef spéciale
         pour identifier la provenance du poste.
-        Cette méthode fournit un moyen de reconstruire cette clef 
+        Cette méthode fournit un moyen de reconstruire cette clef
         afin de générer les URLs.
         """
         return "dae-%s" % self.id
@@ -157,21 +152,21 @@ class Poste(PosteWorkflow, models.Model):
     def get_dossiers(self):
         """
         Liste tous les anciens dossiers liés à ce poste.
-        (Le nom de la relation sur le rh.Poste est mal choisi 
+        (Le nom de la relation sur le rh.Poste est mal choisi
         poste1 au lieu de dossier1)
         Note1 : seulement le dosssier principal fait l'objet de la recherche.
-        Note2 : les dossiers sont retournés du plus récent au plus vieux. 
-        (Ce test est fait en fonction du id, 
+        Note2 : les dossiers sont retournés du plus récent au plus vieux.
+        (Ce test est fait en fonction du id,
         car les dates de création sont absentes de rh v1).
         """
         if self.id_rh is None:
             return []
         postes = [p for p in self.id_rh.poste1.all()]
         return sorted(postes, key=lambda poste: poste.id, reverse=True)
-            
+
     def get_complement_nom(self):
         """
-        Inspecte les modèles rh v1 pour trouver dans le dernier dossier 
+        Inspecte les modèles rh v1 pour trouver dans le dernier dossier
         un complément de titre de poste.
         """
         dossiers = self.get_dossiers()
@@ -192,7 +187,7 @@ class Poste(PosteWorkflow, models.Model):
             return None
 
     def get_default_devise(self):
-        """Récupère la devise par défaut en fonction de l'implantation 
+        """Récupère la devise par défaut en fonction de l'implantation
         (EUR autrement)
         """
         try:
@@ -207,60 +202,81 @@ class Poste(PosteWorkflow, models.Model):
     #####################
 
     def get_couts_minimum(self):
-        return (float)(self.salaire_min + self.indemn_expat_min + + self.indemn_fct_min +  self.charges_patronales_min + self.autre_min)
+        return self.salaire_min + self.indemn_expat_min + self.indemn_fct_min + self.charges_patronales_min + self.autre_min
+
+    def get_salaire_minimum(self):
+        return self.get_couts_minimum() - self.charges_patronales_min
 
     def get_taux_minimum(self):
-        taux_changes = rh.TauxChange.objects.filter(devise=self.devise_min).order_by('annee')
-        for t in taux_changes:
-            if t.implantation == self.implantation:
-                return t.taux
-        if len(taux_changes) > 0:
-            return taux_changes[0].taux
+        if self.devise_min.code == 'EUR':
+          return 1
+        liste_taux = self.devise_min.tauxchange_set.order_by('-annee').filter(implantation=self.implantation)
+        if len(liste_taux) == 0:
+            raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise_min, self.implantation))
         else:
-            raise Exception('Taux indisponible pour la devise %s (%s)' % (self.devise_min, self.implantation))
+            return liste_taux[0].taux
 
     def get_couts_minimum_euros(self):
-        return self.get_couts_minimum() * self.get_taux_minimum()
+        return float(self.get_couts_minimum()) * self.get_taux_minimum()
+
+    def get_salaire_minimum_euros(self):
+        return float(self.get_salaire_minimum()) * self.get_taux_minimum()
 
     def get_couts_maximum(self):
-        return (float)(self.salaire_max + self.indemn_expat_max + + self.indemn_fct_max +  self.charges_patronales_max + self.autre_max)
+        return self.salaire_max + self.indemn_expat_max + self.indemn_fct_max + self.charges_patronales_max + self.autre_max
+
+    def get_salaire_maximum(self):
+        return self.get_couts_maximum() - self.charges_patronales_max
 
     def get_taux_maximum(self):
-        taux_changes = rh.TauxChange.objects.filter(devise=self.devise_max).order_by('annee')
-        for t in taux_changes:
-            if t.implantation == self.implantation:
-                return t.taux
-        if len(taux_changes) > 0:
-            return taux_changes[0].taux
+        if self.devise_max.code == 'EUR':
+          return 1
+        liste_taux = self.devise_max.tauxchange_set.order_by('-annee').filter(implantation=self.implantation)
+        if len(liste_taux) == 0:
+            raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise_max, self.implantation))
         else:
-            raise Exception('Taux indisponible pour la devise %s (%s)' % (self.devise_max, self.implantation))
+            return liste_taux[0].taux
 
     def get_couts_maximum_euros(self):
-        return self.get_couts_maximum() * self.get_taux_maximum()
+        return float(self.get_couts_maximum()) * self.get_taux_maximum()
 
+    def get_salaire_maximum_euros(self):
+        return float(self.get_salaire_maximum()) * self.get_taux_maximum()
 
     def show_taux_minimum(self):
         try:
             return self.get_taux_minimum()
-        except Exception, e:
+        except DeviseException, e:
             return e
 
     def show_couts_minimum_euros(self):
         try:
             return self.get_couts_minimum_euros()
-        except Exception, e:
+        except DeviseException, e:
+            return e
+
+    def show_salaire_minimum_euros(self):
+        try:
+            return self.get_salaire_minimum_euros()
+        except DeviseException, e:
             return e
 
     def show_taux_maximum(self):
         try:
             return self.get_taux_maximum()
-        except Exception, e:
+        except DeviseException, e:
             return e
 
     def show_couts_maximum_euros(self):
         try:
             return self.get_couts_maximum_euros()
-        except Exception, e:
+        except DeviseException, e:
+            return e
+
+    def show_salaire_maximum_euros(self):
+        try:
+            return self.get_salaire_maximum_euros()
+        except DeviseException, e:
             return e
 
 
@@ -286,7 +302,7 @@ class Poste(PosteWorkflow, models.Model):
             return False
         else:
             return True
-    
+
 
     def get_taux_comparaison(self):
         try:
@@ -327,7 +343,7 @@ class Poste(PosteWorkflow, models.Model):
 
     def __unicode__(self):
         """
-        Cette fonction est consommatrice SQL car elle cherche les dossiers 
+        Cette fonction est consommatrice SQL car elle cherche les dossiers
         qui ont été liés à celui-ci.
         """
         complement_nom_poste = self.get_complement_nom()
@@ -364,7 +380,7 @@ class PosteFinancement(models.Model):
         ordering = ['type']
 
     def __unicode__(self):
-        return u"%s %s %s" % (self.get_type_display(), self.pourcentage, self.commentaire)
+        return u"%s %.0f%% %s" % (self.get_type_display(), self.pourcentage, self.commentaire)
 
 
 class PostePiece(models.Model):
@@ -374,8 +390,8 @@ class PostePiece(models.Model):
     poste = models.ForeignKey("Poste")
     nom = models.CharField(verbose_name=u"Nom", max_length=255)
     fichier = models.FileField(verbose_name=u"Fichier", 
-                            upload_to=poste_piece_dispatch, 
-                            storage=storage_prive)
+                    upload_to='dae/poste', 
+                    storage=UPLOAD_STORAGE)
 
 class PosteComparaison(models.Model):
     poste = models.ForeignKey('Poste', related_name='comparaisons_internes')
@@ -386,10 +402,14 @@ class PosteComparaison(models.Model):
     montant = models.IntegerField(null=True)
     devise = models.ForeignKey(rh.Devise, default=5, related_name='+', null=True, blank=True)
 
+    objects = PosteComparaisonManager()
+
     def taux_devise(self):
+        if self.devise.code == 'EUR':
+          return 1
         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))
+            raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.implantation))
         else:
             return liste_taux[0].taux
 
@@ -409,7 +429,7 @@ GENRE_CHOICES = (
 class Employe(models.Model):
 
     # Modèle existant
-    id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+', 
+    id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
                             verbose_name=u'Employé')
     nom = models.CharField(max_length=255)
     prenom = models.CharField(max_length=255, verbose_name=u'Prénom')
@@ -438,9 +458,9 @@ class Dossier(DossierWorkflow, models.Model):
     employe = models.ForeignKey('Employe', related_name='+', editable=False)
     poste = models.ForeignKey('Poste', related_name='dossiers', editable=False)
     statut = models.ForeignKey(rh.Statut, related_name='+')
-    organisme_bstg = models.ForeignKey(rh.OrganismeBstg, 
+    organisme_bstg = models.ForeignKey(rh.OrganismeBstg,
             null=True, blank=True,
-            verbose_name=u"Organisme", 
+            verbose_name=u"Organisme",
             help_text="Si détaché (DET) ou mis à disposition (MAD), \
                     préciser l'organisme.",
             related_name='+')
@@ -451,7 +471,6 @@ class Dossier(DossierWorkflow, models.Model):
         blank=True,)
 
     # Données antérieures de l'employé
-    # la devise??
     statut_anterieur = models.ForeignKey(
             rh.Statut, related_name='+', null=True, blank=True,
             verbose_name=u'Statut antérieur')
@@ -460,7 +479,12 @@ class Dossier(DossierWorkflow, models.Model):
             verbose_name=u'Classement précédent')
     salaire_anterieur = models.DecimalField(
             max_digits=12, decimal_places=2, null=True, default=None,
-            blank=True, verbose_name=u'Salaire précédent')
+            blank=True, verbose_name='Salaire précédent')
+    devise_anterieur = models.ForeignKey(rh.Devise, related_name='+', 
+            null=True, blank=True)
+    type_contrat_anterieur = models.ForeignKey(rh.TypeContrat, 
+            related_name='+', null=True, blank=True, 
+            verbose_name=u'Type contrat antérieur', )
 
     # Données du titulaire précédent
     employe_anterieur = models.ForeignKey(
@@ -474,12 +498,13 @@ class Dossier(DossierWorkflow, models.Model):
             verbose_name=u'Classement titulaire précédent')
     salaire_titulaire_anterieur = models.DecimalField(
             max_digits=12, decimal_places=2, default=None, null=True,
-            blank=True, verbose_name=u'Salaire titulaire précédent')
+            blank=True, verbose_name='Salaire titulaire précédent')
+    devise_titulaire_anterieur = models.ForeignKey(rh.Devise, related_name='+', null=True, blank=True)
 
     # Recrutement
     remplacement = models.BooleanField()
-    statut_residence = models.CharField(max_length=10, default='local', 
-                            verbose_name=u"Statut",
+    statut_residence = models.CharField(max_length=10, default='local',
+                            verbose_name="Statut",
                             choices=STATUT_RESIDENCE_CHOICES)
 
     # Rémunération
@@ -490,13 +515,13 @@ class Dossier(DossierWorkflow, models.Model):
                             verbose_name=u'Salaire de base',
                             null=True, default=None)
     devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
-    regime_travail = models.DecimalField(max_digits=12, 
+    regime_travail = models.DecimalField(max_digits=12,
                             decimal_places=2,
                             default=REGIME_TRAVAIL_DEFAULT,
                             verbose_name=u"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,
                             default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
                             verbose_name=u"Nb. heures par semaine")
 
@@ -531,21 +556,47 @@ class Dossier(DossierWorkflow, models.Model):
                                     verbose_name=u'Compte comptabilité',
                                     choices=COMPTE_COMPTA_CHOICES)
     compte_courriel = models.BooleanField()
-    
+
     # Méta
     date_creation = models.DateTimeField(auto_now_add=True)
+
     # Managers
     objects = DossierManager()
-   
+
     def __unicode__(self):
         return u'[%s] %s - %s' % (self.poste.implantation, self.poste.nom, self.employe)
 
+    def taux_devise(self):
+        if self.devise.code == 'EUR':
+          return 1
+        liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.poste.implantation)
+        if len(liste_taux) == 0:
+            raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.poste.implantation))
+        else:
+            return liste_taux[0].taux
+
+    def get_salaire_anterieur_euros(self):
+        if self.devise_anterieur.code == 'EUR':
+          tx = 1
+        else:
+            liste_taux = self.devise_anterieur.tauxchange_set.order_by('-annee').filter(implantation=self.poste.implantation)
+            if len(liste_taux) == 0:
+                raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise_anterieur, self.poste.implantation))
+            tx = liste_taux[0].taux
+        return (float)(tx) * (float)(self.salaire_anterieur)
+
+    def get_salaire_titulaire_anterieur_euros(self):
+        if self.devise_titulaire_anterieur.code == 'EUR':
+          tx = 1
+        else:
+            liste_taux = self.devise_titulaire_anterieur.tauxchange_set.order_by('-annee').filter(implantation=self.poste.implantation)
+            if len(liste_taux) == 0:
+                raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise_titulaire_anterieur, self.poste.implantation))
+            tx = liste_taux[0].taux
+        return (float)(tx) * (float)(self.salaire_titulaire_anterieur)
+
     def get_salaire_euros(self):
-        try:
-            tx = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.poste.implantation)[0].taux
-        except:
-            tx = 0
+        tx = self.taux_devise()
         return (float)(tx) * (float)(self.salaire)
 
     def get_remunerations_brutes(self):
@@ -632,10 +683,15 @@ class Dossier(DossierWorkflow, models.Model):
 
     def get_total_remunerations_tierces(self):
         total = 0.0
-        for r in self.get_remunerations_tierces():    
+        for r in self.get_remunerations_tierces():
             total += r.montant_euro()
         return total
 
+    def valide(self):
+        return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
+                             DOSSIER_ETAT_DRH_FINALISATION,
+                             DOSSIER_ETAT_FINALISE)
+
 
 # Tester l'enregistrement car les models.py sont importés au complet
 if not reversion.is_registered(Dossier):
@@ -646,10 +702,10 @@ class DossierPiece(models.Model):
     Ex.: Lettre de motivation.
     """
     dossier = models.ForeignKey("Dossier")
-    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)
+    nom = models.CharField(verbose_name="Nom", max_length=255)
+    fichier = models.FileField(verbose_name="Fichier", 
+            upload_to='dae/dossier', 
+            storage=UPLOAD_STORAGE)
 
 
 class DossierComparaison(models.Model):
@@ -657,18 +713,25 @@ class DossierComparaison(models.Model):
     Photo d'une comparaison salariale au moment de l'embauche.
     """
     dossier = models.ForeignKey('Dossier', related_name='comparaisons')
-    statut = models.ForeignKey(rh.Statut, related_name='+', verbose_name=u'Statut', null=True, blank=True, )
-    classement = models.ForeignKey(rh.Classement, related_name='+', verbose_name=u'Classement', null=True, blank=True, )
-    implantation = models.ForeignKey(ref.Implantation, null=True, blank=True)
+    statut = models.ForeignKey(rh.Statut, related_name='+', 
+            verbose_name='Statut', null=True, blank=True, )
+    classement = models.ForeignKey(rh.Classement, related_name='+', 
+            verbose_name='Classement', null=True, blank=True, )
+    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(rh.Devise, default=5, related_name='+', null=True, blank=True)
 
+    objects = DossierComparaisonManager()
+
     def taux_devise(self):
-        liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.dossier.poste.implantation)
+        if self.devise.code == 'EUR':
+          return 1
+        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.dossier.poste.implantation))
+            raise DeviseException(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.implantation))
         else:
             return liste_taux[0].taux
 
@@ -698,9 +761,11 @@ class Remuneration(models.Model):
         return round(self.montant / 12, 2)
 
     def taux_devise(self):
+        if self.devise.code == 'EUR':
+          return 1
         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))
+            raise DeviseException(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
 
@@ -709,3 +774,12 @@ class Remuneration(models.Model):
 
     def montant_euro_mois(self):
         return round(self.montant_euro() / 12, 2)
+
+
+### CONTRATS
+
+class Contrat(models.Model):
+    dossier = models.ForeignKey(Dossier, related_name='contrats')
+    type = models.ForeignKey(rh.TypeContrat, related_name='+')
+    fichier = models.FileField(upload_to='dae/contrats', storage=UPLOAD_STORAGE)
+