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