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)
255 montant_euros
= models
.IntegerField(null
=True)
258 class PosteCommentaire(Commentaire
):
259 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
268 SITUATION_CHOICES
= (
269 ('C', 'Célibataire'),
274 class Employe(AUFMetadata
):
275 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
276 Dossiers qu'il occupe ou a occupé de Postes.
278 Cette classe aurait pu avantageusement s'appeler Personne car la notion
279 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
282 nom
= models
.CharField(max_length
=255)
283 prenom
= models
.CharField(max_length
=255, verbose_name
="Prénom")
284 nom_affichage
= models
.CharField(max_length
=255,
285 verbose_name
="Nom d'affichage",
286 null
=True, blank
=True)
287 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
288 db_column
='nationalite',
289 related_name
='employes_nationalite',
290 verbose_name
="Nationalité")
291 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
292 verbose_name
="Date de naissance",
293 validators
=[validate_date_passee
],
294 null
=True, blank
=True)
295 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
298 situation_famille
= models
.CharField(max_length
=1,
299 choices
=SITUATION_CHOICES
,
300 verbose_name
="Situation familiale",
301 null
=True, blank
=True)
302 date_entree
= models
.DateField(verbose_name
="Date d'entrée à l'AUF",
303 help_text
=HELP_TEXT_DATE
,
304 null
=True, blank
=True)
307 tel_domicile
= models
.CharField(max_length
=255,
308 verbose_name
="Tél. domicile",
309 null
=True, blank
=True)
310 tel_cellulaire
= models
.CharField(max_length
=255,
311 verbose_name
="Tél. cellulaire",
312 null
=True, blank
=True)
313 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
314 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
315 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
316 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
317 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
318 related_name
='employes',
319 null
=True, blank
=True)
322 ordering
= ['nom_affichage','nom','prenom']
323 verbose_name
= "Employé"
324 verbose_name_plural
= "Employés"
326 def __unicode__(self
):
327 return u
'%s' % (self
.get_nom())
330 nom_affichage
= self
.nom_affichage
331 if not nom_affichage
:
332 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
337 if self
.genre
.upper() == u
'M':
339 elif self
.genre
.upper() == u
'F':
343 class EmployePiece(models
.Model
):
344 """Documents relatifs à un employé.
347 employe
= models
.ForeignKey('Employe', db_column
='employe',
349 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
350 fichier
= models
.FileField(verbose_name
="Fichier",
351 upload_to
=dossier_piece_dispatch
,
352 storage
=storage_prive
)
357 def __unicode__(self
):
358 return u
'%s' % (self
.nom
)
360 class EmployeCommentaire(Commentaire
):
361 employe
= models
.ForeignKey('Employe', db_column
='employe',
365 LIEN_PARENTE_CHOICES
= (
366 ('Conjoint', 'Conjoint'),
367 ('Conjointe', 'Conjointe'),
372 class AyantDroit(AUFMetadata
):
373 """Personne en relation avec un Employe.
376 nom
= models
.CharField(max_length
=255)
377 prenom
= models
.CharField(max_length
=255,
378 verbose_name
="Prénom",)
379 nom_affichage
= models
.CharField(max_length
=255,
380 verbose_name
="Nom d'affichage",
381 null
=True, blank
=True)
382 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
383 db_column
='nationalite',
384 related_name
='ayantdroits_nationalite',
385 verbose_name
="Nationalité")
386 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
387 verbose_name
="Date de naissance",
388 validators
=[validate_date_passee
],
389 null
=True, blank
=True)
390 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
393 employe
= models
.ForeignKey('Employe', db_column
='employe',
394 related_name
='ayantdroits',
395 verbose_name
="Employé")
396 lien_parente
= models
.CharField(max_length
=10,
397 choices
=LIEN_PARENTE_CHOICES
,
398 verbose_name
="Lien de parenté",
399 null
=True, blank
=True)
402 ordering
= ['nom_affichage']
403 verbose_name
= "Ayant droit"
404 verbose_name_plural
= "Ayants droit"
406 def __unicode__(self
):
407 return u
'%s' % (self
.get_nom())
410 nom_affichage
= self
.nom_affichage
411 if not nom_affichage
:
412 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
415 class AyantDroitCommentaire(Commentaire
):
416 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
422 STATUT_RESIDENCE_CHOICES
= (
424 ('expat', 'Expatrié'),
427 COMPTE_COMPTA_CHOICES
= (
433 class Dossier_(AUFMetadata
):
434 """Le Dossier regroupe les informations relatives à l'occupation
435 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
438 Plusieurs Contrats peuvent être associés au Dossier.
439 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
440 lequel aucun Dossier n'existe est un poste vacant.
443 employe
= models
.ForeignKey('Employe', db_column
='employe',
444 related_name
='dossiers',
445 verbose_name
="Employé")
446 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
447 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3,
449 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
450 db_column
='organisme_bstg',
452 verbose_name
="Organisme",
453 help_text
="Si détaché (DET) ou \
454 mis à disposition (MAD), \
455 préciser l'organisme.",
456 null
=True, blank
=True)
459 remplacement
= models
.BooleanField(default
=False)
460 statut_residence
= models
.CharField(max_length
=10, default
='local',
461 verbose_name
="Statut", null
=True,
462 choices
=STATUT_RESIDENCE_CHOICES
)
465 classement
= models
.ForeignKey('Classement', db_column
='classement',
467 null
=True, blank
=True)
468 regime_travail
= models
.DecimalField(max_digits
=12, null
=True,
470 default
=REGIME_TRAVAIL_DEFAULT
,
471 verbose_name
="Régime de travail",
472 help_text
="% du temps complet")
473 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
474 decimal_places
=2, null
=True,
475 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
476 verbose_name
="Nb. heures par semaine")
478 # Occupation du Poste par cet Employe (anciennement "mandat")
479 date_debut
= models
.DateField(verbose_name
="Date de début d'occupation \
481 help_text
=HELP_TEXT_DATE
)
482 date_fin
= models
.DateField(verbose_name
="Date de fin d'occupation \
484 help_text
=HELP_TEXT_DATE
,
485 null
=True, blank
=True)
492 ordering
= ['employe__nom', ]
493 verbose_name
= "Dossier"
494 verbose_name_plural
= "Dossiers"
496 def __unicode__(self
):
497 poste
= self
.poste
.nom
498 if self
.employe
.genre
== 'F':
499 poste
= self
.poste
.nom_feminin
500 return u
'%s - %s' % (self
.employe
, poste
)
503 class Dossier(Dossier_
):
504 __doc__
= Dossier_
.__doc__
507 class DossierPiece(models
.Model
):
508 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
509 Ex.: Lettre de motivation.
511 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
513 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
514 fichier
= models
.FileField(verbose_name
="Fichier",
515 upload_to
=dossier_piece_dispatch
,
516 storage
=storage_prive
)
521 def __unicode__(self
):
522 return u
'%s' % (self
.nom
)
524 class DossierCommentaire(Commentaire
):
525 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
531 class RemunerationMixin(AUFMetadata
):
533 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
534 related_name
='%(app_label)s_%(class)s_remunerations')
535 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
537 verbose_name
="Type de rémunération")
538 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
539 db_column
='type_revalorisation',
541 verbose_name
="Type de revalorisation",
542 null
=True, blank
=True)
543 montant
= models
.FloatField(null
=True, blank
=True,
545 # Annuel (12 mois, 52 semaines, 364 jours?)
546 devise
= models
.ForeignKey('Devise', to_field
='id',
547 db_column
='devise', related_name
='+',
549 # commentaire = precision
550 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
551 # date_debut = anciennement date_effectif
552 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
553 verbose_name
="Date de début",
554 null
=True, blank
=True)
555 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
556 verbose_name
="Date de fin",
557 null
=True, blank
=True)
561 ordering
= ['type__nom', '-date_fin']
563 def __unicode__(self
):
564 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
566 class Remuneration_(RemunerationMixin
):
567 """Structure de rémunération (données budgétaires) en situation normale
568 pour un Dossier. Si un Evenement existe, utiliser la structure de
569 rémunération EvenementRemuneration de cet événement.
572 def montant_mois(self
):
573 return round(self
.montant
/ 12, 2)
575 def taux_devise(self
):
576 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
578 def montant_euro(self
):
579 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
581 def montant_euro_mois(self
):
582 return round(self
.montant_euro() / 12, 2)
584 def __unicode__(self
):
586 devise
= self
.devise
.code
589 return "%s %s" % (self
.montant
, devise
)
593 verbose_name
= "Rémunération"
594 verbose_name_plural
= "Rémunérations"
597 class Remuneration(Remuneration_
):
598 __doc__
= Remuneration_
.__doc__
603 class ContratManager(NoDeleteManager
):
604 def get_query_set(self
):
605 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
608 class Contrat(AUFMetadata
):
609 """Document juridique qui encadre la relation de travail d'un Employe
610 pour un Poste particulier. Pour un Dossier (qui documente cette
611 relation de travail) plusieurs contrats peuvent être associés.
614 objects
= ContratManager()
616 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
618 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
620 verbose_name
="Type de contrat")
621 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
622 verbose_name
="Date de début")
623 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
624 verbose_name
="Date de fin",
625 null
=True, blank
=True)
628 ordering
= ['dossier__employe__nom_affichage']
629 verbose_name
= "Contrat"
630 verbose_name_plural
= "Contrats"
632 def __unicode__(self
):
633 return u
'%s - %s' % (self
.dossier
, self
.id)
635 # TODO? class ContratPiece(models.Model):
640 class Evenement_(AUFMetadata
):
641 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
642 d'un Dossier qui vient altérer des informations normales liées à un Dossier
643 (ex.: la Remuneration).
645 Ex.: congé de maternité, maladie...
647 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
648 différent et une rémunération en conséquence. On souhaite toutefois
649 conserver le Dossier intact afin d'éviter une re-saisie des données lors
650 du retour à la normale.
652 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
654 nom
= models
.CharField(max_length
=255)
655 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
656 verbose_name
="Date de début")
657 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
658 verbose_name
="Date de fin",
659 null
=True, blank
=True)
664 verbose_name
= "Évènement"
665 verbose_name_plural
= "Évènements"
667 def __unicode__(self
):
668 return u
'%s' % (self
.nom
)
671 class Evenement(Evenement_
):
672 __doc__
= Evenement_
.__doc__
675 class EvenementRemuneration_(RemunerationMixin
):
676 """Structure de rémunération liée à un Evenement qui remplace
677 temporairement la Remuneration normale d'un Dossier, pour toute la durée
680 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
682 verbose_name
="Évènement")
683 # TODO : le champ dossier hérité de Remuneration doit être dérivé
684 # de l'Evenement associé
688 ordering
= ['evenement', 'type__nom', '-date_fin']
689 verbose_name
= "Évènement - rémunération"
690 verbose_name_plural
= "Évènements - rémunérations"
693 class EvenementRemuneration(EvenementRemuneration_
):
694 __doc__
= EvenementRemuneration_
.__doc__
700 class EvenementRemuneration(EvenementRemuneration_
):
701 __doc__
= EvenementRemuneration_
.__doc__
706 class FamilleEmploi(AUFMetadata
):
707 """Catégorie utilisée dans la gestion des Postes.
708 Catégorie supérieure à TypePoste.
710 nom
= models
.CharField(max_length
=255)
714 verbose_name
= "Famille d'emploi"
715 verbose_name_plural
= "Familles d'emploi"
717 def __unicode__(self
):
718 return u
'%s' % (self
.nom
)
720 class TypePoste(AUFMetadata
):
721 """Catégorie de Poste.
723 nom
= models
.CharField(max_length
=255)
724 nom_feminin
= models
.CharField(max_length
=255,
725 verbose_name
="Nom féminin")
727 is_responsable
= models
.BooleanField(default
=False,
728 verbose_name
="Poste de responsabilité")
729 famille_emploi
= models
.ForeignKey('FamilleEmploi',
730 db_column
='famille_emploi',
732 verbose_name
="Famille d'emploi")
736 verbose_name
= "Type de poste"
737 verbose_name_plural
= "Types de poste"
739 def __unicode__(self
):
740 return u
'%s' % (self
.nom
)
743 TYPE_PAIEMENT_CHOICES
= (
744 ('Régulier', 'Régulier'),
745 ('Ponctuel', 'Ponctuel'),
748 NATURE_REMUNERATION_CHOICES
= (
749 ('Accessoire', 'Accessoire'),
750 ('Charges', 'Charges'),
751 ('Indemnité', 'Indemnité'),
752 ('RAS', 'Rémunération autre source'),
753 ('Traitement', 'Traitement'),
756 class TypeRemuneration(AUFMetadata
):
757 """Catégorie de Remuneration.
759 nom
= models
.CharField(max_length
=255)
760 type_paiement
= models
.CharField(max_length
=30,
761 choices
=TYPE_PAIEMENT_CHOICES
,
762 verbose_name
="Type de paiement")
763 nature_remuneration
= models
.CharField(max_length
=30,
764 choices
=NATURE_REMUNERATION_CHOICES
,
765 verbose_name
="Nature de la rémunération")
769 verbose_name
= "Type de rémunération"
770 verbose_name_plural
= "Types de rémunération"
772 def __unicode__(self
):
773 return u
'%s' % (self
.nom
)
775 class TypeRevalorisation(AUFMetadata
):
776 """Justification du changement de la Remuneration.
777 (Actuellement utilisé dans aucun traitement informatique.)
779 nom
= models
.CharField(max_length
=255)
783 verbose_name
= "Type de revalorisation"
784 verbose_name_plural
= "Types de revalorisation"
786 def __unicode__(self
):
787 return u
'%s' % (self
.nom
)
789 class Service(AUFMetadata
):
790 """Unité administrative où les Postes sont rattachés.
792 nom
= models
.CharField(max_length
=255)
796 verbose_name
= "Service"
797 verbose_name_plural
= "Services"
799 def __unicode__(self
):
800 return u
'%s' % (self
.nom
)
803 TYPE_ORGANISME_CHOICES
= (
804 ('MAD', 'Mise à disposition'),
805 ('DET', 'Détachement'),
808 class OrganismeBstg(AUFMetadata
):
809 """Organisation d'où provient un Employe mis à disposition (MAD) de
810 ou détaché (DET) à l'AUF à titre gratuit.
812 (BSTG = bien et service à titre gratuit.)
814 nom
= models
.CharField(max_length
=255)
815 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
816 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
818 related_name
='organismes_bstg',
819 null
=True, blank
=True)
822 ordering
= ['type', 'nom']
823 verbose_name
= "Organisme BSTG"
824 verbose_name_plural
= "Organismes BSTG"
826 def __unicode__(self
):
827 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
829 class Statut(AUFMetadata
):
830 """Statut de l'Employe dans le cadre d'un Dossier particulier.
833 code
= models
.CharField(max_length
=25, unique
=True)
834 nom
= models
.CharField(max_length
=255)
838 verbose_name
= "Statut d'employé"
839 verbose_name_plural
= "Statuts d'employé"
841 def __unicode__(self
):
842 return u
'%s : %s' % (self
.code
, self
.nom
)
845 TYPE_CLASSEMENT_CHOICES
= (
847 ('T', 'T - Technicien'),
848 ('P', 'P - Professionel'),
850 ('D', 'D - Direction'),
851 ('SO', 'SO - Sans objet [expatriés]'),
852 ('HG', 'HG - Hors grille [direction]'),
856 class Classement_(AUFMetadata
):
857 """Éléments de classement de la
858 "Grille générique de classement hiérarchique".
860 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
861 classement dans la grille. Le classement donne le coefficient utilisé dans:
863 salaire de base = coefficient * valeur du point de l'Implantation du Poste
866 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
867 echelon
= models
.IntegerField(verbose_name
="Échelon")
868 degre
= models
.IntegerField(verbose_name
="Degré")
869 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient",
872 # annee # au lieu de date_debut et date_fin
873 commentaire
= models
.TextField(null
=True, blank
=True)
877 ordering
= ['type','echelon','degre','coefficient']
878 verbose_name
= "Classement"
879 verbose_name_plural
= "Classements"
881 def __unicode__(self
):
882 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
885 class Classement(Classement_
):
886 __doc__
= Classement_
.__doc__
889 class TauxChange_(AUFMetadata
):
890 """Taux de change de la devise vers l'euro (EUR)
891 pour chaque année budgétaire.
894 devise
= models
.ForeignKey('Devise', db_column
='devise',
896 annee
= models
.IntegerField(verbose_name
="Année")
897 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
901 ordering
= ['-annee', 'devise__code']
902 verbose_name
= "Taux de change"
903 verbose_name_plural
= "Taux de change"
905 def __unicode__(self
):
906 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
909 class TauxChange(TauxChange_
):
910 __doc__
= TauxChange_
.__doc__
912 class ValeurPointManager(NoDeleteManager
):
913 def get_query_set(self
):
914 return super(ValeurPointManager
, self
).get_query_set().select_related('devise', 'implantation')
917 class ValeurPoint_(AUFMetadata
):
918 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
919 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
920 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
922 salaire de base = coefficient * valeur du point de l'Implantation du Poste
925 objects
= ValeurPointManager()
927 valeur
= models
.FloatField(null
=True)
928 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
929 related_name
='+', default
=5)
930 implantation
= models
.ForeignKey(ref
.Implantation
,
931 db_column
='implantation',
932 related_name
='%(app_label)s_valeur_point')
934 annee
= models
.IntegerField()
937 ordering
= ['-annee', 'implantation__nom']
939 verbose_name
= "Valeur du point"
940 verbose_name_plural
= "Valeurs du point"
942 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
943 def get_tauxchange_courant(self
):
945 Recherche le taux courant associé à la valeur d'un point.
946 Tous les taux de l'année courante sont chargés, pour optimiser un
947 affichage en liste. (On pourrait probablement améliorer le manager pour
948 lui greffer le taux courant sous forme de JOIN)
950 for tauxchange
in self
.tauxchange
:
951 if tauxchange
.implantation_id
== self
.implantation_id
:
955 def __unicode__(self
):
956 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
959 class ValeurPoint(ValeurPoint_
):
960 __doc__
= ValeurPoint_
.__doc__
963 class Devise(AUFMetadata
):
966 code
= models
.CharField(max_length
=10, unique
=True)
967 nom
= models
.CharField(max_length
=255)
971 verbose_name
= "Devise"
972 verbose_name_plural
= "Devises"
974 def __unicode__(self
):
975 return u
'%s - %s' % (self
.code
, self
.nom
)
977 class TypeContrat(AUFMetadata
):
980 nom
= models
.CharField(max_length
=255)
981 nom_long
= models
.CharField(max_length
=255)
985 verbose_name
= "Type de contrat"
986 verbose_name_plural
= "Types de contrat"
988 def __unicode__(self
):
989 return u
'%s' % (self
.nom
)
994 class ResponsableImplantation(AUFMetadata
):
995 """Le responsable d'une implantation.
996 Anciennement géré sur le Dossier du responsable.
998 employe
= models
.ForeignKey('Employe', db_column
='employe',
1000 null
=True, blank
=True)
1001 implantation
= models
.ForeignKey(ref
.Implantation
,
1002 db_column
='implantation', related_name
='+',
1005 def __unicode__(self
):
1006 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1009 ordering
= ['implantation__nom']
1010 verbose_name
= "Responsable d'implantation"
1011 verbose_name_plural
= "Responsables d'implantation"