fix #1473
[auf_rh_dae.git] / project / dae / models.py
1 # -=- encoding: utf-8 -=-
2
3 import os
4 from django.conf import settings
5 from django.core.files.storage import FileSystemStorage
6 from django.db import models
7 import reversion
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
13
14
15 # Constantes
16 HELP_TEXT_DATE = "format: aaaa-mm-jj"
17 REGIME_TRAVAIL_DEFAULT=100.00
18 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT=35.00
19
20
21 # Upload de fichiers
22 storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
23 base_url=settings.PRIVE_MEDIA_URL)
24
25 def poste_piece_dispatch(instance, filename):
26 path = "poste/%s/%s" % (instance.poste_id, filename)
27 return path
28
29 def dossier_piece_dispatch(instance, filename):
30 path = "dossier/%s/%s" % (instance.dossier_id, filename)
31 return path
32
33
34 ### POSTE
35
36 POSTE_APPEL_CHOICES = (
37 ('interne', 'Interne'),
38 ('externe', 'Externe'),
39 )
40
41
42 class Poste(PosteWorkflow, models.Model):
43 # Modèle existant
44 id_rh = models.ForeignKey(rh.Poste, null=True, related_name='+',
45 editable=False,
46 verbose_name="Mise à jour du poste")
47 nom = models.CharField(verbose_name="Titre du poste", max_length=255)
48 implantation = models.ForeignKey(ref.Implantation)
49 type_poste = models.ForeignKey(rh.TypePoste, null=True, related_name='+')
50 service = models.ForeignKey(rh.Service, related_name='+',
51 verbose_name=u"Direction/Service/Pôle support")
52 responsable = models.ForeignKey(rh.Poste, related_name='+',
53 verbose_name="Poste du responsable")
54
55 # Contrat
56 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
57 default=REGIME_TRAVAIL_DEFAULT,
58 verbose_name="Temps de travail",
59 help_text="% du temps complet")
60 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
61 decimal_places=2,
62 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
63 verbose_name="Nb. heures par semaine")
64
65 # Recrutement
66 local = models.BooleanField(verbose_name="Local", default=True, blank=True)
67 expatrie = models.BooleanField(verbose_name="Expatrié", default=False,
68 blank=True)
69 mise_a_disposition = models.BooleanField(verbose_name="Mise à disposition")
70 appel = models.CharField(max_length=10, default='interne',
71 verbose_name="Appel à candidature",
72 choices=POSTE_APPEL_CHOICES)
73
74 # Rémunération
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)
83 devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
84 devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
85 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
86 default=0)
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)
96
97 # Comparatifs de rémunération
98 devise_comparaison = models.ForeignKey(rh.Devise, related_name='+',
99 default=5)
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)
120
121 # Justification
122 justification = models.TextField()
123
124 # Méta
125 date_creation = models.DateTimeField(auto_now_add=True)
126 date_modification = models.DateTimeField(auto_now=True)
127 date_debut = models.DateField(verbose_name="Date de début",
128 help_text=HELP_TEXT_DATE)
129 date_fin = models.DateField(null=True, blank=True,
130 verbose_name="Date de fin",
131 help_text=HELP_TEXT_DATE)
132 actif = models.BooleanField(default=True)
133 pourvu = models.BooleanField(default=False)
134
135 # Managers
136 objects = PosteManager()
137
138 def _get_key(self):
139 """
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.
144 """
145 return "dae-%s" % self.id
146 key = property(_get_key)
147
148 def get_dossiers(self):
149 """
150 Liste tous les anciens dossiers liés à ce poste.
151 (Le nom de la relation sur le rh.Poste est mal choisi
152 poste1 au lieu de dossier1)
153 Note1 : seulement le dosssier principal fait l'objet de la recherche.
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).
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 """
165 Inspecte les modèles rh v1 pour trouver dans le dernier dossier
166 un complément de titre de poste.
167 """
168 dossiers = self.get_dossiers()
169 if len(dossiers) > 0:
170 nom = dossiers[0].complement1
171 else:
172 nom = ""
173 return nom
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
185 def get_default_devise(self):
186 """Récupère la devise par défaut en fonction de l'implantation
187 (EUR autrement)
188 """
189 try:
190 implantation_devise = rh.TauxChange.objects \
191 .filter(implantation=self.implantation)[0].devise
192 except:
193 implantation_devise = 5 # EUR
194 return implantation_devise
195
196 #####################
197 # Classement de poste
198 #####################
199
200 def get_couts_minimum(self):
201 return (float)(self.salaire_min + self.indemn_expat_min + + self.indemn_fct_min + self.charges_patronales_min + self.autre_min)
202
203 def get_taux_minimum(self):
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:
211 raise Exception('Taux indisponible pour la devise %s (%s)' % (self.devise_min, self.implantation))
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):
217 return (float)(self.salaire_max + self.indemn_expat_max + + self.indemn_fct_max + self.charges_patronales_max + self.autre_max)
218
219 def get_taux_maximum(self):
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:
227 raise Exception('Taux indisponible pour la devise %s (%s)' % (self.devise_max, self.implantation))
228
229 def get_couts_maximum_euros(self):
230 return self.get_couts_maximum() * self.get_taux_maximum()
231
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
258 ######################
259 # Comparaison de poste
260 ######################
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
280
281
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
318
319 def __unicode__(self):
320 """
321 Cette fonction est consommatrice SQL car elle cherche les dossiers
322 qui ont été liés à celui-ci.
323 """
324 complement_nom_poste = self.get_complement_nom()
325 if complement_nom_poste is None:
326 complement_nom_poste = ""
327 data = (
328 self.implantation,
329 self.type_poste.nom,
330 self.nom,
331 )
332 return u'%s - %s (%s)' % data
333
334
335 # Tester l'enregistrement car les models.py sont importés au complet
336 if not reversion.is_registered(Poste):
337 reversion.register(Poste)
338
339
340 POSTE_FINANCEMENT_CHOICES = (
341 ('A', 'A - Frais de personnel'),
342 ('B', 'B - Projet(s)-Titre(s)'),
343 ('C', 'C - Autre')
344 )
345
346 class PosteFinancement(models.Model):
347 poste = models.ForeignKey('Poste', related_name='financements')
348 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
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.")
353
354 class Meta:
355 ordering = ['type']
356
357 def __unicode__(self):
358 return u"%s %s %s" % (self.get_type_display(), self.pourcentage, self.commentaire)
359
360
361 class 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
374
375 GENRE_CHOICES = (
376 ('m', 'Homme'),
377 ('f', 'Femme'),
378 )
379
380 class Employe(models.Model):
381
382 # Modèle existant
383 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
384 verbose_name='Employé')
385 nom = models.CharField(max_length=255)
386 prenom = models.CharField(max_length=255, verbose_name='Prénom')
387 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
388
389 def __unicode__(self):
390 return u'%s %s' % (self.prenom, self.nom.upper())
391
392
393 ### DOSSIER
394
395 STATUT_RESIDENCE_CHOICES = (
396 ('local', 'Local'),
397 ('expat', 'Expatrié'),
398 )
399
400 COMPTE_COMPTA_CHOICES = (
401 ('coda', 'CODA'),
402 ('scs', 'SCS'),
403 ('aucun', 'Aucun'),
404 )
405
406 class Dossier(DossierWorkflow, models.Model):
407
408 # Modèle existant
409 employe = models.ForeignKey('Employe', related_name='+', editable=False)
410 poste = models.ForeignKey('Poste', related_name='+', editable=False)
411 statut = models.ForeignKey(rh.Statut, related_name='+')
412 organisme_bstg = models.ForeignKey(rh.OrganismeBstg,
413 null=True, blank=True,
414 verbose_name="Organisme",
415 help_text="Si détaché (DET) ou mis à disposition (MAD), \
416 préciser l'organisme.",
417 related_name='+')
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,)
423
424 # Données antérieures de l'employé
425 statut_anterieur = models.ForeignKey(
426 rh.Statut, related_name='+', null=True, blank=True,
427 verbose_name='Statut antérieur')
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')
448
449 # Recrutement
450 remplacement = models.BooleanField()
451 statut_residence = models.CharField(max_length=10, default='local',
452 verbose_name="Statut",
453 choices=STATUT_RESIDENCE_CHOICES)
454
455 # Rémunération
456 classement = models.ForeignKey(rh.Classement, related_name='+',
457 null=True, blank=True,
458 verbose_name='Classement proposé')
459 salaire = models.DecimalField(max_digits=12, decimal_places=2,
460 verbose_name='Salaire de base',
461 null=True, default=None)
462 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
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")
468 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
469 decimal_places=2,
470 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
471 verbose_name="Nb. heures par semaine")
472
473 # Contrat
474 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
475 contrat_date_debut = models.DateField(help_text="format: aaaa-mm-jj")
476 contrat_date_fin = models.DateField(null=True, blank=True,
477 help_text="format: aaaa-mm-jj")
478
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
499 # Comptes
500 compte_compta = models.CharField(max_length=10, default='aucun',
501 verbose_name=u'Compte comptabilité',
502 choices=COMPTE_COMPTA_CHOICES)
503 compte_courriel = models.BooleanField()
504
505 # Méta
506 date_creation = models.DateTimeField(auto_now_add=True)
507
508 # Managers
509 objects = DossierManager()
510
511 def __unicode__(self):
512 return u'[%s] %s - %s' % (self.poste.implantation, self.poste.nom, self.employe)
513
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
521 def get_remunerations_brutes(self):
522 """
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
540 """
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):
565 total = 0.0
566 for r in self.get_charges_patronales():
567 total += r.montant_euro()
568 return total
569
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):
598 """
599 2 Salaire MAD
600 """
601 return [r for r in self.remuneration_set.all() if r.type_id in (2, )]
602
603 def get_total_remunerations_tierces(self):
604 total = 0.0
605 for r in self.get_remunerations_tierces():
606 total += r.montant_euro()
607 return total
608
609
610 # Tester l'enregistrement car les models.py sont importés au complet
611 if not reversion.is_registered(Dossier):
612 reversion.register(Dossier)
613
614 class 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
625 class 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
637
638 ### RÉMUNÉRATION
639
640 class Remuneration(models.Model):
641 # Identification
642 dossier = models.ForeignKey('Dossier', db_column='dossier')
643 type = models.ForeignKey(rh.TypeRemuneration, db_column='type',
644 related_name='+')
645 montant = models.DecimalField(max_digits=12, decimal_places=2,
646 null=True) # Annuel
647 devise = models.ForeignKey(rh.Devise, to_field='code',
648 db_column='devise', related_name='+')
649 precision = models.CharField(max_length=255, null=True, blank=True)
650
651 # Méta
652 date_creation = models.DateField(auto_now_add=True)
653 user_creation = models.IntegerField(null=True, blank=True) # TODO : user
654
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):
662 return round(float(self.montant) * float(self.taux_devise()), 2)
663
664 def montant_euro_mois(self):
665 return round(self.montant_euro() / 12, 2)