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