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