form embauche nom du poste précédent
[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,
83 default=40,
84 verbose_name="Nb. heures par semaine")
bd28238f
NC
85
86 # Recrutement
4d25e2ba 87 statut_residence = models.CharField(max_length=10, default='local',
5efcd48e 88 verbose_name="Statut",
bd28238f 89 choices=STATUT_RESIDENCE_CHOICES)
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='+')
96d32304 99 coefficient_min = models.FloatField(null=True) # pour classement "hors grille"
100 coefficient_max = models.FloatField(null=True) # pour classement "hors grille"
4dd75e7b
OL
101 valeur_point_min = models.ForeignKey(rh.ValeurPoint, related_name='+', blank=True, null=True)
102 valeur_point_max = models.ForeignKey(rh.ValeurPoint, related_name='+', blank=True, null=True)
3d627bfd 103 devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
104 devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
5d680e84
NC
105 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
106 default=0)
107 salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
108 default=0)
109 indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
110 default=0)
111 indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
112 default=0)
113 autre_min = models.DecimalField(max_digits=12, decimal_places=2,
114 default=0)
115 autre_max = models.DecimalField(max_digits=12, decimal_places=2,
116 default=0)
117
118 # Comparatifs de rémunération
119 devise_comparaison = models.ForeignKey(rh.Devise, related_name='+',
a3508c67 120 default=5)
5d680e84
NC
121 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
122 null=True, blank=True)
123 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
124 null=True, blank=True)
125 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
126 null=True, blank=True)
127 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
128 null=True, blank=True)
129 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
130 null=True, blank=True)
131 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
132 null=True, blank=True)
133 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
134 null=True, blank=True)
135 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
136 null=True, blank=True)
137 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
138 null=True, blank=True)
139 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
140 null=True, blank=True)
bd28238f 141
a05cc82d
OL
142 # Validations
143 validation_bureau_regional = models.BooleanField(verbose_name="Validation bureau régional")
144 validation_bureau_regional_date = models.DateField(blank=True, null=True)
145 validation_drh = models.BooleanField(verbose_name="Validation DRH")
146 validation_drh_date = models.DateField(blank=True, null=True)
147 validation_secretaire_general = models.BooleanField(verbose_name="Validation secrétaire général")
148 validation_secretaire_general_date = models.DateField(blank=True, null=True)
149 validation_recteur = models.BooleanField(verbose_name="Validation recteur")
150 validation_recteur_date = models.DateField(blank=True, null=True)
151
bd28238f 152 # Méta
5d680e84
NC
153 date_creation = models.DateTimeField(auto_now_add=True)
154 date_modification = models.DateTimeField(auto_now=True)
98d51b59 155 date_debut = models.DateField(verbose_name="Date de début",
9fb2ccd9 156 help_text="format: aaaa-mm-jj")
157 date_fin = models.DateField(null=True, blank=True,
158 verbose_name="Date de fin",
159 help_text="format: aaaa-mm-jj")
bd28238f
NC
160 actif = models.BooleanField(default=True)
161
1c7d67ce
OL
162 # Managers
163 objects = PosteManager()
164
03858ba5
OL
165 def _get_key(self):
166 """
167 Les vues sont montées selon une clef spéciale pour identifier la provenance du poste.
168 Cette méthode fournit un moyen de reconstruire cette clef afin de générer les URLs.
169 """
170 return "dae-%s" % self.id
171 key = property(_get_key)
172
f3333b0e
OL
173 def get_dossiers(self):
174 """
175 Liste tous les anciens dossiers liés à ce poste.
176 (Le nom de la relation sur le rh.Poste est mal choisi poste1 au lieu de dossier1)
177 Note1 : seulement le dosssier principal fait l'objet de la recherche.
178 Note2 : les dossiers sont retournés du plus récent au plus vieux. (Ce test est fait
179 en fonction du id, car les dates de création sont absentes de rh v1).
180 """
181 if self.id_rh is None:
182 return []
183 postes = [p for p in self.id_rh.poste1.all()]
184 return sorted(postes, key=lambda poste: poste.id, reverse=True)
185
186 def get_complement_nom(self):
187 """
188 Inspecte les modèles rh v1 pour trouver dans le dernier dossier un complément de titre de poste.
189 """
190 dossiers = self.get_dossiers()
191 if len(dossiers) > 0:
192 nom = dossiers[0].complement1
193 else:
194 nom = ""
a9c281dd 195 return nom
f3333b0e
OL
196
197 def get_employe(self):
198 """
199 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
200 """
201 dossiers = self.get_dossiers()
202 if len(dossiers) > 0:
203 return dossiers[0].employe
204 else:
205 return None
206
179f6b49
OL
207 def get_default_devise(self):
208 """Récupère la devise par défaut en fonction de l'implantation (EUR autrement)"""
209 try:
210 implantation_devise = rh.TauxChange.objects.filter(implantation=self.implantation)[0].devise
211 except:
212 implantation_devise = 5 # EUR
213 return implantation_devise
214
5d680e84 215 def __unicode__(self):
f3333b0e
OL
216 """
217 Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
218 """
219 complement_nom_poste = self.get_complement_nom()
c7a4aa2b
OL
220 if complement_nom_poste is None:
221 complement_nom_poste = ""
f3333b0e 222 employe = self.get_employe()
c7a4aa2b
OL
223 if employe is None:
224 employe = "VACANT"
f3333b0e
OL
225 data = (
226 self.implantation,
227 self.type_poste.nom,
228 self.nom,
229 self.id,
230 complement_nom_poste,
231 employe,
232 )
233 return u'%s - %s (%s) [dae-%s %s %s]' % data
5d680e84 234
bd28238f 235
a9c281dd
OL
236# Tester l'enregistrement car les models.py sont importés au complet
237if not reversion.is_registered(Poste):
238 reversion.register(Poste)
239
bd28238f 240
5d680e84
NC
241POSTE_FINANCEMENT_CHOICES = (
242 ('A', 'A - Frais de personnel'),
243 ('B', 'B - Projet(s)-Titre(s)'),
244 ('C', 'C - Autre')
245)
bd28238f
NC
246
247
5d680e84 248class PosteFinancement(models.Model):
5d680e84
NC
249 poste = models.ForeignKey('Poste', related_name='financements')
250 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
43d04712 251 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
252 help_text="ex.: 33.33 % (décimale avec point)")
253 commentaire = models.TextField(
254 help_text="Spécifiez la source de financement.")
bd28238f 255
43d04712 256 class Meta:
257 ordering = ['type']
bd28238f
NC
258
259GENRE_CHOICES = (
139686f2
NC
260 ('m', 'Homme'),
261 ('f', 'Femme'),
bd28238f
NC
262)
263
264
265class Employe(models.Model):
bd28238f
NC
266
267 # Modèle existant
da3ca955 268 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
269 verbose_name='Employé')
bd28238f 270 nom = models.CharField(max_length=255)
da3ca955 271 prenom = models.CharField(max_length=255, verbose_name='Prénom')
494ff2be
NC
272 genre = models.CharField(max_length=1, choices=GENRE_CHOICES,
273 null=True, blank=True)
bd28238f 274
139686f2
NC
275 def __unicode__(self):
276 return u'%s %s' % (self.prenom, self.nom)
277
bd28238f
NC
278
279COMPTE_COMPTA_CHOICES = (
494ff2be
NC
280 ('coda', 'CODA'),
281 ('scs', 'SCS'),
282 ('aucun', 'Aucun'),
bd28238f
NC
283)
284
d766bf2c
OL
285class DossierPiece(models.Model):
286 dossier = models.ForeignKey("Dossier")
287 nom = models.CharField(verbose_name="Nom", max_length=255)
288 fichier = models.FileField(verbose_name="Fichier", upload_to=dossier_piece_dispatch, storage=storage_prive)
bd28238f
NC
289
290class Dossier(models.Model):
bd28238f
NC
291
292 # Modèle existant
139686f2
NC
293 employe = models.ForeignKey('Employe', related_name='+', editable=False)
294 poste = models.ForeignKey('Poste', related_name='+', editable=False)
5d680e84 295 statut = models.ForeignKey(rh.Statut, related_name='+')
dfc2c344 296 organisme_bstg = models.ForeignKey(rh.OrganismeBstg,
1f109689 297 null=True, blank=True,
dfc2c344 298 verbose_name="Organisme",
299 help_text="Si détaché (DET) ou mis à disposition (MAD), \
300 préciser l'organisme.",
301 related_name='+')
0288adb5
OL
302 organisme_bstg_autre = models.CharField(max_length=255,
303 verbose_name="Autre organisme",
304 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
305 null=True,
306 blank=True,)
bd28238f 307
139686f2
NC
308 # Données antérieures de l'employé
309 statut_anterieur = models.ForeignKey(
310 rh.Statut, related_name='+', null=True, blank=True,
da3ca955 311 verbose_name='Statut antérieur')
139686f2
NC
312 classement_anterieur = models.ForeignKey(
313 rh.Classement, related_name='+', null=True, blank=True,
314 verbose_name='Classement précédent')
315 salaire_anterieur = models.DecimalField(
316 max_digits=12, decimal_places=2, null=True, default=None,
317 blank=True, verbose_name='Salaire précédent')
318
319 # Données du titulaire précédent
320 employe_anterieur = models.ForeignKey(
321 rh.Employe, related_name='+', null=True, blank=True,
322 verbose_name='Employé précédent')
323 statut_titulaire_anterieur = models.ForeignKey(
324 rh.Statut, related_name='+', null=True, blank=True,
325 verbose_name='Statut titulaire précédent')
326 classement_titulaire_anterieur = models.ForeignKey(
327 rh.Classement, related_name='+', null=True, blank=True,
328 verbose_name='Classement titulaire précédent')
329 salaire_titulaire_anterieur = models.DecimalField(
330 max_digits=12, decimal_places=2, default=None, null=True,
331 blank=True, verbose_name='Salaire titulaire précédent')
494ff2be 332
bd28238f
NC
333 # Recrutement
334 remplacement = models.BooleanField()
4d25e2ba 335 statut_residence = models.CharField(max_length=10, default='local',
336 verbose_name="Statut",
bd28238f
NC
337 choices=STATUT_RESIDENCE_CHOICES)
338
339 # Rémunération
494ff2be
NC
340 classement = models.ForeignKey(rh.Classement, related_name='+',
341 verbose_name='Classement proposé')
342 salaire = models.DecimalField(max_digits=12, decimal_places=2,
bd28238f 343 verbose_name='Salaire de base',
494ff2be 344 null=True, default=None)
e8e75458 345 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
7ad7408e 346 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
4d25e2ba 347 verbose_name="Régime de travail",
348 help_text="% du temps complet")
139686f2 349 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
4d25e2ba 350 decimal_places=2, verbose_name="Nb. heures par semaine")
bd28238f
NC
351
352 # Contrat
5d680e84 353 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
4d25e2ba 354 contrat_date_debut = models.DateField(help_text="format: aaaa-mm-jj")
1f109689 355 contrat_date_fin = models.DateField(null=True, blank=True,
356 help_text="format: aaaa-mm-jj")
bd28238f
NC
357
358 # Comptes
dfc2c344 359 compte_compta = models.CharField(max_length=10, default='aucun',
360 verbose_name=u'Compte comptabilité',
361 choices=COMPTE_COMPTA_CHOICES)
bd28238f 362 compte_courriel = models.BooleanField()
0140cbd2 363
364 # Méta
365 date_creation = models.DateTimeField(auto_now_add=True)
aec2c91e 366
367 def __unicode__(self):
368 return u'%s - %s' % (self.poste.nom, self.employe)
bd28238f 369
0140cbd2 370# Tester l'enregistrement car les models.py sont importés au complet
371if not reversion.is_registered(Dossier):
372 reversion.register(Dossier)
bd28238f
NC
373
374class Remuneration(models.Model):
5d680e84 375 # Identification
bd28238f 376 dossier = models.ForeignKey('Dossier', db_column='dossier')
cb1d62b5
NC
377 type = models.ForeignKey(rh.TypeRemuneration, db_column='type',
378 related_name='+')
5d680e84
NC
379 # TODO: what's that?
380 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
381 # db_column='type_revalorisation')
cb1d62b5
NC
382 montant = models.DecimalField(max_digits=12, decimal_places=2,
383 null=True) # Annuel
494ff2be 384 devise = models.ForeignKey(rh.Devise, to_field='code',
5d680e84 385 db_column='devise', related_name='+')
cb1d62b5
NC
386 precision = models.CharField(max_length=255, null=True, blank=True)
387 # date_effective = models.DateField(null=True, blank=True)
388 # pourcentage = models.IntegerField(null=True, blank=True)
bd28238f 389
5d680e84 390 # Méta
bd28238f 391 date_creation = models.DateField(auto_now_add=True)
494ff2be 392 user_creation = models.IntegerField(null=True, blank=True)
cb1d62b5
NC
393 # desactivation = models.BooleanField(default=False, blank=True)
394 # date_desactivation = models.DateField(null=True, blank=True)
395 # user_desactivation = models.IntegerField(null=True, blank=True)
396 # annulation = models.BooleanField(default=False, blank=True)
397 # date_annulation = models.DateField(null=True, blank=True)
398 # user_annulation = models.IntegerField(null=True, blank=True)
bd28238f 399
ee91bc95
NC
400 def montant_mois(self):
401 return round(self.montant / 12, 2)
402
403 def taux_devise(self):
404 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
405
406 def montant_euro(self):
407 return round(float(self.montant) / float(self.taux_devise()), 2)
408
409 def montant_euro_mois(self):
410 return round(self.montant_euro() / 12, 2)
bd28238f
NC
411
412
413class JustificationPoste(models.Model):
414 pass
415
416
417class JustificationEmploye(models.Model):
418 pass
419
420
421class DocumentPoste(models.Model):
422 pass
423
424
425class DocumentEmploye(models.Model):
426 pass
427
428
429class Validation(models.Model):
430 # user
431 date = models.DateField()
432
433 # avis = ? (CHOICES?)
434
435
436class ValidationPoste(models.Model):
437 poste = models.ForeignKey('Poste')
438
439
440class ValidationEmploye(models.Model):
441 employe = models.ForeignKey('Employe')