Merge branch 'master' into dae_refactoring_process
[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 if not settings.DEBUG:
164 piece_rh.fichier.save(
165 os.path.basename(piece.fichier.name), piece.fichier
166 )
167
168 rh.PosteComparaison.objects.filter(poste=poste_rh).delete()
169 for comp in self.dae_comparaisons_internes.all():
170 poste_rh.rh_comparaisons_internes.create(
171 implantation=comp.implantation,
172 nom=comp.nom,
173 montant=comp.montant,
174 devise=comp.devise
175 )
176
177 rh.PosteFinancement.objects.filter(poste=poste_rh).delete()
178 for financement in self.dae_financements.all():
179 poste_rh.rh_financements.create(
180 type=financement.type,
181 pourcentage=financement.pourcentage,
182 commentaire=financement.commentaire
183 )
184
185 self.id_rh = poste_rh
186 self.save()
187 return poste_rh
188
189 def get_employe(self):
190 """
191 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
192 """
193 dossiers = self.get_dossiers()
194 if len(dossiers) > 0:
195 return dossiers[0].employe
196 else:
197 return None
198
199 def get_default_devise(self):
200 """Récupère la devise par défaut en fonction de l'implantation
201 (EUR autrement)
202 """
203 try:
204 implantation_devise = rh.TauxChange.objects \
205 .filter(implantation=self.implantation)[0].devise
206 except:
207 implantation_devise = 5 # EUR
208 return implantation_devise
209
210 #####################
211 # Classement de poste
212 #####################
213
214 def get_couts_minimum(self):
215 return self.salaire_min + self.indemn_expat_min + \
216 self.indemn_fct_min + self.charges_patronales_min + \
217 self.autre_min
218
219 def get_salaire_minimum(self):
220 return self.get_couts_minimum() - self.charges_patronales_min
221
222 def get_taux_minimum(self):
223 if self.devise_min.code == 'EUR':
224 return 1
225 liste_taux = self.devise_min.tauxchange_set.order_by('-annee')
226 if len(liste_taux) == 0:
227 raise DeviseException(
228 u"La devise %s n'a pas de taux pour l'implantation %s" %
229 (self.devise_min, self.implantation))
230 else:
231 return liste_taux[0].taux
232
233 def get_couts_minimum_euros(self):
234 return float(self.get_couts_minimum()) * self.get_taux_minimum()
235
236 def get_salaire_minimum_euros(self):
237 return float(self.get_salaire_minimum()) * self.get_taux_minimum()
238
239 def get_couts_maximum(self):
240 return self.salaire_max + self.indemn_expat_max + \
241 self.indemn_fct_max + self.charges_patronales_max + \
242 self.autre_max
243
244 def get_salaire_maximum(self):
245 return self.get_couts_maximum() - self.charges_patronales_max
246
247 def get_taux_maximum(self):
248 if self.devise_max.code == 'EUR':
249 return 1
250 liste_taux = self.devise_max.tauxchange_set.order_by('-annee')
251 if len(liste_taux) == 0:
252 raise DeviseException(
253 u"La devise %s n'a pas de taux pour l'implantation %s" %
254 (self.devise_max, self.implantation)
255 )
256 else:
257 return liste_taux[0].taux
258
259 def get_couts_maximum_euros(self):
260 return float(self.get_couts_maximum()) * self.get_taux_maximum()
261
262 def get_salaire_maximum_euros(self):
263 return float(self.get_salaire_maximum()) * self.get_taux_maximum()
264
265 def show_taux_minimum(self):
266 try:
267 return self.get_taux_minimum()
268 except DeviseException, e:
269 return e
270
271 def show_couts_minimum_euros(self):
272 try:
273 return self.get_couts_minimum_euros()
274 except DeviseException, e:
275 return e
276
277 def show_salaire_minimum_euros(self):
278 try:
279 return self.get_salaire_minimum_euros()
280 except DeviseException, e:
281 return e
282
283 def show_taux_maximum(self):
284 try:
285 return self.get_taux_maximum()
286 except DeviseException, e:
287 return e
288
289 def show_couts_maximum_euros(self):
290 try:
291 return self.get_couts_maximum_euros()
292 except DeviseException, e:
293 return e
294
295 def show_salaire_maximum_euros(self):
296 try:
297 return self.get_salaire_maximum_euros()
298 except DeviseException, e:
299 return e
300
301 # Comparaison de poste
302 def est_comparable(self):
303 """
304 Si on a au moins une valeur de saisie dans les comparaisons, alors
305 le poste est comparable.
306 """
307 if self.comp_universite_min is None and \
308 self.comp_fonctionpub_min is None and \
309 self.comp_locale_min is None and \
310 self.comp_ong_min is None and \
311 self.comp_autre_min is None and \
312 self.comp_universite_max is None and \
313 self.comp_fonctionpub_max is None and \
314 self.comp_locale_max is None and \
315 self.comp_ong_max is None and \
316 self.comp_autre_max is None:
317 return False
318 else:
319 return True
320
321 def get_taux_comparaison(self):
322 try:
323 return rh.TauxChange.objects \
324 .filter(devise=self.devise_comparaison)[0].taux
325 except:
326 return 1
327
328 def get_comp_universite_min_euros(self):
329 return (float)(self.comp_universite_min) * self.get_taux_comparaison()
330
331 def get_comp_fonctionpub_min_euros(self):
332 return (float)(self.comp_fonctionpub_min) * self.get_taux_comparaison()
333
334 def get_comp_locale_min_euros(self):
335 return (float)(self.comp_locale_min) * self.get_taux_comparaison()
336
337 def get_comp_ong_min_euros(self):
338 return (float)(self.comp_ong_min) * self.get_taux_comparaison()
339
340 def get_comp_autre_min_euros(self):
341 return (float)(self.comp_autre_min) * self.get_taux_comparaison()
342
343 def get_comp_universite_max_euros(self):
344 return (float)(self.comp_universite_max) * self.get_taux_comparaison()
345
346 def get_comp_fonctionpub_max_euros(self):
347 return (float)(self.comp_fonctionpub_max) * self.get_taux_comparaison()
348
349 def get_comp_locale_max_euros(self):
350 return (float)(self.comp_locale_max) * self.get_taux_comparaison()
351
352 def get_comp_ong_max_euros(self):
353 return (float)(self.comp_ong_max) * self.get_taux_comparaison()
354
355 def get_comp_autre_max_euros(self):
356 return (float)(self.comp_autre_max) * self.get_taux_comparaison()
357
358 def __unicode__(self):
359 """
360 Cette fonction est consommatrice SQL car elle cherche les dossiers
361 qui ont été liés à celui-ci.
362 """
363 try:
364 data = (
365 self.implantation,
366 self.type_poste.nom,
367 self.nom,
368 )
369 except:
370 return self.nom
371 return u'%s - %s (%s)' % data
372
373 reversion.register(Poste, format='xml', follow=[
374 'dae_financements', 'dae_pieces', 'dae_comparaisons_internes'
375 ])
376
377 POSTE_FINANCEMENT_CHOICES = (
378 ('A', 'A - Frais de personnel'),
379 ('B', 'B - Projet(s)-Titre(s)'),
380 ('C', 'C - Autre')
381 )
382
383
384 class PosteFinancement(rh.PosteFinancement_):
385 poste = models.ForeignKey(
386 Poste, db_column='poste', related_name='dae_financements'
387 )
388
389 reversion.register(PosteFinancement, format='xml')
390
391
392 class PostePiece(rh.PostePiece_):
393 poste = models.ForeignKey(
394 Poste, db_column='poste', related_name='dae_pieces'
395 )
396
397 reversion.register(PostePiece, format='xml')
398
399
400 class PosteComparaison(rh.PosteComparaison_):
401 poste = models.ForeignKey(
402 Poste, related_name='dae_comparaisons_internes'
403 )
404 statut = models.ForeignKey(
405 rh.Statut, related_name='+', verbose_name=u'Statut', null=True,
406 blank=True
407 )
408 classement = models.ForeignKey(
409 rh.Classement, related_name='+', verbose_name=u'Classement',
410 null=True, blank=True
411 )
412
413 reversion.register(PosteComparaison, format='xml')
414
415 ### EMPLOYÉ/PERSONNE
416
417 # TODO : migration pour m -> M, f -> F
418
419 GENRE_CHOICES = (
420 ('m', 'Homme'),
421 ('f', 'Femme'),
422 )
423
424
425 class Employe(models.Model):
426
427 # Modèle existant
428 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
429 verbose_name=u'Employé')
430 nom = models.CharField(max_length=255)
431 prenom = models.CharField(max_length=255, verbose_name=u'Prénom')
432 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
433
434 def __unicode__(self):
435 return u'%s %s' % (self.prenom, self.nom.upper())
436
437 def dans_rh(self):
438 """
439 Retourne l'employé RH associé à cet employé DAE.
440 """
441 return self.id_rh
442
443 def importer_dans_rh(self):
444 """
445 Importe l'employé DAE dans un employé RH existant ou nouveau.
446 """
447 employe_rh = self.dans_rh() or rh.Employe.objects.create(
448 nom=self.nom,
449 prenom=self.prenom,
450 genre=self.genre
451 )
452 self.id_rh = employe_rh
453 self.save()
454 return employe_rh
455
456 reversion.register(Employe, format='xml')
457
458
459 ### DOSSIER
460
461 STATUT_RESIDENCE_CHOICES = (
462 ('local', 'Local'),
463 ('expat', 'Expatrié'),
464 )
465
466 COMPTE_COMPTA_CHOICES = (
467 ('coda', 'CODA'),
468 ('scs', 'SCS'),
469 ('aucun', 'Aucun'),
470 )
471
472
473 class Dossier(DossierWorkflow, rh.Dossier_):
474 poste = models.ForeignKey(
475 'Poste', db_column='poste', related_name='dae_dossiers'
476 )
477 employe = models.ForeignKey(
478 'Employe', db_column='employe',
479 related_name='rh_dossiers', verbose_name=u"Employé"
480 )
481 organisme_bstg_autre = models.CharField(max_length=255,
482 verbose_name=u"Autre organisme",
483 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
484 null=True,
485 blank=True,)
486
487 # Lien avec le dossier
488 dossier_rh = models.ForeignKey(
489 rh.Dossier, related_name='dossiers_dae', null=True, blank=True
490 )
491
492 # Données antérieures de l'employé
493 statut_anterieur = models.ForeignKey(
494 rh.Statut, related_name='+', null=True, blank=True,
495 verbose_name=u'Statut antérieur')
496 classement_anterieur = models.ForeignKey(
497 rh.Classement, related_name='+', null=True, blank=True,
498 verbose_name=u'Classement précédent')
499 salaire_anterieur = models.DecimalField(
500 max_digits=12, decimal_places=2, null=True, default=None,
501 blank=True, verbose_name=u'Salaire précédent')
502 devise_anterieur = models.ForeignKey(
503 rh.Devise, related_name='+', null=True, blank=True
504 )
505 type_contrat_anterieur = models.ForeignKey(
506 rh.TypeContrat, related_name='+', null=True, blank=True,
507 verbose_name=u'Type contrat antérieur'
508 )
509
510 # Données du titulaire précédent
511 employe_anterieur = models.ForeignKey(
512 rh.Employe, related_name='+', null=True, blank=True,
513 verbose_name=u'Employé précédent')
514 statut_titulaire_anterieur = models.ForeignKey(
515 rh.Statut, related_name='+', null=True, blank=True,
516 verbose_name=u'Statut titulaire précédent')
517 classement_titulaire_anterieur = models.ForeignKey(
518 rh.Classement, related_name='+', null=True, blank=True,
519 verbose_name=u'Classement titulaire précédent')
520 salaire_titulaire_anterieur = models.DecimalField(
521 max_digits=12, decimal_places=2, default=None, null=True,
522 blank=True, verbose_name=u'Salaire titulaire précédent')
523 devise_titulaire_anterieur = models.ForeignKey(
524 rh.Devise, related_name='+', null=True, blank=True
525 )
526
527 # Rémunération
528 salaire = models.DecimalField(max_digits=13, decimal_places=2,
529 verbose_name=u'Salaire de base',
530 null=True, default=None)
531 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
532
533 # Contrat
534 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
535 contrat_date_debut = models.DateField(help_text=HELP_TEXT_DATE)
536 contrat_date_fin = models.DateField(null=True, blank=True,
537 help_text=HELP_TEXT_DATE)
538
539 # Justifications
540 justif_nouveau_statut_label = u'Justifier le statut que ce type ' \
541 u'de poste nécessite (national, expatrié, màd ou détachement)'
542 justif_nouveau_statut = models.TextField(
543 verbose_name=justif_nouveau_statut_label, null=True, blank=True
544 )
545 justif_nouveau_tmp_remplacement_label = u"Si l'employé effectue un " \
546 u"remplacement temporaire, préciser"
547 justif_nouveau_tmp_remplacement = models.TextField(
548 verbose_name=justif_nouveau_tmp_remplacement_label, null=True,
549 blank=True
550 )
551 justif_nouveau_salaire_label = u"Si le salaire de l'employé ne " \
552 u"correspond pas au classement du poste ou est différent " \
553 u"du salaire antérieur, justifier "
554 justif_nouveau_salaire = models.TextField(
555 verbose_name=justif_nouveau_salaire_label, null=True, blank=True
556 )
557 justif_nouveau_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
558 justif_nouveau_commentaire = models.TextField(
559 verbose_name=justif_nouveau_commentaire_label, null=True, blank=True
560 )
561 justif_rempl_type_contrat_label = \
562 u"Changement de type de contrat, ex : d'un CDD en CDI"
563 justif_rempl_type_contrat = models.TextField(
564 verbose_name=justif_rempl_type_contrat_label, null=True, blank=True
565 )
566 justif_rempl_statut_employe_label = \
567 u"Si le statut de l'employé a été modifié pour ce poste ; " \
568 u"ex : national, expatrié, màd, détachement ? Si oui, justifier"
569 justif_rempl_statut_employe = models.TextField(
570 verbose_name=justif_rempl_statut_employe_label, null=True, blank=True
571 )
572 justif_rempl_evaluation_label = \
573 u"L'évaluation de l'employé est-elle favorable? Préciser"
574 justif_rempl_evaluation = models.TextField(
575 verbose_name=justif_rempl_evaluation_label, null=True, blank=True
576 )
577 justif_rempl_salaire_label = \
578 u"Si le salaire de l'employé est modifié et/ou ne correspond " \
579 u"pas à son classement, justifier"
580 justif_rempl_salaire = models.TextField(
581 verbose_name=justif_rempl_salaire_label, null=True, blank=True
582 )
583 justif_rempl_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
584 justif_rempl_commentaire = models.TextField(
585 verbose_name=justif_rempl_commentaire_label, null=True, blank=True
586 )
587
588 # Comptes
589 compte_compta = models.CharField(max_length=10, default='aucun',
590 verbose_name=u'Compte comptabilité',
591 choices=COMPTE_COMPTA_CHOICES)
592 compte_courriel = models.BooleanField()
593
594 # DAE numérisée
595 dae_numerisee = models.FileField(
596 upload_to='dae/dae_numerisee', storage=UPLOAD_STORAGE, blank=True,
597 null=True, verbose_name="DAE numérisée"
598 )
599
600 # Managers
601 objects = DossierManager()
602
603 def __init__(self, *args, **kwargs):
604 # Bouchon pour créer une date fictive necessaire pour valider un
605 # dossier à cause de l'héritage
606 super(rh.Dossier_, self).__init__(*args, **kwargs)
607 super(DossierWorkflow, self).__init__(*args, **kwargs)
608 import datetime
609 self.date_debut = datetime.datetime.today()
610
611 def __unicode__(self):
612 return u'[%s] %s - %s' % (
613 self.poste.implantation, self.poste.nom, self.employe
614 )
615
616 def dans_rh(self):
617 """
618 Retourne le dossier associé dans le système RH ou ``None`` s'il n'y
619 en a pas.
620 """
621 if self.dossier_rh:
622 return self.dossier_rh
623 today = date.today()
624 poste_rh = self.poste.dans_rh()
625 if poste_rh is None:
626 return None
627 employe_rh = self.employe.dans_rh()
628 if employe_rh is None:
629 return None
630 try:
631 return rh.Dossier.objects.get(
632 Q(date_debut=None) | Q(date_debut__lte=today),
633 Q(date_fin=None) | Q(date_fin__gte=today),
634 poste=poste_rh,
635 employe=employe_rh
636 )
637 except rh.Dossier.DoesNotExist:
638 return None
639
640 def importer_dans_rh(self):
641 """
642 Importe les données du dossier DAE dans un dossier RH existant ou
643 nouveau.
644 """
645 poste_rh = self.poste.importer_dans_rh()
646 employe_rh = self.employe.importer_dans_rh()
647 dossier_rh = self.dans_rh() or \
648 rh.Dossier(poste=poste_rh, employe=employe_rh)
649
650 dossier_rh.statut = self.statut
651 dossier_rh.organisme_bstg = self.organisme_bstg
652 dossier_rh.remplacement = self.remplacement
653 dossier_rh.remplacement_de = self.remplacement_de
654 dossier_rh.statut_residence = self.statut_residence
655 dossier_rh.classement = self.classement
656 dossier_rh.regime_travail = self.regime_travail
657 dossier_rh.regime_travail_nb_heure_semaine = \
658 self.regime_travail_nb_heure_semaine
659 dossier_rh.date_debut = self.contrat_date_debut
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 if not settings.DEBUG:
687 piece_rh.fichier.save(
688 os.path.basename(piece.fichier.name), piece.fichier
689 )
690
691 if self.dae_numerisee:
692 dae_numerisee_rh = dossier_rh.rh_dossierpieces.create(
693 nom=u'DAE numérisée'
694 )
695 if not settings.DEBUG:
696 dae_numerisee_rh.fichier.save(
697 os.path.basename(self.dae_numerisee.name),
698 self.dae_numerisee
699 )
700
701 # Fermer les rémunérations qui commencent avant le début du contrat
702 dossier_rh.rh_remunerations.filter(
703 Q(date_debut=None) | Q(date_debut__lt=self.contrat_date_debut),
704 Q(date_fin=None) | Q(date_fin__gte=self.contrat_date_debut)
705 ).update(date_fin=self.contrat_date_debut - timedelta(1))
706
707 # Effacer les rémunérations qui commencent à la date du contrat
708 dossier_rh.rh_remunerations \
709 .filter(date_debut=self.contrat_date_debut) \
710 .delete()
711
712 for remun in self.dae_remunerations.all():
713 dossier_rh.rh_remunerations.get_or_create(
714 type=remun.type,
715 type_revalorisation=remun.type_revalorisation,
716 montant=remun.montant,
717 devise=remun.devise,
718 commentaire=remun.commentaire,
719 date_debut=self.contrat_date_debut,
720 date_fin=self.contrat_date_fin
721 )
722
723 # Enregistrer le lien avec le dossier RH
724 self.dossier_rh = dossier_rh
725 self.save()
726
727 return dossier_rh
728
729 def get_salaire_anterieur_euros(self):
730 if self.devise_anterieur is None:
731 return None
732 try:
733 taux = self.taux_devise(self.devise_anterieur)
734 except Exception, e:
735 return e
736 if not taux:
737 return None
738 return int(round(float(self.salaire_anterieur) * float(taux), 2))
739
740 def get_salaire_titulaire_anterieur_euros(self):
741 if self.devise_titulaire_anterieur is None:
742 return None
743 try:
744 taux = self.taux_devise(self.devise_titulaire_anterieur)
745 except Exception, e:
746 return e
747 if not taux:
748 return None
749 return int(round(
750 float(self.salaire_titulaire_anterieur) * float(taux), 2
751 ))
752
753 def valide(self):
754 return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
755 DOSSIER_ETAT_DRH_FINALISATION,
756 DOSSIER_ETAT_FINALISE)
757
758 reversion.register(Dossier, format='xml', follow=[
759 'dae_dossierpieces', 'dae_comparaisons', 'dae_remunerations',
760 'dae_contrats'
761 ])
762
763
764 class DossierPiece(rh.DossierPiece_):
765 """
766 Documents relatifs au Dossier (à l'occupation de ce poste par employé).
767 Ex.: Lettre de motivation.
768 """
769 dossier = models.ForeignKey(
770 Dossier, db_column='dossier', related_name='dae_dossierpieces'
771 )
772
773 reversion.register(DossierPiece, format='xml')
774
775
776 class DossierComparaison(rh.DossierComparaison_):
777 """
778 Photo d'une comparaison salariale au moment de l'embauche.
779 """
780 dossier = models.ForeignKey(
781 Dossier, related_name='dae_comparaisons'
782 )
783 statut = models.ForeignKey(
784 rh.Statut, related_name='+', verbose_name='Statut', null=True,
785 blank=True
786 )
787 classement = models.ForeignKey(
788 rh.Classement, related_name='+', verbose_name='Classement',
789 null=True, blank=True
790 )
791
792 reversion.register(DossierComparaison, format='xml')
793
794
795 ### RÉMUNÉRATION
796
797 class Remuneration(rh.Remuneration_):
798 dossier = models.ForeignKey(
799 Dossier, db_column='dossier', related_name='dae_remunerations'
800 )
801
802 reversion.register(Remuneration, format='xml')
803
804
805 ### CONTRATS
806
807 class Contrat(rh.Contrat_):
808 dossier = models.ForeignKey(
809 Dossier, db_column='dossier', related_name='dae_contrats'
810 )
811
812 reversion.register(Contrat, format='xml')
813
814
815 class ProxyDossierStatut(Dossier):
816 class Meta:
817 proxy = True
818 verbose_name = "Statut du dossier"
819 verbose_name_plural = "Statut des dossiers"
820
821
822 class ProxyPosteStatut(Poste):
823 class Meta:
824 proxy = True
825 verbose_name = "Statut du poste"
826 verbose_name_plural = "Statut des postes"