volumetrie postes
[auf_rh_dae.git] / project / dae / models.py
1 # -=- encoding: utf-8 -=-
2
3 import os
4 from datetime import date, timedelta
5
6 from django.conf import settings
7 from django.core.files.storage import FileSystemStorage
8 from django.db import models
9 from django.db.models import Q
10
11 import reversion
12
13 from project.dae.managers import PosteManager, DossierManager
14 from project.dae.workflow import PosteWorkflow, DossierWorkflow
15 from project.dae.workflow import \
16 DOSSIER_ETAT_DRH_FINALISATION, DOSSIER_ETAT_REGION_FINALISATION, \
17 DOSSIER_ETAT_FINALISE
18 from project.rh import models as rh
19 from project.rh.models import HELP_TEXT_DATE
20
21 # Upload de fichiers
22 UPLOAD_STORAGE = FileSystemStorage(settings.PRIVE_MEDIA_ROOT)
23
24
25 ### POSTE
26
27 POSTE_APPEL_CHOICES = (
28 ('interne', 'Interne'),
29 ('externe', 'Externe'),
30 )
31 POSTE_ACTION = (
32 ('N', u"Nouveau poste"),
33 ('M', u"Poste existant"),
34 ('E', u"Évolution de poste"),
35 )
36
37
38 class DeviseException(Exception):
39 silent_variable_failure = True
40
41
42 class Poste(PosteWorkflow, rh.Poste_):
43
44 type_intervention = models.CharField(
45 max_length=1, choices=POSTE_ACTION, default='N'
46 )
47
48 # Modèle existant
49 id_rh = models.ForeignKey(
50 rh.Poste, null=True, related_name='postes_dae', editable=False,
51 verbose_name=u"Mise à jour du poste"
52 )
53
54 # Rémunération
55 indemn_expat_min = models.DecimalField(
56 max_digits=13, decimal_places=2, default=0
57 )
58 indemn_expat_max = models.DecimalField(
59 max_digits=12, decimal_places=2, default=0
60 )
61 indemn_fct_min = models.DecimalField(
62 max_digits=12, decimal_places=2, default=0
63 )
64 indemn_fct_max = models.DecimalField(
65 max_digits=12, decimal_places=2, default=0
66 )
67 charges_patronales_min = models.DecimalField(
68 max_digits=12, decimal_places=2, default=0
69 )
70 charges_patronales_max = models.DecimalField(
71 max_digits=12, decimal_places=2, default=0
72 )
73
74 # Managers
75 objects = PosteManager()
76
77 def _get_key(self):
78 """
79 Les vues sont montées selon une clef spéciale
80 pour identifier la provenance du poste.
81 Cette méthode fournit un moyen de reconstruire cette clef
82 afin de générer les URLs.
83 """
84 return "dae-%s" % self.id
85 key = property(_get_key)
86
87 def get_dossiers(self):
88 """
89 Liste tous les anciens dossiers liés à ce poste.
90 (Le nom de la relation sur le rh.Poste est mal choisi
91 poste1 au lieu de dossier1)
92 Note1 : seulement le dosssier principal fait l'objet de la recherche.
93 Note2 : les dossiers sont retournés du plus récent au plus vieux.
94 (Ce test est fait en fonction du id,
95 car les dates de création sont absentes de rh v1).
96 """
97 if self.id_rh is None:
98 return []
99 return self.id_rh.rh_dossiers.all()
100
101 def dans_rh(self):
102 """
103 Retourne le poste RH s'il existe.
104 """
105 return self.id_rh
106
107 def importer_dans_rh(self):
108 """
109 Importe le poste DAE dans un poste RH existant ou nouveau.
110 """
111 dans_rh = self.dans_rh()
112 poste_rh = dans_rh or rh.Poste()
113 poste_rh.nom = self.nom
114 poste_rh.implantation = self.implantation
115 poste_rh.type_poste = self.type_poste
116 poste_rh.service = self.service
117 poste_rh.responsable = self.responsable
118 poste_rh.regime_travail = self.regime_travail
119 poste_rh.regime_travail_nb_heure_semaine = \
120 self.regime_travail_nb_heure_semaine
121 poste_rh.local = self.local
122 poste_rh.expatrie = self.expatrie
123 poste_rh.mise_a_disposition = self.mise_a_disposition
124 poste_rh.appel = self.appel
125 poste_rh.classement_min = self.classement_min
126 poste_rh.classement_max = self.classement_max
127 poste_rh.valeur_point_min = self.valeur_point_min
128 poste_rh.valeur_point_max = self.valeur_point_max
129 poste_rh.devise_min = self.devise_min
130 poste_rh.devise_max = self.devise_max
131 poste_rh.salaire_min = self.salaire_min
132 poste_rh.salaire_max = self.salaire_max
133 poste_rh.indemn_min = self.indemn_fct_min
134 poste_rh.indemn_max = self.indemn_fct_max
135 poste_rh.autre_min = \
136 self.indemn_expat_min + self.charges_patronales_min + \
137 self.autre_min
138 poste_rh.autre_max = \
139 self.indemn_expat_max + self.charges_patronales_max + \
140 self.autre_max
141 poste_rh.devise_comparaison = self.devise_comparaison
142 poste_rh.comp_locale_min = self.comp_locale_min
143 poste_rh.comp_locale_max = self.comp_locale_max
144 poste_rh.comp_universite_min = self.comp_universite_min
145 poste_rh.comp_universite_max = self.comp_universite_max
146 poste_rh.comp_fonctionpub_min = self.comp_fonctionpub_min
147 poste_rh.comp_fonctionpub_max = self.comp_fonctionpub_max
148 poste_rh.comp_ong_min = self.comp_ong_min
149 poste_rh.comp_ong_max = self.comp_ong_max
150 poste_rh.comp_autre_min = self.comp_autre_min
151 poste_rh.comp_autre_max = self.comp_autre_max
152 poste_rh.justification = self.justification
153 if not dans_rh:
154 poste_rh.date_debut = self.date_debut
155 poste_rh.date_fin = self.date_fin
156 poste_rh.save()
157
158 for piece in self.dae_pieces.all():
159 piece_rh = poste_rh.rh_pieces.create(
160 poste=poste_rh,
161 nom=piece.nom
162 )
163 piece_rh.fichier.save(
164 os.path.basename(piece.fichier.name), piece.fichier
165 )
166
167 rh.PosteComparaison.objects.filter(poste=poste_rh).delete()
168 for comp in self.dae_comparaisons_internes.all():
169 poste_rh.rh_comparaisons_internes.create(
170 implantation=comp.implantation,
171 nom=comp.nom,
172 montant=comp.montant,
173 devise=comp.devise
174 )
175
176 rh.PosteFinancement.objects.filter(poste=poste_rh).delete()
177 for financement in self.dae_financements.all():
178 poste_rh.rh_financements.create(
179 type=financement.type,
180 pourcentage=financement.pourcentage,
181 commentaire=financement.commentaire
182 )
183
184 self.id_rh = poste_rh
185 self.save()
186 return poste_rh
187
188 def get_employe(self):
189 """
190 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
191 """
192 dossiers = self.get_dossiers()
193 if len(dossiers) > 0:
194 return dossiers[0].employe
195 else:
196 return None
197
198 def get_default_devise(self):
199 """Récupère la devise par défaut en fonction de l'implantation
200 (EUR autrement)
201 """
202 try:
203 implantation_devise = rh.TauxChange.objects \
204 .filter(implantation=self.implantation)[0].devise
205 except:
206 implantation_devise = 5 # EUR
207 return implantation_devise
208
209 #####################
210 # Classement de poste
211 #####################
212
213 def get_couts_minimum(self):
214 return self.salaire_min + self.indemn_expat_min + \
215 self.indemn_fct_min + self.charges_patronales_min + \
216 self.autre_min
217
218 def get_salaire_minimum(self):
219 return self.get_couts_minimum() - self.charges_patronales_min
220
221 def get_taux_minimum(self):
222 if self.devise_min.code == 'EUR':
223 return 1
224 liste_taux = self.devise_min.tauxchange_set.order_by('-annee')
225 if len(liste_taux) == 0:
226 raise DeviseException(
227 u"La devise %s n'a pas de taux pour l'implantation %s" %
228 (self.devise_min, self.implantation))
229 else:
230 return liste_taux[0].taux
231
232 def get_couts_minimum_euros(self):
233 return float(self.get_couts_minimum()) * self.get_taux_minimum()
234
235 def get_salaire_minimum_euros(self):
236 return float(self.get_salaire_minimum()) * self.get_taux_minimum()
237
238 def get_couts_maximum(self):
239 return self.salaire_max + self.indemn_expat_max + \
240 self.indemn_fct_max + self.charges_patronales_max + \
241 self.autre_max
242
243 def get_salaire_maximum(self):
244 return self.get_couts_maximum() - self.charges_patronales_max
245
246 def get_taux_maximum(self):
247 if self.devise_max.code == 'EUR':
248 return 1
249 liste_taux = self.devise_max.tauxchange_set.order_by('-annee')
250 if len(liste_taux) == 0:
251 raise DeviseException(
252 u"La devise %s n'a pas de taux pour l'implantation %s" %
253 (self.devise_max, self.implantation)
254 )
255 else:
256 return liste_taux[0].taux
257
258 def get_couts_maximum_euros(self):
259 return float(self.get_couts_maximum()) * self.get_taux_maximum()
260
261 def get_salaire_maximum_euros(self):
262 return float(self.get_salaire_maximum()) * self.get_taux_maximum()
263
264 def show_taux_minimum(self):
265 try:
266 return self.get_taux_minimum()
267 except DeviseException, e:
268 return e
269
270 def show_couts_minimum_euros(self):
271 try:
272 return self.get_couts_minimum_euros()
273 except DeviseException, e:
274 return e
275
276 def show_salaire_minimum_euros(self):
277 try:
278 return self.get_salaire_minimum_euros()
279 except DeviseException, e:
280 return e
281
282 def show_taux_maximum(self):
283 try:
284 return self.get_taux_maximum()
285 except DeviseException, e:
286 return e
287
288 def show_couts_maximum_euros(self):
289 try:
290 return self.get_couts_maximum_euros()
291 except DeviseException, e:
292 return e
293
294 def show_salaire_maximum_euros(self):
295 try:
296 return self.get_salaire_maximum_euros()
297 except DeviseException, e:
298 return e
299
300 # Comparaison de poste
301 def est_comparable(self):
302 """
303 Si on a au moins une valeur de saisie dans les comparaisons, alors
304 le poste est comparable.
305 """
306 if self.comp_universite_min is None and \
307 self.comp_fonctionpub_min is None and \
308 self.comp_locale_min is None and \
309 self.comp_ong_min is None and \
310 self.comp_autre_min is None and \
311 self.comp_universite_max is None and \
312 self.comp_fonctionpub_max is None and \
313 self.comp_locale_max is None and \
314 self.comp_ong_max is None and \
315 self.comp_autre_max is None:
316 return False
317 else:
318 return True
319
320 def get_taux_comparaison(self):
321 try:
322 return rh.TauxChange.objects \
323 .filter(devise=self.devise_comparaison)[0].taux
324 except:
325 return 1
326
327 def get_comp_universite_min_euros(self):
328 return (float)(self.comp_universite_min) * self.get_taux_comparaison()
329
330 def get_comp_fonctionpub_min_euros(self):
331 return (float)(self.comp_fonctionpub_min) * self.get_taux_comparaison()
332
333 def get_comp_locale_min_euros(self):
334 return (float)(self.comp_locale_min) * self.get_taux_comparaison()
335
336 def get_comp_ong_min_euros(self):
337 return (float)(self.comp_ong_min) * self.get_taux_comparaison()
338
339 def get_comp_autre_min_euros(self):
340 return (float)(self.comp_autre_min) * self.get_taux_comparaison()
341
342 def get_comp_universite_max_euros(self):
343 return (float)(self.comp_universite_max) * self.get_taux_comparaison()
344
345 def get_comp_fonctionpub_max_euros(self):
346 return (float)(self.comp_fonctionpub_max) * self.get_taux_comparaison()
347
348 def get_comp_locale_max_euros(self):
349 return (float)(self.comp_locale_max) * self.get_taux_comparaison()
350
351 def get_comp_ong_max_euros(self):
352 return (float)(self.comp_ong_max) * self.get_taux_comparaison()
353
354 def get_comp_autre_max_euros(self):
355 return (float)(self.comp_autre_max) * self.get_taux_comparaison()
356
357 def __unicode__(self):
358 """
359 Cette fonction est consommatrice SQL car elle cherche les dossiers
360 qui ont été liés à celui-ci.
361 """
362 try:
363 data = (
364 self.implantation,
365 self.type_poste.nom,
366 self.nom,
367 )
368 except:
369 return self.nom
370 return u'%s - %s (%s)' % data
371
372 reversion.register(Poste, format='xml', follow=[
373 'dae_financements', 'dae_pieces', 'dae_comparaisons_internes'
374 ])
375
376 POSTE_FINANCEMENT_CHOICES = (
377 ('A', 'A - Frais de personnel'),
378 ('B', 'B - Projet(s)-Titre(s)'),
379 ('C', 'C - Autre')
380 )
381
382
383 class PosteFinancement(rh.PosteFinancement_):
384 poste = models.ForeignKey(
385 Poste, db_column='poste', related_name='dae_financements'
386 )
387
388 reversion.register(PosteFinancement, format='xml')
389
390
391 class PostePiece(rh.PostePiece_):
392 poste = models.ForeignKey(
393 Poste, db_column='poste', related_name='dae_pieces'
394 )
395
396 reversion.register(PostePiece, format='xml')
397
398
399 class PosteComparaison(rh.PosteComparaison_):
400 poste = models.ForeignKey(
401 Poste, related_name='dae_comparaisons_internes'
402 )
403 statut = models.ForeignKey(
404 rh.Statut, related_name='+', verbose_name=u'Statut', null=True,
405 blank=True
406 )
407 classement = models.ForeignKey(
408 rh.Classement, related_name='+', verbose_name=u'Classement',
409 null=True, blank=True
410 )
411
412 reversion.register(PosteComparaison, format='xml')
413
414 ### EMPLOYÉ/PERSONNE
415
416 # TODO : migration pour m -> M, f -> F
417
418 GENRE_CHOICES = (
419 ('m', 'Homme'),
420 ('f', 'Femme'),
421 )
422
423
424 class Employe(models.Model):
425
426 # Modèle existant
427 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
428 verbose_name=u'Employé')
429 nom = models.CharField(max_length=255)
430 prenom = models.CharField(max_length=255, verbose_name=u'Prénom')
431 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
432
433 def __unicode__(self):
434 return u'%s %s' % (self.prenom, self.nom.upper())
435
436 def dans_rh(self):
437 """
438 Retourne l'employé RH associé à cet employé DAE.
439 """
440 return self.id_rh
441
442 def importer_dans_rh(self):
443 """
444 Importe l'employé DAE dans un employé RH existant ou nouveau.
445 """
446 employe_rh = self.dans_rh() or rh.Employe.objects.create(
447 nom=self.nom,
448 prenom=self.prenom,
449 genre=self.genre
450 )
451 self.id_rh = employe_rh
452 self.save()
453 return employe_rh
454
455 reversion.register(Employe, format='xml')
456
457
458 ### DOSSIER
459
460 STATUT_RESIDENCE_CHOICES = (
461 ('local', 'Local'),
462 ('expat', 'Expatrié'),
463 )
464
465 COMPTE_COMPTA_CHOICES = (
466 ('coda', 'CODA'),
467 ('scs', 'SCS'),
468 ('aucun', 'Aucun'),
469 )
470
471
472 class Dossier(DossierWorkflow, rh.Dossier_):
473 poste = models.ForeignKey(
474 'Poste', db_column='poste', related_name='dae_dossiers'
475 )
476 employe = models.ForeignKey(
477 'Employe', db_column='employe',
478 related_name='rh_dossiers', verbose_name=u"Employé"
479 )
480 organisme_bstg_autre = models.CharField(max_length=255,
481 verbose_name=u"Autre organisme",
482 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
483 null=True,
484 blank=True,)
485
486 # Lien avec le dossier
487 dossier_rh = models.ForeignKey(
488 rh.Dossier, related_name='dossiers_dae', null=True, blank=True
489 )
490
491 # Données antérieures de l'employé
492 statut_anterieur = models.ForeignKey(
493 rh.Statut, related_name='+', null=True, blank=True,
494 verbose_name=u'Statut antérieur')
495 classement_anterieur = models.ForeignKey(
496 rh.Classement, related_name='+', null=True, blank=True,
497 verbose_name=u'Classement précédent')
498 salaire_anterieur = models.DecimalField(
499 max_digits=12, decimal_places=2, null=True, default=None,
500 blank=True, verbose_name=u'Salaire précédent')
501 devise_anterieur = models.ForeignKey(
502 rh.Devise, related_name='+', null=True, blank=True
503 )
504 type_contrat_anterieur = models.ForeignKey(
505 rh.TypeContrat, related_name='+', null=True, blank=True,
506 verbose_name=u'Type contrat antérieur'
507 )
508
509 # Données du titulaire précédent
510 employe_anterieur = models.ForeignKey(
511 rh.Employe, related_name='+', null=True, blank=True,
512 verbose_name=u'Employé précédent')
513 statut_titulaire_anterieur = models.ForeignKey(
514 rh.Statut, related_name='+', null=True, blank=True,
515 verbose_name=u'Statut titulaire précédent')
516 classement_titulaire_anterieur = models.ForeignKey(
517 rh.Classement, related_name='+', null=True, blank=True,
518 verbose_name=u'Classement titulaire précédent')
519 salaire_titulaire_anterieur = models.DecimalField(
520 max_digits=12, decimal_places=2, default=None, null=True,
521 blank=True, verbose_name=u'Salaire titulaire précédent')
522 devise_titulaire_anterieur = models.ForeignKey(
523 rh.Devise, related_name='+', null=True, blank=True
524 )
525
526 # Rémunération
527 salaire = models.DecimalField(max_digits=13, decimal_places=2,
528 verbose_name=u'Salaire de base',
529 null=True, default=None)
530 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
531
532 # Contrat
533 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
534 contrat_date_debut = models.DateField(help_text=HELP_TEXT_DATE)
535 contrat_date_fin = models.DateField(null=True, blank=True,
536 help_text=HELP_TEXT_DATE)
537
538 # Justifications
539 justif_nouveau_statut_label = u'Justifier le statut que ce type ' \
540 u'de poste nécessite (national, expatrié, màd ou détachement)'
541 justif_nouveau_statut = models.TextField(
542 verbose_name=justif_nouveau_statut_label, null=True, blank=True
543 )
544 justif_nouveau_tmp_remplacement_label = u"Si l'employé effectue un " \
545 u"remplacement temporaire, préciser"
546 justif_nouveau_tmp_remplacement = models.TextField(
547 verbose_name=justif_nouveau_tmp_remplacement_label, null=True,
548 blank=True
549 )
550 justif_nouveau_salaire_label = u"Si le salaire de l'employé ne " \
551 u"correspond pas au classement du poste ou est différent " \
552 u"du salaire antérieur, justifier "
553 justif_nouveau_salaire = models.TextField(
554 verbose_name=justif_nouveau_salaire_label, null=True, blank=True
555 )
556 justif_nouveau_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
557 justif_nouveau_commentaire = models.TextField(
558 verbose_name=justif_nouveau_commentaire_label, null=True, blank=True
559 )
560 justif_rempl_type_contrat_label = \
561 u"Changement de type de contrat, ex : d'un CDD en CDI"
562 justif_rempl_type_contrat = models.TextField(
563 verbose_name=justif_rempl_type_contrat_label, null=True, blank=True
564 )
565 justif_rempl_statut_employe_label = \
566 u"Si le statut de l'employé a été modifié pour ce poste ; " \
567 u"ex : national, expatrié, màd, détachement ? Si oui, justifier"
568 justif_rempl_statut_employe = models.TextField(
569 verbose_name=justif_rempl_statut_employe_label, null=True, blank=True
570 )
571 justif_rempl_evaluation_label = \
572 u"L'évaluation de l'employé est-elle favorable? Préciser"
573 justif_rempl_evaluation = models.TextField(
574 verbose_name=justif_rempl_evaluation_label, null=True, blank=True
575 )
576 justif_rempl_salaire_label = \
577 u"Si le salaire de l'employé est modifié et/ou ne correspond " \
578 u"pas à son classement, justifier"
579 justif_rempl_salaire = models.TextField(
580 verbose_name=justif_rempl_salaire_label, null=True, blank=True
581 )
582 justif_rempl_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
583 justif_rempl_commentaire = models.TextField(
584 verbose_name=justif_rempl_commentaire_label, null=True, blank=True
585 )
586
587 # Comptes
588 compte_compta = models.CharField(max_length=10, default='aucun',
589 verbose_name=u'Compte comptabilité',
590 choices=COMPTE_COMPTA_CHOICES)
591 compte_courriel = models.BooleanField()
592
593 # DAE numérisée
594 dae_numerisee = models.FileField(
595 upload_to='dae/dae_numerisee', storage=UPLOAD_STORAGE, blank=True,
596 null=True, verbose_name="DAE numérisée"
597 )
598
599 # Managers
600 objects = DossierManager()
601
602 def __init__(self, *args, **kwargs):
603 # Bouchon pour créer une date fictive necessaire pour valider un
604 # dossier à cause de l'héritage
605 super(rh.Dossier_, self).__init__(*args, **kwargs)
606 super(DossierWorkflow, self).__init__(*args, **kwargs)
607 import datetime
608 self.date_debut = datetime.datetime.today()
609
610 def __unicode__(self):
611 return u'[%s] %s - %s' % (
612 self.poste.implantation, self.poste.nom, self.employe
613 )
614
615 def dans_rh(self):
616 """
617 Retourne le dossier associé dans le système RH ou ``None`` s'il n'y
618 en a pas.
619 """
620 if self.dossier_rh:
621 return self.dossier_rh
622 today = date.today()
623 poste_rh = self.poste.dans_rh()
624 if poste_rh is None:
625 return None
626 employe_rh = self.employe.dans_rh()
627 if employe_rh is None:
628 return None
629 try:
630 return rh.Dossier.objects.get(
631 Q(date_debut=None) | Q(date_debut__lte=today),
632 Q(date_fin=None) | Q(date_fin__gte=today),
633 poste=poste_rh,
634 employe=employe_rh
635 )
636 except rh.Dossier.DoesNotExist:
637 return None
638
639 def importer_dans_rh(self):
640 """
641 Importe les données du dossier DAE dans un dossier RH existant ou
642 nouveau.
643 """
644 poste_rh = self.poste.importer_dans_rh()
645 employe_rh = self.employe.importer_dans_rh()
646 dossier_rh = self.dans_rh() or \
647 rh.Dossier(poste=poste_rh, employe=employe_rh)
648
649 dossier_rh.statut = self.statut
650 dossier_rh.organisme_bstg = self.organisme_bstg
651 dossier_rh.remplacement = self.remplacement
652 dossier_rh.remplacement_de = self.remplacement_de
653 dossier_rh.statut_residence = self.statut_residence
654 dossier_rh.classement = self.classement
655 dossier_rh.regime_travail = self.regime_travail
656 dossier_rh.regime_travail_nb_heure_semaine = \
657 self.regime_travail_nb_heure_semaine
658 dossier_rh.date_debut = self.contrat_date_debut
659 dossier_rh.date_fin = self.contrat_date_fin
660 dossier_rh.save()
661
662 rh.DossierComparaison.objects.filter(dossier=dossier_rh).delete()
663 for comp in self.dae_comparaisons.all():
664 dossier_rh.rh_comparaisons.create(
665 implantation=comp.implantation,
666 poste=comp.poste,
667 personne=comp.personne,
668 montant=comp.montant,
669 devise=comp.devise
670 )
671
672 for contrat in self.dae_contrats.all():
673 contrat_rh = dossier_rh.rh_contrats.create(
674 type_contrat=self.type_contrat,
675 date_debut=self.contrat_date_debut,
676 date_fin=self.contrat_date_fin,
677 )
678 contrat_rh.fichier.save(
679 os.path.basename(contrat.fichier.name), contrat.fichier
680 )
681
682 for piece in self.dae_dossierpieces.all():
683 piece_rh = dossier_rh.rh_dossierpieces.create(
684 nom=piece.nom
685 )
686 piece_rh.fichier.save(
687 os.path.basename(piece.fichier.name), piece.fichier
688 )
689
690 if self.dae_numerisee:
691 dae_numerisee_rh = dossier_rh.rh_dossierpieces.create(
692 nom=u'DAE numérisée'
693 )
694 dae_numerisee_rh.fichier.save(
695 os.path.basename(self.dae_numerisee.name),
696 self.dae_numerisee
697 )
698
699 # Fermer les rémunérations qui commencent avant le début du contrat
700 dossier_rh.rh_remunerations.filter(
701 Q(date_debut=None) | Q(date_debut__lt=self.contrat_date_debut),
702 Q(date_fin=None) | Q(date_fin__gte=self.contrat_date_debut)
703 ).update(date_fin=self.contrat_date_debut - timedelta(1))
704
705 # Effacer les rémunérations qui commencent à la date du contrat
706 dossier_rh.rh_remunerations \
707 .filter(date_debut=self.contrat_date_debut) \
708 .delete()
709
710 for remun in self.dae_remunerations.all():
711 dossier_rh.rh_remunerations.get_or_create(
712 type=remun.type,
713 type_revalorisation=remun.type_revalorisation,
714 montant=remun.montant,
715 devise=remun.devise,
716 commentaire=remun.commentaire,
717 date_debut=self.contrat_date_debut,
718 date_fin=self.contrat_date_fin
719 )
720
721 # Enregistrer le lien avec le dossier RH
722 self.dossier_rh = dossier_rh
723 self.save()
724
725 return dossier_rh
726
727 def get_salaire_anterieur_euros(self):
728 if self.devise_anterieur is None:
729 return None
730 try:
731 taux = self.taux_devise(self.devise_anterieur)
732 except Exception, e:
733 return e
734 if not taux:
735 return None
736 return int(round(float(self.salaire_anterieur) * float(taux), 2))
737
738 def get_salaire_titulaire_anterieur_euros(self):
739 if self.devise_titulaire_anterieur is None:
740 return None
741 try:
742 taux = self.taux_devise(self.devise_titulaire_anterieur)
743 except Exception, e:
744 return e
745 if not taux:
746 return None
747 return int(round(
748 float(self.salaire_titulaire_anterieur) * float(taux), 2
749 ))
750
751 def valide(self):
752 return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
753 DOSSIER_ETAT_DRH_FINALISATION,
754 DOSSIER_ETAT_FINALISE)
755
756 reversion.register(Dossier, format='xml', follow=[
757 'dae_dossierpieces', 'dae_comparaisons', 'dae_remunerations',
758 'dae_contrats'
759 ])
760
761
762 class DossierPiece(rh.DossierPiece_):
763 """
764 Documents relatifs au Dossier (à l'occupation de ce poste par employé).
765 Ex.: Lettre de motivation.
766 """
767 dossier = models.ForeignKey(
768 Dossier, db_column='dossier', related_name='dae_dossierpieces'
769 )
770
771 reversion.register(DossierPiece, format='xml')
772
773
774 class DossierComparaison(rh.DossierComparaison_):
775 """
776 Photo d'une comparaison salariale au moment de l'embauche.
777 """
778 dossier = models.ForeignKey(
779 Dossier, related_name='dae_comparaisons'
780 )
781 statut = models.ForeignKey(
782 rh.Statut, related_name='+', verbose_name='Statut', null=True,
783 blank=True
784 )
785 classement = models.ForeignKey(
786 rh.Classement, related_name='+', verbose_name='Classement',
787 null=True, blank=True
788 )
789
790 reversion.register(DossierComparaison, format='xml')
791
792
793 ### RÉMUNÉRATION
794
795 class Remuneration(rh.Remuneration_):
796 dossier = models.ForeignKey(
797 Dossier, db_column='dossier', related_name='dae_remunerations'
798 )
799
800 reversion.register(Remuneration, format='xml')
801
802
803 ### CONTRATS
804
805 class Contrat(rh.Contrat_):
806 dossier = models.ForeignKey(
807 Dossier, db_column='dossier', related_name='dae_contrats'
808 )
809
810 reversion.register(Contrat, format='xml')
811
812
813 class ProxyDossierStatut(Dossier):
814 class Meta:
815 proxy = True
816 verbose_name = "Statut du dossier"
817 verbose_name_plural = "Statut des dossiers"
818
819
820 class ProxyPosteStatut(Poste):
821 class Meta:
822 proxy = True
823 verbose_name = "Statut du poste"
824 verbose_name_plural = "Statut des postes"