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