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