[#2658] Retrait du hack app_label() dans rh.models
[auf_rh_dae.git] / project / dae / models.py
CommitLineData
bd28238f 1# -=- encoding: utf-8 -=-
3f3cf5f3 2
fb0ed970
EMS
3import os
4from datetime import date, timedelta
5
5a1f75cb
EMS
6import reversion
7from auf.django.metadata.models import AUFMetadata
5633fa41 8from django.conf import settings
36341125 9from django.core.files.storage import FileSystemStorage
a9c281dd 10from django.db import models
fb0ed970 11from django.db.models import Q
5a1f75cb 12
de151a1e 13from project.dae.managers import PosteManager, DossierManager
5a1f75cb
EMS
14from project.dae.workflow import PosteWorkflow, DossierWorkflow
15from project.dae.workflow import \
16 DOSSIER_ETAT_DRH_FINALISATION, DOSSIER_ETAT_REGION_FINALISATION, \
17 DOSSIER_ETAT_FINALISE
18
19# XXX: Saloperie, il faut importer rh.models à partir d'un autre namespace
20# à cause du hack app_context() dans project.rh.models. Très fragile. Il
21# faut régler ça aussi vite que possible.
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 )
167 piece_rh.fichier.save(
168 os.path.basename(piece.fichier.name), piece.fichier
169 )
170
171 rh.PosteComparaison.objects.filter(poste=poste_rh).delete()
172 for comp in self.dae_comparaisons_internes.all():
173 poste_rh.rh_comparaisons_internes.create(
174 implantation=comp.implantation,
175 nom=comp.nom,
176 montant=comp.montant,
177 devise=comp.devise
178 )
179
180 rh.PosteFinancement.objects.filter(poste=poste_rh).delete()
181 for financement in self.dae_financements.all():
182 poste_rh.rh_financements.create(
183 type=financement.type,
184 pourcentage=financement.pourcentage,
185 commentaire=financement.commentaire
186 )
187
8b08fd5a
EMS
188 self.id_rh = poste_rh
189 self.save()
fb0ed970
EMS
190 return poste_rh
191
f3333b0e
OL
192 def get_employe(self):
193 """
194 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
195 """
196 dossiers = self.get_dossiers()
197 if len(dossiers) > 0:
198 return dossiers[0].employe
199 else:
200 return None
201
179f6b49 202 def get_default_devise(self):
1bc84af4 203 """Récupère la devise par défaut en fonction de l'implantation
2d4d2fcf 204 (EUR autrement)
205 """
179f6b49 206 try:
6e4600ef 207 implantation_devise = rh.TauxChange.objects \
208 .filter(implantation=self.implantation)[0].devise
179f6b49 209 except:
5a1f75cb 210 implantation_devise = 5 # EUR
179f6b49
OL
211 return implantation_devise
212
c0413a6f
OL
213 #####################
214 # Classement de poste
215 #####################
216
217 def get_couts_minimum(self):
5a1f75cb
EMS
218 return self.salaire_min + self.indemn_expat_min + \
219 self.indemn_fct_min + self.charges_patronales_min + \
220 self.autre_min
83b94a87
EMS
221
222 def get_salaire_minimum(self):
223 return self.get_couts_minimum() - self.charges_patronales_min
c0413a6f
OL
224
225 def get_taux_minimum(self):
4e439a89 226 if self.devise_min.code == 'EUR':
5a1f75cb 227 return 1
2455f48d 228 liste_taux = self.devise_min.tauxchange_set.order_by('-annee')
4e439a89 229 if len(liste_taux) == 0:
5a1f75cb
EMS
230 raise DeviseException(
231 u"La devise %s n'a pas de taux pour l'implantation %s" %
232 (self.devise_min, self.implantation))
b6825282 233 else:
4e439a89 234 return liste_taux[0].taux
c0413a6f
OL
235
236 def get_couts_minimum_euros(self):
fa5b95ed 237 return float(self.get_couts_minimum()) * self.get_taux_minimum()
c0413a6f 238
83b94a87 239 def get_salaire_minimum_euros(self):
fa5b95ed 240 return float(self.get_salaire_minimum()) * self.get_taux_minimum()
83b94a87 241
c0413a6f 242 def get_couts_maximum(self):
5a1f75cb
EMS
243 return self.salaire_max + self.indemn_expat_max + \
244 self.indemn_fct_max + self.charges_patronales_max + \
245 self.autre_max
83b94a87
EMS
246
247 def get_salaire_maximum(self):
248 return self.get_couts_maximum() - self.charges_patronales_max
c0413a6f
OL
249
250 def get_taux_maximum(self):
4e439a89 251 if self.devise_max.code == 'EUR':
5a1f75cb 252 return 1
2455f48d 253 liste_taux = self.devise_max.tauxchange_set.order_by('-annee')
4e439a89 254 if len(liste_taux) == 0:
5a1f75cb
EMS
255 raise DeviseException(
256 u"La devise %s n'a pas de taux pour l'implantation %s" %
257 (self.devise_max, self.implantation)
258 )
b6825282 259 else:
4e439a89 260 return liste_taux[0].taux
c0413a6f
OL
261
262 def get_couts_maximum_euros(self):
fa5b95ed 263 return float(self.get_couts_maximum()) * self.get_taux_maximum()
c0413a6f 264
83b94a87 265 def get_salaire_maximum_euros(self):
fa5b95ed 266 return float(self.get_salaire_maximum()) * self.get_taux_maximum()
12c7f8a7
OL
267
268 def show_taux_minimum(self):
269 try:
270 return self.get_taux_minimum()
83b94a87 271 except DeviseException, e:
12c7f8a7
OL
272 return e
273
274 def show_couts_minimum_euros(self):
275 try:
276 return self.get_couts_minimum_euros()
83b94a87
EMS
277 except DeviseException, e:
278 return e
279
280 def show_salaire_minimum_euros(self):
281 try:
282 return self.get_salaire_minimum_euros()
283 except DeviseException, e:
12c7f8a7
OL
284 return e
285
286 def show_taux_maximum(self):
287 try:
288 return self.get_taux_maximum()
83b94a87 289 except DeviseException, e:
12c7f8a7
OL
290 return e
291
292 def show_couts_maximum_euros(self):
293 try:
294 return self.get_couts_maximum_euros()
83b94a87
EMS
295 except DeviseException, e:
296 return e
297
298 def show_salaire_maximum_euros(self):
299 try:
300 return self.get_salaire_maximum_euros()
301 except DeviseException, e:
12c7f8a7
OL
302 return e
303
c0413a6f 304 # Comparaison de poste
a3fee9c5
OL
305 def est_comparable(self):
306 """
5a1f75cb
EMS
307 Si on a au moins une valeur de saisie dans les comparaisons, alors
308 le poste est comparable.
a3fee9c5
OL
309 """
310 if self.comp_universite_min is None and \
311 self.comp_fonctionpub_min is None and \
312 self.comp_locale_min is None and \
313 self.comp_ong_min is None and \
314 self.comp_autre_min is None and \
315 self.comp_universite_max is None and \
316 self.comp_fonctionpub_max is None and \
317 self.comp_locale_max is None and \
318 self.comp_ong_max is None and \
319 self.comp_autre_max is None:
320 return False
321 else:
322 return True
1bc84af4 323
c0413a6f
OL
324 def get_taux_comparaison(self):
325 try:
5a1f75cb
EMS
326 return rh.TauxChange.objects \
327 .filter(devise=self.devise_comparaison)[0].taux
c0413a6f
OL
328 except:
329 return 1
330
331 def get_comp_universite_min_euros(self):
332 return (float)(self.comp_universite_min) * self.get_taux_comparaison()
333
334 def get_comp_fonctionpub_min_euros(self):
335 return (float)(self.comp_fonctionpub_min) * self.get_taux_comparaison()
336
337 def get_comp_locale_min_euros(self):
338 return (float)(self.comp_locale_min) * self.get_taux_comparaison()
339
340 def get_comp_ong_min_euros(self):
341 return (float)(self.comp_ong_min) * self.get_taux_comparaison()
342
343 def get_comp_autre_min_euros(self):
344 return (float)(self.comp_autre_min) * self.get_taux_comparaison()
345
346 def get_comp_universite_max_euros(self):
347 return (float)(self.comp_universite_max) * self.get_taux_comparaison()
348
349 def get_comp_fonctionpub_max_euros(self):
350 return (float)(self.comp_fonctionpub_max) * self.get_taux_comparaison()
351
352 def get_comp_locale_max_euros(self):
353 return (float)(self.comp_locale_max) * self.get_taux_comparaison()
354
355 def get_comp_ong_max_euros(self):
356 return (float)(self.comp_ong_max) * self.get_taux_comparaison()
357
358 def get_comp_autre_max_euros(self):
359 return (float)(self.comp_autre_max) * self.get_taux_comparaison()
360
5d680e84 361 def __unicode__(self):
f3333b0e 362 """
1bc84af4 363 Cette fonction est consommatrice SQL car elle cherche les dossiers
2d4d2fcf 364 qui ont été liés à celui-ci.
f3333b0e 365 """
f3333b0e
OL
366 data = (
367 self.implantation,
368 self.type_poste.nom,
369 self.nom,
f3333b0e 370 )
a7c68130 371 return u'%s - %s (%s)' % data
5d680e84 372
a9c281dd
OL
373# Tester l'enregistrement car les models.py sont importés au complet
374if not reversion.is_registered(Poste):
375 reversion.register(Poste)
376
5d680e84
NC
377POSTE_FINANCEMENT_CHOICES = (
378 ('A', 'A - Frais de personnel'),
379 ('B', 'B - Projet(s)-Titre(s)'),
380 ('C', 'C - Autre')
381)
bd28238f 382
5a1f75cb 383
317ce433 384class PosteFinancement(rh.PosteFinancement_):
4ba84959
EMS
385 poste = models.ForeignKey(
386 Poste, db_column='poste', related_name='dae_financements'
387 )
1d0f4eef 388
5a1f75cb 389
317ce433 390class PostePiece(rh.PostePiece_):
4ba84959
EMS
391 poste = models.ForeignKey(
392 Poste, db_column='poste', related_name='dae_pieces'
393 )
068d1462 394
5a1f75cb 395
317ce433 396class PosteComparaison(rh.PosteComparaison_):
4ba84959
EMS
397 poste = models.ForeignKey(
398 Poste, related_name='dae_comparaisons_internes'
399 )
5a1f75cb
EMS
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 )
068d1462 408
2d4d2fcf 409### EMPLOYÉ/PERSONNE
410
411# TODO : migration pour m -> M, f -> F
c589d980 412
bd28238f 413GENRE_CHOICES = (
139686f2
NC
414 ('m', 'Homme'),
415 ('f', 'Femme'),
bd28238f
NC
416)
417
5a1f75cb 418
a2c3ad52 419class Employe(AUFMetadata):
bd28238f
NC
420
421 # Modèle existant
428e3c0b 422 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
c1195471 423 verbose_name=u'Employé')
bd28238f 424 nom = models.CharField(max_length=255)
c1195471 425 prenom = models.CharField(max_length=255, verbose_name=u'Prénom')
07b40eda 426 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
bd28238f 427
139686f2 428 def __unicode__(self):
2d4d2fcf 429 return u'%s %s' % (self.prenom, self.nom.upper())
139686f2 430
47b60f16 431 def dans_rh(self):
8b08fd5a
EMS
432 """
433 Retourne l'employé RH associé à cet employé DAE.
434 """
435 return self.id_rh
47b60f16 436
fb0ed970 437 def importer_dans_rh(self):
8b08fd5a
EMS
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(
fb0ed970
EMS
442 nom=self.nom,
443 prenom=self.prenom,
444 genre=self.genre
445 )
8b08fd5a
EMS
446 self.id_rh = employe_rh
447 self.save()
448 return employe_rh
fb0ed970 449
bd28238f 450
2d4d2fcf 451### DOSSIER
452
453STATUT_RESIDENCE_CHOICES = (
454 ('local', 'Local'),
455 ('expat', 'Expatrié'),
456)
457
bd28238f 458COMPTE_COMPTA_CHOICES = (
494ff2be
NC
459 ('coda', 'CODA'),
460 ('scs', 'SCS'),
461 ('aucun', 'Aucun'),
bd28238f
NC
462)
463
5a1f75cb 464
16b1454e 465class Dossier(DossierWorkflow, rh.Dossier_):
5a1f75cb 466 poste = models.ForeignKey(
4ba84959 467 'Poste', db_column='poste', related_name='dae_dossiers'
5a1f75cb
EMS
468 )
469 employe = models.ForeignKey(
470 'Employe', db_column='employe',
4ba84959 471 related_name='rh_dossiers', verbose_name=u"Employé"
5a1f75cb 472 )
0288adb5 473 organisme_bstg_autre = models.CharField(max_length=255,
c1195471 474 verbose_name=u"Autre organisme",
0288adb5
OL
475 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
476 null=True,
477 blank=True,)
bd28238f 478
8b08fd5a
EMS
479 # Lien avec le dossier
480 dossier_rh = models.ForeignKey(
481 rh.Dossier, related_name='dossiers_dae', null=True, blank=True
482 )
483
139686f2
NC
484 # Données antérieures de l'employé
485 statut_anterieur = models.ForeignKey(
486 rh.Statut, related_name='+', null=True, blank=True,
c1195471 487 verbose_name=u'Statut antérieur')
139686f2
NC
488 classement_anterieur = models.ForeignKey(
489 rh.Classement, related_name='+', null=True, blank=True,
c1195471 490 verbose_name=u'Classement précédent')
139686f2
NC
491 salaire_anterieur = models.DecimalField(
492 max_digits=12, decimal_places=2, null=True, default=None,
481fbd33 493 blank=True, verbose_name=u'Salaire précédent')
5a1f75cb
EMS
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 )
139686f2
NC
501
502 # Données du titulaire précédent
503 employe_anterieur = models.ForeignKey(
504 rh.Employe, related_name='+', null=True, blank=True,
c1195471 505 verbose_name=u'Employé précédent')
139686f2
NC
506 statut_titulaire_anterieur = models.ForeignKey(
507 rh.Statut, related_name='+', null=True, blank=True,
c1195471 508 verbose_name=u'Statut titulaire précédent')
139686f2
NC
509 classement_titulaire_anterieur = models.ForeignKey(
510 rh.Classement, related_name='+', null=True, blank=True,
c1195471 511 verbose_name=u'Classement titulaire précédent')
139686f2
NC
512 salaire_titulaire_anterieur = models.DecimalField(
513 max_digits=12, decimal_places=2, default=None, null=True,
481fbd33 514 blank=True, verbose_name=u'Salaire titulaire précédent')
5a1f75cb
EMS
515 devise_titulaire_anterieur = models.ForeignKey(
516 rh.Devise, related_name='+', null=True, blank=True
517 )
494ff2be 518
bd28238f 519 # Rémunération
16b1454e 520 salaire = models.DecimalField(max_digits=13, decimal_places=2,
c1195471 521 verbose_name=u'Salaire de base',
2d4d2fcf 522 null=True, default=None)
e8e75458 523 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
bd28238f
NC
524
525 # Contrat
5d680e84 526 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
b666864e 527 contrat_date_debut = models.DateField(help_text=HELP_TEXT_DATE)
1f109689 528 contrat_date_fin = models.DateField(null=True, blank=True,
b666864e 529 help_text=HELP_TEXT_DATE)
bd28238f 530
29dffede 531 # Justifications
5a1f75cb
EMS
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 )
a83daab3 549 justif_nouveau_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
5a1f75cb
EMS
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 )
a83daab3 575 justif_rempl_commentaire_label = u"COMMENTAIRES ADDITIONNELS"
5a1f75cb
EMS
576 justif_rempl_commentaire = models.TextField(
577 verbose_name=justif_rempl_commentaire_label, null=True, blank=True
578 )
29dffede 579
bd28238f 580 # Comptes
dfc2c344 581 compte_compta = models.CharField(max_length=10, default='aucun',
582 verbose_name=u'Compte comptabilité',
583 choices=COMPTE_COMPTA_CHOICES)
bd28238f 584 compte_courriel = models.BooleanField()
428e3c0b 585
c3f0b49f 586 # DAE numérisée
5a1f75cb
EMS
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 )
c3f0b49f 591
e4f56614
OL
592 # Managers
593 objects = DossierManager()
428e3c0b 594
16b1454e 595 def __init__(self, *args, **kwargs):
5a1f75cb
EMS
596 # Bouchon pour créer une date fictive necessaire pour valider un
597 # dossier à cause de l'héritage
16b1454e
OL
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
aec2c91e 603 def __unicode__(self):
5a1f75cb
EMS
604 return u'[%s] %s - %s' % (
605 self.poste.implantation, self.poste.nom, self.employe
606 )
bd28238f 607
47b60f16
EMS
608 def dans_rh(self):
609 """
610 Retourne le dossier associé dans le système RH ou ``None`` s'il n'y
611 en a pas.
612 """
8b08fd5a
EMS
613 if self.dossier_rh:
614 return self.dossier_rh
fb0ed970 615 today = date.today()
47b60f16
EMS
616 poste_rh = self.poste.dans_rh()
617 if poste_rh is None:
618 return None
619 employe_rh = self.employe.dans_rh()
620 if employe_rh is None:
621 return None
fb0ed970 622 try:
47b60f16 623 return rh.Dossier.objects.get(
fb0ed970
EMS
624 Q(date_debut=None) | Q(date_debut__lte=today),
625 Q(date_fin=None) | Q(date_fin__gte=today),
626 poste=poste_rh,
627 employe=employe_rh
628 )
629 except rh.Dossier.DoesNotExist:
47b60f16
EMS
630 return None
631
632 def importer_dans_rh(self):
8b08fd5a
EMS
633 """
634 Importe les données du dossier DAE dans un dossier RH existant ou
635 nouveau.
636 """
47b60f16
EMS
637 poste_rh = self.poste.importer_dans_rh()
638 employe_rh = self.employe.importer_dans_rh()
639 dossier_rh = self.dans_rh() or \
640 rh.Dossier(poste=poste_rh, employe=employe_rh)
fb0ed970
EMS
641
642 dossier_rh.statut = self.statut
643 dossier_rh.organisme_bstg = self.organisme_bstg
644 dossier_rh.remplacement = self.remplacement
645 dossier_rh.remplacement_de = self.remplacement_de
646 dossier_rh.statut_residence = self.statut_residence
647 dossier_rh.classement = self.classement
648 dossier_rh.regime_travail = self.regime_travail
649 dossier_rh.regime_travail_nb_heure_semaine = \
650 self.regime_travail_nb_heure_semaine
d4a03f25
EMS
651 dossier_rh.date_debut = self.contrat_date_debut
652 dossier_rh.date_fin = self.contrat_date_fin
fb0ed970
EMS
653 dossier_rh.save()
654
655 rh.DossierComparaison.objects.filter(dossier=dossier_rh).delete()
656 for comp in self.dae_comparaisons.all():
657 dossier_rh.rh_comparaisons.create(
658 implantation=comp.implantation,
659 poste=comp.poste,
660 personne=comp.personne,
661 montant=comp.montant,
662 devise=comp.devise
663 )
664
d4a03f25
EMS
665 for contrat in self.dae_contrats.all():
666 contrat_rh = dossier_rh.rh_contrats.create(
fb0ed970
EMS
667 type_contrat=self.type_contrat,
668 date_debut=self.contrat_date_debut,
669 date_fin=self.contrat_date_fin,
670 )
d4a03f25
EMS
671 contrat_rh.fichier.save(
672 os.path.basename(contrat.fichier.name), contrat.fichier
673 )
fb0ed970
EMS
674
675 for piece in self.dae_dossierpieces.all():
676 piece_rh = dossier_rh.rh_dossierpieces.create(
677 nom=piece.nom
678 )
679 piece_rh.fichier.save(
680 os.path.basename(piece.fichier.name), piece.fichier
681 )
682
d4a03f25
EMS
683 if self.dae_numerisee:
684 dae_numerisee_rh = dossier_rh.rh_dossierpieces.create(
685 nom=u'DAE numérisée'
686 )
687 dae_numerisee_rh.fichier.save(
688 os.path.basename(self.dae_numerisee.name),
689 self.dae_numerisee
690 )
691
fb0ed970
EMS
692 # Fermer les rémunérations qui commencent avant le début du contrat
693 dossier_rh.rh_remunerations.filter(
694 Q(date_debut=None) | Q(date_debut__lt=self.contrat_date_debut),
695 Q(date_fin=None) | Q(date_fin__gte=self.contrat_date_debut)
696 ).update(date_fin=self.contrat_date_debut - timedelta(1))
697
698 # Effacer les rémunérations qui commencent à la date du contrat
699 dossier_rh.rh_remunerations \
700 .filter(date_debut=self.contrat_date_debut) \
701 .delete()
702
703 for remun in self.dae_remunerations.all():
704 dossier_rh.rh_remunerations.get_or_create(
705 type=remun.type,
706 type_revalorisation=remun.type_revalorisation,
707 montant=remun.montant,
708 devise=remun.devise,
709 commentaire=remun.commentaire,
710 date_debut=self.contrat_date_debut,
711 date_fin=self.contrat_date_fin
712 )
713
8b08fd5a
EMS
714 # Enregistrer le lien avec le dossier RH
715 self.dossier_rh = dossier_rh
716 self.save()
717
fb0ed970
EMS
718 return dossier_rh
719
eed93931 720 def get_salaire_anterieur_euros(self):
88a0e98d
OL
721 if self.devise_anterieur is None:
722 return None
03ff41e3
OL
723 try:
724 taux = self.taux_devise(self.devise_anterieur)
725 except Exception, e:
726 return e
727 if not taux:
728 return None
729 return int(round(float(self.salaire_anterieur) * float(taux), 2))
730
eed93931 731 def get_salaire_titulaire_anterieur_euros(self):
16b1454e
OL
732 if self.devise_titulaire_anterieur is None:
733 return None
03ff41e3 734 try:
88a0e98d 735 taux = self.taux_devise(self.devise_titulaire_anterieur)
03ff41e3
OL
736 except Exception, e:
737 return e
738 if not taux:
16b1454e 739 return None
5a1f75cb
EMS
740 return int(round(
741 float(self.salaire_titulaire_anterieur) * float(taux), 2
742 ))
eed93931 743
c511cd1f
EMS
744 def valide(self):
745 return self.etat in (DOSSIER_ETAT_REGION_FINALISATION,
746 DOSSIER_ETAT_DRH_FINALISATION,
747 DOSSIER_ETAT_FINALISE)
748
bd28238f 749
0140cbd2 750# Tester l'enregistrement car les models.py sont importés au complet
751if not reversion.is_registered(Dossier):
752 reversion.register(Dossier)
bd28238f 753
5a1f75cb 754
a4125771 755class DossierPiece(rh.DossierPiece_):
4ba84959
EMS
756 """
757 Documents relatifs au Dossier (à l'occupation de ce poste par employé).
2d4d2fcf 758 Ex.: Lettre de motivation.
759 """
4ba84959
EMS
760 dossier = models.ForeignKey(
761 Dossier, db_column='dossier', related_name='dae_dossierpieces'
762 )
2d4d2fcf 763
5a1f75cb 764
a4125771 765class DossierComparaison(rh.DossierComparaison_):
03b395db
OL
766 """
767 Photo d'une comparaison salariale au moment de l'embauche.
768 """
4ba84959
EMS
769 dossier = models.ForeignKey(
770 Dossier, related_name='dae_comparaisons'
771 )
5a1f75cb
EMS
772 statut = models.ForeignKey(
773 rh.Statut, related_name='+', verbose_name='Statut', null=True,
774 blank=True
775 )
776 classement = models.ForeignKey(
777 rh.Classement, related_name='+', verbose_name='Classement',
778 null=True, blank=True
779 )
03b395db 780
c589d980 781
2d4d2fcf 782### RÉMUNÉRATION
783
a4125771 784class Remuneration(rh.Remuneration_):
4ba84959
EMS
785 dossier = models.ForeignKey(
786 Dossier, db_column='dossier', related_name='dae_remunerations'
787 )
c1834169 788
5a1f75cb 789
c1834169
EMS
790### CONTRATS
791
a4125771 792class Contrat(rh.Contrat_):
4ba84959
EMS
793 dossier = models.ForeignKey(
794 Dossier, db_column='dossier', related_name='dae_contrats'
795 )