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