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