1 # -=- encoding: utf-8 -=-
3 from django
.core
.files
.storage
import FileSystemStorage
4 from django
.db
import models
5 from django
.conf
import settings
6 from auf
.django
.metadata
.models
import AUFMetadata
7 from auf
.django
.metadata
.managers
import NoDeleteManager
8 import datamaster_modeles
.models
as ref
9 from validators
import validate_date_passee
12 HELP_TEXT_DATE
= "format: aaaa-mm-jj"
13 REGIME_TRAVAIL_DEFAULT
= 100.00
14 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
= 35.00
18 storage_prive
= FileSystemStorage(settings
.PRIVE_MEDIA_ROOT
,
19 base_url
=settings
.PRIVE_MEDIA_URL
)
21 def poste_piece_dispatch(instance
, filename
):
22 path
= "poste/%s/%s" % (instance
.poste_id
, filename
)
25 def dossier_piece_dispatch(instance
, filename
):
26 path
= "dossier/%s/%s" % (instance
.dossier_id
, filename
)
30 class Commentaire(AUFMetadata
):
31 texte
= models
.TextField()
32 owner
= models
.ForeignKey('auth.User', db_column
='owner', related_name
='+')
36 ordering
= ['-date_creation']
38 def __unicode__(self
):
39 return u
'%s' % (self
.texte
)
44 POSTE_APPEL_CHOICES
= (
45 ('interne', 'Interne'),
46 ('externe', 'Externe'),
49 class PosteManager(NoDeleteManager
):
50 def get_query_set(self
):
51 return super(PosteManager
, self
).get_query_set().select_related('implantation')
53 class Poste_(AUFMetadata
):
54 """Un Poste est un emploi (job) à combler dans une implantation.
55 Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
56 Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
59 objects
= PosteManager()
62 nom
= models
.CharField(max_length
=255,
63 verbose_name
= u
"Titre du poste", )
64 nom_feminin
= models
.CharField(max_length
=255,
65 verbose_name
= u
"Titre du poste (au féminin)",
67 implantation
= models
.ForeignKey(ref
.Implantation
,
68 db_column
='implantation', related_name
='+')
69 type_poste
= models
.ForeignKey('TypePoste', db_column
='type_poste',
72 service
= models
.ForeignKey('Service', db_column
='service', null
=True,
74 verbose_name
= u
"Direction/Service/Pôle support",
75 default
=1) # default = Rectorat
76 responsable
= models
.ForeignKey('Poste', db_column
='responsable',
77 related_name
='+', null
=True,
78 verbose_name
= u
"Poste du responsable",
79 default
=149) # default = Recteur
82 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
83 default
=REGIME_TRAVAIL_DEFAULT
, null
=True,
84 verbose_name
= u
"Temps de travail",
85 help_text
="% du temps complet")
86 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
87 decimal_places
=2, null
=True,
88 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
89 verbose_name
= u
"Nb. heures par semaine")
92 local
= models
.NullBooleanField(verbose_name
= u
"Local", default
=True,
93 null
=True, blank
=True)
94 expatrie
= models
.NullBooleanField(verbose_name
= u
"Expatrié", default
=False,
95 null
=True, blank
=True)
96 mise_a_disposition
= models
.NullBooleanField(
97 verbose_name
= u
"Mise à disposition",
98 null
=True, default
=False)
99 appel
= models
.CharField(max_length
=10, null
=True,
100 verbose_name
= u
"Appel à candidature",
101 choices
=POSTE_APPEL_CHOICES
,
105 classement_min
= models
.ForeignKey('Classement',
106 db_column
='classement_min', related_name
='+',
107 null
=True, blank
=True)
108 classement_max
= models
.ForeignKey('Classement',
109 db_column
='classement_max', related_name
='+',
110 null
=True, blank
=True)
111 valeur_point_min
= models
.ForeignKey('ValeurPoint',
112 db_column
='valeur_point_min', related_name
='+',
113 null
=True, blank
=True)
114 valeur_point_max
= models
.ForeignKey('ValeurPoint',
115 db_column
='valeur_point_max', related_name
='+',
116 null
=True, blank
=True)
117 devise_min
= models
.ForeignKey('Devise', db_column
='devise_min', null
=True,
118 related_name
='+', default
=5)
119 devise_max
= models
.ForeignKey('Devise', db_column
='devise_max', null
=True,
120 related_name
='+', default
=5)
121 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
122 null
=True, default
=0)
123 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
124 null
=True, default
=0)
125 indemn_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
126 null
=True, default
=0)
127 indemn_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
128 null
=True, default
=0)
129 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
130 null
=True, default
=0)
131 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
132 null
=True, default
=0)
134 # Comparatifs de rémunération
135 devise_comparaison
= models
.ForeignKey('Devise', null
=True,
136 db_column
='devise_comparaison',
139 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
140 null
=True, blank
=True)
141 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
142 null
=True, blank
=True)
143 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
144 null
=True, blank
=True)
145 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
146 null
=True, blank
=True)
147 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
148 null
=True, blank
=True)
149 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
150 null
=True, blank
=True)
151 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
152 null
=True, blank
=True)
153 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
154 null
=True, blank
=True)
155 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
156 null
=True, blank
=True)
157 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
158 null
=True, blank
=True)
161 justification
= models
.TextField(null
=True, blank
=True)
164 date_validation
= models
.DateTimeField(null
=True, blank
=True) # de dae
165 date_debut
= models
.DateField(verbose_name
=u
"Date de début",
166 help_text
=HELP_TEXT_DATE
,
167 null
=True, blank
=True)
168 date_fin
= models
.DateField(verbose_name
=u
"Date de fin",
169 help_text
=HELP_TEXT_DATE
,
170 null
=True, blank
=True)
174 ordering
= ['implantation__nom', 'nom']
175 verbose_name
= u
"Poste"
176 verbose_name_plural
= u
"Postes"
178 def __unicode__(self
):
179 representation
= u
'%s - %s [%s]' % (self
.implantation
, self
.nom
,
182 representation
= representation
+ u
' (vacant)'
183 return representation
186 # TODO : si existe un dossier actif pour ce poste, return False
187 # self.dossier_set.all() fonctionne pas
192 __doc__
= Poste_
.__doc__
196 __doc__
= Poste_
.__doc__
199 POSTE_FINANCEMENT_CHOICES
= (
200 ('A', 'A - Frais de personnel'),
201 ('B', 'B - Projet(s)-Titre(s)'),
206 class PosteFinancement_(models
.Model
):
207 """Pour un Poste, structure d'informations décrivant comment on prévoit
210 poste
= models
.ForeignKey('Poste', db_column
='poste',
211 related_name
='%(app_label)s_financements')
212 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
213 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
214 help_text
="ex.: 33.33 % (décimale avec point)")
215 commentaire
= models
.TextField(
216 help_text
="Spécifiez la source de financement.")
222 def __unicode__(self
):
223 return u
'%s : %s %' % (self
.type, self
.pourcentage
)
226 class PosteFinancement(PosteFinancement_
):
227 __doc__
= PosteFinancement_
.__doc__
230 class PostePiece(models
.Model
):
231 """Documents relatifs au Poste.
232 Ex.: Description de poste
234 poste
= models
.ForeignKey('Poste', db_column
='poste',
235 related_name
='pieces')
236 nom
= models
.CharField(verbose_name
= u
"Nom", max_length
=255)
237 fichier
= models
.FileField(verbose_name
= u
"Fichier",
238 upload_to
=poste_piece_dispatch
,
239 storage
=storage_prive
)
244 def __unicode__(self
):
245 return u
'%s' % (self
.nom
)
247 class PosteComparaison(models
.Model
):
249 De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
251 poste
= models
.ForeignKey('Poste', related_name
='comparaisons_internes')
252 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True, related_name
="+")
253 nom
= models
.CharField(verbose_name
= u
"Poste", max_length
=255, null
=True, blank
=True)
254 montant
= models
.IntegerField(null
=True)
255 devise
= models
.ForeignKey("Devise", default
=5, related_name
='+', null
=True, blank
=True)
257 def taux_devise(self
):
258 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
259 if len(liste_taux
) == 0:
260 raise Exception(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
262 return liste_taux
[0].taux
264 def montant_euros(self
):
265 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
268 class PosteCommentaire(Commentaire
):
269 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
278 SITUATION_CHOICES
= (
279 ('C', 'Célibataire'),
284 class Employe(AUFMetadata
):
285 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
286 Dossiers qu'il occupe ou a occupé de Postes.
288 Cette classe aurait pu avantageusement s'appeler Personne car la notion
289 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
292 nom
= models
.CharField(max_length
=255)
293 prenom
= models
.CharField(max_length
=255, verbose_name
= u
"Prénom")
294 nom_affichage
= models
.CharField(max_length
=255,
295 verbose_name
= u
"Nom d'affichage",
296 null
=True, blank
=True)
297 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
298 db_column
='nationalite',
299 related_name
='employes_nationalite',
300 verbose_name
= u
"Nationalité")
301 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
302 verbose_name
= u
"Date de naissance",
303 validators
=[validate_date_passee
],
304 null
=True, blank
=True)
305 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
308 situation_famille
= models
.CharField(max_length
=1,
309 choices
=SITUATION_CHOICES
,
310 verbose_name
= u
"Situation familiale",
311 null
=True, blank
=True)
312 date_entree
= models
.DateField(verbose_name
= u
"Date d'entrée à l'AUF",
313 help_text
=HELP_TEXT_DATE
,
314 null
=True, blank
=True)
317 tel_domicile
= models
.CharField(max_length
=255,
318 verbose_name
= u
"Tél. domicile",
319 null
=True, blank
=True)
320 tel_cellulaire
= models
.CharField(max_length
=255,
321 verbose_name
= u
"Tél. cellulaire",
322 null
=True, blank
=True)
323 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
324 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
325 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
326 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
327 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
328 related_name
='employes',
329 null
=True, blank
=True)
332 ordering
= ['nom_affichage','nom','prenom']
333 verbose_name
= u
"Employé"
334 verbose_name_plural
= u
"Employés"
336 def __unicode__(self
):
337 return u
'%s' % (self
.get_nom())
340 nom_affichage
= self
.nom_affichage
341 if not nom_affichage
:
342 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
347 if self
.genre
.upper() == u
'M':
349 elif self
.genre
.upper() == u
'F':
353 class EmployePiece(models
.Model
):
354 """Documents relatifs à un employé.
357 employe
= models
.ForeignKey('Employe', db_column
='employe',
359 nom
= models
.CharField(verbose_name
= u
"Nom", max_length
=255)
360 fichier
= models
.FileField(verbose_name
= u
"Fichier",
361 upload_to
=dossier_piece_dispatch
,
362 storage
=storage_prive
)
367 def __unicode__(self
):
368 return u
'%s' % (self
.nom
)
370 class EmployeCommentaire(Commentaire
):
371 employe
= models
.ForeignKey('Employe', db_column
='employe',
375 LIEN_PARENTE_CHOICES
= (
376 ('Conjoint', 'Conjoint'),
377 ('Conjointe', 'Conjointe'),
382 class AyantDroit(AUFMetadata
):
383 """Personne en relation avec un Employe.
386 nom
= models
.CharField(max_length
=255)
387 prenom
= models
.CharField(max_length
=255,
388 verbose_name
= u
"Prénom",)
389 nom_affichage
= models
.CharField(max_length
=255,
390 verbose_name
= u
"Nom d'affichage",
391 null
=True, blank
=True)
392 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
393 db_column
='nationalite',
394 related_name
='ayantdroits_nationalite',
395 verbose_name
= u
"Nationalité")
396 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
397 verbose_name
= u
"Date de naissance",
398 validators
=[validate_date_passee
],
399 null
=True, blank
=True)
400 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
403 employe
= models
.ForeignKey('Employe', db_column
='employe',
404 related_name
='ayantdroits',
405 verbose_name
= u
"Employé")
406 lien_parente
= models
.CharField(max_length
=10,
407 choices
=LIEN_PARENTE_CHOICES
,
408 verbose_name
= u
"Lien de parenté",
409 null
=True, blank
=True)
412 ordering
= ['nom_affichage']
413 verbose_name
= u
"Ayant droit"
414 verbose_name_plural
= u
"Ayants droit"
416 def __unicode__(self
):
417 return u
'%s' % (self
.get_nom())
420 nom_affichage
= self
.nom_affichage
421 if not nom_affichage
:
422 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
425 class AyantDroitCommentaire(Commentaire
):
426 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
432 STATUT_RESIDENCE_CHOICES
= (
434 ('expat', 'Expatrié'),
437 COMPTE_COMPTA_CHOICES
= (
443 class Dossier_(AUFMetadata
):
444 """Le Dossier regroupe les informations relatives à l'occupation
445 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
448 Plusieurs Contrats peuvent être associés au Dossier.
449 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
450 lequel aucun Dossier n'existe est un poste vacant.
453 employe
= models
.ForeignKey('Employe', db_column
='employe',
454 related_name
='dossiers',
455 verbose_name
=u
"Employé")
456 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
457 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3,
459 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
460 db_column
='organisme_bstg',
462 verbose_name
= u
"Organisme",
463 help_text
="Si détaché (DET) ou \
464 mis à disposition (MAD), \
465 préciser l'organisme.",
466 null
=True, blank
=True)
469 remplacement
= models
.BooleanField(default
=False)
470 statut_residence
= models
.CharField(max_length
=10, default
='local',
471 verbose_name
= u
"Statut", null
=True,
472 choices
=STATUT_RESIDENCE_CHOICES
)
475 classement
= models
.ForeignKey('Classement', db_column
='classement',
477 null
=True, blank
=True)
478 regime_travail
= models
.DecimalField(max_digits
=12, null
=True,
480 default
=REGIME_TRAVAIL_DEFAULT
,
481 verbose_name
= u
"Régime de travail",
482 help_text
="% du temps complet")
483 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
484 decimal_places
=2, null
=True,
485 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
486 verbose_name
= u
"Nb. heures par semaine")
488 # Occupation du Poste par cet Employe (anciennement "mandat")
489 date_debut
= models
.DateField(verbose_name
= u
"Date de début d'occupation \
491 help_text
=HELP_TEXT_DATE
)
492 date_fin
= models
.DateField(verbose_name
= u
"Date de fin d'occupation \
494 help_text
=HELP_TEXT_DATE
,
495 null
=True, blank
=True)
502 ordering
= ['employe__nom', ]
503 verbose_name
= u
"Dossier"
504 verbose_name_plural
= "Dossiers"
506 def __unicode__(self
):
507 poste
= self
.poste
.nom
508 if self
.employe
.genre
== 'F':
509 poste
= self
.poste
.nom_feminin
510 return u
'%s - %s' % (self
.employe
, poste
)
513 class Dossier(Dossier_
):
514 __doc__
= Dossier_
.__doc__
517 class DossierPiece(models
.Model
):
518 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
519 Ex.: Lettre de motivation.
521 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
523 nom
= models
.CharField(verbose_name
= u
"Nom", max_length
=255)
524 fichier
= models
.FileField(verbose_name
= u
"Fichier",
525 upload_to
=dossier_piece_dispatch
,
526 storage
=storage_prive
)
531 def __unicode__(self
):
532 return u
'%s' % (self
.nom
)
534 class DossierCommentaire(Commentaire
):
535 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
538 class DossierComparaison(models
.Model
):
540 Photo d'une comparaison salariale au moment de l'embauche.
542 dossier
= models
.ForeignKey('Dossier', related_name
='comparaisons')
543 implantation
= models
.ForeignKey(ref
.Implantation
, related_name
="+", null
=True, blank
=True)
544 poste
= models
.CharField(max_length
=255, null
=True, blank
=True)
545 personne
= models
.CharField(max_length
=255, null
=True, blank
=True)
546 montant
= models
.IntegerField(null
=True)
547 devise
= models
.ForeignKey('Devise', default
=5, related_name
='+', null
=True, blank
=True)
549 def taux_devise(self
):
550 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.dossier
.poste
.implantation
)
551 if len(liste_taux
) == 0:
552 raise Exception(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.dossier
.poste
.implantation
))
554 return liste_taux
[0].taux
556 def montant_euros(self
):
557 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
562 class RemunerationMixin(AUFMetadata
):
564 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
565 related_name
='%(app_label)s_%(class)s_remunerations')
566 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
568 verbose_name
= u
"Type de rémunération")
569 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
570 db_column
='type_revalorisation',
572 verbose_name
= u
"Type de revalorisation",
573 null
=True, blank
=True)
574 montant
= models
.FloatField(null
=True, blank
=True,
576 # Annuel (12 mois, 52 semaines, 364 jours?)
577 devise
= models
.ForeignKey('Devise', to_field
='id',
578 db_column
='devise', related_name
='+',
580 # commentaire = precision
581 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
582 # date_debut = anciennement date_effectif
583 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
584 verbose_name
= u
"Date de début",
585 null
=True, blank
=True)
586 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
587 verbose_name
= u
"Date de fin",
588 null
=True, blank
=True)
592 ordering
= ['type__nom', '-date_fin']
594 def __unicode__(self
):
595 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
597 class Remuneration_(RemunerationMixin
):
598 """Structure de rémunération (données budgétaires) en situation normale
599 pour un Dossier. Si un Evenement existe, utiliser la structure de
600 rémunération EvenementRemuneration de cet événement.
603 def montant_mois(self
):
604 return round(self
.montant
/ 12, 2)
606 def taux_devise(self
):
607 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
609 def montant_euro(self
):
610 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
612 def montant_euro_mois(self
):
613 return round(self
.montant_euro() / 12, 2)
615 def __unicode__(self
):
617 devise
= self
.devise
.code
620 return "%s %s" % (self
.montant
, devise
)
624 verbose_name
= u
"Rémunération"
625 verbose_name_plural
= u
"Rémunérations"
628 class Remuneration(Remuneration_
):
629 __doc__
= Remuneration_
.__doc__
634 class ContratManager(NoDeleteManager
):
635 def get_query_set(self
):
636 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
639 class Contrat(AUFMetadata
):
640 """Document juridique qui encadre la relation de travail d'un Employe
641 pour un Poste particulier. Pour un Dossier (qui documente cette
642 relation de travail) plusieurs contrats peuvent être associés.
645 objects
= ContratManager()
647 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
649 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
651 verbose_name
= u
"Type de contrat")
652 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
653 verbose_name
= u
"Date de début")
654 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
655 verbose_name
= u
"Date de fin",
656 null
=True, blank
=True)
659 ordering
= ['dossier__employe__nom_affichage']
660 verbose_name
= u
"Contrat"
661 verbose_name_plural
= u
"Contrats"
663 def __unicode__(self
):
664 return u
'%s - %s' % (self
.dossier
, self
.id)
666 # TODO? class ContratPiece(models.Model):
671 class Evenement_(AUFMetadata
):
672 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
673 d'un Dossier qui vient altérer des informations normales liées à un Dossier
674 (ex.: la Remuneration).
676 Ex.: congé de maternité, maladie...
678 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
679 différent et une rémunération en conséquence. On souhaite toutefois
680 conserver le Dossier intact afin d'éviter une re-saisie des données lors
681 du retour à la normale.
683 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
685 nom
= models
.CharField(max_length
=255)
686 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
687 verbose_name
= u
"Date de début")
688 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
689 verbose_name
= u
"Date de fin",
690 null
=True, blank
=True)
695 verbose_name
= u
"Évènement"
696 verbose_name_plural
= u
"Évènements"
698 def __unicode__(self
):
699 return u
'%s' % (self
.nom
)
702 class Evenement(Evenement_
):
703 __doc__
= Evenement_
.__doc__
706 class EvenementRemuneration_(RemunerationMixin
):
707 """Structure de rémunération liée à un Evenement qui remplace
708 temporairement la Remuneration normale d'un Dossier, pour toute la durée
711 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
713 verbose_name
= u
"Évènement")
714 # TODO : le champ dossier hérité de Remuneration doit être dérivé
715 # de l'Evenement associé
719 ordering
= ['evenement', 'type__nom', '-date_fin']
720 verbose_name
= u
"Évènement - rémunération"
721 verbose_name_plural
= u
"Évènements - rémunérations"
724 class EvenementRemuneration(EvenementRemuneration_
):
725 __doc__
= EvenementRemuneration_
.__doc__
731 class EvenementRemuneration(EvenementRemuneration_
):
732 __doc__
= EvenementRemuneration_
.__doc__
737 class FamilleEmploi(AUFMetadata
):
738 """Catégorie utilisée dans la gestion des Postes.
739 Catégorie supérieure à TypePoste.
741 nom
= models
.CharField(max_length
=255)
745 verbose_name
= u
"Famille d'emploi"
746 verbose_name_plural
= u
"Familles d'emploi"
748 def __unicode__(self
):
749 return u
'%s' % (self
.nom
)
751 class TypePoste(AUFMetadata
):
752 """Catégorie de Poste.
754 nom
= models
.CharField(max_length
=255)
755 nom_feminin
= models
.CharField(max_length
=255,
756 verbose_name
= u
"Nom féminin")
758 is_responsable
= models
.BooleanField(default
=False,
759 verbose_name
= u
"Poste de responsabilité")
760 famille_emploi
= models
.ForeignKey('FamilleEmploi',
761 db_column
='famille_emploi',
763 verbose_name
= u
"Famille d'emploi")
767 verbose_name
= u
"Type de poste"
768 verbose_name_plural
= u
"Types de poste"
770 def __unicode__(self
):
771 return u
'%s' % (self
.nom
)
774 TYPE_PAIEMENT_CHOICES
= (
775 ('Régulier', 'Régulier'),
776 ('Ponctuel', 'Ponctuel'),
779 NATURE_REMUNERATION_CHOICES
= (
780 ('Accessoire', 'Accessoire'),
781 ('Charges', 'Charges'),
782 ('Indemnité', 'Indemnité'),
783 ('RAS', 'Rémunération autre source'),
784 ('Traitement', 'Traitement'),
787 class TypeRemuneration(AUFMetadata
):
788 """Catégorie de Remuneration.
790 nom
= models
.CharField(max_length
=255)
791 type_paiement
= models
.CharField(max_length
=30,
792 choices
=TYPE_PAIEMENT_CHOICES
,
793 verbose_name
= u
"Type de paiement")
794 nature_remuneration
= models
.CharField(max_length
=30,
795 choices
=NATURE_REMUNERATION_CHOICES
,
796 verbose_name
= u
"Nature de la rémunération")
800 verbose_name
= u
"Type de rémunération"
801 verbose_name_plural
= u
"Types de rémunération"
803 def __unicode__(self
):
804 return u
'%s' % (self
.nom
)
806 class TypeRevalorisation(AUFMetadata
):
807 """Justification du changement de la Remuneration.
808 (Actuellement utilisé dans aucun traitement informatique.)
810 nom
= models
.CharField(max_length
=255)
814 verbose_name
= u
"Type de revalorisation"
815 verbose_name_plural
= u
"Types de revalorisation"
817 def __unicode__(self
):
818 return u
'%s' % (self
.nom
)
820 class Service(AUFMetadata
):
821 """Unité administrative où les Postes sont rattachés.
823 nom
= models
.CharField(max_length
=255)
827 verbose_name
= u
"Service"
828 verbose_name_plural
= u
"Services"
830 def __unicode__(self
):
831 return u
'%s' % (self
.nom
)
834 TYPE_ORGANISME_CHOICES
= (
835 ('MAD', 'Mise à disposition'),
836 ('DET', 'Détachement'),
839 class OrganismeBstg(AUFMetadata
):
840 """Organisation d'où provient un Employe mis à disposition (MAD) de
841 ou détaché (DET) à l'AUF à titre gratuit.
843 (BSTG = bien et service à titre gratuit.)
845 nom
= models
.CharField(max_length
=255)
846 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
847 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
849 related_name
='organismes_bstg',
850 null
=True, blank
=True)
853 ordering
= ['type', 'nom']
854 verbose_name
= u
"Organisme BSTG"
855 verbose_name_plural
= u
"Organismes BSTG"
857 def __unicode__(self
):
858 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
860 class Statut(AUFMetadata
):
861 """Statut de l'Employe dans le cadre d'un Dossier particulier.
864 code
= models
.CharField(max_length
=25, unique
=True)
865 nom
= models
.CharField(max_length
=255)
869 verbose_name
= u
"Statut d'employé"
870 verbose_name_plural
= u
"Statuts d'employé"
872 def __unicode__(self
):
873 return u
'%s : %s' % (self
.code
, self
.nom
)
876 TYPE_CLASSEMENT_CHOICES
= (
878 ('T', 'T - Technicien'),
879 ('P', 'P - Professionel'),
881 ('D', 'D - Direction'),
882 ('SO', 'SO - Sans objet [expatriés]'),
883 ('HG', 'HG - Hors grille [direction]'),
887 class Classement_(AUFMetadata
):
888 """Éléments de classement de la
889 "Grille générique de classement hiérarchique".
891 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
892 classement dans la grille. Le classement donne le coefficient utilisé dans:
894 salaire de base = coefficient * valeur du point de l'Implantation du Poste
897 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
898 echelon
= models
.IntegerField(verbose_name
= u
"Échelon")
899 degre
= models
.IntegerField(verbose_name
= u
"Degré")
900 coefficient
= models
.FloatField(default
=0, verbose_name
= u
"Coéfficient",
903 # annee # au lieu de date_debut et date_fin
904 commentaire
= models
.TextField(null
=True, blank
=True)
908 ordering
= ['type','echelon','degre','coefficient']
909 verbose_name
= u
"Classement"
910 verbose_name_plural
= u
"Classements"
912 def __unicode__(self
):
913 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
916 class Classement(Classement_
):
917 __doc__
= Classement_
.__doc__
920 class TauxChange_(AUFMetadata
):
921 """Taux de change de la devise vers l'euro (EUR)
922 pour chaque année budgétaire.
925 devise
= models
.ForeignKey('Devise', db_column
='devise',
927 annee
= models
.IntegerField(verbose_name
= u
"Année")
928 taux
= models
.FloatField(verbose_name
= u
"Taux vers l'euro")
932 ordering
= ['-annee', 'devise__code']
933 verbose_name
= u
"Taux de change"
934 verbose_name_plural
= u
"Taux de change"
936 def __unicode__(self
):
937 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
940 class TauxChange(TauxChange_
):
941 __doc__
= TauxChange_
.__doc__
943 class ValeurPointManager(NoDeleteManager
):
944 def get_query_set(self
):
945 return super(ValeurPointManager
, self
).get_query_set().select_related('devise', 'implantation')
948 class ValeurPoint_(AUFMetadata
):
949 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
950 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
951 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
953 salaire de base = coefficient * valeur du point de l'Implantation du Poste
956 objects
= ValeurPointManager()
958 valeur
= models
.FloatField(null
=True)
959 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
960 related_name
='+', default
=5)
961 implantation
= models
.ForeignKey(ref
.Implantation
,
962 db_column
='implantation',
963 related_name
='%(app_label)s_valeur_point')
965 annee
= models
.IntegerField()
968 ordering
= ['-annee', 'implantation__nom']
970 verbose_name
= u
"Valeur du point"
971 verbose_name_plural
= u
"Valeurs du point"
973 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
974 def get_tauxchange_courant(self
):
976 Recherche le taux courant associé à la valeur d'un point.
977 Tous les taux de l'année courante sont chargés, pour optimiser un
978 affichage en liste. (On pourrait probablement améliorer le manager pour
979 lui greffer le taux courant sous forme de JOIN)
981 for tauxchange
in self
.tauxchange
:
982 if tauxchange
.implantation_id
== self
.implantation_id
:
986 def __unicode__(self
):
987 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
990 class ValeurPoint(ValeurPoint_
):
991 __doc__
= ValeurPoint_
.__doc__
994 class Devise(AUFMetadata
):
997 code
= models
.CharField(max_length
=10, unique
=True)
998 nom
= models
.CharField(max_length
=255)
1002 verbose_name
= u
"Devise"
1003 verbose_name_plural
= u
"Devises"
1005 def __unicode__(self
):
1006 return u
'%s - %s' % (self
.code
, self
.nom
)
1008 class TypeContrat(AUFMetadata
):
1011 nom
= models
.CharField(max_length
=255)
1012 nom_long
= models
.CharField(max_length
=255)
1016 verbose_name
= u
"Type de contrat"
1017 verbose_name_plural
= u
"Types de contrat"
1019 def __unicode__(self
):
1020 return u
'%s' % (self
.nom
)
1025 class ResponsableImplantation(AUFMetadata
):
1026 """Le responsable d'une implantation.
1027 Anciennement géré sur le Dossier du responsable.
1029 employe
= models
.ForeignKey('Employe', db_column
='employe',
1031 null
=True, blank
=True)
1032 implantation
= models
.ForeignKey(ref
.Implantation
,
1033 db_column
='implantation', related_name
='+',
1036 def __unicode__(self
):
1037 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1040 ordering
= ['implantation__nom']
1041 verbose_name
= u
"Responsable d'implantation"
1042 verbose_name_plural
= u
"Responsables d'implantation"