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