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
10 from managers
import DossierManager
, PosteManager
11 import datamaster_modeles
.models
as ref
12 from rh_v1
import models
as rh
16 HELP_TEXT_DATE
= "format: aaaa-mm-jj"
17 REGIME_TRAVAIL_DEFAULT
=100.00
18 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
=35.00
22 storage_prive
= FileSystemStorage(settings
.PRIVE_MEDIA_ROOT
,
23 base_url
=settings
.PRIVE_MEDIA_URL
)
25 def poste_piece_dispatch(instance
, filename
):
26 path
= "poste/%s/%s" % (instance
.poste_id
, filename
)
29 def dossier_piece_dispatch(instance
, filename
):
30 path
= "dossier/%s/%s" % (instance
.dossier_id
, filename
)
36 POSTE_APPEL_CHOICES
= (
37 ('interne', 'Interne'),
38 ('externe', 'Externe'),
41 ('N', u
"Nouveau poste"),
42 ('M', u
"Poste existant"),
43 ('E', u
"Évolution de poste"),
47 class DeviseException(Exception):
48 silent_variable_failure
= True
51 class Poste(PosteWorkflow
, models
.Model
):
53 type_intervention
= models
.CharField(max_length
=1, choices
=POSTE_ACTION
, default
='N')
56 id_rh
= models
.ForeignKey(rh
.Poste
, null
=True, related_name
='+',
58 verbose_name
="Mise à jour du poste")
59 nom
= models
.CharField(verbose_name
="Titre du poste", max_length
=255)
60 implantation
= models
.ForeignKey(ref
.Implantation
)
61 type_poste
= models
.ForeignKey(rh
.TypePoste
, null
=True, related_name
='+')
62 service
= models
.ForeignKey(rh
.Service
, related_name
='+',
63 verbose_name
=u
"Direction/Service/Pôle support")
64 responsable
= models
.ForeignKey(rh
.Poste
, related_name
='+',
65 verbose_name
="Poste du responsable")
68 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
69 default
=REGIME_TRAVAIL_DEFAULT
,
70 verbose_name
="Temps de travail",
71 help_text
="% du temps complet")
72 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
74 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
75 verbose_name
="Nb. heures par semaine")
78 local
= models
.BooleanField(verbose_name
="Local", default
=True, blank
=True)
79 expatrie
= models
.BooleanField(verbose_name
="Expatrié", default
=False,
81 mise_a_disposition
= models
.BooleanField(verbose_name
="Mise à disposition")
82 appel
= models
.CharField(max_length
=10, default
='interne',
83 verbose_name
="Appel à candidature",
84 choices
=POSTE_APPEL_CHOICES
)
87 classement_min
= models
.ForeignKey(rh
.Classement
, related_name
='+',
88 blank
=True, null
=True)
89 classement_max
= models
.ForeignKey(rh
.Classement
, related_name
='+',
90 blank
=True, null
=True)
91 valeur_point_min
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+',
92 blank
=True, null
=True)
93 valeur_point_max
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+',
94 blank
=True, null
=True)
95 devise_min
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
96 devise_max
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
97 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
99 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
100 indemn_expat_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
101 indemn_expat_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
102 indemn_fct_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
103 indemn_fct_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
104 charges_patronales_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
105 charges_patronales_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
106 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
107 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2, default
=0)
109 # Comparatifs de rémunération
110 devise_comparaison
= models
.ForeignKey(rh
.Devise
, related_name
='+',
112 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
113 null
=True, blank
=True)
114 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
115 null
=True, blank
=True)
116 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
117 null
=True, blank
=True)
118 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
119 null
=True, blank
=True)
120 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
121 null
=True, blank
=True)
122 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
123 null
=True, blank
=True)
124 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
125 null
=True, blank
=True)
126 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
127 null
=True, blank
=True)
128 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
129 null
=True, blank
=True)
130 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
131 null
=True, blank
=True)
134 justification
= models
.TextField()
137 date_creation
= models
.DateTimeField(auto_now_add
=True)
138 date_modification
= models
.DateTimeField(auto_now
=True)
139 date_debut
= models
.DateField(verbose_name
="Date de début",
140 help_text
=HELP_TEXT_DATE
,
141 null
=True, blank
=True)
142 date_fin
= models
.DateField(null
=True, blank
=True,
143 verbose_name
="Date de fin",
144 help_text
=HELP_TEXT_DATE
)
145 actif
= models
.BooleanField(default
=True)
148 objects
= PosteManager()
152 Les vues sont montées selon une clef spéciale
153 pour identifier la provenance du poste.
154 Cette méthode fournit un moyen de reconstruire cette clef
155 afin de générer les URLs.
157 return "dae-%s" % self
.id
158 key
= property(_get_key
)
160 def get_dossiers(self
):
162 Liste tous les anciens dossiers liés à ce poste.
163 (Le nom de la relation sur le rh.Poste est mal choisi
164 poste1 au lieu de dossier1)
165 Note1 : seulement le dosssier principal fait l'objet de la recherche.
166 Note2 : les dossiers sont retournés du plus récent au plus vieux.
167 (Ce test est fait en fonction du id,
168 car les dates de création sont absentes de rh v1).
170 if self
.id_rh
is None:
172 postes
= [p
for p
in self
.id_rh
.poste1
.all()]
173 return sorted(postes
, key
=lambda poste
: poste
.id, reverse
=True)
175 def get_complement_nom(self
):
177 Inspecte les modèles rh v1 pour trouver dans le dernier dossier
178 un complément de titre de poste.
180 dossiers
= self
.get_dossiers()
181 if len(dossiers
) > 0:
182 nom
= dossiers
[0].complement1
187 def get_employe(self
):
189 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
191 dossiers
= self
.get_dossiers()
192 if len(dossiers
) > 0:
193 return dossiers
[0].employe
197 def get_default_devise(self
):
198 """Récupère la devise par défaut en fonction de l'implantation
202 implantation_devise
= rh
.TauxChange
.objects \
203 .filter(implantation
=self
.implantation
)[0].devise
205 implantation_devise
= 5 # EUR
206 return implantation_devise
208 #####################
209 # Classement de poste
210 #####################
212 def get_couts_minimum(self
):
213 return (float)(self
.salaire_min
+ self
.indemn_expat_min
+ + self
.indemn_fct_min
+ self
.charges_patronales_min
+ self
.autre_min
)
215 def get_taux_minimum(self
):
216 if self
.devise_min
.code
== 'EUR':
218 liste_taux
= self
.devise_min
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
219 if len(liste_taux
) == 0:
220 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise_min
, self
.implantation
))
222 return liste_taux
[0].taux
224 def get_couts_minimum_euros(self
):
225 return self
.get_couts_minimum() * self
.get_taux_minimum()
227 def get_couts_maximum(self
):
228 return (float)(self
.salaire_max
+ self
.indemn_expat_max
+ + self
.indemn_fct_max
+ self
.charges_patronales_max
+ self
.autre_max
)
230 def get_taux_maximum(self
):
231 if self
.devise_max
.code
== 'EUR':
233 liste_taux
= self
.devise_max
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
234 if len(liste_taux
) == 0:
235 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise_max
, self
.implantation
))
237 return liste_taux
[0].taux
239 def get_couts_maximum_euros(self
):
240 return self
.get_couts_maximum() * self
.get_taux_maximum()
243 def show_taux_minimum(self
):
245 return self
.get_taux_minimum()
249 def show_couts_minimum_euros(self
):
251 return self
.get_couts_minimum_euros()
255 def show_taux_maximum(self
):
257 return self
.get_taux_maximum()
261 def show_couts_maximum_euros(self
):
263 return self
.get_couts_maximum_euros()
268 ######################
269 # Comparaison de poste
270 ######################
272 def est_comparable(self
):
274 Si on a au moins une valeur de saisie dans les comparaisons, alors le poste
277 if self
.comp_universite_min
is None and \
278 self
.comp_fonctionpub_min
is None and \
279 self
.comp_locale_min
is None and \
280 self
.comp_ong_min
is None and \
281 self
.comp_autre_min
is None and \
282 self
.comp_universite_max
is None and \
283 self
.comp_fonctionpub_max
is None and \
284 self
.comp_locale_max
is None and \
285 self
.comp_ong_max
is None and \
286 self
.comp_autre_max
is None:
292 def get_taux_comparaison(self
):
294 return rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
, devise
=self
.devise_comparaison
)[0].taux
298 def get_comp_universite_min_euros(self
):
299 return (float)(self
.comp_universite_min
) * self
.get_taux_comparaison()
301 def get_comp_fonctionpub_min_euros(self
):
302 return (float)(self
.comp_fonctionpub_min
) * self
.get_taux_comparaison()
304 def get_comp_locale_min_euros(self
):
305 return (float)(self
.comp_locale_min
) * self
.get_taux_comparaison()
307 def get_comp_ong_min_euros(self
):
308 return (float)(self
.comp_ong_min
) * self
.get_taux_comparaison()
310 def get_comp_autre_min_euros(self
):
311 return (float)(self
.comp_autre_min
) * self
.get_taux_comparaison()
313 def get_comp_universite_max_euros(self
):
314 return (float)(self
.comp_universite_max
) * self
.get_taux_comparaison()
316 def get_comp_fonctionpub_max_euros(self
):
317 return (float)(self
.comp_fonctionpub_max
) * self
.get_taux_comparaison()
319 def get_comp_locale_max_euros(self
):
320 return (float)(self
.comp_locale_max
) * self
.get_taux_comparaison()
322 def get_comp_ong_max_euros(self
):
323 return (float)(self
.comp_ong_max
) * self
.get_taux_comparaison()
325 def get_comp_autre_max_euros(self
):
326 return (float)(self
.comp_autre_max
) * self
.get_taux_comparaison()
329 def __unicode__(self
):
331 Cette fonction est consommatrice SQL car elle cherche les dossiers
332 qui ont été liés à celui-ci.
334 complement_nom_poste
= self
.get_complement_nom()
335 if complement_nom_poste
is None:
336 complement_nom_poste
= ""
342 return u
'%s - %s (%s)' % data
345 # Tester l'enregistrement car les models.py sont importés au complet
346 if not reversion
.is_registered(Poste
):
347 reversion
.register(Poste
)
350 POSTE_FINANCEMENT_CHOICES
= (
351 ('A', 'A - Frais de personnel'),
352 ('B', 'B - Projet(s)-Titre(s)'),
356 class PosteFinancement(models
.Model
):
357 poste
= models
.ForeignKey('Poste', related_name
='financements')
358 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
359 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
360 help_text
="ex.: 33.33 % (décimale avec point)")
361 commentaire
= models
.TextField(
362 help_text
="Spécifiez la source de financement.")
367 def __unicode__(self
):
368 return u
"%s %s %s" % (self
.get_type_display(), self
.pourcentage
, self
.commentaire
)
371 class PostePiece(models
.Model
):
372 """Documents relatifs au Poste
373 Ex.: Description de poste
375 poste
= models
.ForeignKey("Poste")
376 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
377 fichier
= models
.FileField(verbose_name
="Fichier",
378 upload_to
=poste_piece_dispatch
,
379 storage
=storage_prive
)
381 class PosteComparaison(models
.Model
):
382 poste
= models
.ForeignKey('Poste', related_name
='comparaisons_internes')
383 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+', verbose_name
='Statut', null
=True, blank
=True, )
384 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+', verbose_name
='Classement', null
=True, blank
=True, )
385 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True)
386 nom
= models
.CharField(verbose_name
="Poste", max_length
=255, null
=True, blank
=True)
387 montant
= models
.IntegerField(null
=True)
388 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+', null
=True, blank
=True)
390 def taux_devise(self
):
391 if self
.devise
.code
== 'EUR':
393 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
394 if len(liste_taux
) == 0:
395 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
397 return liste_taux
[0].taux
399 def montant_euros(self
):
400 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
405 # TODO : migration pour m -> M, f -> F
412 class Employe(models
.Model
):
415 id_rh
= models
.ForeignKey(rh
.Employe
, null
=True, related_name
='+',
416 verbose_name
='Employé')
417 nom
= models
.CharField(max_length
=255)
418 prenom
= models
.CharField(max_length
=255, verbose_name
='Prénom')
419 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
421 def __unicode__(self
):
422 return u
'%s %s' % (self
.prenom
, self
.nom
.upper())
427 STATUT_RESIDENCE_CHOICES
= (
429 ('expat', 'Expatrié'),
432 COMPTE_COMPTA_CHOICES
= (
438 class Dossier(DossierWorkflow
, models
.Model
):
441 employe
= models
.ForeignKey('Employe', related_name
='+', editable
=False)
442 poste
= models
.ForeignKey('Poste', related_name
='dossiers', editable
=False)
443 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+')
444 organisme_bstg
= models
.ForeignKey(rh
.OrganismeBstg
,
445 null
=True, blank
=True,
446 verbose_name
="Organisme",
447 help_text
="Si détaché (DET) ou mis à disposition (MAD), \
448 préciser l'organisme.",
450 organisme_bstg_autre
= models
.CharField(max_length
=255,
451 verbose_name
="Autre organisme",
452 help_text
="indiquer l'organisme ici s'il n'est pas dans la liste",
456 # Données antérieures de l'employé
457 statut_anterieur
= models
.ForeignKey(
458 rh
.Statut
, related_name
='+', null
=True, blank
=True,
459 verbose_name
='Statut antérieur')
460 classement_anterieur
= models
.ForeignKey(
461 rh
.Classement
, related_name
='+', null
=True, blank
=True,
462 verbose_name
='Classement précédent')
463 salaire_anterieur
= models
.DecimalField(
464 max_digits
=12, decimal_places
=2, null
=True, default
=None,
465 blank
=True, verbose_name
='Salaire précédent')
466 devise_anterieur
= models
.ForeignKey(rh
.Devise
, related_name
='+', null
=True, blank
=True)
467 type_contrat_anterieur
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+', null
=True, blank
=True, verbose_name
=u
'Type contrat antérieur', )
469 # Données du titulaire précédent
470 employe_anterieur
= models
.ForeignKey(
471 rh
.Employe
, related_name
='+', null
=True, blank
=True,
472 verbose_name
='Employé précédent')
473 statut_titulaire_anterieur
= models
.ForeignKey(
474 rh
.Statut
, related_name
='+', null
=True, blank
=True,
475 verbose_name
='Statut titulaire précédent')
476 classement_titulaire_anterieur
= models
.ForeignKey(
477 rh
.Classement
, related_name
='+', null
=True, blank
=True,
478 verbose_name
='Classement titulaire précédent')
479 salaire_titulaire_anterieur
= models
.DecimalField(
480 max_digits
=12, decimal_places
=2, default
=None, null
=True,
481 blank
=True, verbose_name
='Salaire titulaire précédent')
482 devise_titulaire_anterieur
= models
.ForeignKey(rh
.Devise
, related_name
='+', null
=True, blank
=True)
485 remplacement
= models
.BooleanField()
486 statut_residence
= models
.CharField(max_length
=10, default
='local',
487 verbose_name
="Statut",
488 choices
=STATUT_RESIDENCE_CHOICES
)
491 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+',
492 null
=True, blank
=True,
493 verbose_name
='Classement proposé')
494 salaire
= models
.DecimalField(max_digits
=12, decimal_places
=2,
495 verbose_name
='Salaire de base',
496 null
=True, default
=None)
497 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
498 regime_travail
= models
.DecimalField(max_digits
=12,
500 default
=REGIME_TRAVAIL_DEFAULT
,
501 verbose_name
="Régime de travail",
502 help_text
="% du temps complet")
503 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
505 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
506 verbose_name
="Nb. heures par semaine")
509 type_contrat
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
510 contrat_date_debut
= models
.DateField(help_text
="format: aaaa-mm-jj")
511 contrat_date_fin
= models
.DateField(null
=True, blank
=True,
512 help_text
="format: aaaa-mm-jj")
515 justif_nouveau_statut_label
= u
'Justifier le statut que ce type de poste nécessite (national, expatrié, màd ou détachement)'
516 justif_nouveau_statut
= models
.TextField(verbose_name
=justif_nouveau_statut_label
, null
=True, blank
=True)
517 justif_nouveau_tmp_remplacement_label
= u
"Si l'employé effectue un remplacement temporaire, préciser"
518 justif_nouveau_tmp_remplacement
= models
.TextField(verbose_name
=justif_nouveau_tmp_remplacement_label
, null
=True, blank
=True)
519 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 "
520 justif_nouveau_salaire
= models
.TextField(verbose_name
=justif_nouveau_salaire_label
, null
=True, blank
=True)
521 justif_nouveau_commentaire_label
= u
"COMMENTAIRES ADDITIONNELS"
522 justif_nouveau_commentaire
= models
.TextField(verbose_name
=justif_nouveau_commentaire_label
, null
=True, blank
=True)
523 justif_rempl_type_contrat_label
= u
"Changement de type de contrat, ex : d'un CDD en CDI"
524 justif_rempl_type_contrat
= models
.TextField(verbose_name
=justif_rempl_type_contrat_label
, null
=True, blank
=True)
525 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"
526 justif_rempl_statut_employe
= models
.TextField(verbose_name
=justif_rempl_statut_employe_label
, null
=True, blank
=True)
527 justif_rempl_evaluation_label
= u
"L'évaluation de l'employé est-elle favorable? Préciser"
528 justif_rempl_evaluation
= models
.TextField(verbose_name
=justif_rempl_evaluation_label
, null
=True, blank
=True)
529 justif_rempl_salaire_label
= u
"Si le salaire de l'employé est modifié et/ou ne correspond pas à son classement, justifier"
530 justif_rempl_salaire
= models
.TextField(verbose_name
=justif_rempl_salaire_label
, null
=True, blank
=True)
531 justif_rempl_commentaire_label
= u
"COMMENTAIRES ADDITIONNELS"
532 justif_rempl_commentaire
= models
.TextField(verbose_name
=justif_rempl_commentaire_label
, null
=True, blank
=True)
535 compte_compta
= models
.CharField(max_length
=10, default
='aucun',
536 verbose_name
=u
'Compte comptabilité',
537 choices
=COMPTE_COMPTA_CHOICES
)
538 compte_courriel
= models
.BooleanField()
541 date_creation
= models
.DateTimeField(auto_now_add
=True)
544 objects
= DossierManager()
546 def __unicode__(self
):
547 return u
'[%s] %s - %s' % (self
.poste
.implantation
, self
.poste
.nom
, self
.employe
)
549 def taux_devise(self
):
550 if self
.devise
.code
== 'EUR':
552 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.poste
.implantation
)
553 if len(liste_taux
) == 0:
554 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.poste
.implantation
))
556 return liste_taux
[0].taux
558 def get_salaire_anterieur_euros(self
):
559 if self
.devise_anterieur
.code
== 'EUR':
562 liste_taux
= self
.devise_anterieur
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.poste
.implantation
)
563 if len(liste_taux
) == 0:
564 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise_anterieur
, self
.poste
.implantation
))
565 tx
= liste_taux
[0].taux
566 return (float)(tx
) * (float)(self
.salaire_anterieur
)
568 def get_salaire_titulaire_anterieur_euros(self
):
569 if self
.devise_titulaire_anterieur
.code
== 'EUR':
572 liste_taux
= self
.devise_titulaire_anterieur
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.poste
.implantation
)
573 if len(liste_taux
) == 0:
574 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise_titulaire_anterieur
, self
.poste
.implantation
))
575 tx
= liste_taux
[0].taux
576 return (float)(tx
) * (float)(self
.salaire_titulaire_anterieur
)
578 def get_salaire_euros(self
):
579 tx
= self
.taux_devise()
580 return (float)(tx
) * (float)(self
.salaire
)
582 def get_remunerations_brutes(self
):
586 4 Indemnité d'expatriation
587 5 Indemnité pour frais
588 6 Indemnité de logement
589 7 Indemnité de fonction
590 8 Indemnité de responsabilité
591 9 Indemnité de transport
592 10 Indemnité compensatrice
593 11 Indemnité de subsistance
594 12 Indemnité différentielle
595 13 Prime d'installation
598 16 Indemnité de départ
599 18 Prime de 13ième mois
602 ids
= [1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19]
603 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in ids
]
605 def get_charges_salariales(self
):
607 20 Charges salariales ?
610 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in ids
]
612 def get_total_charges_salariales(self
):
614 for r
in self
.get_charges_salariales():
615 total
+= r
.montant_euro()
618 def get_charges_patronales(self
):
620 17 Charges patronales
623 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in ids
]
625 def get_total_charges_patronales(self
):
627 for r
in self
.get_charges_patronales():
628 total
+= r
.montant_euro()
631 def get_salaire_brut(self
):
633 somme des rémuérations brutes
636 for r
in self
.get_remunerations_brutes():
637 total
+= r
.montant_euro()
640 def get_salaire_net(self
):
642 salaire brut - charges salariales
645 for r
in self
.get_charges_salariales():
646 total_charges
+= r
.montant_euro()
647 return self
.get_salaire_brut() - total_charges
649 def get_couts_auf(self
):
651 salaire net + charges patronales
654 for r
in self
.get_charges_patronales():
655 total_charges
+= r
.montant_euro()
656 return self
.get_salaire_net() + total_charges
658 def get_remunerations_tierces(self
):
662 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in (2, )]
664 def get_total_remunerations_tierces(self
):
666 for r
in self
.get_remunerations_tierces():
667 total
+= r
.montant_euro()
671 # Tester l'enregistrement car les models.py sont importés au complet
672 if not reversion
.is_registered(Dossier
):
673 reversion
.register(Dossier
)
675 class DossierPiece(models
.Model
):
676 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
677 Ex.: Lettre de motivation.
679 dossier
= models
.ForeignKey("Dossier")
680 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
681 fichier
= models
.FileField(verbose_name
="Fichier",
682 upload_to
=dossier_piece_dispatch
,
683 storage
=storage_prive
)
686 class DossierComparaison(models
.Model
):
688 Photo d'une comparaison salariale au moment de l'embauche.
690 dossier
= models
.ForeignKey('Dossier', related_name
='comparaisons')
691 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+', verbose_name
='Statut', null
=True, blank
=True, )
692 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+', verbose_name
='Classement', null
=True, blank
=True, )
693 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True)
694 poste
= models
.CharField(max_length
=255, null
=True, blank
=True)
695 personne
= models
.CharField(max_length
=255, null
=True, blank
=True)
696 montant
= models
.IntegerField(null
=True)
697 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+', null
=True, blank
=True)
699 def taux_devise(self
):
700 if self
.devise
.code
== 'EUR':
702 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
703 if len(liste_taux
) == 0:
704 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
706 return liste_taux
[0].taux
708 def montant_euros(self
):
709 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
715 class Remuneration(models
.Model
):
717 dossier
= models
.ForeignKey('Dossier', db_column
='dossier')
718 type = models
.ForeignKey(rh
.TypeRemuneration
, db_column
='type',
720 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
722 devise
= models
.ForeignKey(rh
.Devise
, to_field
='code',
723 db_column
='devise', related_name
='+')
724 precision
= models
.CharField(max_length
=255, null
=True, blank
=True)
727 date_creation
= models
.DateField(auto_now_add
=True)
728 user_creation
= models
.IntegerField(null
=True, blank
=True) # TODO : user
730 def montant_mois(self
):
731 return round(self
.montant
/ 12, 2)
733 def taux_devise(self
):
734 if self
.devise
.code
== 'EUR':
736 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.dossier
.poste
.implantation
)
737 if len(liste_taux
) == 0:
738 raise DeviseException(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.dossier
.poste
.implantation
))
740 return liste_taux
[0].taux
742 def montant_euro(self
):
743 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
745 def montant_euro_mois(self
):
746 return round(self
.montant_euro() / 12, 2)