retrait des coeff
[auf_rh_dae.git] / project / dae / models.py
CommitLineData
bd28238f 1# -=- encoding: utf-8 -=-
3f3cf5f3 2
36341125
OL
3import os
4from django.core.files.storage import FileSystemStorage
a9c281dd
OL
5from django.db import models
6import reversion
8fa94e8b 7from workflow import PosteWorkflow
bd28238f 8import datamaster_modeles.models as ref
a9c281dd 9from rh_v1 import models as rh
36341125 10import settings
bd28238f
NC
11
12STATUT_RESIDENCE_CHOICES = (
5d680e84
NC
13 ('local', 'Local'),
14 ('expat', 'Expatrié'),
bd28238f
NC
15)
16
17POSTE_APPEL_CHOICES = (
5d680e84
NC
18 ('interne', 'Interne'),
19 ('externe', 'Externe'),
bd28238f
NC
20)
21
22POSTE_STATUT_CHOICES = (
23 ('MAD', 'Mise à disposition'),
24 ('DET', 'Détachement'),
25)
26
d766bf2c 27# Upload de fichiers
36341125
OL
28storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT, base_url=settings.PRIVE_MEDIA_URL)
29
30def poste_piece_dispatch(instance, filename):
fe13cd48 31 path = "poste/%s/%s" % (instance.poste_id, filename)
36341125
OL
32 return path
33
d766bf2c 34def dossier_piece_dispatch(instance, filename):
fe13cd48 35 path = "dossier/%s/%s" % (instance.dossier_id, filename)
d766bf2c
OL
36 return path
37
36341125
OL
38
39class PostePiece(models.Model):
40 poste = models.ForeignKey("Poste")
41 nom = models.CharField(verbose_name="Nom", max_length=255)
42 fichier = models.FileField(verbose_name="Fichier", upload_to=poste_piece_dispatch, storage=storage_prive)
43
1c7d67ce
OL
44class PosteManager(models.Manager):
45 """
46 Chargement de tous les objets FK existants sur chaque QuerySet.
47 """
48 def get_query_set(self):
49 fkeys = (
50 'id_rh',
51 'responsable',
52 'implantation',
53 'type_poste',
54 'service',
55 'classement_min',
56 'classement_max',
57 'valeur_point_min',
58 'valeur_point_max',
59 )
98d51b59
NC
60 return super(PosteManager, self).get_query_set() \
61 .select_related(*fkeys).all()
1c7d67ce
OL
62
63
8fa94e8b 64class Poste(PosteWorkflow, models.Model):
bd28238f 65 # Modèle existant
5d680e84 66 id_rh = models.ForeignKey(rh.Poste, null=True, related_name='+',
98d51b59
NC
67 editable=False,
68 verbose_name="Mise à jour du poste")
ce110fb9 69 nom = models.CharField(verbose_name="Titre du poste", max_length=255)
5d680e84
NC
70 implantation = models.ForeignKey(ref.Implantation)
71 type_poste = models.ForeignKey(rh.TypePoste, null=True, related_name='+')
98d51b59
NC
72 service = models.ForeignKey(rh.Service, related_name='+',
73 verbose_name=u"Direction/Service/Pôle support")
74 responsable = models.ForeignKey(rh.Poste, related_name='+',
5efcd48e 75 verbose_name="Poste du responsable")
9a85768a 76
5d680e84 77 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
5efcd48e 78 default=100,
79 verbose_name="Temps de travail",
80 help_text="% du temps complet")
5d680e84 81 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
5efcd48e 82 decimal_places=2,
5fe0ced5 83 default=35,
5efcd48e 84 verbose_name="Nb. heures par semaine")
bd28238f
NC
85
86 # Recrutement
154677c3
OL
87 local = models.BooleanField(verbose_name="Local", default=True, blank=True)
88 expatrie = models.BooleanField(verbose_name="Expatrié", default=False, blank=True)
89
5d680e84 90 # TODO null?
a3508c67 91 mise_a_disposition = models.BooleanField(verbose_name="Mise à disposition")
98d51b59
NC
92 appel = models.CharField(max_length=10, default='interne',
93 verbose_name="Appel à candidature",
5d680e84 94 choices=POSTE_APPEL_CHOICES)
bd28238f
NC
95
96 # Rémunération
5d680e84
NC
97 classement_min = models.ForeignKey(rh.Classement, related_name='+')
98 classement_max = models.ForeignKey(rh.Classement, related_name='+')
7dcb8d40
OL
99
100 # En fait, les coefficient n'ont pas de valeur dans ces cas, les couts sont calculés
101 # et mis dans les coûts globals
102 #coefficient_min = models.FloatField(null=True) # pour classement "hors grille"
103 #coefficient_max = models.FloatField(null=True) # pour classement "hors grille"
104
4dd75e7b
OL
105 valeur_point_min = models.ForeignKey(rh.ValeurPoint, related_name='+', blank=True, null=True)
106 valeur_point_max = models.ForeignKey(rh.ValeurPoint, related_name='+', blank=True, null=True)
3d627bfd 107 devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
108 devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
5d680e84
NC
109 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
110 default=0)
111 salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
112 default=0)
113 indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
114 default=0)
115 indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
116 default=0)
117 autre_min = models.DecimalField(max_digits=12, decimal_places=2,
118 default=0)
119 autre_max = models.DecimalField(max_digits=12, decimal_places=2,
120 default=0)
121
122 # Comparatifs de rémunération
123 devise_comparaison = models.ForeignKey(rh.Devise, related_name='+',
a3508c67 124 default=5)
5d680e84
NC
125 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
126 null=True, blank=True)
127 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
128 null=True, blank=True)
129 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
130 null=True, blank=True)
131 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
132 null=True, blank=True)
133 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
134 null=True, blank=True)
135 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
136 null=True, blank=True)
137 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
138 null=True, blank=True)
139 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
140 null=True, blank=True)
141 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
142 null=True, blank=True)
143 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
144 null=True, blank=True)
bd28238f 145
2e092e0c
OL
146 # Justification
147 justification = models.TextField()
148
a05cc82d
OL
149 # Validations
150 validation_bureau_regional = models.BooleanField(verbose_name="Validation bureau régional")
151 validation_bureau_regional_date = models.DateField(blank=True, null=True)
152 validation_drh = models.BooleanField(verbose_name="Validation DRH")
153 validation_drh_date = models.DateField(blank=True, null=True)
154 validation_secretaire_general = models.BooleanField(verbose_name="Validation secrétaire général")
155 validation_secretaire_general_date = models.DateField(blank=True, null=True)
156 validation_recteur = models.BooleanField(verbose_name="Validation recteur")
157 validation_recteur_date = models.DateField(blank=True, null=True)
158
bd28238f 159 # Méta
5d680e84
NC
160 date_creation = models.DateTimeField(auto_now_add=True)
161 date_modification = models.DateTimeField(auto_now=True)
98d51b59 162 date_debut = models.DateField(verbose_name="Date de début",
9fb2ccd9 163 help_text="format: aaaa-mm-jj")
164 date_fin = models.DateField(null=True, blank=True,
165 verbose_name="Date de fin",
166 help_text="format: aaaa-mm-jj")
bd28238f
NC
167 actif = models.BooleanField(default=True)
168
1c7d67ce
OL
169 # Managers
170 objects = PosteManager()
171
03858ba5
OL
172 def _get_key(self):
173 """
174 Les vues sont montées selon une clef spéciale pour identifier la provenance du poste.
175 Cette méthode fournit un moyen de reconstruire cette clef afin de générer les URLs.
176 """
177 return "dae-%s" % self.id
178 key = property(_get_key)
179
f3333b0e
OL
180 def get_dossiers(self):
181 """
182 Liste tous les anciens dossiers liés à ce poste.
183 (Le nom de la relation sur le rh.Poste est mal choisi poste1 au lieu de dossier1)
184 Note1 : seulement le dosssier principal fait l'objet de la recherche.
185 Note2 : les dossiers sont retournés du plus récent au plus vieux. (Ce test est fait
186 en fonction du id, car les dates de création sont absentes de rh v1).
187 """
188 if self.id_rh is None:
189 return []
190 postes = [p for p in self.id_rh.poste1.all()]
191 return sorted(postes, key=lambda poste: poste.id, reverse=True)
192
193 def get_complement_nom(self):
194 """
195 Inspecte les modèles rh v1 pour trouver dans le dernier dossier un complément de titre de poste.
196 """
197 dossiers = self.get_dossiers()
198 if len(dossiers) > 0:
199 nom = dossiers[0].complement1
200 else:
201 nom = ""
a9c281dd 202 return nom
f3333b0e
OL
203
204 def get_employe(self):
205 """
206 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
207 """
208 dossiers = self.get_dossiers()
209 if len(dossiers) > 0:
210 return dossiers[0].employe
211 else:
212 return None
213
179f6b49
OL
214 def get_default_devise(self):
215 """Récupère la devise par défaut en fonction de l'implantation (EUR autrement)"""
216 try:
217 implantation_devise = rh.TauxChange.objects.filter(implantation=self.implantation)[0].devise
218 except:
219 implantation_devise = 5 # EUR
220 return implantation_devise
221
5d680e84 222 def __unicode__(self):
f3333b0e
OL
223 """
224 Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
225 """
226 complement_nom_poste = self.get_complement_nom()
c7a4aa2b
OL
227 if complement_nom_poste is None:
228 complement_nom_poste = ""
f3333b0e 229 employe = self.get_employe()
c7a4aa2b
OL
230 if employe is None:
231 employe = "VACANT"
f3333b0e
OL
232 data = (
233 self.implantation,
234 self.type_poste.nom,
235 self.nom,
236 self.id,
237 complement_nom_poste,
238 employe,
239 )
240 return u'%s - %s (%s) [dae-%s %s %s]' % data
5d680e84 241
bd28238f 242
a9c281dd
OL
243# Tester l'enregistrement car les models.py sont importés au complet
244if not reversion.is_registered(Poste):
245 reversion.register(Poste)
246
bd28238f 247
5d680e84
NC
248POSTE_FINANCEMENT_CHOICES = (
249 ('A', 'A - Frais de personnel'),
250 ('B', 'B - Projet(s)-Titre(s)'),
251 ('C', 'C - Autre')
252)
bd28238f
NC
253
254
5d680e84 255class PosteFinancement(models.Model):
5d680e84
NC
256 poste = models.ForeignKey('Poste', related_name='financements')
257 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
43d04712 258 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
259 help_text="ex.: 33.33 % (décimale avec point)")
260 commentaire = models.TextField(
261 help_text="Spécifiez la source de financement.")
bd28238f 262
43d04712 263 class Meta:
264 ordering = ['type']
bd28238f
NC
265
266GENRE_CHOICES = (
139686f2
NC
267 ('m', 'Homme'),
268 ('f', 'Femme'),
bd28238f
NC
269)
270
271
272class Employe(models.Model):
bd28238f
NC
273
274 # Modèle existant
da3ca955 275 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
276 verbose_name='Employé')
bd28238f 277 nom = models.CharField(max_length=255)
da3ca955 278 prenom = models.CharField(max_length=255, verbose_name='Prénom')
494ff2be
NC
279 genre = models.CharField(max_length=1, choices=GENRE_CHOICES,
280 null=True, blank=True)
bd28238f 281
139686f2
NC
282 def __unicode__(self):
283 return u'%s %s' % (self.prenom, self.nom)
284
bd28238f
NC
285
286COMPTE_COMPTA_CHOICES = (
494ff2be
NC
287 ('coda', 'CODA'),
288 ('scs', 'SCS'),
289 ('aucun', 'Aucun'),
bd28238f
NC
290)
291
d766bf2c
OL
292class DossierPiece(models.Model):
293 dossier = models.ForeignKey("Dossier")
294 nom = models.CharField(verbose_name="Nom", max_length=255)
295 fichier = models.FileField(verbose_name="Fichier", upload_to=dossier_piece_dispatch, storage=storage_prive)
bd28238f
NC
296
297class Dossier(models.Model):
bd28238f
NC
298
299 # Modèle existant
139686f2
NC
300 employe = models.ForeignKey('Employe', related_name='+', editable=False)
301 poste = models.ForeignKey('Poste', related_name='+', editable=False)
5d680e84 302 statut = models.ForeignKey(rh.Statut, related_name='+')
dfc2c344 303 organisme_bstg = models.ForeignKey(rh.OrganismeBstg,
1f109689 304 null=True, blank=True,
dfc2c344 305 verbose_name="Organisme",
306 help_text="Si détaché (DET) ou mis à disposition (MAD), \
307 préciser l'organisme.",
308 related_name='+')
0288adb5
OL
309 organisme_bstg_autre = models.CharField(max_length=255,
310 verbose_name="Autre organisme",
311 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
312 null=True,
313 blank=True,)
bd28238f 314
139686f2
NC
315 # Données antérieures de l'employé
316 statut_anterieur = models.ForeignKey(
317 rh.Statut, related_name='+', null=True, blank=True,
da3ca955 318 verbose_name='Statut antérieur')
139686f2
NC
319 classement_anterieur = models.ForeignKey(
320 rh.Classement, related_name='+', null=True, blank=True,
321 verbose_name='Classement précédent')
322 salaire_anterieur = models.DecimalField(
323 max_digits=12, decimal_places=2, null=True, default=None,
324 blank=True, verbose_name='Salaire précédent')
325
326 # Données du titulaire précédent
327 employe_anterieur = models.ForeignKey(
328 rh.Employe, related_name='+', null=True, blank=True,
329 verbose_name='Employé précédent')
330 statut_titulaire_anterieur = models.ForeignKey(
331 rh.Statut, related_name='+', null=True, blank=True,
332 verbose_name='Statut titulaire précédent')
333 classement_titulaire_anterieur = models.ForeignKey(
334 rh.Classement, related_name='+', null=True, blank=True,
335 verbose_name='Classement titulaire précédent')
336 salaire_titulaire_anterieur = models.DecimalField(
337 max_digits=12, decimal_places=2, default=None, null=True,
338 blank=True, verbose_name='Salaire titulaire précédent')
494ff2be 339
bd28238f
NC
340 # Recrutement
341 remplacement = models.BooleanField()
4d25e2ba 342 statut_residence = models.CharField(max_length=10, default='local',
343 verbose_name="Statut",
bd28238f
NC
344 choices=STATUT_RESIDENCE_CHOICES)
345
346 # Rémunération
494ff2be
NC
347 classement = models.ForeignKey(rh.Classement, related_name='+',
348 verbose_name='Classement proposé')
349 salaire = models.DecimalField(max_digits=12, decimal_places=2,
bd28238f 350 verbose_name='Salaire de base',
494ff2be 351 null=True, default=None)
e8e75458 352 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
7ad7408e 353 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
4d25e2ba 354 verbose_name="Régime de travail",
355 help_text="% du temps complet")
139686f2 356 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
4d25e2ba 357 decimal_places=2, verbose_name="Nb. heures par semaine")
bd28238f
NC
358
359 # Contrat
5d680e84 360 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
4d25e2ba 361 contrat_date_debut = models.DateField(help_text="format: aaaa-mm-jj")
1f109689 362 contrat_date_fin = models.DateField(null=True, blank=True,
363 help_text="format: aaaa-mm-jj")
bd28238f
NC
364
365 # Comptes
dfc2c344 366 compte_compta = models.CharField(max_length=10, default='aucun',
367 verbose_name=u'Compte comptabilité',
368 choices=COMPTE_COMPTA_CHOICES)
bd28238f 369 compte_courriel = models.BooleanField()
0140cbd2 370
371 # Méta
372 date_creation = models.DateTimeField(auto_now_add=True)
aec2c91e 373
374 def __unicode__(self):
375 return u'%s - %s' % (self.poste.nom, self.employe)
bd28238f 376
0140cbd2 377# Tester l'enregistrement car les models.py sont importés au complet
378if not reversion.is_registered(Dossier):
379 reversion.register(Dossier)
bd28238f
NC
380
381class Remuneration(models.Model):
5d680e84 382 # Identification
bd28238f 383 dossier = models.ForeignKey('Dossier', db_column='dossier')
cb1d62b5
NC
384 type = models.ForeignKey(rh.TypeRemuneration, db_column='type',
385 related_name='+')
5d680e84
NC
386 # TODO: what's that?
387 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
388 # db_column='type_revalorisation')
cb1d62b5
NC
389 montant = models.DecimalField(max_digits=12, decimal_places=2,
390 null=True) # Annuel
494ff2be 391 devise = models.ForeignKey(rh.Devise, to_field='code',
5d680e84 392 db_column='devise', related_name='+')
cb1d62b5
NC
393 precision = models.CharField(max_length=255, null=True, blank=True)
394 # date_effective = models.DateField(null=True, blank=True)
395 # pourcentage = models.IntegerField(null=True, blank=True)
bd28238f 396
5d680e84 397 # Méta
bd28238f 398 date_creation = models.DateField(auto_now_add=True)
494ff2be 399 user_creation = models.IntegerField(null=True, blank=True)
cb1d62b5
NC
400 # desactivation = models.BooleanField(default=False, blank=True)
401 # date_desactivation = models.DateField(null=True, blank=True)
402 # user_desactivation = models.IntegerField(null=True, blank=True)
403 # annulation = models.BooleanField(default=False, blank=True)
404 # date_annulation = models.DateField(null=True, blank=True)
405 # user_annulation = models.IntegerField(null=True, blank=True)
bd28238f 406
ee91bc95
NC
407 def montant_mois(self):
408 return round(self.montant / 12, 2)
409
410 def taux_devise(self):
411 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
412
413 def montant_euro(self):
414 return round(float(self.montant) / float(self.taux_devise()), 2)
415
416 def montant_euro_mois(self):
417 return round(self.montant_euro() / 12, 2)
bd28238f
NC
418
419
72db8238
OL
420TYPE_JUSTIFICATIONS = (
421 ('N', 'Nouvel employé'),
422 ('R', 'Renouvellement employé'),
423)
bd28238f 424
72db8238
OL
425class JustificationQuestion(models.Model):
426 question = models.CharField(max_length=255)
427 type = models.CharField(max_length=255, choices=TYPE_JUSTIFICATIONS)
428
429 def __unicode__(self,):
430 return self.question
bd28238f 431
72db8238
OL
432class JustificationNouvelEmploye(models.Model):
433 dossier = models.ForeignKey("Dossier")
434 question = models.ForeignKey("JustificationQuestion")
435 reponse = models.TextField()
bd28238f 436
72db8238
OL
437class JustificationAutreEmploye(models.Model):
438 dossier = models.ForeignKey("Dossier")
439 question = models.ForeignKey("JustificationQuestion")
440 reponse = models.TextField()
bd28238f
NC
441
442
443class Validation(models.Model):
444 # user
445 date = models.DateField()
446
447 # avis = ? (CHOICES?)
448
449
450class ValidationPoste(models.Model):
451 poste = models.ForeignKey('Poste')
452
453
454class ValidationEmploye(models.Model):
455 employe = models.ForeignKey('Employe')