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