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