e991319f295e5d154b56b3be8dfe2d36daae472c
[auf_rh_dae.git] / project / rh / models.py
1 # -=- encoding: utf-8 -=-
2
3 import datetime
4
5 from django.core.files.storage import FileSystemStorage
6 from django.db import models
7 import settings
8
9 import datamaster_modeles.models as ref
10
11
12 # Constantes
13 HELP_TEXT_DATE = "format: aaaa-mm-jj"
14 REGIME_TRAVAIL_DEFAULT = 100.00
15 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT = 35.00
16
17
18 # Upload de fichiers
19 storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
20 base_url=settings.PRIVE_MEDIA_URL)
21
22 def poste_piece_dispatch(instance, filename):
23 path = "poste/%s/%s" % (instance.poste_id, filename)
24 return path
25
26 def dossier_piece_dispatch(instance, filename):
27 path = "dossier/%s/%s" % (instance.dossier_id, filename)
28 return path
29
30 # Abstracts
31 class Metadata(models.Model):
32 """Méta-données AUF.
33 Metadata.actif = flag remplaçant la suppression.
34 actif == False : objet réputé supprimé.
35 """
36 actif = models.BooleanField(default=True)
37 date_creation = models.DateField(auto_now_add=True)
38 user_creation = models.ForeignKey('auth.User',
39 db_column='user_creation', related_name='+',
40 null=True, blank=True)
41 date_modification = models.DateField(auto_now=True)
42 user_modification = models.ForeignKey('auth.User',
43 db_column='user_modification', related_name='+',
44 null=True, blank=True)
45 date_desactivation = models.DateField(null=True, blank=True)
46 user_desactivation = models.ForeignKey('auth.User',
47 db_column='user_desactivation', related_name='+',
48 null=True, blank=True)
49
50 class Meta:
51 abstract = True
52
53 class Commentaire(Metadata):
54 texte = models.TextField()
55 owner = models.ForeignKey('auth.User', db_column='owner', related_name='+')
56
57 class Meta:
58 abstract = True
59 ordering = ['-date_creation']
60
61 def __unicode__(self):
62 return u'%s' % (self.texte)
63
64
65 ### POSTE
66
67 POSTE_APPEL_CHOICES = (
68 ('interne', 'Interne'),
69 ('externe', 'Externe'),
70 )
71
72 class Poste_(Metadata):
73 """Un Poste est un emploi (job) à combler dans une implantation.
74 Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
75 Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
76 """
77 # Identification
78 nom = models.CharField(max_length=255,
79 verbose_name="Titre du poste", )
80 nom_feminin = models.CharField(max_length=255,
81 verbose_name="Titre du poste (au féminin)",
82 null=True)
83 implantation = models.ForeignKey(ref.Implantation,
84 db_column='implantation', related_name='+')
85 type_poste = models.ForeignKey('TypePoste', db_column='type_poste',
86 related_name='+',
87 null=True)
88 service = models.ForeignKey('Service', db_column='service',
89 related_name='+',
90 verbose_name="Direction/Service/Pôle support",
91 default=1) # default = Rectorat
92 responsable = models.ForeignKey('Poste', db_column='responsable',
93 related_name='+',
94 verbose_name="Poste du responsable",
95 default=149) # default = Recteur
96
97 # Contrat
98 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
99 default=REGIME_TRAVAIL_DEFAULT,
100 verbose_name="Temps de travail",
101 help_text="% du temps complet")
102 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
103 decimal_places=2,
104 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
105 verbose_name="Nb. heures par semaine")
106
107 # Recrutement
108 local = models.BooleanField(verbose_name="Local", default=True,
109 blank=True)
110 expatrie = models.BooleanField(verbose_name="Expatrié", default=False,
111 blank=True)
112 mise_a_disposition = models.BooleanField(
113 verbose_name="Mise à disposition",
114 default=False)
115 appel = models.CharField(max_length=10,
116 verbose_name="Appel à candidature",
117 choices=POSTE_APPEL_CHOICES,
118 default='interne')
119
120 # Rémunération
121 classement_min = models.ForeignKey('Classement',
122 db_column='classement_min', related_name='+',
123 null=True, blank=True)
124 classement_max = models.ForeignKey('Classement',
125 db_column='classement_max', related_name='+',
126 null=True, blank=True)
127 valeur_point_min = models.ForeignKey('ValeurPoint',
128 db_column='valeur_point_min', related_name='+',
129 null=True, blank=True)
130 valeur_point_max = models.ForeignKey('ValeurPoint',
131 db_column='valeur_point_max', related_name='+',
132 null=True, blank=True)
133 devise_min = models.ForeignKey('Devise', db_column='devise_min',
134 related_name='+', default=5)
135 devise_max = models.ForeignKey('Devise', db_column='devise_max',
136 related_name='+', default=5)
137 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
138 default=0)
139 salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
140 default=0)
141 indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
142 default=0)
143 indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
144 default=0)
145 autre_min = models.DecimalField(max_digits=12, decimal_places=2,
146 default=0)
147 autre_max = models.DecimalField(max_digits=12, decimal_places=2,
148 default=0)
149
150 # Comparatifs de rémunération
151 devise_comparaison = models.ForeignKey('Devise',
152 db_column='devise_comparaison',
153 related_name='+',
154 default=5)
155 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
156 null=True, blank=True)
157 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
158 null=True, blank=True)
159 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
160 null=True, blank=True)
161 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
162 null=True, blank=True)
163 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
164 null=True, blank=True)
165 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
166 null=True, blank=True)
167 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
168 null=True, blank=True)
169 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
170 null=True, blank=True)
171 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
172 null=True, blank=True)
173 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
174 null=True, blank=True)
175
176 # Justification
177 justification = models.TextField(null=True, blank=True)
178
179 # Autres Metadata
180 date_validation = models.DateTimeField(null=True, blank=True) # de dae
181 date_debut = models.DateField(verbose_name="Date de début",
182 help_text=HELP_TEXT_DATE)
183 date_fin = models.DateField(verbose_name="Date de fin",
184 help_text=HELP_TEXT_DATE,
185 null=True, blank=True)
186
187 class Meta:
188 abstract = True
189 ordering = ['implantation__nom', 'nom']
190 verbose_name = "Poste"
191 verbose_name_plural = "Postes"
192
193 def __unicode__(self):
194 representation = u'%s - %s [%s]' % (self.implantation, self.nom,
195 self.id)
196 if self.is_vacant():
197 representation = representation + u' (vacant)'
198 return representation
199
200 def is_vacant(self):
201 # TODO : si existe un dossier actif pour ce poste, return False
202 # self.dossier_set.all() fonctionne pas
203 return False
204
205
206 class Poste(Poste_):
207 __doc__ = Poste_.__doc__
208
209
210 POSTE_FINANCEMENT_CHOICES = (
211 ('A', 'A - Frais de personnel'),
212 ('B', 'B - Projet(s)-Titre(s)'),
213 ('C', 'C - Autre')
214 )
215
216
217 class PosteFinancement_(models.Model):
218 """Pour un Poste, structure d'informations décrivant comment on prévoit
219 financer ce Poste.
220 """
221 poste = models.ForeignKey('Poste', db_column='poste',
222 related_name='%(app_label)s_financements')
223 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
224 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
225 help_text="ex.: 33.33 % (décimale avec point)")
226 commentaire = models.TextField(
227 help_text="Spécifiez la source de financement.")
228
229 class Meta:
230 abstract = True
231 ordering = ['type']
232
233 def __unicode__(self):
234 return u'%s : %s %' % (self.type, self.pourcentage)
235
236
237 class PosteFinancement(PosteFinancement_):
238 __doc__ = PosteFinancement_.__doc__
239
240
241 class PostePiece(models.Model):
242 """Documents relatifs au Poste.
243 Ex.: Description de poste
244 """
245 poste = models.ForeignKey('Poste', db_column='poste',
246 related_name='pieces')
247 nom = models.CharField(verbose_name="Nom", max_length=255)
248 fichier = models.FileField(verbose_name="Fichier",
249 upload_to=poste_piece_dispatch,
250 storage=storage_prive)
251
252 class Meta:
253 ordering = ['nom']
254
255 def __unicode__(self):
256 return u'%s' % (self.nom)
257
258 class PosteComparaison(models.Model):
259 """
260 De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
261 """
262 poste = models.ForeignKey('Poste', related_name='comparaisons_internes')
263 implantation = models.ForeignKey(ref.Implantation, null=True, blank=True)
264 nom = models.CharField(verbose_name="Poste", max_length=255, null=True, blank=True)
265 montant = models.IntegerField(null=True)
266 devise = models.ForeignKey("Devise", default=5, related_name='+', null=True, blank=True)
267 montant_euros = models.IntegerField(null=True)
268
269
270 class PosteCommentaire(Commentaire):
271 poste = models.ForeignKey('Poste', db_column='poste', related_name='+')
272
273
274 ### EMPLOYÉ/PERSONNE
275
276 GENRE_CHOICES = (
277 ('M', 'Homme'),
278 ('F', 'Femme'),
279 )
280 SITUATION_CHOICES = (
281 ('C', 'Célibataire'),
282 ('F', 'Fiancé'),
283 ('M', 'Marié'),
284 )
285
286 class Employe(Metadata):
287 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
288 Dossiers qu'il occupe ou a occupé de Postes.
289
290 Cette classe aurait pu avantageusement s'appeler Personne car la notion
291 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
292 """
293 # Identification
294 nom = models.CharField(max_length=255)
295 prenom = models.CharField(max_length=255, verbose_name="Prénom")
296 nom_affichage = models.CharField(max_length=255,
297 verbose_name="Nom d'affichage",
298 null=True, blank=True)
299 nationalite = models.ForeignKey(ref.Pays, to_field='code',
300 db_column='nationalite',
301 related_name='employes_nationalite',
302 verbose_name="Nationalité")
303 date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
304 verbose_name="Date de naissance",
305 null=True, blank=True)
306 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
307
308 # Infos personnelles
309 situation_famille = models.CharField(max_length=1,
310 choices=SITUATION_CHOICES,
311 verbose_name="Situation familiale",
312 null=True, blank=True)
313 date_entree = models.DateField(verbose_name="Date d'entrée à l'AUF",
314 help_text=HELP_TEXT_DATE,
315 null=True, blank=True)
316
317 # Coordonnées
318 tel_domicile = models.CharField(max_length=255,
319 verbose_name="Tél. domicile",
320 null=True, blank=True)
321 tel_cellulaire = models.CharField(max_length=255,
322 verbose_name="Tél. cellulaire",
323 null=True, blank=True)
324 adresse = models.CharField(max_length=255, null=True, blank=True)
325 ville = models.CharField(max_length=255, null=True, blank=True)
326 province = models.CharField(max_length=255, null=True, blank=True)
327 code_postal = models.CharField(max_length=255, null=True, blank=True)
328 pays = models.ForeignKey(ref.Pays, to_field='code', db_column='pays',
329 related_name='employes',
330 null=True, blank=True)
331
332 class Meta:
333 ordering = ['nom_affichage','nom','prenom']
334 verbose_name = "Employé"
335 verbose_name_plural = "Employés"
336
337 def __unicode__(self):
338 return u'%s' % (self.get_nom())
339
340 def get_nom(self):
341 nom_affichage = self.nom_affichage
342 if not nom_affichage:
343 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
344 return nom_affichage
345
346 class EmployePiece(models.Model):
347 """Documents relatifs à un employé.
348 Ex.: CV...
349 """
350 employe = models.ForeignKey('Employe', db_column='employe',
351 related_name='+')
352 nom = models.CharField(verbose_name="Nom", max_length=255)
353 fichier = models.FileField(verbose_name="Fichier",
354 upload_to=dossier_piece_dispatch,
355 storage=storage_prive)
356
357 class Meta:
358 ordering = ['nom']
359
360 def __unicode__(self):
361 return u'%s' % (self.nom)
362
363 class EmployeCommentaire(Commentaire):
364 employe = models.ForeignKey('Employe', db_column='employe',
365 related_name='+')
366
367
368 LIEN_PARENTE_CHOICES = (
369 ('Conjoint', 'Conjoint'),
370 ('Conjointe', 'Conjointe'),
371 ('Fille', 'Fille'),
372 ('Fils', 'Fils'),
373 )
374
375 class AyantDroit(Metadata):
376 """Personne en relation avec un Employe.
377 """
378 # Identification
379 nom = models.CharField(max_length=255)
380 prenom = models.CharField(max_length=255,
381 verbose_name="Prénom",)
382 nom_affichage = models.CharField(max_length=255,
383 verbose_name="Nom d'affichage",
384 null=True, blank=True)
385 nationalite = models.ForeignKey(ref.Pays, to_field='code',
386 db_column='nationalite',
387 related_name='ayantdroits_nationalite',
388 verbose_name="Nationalité")
389 date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
390 verbose_name="Date de naissance",
391 null=True, blank=True)
392 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
393
394 # Relation
395 employe = models.ForeignKey('Employe', db_column='employe',
396 related_name='ayantdroits',
397 verbose_name="Employé")
398 lien_parente = models.CharField(max_length=10,
399 choices=LIEN_PARENTE_CHOICES,
400 verbose_name="Lien de parenté",
401 null=True, blank=True)
402
403 class Meta:
404 ordering = ['nom_affichage']
405 verbose_name = "Ayant droit"
406 verbose_name_plural = "Ayants droit"
407
408 def __unicode__(self):
409 return u'%s' % (self.get_nom())
410
411 def get_nom(self):
412 nom_affichage = self.nom_affichage
413 if not nom_affichage:
414 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
415 return nom_affichage
416
417 class AyantDroitCommentaire(Commentaire):
418 ayant_droit = models.ForeignKey('AyantDroit', db_column='ayant_droit',
419 related_name='+')
420
421
422 ### DOSSIER
423
424 STATUT_RESIDENCE_CHOICES = (
425 ('local', 'Local'),
426 ('expat', 'Expatrié'),
427 )
428
429 COMPTE_COMPTA_CHOICES = (
430 ('coda', 'CODA'),
431 ('scs', 'SCS'),
432 ('aucun', 'Aucun'),
433 )
434
435
436 class Dossier_(Metadata):
437 """Le Dossier regroupe les informations relatives à l'occupation
438 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
439 par un Employe.
440
441 Plusieurs Contrats peuvent être associés au Dossier.
442 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
443 lequel aucun Dossier n'existe est un poste vacant.
444 """
445 # Identification
446 employe = models.ForeignKey('Employe', db_column='employe',
447 related_name='+',
448 verbose_name="Employé")
449 poste = models.ForeignKey('Poste', db_column='poste',
450 related_name='+', editable=False)
451 statut = models.ForeignKey('Statut', related_name='+', default=3)
452 organisme_bstg = models.ForeignKey('OrganismeBstg',
453 db_column='organisme_bstg',
454 related_name='+',
455 verbose_name="Organisme",
456 help_text="Si détaché (DET) ou \
457 mis à disposition (MAD), \
458 préciser l'organisme.",
459 null=True, blank=True)
460
461 # Recrutement
462 remplacement = models.BooleanField(default=False)
463 statut_residence = models.CharField(max_length=10, default='local',
464 verbose_name="Statut",
465 choices=STATUT_RESIDENCE_CHOICES)
466
467 # Rémunération
468 classement = models.ForeignKey('Classement', db_column='classement',
469 related_name='+',
470 null=True, blank=True)
471 regime_travail = models.DecimalField(max_digits=12,
472 decimal_places=2,
473 default=REGIME_TRAVAIL_DEFAULT,
474 verbose_name="Régime de travail",
475 help_text="% du temps complet")
476 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
477 decimal_places=2,
478 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
479 verbose_name="Nb. heures par semaine")
480
481 # Occupation du Poste par cet Employe (anciennement "mandat")
482 date_debut = models.DateField(verbose_name="Date de début d'occupation \
483 de poste",
484 help_text=HELP_TEXT_DATE)
485 date_fin = models.DateField(verbose_name="Date de fin d'occupation \
486 de poste",
487 help_text=HELP_TEXT_DATE,
488 null=True, blank=True)
489
490 # Comptes
491 # TODO?
492
493 class Meta:
494 abstract = True
495 ordering = ['employe__nom_affichage', 'employe__nom', 'poste__nom']
496 verbose_name = "Dossier"
497 verbose_name_plural = "Dossiers"
498
499 def __unicode__(self):
500 poste = self.poste.nom
501 if self.employe.genre == 'F':
502 poste = self.poste.nom_feminin
503 return u'%s - %s' % (self.employe, poste)
504
505
506 class Dossier(Dossier_):
507 __doc__ = Dossier_.__doc__
508
509
510 class DossierPiece(models.Model):
511 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
512 Ex.: Lettre de motivation.
513 """
514 dossier = models.ForeignKey('Dossier', db_column='dossier',
515 related_name='+')
516 nom = models.CharField(verbose_name="Nom", max_length=255)
517 fichier = models.FileField(verbose_name="Fichier",
518 upload_to=dossier_piece_dispatch,
519 storage=storage_prive)
520
521 class Meta:
522 ordering = ['nom']
523
524 def __unicode__(self):
525 return u'%s' % (self.nom)
526
527 class DossierCommentaire(Commentaire):
528 dossier = models.ForeignKey('Dossier', db_column='dossier',
529 related_name='+')
530
531
532 ### RÉMUNÉRATION
533
534 class RemunerationMixin(Metadata):
535 # Identification
536 dossier = models.ForeignKey('Dossier', db_column='dossier',
537 related_name='%(app_label)s_%(class)s_remunerations')
538 type = models.ForeignKey('TypeRemuneration', db_column='type',
539 related_name='+',
540 verbose_name="Type de rémunération")
541 type_revalorisation = models.ForeignKey('TypeRevalorisation',
542 db_column='type_revalorisation',
543 related_name='+',
544 verbose_name="Type de revalorisation",
545 null=True, blank=True)
546 montant = models.FloatField(null=True, blank=True,
547 default=0)
548 # Annuel (12 mois, 52 semaines, 364 jours?)
549 devise = models.ForeignKey('Devise', to_field='id',
550 db_column='devise', related_name='+',
551 default=5)
552 # commentaire = precision
553 commentaire = models.CharField(max_length=255, null=True, blank=True)
554 # date_debut = anciennement date_effectif
555 date_debut = models.DateField(help_text=HELP_TEXT_DATE,
556 verbose_name="Date de début",
557 null=True, blank=True)
558 date_fin = models.DateField(help_text=HELP_TEXT_DATE,
559 verbose_name="Date de fin",
560 null=True, blank=True)
561
562 class Meta:
563 abstract = True
564 ordering = ['type__nom', '-date_fin']
565
566 def __unicode__(self):
567 return u'%s %s (%s)' % (self.montant, self.devise.code, self.type.nom)
568
569 class Remuneration_(RemunerationMixin):
570 """Structure de rémunération (données budgétaires) en situation normale
571 pour un Dossier. Si un Evenement existe, utiliser la structure de
572 rémunération EvenementRemuneration de cet événement.
573 """
574
575 def montant_mois(self):
576 return round(self.montant / 12, 2)
577
578 def taux_devise(self):
579 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
580
581 def montant_euro(self):
582 return round(float(self.montant) / float(self.taux_devise()), 2)
583
584 def montant_euro_mois(self):
585 return round(self.montant_euro() / 12, 2)
586
587 def __unicode__(self):
588 try:
589 devise = self.devise.code
590 except:
591 devise = "???"
592 return "%s %s" % (self.montant, devise)
593
594 class Meta:
595 abstract = True
596 verbose_name = "Rémunération"
597 verbose_name_plural = "Rémunérations"
598
599
600 class Remuneration(Remuneration_):
601 __doc__ = Remuneration_.__doc__
602
603
604 ### CONTRATS
605
606 class Contrat(Metadata):
607 """Document juridique qui encadre la relation de travail d'un Employe
608 pour un Poste particulier. Pour un Dossier (qui documente cette
609 relation de travail) plusieurs contrats peuvent être associés.
610 """
611 dossier = models.ForeignKey('Dossier', db_column='dossier',
612 related_name='+')
613 type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
614 related_name='+',
615 verbose_name="Type de contrat")
616 date_debut = models.DateField(help_text=HELP_TEXT_DATE,
617 verbose_name="Date de début")
618 date_fin = models.DateField(help_text=HELP_TEXT_DATE,
619 verbose_name="Date de fin",
620 null=True, blank=True)
621
622 class Meta:
623 ordering = ['dossier__employe__nom_affichage']
624 verbose_name = "Contrat"
625 verbose_name_plural = "Contrats"
626
627 def __unicode__(self):
628 return u'%s - %s' % (self.dossier, self.id)
629
630 # TODO? class ContratPiece(models.Model):
631
632
633 ### ÉVÉNEMENTS
634
635 class Evenement_(Metadata):
636 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
637 d'un Dossier qui vient altérer des informations normales liées à un Dossier
638 (ex.: la Remuneration).
639
640 Ex.: congé de maternité, maladie...
641
642 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
643 différent et une rémunération en conséquence. On souhaite toutefois
644 conserver le Dossier intact afin d'éviter une re-saisie des données lors
645 du retour à la normale.
646 """
647 dossier = models.ForeignKey('Dossier', db_column='dossier',
648 related_name='+')
649 nom = models.CharField(max_length=255)
650 date_debut = models.DateField(help_text=HELP_TEXT_DATE,
651 verbose_name="Date de début")
652 date_fin = models.DateField(help_text=HELP_TEXT_DATE,
653 verbose_name="Date de fin",
654 null=True, blank=True)
655
656 class Meta:
657 abstract = True
658 ordering = ['nom']
659 verbose_name = "Évènement"
660 verbose_name_plural = "Évènements"
661
662 def __unicode__(self):
663 return u'%s' % (self.nom)
664
665
666 class Evenement(Evenement_):
667 __doc__ = Evenement_.__doc__
668
669
670 class EvenementRemuneration_(RemunerationMixin):
671 """Structure de rémunération liée à un Evenement qui remplace
672 temporairement la Remuneration normale d'un Dossier, pour toute la durée
673 de l'Evenement.
674 """
675 evenement = models.ForeignKey("Evenement", db_column='evenement',
676 related_name='+',
677 verbose_name="Évènement")
678 # TODO : le champ dossier hérité de Remuneration doit être dérivé
679 # de l'Evenement associé
680
681 class Meta:
682 abstract = True
683 ordering = ['evenement', 'type__nom', '-date_fin']
684 verbose_name = "Évènement - rémunération"
685 verbose_name_plural = "Évènements - rémunérations"
686
687
688 class EvenementRemuneration(EvenementRemuneration_):
689 __doc__ = EvenementRemuneration_.__doc__
690
691
692 ### RÉFÉRENCES RH
693
694 class FamilleEmploi(Metadata):
695 """Catégorie utilisée dans la gestion des Postes.
696 Catégorie supérieure à TypePoste.
697 """
698 nom = models.CharField(max_length=255)
699
700 class Meta:
701 ordering = ['nom']
702 verbose_name = "Famille d'emploi"
703 verbose_name_plural = "Familles d'emploi"
704
705 def __unicode__(self):
706 return u'%s' % (self.nom)
707
708 class TypePoste(Metadata):
709 """Catégorie de Poste.
710 """
711 nom = models.CharField(max_length=255)
712 nom_feminin = models.CharField(max_length=255,
713 verbose_name="Nom féminin")
714
715 is_responsable = models.BooleanField(default=False,
716 verbose_name="Poste de responsabilité")
717 famille_emploi = models.ForeignKey('FamilleEmploi',
718 db_column='famille_emploi',
719 related_name='+',
720 verbose_name="Famille d'emploi")
721
722 class Meta:
723 ordering = ['nom']
724 verbose_name = "Type de poste"
725 verbose_name_plural = "Types de poste"
726
727 def __unicode__(self):
728 return u'%s' % (self.nom)
729
730
731 TYPE_PAIEMENT_CHOICES = (
732 ('Régulier', 'Régulier'),
733 ('Ponctuel', 'Ponctuel'),
734 )
735
736 NATURE_REMUNERATION_CHOICES = (
737 ('Accessoire', 'Accessoire'),
738 ('Charges', 'Charges'),
739 ('Indemnité', 'Indemnité'),
740 ('RAS', 'Rémunération autre source'),
741 ('Traitement', 'Traitement'),
742 )
743
744 class TypeRemuneration(Metadata):
745 """Catégorie de Remuneration.
746 """
747 nom = models.CharField(max_length=255)
748 type_paiement = models.CharField(max_length=30,
749 choices=TYPE_PAIEMENT_CHOICES,
750 verbose_name="Type de paiement")
751 nature_remuneration = models.CharField(max_length=30,
752 choices=NATURE_REMUNERATION_CHOICES,
753 verbose_name="Nature de la rémunération")
754
755 class Meta:
756 ordering = ['nom']
757 verbose_name = "Type de rémunération"
758 verbose_name_plural = "Types de rémunération"
759
760 def __unicode__(self):
761 return u'%s' % (self.nom)
762
763 class TypeRevalorisation(Metadata):
764 """Justification du changement de la Remuneration.
765 (Actuellement utilisé dans aucun traitement informatique.)
766 """
767 nom = models.CharField(max_length=255)
768
769 class Meta:
770 ordering = ['nom']
771 verbose_name = "Type de revalorisation"
772 verbose_name_plural = "Types de revalorisation"
773
774 def __unicode__(self):
775 return u'%s' % (self.nom)
776
777 class Service(Metadata):
778 """Unité administrative où les Postes sont rattachés.
779 """
780 nom = models.CharField(max_length=255)
781
782 class Meta:
783 ordering = ['nom']
784 verbose_name = "Service"
785 verbose_name_plural = "Services"
786
787 def __unicode__(self):
788 return u'%s' % (self.nom)
789
790
791 TYPE_ORGANISME_CHOICES = (
792 ('MAD', 'Mise à disposition'),
793 ('DET', 'Détachement'),
794 )
795
796 class OrganismeBstg(Metadata):
797 """Organisation d'où provient un Employe mis à disposition (MAD) de
798 ou détaché (DET) à l'AUF à titre gratuit.
799
800 (BSTG = bien et service à titre gratuit.)
801 """
802 nom = models.CharField(max_length=255)
803 type = models.CharField(max_length=10, choices=TYPE_ORGANISME_CHOICES)
804 pays = models.ForeignKey(ref.Pays, to_field='code',
805 db_column='pays',
806 related_name='organismes_bstg',
807 null=True, blank=True)
808
809 class Meta:
810 ordering = ['type', 'nom']
811 verbose_name = "Organisme BSTG"
812 verbose_name_plural = "Organismes BSTG"
813
814 def __unicode__(self):
815 return u'%s (%s)' % (self.nom, self.get_type_display())
816
817 class Statut(Metadata):
818 """Statut de l'Employe dans le cadre d'un Dossier particulier.
819 """
820 # Identification
821 code = models.CharField(max_length=25, unique=True)
822 nom = models.CharField(max_length=255)
823
824 class Meta:
825 ordering = ['code']
826 verbose_name = "Statut d'employé"
827 verbose_name_plural = "Statuts d'employé"
828
829 def __unicode__(self):
830 return u'%s : %s' % (self.code, self.nom)
831
832
833 TYPE_CLASSEMENT_CHOICES = (
834 ('S', 'S -Soutien'),
835 ('T', 'T - Technicien'),
836 ('P', 'P - Professionel'),
837 ('C', 'C - Cadre'),
838 ('D', 'D - Direction'),
839 ('SO', 'SO - Sans objet [expatriés]'),
840 ('HG', 'HG - Hors grille [direction]'),
841 )
842
843
844 class Classement_(Metadata):
845 """Éléments de classement de la
846 "Grille générique de classement hiérarchique".
847
848 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
849 classement dans la grille. Le classement donne le coefficient utilisé dans:
850
851 salaire de base = coefficient * valeur du point de l'Implantation du Poste
852 """
853 # Identification
854 type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
855 echelon = models.IntegerField(verbose_name="Échelon")
856 degre = models.IntegerField(verbose_name="Degré")
857 coefficient = models.FloatField(default=0, verbose_name="Coéfficient")
858 # Méta
859 # annee # au lieu de date_debut et date_fin
860 commentaire = models.TextField(null=True, blank=True)
861
862 class Meta:
863 abstract = True
864 ordering = ['type','echelon','degre','coefficient']
865 verbose_name = "Classement"
866 verbose_name_plural = "Classements"
867
868 def __unicode__(self):
869 return u'%s.%s.%s (%s)' % (self.type, self.echelon, self.degre,
870 self.coefficient)
871
872 class Classement(Classement_):
873 __doc__ = Classement_.__doc__
874
875
876 class TauxChange_(Metadata):
877 """Taux de change de la devise vers l'euro (EUR)
878 pour chaque année budgétaire.
879 """
880 # Identification
881 devise = models.ForeignKey('Devise', db_column='devise',
882 related_name='+')
883 annee = models.IntegerField(verbose_name="Année")
884 taux = models.FloatField(verbose_name="Taux vers l'euro")
885
886 class Meta:
887 abstract = True
888 ordering = ['-annee', 'devise__code']
889 verbose_name = "Taux de change"
890 verbose_name_plural = "Taux de change"
891
892 def __unicode__(self):
893 return u'%s : %s € (%s)' % (self.devise, self.taux, self.annee)
894
895
896 class TauxChange(TauxChange_):
897 __doc__ = TauxChange_.__doc__
898
899
900 class ValeurPoint_(Metadata):
901 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
902 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
903 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
904
905 salaire de base = coefficient * valeur du point de l'Implantation du Poste
906 """
907 valeur = models.FloatField()
908 devise = models.ForeignKey('Devise', db_column='devise',
909 related_name='+', default=5)
910 implantation = models.ForeignKey(ref.Implantation,
911 db_column='implantation',
912 related_name='%(app_label)s_valeur_point')
913 # Méta
914 annee = models.IntegerField()
915
916 class Meta:
917 abstract = True
918 ordering = ['annee']
919 verbose_name = "Valeur du point"
920 verbose_name_plural = "Valeurs du point"
921
922 def __unicode__(self):
923 return u'%s %s (%s)' % (self.valeur, self.devise, self.annee)
924
925
926 class ValeurPoint(ValeurPoint_):
927 __doc__ = ValeurPoint_.__doc__
928
929
930 class Devise(Metadata):
931 """Devise monétaire.
932 """
933 code = models.CharField(max_length=10, unique=True)
934 nom = models.CharField(max_length=255)
935
936 class Meta:
937 ordering = ['code']
938 verbose_name = "Devise"
939 verbose_name_plural = "Devises"
940
941 def __unicode__(self):
942 return u'%s - %s' % (self.code, self.nom)
943
944 class TypeContrat(Metadata):
945 """Type de contrat.
946 """
947 nom = models.CharField(max_length=255)
948 nom_long = models.CharField(max_length=255)
949
950 class Meta:
951 ordering = ['nom']
952 verbose_name = "Type de contrat"
953 verbose_name_plural = "Types de contrat"
954
955 def __unicode__(self):
956 return u'%s' % (self.nom)
957
958
959 ### AUTRES
960
961 class ResponsableImplantation(Metadata):
962 """Le responsable d'une implantation.
963 Anciennement géré sur le Dossier du responsable.
964 """
965 employe = models.ForeignKey('Employe', db_column='employe',
966 related_name='+',
967 null=True, blank=True)
968 implantation = models.ForeignKey(ref.Implantation,
969 db_column='implantation', related_name='+',
970 unique=True)
971
972 def __unicode__(self):
973 return u'%s : %s' % (self.implantation, self.employe)
974
975 class Meta:
976 ordering = ['implantation__nom']
977 verbose_name = "Responsable d'implantation"
978 verbose_name_plural = "Responsables d'implantation"