1 # -=- encoding: utf-8 -=-
4 from django
.conf
import settings
5 from django
.core
.files
.storage
import FileSystemStorage
6 from django
.db
import models
8 from workflow
import PosteWorkflow
, DossierWorkflow
9 from workflow
import DOSSIER_ETAT_DRH_FINALISATION
, DOSSIER_ETAT_REGION_FINALISATION
, \
11 from managers
import DossierManager
, PosteManager
, PosteComparaisonManager
12 import datamaster_modeles
.models
as ref
13 from rh_v1
import models
as rh
17 HELP_TEXT_DATE
= "format: aaaa-mm-jj"
18 REGIME_TRAVAIL_DEFAULT
=100.00
19 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
=35.00
22 UPLOAD_STORAGE
= FileSystemStorage(settings
.PRIVE_MEDIA_ROOT
)
27 POSTE_APPEL_CHOICES
= (
28 ('interne', 'Interne'),
29 ('externe', 'Externe'),
32 ('N', u
"Nouveau poste"),
33 ('M', u
"Poste existant"),
34 ('E', u
"Évolution de poste"),
38 class DeviseException(Exception):
39 silent_variable_failure
= True
42 class Poste(PosteWorkflow
, models
.Model
):
44 type_intervention
= models
.CharField(max_length
=1, choices
=POSTE_ACTION
, default
='N')
47 id_rh
= models
.ForeignKey(rh
.Poste
, null
=True, related_name
='+',
49 verbose_name
="Mise à jour du poste")
50 nom
= models
.CharField(verbose_name
="Titre du poste", max_length
=255)
51 implantation
= models
.ForeignKey(ref
.Implantation
)
52 type_poste
= models
.ForeignKey(rh
.TypePoste
, null
=True, related_name
='+')
53 service
= models
.ForeignKey(rh
.Service
, related_name
='+',
54 verbose_name
=u
"Direction/Service/Pôle support")
55 responsable
= models
.ForeignKey(rh
.Poste
, related_name
='+',
56 verbose_name
="Poste du responsable")
59 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
60 default
=REGIME_TRAVAIL_DEFAULT
,
61 verbose_name
="Temps de travail",
62 help_text
="% du temps complet")
63 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
65 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
66 verbose_name
="Nb. heures par semaine")
69 local
= models
.BooleanField(verbose_name
="Local", default
=True, blank
=True)
70 expatrie
= models
.BooleanField(verbose_name
="Expatrié", default
=False,
72 mise_a_disposition
= models
.BooleanField(verbose_name
="Mise à disposition")
73 appel
= models
.CharField(max_length
=10, default
='interne',
74 verbose_name
="Appel à candidature",
75 choices
=POSTE_APPEL_CHOICES
)
78 classement_min
= models
.ForeignKey(rh
.Classement
, related_name
='+',
79 blank
=True, null
=True)
80 classement_max
= models
.ForeignKey(rh
.Classement
, related_name
='+',
81 blank
=True, null
=True)
82 valeur_point_min
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+',
83 blank
=True, null
=True)
84 valeur_point_max
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+',
85 blank
=True, null
=True)
86 devise_min
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
87 devise_max
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
88 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
90 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
91 indemn_expat_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
92 indemn_expat_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
93 indemn_fct_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
94 indemn_fct_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
95 charges_patronales_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
96 charges_patronales_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
97 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
98 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
100 # Comparatifs de rémunération
101 devise_comparaison
= models
.ForeignKey(rh
.Devise
, related_name
='+',
103 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
104 null
=True, blank
=True)
105 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
106 null
=True, blank
=True)
107 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
108 null
=True, blank
=True)
109 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
110 null
=True, blank
=True)
111 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
112 null
=True, blank
=True)
113 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
114 null
=True, blank
=True)
115 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
116 null
=True, blank
=True)
117 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
118 null
=True, blank
=True)
119 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
120 null
=True, blank
=True)
121 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
122 null
=True, blank
=True)
125 justification
= models
.TextField()
128 date_creation
= models
.DateTimeField(auto_now_add
=True)
129 date_modification
= models
.DateTimeField(auto_now
=True)
130 date_debut
= models
.DateField(verbose_name
="Date de début",
131 help_text
=HELP_TEXT_DATE
,
132 null
=True, blank
=True)
133 date_fin
= models
.DateField(null
=True, blank
=True,
134 verbose_name
="Date de fin",
135 help_text
=HELP_TEXT_DATE
)
136 actif
= models
.BooleanField(default
=True)
139 objects
= PosteManager()
143 Les vues sont montées selon une clef spéciale
144 pour identifier la provenance du poste.
145 Cette méthode fournit un moyen de reconstruire cette clef
146 afin de générer les URLs.
148 return "dae-%s" % self
.id
149 key
= property(_get_key
)
151 def get_dossiers(self
):
153 Liste tous les anciens dossiers liés à ce poste.
154 (Le nom de la relation sur le rh.Poste est mal choisi
155 poste1 au lieu de dossier1)
156 Note1 : seulement le dosssier principal fait l'objet de la recherche.
157 Note2 : les dossiers sont retournés du plus récent au plus vieux.
158 (Ce test est fait en fonction du id,
159 car les dates de création sont absentes de rh v1).
161 if self
.id_rh
is None:
163 postes
= [p
for p
in self
.id_rh
.poste1
.all()]
164 return sorted(postes
, key
=lambda poste
: poste
.id, reverse
=True)
166 def get_complement_nom(self
):
168 Inspecte les modèles rh v1 pour trouver dans le dernier dossier
169 un complément de titre de poste.
171 dossiers
= self
.get_dossiers()
172 if len(dossiers
) > 0:
173 nom
= dossiers
[0].complement1
178 def get_employe(self
):
180 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
182 dossiers
= self
.get_dossiers()
183 if len(dossiers
) > 0:
184 return dossiers
[0].employe
188 def get_default_devise(self
):
189 """Récupère la devise par défaut en fonction de l'implantation
193 implantation_devise
= rh
.TauxChange
.objects \
194 .filter(implantation
=self
.implantation
)[0].devise
196 implantation_devise
= 5 # EUR
197 return implantation_devise
199 #####################
200 # Classement de poste
201 #####################
203 def get_couts_minimum(self
):
204 return self
.salaire_min
+ self
.indemn_expat_min
+ self
.indemn_fct_min
+ self
.charges_patronales_min
+ self
.autre_min
206 def get_salaire_minimum(self
):
207 return self
.get_couts_minimum() - self
.charges_patronales_min
209 def get_taux_minimum(self
):
210 taux_changes
= rh
.TauxChange
.objects
.filter(devise
=self
.devise_min
).order_by('annee')
211 for t
in taux_changes
:
212 if t
.implantation
== self
.implantation
:
214 if len(taux_changes
) > 0:
215 return taux_changes
[0].taux
217 raise DeviseException('Taux indisponible pour la devise %s (%s)' % (self
.devise_min
, self
.implantation
))
219 def get_couts_minimum_euros(self
):
220 return float(self
.get_couts_minimum()) * self
.get_taux_minimum()
222 def get_salaire_minimum_euros(self
):
223 return float(self
.get_salaire_minimum()) * self
.get_taux_minimum()
225 def get_couts_maximum(self
):
226 return self
.salaire_max
+ self
.indemn_expat_max
+ self
.indemn_fct_max
+ self
.charges_patronales_max
+ self
.autre_max
228 def get_salaire_maximum(self
):
229 return self
.get_couts_maximum() - self
.charges_patronales_max
231 def get_taux_maximum(self
):
232 taux_changes
= rh
.TauxChange
.objects
.filter(devise
=self
.devise_max
).order_by('annee')
233 for t
in taux_changes
:
234 if t
.implantation
== self
.implantation
:
236 if len(taux_changes
) > 0:
237 return taux_changes
[0].taux
239 raise DeviseException('Taux indisponible pour la devise %s (%s)' % (self
.devise_max
, self
.implantation
))
241 def get_couts_maximum_euros(self
):
242 return float(self
.get_couts_maximum()) * self
.get_taux_maximum()
244 def get_salaire_maximum_euros(self
):
245 return float(self
.get_salaire_maximum()) * self
.get_taux_maximum()
247 def show_taux_minimum(self
):
249 return self
.get_taux_minimum()
250 except DeviseException
, e
:
253 def show_couts_minimum_euros(self
):
255 return self
.get_couts_minimum_euros()
256 except DeviseException
, e
:
259 def show_salaire_minimum_euros(self
):
261 return self
.get_salaire_minimum_euros()
262 except DeviseException
, e
:
265 def show_taux_maximum(self
):
267 return self
.get_taux_maximum()
268 except DeviseException
, e
:
271 def show_couts_maximum_euros(self
):
273 return self
.get_couts_maximum_euros()
274 except DeviseException
, e
:
277 def show_salaire_maximum_euros(self
):
279 return self
.get_salaire_maximum_euros()
280 except DeviseException
, e
:
284 ######################
285 # Comparaison de poste
286 ######################
288 def est_comparable(self
):
290 Si on a au moins une valeur de saisie dans les comparaisons, alors le poste
293 if self
.comp_universite_min
is None and \
294 self
.comp_fonctionpub_min
is None and \
295 self
.comp_locale_min
is None and \
296 self
.comp_ong_min
is None and \
297 self
.comp_autre_min
is None and \
298 self
.comp_universite_max
is None and \
299 self
.comp_fonctionpub_max
is None and \
300 self
.comp_locale_max
is None and \
301 self
.comp_ong_max
is None and \
302 self
.comp_autre_max
is None:
308 def get_taux_comparaison(self
):
310 return rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
, devise
=self
.devise_comparaison
)[0].taux
314 def get_comp_universite_min_euros(self
):
315 return (float)(self
.comp_universite_min
) * self
.get_taux_comparaison()
317 def get_comp_fonctionpub_min_euros(self
):
318 return (float)(self
.comp_fonctionpub_min
) * self
.get_taux_comparaison()
320 def get_comp_locale_min_euros(self
):
321 return (float)(self
.comp_locale_min
) * self
.get_taux_comparaison()
323 def get_comp_ong_min_euros(self
):
324 return (float)(self
.comp_ong_min
) * self
.get_taux_comparaison()
326 def get_comp_autre_min_euros(self
):
327 return (float)(self
.comp_autre_min
) * self
.get_taux_comparaison()
329 def get_comp_universite_max_euros(self
):
330 return (float)(self
.comp_universite_max
) * self
.get_taux_comparaison()
332 def get_comp_fonctionpub_max_euros(self
):
333 return (float)(self
.comp_fonctionpub_max
) * self
.get_taux_comparaison()
335 def get_comp_locale_max_euros(self
):
336 return (float)(self
.comp_locale_max
) * self
.get_taux_comparaison()
338 def get_comp_ong_max_euros(self
):
339 return (float)(self
.comp_ong_max
) * self
.get_taux_comparaison()
341 def get_comp_autre_max_euros(self
):
342 return (float)(self
.comp_autre_max
) * self
.get_taux_comparaison()
345 def __unicode__(self
):
347 Cette fonction est consommatrice SQL car elle cherche les dossiers
348 qui ont été liés à celui-ci.
350 complement_nom_poste
= self
.get_complement_nom()
351 if complement_nom_poste
is None:
352 complement_nom_poste
= ""
358 return u
'%s - %s (%s)' % data
361 # Tester l'enregistrement car les models.py sont importés au complet
362 if not reversion
.is_registered(Poste
):
363 reversion
.register(Poste
)
366 POSTE_FINANCEMENT_CHOICES
= (
367 ('A', 'A - Frais de personnel'),
368 ('B', 'B - Projet(s)-Titre(s)'),
372 class PosteFinancement(models
.Model
):
373 poste
= models
.ForeignKey('Poste', related_name
='financements')
374 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
375 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
376 help_text
="ex.: 33.33 % (décimale avec point)")
377 commentaire
= models
.TextField(
378 help_text
="Spécifiez la source de financement.")
383 def __unicode__(self
):
384 return u
"%s %.0f%% %s" % (self
.get_type_display(), self
.pourcentage
, self
.commentaire
)
387 class PostePiece(models
.Model
):
388 """Documents relatifs au Poste
389 Ex.: Description de poste
391 poste
= models
.ForeignKey("Poste")
392 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
393 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
='dae/poste', storage
=UPLOAD_STORAGE
)
395 class PosteComparaison(models
.Model
):
396 poste
= models
.ForeignKey('Poste', related_name
='comparaisons_internes')
397 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True, related_name
="+")
398 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+', verbose_name
='Statut', null
=True, blank
=True, )
399 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+', verbose_name
='Classement', null
=True, blank
=True, )
400 nom
= models
.CharField(verbose_name
="Poste", max_length
=255, null
=True, blank
=True)
401 montant
= models
.IntegerField(null
=True)
402 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+', null
=True, blank
=True)
404 objects
= PosteComparaisonManager()
406 def taux_devise(self
):
407 if self
.devise
.code
== 'EUR':
409 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
410 if len(liste_taux
) == 0:
411 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
413 return liste_taux
[0].taux
415 def montant_euros(self
):
416 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
421 # TODO : migration pour m -> M, f -> F
428 class Employe(models
.Model
):
431 id_rh
= models
.ForeignKey(rh
.Employe
, null
=True, related_name
='+',
432 verbose_name
='Employé')
433 nom
= models
.CharField(max_length
=255)
434 prenom
= models
.CharField(max_length
=255, verbose_name
='Prénom')
435 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
437 def __unicode__(self
):
438 return u
'%s %s' % (self
.prenom
, self
.nom
.upper())
443 STATUT_RESIDENCE_CHOICES
= (
445 ('expat', 'Expatrié'),
448 COMPTE_COMPTA_CHOICES
= (
454 class Dossier(DossierWorkflow
, models
.Model
):
457 employe
= models
.ForeignKey('Employe', related_name
='+', editable
=False)
458 poste
= models
.ForeignKey('Poste', related_name
='dossiers', editable
=False)
459 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+')
460 organisme_bstg
= models
.ForeignKey(rh
.OrganismeBstg
,
461 null
=True, blank
=True,
462 verbose_name
="Organisme",
463 help_text
="Si détaché (DET) ou mis à disposition (MAD), \
464 préciser l'organisme.",
466 organisme_bstg_autre
= models
.CharField(max_length
=255,
467 verbose_name
="Autre organisme",
468 help_text
="indiquer l'organisme ici s'il n'est pas dans la liste",
472 # Données antérieures de l'employé
473 statut_anterieur
= models
.ForeignKey(
474 rh
.Statut
, related_name
='+', null
=True, blank
=True,
475 verbose_name
='Statut antérieur')
476 classement_anterieur
= models
.ForeignKey(
477 rh
.Classement
, related_name
='+', null
=True, blank
=True,
478 verbose_name
='Classement précédent')
479 salaire_anterieur
= models
.DecimalField(
480 max_digits
=12, decimal_places
=2, null
=True, default
=None,
481 blank
=True, verbose_name
='Salaire précédent')
482 devise_anterieur
= models
.ForeignKey(rh
.Devise
, related_name
='+', null
=True, blank
=True)
483 type_contrat_anterieur
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+', null
=True, blank
=True, verbose_name
=u
'Type contrat antérieur', )
485 # Données du titulaire précédent
486 employe_anterieur
= models
.ForeignKey(
487 rh
.Employe
, related_name
='+', null
=True, blank
=True,
488 verbose_name
='Employé précédent')
489 statut_titulaire_anterieur
= models
.ForeignKey(
490 rh
.Statut
, related_name
='+', null
=True, blank
=True,
491 verbose_name
='Statut titulaire précédent')
492 classement_titulaire_anterieur
= models
.ForeignKey(
493 rh
.Classement
, related_name
='+', null
=True, blank
=True,
494 verbose_name
='Classement titulaire précédent')
495 salaire_titulaire_anterieur
= models
.DecimalField(
496 max_digits
=12, decimal_places
=2, default
=None, null
=True,
497 blank
=True, verbose_name
='Salaire titulaire précédent')
498 devise_titulaire_anterieur
= models
.ForeignKey(rh
.Devise
, related_name
='+', null
=True, blank
=True)
501 remplacement
= models
.BooleanField()
502 statut_residence
= models
.CharField(max_length
=10, default
='local',
503 verbose_name
="Statut",
504 choices
=STATUT_RESIDENCE_CHOICES
)
507 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+',
508 null
=True, blank
=True,
509 verbose_name
='Classement proposé')
510 salaire
= models
.DecimalField(max_digits
=12, decimal_places
=2,
511 verbose_name
='Salaire de base',
512 null
=True, default
=None)
513 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
514 regime_travail
= models
.DecimalField(max_digits
=12,
516 default
=REGIME_TRAVAIL_DEFAULT
,
517 verbose_name
="Régime de travail",
518 help_text
="% du temps complet")
519 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
521 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
522 verbose_name
="Nb. heures par semaine")
525 type_contrat
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
526 contrat_date_debut
= models
.DateField(help_text
="format: aaaa-mm-jj")
527 contrat_date_fin
= models
.DateField(null
=True, blank
=True,
528 help_text
="format: aaaa-mm-jj")
531 justif_nouveau_statut_label
= u
'Justifier le statut que ce type de poste nécessite (national, expatrié, màd ou détachement)'
532 justif_nouveau_statut
= models
.TextField(verbose_name
=justif_nouveau_statut_label
, null
=True, blank
=True)
533 justif_nouveau_tmp_remplacement_label
= u
"Si l'employé effectue un remplacement temporaire, préciser"
534 justif_nouveau_tmp_remplacement
= models
.TextField(verbose_name
=justif_nouveau_tmp_remplacement_label
, null
=True, blank
=True)
535 justif_nouveau_salaire_label
= u
"Si le salaire de l'employé ne correspond pas au classement du poste ou est différent du salaire antérieur, justifier "
536 justif_nouveau_salaire
= models
.TextField(verbose_name
=justif_nouveau_salaire_label
, null
=True, blank
=True)
537 justif_nouveau_commentaire_label
= u
"COMMENTAIRES ADDITIONNELS"
538 justif_nouveau_commentaire
= models
.TextField(verbose_name
=justif_nouveau_commentaire_label
, null
=True, blank
=True)
539 justif_rempl_type_contrat_label
= u
"Changement de type de contrat, ex : d'un CDD en CDI"
540 justif_rempl_type_contrat
= models
.TextField(verbose_name
=justif_rempl_type_contrat_label
, null
=True, blank
=True)
541 justif_rempl_statut_employe_label
= u
"Si le statut de l'employé a été modifié pour ce poste ; ex :national, expatrié, màd, détachement ? Si oui, justifier"
542 justif_rempl_statut_employe
= models
.TextField(verbose_name
=justif_rempl_statut_employe_label
, null
=True, blank
=True)
543 justif_rempl_evaluation_label
= u
"L'évaluation de l'employé est-elle favorable? Préciser"
544 justif_rempl_evaluation
= models
.TextField(verbose_name
=justif_rempl_evaluation_label
, null
=True, blank
=True)
545 justif_rempl_salaire_label
= u
"Si le salaire de l'employé est modifié et/ou ne correspond pas à son classement, justifier"
546 justif_rempl_salaire
= models
.TextField(verbose_name
=justif_rempl_salaire_label
, null
=True, blank
=True)
547 justif_rempl_commentaire_label
= u
"COMMENTAIRES ADDITIONNELS"
548 justif_rempl_commentaire
= models
.TextField(verbose_name
=justif_rempl_commentaire_label
, null
=True, blank
=True)
551 compte_compta
= models
.CharField(max_length
=10, default
='aucun',
552 verbose_name
=u
'Compte comptabilité',
553 choices
=COMPTE_COMPTA_CHOICES
)
554 compte_courriel
= models
.BooleanField()
557 date_creation
= models
.DateTimeField(auto_now_add
=True)
560 objects
= DossierManager()
562 def __unicode__(self
):
563 return u
'[%s] %s - %s' % (self
.poste
.implantation
, self
.poste
.nom
, self
.employe
)
565 def taux_devise(self
):
566 if self
.devise
.code
== 'EUR':
568 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.poste
.implantation
)
569 if len(liste_taux
) == 0:
570 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.poste
.implantation
))
572 return liste_taux
[0].taux
574 def get_salaire_anterieur_euros(self
):
575 if self
.devise_anterieur
.code
== 'EUR':
578 liste_taux
= self
.devise_anterieur
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.poste
.implantation
)
579 if len(liste_taux
) == 0:
580 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise_anterieur
, self
.poste
.implantation
))
581 tx
= liste_taux
[0].taux
582 return (float)(tx
) * (float)(self
.salaire_anterieur
)
584 def get_salaire_titulaire_anterieur_euros(self
):
585 if self
.devise_anterieur
.code
== 'EUR':
588 liste_taux
= self
.devise_titulaire_anterieur
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.poste
.implantation
)
589 if len(liste_taux
) == 0:
590 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise_titulaire_anterieur
, self
.poste
.implantation
))
591 tx
= liste_taux
[0].taux
592 return (float)(tx
) * (float)(self
.salaire_titulaire_anterieur
)
594 def get_salaire_euros(self
):
595 tx
= self
.taux_devise()
596 return (float)(tx
) * (float)(self
.salaire
)
598 def get_remunerations_brutes(self
):
602 4 Indemnité d'expatriation
603 5 Indemnité pour frais
604 6 Indemnité de logement
605 7 Indemnité de fonction
606 8 Indemnité de responsabilité
607 9 Indemnité de transport
608 10 Indemnité compensatrice
609 11 Indemnité de subsistance
610 12 Indemnité différentielle
611 13 Prime d'installation
614 16 Indemnité de départ
615 18 Prime de 13ième mois
618 ids
= [1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19]
619 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in ids
]
621 def get_charges_salariales(self
):
623 20 Charges salariales ?
626 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in ids
]
628 def get_total_charges_salariales(self
):
630 for r
in self
.get_charges_salariales():
631 total
+= r
.montant_euro()
634 def get_charges_patronales(self
):
636 17 Charges patronales
639 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in ids
]
641 def get_total_charges_patronales(self
):
643 for r
in self
.get_charges_patronales():
644 total
+= r
.montant_euro()
647 def get_salaire_brut(self
):
649 somme des rémuérations brutes
652 for r
in self
.get_remunerations_brutes():
653 total
+= r
.montant_euro()
656 def get_salaire_net(self
):
658 salaire brut - charges salariales
661 for r
in self
.get_charges_salariales():
662 total_charges
+= r
.montant_euro()
663 return self
.get_salaire_brut() - total_charges
665 def get_couts_auf(self
):
667 salaire net + charges patronales
670 for r
in self
.get_charges_patronales():
671 total_charges
+= r
.montant_euro()
672 return self
.get_salaire_net() + total_charges
674 def get_remunerations_tierces(self
):
678 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in (2, )]
680 def get_total_remunerations_tierces(self
):
682 for r
in self
.get_remunerations_tierces():
683 total
+= r
.montant_euro()
687 return self
.etat
in (DOSSIER_ETAT_REGION_FINALISATION
,
688 DOSSIER_ETAT_DRH_FINALISATION
,
689 DOSSIER_ETAT_FINALISE
)
692 # Tester l'enregistrement car les models.py sont importés au complet
693 if not reversion
.is_registered(Dossier
):
694 reversion
.register(Dossier
)
696 class DossierPiece(models
.Model
):
697 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
698 Ex.: Lettre de motivation.
700 dossier
= models
.ForeignKey("Dossier")
701 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
702 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
='dae/dossier', storage
=UPLOAD_STORAGE
)
705 class DossierComparaison(models
.Model
):
707 Photo d'une comparaison salariale au moment de l'embauche.
709 dossier
= models
.ForeignKey('Dossier', related_name
='comparaisons')
710 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+', verbose_name
='Statut', null
=True, blank
=True, )
711 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+', verbose_name
='Classement', null
=True, blank
=True, )
712 implantation
= models
.ForeignKey(ref
.Implantation
, related_name
='+', null
=True, blank
=True)
713 poste
= models
.CharField(max_length
=255, null
=True, blank
=True)
714 personne
= models
.CharField(max_length
=255, null
=True, blank
=True)
715 montant
= models
.IntegerField(null
=True)
716 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+', null
=True, blank
=True)
718 def taux_devise(self
):
719 if self
.devise
.code
== 'EUR':
721 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
722 if len(liste_taux
) == 0:
723 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
725 return liste_taux
[0].taux
727 def montant_euros(self
):
728 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
734 class Remuneration(models
.Model
):
736 dossier
= models
.ForeignKey('Dossier', db_column
='dossier')
737 type = models
.ForeignKey(rh
.TypeRemuneration
, db_column
='type',
739 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
741 devise
= models
.ForeignKey(rh
.Devise
, to_field
='code',
742 db_column
='devise', related_name
='+')
743 precision
= models
.CharField(max_length
=255, null
=True, blank
=True)
746 date_creation
= models
.DateField(auto_now_add
=True)
747 user_creation
= models
.IntegerField(null
=True, blank
=True) # TODO : user
749 def montant_mois(self
):
750 return round(self
.montant
/ 12, 2)
752 def taux_devise(self
):
753 if self
.devise
.code
== 'EUR':
755 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.dossier
.poste
.implantation
)
756 if len(liste_taux
) == 0:
757 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.dossier
.poste
.implantation
))
759 return liste_taux
[0].taux
761 def montant_euro(self
):
762 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
764 def montant_euro_mois(self
):
765 return round(self
.montant_euro() / 12, 2)
770 class Contrat(models
.Model
):
771 dossier
= models
.ForeignKey(Dossier
, related_name
='contrats')
772 type = models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
773 fichier
= models
.FileField(upload_to
='dae/contrats', storage
=UPLOAD_STORAGE
)