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
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 null
=True, blank
=True)
294 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
297 situation_famille
= models
.CharField(max_length
=1,
298 choices
=SITUATION_CHOICES
,
299 verbose_name
="Situation familiale",
300 null
=True, blank
=True)
301 date_entree
= models
.DateField(verbose_name
="Date d'entrée à l'AUF",
302 help_text
=HELP_TEXT_DATE
,
303 null
=True, blank
=True)
306 tel_domicile
= models
.CharField(max_length
=255,
307 verbose_name
="Tél. domicile",
308 null
=True, blank
=True)
309 tel_cellulaire
= models
.CharField(max_length
=255,
310 verbose_name
="Tél. cellulaire",
311 null
=True, blank
=True)
312 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
313 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
314 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
315 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
316 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
317 related_name
='employes',
318 null
=True, blank
=True)
321 ordering
= ['nom_affichage','nom','prenom']
322 verbose_name
= "Employé"
323 verbose_name_plural
= "Employés"
325 def __unicode__(self
):
326 return u
'%s' % (self
.get_nom())
329 nom_affichage
= self
.nom_affichage
330 if not nom_affichage
:
331 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
336 if self
.genre
.upper() == u
'M':
338 elif self
.genre
.upper() == u
'F':
342 class EmployePiece(models
.Model
):
343 """Documents relatifs à un employé.
346 employe
= models
.ForeignKey('Employe', db_column
='employe',
348 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
349 fichier
= models
.FileField(verbose_name
="Fichier",
350 upload_to
=dossier_piece_dispatch
,
351 storage
=storage_prive
)
356 def __unicode__(self
):
357 return u
'%s' % (self
.nom
)
359 class EmployeCommentaire(Commentaire
):
360 employe
= models
.ForeignKey('Employe', db_column
='employe',
364 LIEN_PARENTE_CHOICES
= (
365 ('Conjoint', 'Conjoint'),
366 ('Conjointe', 'Conjointe'),
371 class AyantDroit(AUFMetadata
):
372 """Personne en relation avec un Employe.
375 nom
= models
.CharField(max_length
=255)
376 prenom
= models
.CharField(max_length
=255,
377 verbose_name
="Prénom",)
378 nom_affichage
= models
.CharField(max_length
=255,
379 verbose_name
="Nom d'affichage",
380 null
=True, blank
=True)
381 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
382 db_column
='nationalite',
383 related_name
='ayantdroits_nationalite',
384 verbose_name
="Nationalité")
385 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
386 verbose_name
="Date de naissance",
387 null
=True, blank
=True)
388 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
391 employe
= models
.ForeignKey('Employe', db_column
='employe',
392 related_name
='ayantdroits',
393 verbose_name
="Employé")
394 lien_parente
= models
.CharField(max_length
=10,
395 choices
=LIEN_PARENTE_CHOICES
,
396 verbose_name
="Lien de parenté",
397 null
=True, blank
=True)
400 ordering
= ['nom_affichage']
401 verbose_name
= "Ayant droit"
402 verbose_name_plural
= "Ayants droit"
404 def __unicode__(self
):
405 return u
'%s' % (self
.get_nom())
408 nom_affichage
= self
.nom_affichage
409 if not nom_affichage
:
410 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
413 class AyantDroitCommentaire(Commentaire
):
414 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
420 STATUT_RESIDENCE_CHOICES
= (
422 ('expat', 'Expatrié'),
425 COMPTE_COMPTA_CHOICES
= (
431 class Dossier_(AUFMetadata
):
432 """Le Dossier regroupe les informations relatives à l'occupation
433 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
436 Plusieurs Contrats peuvent être associés au Dossier.
437 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
438 lequel aucun Dossier n'existe est un poste vacant.
441 employe
= models
.ForeignKey('Employe', db_column
='employe',
442 related_name
='dossiers',
443 verbose_name
="Employé")
444 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
445 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3,
447 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
448 db_column
='organisme_bstg',
450 verbose_name
="Organisme",
451 help_text
="Si détaché (DET) ou \
452 mis à disposition (MAD), \
453 préciser l'organisme.",
454 null
=True, blank
=True)
457 remplacement
= models
.BooleanField(default
=False)
458 statut_residence
= models
.CharField(max_length
=10, default
='local',
459 verbose_name
="Statut", null
=True,
460 choices
=STATUT_RESIDENCE_CHOICES
)
463 classement
= models
.ForeignKey('Classement', db_column
='classement',
465 null
=True, blank
=True)
466 regime_travail
= models
.DecimalField(max_digits
=12, null
=True,
468 default
=REGIME_TRAVAIL_DEFAULT
,
469 verbose_name
="Régime de travail",
470 help_text
="% du temps complet")
471 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
472 decimal_places
=2, null
=True,
473 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
474 verbose_name
="Nb. heures par semaine")
476 # Occupation du Poste par cet Employe (anciennement "mandat")
477 date_debut
= models
.DateField(verbose_name
="Date de début d'occupation \
479 help_text
=HELP_TEXT_DATE
)
480 date_fin
= models
.DateField(verbose_name
="Date de fin d'occupation \
482 help_text
=HELP_TEXT_DATE
,
483 null
=True, blank
=True)
490 ordering
= ['employe__nom_affichage', 'employe__nom', 'poste__nom']
491 verbose_name
= "Dossier"
492 verbose_name_plural
= "Dossiers"
494 def __unicode__(self
):
495 poste
= self
.poste
.nom
496 if self
.employe
.genre
== 'F':
497 poste
= self
.poste
.nom_feminin
498 return u
'%s - %s' % (self
.employe
, poste
)
501 class Dossier(Dossier_
):
502 __doc__
= Dossier_
.__doc__
505 class DossierPiece(models
.Model
):
506 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
507 Ex.: Lettre de motivation.
509 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
511 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
512 fichier
= models
.FileField(verbose_name
="Fichier",
513 upload_to
=dossier_piece_dispatch
,
514 storage
=storage_prive
)
519 def __unicode__(self
):
520 return u
'%s' % (self
.nom
)
522 class DossierCommentaire(Commentaire
):
523 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
529 class RemunerationMixin(AUFMetadata
):
531 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
532 related_name
='%(app_label)s_%(class)s_remunerations')
533 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
535 verbose_name
="Type de rémunération")
536 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
537 db_column
='type_revalorisation',
539 verbose_name
="Type de revalorisation",
540 null
=True, blank
=True)
541 montant
= models
.FloatField(null
=True, blank
=True,
543 # Annuel (12 mois, 52 semaines, 364 jours?)
544 devise
= models
.ForeignKey('Devise', to_field
='id',
545 db_column
='devise', related_name
='+',
547 # commentaire = precision
548 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
549 # date_debut = anciennement date_effectif
550 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
551 verbose_name
="Date de début",
552 null
=True, blank
=True)
553 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
554 verbose_name
="Date de fin",
555 null
=True, blank
=True)
559 ordering
= ['type__nom', '-date_fin']
561 def __unicode__(self
):
562 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
564 class Remuneration_(RemunerationMixin
):
565 """Structure de rémunération (données budgétaires) en situation normale
566 pour un Dossier. Si un Evenement existe, utiliser la structure de
567 rémunération EvenementRemuneration de cet événement.
570 def montant_mois(self
):
571 return round(self
.montant
/ 12, 2)
573 def taux_devise(self
):
574 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
576 def montant_euro(self
):
577 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
579 def montant_euro_mois(self
):
580 return round(self
.montant_euro() / 12, 2)
582 def __unicode__(self
):
584 devise
= self
.devise
.code
587 return "%s %s" % (self
.montant
, devise
)
591 verbose_name
= "Rémunération"
592 verbose_name_plural
= "Rémunérations"
595 class Remuneration(Remuneration_
):
596 __doc__
= Remuneration_
.__doc__
601 class ContratManager(NoDeleteManager
):
602 def get_query_set(self
):
603 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
606 class Contrat(AUFMetadata
):
607 """Document juridique qui encadre la relation de travail d'un Employe
608 pour un Poste particulier. Pour un Dossier (qui documente cette
609 relation de travail) plusieurs contrats peuvent être associés.
612 objects
= ContratManager()
614 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
616 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
618 verbose_name
="Type de contrat")
619 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
620 verbose_name
="Date de début")
621 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
622 verbose_name
="Date de fin",
623 null
=True, blank
=True)
626 ordering
= ['dossier__employe__nom_affichage']
627 verbose_name
= "Contrat"
628 verbose_name_plural
= "Contrats"
630 def __unicode__(self
):
631 return u
'%s - %s' % (self
.dossier
, self
.id)
633 # TODO? class ContratPiece(models.Model):
638 class Evenement_(AUFMetadata
):
639 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
640 d'un Dossier qui vient altérer des informations normales liées à un Dossier
641 (ex.: la Remuneration).
643 Ex.: congé de maternité, maladie...
645 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
646 différent et une rémunération en conséquence. On souhaite toutefois
647 conserver le Dossier intact afin d'éviter une re-saisie des données lors
648 du retour à la normale.
650 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
652 nom
= models
.CharField(max_length
=255)
653 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
654 verbose_name
="Date de début")
655 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
656 verbose_name
="Date de fin",
657 null
=True, blank
=True)
662 verbose_name
= "Évènement"
663 verbose_name_plural
= "Évènements"
665 def __unicode__(self
):
666 return u
'%s' % (self
.nom
)
669 class Evenement(Evenement_
):
670 __doc__
= Evenement_
.__doc__
673 class EvenementRemuneration_(RemunerationMixin
):
674 """Structure de rémunération liée à un Evenement qui remplace
675 temporairement la Remuneration normale d'un Dossier, pour toute la durée
678 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
680 verbose_name
="Évènement")
681 # TODO : le champ dossier hérité de Remuneration doit être dérivé
682 # de l'Evenement associé
686 ordering
= ['evenement', 'type__nom', '-date_fin']
687 verbose_name
= "Évènement - rémunération"
688 verbose_name_plural
= "Évènements - rémunérations"
691 class EvenementRemuneration(EvenementRemuneration_
):
692 __doc__
= EvenementRemuneration_
.__doc__
698 class EvenementRemuneration(EvenementRemuneration_
):
699 __doc__
= EvenementRemuneration_
.__doc__
704 class FamilleEmploi(AUFMetadata
):
705 """Catégorie utilisée dans la gestion des Postes.
706 Catégorie supérieure à TypePoste.
708 nom
= models
.CharField(max_length
=255)
712 verbose_name
= "Famille d'emploi"
713 verbose_name_plural
= "Familles d'emploi"
715 def __unicode__(self
):
716 return u
'%s' % (self
.nom
)
718 class TypePoste(AUFMetadata
):
719 """Catégorie de Poste.
721 nom
= models
.CharField(max_length
=255)
722 nom_feminin
= models
.CharField(max_length
=255,
723 verbose_name
="Nom féminin")
725 is_responsable
= models
.BooleanField(default
=False,
726 verbose_name
="Poste de responsabilité")
727 famille_emploi
= models
.ForeignKey('FamilleEmploi',
728 db_column
='famille_emploi',
730 verbose_name
="Famille d'emploi")
734 verbose_name
= "Type de poste"
735 verbose_name_plural
= "Types de poste"
737 def __unicode__(self
):
738 return u
'%s' % (self
.nom
)
741 TYPE_PAIEMENT_CHOICES
= (
742 ('Régulier', 'Régulier'),
743 ('Ponctuel', 'Ponctuel'),
746 NATURE_REMUNERATION_CHOICES
= (
747 ('Accessoire', 'Accessoire'),
748 ('Charges', 'Charges'),
749 ('Indemnité', 'Indemnité'),
750 ('RAS', 'Rémunération autre source'),
751 ('Traitement', 'Traitement'),
754 class TypeRemuneration(AUFMetadata
):
755 """Catégorie de Remuneration.
757 nom
= models
.CharField(max_length
=255)
758 type_paiement
= models
.CharField(max_length
=30,
759 choices
=TYPE_PAIEMENT_CHOICES
,
760 verbose_name
="Type de paiement")
761 nature_remuneration
= models
.CharField(max_length
=30,
762 choices
=NATURE_REMUNERATION_CHOICES
,
763 verbose_name
="Nature de la rémunération")
767 verbose_name
= "Type de rémunération"
768 verbose_name_plural
= "Types de rémunération"
770 def __unicode__(self
):
771 return u
'%s' % (self
.nom
)
773 class TypeRevalorisation(AUFMetadata
):
774 """Justification du changement de la Remuneration.
775 (Actuellement utilisé dans aucun traitement informatique.)
777 nom
= models
.CharField(max_length
=255)
781 verbose_name
= "Type de revalorisation"
782 verbose_name_plural
= "Types de revalorisation"
784 def __unicode__(self
):
785 return u
'%s' % (self
.nom
)
787 class Service(AUFMetadata
):
788 """Unité administrative où les Postes sont rattachés.
790 nom
= models
.CharField(max_length
=255)
794 verbose_name
= "Service"
795 verbose_name_plural
= "Services"
797 def __unicode__(self
):
798 return u
'%s' % (self
.nom
)
801 TYPE_ORGANISME_CHOICES
= (
802 ('MAD', 'Mise à disposition'),
803 ('DET', 'Détachement'),
806 class OrganismeBstg(AUFMetadata
):
807 """Organisation d'où provient un Employe mis à disposition (MAD) de
808 ou détaché (DET) à l'AUF à titre gratuit.
810 (BSTG = bien et service à titre gratuit.)
812 nom
= models
.CharField(max_length
=255)
813 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
814 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
816 related_name
='organismes_bstg',
817 null
=True, blank
=True)
820 ordering
= ['type', 'nom']
821 verbose_name
= "Organisme BSTG"
822 verbose_name_plural
= "Organismes BSTG"
824 def __unicode__(self
):
825 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
827 class Statut(AUFMetadata
):
828 """Statut de l'Employe dans le cadre d'un Dossier particulier.
831 code
= models
.CharField(max_length
=25, unique
=True)
832 nom
= models
.CharField(max_length
=255)
836 verbose_name
= "Statut d'employé"
837 verbose_name_plural
= "Statuts d'employé"
839 def __unicode__(self
):
840 return u
'%s : %s' % (self
.code
, self
.nom
)
843 TYPE_CLASSEMENT_CHOICES
= (
845 ('T', 'T - Technicien'),
846 ('P', 'P - Professionel'),
848 ('D', 'D - Direction'),
849 ('SO', 'SO - Sans objet [expatriés]'),
850 ('HG', 'HG - Hors grille [direction]'),
854 class Classement_(AUFMetadata
):
855 """Éléments de classement de la
856 "Grille générique de classement hiérarchique".
858 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
859 classement dans la grille. Le classement donne le coefficient utilisé dans:
861 salaire de base = coefficient * valeur du point de l'Implantation du Poste
864 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
865 echelon
= models
.IntegerField(verbose_name
="Échelon")
866 degre
= models
.IntegerField(verbose_name
="Degré")
867 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient",
870 # annee # au lieu de date_debut et date_fin
871 commentaire
= models
.TextField(null
=True, blank
=True)
875 ordering
= ['type','echelon','degre','coefficient']
876 verbose_name
= "Classement"
877 verbose_name_plural
= "Classements"
879 def __unicode__(self
):
880 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
883 class Classement(Classement_
):
884 __doc__
= Classement_
.__doc__
887 class TauxChange_(AUFMetadata
):
888 """Taux de change de la devise vers l'euro (EUR)
889 pour chaque année budgétaire.
892 devise
= models
.ForeignKey('Devise', db_column
='devise',
894 annee
= models
.IntegerField(verbose_name
="Année")
895 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
899 ordering
= ['-annee', 'devise__code']
900 verbose_name
= "Taux de change"
901 verbose_name_plural
= "Taux de change"
903 def __unicode__(self
):
904 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
907 class TauxChange(TauxChange_
):
908 __doc__
= TauxChange_
.__doc__
910 class ValeurPointManager(NoDeleteManager
):
911 def get_query_set(self
):
912 return super(ValeurPointManager
, self
).get_query_set().select_related('devise', 'implantation')
915 class ValeurPoint_(AUFMetadata
):
916 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
917 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
918 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
920 salaire de base = coefficient * valeur du point de l'Implantation du Poste
923 objects
= ValeurPointManager()
925 valeur
= models
.FloatField(null
=True)
926 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
927 related_name
='+', default
=5)
928 implantation
= models
.ForeignKey(ref
.Implantation
,
929 db_column
='implantation',
930 related_name
='%(app_label)s_valeur_point')
932 annee
= models
.IntegerField()
935 ordering
= ['-annee', 'implantation__nom']
937 verbose_name
= "Valeur du point"
938 verbose_name_plural
= "Valeurs du point"
940 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
941 def get_tauxchange_courant(self
):
943 Recherche le taux courant associé à la valeur d'un point.
944 Tous les taux de l'année courante sont chargés, pour optimiser un
945 affichage en liste. (On pourrait probablement améliorer le manager pour
946 lui greffer le taux courant sous forme de JOIN)
948 for tauxchange
in self
.tauxchange
:
949 if tauxchange
.implantation_id
== self
.implantation_id
:
953 def __unicode__(self
):
954 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
957 class ValeurPoint(ValeurPoint_
):
958 __doc__
= ValeurPoint_
.__doc__
961 class Devise(AUFMetadata
):
964 code
= models
.CharField(max_length
=10, unique
=True)
965 nom
= models
.CharField(max_length
=255)
969 verbose_name
= "Devise"
970 verbose_name_plural
= "Devises"
972 def __unicode__(self
):
973 return u
'%s - %s' % (self
.code
, self
.nom
)
975 class TypeContrat(AUFMetadata
):
978 nom
= models
.CharField(max_length
=255)
979 nom_long
= models
.CharField(max_length
=255)
983 verbose_name
= "Type de contrat"
984 verbose_name_plural
= "Types de contrat"
986 def __unicode__(self
):
987 return u
'%s' % (self
.nom
)
992 class ResponsableImplantation(AUFMetadata
):
993 """Le responsable d'une implantation.
994 Anciennement géré sur le Dossier du responsable.
996 employe
= models
.ForeignKey('Employe', db_column
='employe',
998 null
=True, blank
=True)
999 implantation
= models
.ForeignKey(ref
.Implantation
,
1000 db_column
='implantation', related_name
='+',
1003 def __unicode__(self
):
1004 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1007 ordering
= ['implantation__nom']
1008 verbose_name
= "Responsable d'implantation"
1009 verbose_name_plural
= "Responsables d'implantation"