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