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 null
=True, blank
=True)
389 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
392 employe
= models
.ForeignKey('Employe', db_column
='employe',
393 related_name
='ayantdroits',
394 verbose_name
="Employé")
395 lien_parente
= models
.CharField(max_length
=10,
396 choices
=LIEN_PARENTE_CHOICES
,
397 verbose_name
="Lien de parenté",
398 null
=True, blank
=True)
401 ordering
= ['nom_affichage']
402 verbose_name
= "Ayant droit"
403 verbose_name_plural
= "Ayants droit"
405 def __unicode__(self
):
406 return u
'%s' % (self
.get_nom())
409 nom_affichage
= self
.nom_affichage
410 if not nom_affichage
:
411 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
414 class AyantDroitCommentaire(Commentaire
):
415 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
421 STATUT_RESIDENCE_CHOICES
= (
423 ('expat', 'Expatrié'),
426 COMPTE_COMPTA_CHOICES
= (
432 class Dossier_(AUFMetadata
):
433 """Le Dossier regroupe les informations relatives à l'occupation
434 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
437 Plusieurs Contrats peuvent être associés au Dossier.
438 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
439 lequel aucun Dossier n'existe est un poste vacant.
442 employe
= models
.ForeignKey('Employe', db_column
='employe',
443 related_name
='dossiers',
444 verbose_name
="Employé")
445 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
446 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3,
448 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
449 db_column
='organisme_bstg',
451 verbose_name
="Organisme",
452 help_text
="Si détaché (DET) ou \
453 mis à disposition (MAD), \
454 préciser l'organisme.",
455 null
=True, blank
=True)
458 remplacement
= models
.BooleanField(default
=False)
459 statut_residence
= models
.CharField(max_length
=10, default
='local',
460 verbose_name
="Statut", null
=True,
461 choices
=STATUT_RESIDENCE_CHOICES
)
464 classement
= models
.ForeignKey('Classement', db_column
='classement',
466 null
=True, blank
=True)
467 regime_travail
= models
.DecimalField(max_digits
=12, null
=True,
469 default
=REGIME_TRAVAIL_DEFAULT
,
470 verbose_name
="Régime de travail",
471 help_text
="% du temps complet")
472 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
473 decimal_places
=2, null
=True,
474 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
475 verbose_name
="Nb. heures par semaine")
477 # Occupation du Poste par cet Employe (anciennement "mandat")
478 date_debut
= models
.DateField(verbose_name
="Date de début d'occupation \
480 help_text
=HELP_TEXT_DATE
)
481 date_fin
= models
.DateField(verbose_name
="Date de fin d'occupation \
483 help_text
=HELP_TEXT_DATE
,
484 null
=True, blank
=True)
491 ordering
= ['employe__nom_affichage', 'employe__nom', 'poste__nom']
492 verbose_name
= "Dossier"
493 verbose_name_plural
= "Dossiers"
495 def __unicode__(self
):
496 poste
= self
.poste
.nom
497 if self
.employe
.genre
== 'F':
498 poste
= self
.poste
.nom_feminin
499 return u
'%s - %s' % (self
.employe
, poste
)
502 class Dossier(Dossier_
):
503 __doc__
= Dossier_
.__doc__
506 class DossierPiece(models
.Model
):
507 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
508 Ex.: Lettre de motivation.
510 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
512 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
513 fichier
= models
.FileField(verbose_name
="Fichier",
514 upload_to
=dossier_piece_dispatch
,
515 storage
=storage_prive
)
520 def __unicode__(self
):
521 return u
'%s' % (self
.nom
)
523 class DossierCommentaire(Commentaire
):
524 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
530 class RemunerationMixin(AUFMetadata
):
532 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
533 related_name
='%(app_label)s_%(class)s_remunerations')
534 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
536 verbose_name
="Type de rémunération")
537 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
538 db_column
='type_revalorisation',
540 verbose_name
="Type de revalorisation",
541 null
=True, blank
=True)
542 montant
= models
.FloatField(null
=True, blank
=True,
544 # Annuel (12 mois, 52 semaines, 364 jours?)
545 devise
= models
.ForeignKey('Devise', to_field
='id',
546 db_column
='devise', related_name
='+',
548 # commentaire = precision
549 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
550 # date_debut = anciennement date_effectif
551 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
552 verbose_name
="Date de début",
553 null
=True, blank
=True)
554 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
555 verbose_name
="Date de fin",
556 null
=True, blank
=True)
560 ordering
= ['type__nom', '-date_fin']
562 def __unicode__(self
):
563 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
565 class Remuneration_(RemunerationMixin
):
566 """Structure de rémunération (données budgétaires) en situation normale
567 pour un Dossier. Si un Evenement existe, utiliser la structure de
568 rémunération EvenementRemuneration de cet événement.
571 def montant_mois(self
):
572 return round(self
.montant
/ 12, 2)
574 def taux_devise(self
):
575 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
577 def montant_euro(self
):
578 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
580 def montant_euro_mois(self
):
581 return round(self
.montant_euro() / 12, 2)
583 def __unicode__(self
):
585 devise
= self
.devise
.code
588 return "%s %s" % (self
.montant
, devise
)
592 verbose_name
= "Rémunération"
593 verbose_name_plural
= "Rémunérations"
596 class Remuneration(Remuneration_
):
597 __doc__
= Remuneration_
.__doc__
602 class ContratManager(NoDeleteManager
):
603 def get_query_set(self
):
604 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
607 class Contrat(AUFMetadata
):
608 """Document juridique qui encadre la relation de travail d'un Employe
609 pour un Poste particulier. Pour un Dossier (qui documente cette
610 relation de travail) plusieurs contrats peuvent être associés.
613 objects
= ContratManager()
615 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
617 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
619 verbose_name
="Type de contrat")
620 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
621 verbose_name
="Date de début")
622 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
623 verbose_name
="Date de fin",
624 null
=True, blank
=True)
627 ordering
= ['dossier__employe__nom_affichage']
628 verbose_name
= "Contrat"
629 verbose_name_plural
= "Contrats"
631 def __unicode__(self
):
632 return u
'%s - %s' % (self
.dossier
, self
.id)
634 # TODO? class ContratPiece(models.Model):
639 class Evenement_(AUFMetadata
):
640 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
641 d'un Dossier qui vient altérer des informations normales liées à un Dossier
642 (ex.: la Remuneration).
644 Ex.: congé de maternité, maladie...
646 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
647 différent et une rémunération en conséquence. On souhaite toutefois
648 conserver le Dossier intact afin d'éviter une re-saisie des données lors
649 du retour à la normale.
651 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
653 nom
= models
.CharField(max_length
=255)
654 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
655 verbose_name
="Date de début")
656 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
657 verbose_name
="Date de fin",
658 null
=True, blank
=True)
663 verbose_name
= "Évènement"
664 verbose_name_plural
= "Évènements"
666 def __unicode__(self
):
667 return u
'%s' % (self
.nom
)
670 class Evenement(Evenement_
):
671 __doc__
= Evenement_
.__doc__
674 class EvenementRemuneration_(RemunerationMixin
):
675 """Structure de rémunération liée à un Evenement qui remplace
676 temporairement la Remuneration normale d'un Dossier, pour toute la durée
679 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
681 verbose_name
="Évènement")
682 # TODO : le champ dossier hérité de Remuneration doit être dérivé
683 # de l'Evenement associé
687 ordering
= ['evenement', 'type__nom', '-date_fin']
688 verbose_name
= "Évènement - rémunération"
689 verbose_name_plural
= "Évènements - rémunérations"
692 class EvenementRemuneration(EvenementRemuneration_
):
693 __doc__
= EvenementRemuneration_
.__doc__
699 class EvenementRemuneration(EvenementRemuneration_
):
700 __doc__
= EvenementRemuneration_
.__doc__
705 class FamilleEmploi(AUFMetadata
):
706 """Catégorie utilisée dans la gestion des Postes.
707 Catégorie supérieure à TypePoste.
709 nom
= models
.CharField(max_length
=255)
713 verbose_name
= "Famille d'emploi"
714 verbose_name_plural
= "Familles d'emploi"
716 def __unicode__(self
):
717 return u
'%s' % (self
.nom
)
719 class TypePoste(AUFMetadata
):
720 """Catégorie de Poste.
722 nom
= models
.CharField(max_length
=255)
723 nom_feminin
= models
.CharField(max_length
=255,
724 verbose_name
="Nom féminin")
726 is_responsable
= models
.BooleanField(default
=False,
727 verbose_name
="Poste de responsabilité")
728 famille_emploi
= models
.ForeignKey('FamilleEmploi',
729 db_column
='famille_emploi',
731 verbose_name
="Famille d'emploi")
735 verbose_name
= "Type de poste"
736 verbose_name_plural
= "Types de poste"
738 def __unicode__(self
):
739 return u
'%s' % (self
.nom
)
742 TYPE_PAIEMENT_CHOICES
= (
743 ('Régulier', 'Régulier'),
744 ('Ponctuel', 'Ponctuel'),
747 NATURE_REMUNERATION_CHOICES
= (
748 ('Accessoire', 'Accessoire'),
749 ('Charges', 'Charges'),
750 ('Indemnité', 'Indemnité'),
751 ('RAS', 'Rémunération autre source'),
752 ('Traitement', 'Traitement'),
755 class TypeRemuneration(AUFMetadata
):
756 """Catégorie de Remuneration.
758 nom
= models
.CharField(max_length
=255)
759 type_paiement
= models
.CharField(max_length
=30,
760 choices
=TYPE_PAIEMENT_CHOICES
,
761 verbose_name
="Type de paiement")
762 nature_remuneration
= models
.CharField(max_length
=30,
763 choices
=NATURE_REMUNERATION_CHOICES
,
764 verbose_name
="Nature de la rémunération")
768 verbose_name
= "Type de rémunération"
769 verbose_name_plural
= "Types de rémunération"
771 def __unicode__(self
):
772 return u
'%s' % (self
.nom
)
774 class TypeRevalorisation(AUFMetadata
):
775 """Justification du changement de la Remuneration.
776 (Actuellement utilisé dans aucun traitement informatique.)
778 nom
= models
.CharField(max_length
=255)
782 verbose_name
= "Type de revalorisation"
783 verbose_name_plural
= "Types de revalorisation"
785 def __unicode__(self
):
786 return u
'%s' % (self
.nom
)
788 class Service(AUFMetadata
):
789 """Unité administrative où les Postes sont rattachés.
791 nom
= models
.CharField(max_length
=255)
795 verbose_name
= "Service"
796 verbose_name_plural
= "Services"
798 def __unicode__(self
):
799 return u
'%s' % (self
.nom
)
802 TYPE_ORGANISME_CHOICES
= (
803 ('MAD', 'Mise à disposition'),
804 ('DET', 'Détachement'),
807 class OrganismeBstg(AUFMetadata
):
808 """Organisation d'où provient un Employe mis à disposition (MAD) de
809 ou détaché (DET) à l'AUF à titre gratuit.
811 (BSTG = bien et service à titre gratuit.)
813 nom
= models
.CharField(max_length
=255)
814 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
815 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
817 related_name
='organismes_bstg',
818 null
=True, blank
=True)
821 ordering
= ['type', 'nom']
822 verbose_name
= "Organisme BSTG"
823 verbose_name_plural
= "Organismes BSTG"
825 def __unicode__(self
):
826 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
828 class Statut(AUFMetadata
):
829 """Statut de l'Employe dans le cadre d'un Dossier particulier.
832 code
= models
.CharField(max_length
=25, unique
=True)
833 nom
= models
.CharField(max_length
=255)
837 verbose_name
= "Statut d'employé"
838 verbose_name_plural
= "Statuts d'employé"
840 def __unicode__(self
):
841 return u
'%s : %s' % (self
.code
, self
.nom
)
844 TYPE_CLASSEMENT_CHOICES
= (
846 ('T', 'T - Technicien'),
847 ('P', 'P - Professionel'),
849 ('D', 'D - Direction'),
850 ('SO', 'SO - Sans objet [expatriés]'),
851 ('HG', 'HG - Hors grille [direction]'),
855 class Classement_(AUFMetadata
):
856 """Éléments de classement de la
857 "Grille générique de classement hiérarchique".
859 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
860 classement dans la grille. Le classement donne le coefficient utilisé dans:
862 salaire de base = coefficient * valeur du point de l'Implantation du Poste
865 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
866 echelon
= models
.IntegerField(verbose_name
="Échelon")
867 degre
= models
.IntegerField(verbose_name
="Degré")
868 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient",
871 # annee # au lieu de date_debut et date_fin
872 commentaire
= models
.TextField(null
=True, blank
=True)
876 ordering
= ['type','echelon','degre','coefficient']
877 verbose_name
= "Classement"
878 verbose_name_plural
= "Classements"
880 def __unicode__(self
):
881 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
884 class Classement(Classement_
):
885 __doc__
= Classement_
.__doc__
888 class TauxChange_(AUFMetadata
):
889 """Taux de change de la devise vers l'euro (EUR)
890 pour chaque année budgétaire.
893 devise
= models
.ForeignKey('Devise', db_column
='devise',
895 annee
= models
.IntegerField(verbose_name
="Année")
896 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
900 ordering
= ['-annee', 'devise__code']
901 verbose_name
= "Taux de change"
902 verbose_name_plural
= "Taux de change"
904 def __unicode__(self
):
905 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
908 class TauxChange(TauxChange_
):
909 __doc__
= TauxChange_
.__doc__
911 class ValeurPointManager(NoDeleteManager
):
912 def get_query_set(self
):
913 return super(ValeurPointManager
, self
).get_query_set().select_related('devise', 'implantation')
916 class ValeurPoint_(AUFMetadata
):
917 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
918 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
919 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
921 salaire de base = coefficient * valeur du point de l'Implantation du Poste
924 objects
= ValeurPointManager()
926 valeur
= models
.FloatField(null
=True)
927 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
928 related_name
='+', default
=5)
929 implantation
= models
.ForeignKey(ref
.Implantation
,
930 db_column
='implantation',
931 related_name
='%(app_label)s_valeur_point')
933 annee
= models
.IntegerField()
936 ordering
= ['-annee', 'implantation__nom']
938 verbose_name
= "Valeur du point"
939 verbose_name_plural
= "Valeurs du point"
941 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
942 def get_tauxchange_courant(self
):
944 Recherche le taux courant associé à la valeur d'un point.
945 Tous les taux de l'année courante sont chargés, pour optimiser un
946 affichage en liste. (On pourrait probablement améliorer le manager pour
947 lui greffer le taux courant sous forme de JOIN)
949 for tauxchange
in self
.tauxchange
:
950 if tauxchange
.implantation_id
== self
.implantation_id
:
954 def __unicode__(self
):
955 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
958 class ValeurPoint(ValeurPoint_
):
959 __doc__
= ValeurPoint_
.__doc__
962 class Devise(AUFMetadata
):
965 code
= models
.CharField(max_length
=10, unique
=True)
966 nom
= models
.CharField(max_length
=255)
970 verbose_name
= "Devise"
971 verbose_name_plural
= "Devises"
973 def __unicode__(self
):
974 return u
'%s - %s' % (self
.code
, self
.nom
)
976 class TypeContrat(AUFMetadata
):
979 nom
= models
.CharField(max_length
=255)
980 nom_long
= models
.CharField(max_length
=255)
984 verbose_name
= "Type de contrat"
985 verbose_name_plural
= "Types de contrat"
987 def __unicode__(self
):
988 return u
'%s' % (self
.nom
)
993 class ResponsableImplantation(AUFMetadata
):
994 """Le responsable d'une implantation.
995 Anciennement géré sur le Dossier du responsable.
997 employe
= models
.ForeignKey('Employe', db_column
='employe',
999 null
=True, blank
=True)
1000 implantation
= models
.ForeignKey(ref
.Implantation
,
1001 db_column
='implantation', related_name
='+',
1004 def __unicode__(self
):
1005 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1008 ordering
= ['implantation__nom']
1009 verbose_name
= "Responsable d'implantation"
1010 verbose_name_plural
= "Responsables d'implantation"