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