Ajouté un manager et modele proxy pour obtenir une liste filtrée des Implantations
[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
bd28238f 479COMPTE_COMPTA_CHOICES = (
494ff2be
NC
480 ('coda', 'CODA'),
481 ('scs', 'SCS'),
482 ('aucun', 'Aucun'),
bd28238f
NC
483)
484
5a1f75cb 485
16b1454e 486class Dossier(DossierWorkflow, rh.Dossier_):
5a1f75cb 487 poste = models.ForeignKey(
4ba84959 488 'Poste', db_column='poste', related_name='dae_dossiers'
5a1f75cb
EMS
489 )
490 employe = models.ForeignKey(
491 'Employe', db_column='employe',
4ba84959 492 related_name='rh_dossiers', verbose_name=u"Employé"
5a1f75cb 493 )
0288adb5 494 organisme_bstg_autre = models.CharField(max_length=255,
c1195471 495 verbose_name=u"Autre organisme",
0288adb5
OL
496 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
497 null=True,
498 blank=True,)
bd28238f 499
8b08fd5a
EMS
500 # Lien avec le dossier
501 dossier_rh = models.ForeignKey(
502 rh.Dossier, related_name='dossiers_dae', null=True, blank=True
503 )
504
139686f2
NC
505 # Données antérieures de l'employé
506 statut_anterieur = models.ForeignKey(
507 rh.Statut, related_name='+', null=True, blank=True,
c1195471 508 verbose_name=u'Statut antérieur')
139686f2
NC
509 classement_anterieur = models.ForeignKey(
510 rh.Classement, related_name='+', null=True, blank=True,
c1195471 511 verbose_name=u'Classement précédent')
139686f2
NC
512 salaire_anterieur = models.DecimalField(
513 max_digits=12, decimal_places=2, null=True, default=None,
481fbd33 514 blank=True, verbose_name=u'Salaire précédent')
5a1f75cb
EMS
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 )
139686f2
NC
522
523 # Données du titulaire précédent
524 employe_anterieur = models.ForeignKey(
525 rh.Employe, related_name='+', null=True, blank=True,
c1195471 526 verbose_name=u'Employé précédent')
139686f2
NC
527 statut_titulaire_anterieur = models.ForeignKey(
528 rh.Statut, related_name='+', null=True, blank=True,
c1195471 529 verbose_name=u'Statut titulaire précédent')
139686f2
NC
530 classement_titulaire_anterieur = models.ForeignKey(
531 rh.Classement, related_name='+', null=True, blank=True,
c1195471 532 verbose_name=u'Classement titulaire précédent')
139686f2
NC
533 salaire_titulaire_anterieur = models.DecimalField(
534 max_digits=12, decimal_places=2, default=None, null=True,
481fbd33 535 blank=True, verbose_name=u'Salaire titulaire précédent')
5a1f75cb
EMS
536 devise_titulaire_anterieur = models.ForeignKey(
537 rh.Devise, related_name='+', null=True, blank=True
538 )
494ff2be 539
bd28238f 540 # Rémunération
16b1454e 541 salaire = models.DecimalField(max_digits=13, decimal_places=2,
c1195471 542 verbose_name=u'Salaire de base',
2d4d2fcf 543 null=True, default=None)
e8e75458 544 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
bd28238f
NC
545
546 # Contrat
5d680e84 547 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
b666864e 548 contrat_date_debut = models.DateField(help_text=HELP_TEXT_DATE)
1f109689 549 contrat_date_fin = models.DateField(null=True, blank=True,
b666864e 550 help_text=HELP_TEXT_DATE)
bd28238f 551
29dffede 552 # Justifications
5a1f75cb
EMS
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 )
a83daab3 570 justif_nouveau_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
5a1f75cb
EMS
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 )
a83daab3 596 justif_rempl_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
5a1f75cb
EMS
597 justif_rempl_commentaire = models.TextField(
598 verbose_name=justif_rempl_commentaire_label, null=True, blank=True
599 )
29dffede 600
bd28238f 601 # Comptes
dfc2c344 602 compte_compta = models.CharField(max_length=10, default='aucun',
603 verbose_name=u'Compte comptabilité',
604 choices=COMPTE_COMPTA_CHOICES)
bd28238f 605 compte_courriel = models.BooleanField()
428e3c0b 606
c3f0b49f 607 # DAE numérisée
5a1f75cb
EMS
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 )
c3f0b49f 612
e4f56614
OL
613 # Managers
614 objects = DossierManager()
428e3c0b 615
16b1454e 616 def __init__(self, *args, **kwargs):
5a1f75cb
EMS
617 # Bouchon pour créer une date fictive necessaire pour valider un
618 # dossier à cause de l'héritage
16b1454e
OL
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
aec2c91e 624 def __unicode__(self):
5a1f75cb
EMS
625 return u'[%s] %s - %s' % (
626 self.poste.implantation, self.poste.nom, self.employe
627 )
bd28238f 628
47b60f16
EMS
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 """
8b08fd5a
EMS
634 if self.dossier_rh:
635 return self.dossier_rh
fb0ed970 636 today = date.today()
47b60f16
EMS
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
fb0ed970 643 try:
47b60f16 644 return rh.Dossier.objects.get(
fb0ed970
EMS
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:
47b60f16
EMS
651 return None
652
653 def importer_dans_rh(self):
8b08fd5a
EMS
654 """
655 Importe les données du dossier DAE dans un dossier RH existant ou
656 nouveau.
657 """
47b60f16
EMS
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)
fb0ed970
EMS
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
9623a926 670 dossier_rh.est_cadre = self.est_cadre
fb0ed970
EMS
671 dossier_rh.regime_travail_nb_heure_semaine = \
672 self.regime_travail_nb_heure_semaine
d4a03f25 673 dossier_rh.date_debut = self.contrat_date_debut
fb0ed970
EMS
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
d4a03f25
EMS
686 for contrat in self.dae_contrats.all():
687 contrat_rh = dossier_rh.rh_contrats.create(
fb0ed970
EMS
688 type_contrat=self.type_contrat,
689 date_debut=self.contrat_date_debut,
690 date_fin=self.contrat_date_fin,
691 )
d4a03f25
EMS
692 contrat_rh.fichier.save(
693 os.path.basename(contrat.fichier.name), contrat.fichier
694 )
fb0ed970
EMS
695
696 for piece in self.dae_dossierpieces.all():
697 piece_rh = dossier_rh.rh_dossierpieces.create(
698 nom=piece.nom
699 )
7ad3b930
OL
700 if not settings.DEBUG:
701 piece_rh.fichier.save(
702 os.path.basename(piece.fichier.name), piece.fichier
703 )
fb0ed970 704
d4a03f25
EMS
705 if self.dae_numerisee:
706 dae_numerisee_rh = dossier_rh.rh_dossierpieces.create(
707 nom=u'DAE numérisée'
708 )
66fefd2f
OL
709 if not settings.DEBUG:
710 dae_numerisee_rh.fichier.save(
711 os.path.basename(self.dae_numerisee.name),
712 self.dae_numerisee
713 )
d4a03f25 714
fb0ed970
EMS
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
8b08fd5a
EMS
737 # Enregistrer le lien avec le dossier RH
738 self.dossier_rh = dossier_rh
739 self.save()
740
fb0ed970
EMS
741 return dossier_rh
742
eed93931 743 def get_salaire_anterieur_euros(self):
88a0e98d
OL
744 if self.devise_anterieur is None:
745 return None
03ff41e3
OL
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
eed93931 754 def get_salaire_titulaire_anterieur_euros(self):
16b1454e
OL
755 if self.devise_titulaire_anterieur is None:
756 return None
03ff41e3 757 try:
88a0e98d 758 taux = self.taux_devise(self.devise_titulaire_anterieur)
03ff41e3
OL
759 except Exception, e:
760 return e
761 if not taux:
16b1454e 762 return None
5a1f75cb
EMS
763 return int(round(
764 float(self.salaire_titulaire_anterieur) * float(taux), 2
765 ))
eed93931 766
c511cd1f
EMS
767 def valide(self):
768 return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
769 DOSSIER_ETAT_DRH_FINALISATION,
770 DOSSIER_ETAT_FINALISE)
771
cea09938
EMS
772reversion.register(Dossier, format='xml', follow=[
773 'dae_dossierpieces', 'dae_comparaisons', 'dae_remunerations',
774 'dae_contrats'
775])
bd28238f 776
5a1f75cb 777
a4125771 778class DossierPiece(rh.DossierPiece_):
4ba84959
EMS
779 """
780 Documents relatifs au Dossier (à l'occupation de ce poste par employé).
2d4d2fcf 781 Ex.: Lettre de motivation.
782 """
4ba84959
EMS
783 dossier = models.ForeignKey(
784 Dossier, db_column='dossier', related_name='dae_dossierpieces'
785 )
2d4d2fcf 786
cea09938
EMS
787reversion.register(DossierPiece, format='xml')
788
5a1f75cb 789
a4125771 790class DossierComparaison(rh.DossierComparaison_):
03b395db
OL
791 """
792 Photo d'une comparaison salariale au moment de l'embauche.
793 """
4ba84959
EMS
794 dossier = models.ForeignKey(
795 Dossier, related_name='dae_comparaisons'
796 )
5a1f75cb
EMS
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 )
03b395db 805
cea09938
EMS
806reversion.register(DossierComparaison, format='xml')
807
b9098c33
BS
808class DossierComparaisonRemuneration(rh.Remuneration_):
809 dossier_comparaison = models.ForeignKey(
810 DossierComparaison, related_name='dossier_comparaison_remunerations'
811 )
812
813reversion.register(DossierComparaisonRemuneration, format='xml')
c589d980 814
2d4d2fcf 815### RÉMUNÉRATION
816
a4125771 817class Remuneration(rh.Remuneration_):
4ba84959
EMS
818 dossier = models.ForeignKey(
819 Dossier, db_column='dossier', related_name='dae_remunerations'
820 )
c1834169 821
cea09938
EMS
822reversion.register(Remuneration, format='xml')
823
5a1f75cb 824
c1834169
EMS
825### CONTRATS
826
a4125771 827class Contrat(rh.Contrat_):
4ba84959
EMS
828 dossier = models.ForeignKey(
829 Dossier, db_column='dossier', related_name='dae_contrats'
830 )
cea09938
EMS
831
832reversion.register(Contrat, format='xml')
f80b83c6 833
fb4ded65
OL
834
835class ProxyDossierStatut(Dossier):
836 class Meta:
837 proxy = True
838 verbose_name = "Statut du dossier"
839 verbose_name_plural = "Statut des dossiers"
840
841
842class ProxyPosteStatut(Poste):
843 class Meta:
844 proxy = True
845 verbose_name = "Statut du poste"
846 verbose_name_plural = "Statut des postes"
1d521201
BS
847
848
849class ProxyImplantation(ref.Implantation):
850
851 # Ajout d'un manager pour ref.Implantation
852 dae_manager = ImplantationManager()
853
854 class Meta:
855 proxy = True