fix #1473
[auf_rh_dae.git] / project / dae / models.py
CommitLineData
bd28238f 1# -=- encoding: utf-8 -=-
3f3cf5f3 2
36341125 3import os
5633fa41 4from django.conf import settings
36341125 5from django.core.files.storage import FileSystemStorage
a9c281dd
OL
6from django.db import models
7import reversion
afc204bf 8from workflow import PosteWorkflow, DossierWorkflow
515124ec 9from workflow import DOSSIER_ETAT_DRH_FINALISATION
f258e4e7 10from managers import DossierManager, PosteManager
bd28238f 11import datamaster_modeles.models as ref
a9c281dd 12from rh_v1 import models as rh
bd28238f 13
bd28238f 14
2d4d2fcf 15# Constantes
16HELP_TEXT_DATE = "format: aaaa-mm-jj"
17REGIME_TRAVAIL_DEFAULT=100.00
18REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT=35.00
bd28238f 19
bd28238f 20
d766bf2c 21# Upload de fichiers
2d4d2fcf 22storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
23 base_url=settings.PRIVE_MEDIA_URL)
36341125
OL
24
25def poste_piece_dispatch(instance, filename):
fe13cd48 26 path = "poste/%s/%s" % (instance.poste_id, filename)
36341125
OL
27 return path
28
d766bf2c 29def dossier_piece_dispatch(instance, filename):
fe13cd48 30 path = "dossier/%s/%s" % (instance.dossier_id, filename)
d766bf2c
OL
31 return path
32
36341125 33
2d4d2fcf 34### POSTE
35
36POSTE_APPEL_CHOICES = (
37 ('interne', 'Interne'),
38 ('externe', 'Externe'),
39)
36341125 40
1c7d67ce 41
8fa94e8b 42class Poste(PosteWorkflow, models.Model):
bd28238f 43 # Modèle existant
5d680e84 44 id_rh = models.ForeignKey(rh.Poste, null=True, related_name='+',
2d4d2fcf 45 editable=False,
46 verbose_name="Mise à jour du poste")
ce110fb9 47 nom = models.CharField(verbose_name="Titre du poste", max_length=255)
5d680e84
NC
48 implantation = models.ForeignKey(ref.Implantation)
49 type_poste = models.ForeignKey(rh.TypePoste, null=True, related_name='+')
98d51b59 50 service = models.ForeignKey(rh.Service, related_name='+',
2d4d2fcf 51 verbose_name=u"Direction/Service/Pôle support")
98d51b59 52 responsable = models.ForeignKey(rh.Poste, related_name='+',
2d4d2fcf 53 verbose_name="Poste du responsable")
9a85768a 54
2d4d2fcf 55 # Contrat
5d680e84 56 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
2d4d2fcf 57 default=REGIME_TRAVAIL_DEFAULT,
58 verbose_name="Temps de travail",
59 help_text="% du temps complet")
5d680e84 60 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
2d4d2fcf 61 decimal_places=2,
62 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
63 verbose_name="Nb. heures par semaine")
bd28238f
NC
64
65 # Recrutement
154677c3 66 local = models.BooleanField(verbose_name="Local", default=True, blank=True)
2d4d2fcf 67 expatrie = models.BooleanField(verbose_name="Expatrié", default=False,
68 blank=True)
a3508c67 69 mise_a_disposition = models.BooleanField(verbose_name="Mise à disposition")
98d51b59 70 appel = models.CharField(max_length=10, default='interne',
2d4d2fcf 71 verbose_name="Appel à candidature",
72 choices=POSTE_APPEL_CHOICES)
bd28238f
NC
73
74 # Rémunération
2d4d2fcf 75 classement_min = models.ForeignKey(rh.Classement, related_name='+',
76 blank=True, null=True)
77 classement_max = models.ForeignKey(rh.Classement, related_name='+',
78 blank=True, null=True)
79 valeur_point_min = models.ForeignKey(rh.ValeurPoint, related_name='+',
80 blank=True, null=True)
81 valeur_point_max = models.ForeignKey(rh.ValeurPoint, related_name='+',
82 blank=True, null=True)
3d627bfd 83 devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
84 devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
5d680e84
NC
85 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
86 default=0)
5f61bccb
OL
87 salaire_max = models.DecimalField(max_digits=12, decimal_places=2, default=0)
88 indemn_expat_min = models.DecimalField(max_digits=12, decimal_places=2, default=0)
89 indemn_expat_max = models.DecimalField(max_digits=12, decimal_places=2, default=0)
90 indemn_fct_min = models.DecimalField(max_digits=12, decimal_places=2, default=0)
91 indemn_fct_max = models.DecimalField(max_digits=12, decimal_places=2, default=0)
92 charges_patronales_min = models.DecimalField(max_digits=12, decimal_places=2, default=0)
93 charges_patronales_max = models.DecimalField(max_digits=12, decimal_places=2, default=0)
94 autre_min = models.DecimalField(max_digits=12, decimal_places=2, default=0)
95 autre_max = models.DecimalField(max_digits=12, decimal_places=2, default=0)
5d680e84
NC
96
97 # Comparatifs de rémunération
98 devise_comparaison = models.ForeignKey(rh.Devise, related_name='+',
a3508c67 99 default=5)
5d680e84
NC
100 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
101 null=True, blank=True)
102 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
103 null=True, blank=True)
104 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
105 null=True, blank=True)
106 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
107 null=True, blank=True)
108 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
109 null=True, blank=True)
110 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
111 null=True, blank=True)
112 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
113 null=True, blank=True)
114 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
115 null=True, blank=True)
116 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
117 null=True, blank=True)
118 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
119 null=True, blank=True)
bd28238f 120
2e092e0c
OL
121 # Justification
122 justification = models.TextField()
123
bd28238f 124 # Méta
5d680e84
NC
125 date_creation = models.DateTimeField(auto_now_add=True)
126 date_modification = models.DateTimeField(auto_now=True)
98d51b59 127 date_debut = models.DateField(verbose_name="Date de début",
2d4d2fcf 128 help_text=HELP_TEXT_DATE)
9fb2ccd9 129 date_fin = models.DateField(null=True, blank=True,
130 verbose_name="Date de fin",
2d4d2fcf 131 help_text=HELP_TEXT_DATE)
bd28238f 132 actif = models.BooleanField(default=True)
515124ec 133 pourvu = models.BooleanField(default=False)
bd28238f 134
1c7d67ce
OL
135 # Managers
136 objects = PosteManager()
137
03858ba5
OL
138 def _get_key(self):
139 """
2d4d2fcf 140 Les vues sont montées selon une clef spéciale
141 pour identifier la provenance du poste.
142 Cette méthode fournit un moyen de reconstruire cette clef
143 afin de générer les URLs.
03858ba5
OL
144 """
145 return "dae-%s" % self.id
146 key = property(_get_key)
147
f3333b0e
OL
148 def get_dossiers(self):
149 """
150 Liste tous les anciens dossiers liés à ce poste.
2d4d2fcf 151 (Le nom de la relation sur le rh.Poste est mal choisi
152 poste1 au lieu de dossier1)
f3333b0e 153 Note1 : seulement le dosssier principal fait l'objet de la recherche.
2d4d2fcf 154 Note2 : les dossiers sont retournés du plus récent au plus vieux.
155 (Ce test est fait en fonction du id,
156 car les dates de création sont absentes de rh v1).
f3333b0e
OL
157 """
158 if self.id_rh is None:
159 return []
160 postes = [p for p in self.id_rh.poste1.all()]
161 return sorted(postes, key=lambda poste: poste.id, reverse=True)
162
163 def get_complement_nom(self):
164 """
2d4d2fcf 165 Inspecte les modèles rh v1 pour trouver dans le dernier dossier
166 un complément de titre de poste.
f3333b0e
OL
167 """
168 dossiers = self.get_dossiers()
169 if len(dossiers) > 0:
170 nom = dossiers[0].complement1
171 else:
172 nom = ""
a9c281dd 173 return nom
f3333b0e
OL
174
175 def get_employe(self):
176 """
177 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
178 """
179 dossiers = self.get_dossiers()
180 if len(dossiers) > 0:
181 return dossiers[0].employe
182 else:
183 return None
184
179f6b49 185 def get_default_devise(self):
2d4d2fcf 186 """Récupère la devise par défaut en fonction de l'implantation
187 (EUR autrement)
188 """
179f6b49 189 try:
6e4600ef 190 implantation_devise = rh.TauxChange.objects \
191 .filter(implantation=self.implantation)[0].devise
179f6b49
OL
192 except:
193 implantation_devise = 5 # EUR
194 return implantation_devise
195
c0413a6f
OL
196 #####################
197 # Classement de poste
198 #####################
199
200 def get_couts_minimum(self):
5f61bccb 201 return (float)(self.salaire_min + self.indemn_expat_min + + self.indemn_fct_min + self.charges_patronales_min + self.autre_min)
c0413a6f
OL
202
203 def get_taux_minimum(self):
b6825282
OL
204 taux_changes = rh.TauxChange.objects.filter(devise=self.devise_min).order_by('annee')
205 for t in taux_changes:
206 if t.implantation == self.implantation:
207 return t.taux
208 if len(taux_changes) > 0:
209 return taux_changes[0].taux
210 else:
12c7f8a7 211 raise Exception('Taux indisponible pour la devise %s (%s)' % (self.devise_min, self.implantation))
c0413a6f
OL
212
213 def get_couts_minimum_euros(self):
214 return self.get_couts_minimum() * self.get_taux_minimum()
215
216 def get_couts_maximum(self):
5f61bccb 217 return (float)(self.salaire_max + self.indemn_expat_max + + self.indemn_fct_max + self.charges_patronales_max + self.autre_max)
c0413a6f
OL
218
219 def get_taux_maximum(self):
b6825282
OL
220 taux_changes = rh.TauxChange.objects.filter(devise=self.devise_max).order_by('annee')
221 for t in taux_changes:
222 if t.implantation == self.implantation:
223 return t.taux
224 if len(taux_changes) > 0:
225 return taux_changes[0].taux
226 else:
12c7f8a7 227 raise Exception('Taux indisponible pour la devise %s (%s)' % (self.devise_max, self.implantation))
c0413a6f
OL
228
229 def get_couts_maximum_euros(self):
230 return self.get_couts_maximum() * self.get_taux_maximum()
231
12c7f8a7
OL
232
233 def show_taux_minimum(self):
234 try:
235 return self.get_taux_minimum()
236 except Exception, e:
237 return e
238
239 def show_couts_minimum_euros(self):
240 try:
241 return self.get_couts_minimum_euros()
242 except Exception, e:
243 return e
244
245 def show_taux_maximum(self):
246 try:
247 return self.get_taux_maximum()
248 except Exception, e:
249 return e
250
251 def show_couts_maximum_euros(self):
252 try:
253 return self.get_couts_maximum_euros()
254 except Exception, e:
255 return e
256
257
c0413a6f
OL
258 ######################
259 # Comparaison de poste
260 ######################
a3fee9c5
OL
261
262 def est_comparable(self):
263 """
264 Si on a au moins une valeur de saisie dans les comparaisons, alors le poste
265 est comparable.
266 """
267 if self.comp_universite_min is None and \
268 self.comp_fonctionpub_min is None and \
269 self.comp_locale_min is None and \
270 self.comp_ong_min is None and \
271 self.comp_autre_min is None and \
272 self.comp_universite_max is None and \
273 self.comp_fonctionpub_max is None and \
274 self.comp_locale_max is None and \
275 self.comp_ong_max is None and \
276 self.comp_autre_max is None:
277 return False
278 else:
279 return True
c0413a6f 280
a3fee9c5 281
c0413a6f
OL
282 def get_taux_comparaison(self):
283 try:
284 return rh.TauxChange.objects.filter(implantation=self.implantation, devise=self.devise_comparaison)[0].taux
285 except:
286 return 1
287
288 def get_comp_universite_min_euros(self):
289 return (float)(self.comp_universite_min) * self.get_taux_comparaison()
290
291 def get_comp_fonctionpub_min_euros(self):
292 return (float)(self.comp_fonctionpub_min) * self.get_taux_comparaison()
293
294 def get_comp_locale_min_euros(self):
295 return (float)(self.comp_locale_min) * self.get_taux_comparaison()
296
297 def get_comp_ong_min_euros(self):
298 return (float)(self.comp_ong_min) * self.get_taux_comparaison()
299
300 def get_comp_autre_min_euros(self):
301 return (float)(self.comp_autre_min) * self.get_taux_comparaison()
302
303 def get_comp_universite_max_euros(self):
304 return (float)(self.comp_universite_max) * self.get_taux_comparaison()
305
306 def get_comp_fonctionpub_max_euros(self):
307 return (float)(self.comp_fonctionpub_max) * self.get_taux_comparaison()
308
309 def get_comp_locale_max_euros(self):
310 return (float)(self.comp_locale_max) * self.get_taux_comparaison()
311
312 def get_comp_ong_max_euros(self):
313 return (float)(self.comp_ong_max) * self.get_taux_comparaison()
314
315 def get_comp_autre_max_euros(self):
316 return (float)(self.comp_autre_max) * self.get_taux_comparaison()
317
a9e52624 318
5d680e84 319 def __unicode__(self):
f3333b0e 320 """
2d4d2fcf 321 Cette fonction est consommatrice SQL car elle cherche les dossiers
322 qui ont été liés à celui-ci.
f3333b0e
OL
323 """
324 complement_nom_poste = self.get_complement_nom()
c7a4aa2b
OL
325 if complement_nom_poste is None:
326 complement_nom_poste = ""
f3333b0e
OL
327 data = (
328 self.implantation,
329 self.type_poste.nom,
330 self.nom,
f3333b0e 331 )
a7c68130 332 return u'%s - %s (%s)' % data
5d680e84 333
bd28238f 334
a9c281dd
OL
335# Tester l'enregistrement car les models.py sont importés au complet
336if not reversion.is_registered(Poste):
337 reversion.register(Poste)
338
bd28238f 339
5d680e84
NC
340POSTE_FINANCEMENT_CHOICES = (
341 ('A', 'A - Frais de personnel'),
342 ('B', 'B - Projet(s)-Titre(s)'),
343 ('C', 'C - Autre')
344)
bd28238f 345
5d680e84 346class PosteFinancement(models.Model):
5d680e84
NC
347 poste = models.ForeignKey('Poste', related_name='financements')
348 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
43d04712 349 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
350 help_text="ex.: 33.33 % (décimale avec point)")
351 commentaire = models.TextField(
352 help_text="Spécifiez la source de financement.")
bd28238f 353
43d04712 354 class Meta:
355 ordering = ['type']
bd28238f 356
c0413a6f
OL
357 def __unicode__(self):
358 return u"%s %s %s" % (self.get_type_display(), self.pourcentage, self.commentaire)
359
c589d980 360
2d4d2fcf 361class PostePiece(models.Model):
362 """Documents relatifs au Poste
363 Ex.: Description de poste
364 """
365 poste = models.ForeignKey("Poste")
366 nom = models.CharField(verbose_name="Nom", max_length=255)
367 fichier = models.FileField(verbose_name="Fichier",
368 upload_to=poste_piece_dispatch,
369 storage=storage_prive)
370
371### EMPLOYÉ/PERSONNE
372
373# TODO : migration pour m -> M, f -> F
c589d980 374
bd28238f 375GENRE_CHOICES = (
139686f2
NC
376 ('m', 'Homme'),
377 ('f', 'Femme'),
bd28238f
NC
378)
379
bd28238f 380class Employe(models.Model):
bd28238f
NC
381
382 # Modèle existant
da3ca955 383 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
384 verbose_name='Employé')
bd28238f 385 nom = models.CharField(max_length=255)
da3ca955 386 prenom = models.CharField(max_length=255, verbose_name='Prénom')
07b40eda 387 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
bd28238f 388
139686f2 389 def __unicode__(self):
2d4d2fcf 390 return u'%s %s' % (self.prenom, self.nom.upper())
139686f2 391
bd28238f 392
2d4d2fcf 393### DOSSIER
394
395STATUT_RESIDENCE_CHOICES = (
396 ('local', 'Local'),
397 ('expat', 'Expatrié'),
398)
399
bd28238f 400COMPTE_COMPTA_CHOICES = (
494ff2be
NC
401 ('coda', 'CODA'),
402 ('scs', 'SCS'),
403 ('aucun', 'Aucun'),
bd28238f
NC
404)
405
afc204bf 406class Dossier(DossierWorkflow, models.Model):
bd28238f
NC
407
408 # Modèle existant
139686f2
NC
409 employe = models.ForeignKey('Employe', related_name='+', editable=False)
410 poste = models.ForeignKey('Poste', related_name='+', editable=False)
5d680e84 411 statut = models.ForeignKey(rh.Statut, related_name='+')
dfc2c344 412 organisme_bstg = models.ForeignKey(rh.OrganismeBstg,
1f109689 413 null=True, blank=True,
dfc2c344 414 verbose_name="Organisme",
415 help_text="Si détaché (DET) ou mis à disposition (MAD), \
416 préciser l'organisme.",
417 related_name='+')
0288adb5
OL
418 organisme_bstg_autre = models.CharField(max_length=255,
419 verbose_name="Autre organisme",
420 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
421 null=True,
422 blank=True,)
bd28238f 423
139686f2
NC
424 # Données antérieures de l'employé
425 statut_anterieur = models.ForeignKey(
426 rh.Statut, related_name='+', null=True, blank=True,
da3ca955 427 verbose_name='Statut antérieur')
139686f2
NC
428 classement_anterieur = models.ForeignKey(
429 rh.Classement, related_name='+', null=True, blank=True,
430 verbose_name='Classement précédent')
431 salaire_anterieur = models.DecimalField(
432 max_digits=12, decimal_places=2, null=True, default=None,
433 blank=True, verbose_name='Salaire précédent')
434
435 # Données du titulaire précédent
436 employe_anterieur = models.ForeignKey(
437 rh.Employe, related_name='+', null=True, blank=True,
438 verbose_name='Employé précédent')
439 statut_titulaire_anterieur = models.ForeignKey(
440 rh.Statut, related_name='+', null=True, blank=True,
441 verbose_name='Statut titulaire précédent')
442 classement_titulaire_anterieur = models.ForeignKey(
443 rh.Classement, related_name='+', null=True, blank=True,
444 verbose_name='Classement titulaire précédent')
445 salaire_titulaire_anterieur = models.DecimalField(
446 max_digits=12, decimal_places=2, default=None, null=True,
447 blank=True, verbose_name='Salaire titulaire précédent')
494ff2be 448
bd28238f
NC
449 # Recrutement
450 remplacement = models.BooleanField()
4d25e2ba 451 statut_residence = models.CharField(max_length=10, default='local',
2d4d2fcf 452 verbose_name="Statut",
453 choices=STATUT_RESIDENCE_CHOICES)
bd28238f
NC
454
455 # Rémunération
494ff2be 456 classement = models.ForeignKey(rh.Classement, related_name='+',
2d4d2fcf 457 null=True, blank=True,
458 verbose_name='Classement proposé')
494ff2be 459 salaire = models.DecimalField(max_digits=12, decimal_places=2,
2d4d2fcf 460 verbose_name='Salaire de base',
461 null=True, default=None)
e8e75458 462 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
2d4d2fcf 463 regime_travail = models.DecimalField(max_digits=12,
464 decimal_places=2,
465 default=REGIME_TRAVAIL_DEFAULT,
466 verbose_name="Régime de travail",
467 help_text="% du temps complet")
139686f2 468 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
2d4d2fcf 469 decimal_places=2,
470 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
471 verbose_name="Nb. heures par semaine")
bd28238f
NC
472
473 # Contrat
5d680e84 474 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
4d25e2ba 475 contrat_date_debut = models.DateField(help_text="format: aaaa-mm-jj")
1f109689 476 contrat_date_fin = models.DateField(null=True, blank=True,
477 help_text="format: aaaa-mm-jj")
bd28238f 478
29dffede
OL
479 # Justifications
480 justif_nouveau_statut_label = u'Justifier le statut que ce type de poste nécessite (national, expatrié, màd ou détachement)'
481 justif_nouveau_statut = models.TextField(verbose_name=justif_nouveau_statut_label, null=True, blank=True)
482 justif_nouveau_tmp_remplacement_label = u"Si l'employé effectue un remplacement temporaire, préciser le motif"
483 justif_nouveau_tmp_remplacement = models.TextField(verbose_name=justif_nouveau_tmp_remplacement_label, null=True, blank=True)
484 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, jusitfier"
485 justif_nouveau_salaire = models.TextField(verbose_name=justif_nouveau_salaire_label, null=True, blank=True)
486 justif_nouveau_commentaire_label = u"Commentaires additionnels"
487 justif_nouveau_commentaire = models.TextField(verbose_name=justif_nouveau_commentaire_label, null=True, blank=True)
488 justif_rempl_type_contrat_label = u"Changement de type de contrat, ex : d'un CDD en CDI"
489 justif_rempl_type_contrat = models.TextField(verbose_name=justif_rempl_type_contrat_label, null=True, blank=True)
490 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"
491 justif_rempl_statut_employe = models.TextField(verbose_name=justif_rempl_statut_employe_label, null=True, blank=True)
492 justif_rempl_evaluation_label = u"L'évaluation de l'employé est-elle favorable, préciser"
493 justif_rempl_evaluation = models.TextField(verbose_name=justif_rempl_evaluation_label, null=True, blank=True)
494 justif_rempl_salaire_label = u"Si le salaire de l'employé est modifié et/ou ne correspond pas à son classement, justifier"
495 justif_rempl_salaire = models.TextField(verbose_name=justif_rempl_salaire_label, null=True, blank=True)
496 justif_rempl_commentaire_label = u"Commentaires additionnels"
497 justif_rempl_commentaire = models.TextField(verbose_name=justif_rempl_commentaire_label, null=True, blank=True)
498
bd28238f 499 # Comptes
dfc2c344 500 compte_compta = models.CharField(max_length=10, default='aucun',
501 verbose_name=u'Compte comptabilité',
502 choices=COMPTE_COMPTA_CHOICES)
bd28238f 503 compte_courriel = models.BooleanField()
0140cbd2 504
505 # Méta
506 date_creation = models.DateTimeField(auto_now_add=True)
e4f56614
OL
507
508 # Managers
509 objects = DossierManager()
510
aec2c91e 511 def __unicode__(self):
e4f56614 512 return u'[%s] %s - %s' % (self.poste.implantation, self.poste.nom, self.employe)
bd28238f 513
b1baa306
OL
514 def get_salaire_euros(self):
515 try:
516 tx = rh.TauxChange.objects.filter(implantation=self.poste.implantation, devise=self.devise)[0].taux
517 except:
518 tx = 1
519 return (float)(tx) * (float)(self.salaire)
520
bf6fbbcf 521 def get_remunerations_brutes(self):
57bd966c 522 """
bf6fbbcf
OL
523 1 Salaire de base
524 3 Indemnité de base
525 4 Indemnité d'expatriation
526 5 Indemnité pour frais
527 6 Indemnité de logement
528 7 Indemnité de fonction
529 8 Indemnité de responsabilité
530 9 Indemnité de transport
531 10 Indemnité compensatrice
532 11 Indemnité de subsistance
533 12 Indemnité différentielle
534 13 Prime d'installation
535 14 Billet d'avion
536 15 Déménagement
537 16 Indemnité de départ
538 18 Prime de 13ième mois
539 19 Prime d'intérim
57bd966c 540 """
bf6fbbcf
OL
541 ids = [1,3,4,5,6,7,8,9,10,11,12,13,14,15,16,18,19]
542 return [r for r in self.remuneration_set.all() if r.type_id in ids]
543
544 def get_charges_salariales(self):
545 """
546 20 Charges salariales ?
547 """
548 ids = [20, ]
549 return [r for r in self.remuneration_set.all() if r.type_id in ids]
550
551 def get_total_charges_salariales(self):
552 total = 0.0
553 for r in self.get_charges_salariales():
554 total += r.montant_euro()
555 return total
556
557 def get_charges_patronales(self):
558 """
559 17 Charges patronales
560 """
561 ids = [17, ]
562 return [r for r in self.remuneration_set.all() if r.type_id in ids]
563
564 def get_total_charges_patronales(self):
a9e52624 565 total = 0.0
bf6fbbcf 566 for r in self.get_charges_patronales():
a9e52624
OL
567 total += r.montant_euro()
568 return total
57bd966c 569
bf6fbbcf
OL
570 def get_salaire_brut(self):
571 """
572 somme des rémuérations brutes
573 """
574 total = 0.0
575 for r in self.get_remunerations_brutes():
576 total += r.montant_euro()
577 return total
578
579 def get_salaire_net(self):
580 """
581 salaire brut - charges salariales
582 """
583 total_charges = 0.0
584 for r in self.get_charges_salariales():
585 total_charges += r.montant_euro()
586 return self.get_salaire_brut() - total_charges
587
588 def get_couts_auf(self):
589 """
590 salaire net + charges patronales
591 """
592 total_charges = 0.0
593 for r in self.get_charges_patronales():
594 total_charges += r.montant_euro()
595 return self.get_salaire_net() + total_charges
596
597 def get_remunerations_tierces(self):
57bd966c 598 """
bf6fbbcf 599 2 Salaire MAD
57bd966c
OL
600 """
601 return [r for r in self.remuneration_set.all() if r.type_id in (2, )]
602
bf6fbbcf 603 def get_total_remunerations_tierces(self):
a9e52624 604 total = 0.0
bf6fbbcf 605 for r in self.get_remunerations_tierces():
a9e52624
OL
606 total += r.montant_euro()
607 return total
608
bd28238f 609
0140cbd2 610# Tester l'enregistrement car les models.py sont importés au complet
611if not reversion.is_registered(Dossier):
612 reversion.register(Dossier)
bd28238f 613
2d4d2fcf 614class DossierPiece(models.Model):
615 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
616 Ex.: Lettre de motivation.
617 """
618 dossier = models.ForeignKey("Dossier")
619 nom = models.CharField(verbose_name="Nom", max_length=255)
620 fichier = models.FileField(verbose_name="Fichier",
621 upload_to=dossier_piece_dispatch,
622 storage=storage_prive)
623
624
03b395db
OL
625class DossierComparaison(models.Model):
626 """
627 Photo d'une comparaison salariale au moment de l'embauche.
628 """
629 dossier = models.ForeignKey('Dossier', related_name='comparaisons')
630 implantation = models.ForeignKey(ref.Implantation, null=True, blank=True)
631 poste = models.CharField(max_length=255, null=True, blank=True)
632 personne = models.CharField(max_length=255, null=True, blank=True)
633 montant = models.IntegerField(null=True)
634 devise = models.ForeignKey(rh.Devise, default=5, related_name='+', null=True, blank=True)
635 montant_euros = models.IntegerField(null=True)
636
c589d980 637
2d4d2fcf 638### RÉMUNÉRATION
639
bd28238f 640class Remuneration(models.Model):
5d680e84 641 # Identification
bd28238f 642 dossier = models.ForeignKey('Dossier', db_column='dossier')
cb1d62b5
NC
643 type = models.ForeignKey(rh.TypeRemuneration, db_column='type',
644 related_name='+')
cb1d62b5
NC
645 montant = models.DecimalField(max_digits=12, decimal_places=2,
646 null=True) # Annuel
494ff2be 647 devise = models.ForeignKey(rh.Devise, to_field='code',
5d680e84 648 db_column='devise', related_name='+')
cb1d62b5 649 precision = models.CharField(max_length=255, null=True, blank=True)
bd28238f 650
5d680e84 651 # Méta
bd28238f 652 date_creation = models.DateField(auto_now_add=True)
2d4d2fcf 653 user_creation = models.IntegerField(null=True, blank=True) # TODO : user
bd28238f 654
ee91bc95
NC
655 def montant_mois(self):
656 return round(self.montant / 12, 2)
657
658 def taux_devise(self):
659 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
660
661 def montant_euro(self):
a9e52624 662 return round(float(self.montant) * float(self.taux_devise()), 2)
ee91bc95
NC
663
664 def montant_euro_mois(self):
665 return round(self.montant_euro() / 12, 2)