autre organisme
[auf_rh_dae.git] / project / dae / models.py
1 # -=- encoding: utf-8 -=-
2
3 import os
4 from django.core.files.storage import FileSystemStorage
5 from django.db import models
6 import reversion
7 from workflow import PosteWorkflow
8 import datamaster_modeles.models as ref
9 from rh_v1 import models as rh
10 import settings
11
12 STATUT_RESIDENCE_CHOICES = (
13 ('local', 'Local'),
14 ('expat', 'Expatrié'),
15 )
16
17 POSTE_APPEL_CHOICES = (
18 ('interne', 'Interne'),
19 ('externe', 'Externe'),
20 )
21
22 POSTE_STATUT_CHOICES = (
23 ('MAD', 'Mise à disposition'),
24 ('DET', 'Détachement'),
25 )
26
27 # Upload de fichiers
28 storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT, base_url=settings.PRIVE_MEDIA_URL)
29
30 def poste_piece_dispatch(instance, filename):
31 path = "poste/%s/%s" % (instance.poste_id, filename)
32 return path
33
34 def dossier_piece_dispatch(instance, filename):
35 path = "dossier/%s/%s" % (instance.dossier_id, filename)
36 return path
37
38
39 class 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
44 class 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 )
60 return super(PosteManager, self).get_query_set() \
61 .select_related(*fkeys).all()
62
63
64 class Poste(PosteWorkflow, models.Model):
65 # Modèle existant
66 id_rh = models.ForeignKey(rh.Poste, null=True, related_name='+',
67 editable=False,
68 verbose_name="Mise à jour du poste")
69 nom = models.CharField(verbose_name="Titre du poste", max_length=255)
70 implantation = models.ForeignKey(ref.Implantation)
71 type_poste = models.ForeignKey(rh.TypePoste, null=True, related_name='+')
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='+',
75 verbose_name="Poste du responsable")
76
77 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
78 default=100,
79 verbose_name="Temps de travail",
80 help_text="% du temps complet")
81 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
82 decimal_places=2,
83 default=40,
84 verbose_name="Nb. heures par semaine")
85
86 # Recrutement
87 statut_residence = models.CharField(max_length=10, default='local',
88 verbose_name="Statut",
89 choices=STATUT_RESIDENCE_CHOICES)
90 # TODO null?
91 mise_a_disposition = models.BooleanField(verbose_name="Mise à disposition")
92 appel = models.CharField(max_length=10, default='interne',
93 verbose_name="Appel à candidature",
94 choices=POSTE_APPEL_CHOICES)
95
96 # Rémunération
97 classement_min = models.ForeignKey(rh.Classement, related_name='+')
98 classement_max = models.ForeignKey(rh.Classement, related_name='+')
99 coefficient_min = models.FloatField(null=True) # pour classement "hors grille"
100 coefficient_max = models.FloatField(null=True) # pour classement "hors grille"
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)
103 devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
104 devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
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='+',
120 default=5)
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)
141
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
152 # Méta
153 date_creation = models.DateTimeField(auto_now_add=True)
154 date_modification = models.DateTimeField(auto_now=True)
155 date_debut = models.DateField(verbose_name="Date de début",
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")
160 actif = models.BooleanField(default=True)
161
162 # Managers
163 objects = PosteManager()
164
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
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 = ""
195 return nom
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
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
215 def __unicode__(self):
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()
220 if complement_nom_poste is None:
221 complement_nom_poste = ""
222 employe = self.get_employe()
223 if employe is None:
224 employe = "VACANT"
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
234
235
236 # Tester l'enregistrement car les models.py sont importés au complet
237 if not reversion.is_registered(Poste):
238 reversion.register(Poste)
239
240
241 POSTE_FINANCEMENT_CHOICES = (
242 ('A', 'A - Frais de personnel'),
243 ('B', 'B - Projet(s)-Titre(s)'),
244 ('C', 'C - Autre')
245 )
246
247
248 class PosteFinancement(models.Model):
249 montant = models.DecimalField(max_digits=12, decimal_places=2,
250 help_text="ex.: 12000.00 € (décimale avec point, devise = EUR)")
251 poste = models.ForeignKey('Poste', related_name='financements')
252 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
253 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
254 help_text="ex.: 33.33 % (décimale avec point)")
255 commentaire = models.TextField(
256 help_text="Spécifiez la source de financement.")
257
258 class Meta:
259 ordering = ['type']
260
261 GENRE_CHOICES = (
262 ('m', 'Homme'),
263 ('f', 'Femme'),
264 )
265
266
267 class Employe(models.Model):
268
269 # Modèle existant
270 id_rh = models.ForeignKey(rh.Employe, null=True, related_name='+',
271 verbose_name='Employé')
272 nom = models.CharField(max_length=255)
273 prenom = models.CharField(max_length=255, verbose_name='Prénom')
274 genre = models.CharField(max_length=1, choices=GENRE_CHOICES,
275 null=True, blank=True)
276
277 def __unicode__(self):
278 return u'%s %s' % (self.prenom, self.nom)
279
280
281 COMPTE_COMPTA_CHOICES = (
282 ('coda', 'CODA'),
283 ('scs', 'SCS'),
284 ('aucun', 'Aucun'),
285 )
286
287 class DossierPiece(models.Model):
288 dossier = models.ForeignKey("Dossier")
289 nom = models.CharField(verbose_name="Nom", max_length=255)
290 fichier = models.FileField(verbose_name="Fichier", upload_to=dossier_piece_dispatch, storage=storage_prive)
291
292 class Dossier(models.Model):
293
294 # Modèle existant
295 employe = models.ForeignKey('Employe', related_name='+', editable=False)
296 poste = models.ForeignKey('Poste', related_name='+', editable=False)
297 statut = models.ForeignKey(rh.Statut, related_name='+')
298 organisme_bstg = models.ForeignKey(rh.OrganismeBstg,
299 null=True, blank=True,
300 verbose_name="Organisme",
301 help_text="Si détaché (DET) ou mis à disposition (MAD), \
302 préciser l'organisme.",
303 related_name='+')
304 organisme_bstg_autre = models.CharField(max_length=255,
305 verbose_name="Autre organisme",
306 help_text="indiquer l'organisme ici s'il n'est pas dans la liste",
307 null=True,
308 blank=True,)
309
310 # Données antérieures de l'employé
311 statut_anterieur = models.ForeignKey(
312 rh.Statut, related_name='+', null=True, blank=True,
313 verbose_name='Statut antérieur')
314 #type contrat
315 classement_anterieur = models.ForeignKey(
316 rh.Classement, related_name='+', null=True, blank=True,
317 verbose_name='Classement précédent')
318 salaire_anterieur = models.DecimalField(
319 max_digits=12, decimal_places=2, null=True, default=None,
320 blank=True, verbose_name='Salaire précédent')
321
322 # Données du titulaire précédent
323 employe_anterieur = models.ForeignKey(
324 rh.Employe, related_name='+', null=True, blank=True,
325 verbose_name='Employé précédent')
326 statut_titulaire_anterieur = models.ForeignKey(
327 rh.Statut, related_name='+', null=True, blank=True,
328 verbose_name='Statut titulaire précédent')
329 classement_titulaire_anterieur = models.ForeignKey(
330 rh.Classement, related_name='+', null=True, blank=True,
331 verbose_name='Classement titulaire précédent')
332 salaire_titulaire_anterieur = models.DecimalField(
333 max_digits=12, decimal_places=2, default=None, null=True,
334 blank=True, verbose_name='Salaire titulaire précédent')
335
336 # Recrutement
337 remplacement = models.BooleanField()
338 statut_residence = models.CharField(max_length=10, default='local',
339 verbose_name="Statut",
340 choices=STATUT_RESIDENCE_CHOICES)
341
342 # Rémunération
343 classement = models.ForeignKey(rh.Classement, related_name='+',
344 verbose_name='Classement proposé')
345 salaire = models.DecimalField(max_digits=12, decimal_places=2,
346 verbose_name='Salaire de base',
347 null=True, default=None)
348 devise = models.ForeignKey(rh.Devise, default=5, related_name='+')
349 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
350 verbose_name="Régime de travail",
351 help_text="% du temps complet")
352 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
353 decimal_places=2, verbose_name="Nb. heures par semaine")
354
355 # Contrat
356 type_contrat = models.ForeignKey(rh.TypeContrat, related_name='+')
357 contrat_date_debut = models.DateField(help_text="format: aaaa-mm-jj")
358 contrat_date_fin = models.DateField(null=True, blank=True,
359 help_text="format: aaaa-mm-jj")
360
361 # Comptes
362 compte_compta = models.CharField(max_length=10, default='aucun',
363 verbose_name=u'Compte comptabilité',
364 choices=COMPTE_COMPTA_CHOICES)
365 compte_courriel = models.BooleanField()
366
367 # Méta
368 date_creation = models.DateTimeField(auto_now_add=True)
369
370 def __unicode__(self):
371 return u'%s - %s' % (self.poste.nom, self.employe)
372
373 # Tester l'enregistrement car les models.py sont importés au complet
374 if not reversion.is_registered(Dossier):
375 reversion.register(Dossier)
376
377 class Remuneration(models.Model):
378 # Identification
379 dossier = models.ForeignKey('Dossier', db_column='dossier')
380 type = models.ForeignKey(rh.TypeRemuneration, db_column='type',
381 related_name='+')
382 # TODO: what's that?
383 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
384 # db_column='type_revalorisation')
385 montant = models.DecimalField(max_digits=12, decimal_places=2,
386 null=True) # Annuel
387 devise = models.ForeignKey(rh.Devise, to_field='code',
388 db_column='devise', related_name='+')
389 precision = models.CharField(max_length=255, null=True, blank=True)
390 # date_effective = models.DateField(null=True, blank=True)
391 # pourcentage = models.IntegerField(null=True, blank=True)
392
393 # Méta
394 date_creation = models.DateField(auto_now_add=True)
395 user_creation = models.IntegerField(null=True, blank=True)
396 # desactivation = models.BooleanField(default=False, blank=True)
397 # date_desactivation = models.DateField(null=True, blank=True)
398 # user_desactivation = models.IntegerField(null=True, blank=True)
399 # annulation = models.BooleanField(default=False, blank=True)
400 # date_annulation = models.DateField(null=True, blank=True)
401 # user_annulation = models.IntegerField(null=True, blank=True)
402
403 def montant_mois(self):
404 return round(self.montant / 12, 2)
405
406 def taux_devise(self):
407 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
408
409 def montant_euro(self):
410 return round(float(self.montant) / float(self.taux_devise()), 2)
411
412 def montant_euro_mois(self):
413 return round(self.montant_euro() / 12, 2)
414
415
416 class JustificationPoste(models.Model):
417 pass
418
419
420 class JustificationEmploye(models.Model):
421 pass
422
423
424 class DocumentPoste(models.Model):
425 pass
426
427
428 class DocumentEmploye(models.Model):
429 pass
430
431
432 class Validation(models.Model):
433 # user
434 date = models.DateField()
435
436 # avis = ? (CHOICES?)
437
438
439 class ValidationPoste(models.Model):
440 poste = models.ForeignKey('Poste')
441
442
443 class ValidationEmploye(models.Model):
444 employe = models.ForeignKey('Employe')