fix for 500 error
[auf_rh_dae.git] / project / dae / models.py
CommitLineData
bd28238f 1# -=- encoding: utf-8 -=-
3f3cf5f3 2
fb0ed970 3import os
b9098c33 4from dateutil.relativedelta import relativedelta
fb0ed970
EMS
5from datetime import date, timedelta
6
5633fa41 7from django.conf import settings
36341125 8from django.core.files.storage import FileSystemStorage
a9c281dd 9from django.db import models
fb0ed970 10from django.db.models import Q
5a1f75cb 11
75f0e87b
DB
12import reversion
13
1d521201
BS
14from auf.django.references import models as ref
15
16from project.dae.managers import (
17 PosteManager, DossierManager, ImplantationManager)
5a1f75cb
EMS
18from project.dae.workflow import PosteWorkflow, DossierWorkflow
19from project.dae.workflow import \
20 DOSSIER_ETAT_DRH_FINALISATION, DOSSIER_ETAT_REGION_FINALISATION, \
21 DOSSIER_ETAT_FINALISE
4ba84959
EMS
22from project.rh import models as rh
23from project.rh.models import HELP_TEXT_DATE
bd28238f 24
d766bf2c 25# Upload de fichiers
34dad720 26UPLOAD_STORAGE = FileSystemStorage(settings.PRIVE_MEDIA_ROOT)
d766bf2c 27
36341125 28
2d4d2fcf 29### POSTE
30
31POSTE_APPEL_CHOICES = (
32 ('interne', 'Interne'),
33 ('externe', 'Externe'),
34)
c3be904d
OL
35POSTE_ACTION = (
36 ('N', u"Nouveau poste"),
37 ('M', u"Poste existant"),
38 ('E', u"Évolution de poste"),
39)
40
36341125 41
ae5c920b 42class DeviseException(Exception):
5a1f75cb 43 silent_variable_failure = True
ae5c920b 44
1c7d67ce 45
3f5cbabe 46class Poste(PosteWorkflow, rh.Poste_):
c3be904d 47
5a1f75cb
EMS
48 type_intervention = models.CharField(
49 max_length=1, choices=POSTE_ACTION, default='N'
50 )
c3be904d 51
bd28238f 52 # Modèle existant
8b08fd5a 53 id_rh = models.ForeignKey(
de151a1e 54 rh.Poste, null=True, related_name='postes_dae', editable=False,
8b08fd5a
EMS
55 verbose_name=u"Mise à jour du poste"
56 )
bd28238f
NC
57
58 # Rémunération
5a1f75cb
EMS
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 )
bd28238f 77
1c7d67ce
OL
78 # Managers
79 objects = PosteManager()
80
03858ba5
OL
81 def _get_key(self):
82 """
1bc84af4 83 Les vues sont montées selon une clef spéciale
2d4d2fcf 84 pour identifier la provenance du poste.
1bc84af4 85 Cette méthode fournit un moyen de reconstruire cette clef
2d4d2fcf 86 afin de générer les URLs.
03858ba5
OL
87 """
88 return "dae-%s" % self.id
89 key = property(_get_key)
90
f3333b0e
OL
91 def get_dossiers(self):
92 """
93 Liste tous les anciens dossiers liés à ce poste.
1bc84af4 94 (Le nom de la relation sur le rh.Poste est mal choisi
2d4d2fcf 95 poste1 au lieu de dossier1)
f3333b0e 96 Note1 : seulement le dosssier principal fait l'objet de la recherche.
1bc84af4
EMS
97 Note2 : les dossiers sont retournés du plus récent au plus vieux.
98 (Ce test est fait en fonction du id,
2d4d2fcf 99 car les dates de création sont absentes de rh v1).
f3333b0e
OL
100 """
101 if self.id_rh is None:
102 return []
16b1454e 103 return self.id_rh.rh_dossiers.all()
428e3c0b 104
47b60f16 105 def dans_rh(self):
8b08fd5a
EMS
106 """
107 Retourne le poste RH s'il existe.
108 """
109 return self.id_rh
47b60f16 110
fb0ed970 111 def importer_dans_rh(self):
8b08fd5a
EMS
112 """
113 Importe le poste DAE dans un poste RH existant ou nouveau.
114 """
3e30a959
EMS
115 dans_rh = self.dans_rh()
116 poste_rh = dans_rh or rh.Poste()
fb0ed970
EMS
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
d4a03f25
EMS
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
fb0ed970
EMS
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
3e30a959
EMS
157 if not dans_rh:
158 poste_rh.date_debut = self.date_debut
159 poste_rh.date_fin = self.date_fin
fb0ed970
EMS
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 )
7ad3b930
OL
167 if not settings.DEBUG:
168 piece_rh.fichier.save(
169 os.path.basename(piece.fichier.name), piece.fichier
fb0ed970
EMS
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
8b08fd5a
EMS
189 self.id_rh = poste_rh
190 self.save()
fb0ed970
EMS
191 return poste_rh
192
f3333b0e
OL
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
179f6b49 203 def get_default_devise(self):
1bc84af4 204 """Récupère la devise par défaut en fonction de l'implantation
2d4d2fcf 205 (EUR autrement)
206 """
179f6b49 207 try:
6e4600ef 208 implantation_devise = rh.TauxChange.objects \
209 .filter(implantation=self.implantation)[0].devise
179f6b49 210 except:
5a1f75cb 211 implantation_devise = 5 # EUR
179f6b49
OL
212 return implantation_devise
213
c0413a6f
OL
214 #####################
215 # Classement de poste
216 #####################
217
218 def get_couts_minimum(self):
5a1f75cb
EMS
219 return self.salaire_min + self.indemn_expat_min + \
220 self.indemn_fct_min + self.charges_patronales_min + \
221 self.autre_min
83b94a87
EMS
222
223 def get_salaire_minimum(self):
224 return self.get_couts_minimum() - self.charges_patronales_min
c0413a6f
OL
225
226 def get_taux_minimum(self):
4e439a89 227 if self.devise_min.code == 'EUR':
5a1f75cb 228 return 1
2455f48d 229 liste_taux = self.devise_min.tauxchange_set.order_by('-annee')
4e439a89 230 if len(liste_taux) == 0:
5a1f75cb
EMS
231 raise DeviseException(
232 u"La devise %s n'a pas de taux pour l'implantation %s" %
233 (self.devise_min, self.implantation))
b6825282 234 else:
4e439a89 235 return liste_taux[0].taux
c0413a6f
OL
236
237 def get_couts_minimum_euros(self):
fa5b95ed 238 return float(self.get_couts_minimum()) * self.get_taux_minimum()
c0413a6f 239
83b94a87 240 def get_salaire_minimum_euros(self):
fa5b95ed 241 return float(self.get_salaire_minimum()) * self.get_taux_minimum()
83b94a87 242
c0413a6f 243 def get_couts_maximum(self):
5a1f75cb
EMS
244 return self.salaire_max + self.indemn_expat_max + \
245 self.indemn_fct_max + self.charges_patronales_max + \
246 self.autre_max
83b94a87
EMS
247
248 def get_salaire_maximum(self):
249 return self.get_couts_maximum() - self.charges_patronales_max
c0413a6f
OL
250
251 def get_taux_maximum(self):
4e439a89 252 if self.devise_max.code == 'EUR':
5a1f75cb 253 return 1
2455f48d 254 liste_taux = self.devise_max.tauxchange_set.order_by('-annee')
4e439a89 255 if len(liste_taux) == 0:
5a1f75cb
EMS
256 raise DeviseException(
257 u"La devise %s n'a pas de taux pour l'implantation %s" %
258 (self.devise_max, self.implantation)
259 )
b6825282 260 else:
4e439a89 261 return liste_taux[0].taux
c0413a6f
OL
262
263 def get_couts_maximum_euros(self):
fa5b95ed 264 return float(self.get_couts_maximum()) * self.get_taux_maximum()
c0413a6f 265
83b94a87 266 def get_salaire_maximum_euros(self):
fa5b95ed 267 return float(self.get_salaire_maximum()) * self.get_taux_maximum()
12c7f8a7
OL
268
269 def show_taux_minimum(self):
270 try:
271 return self.get_taux_minimum()
83b94a87 272 except DeviseException, e:
12c7f8a7
OL
273 return e
274
275 def show_couts_minimum_euros(self):
276 try:
277 return self.get_couts_minimum_euros()
83b94a87
EMS
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:
12c7f8a7
OL
285 return e
286
287 def show_taux_maximum(self):
288 try:
289 return self.get_taux_maximum()
83b94a87 290 except DeviseException, e:
12c7f8a7
OL
291 return e
292
293 def show_couts_maximum_euros(self):
294 try:
295 return self.get_couts_maximum_euros()
83b94a87
EMS
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:
12c7f8a7
OL
303 return e
304
c0413a6f 305 # Comparaison de poste
a3fee9c5
OL
306 def est_comparable(self):
307 """
5a1f75cb
EMS
308 Si on a au moins une valeur de saisie dans les comparaisons, alors
309 le poste est comparable.
a3fee9c5
OL
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
1bc84af4 324
c0413a6f
OL
325 def get_taux_comparaison(self):
326 try:
5a1f75cb
EMS
327 return rh.TauxChange.objects \
328 .filter(devise=self.devise_comparaison)[0].taux
c0413a6f
OL
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
5d680e84 362 def __unicode__(self):
f3333b0e 363 """
1bc84af4 364 Cette fonction est consommatrice SQL car elle cherche les dossiers
2d4d2fcf 365 qui ont été liés à celui-ci.
f3333b0e 366 """
5ac79652
OL
367 try:
368 data = (
369 self.implantation,
370 self.type_poste.nom,
371 self.nom,
372 )
373 except:
374 return self.nom
a7c68130 375 return u'%s - %s (%s)' % data
5d680e84 376
cea09938
EMS
377reversion.register(Poste, format='xml', follow=[
378 'dae_financements', 'dae_pieces', 'dae_comparaisons_internes'
379])
a9c281dd 380
5d680e84
NC
381POSTE_FINANCEMENT_CHOICES = (
382 ('A', 'A - Frais de personnel'),
383 ('B', 'B - Projet(s)-Titre(s)'),
384 ('C', 'C - Autre')
385)
bd28238f 386
5a1f75cb 387
317ce433 388class PosteFinancement(rh.PosteFinancement_):
4ba84959
EMS
389 poste = models.ForeignKey(
390 Poste, db_column='poste', related_name='dae_financements'
391 )
1d0f4eef 392
cea09938
EMS
393reversion.register(PosteFinancement, format='xml')
394
5a1f75cb 395
317ce433 396class PostePiece(rh.PostePiece_):
4ba84959
EMS
397 poste = models.ForeignKey(
398 Poste, db_column='poste', related_name='dae_pieces'
399 )
068d1462 400
cea09938
EMS
401reversion.register(PostePiece, format='xml')
402
5a1f75cb 403
317ce433 404class PosteComparaison(rh.PosteComparaison_):
4ba84959
EMS
405 poste = models.ForeignKey(
406 Poste, related_name='dae_comparaisons_internes'
407 )
5a1f75cb
EMS
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 )
068d1462 416
cea09938
EMS
417reversion.register(PosteComparaison, format='xml')
418
b9098c33
BS
419
420class PosteComparaisonRemuneration(rh.Remuneration_):
421 poste_comparaison = models.ForeignKey(
422 PosteComparaison, related_name='poste_comparaison_remunerations'
423 )
424
425reversion.register(PosteComparaisonRemuneration, format='xml')
426
427
2d4d2fcf 428### EMPLOYÉ/PERSONNE
429
430# TODO : migration pour m -> M, f -> F
c589d980 431
bd28238f 432GENRE_CHOICES = (
139686f2
NC
433 ('m', 'Homme'),
434 ('f', 'Femme'),
bd28238f
NC
435)
436
5a1f75cb 437
cea09938 438class Employe(models.Model):
bd28238f
NC
439
440 # Modèle existant
428e3c0b 441 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
c1195471 442 verbose_name=u'Employé')
bd28238f 443 nom = models.CharField(max_length=255)
c1195471 444 prenom = models.CharField(max_length=255, verbose_name=u'Prénom')
07b40eda 445 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
bd28238f 446
139686f2 447 def __unicode__(self):
2d4d2fcf 448 return u'%s %s' % (self.prenom, self.nom.upper())
139686f2 449
47b60f16 450 def dans_rh(self):
8b08fd5a
EMS
451 """
452 Retourne l'employé RH associé à cet employé DAE.
453 """
454 return self.id_rh
47b60f16 455
fb0ed970 456 def importer_dans_rh(self):
8b08fd5a
EMS
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(
fb0ed970
EMS
461 nom=self.nom,
462 prenom=self.prenom,
463 genre=self.genre
464 )
8b08fd5a
EMS
465 self.id_rh = employe_rh
466 self.save()
467 return employe_rh
fb0ed970 468
cea09938
EMS
469reversion.register(Employe, format='xml')
470
bd28238f 471
2d4d2fcf 472### DOSSIER
473
474STATUT_RESIDENCE_CHOICES = (
475 ('local', 'Local'),
476 ('expat', 'Expatrié'),
477)
478
16b1454e 479class Dossier(DossierWorkflow, rh.Dossier_):
5a1f75cb 480 poste = models.ForeignKey(
4ba84959 481 'Poste', db_column='poste', related_name='dae_dossiers'
5a1f75cb
EMS
482 )
483 employe = models.ForeignKey(
484 'Employe', db_column='employe',
4ba84959 485 related_name='rh_dossiers', verbose_name=u"Employé"
5a1f75cb 486 )
0288adb5 487 organisme_bstg_autre = models.CharField(max_length=255,
c1195471 488 verbose_name=u"Autre organisme",
0288adb5
OL
489 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
490 null=True,
491 blank=True,)
bd28238f 492
8b08fd5a
EMS
493 # Lien avec le dossier
494 dossier_rh = models.ForeignKey(
495 rh.Dossier, related_name='dossiers_dae', null=True, blank=True
496 )
497
139686f2
NC
498 # Données antérieures de l'employé
499 statut_anterieur = models.ForeignKey(
500 rh.Statut, related_name='+', null=True, blank=True,
c1195471 501 verbose_name=u'Statut antérieur')
139686f2
NC
502 classement_anterieur = models.ForeignKey(
503 rh.Classement, related_name='+', null=True, blank=True,
c1195471 504 verbose_name=u'Classement précédent')
139686f2
NC
505 salaire_anterieur = models.DecimalField(
506 max_digits=12, decimal_places=2, null=True, default=None,
481fbd33 507 blank=True, verbose_name=u'Salaire précédent')
5a1f75cb
EMS
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 )
139686f2
NC
515
516 # Données du titulaire précédent
517 employe_anterieur = models.ForeignKey(
518 rh.Employe, related_name='+', null=True, blank=True,
c1195471 519 verbose_name=u'Employé précédent')
139686f2
NC
520 statut_titulaire_anterieur = models.ForeignKey(
521 rh.Statut, related_name='+', null=True, blank=True,
c1195471 522 verbose_name=u'Statut titulaire précédent')
139686f2
NC
523 classement_titulaire_anterieur = models.ForeignKey(
524 rh.Classement, related_name='+', null=True, blank=True,
c1195471 525 verbose_name=u'Classement titulaire précédent')
139686f2
NC
526 salaire_titulaire_anterieur = models.DecimalField(
527 max_digits=12, decimal_places=2, default=None, null=True,
481fbd33 528 blank=True, verbose_name=u'Salaire titulaire précédent')
5a1f75cb
EMS
529 devise_titulaire_anterieur = models.ForeignKey(
530 rh.Devise, related_name='+', null=True, blank=True
531 )
494ff2be 532
bd28238f 533 # Rémunération
16b1454e 534 salaire = models.DecimalField(max_digits=13, decimal_places=2,
c1195471 535 verbose_name=u'Salaire de base',
2d4d2fcf 536 null=True, default=None)
e8e75458 537 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
bd28238f
NC
538
539 # Contrat
5d680e84 540 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
b666864e 541 contrat_date_debut = models.DateField(help_text=HELP_TEXT_DATE)
1f109689 542 contrat_date_fin = models.DateField(null=True, blank=True,
b666864e 543 help_text=HELP_TEXT_DATE)
bd28238f 544
29dffede 545 # Justifications
5a1f75cb
EMS
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 )
a83daab3 563 justif_nouveau_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
5a1f75cb
EMS
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 )
a83daab3 589 justif_rempl_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
5a1f75cb
EMS
590 justif_rempl_commentaire = models.TextField(
591 verbose_name=justif_rempl_commentaire_label, null=True, blank=True
592 )
29dffede 593
c3f0b49f 594 # DAE numérisée
5a1f75cb
EMS
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 )
c3f0b49f 599
e4f56614
OL
600 # Managers
601 objects = DossierManager()
428e3c0b 602
16b1454e 603 def __init__(self, *args, **kwargs):
5a1f75cb
EMS
604 # Bouchon pour créer une date fictive necessaire pour valider un
605 # dossier à cause de l'héritage
16b1454e
OL
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
aec2c91e 611 def __unicode__(self):
5a1f75cb
EMS
612 return u'[%s] %s - %s' % (
613 self.poste.implantation, self.poste.nom, self.employe
614 )
bd28238f 615
47b60f16
EMS
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 """
8b08fd5a
EMS
621 if self.dossier_rh:
622 return self.dossier_rh
fb0ed970 623 today = date.today()
47b60f16
EMS
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
fb0ed970 630 try:
47b60f16 631 return rh.Dossier.objects.get(
fb0ed970
EMS
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:
47b60f16
EMS
638 return None
639
640 def importer_dans_rh(self):
8b08fd5a
EMS
641 """
642 Importe les données du dossier DAE dans un dossier RH existant ou
643 nouveau.
644 """
47b60f16
EMS
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)
fb0ed970
EMS
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
9623a926 657 dossier_rh.est_cadre = self.est_cadre
89d105e3
BS
658 dossier_rh.compte_compta = self.compte_compta
659 dossier_rh.compte_courriel = self.compte_courriel
fb0ed970
EMS
660 dossier_rh.regime_travail_nb_heure_semaine = \
661 self.regime_travail_nb_heure_semaine
d4a03f25 662 dossier_rh.date_debut = self.contrat_date_debut
fb0ed970
EMS
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
d4a03f25
EMS
675 for contrat in self.dae_contrats.all():
676 contrat_rh = dossier_rh.rh_contrats.create(
fb0ed970
EMS
677 type_contrat=self.type_contrat,
678 date_debut=self.contrat_date_debut,
679 date_fin=self.contrat_date_fin,
680 )
d4a03f25
EMS
681 contrat_rh.fichier.save(
682 os.path.basename(contrat.fichier.name), contrat.fichier
683 )
fb0ed970
EMS
684
685 for piece in self.dae_dossierpieces.all():
686 piece_rh = dossier_rh.rh_dossierpieces.create(
687 nom=piece.nom
688 )
7ad3b930
OL
689 if not settings.DEBUG:
690 piece_rh.fichier.save(
691 os.path.basename(piece.fichier.name), piece.fichier
692 )
fb0ed970 693
d4a03f25
EMS
694 if self.dae_numerisee:
695 dae_numerisee_rh = dossier_rh.rh_dossierpieces.create(
696 nom=u'DAE numérisée'
697 )
66fefd2f
OL
698 if not settings.DEBUG:
699 dae_numerisee_rh.fichier.save(
700 os.path.basename(self.dae_numerisee.name),
701 self.dae_numerisee
702 )
d4a03f25 703
fb0ed970
EMS
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
8b08fd5a
EMS
726 # Enregistrer le lien avec le dossier RH
727 self.dossier_rh = dossier_rh
728 self.save()
729
fb0ed970
EMS
730 return dossier_rh
731
eed93931 732 def get_salaire_anterieur_euros(self):
88a0e98d
OL
733 if self.devise_anterieur is None:
734 return None
03ff41e3
OL
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
eed93931 743 def get_salaire_titulaire_anterieur_euros(self):
16b1454e
OL
744 if self.devise_titulaire_anterieur is None:
745 return None
03ff41e3 746 try:
88a0e98d 747 taux = self.taux_devise(self.devise_titulaire_anterieur)
03ff41e3
OL
748 except Exception, e:
749 return e
750 if not taux:
16b1454e 751 return None
5a1f75cb
EMS
752 return int(round(
753 float(self.salaire_titulaire_anterieur) * float(taux), 2
754 ))
eed93931 755
c511cd1f
EMS
756 def valide(self):
757 return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
758 DOSSIER_ETAT_DRH_FINALISATION,
759 DOSSIER_ETAT_FINALISE)
760
cea09938
EMS
761reversion.register(Dossier, format='xml', follow=[
762 'dae_dossierpieces', 'dae_comparaisons', 'dae_remunerations',
763 'dae_contrats'
764])
bd28238f 765
5a1f75cb 766
a4125771 767class DossierPiece(rh.DossierPiece_):
4ba84959
EMS
768 """
769 Documents relatifs au Dossier (à l'occupation de ce poste par employé).
2d4d2fcf 770 Ex.: Lettre de motivation.
771 """
4ba84959
EMS
772 dossier = models.ForeignKey(
773 Dossier, db_column='dossier', related_name='dae_dossierpieces'
774 )
2d4d2fcf 775
cea09938
EMS
776reversion.register(DossierPiece, format='xml')
777
5a1f75cb 778
a4125771 779class DossierComparaison(rh.DossierComparaison_):
03b395db
OL
780 """
781 Photo d'une comparaison salariale au moment de l'embauche.
782 """
4ba84959
EMS
783 dossier = models.ForeignKey(
784 Dossier, related_name='dae_comparaisons'
785 )
5a1f75cb
EMS
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 )
03b395db 794
cea09938
EMS
795reversion.register(DossierComparaison, format='xml')
796
b9098c33
BS
797class DossierComparaisonRemuneration(rh.Remuneration_):
798 dossier_comparaison = models.ForeignKey(
799 DossierComparaison, related_name='dossier_comparaison_remunerations'
800 )
801
802reversion.register(DossierComparaisonRemuneration, format='xml')
c589d980 803
2d4d2fcf 804### RÉMUNÉRATION
805
a4125771 806class Remuneration(rh.Remuneration_):
4ba84959
EMS
807 dossier = models.ForeignKey(
808 Dossier, db_column='dossier', related_name='dae_remunerations'
809 )
c1834169 810
cea09938
EMS
811reversion.register(Remuneration, format='xml')
812
5a1f75cb 813
c1834169
EMS
814### CONTRATS
815
a4125771 816class Contrat(rh.Contrat_):
4ba84959
EMS
817 dossier = models.ForeignKey(
818 Dossier, db_column='dossier', related_name='dae_contrats'
819 )
cea09938
EMS
820
821reversion.register(Contrat, format='xml')
f80b83c6 822
fb4ded65
OL
823
824class ProxyDossierStatut(Dossier):
825 class Meta:
826 proxy = True
827 verbose_name = "Statut du dossier"
828 verbose_name_plural = "Statut des dossiers"
829
830
831class ProxyPosteStatut(Poste):
832 class Meta:
833 proxy = True
834 verbose_name = "Statut du poste"
835 verbose_name_plural = "Statut des postes"
1d521201
BS
836
837
838class ProxyImplantation(ref.Implantation):
839
840 # Ajout d'un manager pour ref.Implantation
841 dae_manager = ImplantationManager()
842
843 class Meta:
844 proxy = True