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
="Titre du poste", )
64 nom_feminin
= models
.CharField(max_length
=255,
65 verbose_name
="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
="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
="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
="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
="Nb. heures par semaine")
92 local
= models
.NullBooleanField(verbose_name
="Local", default
=True,
93 null
=True, blank
=True)
94 expatrie
= models
.NullBooleanField(verbose_name
="Expatrié", default
=False,
95 null
=True, blank
=True)
96 mise_a_disposition
= models
.NullBooleanField(
97 verbose_name
="Mise à disposition",
98 null
=True, default
=False)
99 appel
= models
.CharField(max_length
=10, null
=True,
100 verbose_name
="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
="Date de début", null
=True,
166 help_text
=HELP_TEXT_DATE
)
167 date_fin
= models
.DateField(verbose_name
="Date de fin",
168 help_text
=HELP_TEXT_DATE
,
169 null
=True, blank
=True)
173 ordering
= ['implantation__nom', 'nom']
174 verbose_name
= "Poste"
175 verbose_name_plural
= "Postes"
177 def __unicode__(self
):
178 representation
= u
'%s - %s [%s]' % (self
.implantation
, self
.nom
,
181 representation
= representation
+ u
' (vacant)'
182 return representation
185 # TODO : si existe un dossier actif pour ce poste, return False
186 # self.dossier_set.all() fonctionne pas
191 __doc__
= Poste_
.__doc__
195 __doc__
= Poste_
.__doc__
198 POSTE_FINANCEMENT_CHOICES
= (
199 ('A', 'A - Frais de personnel'),
200 ('B', 'B - Projet(s)-Titre(s)'),
205 class PosteFinancement_(models
.Model
):
206 """Pour un Poste, structure d'informations décrivant comment on prévoit
209 poste
= models
.ForeignKey('Poste', db_column
='poste',
210 related_name
='%(app_label)s_financements')
211 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
212 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
213 help_text
="ex.: 33.33 % (décimale avec point)")
214 commentaire
= models
.TextField(
215 help_text
="Spécifiez la source de financement.")
221 def __unicode__(self
):
222 return u
'%s : %s %' % (self
.type, self
.pourcentage
)
225 class PosteFinancement(PosteFinancement_
):
226 __doc__
= PosteFinancement_
.__doc__
229 class PostePiece(models
.Model
):
230 """Documents relatifs au Poste.
231 Ex.: Description de poste
233 poste
= models
.ForeignKey('Poste', db_column
='poste',
234 related_name
='pieces')
235 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
236 fichier
= models
.FileField(verbose_name
="Fichier",
237 upload_to
=poste_piece_dispatch
,
238 storage
=storage_prive
)
243 def __unicode__(self
):
244 return u
'%s' % (self
.nom
)
246 class PosteComparaison(models
.Model
):
248 De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
250 poste
= models
.ForeignKey('Poste', related_name
='comparaisons_internes')
251 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True, related_name
="+")
252 nom
= models
.CharField(verbose_name
="Poste", max_length
=255, null
=True, blank
=True)
253 montant
= models
.IntegerField(null
=True)
254 devise
= models
.ForeignKey("Devise", default
=5, related_name
='+', null
=True, blank
=True)
256 def taux_devise(self
):
257 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
258 if len(liste_taux
) == 0:
259 raise Exception(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
261 return liste_taux
[0].taux
263 def montant_euros(self
):
264 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
267 class PosteCommentaire(Commentaire
):
268 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
277 SITUATION_CHOICES
= (
278 ('C', 'Célibataire'),
283 class Employe(AUFMetadata
):
284 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
285 Dossiers qu'il occupe ou a occupé de Postes.
287 Cette classe aurait pu avantageusement s'appeler Personne car la notion
288 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
291 nom
= models
.CharField(max_length
=255)
292 prenom
= models
.CharField(max_length
=255, verbose_name
="Prénom")
293 nom_affichage
= models
.CharField(max_length
=255,
294 verbose_name
="Nom d'affichage",
295 null
=True, blank
=True)
296 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
297 db_column
='nationalite',
298 related_name
='employes_nationalite',
299 verbose_name
="Nationalité")
300 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
301 verbose_name
="Date de naissance",
302 validators
=[validate_date_passee
],
303 null
=True, blank
=True)
304 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
307 situation_famille
= models
.CharField(max_length
=1,
308 choices
=SITUATION_CHOICES
,
309 verbose_name
="Situation familiale",
310 null
=True, blank
=True)
311 date_entree
= models
.DateField(verbose_name
="Date d'entrée à l'AUF",
312 help_text
=HELP_TEXT_DATE
,
313 null
=True, blank
=True)
316 tel_domicile
= models
.CharField(max_length
=255,
317 verbose_name
="Tél. domicile",
318 null
=True, blank
=True)
319 tel_cellulaire
= models
.CharField(max_length
=255,
320 verbose_name
="Tél. cellulaire",
321 null
=True, blank
=True)
322 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
323 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
324 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
325 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
326 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
327 related_name
='employes',
328 null
=True, blank
=True)
331 ordering
= ['nom_affichage','nom','prenom']
332 verbose_name
= "Employé"
333 verbose_name_plural
= "Employés"
335 def __unicode__(self
):
336 return u
'%s' % (self
.get_nom())
339 nom_affichage
= self
.nom_affichage
340 if not nom_affichage
:
341 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
346 if self
.genre
.upper() == u
'M':
348 elif self
.genre
.upper() == u
'F':
352 class EmployePiece(models
.Model
):
353 """Documents relatifs à un employé.
356 employe
= models
.ForeignKey('Employe', db_column
='employe',
358 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
359 fichier
= models
.FileField(verbose_name
="Fichier",
360 upload_to
=dossier_piece_dispatch
,
361 storage
=storage_prive
)
366 def __unicode__(self
):
367 return u
'%s' % (self
.nom
)
369 class EmployeCommentaire(Commentaire
):
370 employe
= models
.ForeignKey('Employe', db_column
='employe',
374 LIEN_PARENTE_CHOICES
= (
375 ('Conjoint', 'Conjoint'),
376 ('Conjointe', 'Conjointe'),
381 class AyantDroit(AUFMetadata
):
382 """Personne en relation avec un Employe.
385 nom
= models
.CharField(max_length
=255)
386 prenom
= models
.CharField(max_length
=255,
387 verbose_name
="Prénom",)
388 nom_affichage
= models
.CharField(max_length
=255,
389 verbose_name
="Nom d'affichage",
390 null
=True, blank
=True)
391 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
392 db_column
='nationalite',
393 related_name
='ayantdroits_nationalite',
394 verbose_name
="Nationalité")
395 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
396 verbose_name
="Date de naissance",
397 validators
=[validate_date_passee
],
398 null
=True, blank
=True)
399 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
402 employe
= models
.ForeignKey('Employe', db_column
='employe',
403 related_name
='ayantdroits',
404 verbose_name
="Employé")
405 lien_parente
= models
.CharField(max_length
=10,
406 choices
=LIEN_PARENTE_CHOICES
,
407 verbose_name
="Lien de parenté",
408 null
=True, blank
=True)
411 ordering
= ['nom_affichage']
412 verbose_name
= "Ayant droit"
413 verbose_name_plural
= "Ayants droit"
415 def __unicode__(self
):
416 return u
'%s' % (self
.get_nom())
419 nom_affichage
= self
.nom_affichage
420 if not nom_affichage
:
421 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
424 class AyantDroitCommentaire(Commentaire
):
425 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
431 STATUT_RESIDENCE_CHOICES
= (
433 ('expat', 'Expatrié'),
436 COMPTE_COMPTA_CHOICES
= (
442 class Dossier_(AUFMetadata
):
443 """Le Dossier regroupe les informations relatives à l'occupation
444 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
447 Plusieurs Contrats peuvent être associés au Dossier.
448 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
449 lequel aucun Dossier n'existe est un poste vacant.
452 employe
= models
.ForeignKey('Employe', db_column
='employe',
453 related_name
='dossiers',
454 verbose_name
="Employé")
455 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
456 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3,
458 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
459 db_column
='organisme_bstg',
461 verbose_name
="Organisme",
462 help_text
="Si détaché (DET) ou \
463 mis à disposition (MAD), \
464 préciser l'organisme.",
465 null
=True, blank
=True)
468 remplacement
= models
.BooleanField(default
=False)
469 statut_residence
= models
.CharField(max_length
=10, default
='local',
470 verbose_name
="Statut", null
=True,
471 choices
=STATUT_RESIDENCE_CHOICES
)
474 classement
= models
.ForeignKey('Classement', db_column
='classement',
476 null
=True, blank
=True)
477 regime_travail
= models
.DecimalField(max_digits
=12, null
=True,
479 default
=REGIME_TRAVAIL_DEFAULT
,
480 verbose_name
="Régime de travail",
481 help_text
="% du temps complet")
482 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
483 decimal_places
=2, null
=True,
484 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
485 verbose_name
="Nb. heures par semaine")
487 # Occupation du Poste par cet Employe (anciennement "mandat")
488 date_debut
= models
.DateField(verbose_name
="Date de début d'occupation \
490 help_text
=HELP_TEXT_DATE
)
491 date_fin
= models
.DateField(verbose_name
="Date de fin d'occupation \
493 help_text
=HELP_TEXT_DATE
,
494 null
=True, blank
=True)
501 ordering
= ['employe__nom', ]
502 verbose_name
= "Dossier"
503 verbose_name_plural
= "Dossiers"
505 def __unicode__(self
):
506 poste
= self
.poste
.nom
507 if self
.employe
.genre
== 'F':
508 poste
= self
.poste
.nom_feminin
509 return u
'%s - %s' % (self
.employe
, poste
)
512 class Dossier(Dossier_
):
513 __doc__
= Dossier_
.__doc__
516 class DossierPiece(models
.Model
):
517 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
518 Ex.: Lettre de motivation.
520 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
522 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
523 fichier
= models
.FileField(verbose_name
="Fichier",
524 upload_to
=dossier_piece_dispatch
,
525 storage
=storage_prive
)
530 def __unicode__(self
):
531 return u
'%s' % (self
.nom
)
533 class DossierCommentaire(Commentaire
):
534 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
537 class DossierComparaison(models
.Model
):
539 Photo d'une comparaison salariale au moment de l'embauche.
541 dossier
= models
.ForeignKey('Dossier', related_name
='comparaisons')
542 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True)
543 poste
= models
.CharField(max_length
=255, null
=True, blank
=True)
544 personne
= models
.CharField(max_length
=255, null
=True, blank
=True)
545 montant
= models
.IntegerField(null
=True)
546 devise
= models
.ForeignKey('Devise', default
=5, related_name
='+', null
=True, blank
=True)
548 def taux_devise(self
):
549 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.dossier
.poste
.implantation
)
550 if len(liste_taux
) == 0:
551 raise Exception(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.dossier
.poste
.implantation
))
553 return liste_taux
[0].taux
555 def montant_euros(self
):
556 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
561 class RemunerationMixin(AUFMetadata
):
563 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
564 related_name
='%(app_label)s_%(class)s_remunerations')
565 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
567 verbose_name
="Type de rémunération")
568 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
569 db_column
='type_revalorisation',
571 verbose_name
="Type de revalorisation",
572 null
=True, blank
=True)
573 montant
= models
.FloatField(null
=True, blank
=True,
575 # Annuel (12 mois, 52 semaines, 364 jours?)
576 devise
= models
.ForeignKey('Devise', to_field
='id',
577 db_column
='devise', related_name
='+',
579 # commentaire = precision
580 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
581 # date_debut = anciennement date_effectif
582 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
583 verbose_name
="Date de début",
584 null
=True, blank
=True)
585 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
586 verbose_name
="Date de fin",
587 null
=True, blank
=True)
591 ordering
= ['type__nom', '-date_fin']
593 def __unicode__(self
):
594 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
596 class Remuneration_(RemunerationMixin
):
597 """Structure de rémunération (données budgétaires) en situation normale
598 pour un Dossier. Si un Evenement existe, utiliser la structure de
599 rémunération EvenementRemuneration de cet événement.
602 def montant_mois(self
):
603 return round(self
.montant
/ 12, 2)
605 def taux_devise(self
):
606 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
608 def montant_euro(self
):
609 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
611 def montant_euro_mois(self
):
612 return round(self
.montant_euro() / 12, 2)
614 def __unicode__(self
):
616 devise
= self
.devise
.code
619 return "%s %s" % (self
.montant
, devise
)
623 verbose_name
= "Rémunération"
624 verbose_name_plural
= "Rémunérations"
627 class Remuneration(Remuneration_
):
628 __doc__
= Remuneration_
.__doc__
633 class ContratManager(NoDeleteManager
):
634 def get_query_set(self
):
635 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
638 class Contrat(AUFMetadata
):
639 """Document juridique qui encadre la relation de travail d'un Employe
640 pour un Poste particulier. Pour un Dossier (qui documente cette
641 relation de travail) plusieurs contrats peuvent être associés.
644 objects
= ContratManager()
646 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
648 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
650 verbose_name
="Type de contrat")
651 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
652 verbose_name
="Date de début")
653 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
654 verbose_name
="Date de fin",
655 null
=True, blank
=True)
658 ordering
= ['dossier__employe__nom_affichage']
659 verbose_name
= "Contrat"
660 verbose_name_plural
= "Contrats"
662 def __unicode__(self
):
663 return u
'%s - %s' % (self
.dossier
, self
.id)
665 # TODO? class ContratPiece(models.Model):
670 class Evenement_(AUFMetadata
):
671 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
672 d'un Dossier qui vient altérer des informations normales liées à un Dossier
673 (ex.: la Remuneration).
675 Ex.: congé de maternité, maladie...
677 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
678 différent et une rémunération en conséquence. On souhaite toutefois
679 conserver le Dossier intact afin d'éviter une re-saisie des données lors
680 du retour à la normale.
682 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
684 nom
= models
.CharField(max_length
=255)
685 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
686 verbose_name
="Date de début")
687 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
688 verbose_name
="Date de fin",
689 null
=True, blank
=True)
694 verbose_name
= "Évènement"
695 verbose_name_plural
= "Évènements"
697 def __unicode__(self
):
698 return u
'%s' % (self
.nom
)
701 class Evenement(Evenement_
):
702 __doc__
= Evenement_
.__doc__
705 class EvenementRemuneration_(RemunerationMixin
):
706 """Structure de rémunération liée à un Evenement qui remplace
707 temporairement la Remuneration normale d'un Dossier, pour toute la durée
710 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
712 verbose_name
="Évènement")
713 # TODO : le champ dossier hérité de Remuneration doit être dérivé
714 # de l'Evenement associé
718 ordering
= ['evenement', 'type__nom', '-date_fin']
719 verbose_name
= "Évènement - rémunération"
720 verbose_name_plural
= "Évènements - rémunérations"
723 class EvenementRemuneration(EvenementRemuneration_
):
724 __doc__
= EvenementRemuneration_
.__doc__
730 class EvenementRemuneration(EvenementRemuneration_
):
731 __doc__
= EvenementRemuneration_
.__doc__
736 class FamilleEmploi(AUFMetadata
):
737 """Catégorie utilisée dans la gestion des Postes.
738 Catégorie supérieure à TypePoste.
740 nom
= models
.CharField(max_length
=255)
744 verbose_name
= "Famille d'emploi"
745 verbose_name_plural
= "Familles d'emploi"
747 def __unicode__(self
):
748 return u
'%s' % (self
.nom
)
750 class TypePoste(AUFMetadata
):
751 """Catégorie de Poste.
753 nom
= models
.CharField(max_length
=255)
754 nom_feminin
= models
.CharField(max_length
=255,
755 verbose_name
="Nom féminin")
757 is_responsable
= models
.BooleanField(default
=False,
758 verbose_name
="Poste de responsabilité")
759 famille_emploi
= models
.ForeignKey('FamilleEmploi',
760 db_column
='famille_emploi',
762 verbose_name
="Famille d'emploi")
766 verbose_name
= "Type de poste"
767 verbose_name_plural
= "Types de poste"
769 def __unicode__(self
):
770 return u
'%s' % (self
.nom
)
773 TYPE_PAIEMENT_CHOICES
= (
774 ('Régulier', 'Régulier'),
775 ('Ponctuel', 'Ponctuel'),
778 NATURE_REMUNERATION_CHOICES
= (
779 ('Accessoire', 'Accessoire'),
780 ('Charges', 'Charges'),
781 ('Indemnité', 'Indemnité'),
782 ('RAS', 'Rémunération autre source'),
783 ('Traitement', 'Traitement'),
786 class TypeRemuneration(AUFMetadata
):
787 """Catégorie de Remuneration.
789 nom
= models
.CharField(max_length
=255)
790 type_paiement
= models
.CharField(max_length
=30,
791 choices
=TYPE_PAIEMENT_CHOICES
,
792 verbose_name
="Type de paiement")
793 nature_remuneration
= models
.CharField(max_length
=30,
794 choices
=NATURE_REMUNERATION_CHOICES
,
795 verbose_name
="Nature de la rémunération")
799 verbose_name
= "Type de rémunération"
800 verbose_name_plural
= "Types de rémunération"
802 def __unicode__(self
):
803 return u
'%s' % (self
.nom
)
805 class TypeRevalorisation(AUFMetadata
):
806 """Justification du changement de la Remuneration.
807 (Actuellement utilisé dans aucun traitement informatique.)
809 nom
= models
.CharField(max_length
=255)
813 verbose_name
= "Type de revalorisation"
814 verbose_name_plural
= "Types de revalorisation"
816 def __unicode__(self
):
817 return u
'%s' % (self
.nom
)
819 class Service(AUFMetadata
):
820 """Unité administrative où les Postes sont rattachés.
822 nom
= models
.CharField(max_length
=255)
826 verbose_name
= "Service"
827 verbose_name_plural
= "Services"
829 def __unicode__(self
):
830 return u
'%s' % (self
.nom
)
833 TYPE_ORGANISME_CHOICES
= (
834 ('MAD', 'Mise à disposition'),
835 ('DET', 'Détachement'),
838 class OrganismeBstg(AUFMetadata
):
839 """Organisation d'où provient un Employe mis à disposition (MAD) de
840 ou détaché (DET) à l'AUF à titre gratuit.
842 (BSTG = bien et service à titre gratuit.)
844 nom
= models
.CharField(max_length
=255)
845 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
846 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
848 related_name
='organismes_bstg',
849 null
=True, blank
=True)
852 ordering
= ['type', 'nom']
853 verbose_name
= "Organisme BSTG"
854 verbose_name_plural
= "Organismes BSTG"
856 def __unicode__(self
):
857 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
859 class Statut(AUFMetadata
):
860 """Statut de l'Employe dans le cadre d'un Dossier particulier.
863 code
= models
.CharField(max_length
=25, unique
=True)
864 nom
= models
.CharField(max_length
=255)
868 verbose_name
= "Statut d'employé"
869 verbose_name_plural
= "Statuts d'employé"
871 def __unicode__(self
):
872 return u
'%s : %s' % (self
.code
, self
.nom
)
875 TYPE_CLASSEMENT_CHOICES
= (
877 ('T', 'T - Technicien'),
878 ('P', 'P - Professionel'),
880 ('D', 'D - Direction'),
881 ('SO', 'SO - Sans objet [expatriés]'),
882 ('HG', 'HG - Hors grille [direction]'),
886 class Classement_(AUFMetadata
):
887 """Éléments de classement de la
888 "Grille générique de classement hiérarchique".
890 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
891 classement dans la grille. Le classement donne le coefficient utilisé dans:
893 salaire de base = coefficient * valeur du point de l'Implantation du Poste
896 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
897 echelon
= models
.IntegerField(verbose_name
="Échelon")
898 degre
= models
.IntegerField(verbose_name
="Degré")
899 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient",
902 # annee # au lieu de date_debut et date_fin
903 commentaire
= models
.TextField(null
=True, blank
=True)
907 ordering
= ['type','echelon','degre','coefficient']
908 verbose_name
= "Classement"
909 verbose_name_plural
= "Classements"
911 def __unicode__(self
):
912 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
915 class Classement(Classement_
):
916 __doc__
= Classement_
.__doc__
919 class TauxChange_(AUFMetadata
):
920 """Taux de change de la devise vers l'euro (EUR)
921 pour chaque année budgétaire.
924 devise
= models
.ForeignKey('Devise', db_column
='devise',
926 annee
= models
.IntegerField(verbose_name
="Année")
927 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
931 ordering
= ['-annee', 'devise__code']
932 verbose_name
= "Taux de change"
933 verbose_name_plural
= "Taux de change"
935 def __unicode__(self
):
936 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
939 class TauxChange(TauxChange_
):
940 __doc__
= TauxChange_
.__doc__
942 class ValeurPointManager(NoDeleteManager
):
943 def get_query_set(self
):
944 return super(ValeurPointManager
, self
).get_query_set().select_related('devise', 'implantation')
947 class ValeurPoint_(AUFMetadata
):
948 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
949 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
950 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
952 salaire de base = coefficient * valeur du point de l'Implantation du Poste
955 objects
= ValeurPointManager()
957 valeur
= models
.FloatField(null
=True)
958 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
959 related_name
='+', default
=5)
960 implantation
= models
.ForeignKey(ref
.Implantation
,
961 db_column
='implantation',
962 related_name
='%(app_label)s_valeur_point')
964 annee
= models
.IntegerField()
967 ordering
= ['-annee', 'implantation__nom']
969 verbose_name
= "Valeur du point"
970 verbose_name_plural
= "Valeurs du point"
972 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
973 def get_tauxchange_courant(self
):
975 Recherche le taux courant associé à la valeur d'un point.
976 Tous les taux de l'année courante sont chargés, pour optimiser un
977 affichage en liste. (On pourrait probablement améliorer le manager pour
978 lui greffer le taux courant sous forme de JOIN)
980 for tauxchange
in self
.tauxchange
:
981 if tauxchange
.implantation_id
== self
.implantation_id
:
985 def __unicode__(self
):
986 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
989 class ValeurPoint(ValeurPoint_
):
990 __doc__
= ValeurPoint_
.__doc__
993 class Devise(AUFMetadata
):
996 code
= models
.CharField(max_length
=10, unique
=True)
997 nom
= models
.CharField(max_length
=255)
1001 verbose_name
= "Devise"
1002 verbose_name_plural
= "Devises"
1004 def __unicode__(self
):
1005 return u
'%s - %s' % (self
.code
, self
.nom
)
1007 class TypeContrat(AUFMetadata
):
1010 nom
= models
.CharField(max_length
=255)
1011 nom_long
= models
.CharField(max_length
=255)
1015 verbose_name
= "Type de contrat"
1016 verbose_name_plural
= "Types de contrat"
1018 def __unicode__(self
):
1019 return u
'%s' % (self
.nom
)
1024 class ResponsableImplantation(AUFMetadata
):
1025 """Le responsable d'une implantation.
1026 Anciennement géré sur le Dossier du responsable.
1028 employe
= models
.ForeignKey('Employe', db_column
='employe',
1030 null
=True, blank
=True)
1031 implantation
= models
.ForeignKey(ref
.Implantation
,
1032 db_column
='implantation', related_name
='+',
1035 def __unicode__(self
):
1036 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1039 ordering
= ['implantation__nom']
1040 verbose_name
= "Responsable d'implantation"
1041 verbose_name_plural
= "Responsables d'implantation"