fbb8f7a6453de100e6ad7d8e313199195068f903
[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 COMPTE_COMPTA_CHOICES = (
480 ('coda', 'CODA'),
481 ('scs', 'SCS'),
482 ('aucun', 'Aucun'),
483 )
484
485
486 class Dossier(DossierWorkflow, rh.Dossier_):
487 poste = models.ForeignKey(
488 'Poste', db_column='poste', related_name='dae_dossiers'
489 )
490 employe = models.ForeignKey(
491 'Employe', db_column='employe',
492 related_name='rh_dossiers', verbose_name=u"Employé"
493 )
494 organisme_bstg_autre = models.CharField(max_length=255,
495 verbose_name=u"Autre organisme",
496 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
497 null=True,
498 blank=True,)
499
500 # Lien avec le dossier
501 dossier_rh = models.ForeignKey(
502 rh.Dossier, related_name='dossiers_dae', null=True, blank=True
503 )
504
505 # Données antérieures de l'employé
506 statut_anterieur = models.ForeignKey(
507 rh.Statut, related_name='+', null=True, blank=True,
508 verbose_name=u'Statut antérieur')
509 classement_anterieur = models.ForeignKey(
510 rh.Classement, related_name='+', null=True, blank=True,
511 verbose_name=u'Classement précédent')
512 salaire_anterieur = models.DecimalField(
513 max_digits=12, decimal_places=2, null=True, default=None,
514 blank=True, verbose_name=u'Salaire précédent')
515 devise_anterieur = models.ForeignKey(
516 rh.Devise, related_name='+', null=True, blank=True
517 )
518 type_contrat_anterieur = models.ForeignKey(
519 rh.TypeContrat, related_name='+', null=True, blank=True,
520 verbose_name=u'Type contrat antérieur'
521 )
522
523 # Données du titulaire précédent
524 employe_anterieur = models.ForeignKey(
525 rh.Employe, related_name='+', null=True, blank=True,
526 verbose_name=u'Employé précédent')
527 statut_titulaire_anterieur = models.ForeignKey(
528 rh.Statut, related_name='+', null=True, blank=True,
529 verbose_name=u'Statut titulaire précédent')
530 classement_titulaire_anterieur = models.ForeignKey(
531 rh.Classement, related_name='+', null=True, blank=True,
532 verbose_name=u'Classement titulaire précédent')
533 salaire_titulaire_anterieur = models.DecimalField(
534 max_digits=12, decimal_places=2, default=None, null=True,
535 blank=True, verbose_name=u'Salaire titulaire précédent')
536 devise_titulaire_anterieur = models.ForeignKey(
537 rh.Devise, related_name='+', null=True, blank=True
538 )
539
540 # Rémunération
541 salaire = models.DecimalField(max_digits=13, decimal_places=2,
542 verbose_name=u'Salaire de base',
543 null=True, default=None)
544 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
545
546 # Contrat
547 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
548 contrat_date_debut = models.DateField(help_text=HELP_TEXT_DATE)
549 contrat_date_fin = models.DateField(null=True, blank=True,
550 help_text=HELP_TEXT_DATE)
551
552 # Justifications
553 justif_nouveau_statut_label = u'Justifier le statut que ce type ' \
554 u'de poste nécessite (national, expatrié, màd ou détachement)'
555 justif_nouveau_statut = models.TextField(
556 verbose_name=justif_nouveau_statut_label, null=True, blank=True
557 )
558 justif_nouveau_tmp_remplacement_label = u"Si l'employé effectue un " \
559 u"remplacement temporaire, préciser"
560 justif_nouveau_tmp_remplacement = models.TextField(
561 verbose_name=justif_nouveau_tmp_remplacement_label, null=True,
562 blank=True
563 )
564 justif_nouveau_salaire_label = u"Si le salaire de l'employé ne " \
565 u"correspond pas au classement du poste ou est différent " \
566 u"du salaire antérieur, justifier "
567 justif_nouveau_salaire = models.TextField(
568 verbose_name=justif_nouveau_salaire_label, null=True, blank=True
569 )
570 justif_nouveau_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
571 justif_nouveau_commentaire = models.TextField(
572 verbose_name=justif_nouveau_commentaire_label, null=True, blank=True
573 )
574 justif_rempl_type_contrat_label = \
575 u"Changement de type de contrat, ex : d'un CDD en CDI"
576 justif_rempl_type_contrat = models.TextField(
577 verbose_name=justif_rempl_type_contrat_label, null=True, blank=True
578 )
579 justif_rempl_statut_employe_label = \
580 u"Si le statut de l'employé a été modifié pour ce poste ; " \
581 u"ex : national, expatrié, màd, détachement ? Si oui, justifier"
582 justif_rempl_statut_employe = models.TextField(
583 verbose_name=justif_rempl_statut_employe_label, null=True, blank=True
584 )
585 justif_rempl_evaluation_label = \
586 u"L'évaluation de l'employé est-elle favorable? Préciser"
587 justif_rempl_evaluation = models.TextField(
588 verbose_name=justif_rempl_evaluation_label, null=True, blank=True
589 )
590 justif_rempl_salaire_label = \
591 u"Si le salaire de l'employé est modifié et/ou ne correspond " \
592 u"pas à son classement, justifier"
593 justif_rempl_salaire = models.TextField(
594 verbose_name=justif_rempl_salaire_label, null=True, blank=True
595 )
596 justif_rempl_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
597 justif_rempl_commentaire = models.TextField(
598 verbose_name=justif_rempl_commentaire_label, null=True, blank=True
599 )
600
601 # Comptes
602 compte_compta = models.CharField(max_length=10, default='aucun',
603 verbose_name=u'Compte comptabilité',
604 choices=COMPTE_COMPTA_CHOICES)
605 compte_courriel = models.BooleanField()
606
607 # DAE numérisée
608 dae_numerisee = models.FileField(
609 upload_to='dae/dae_numerisee', storage=UPLOAD_STORAGE, blank=True,
610 null=True, verbose_name="DAE numérisée"
611 )
612
613 # Managers
614 objects = DossierManager()
615
616 def __init__(self, *args, **kwargs):
617 # Bouchon pour créer une date fictive necessaire pour valider un
618 # dossier à cause de l'héritage
619 super(rh.Dossier_, self).__init__(*args, **kwargs)
620 super(DossierWorkflow, self).__init__(*args, **kwargs)
621 import datetime
622 self.date_debut = datetime.datetime.today()
623
624 def __unicode__(self):
625 return u'[%s] %s - %s' % (
626 self.poste.implantation, self.poste.nom, self.employe
627 )
628
629 def dans_rh(self):
630 """
631 Retourne le dossier associé dans le système RH ou ``None`` s'il n'y
632 en a pas.
633 """
634 if self.dossier_rh:
635 return self.dossier_rh
636 today = date.today()
637 poste_rh = self.poste.dans_rh()
638 if poste_rh is None:
639 return None
640 employe_rh = self.employe.dans_rh()
641 if employe_rh is None:
642 return None
643 try:
644 return rh.Dossier.objects.get(
645 Q(date_debut=None) | Q(date_debut__lte=today),
646 Q(date_fin=None) | Q(date_fin__gte=today),
647 poste=poste_rh,
648 employe=employe_rh
649 )
650 except rh.Dossier.DoesNotExist:
651 return None
652
653 def importer_dans_rh(self):
654 """
655 Importe les données du dossier DAE dans un dossier RH existant ou
656 nouveau.
657 """
658 poste_rh = self.poste.importer_dans_rh()
659 employe_rh = self.employe.importer_dans_rh()
660 dossier_rh = self.dans_rh() or \
661 rh.Dossier(poste=poste_rh, employe=employe_rh)
662
663 dossier_rh.statut = self.statut
664 dossier_rh.organisme_bstg = self.organisme_bstg
665 dossier_rh.remplacement = self.remplacement
666 dossier_rh.remplacement_de = self.remplacement_de
667 dossier_rh.statut_residence = self.statut_residence
668 dossier_rh.classement = self.classement
669 dossier_rh.regime_travail = self.regime_travail
670 dossier_rh.est_cadre = self.est_cadre
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_salaire_anterieur_euros(self):
744 if self.devise_anterieur is None:
745 return None
746 try:
747 taux = self.taux_devise(self.devise_anterieur)
748 except Exception, e:
749 return e
750 if not taux:
751 return None
752 return int(round(float(self.salaire_anterieur) * float(taux), 2))
753
754 def get_salaire_titulaire_anterieur_euros(self):
755 if self.devise_titulaire_anterieur is None:
756 return None
757 try:
758 taux = self.taux_devise(self.devise_titulaire_anterieur)
759 except Exception, e:
760 return e
761 if not taux:
762 return None
763 return int(round(
764 float(self.salaire_titulaire_anterieur) * float(taux), 2
765 ))
766
767 def valide(self):
768 return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
769 DOSSIER_ETAT_DRH_FINALISATION,
770 DOSSIER_ETAT_FINALISE)
771
772 reversion.register(Dossier, format='xml', follow=[
773 'dae_dossierpieces', 'dae_comparaisons', 'dae_remunerations',
774 'dae_contrats'
775 ])
776
777
778 class DossierPiece(rh.DossierPiece_):
779 """
780 Documents relatifs au Dossier (à l'occupation de ce poste par employé).
781 Ex.: Lettre de motivation.
782 """
783 dossier = models.ForeignKey(
784 Dossier, db_column='dossier', related_name='dae_dossierpieces'
785 )
786
787 reversion.register(DossierPiece, format='xml')
788
789
790 class DossierComparaison(rh.DossierComparaison_):
791 """
792 Photo d'une comparaison salariale au moment de l'embauche.
793 """
794 dossier = models.ForeignKey(
795 Dossier, related_name='dae_comparaisons'
796 )
797 statut = models.ForeignKey(
798 rh.Statut, related_name='+', verbose_name='Statut', null=True,
799 blank=True
800 )
801 classement = models.ForeignKey(
802 rh.Classement, related_name='+', verbose_name='Classement',
803 null=True, blank=True
804 )
805
806 reversion.register(DossierComparaison, format='xml')
807
808 class DossierComparaisonRemuneration(rh.Remuneration_):
809 dossier_comparaison = models.ForeignKey(
810 DossierComparaison, related_name='dossier_comparaison_remunerations'
811 )
812
813 reversion.register(DossierComparaisonRemuneration, format='xml')
814
815 ### RÉMUNÉRATION
816
817 class Remuneration(rh.Remuneration_):
818 dossier = models.ForeignKey(
819 Dossier, db_column='dossier', related_name='dae_remunerations'
820 )
821
822 reversion.register(Remuneration, format='xml')
823
824
825 ### CONTRATS
826
827 class Contrat(rh.Contrat_):
828 dossier = models.ForeignKey(
829 Dossier, db_column='dossier', related_name='dae_contrats'
830 )
831
832 reversion.register(Contrat, format='xml')
833
834
835 class ProxyDossierStatut(Dossier):
836 class Meta:
837 proxy = True
838 verbose_name = "Statut du dossier"
839 verbose_name_plural = "Statut des dossiers"
840
841
842 class ProxyPosteStatut(Poste):
843 class Meta:
844 proxy = True
845 verbose_name = "Statut du poste"
846 verbose_name_plural = "Statut des postes"
847
848
849 class ProxyImplantation(ref.Implantation):
850
851 # Ajout d'un manager pour ref.Implantation
852 dae_manager = ImplantationManager()
853
854 class Meta:
855 proxy = True