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
)
334 class EmployePiece(models
.Model
):
335 """Documents relatifs à un employé.
338 employe
= models
.ForeignKey('Employe', db_column
='employe',
340 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
341 fichier
= models
.FileField(verbose_name
="Fichier",
342 upload_to
=dossier_piece_dispatch
,
343 storage
=storage_prive
)
348 def __unicode__(self
):
349 return u
'%s' % (self
.nom
)
351 class EmployeCommentaire(Commentaire
):
352 employe
= models
.ForeignKey('Employe', db_column
='employe',
356 LIEN_PARENTE_CHOICES
= (
357 ('Conjoint', 'Conjoint'),
358 ('Conjointe', 'Conjointe'),
363 class AyantDroit(AUFMetadata
):
364 """Personne en relation avec un Employe.
367 nom
= models
.CharField(max_length
=255)
368 prenom
= models
.CharField(max_length
=255,
369 verbose_name
="Prénom",)
370 nom_affichage
= models
.CharField(max_length
=255,
371 verbose_name
="Nom d'affichage",
372 null
=True, blank
=True)
373 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
374 db_column
='nationalite',
375 related_name
='ayantdroits_nationalite',
376 verbose_name
="Nationalité")
377 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
378 verbose_name
="Date de naissance",
379 null
=True, blank
=True)
380 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
383 employe
= models
.ForeignKey('Employe', db_column
='employe',
384 related_name
='ayantdroits',
385 verbose_name
="Employé")
386 lien_parente
= models
.CharField(max_length
=10,
387 choices
=LIEN_PARENTE_CHOICES
,
388 verbose_name
="Lien de parenté",
389 null
=True, blank
=True)
392 ordering
= ['nom_affichage']
393 verbose_name
= "Ayant droit"
394 verbose_name_plural
= "Ayants droit"
396 def __unicode__(self
):
397 return u
'%s' % (self
.get_nom())
400 nom_affichage
= self
.nom_affichage
401 if not nom_affichage
:
402 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
405 class AyantDroitCommentaire(Commentaire
):
406 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
412 STATUT_RESIDENCE_CHOICES
= (
414 ('expat', 'Expatrié'),
417 COMPTE_COMPTA_CHOICES
= (
423 class Dossier_(AUFMetadata
):
424 """Le Dossier regroupe les informations relatives à l'occupation
425 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
428 Plusieurs Contrats peuvent être associés au Dossier.
429 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
430 lequel aucun Dossier n'existe est un poste vacant.
433 employe
= models
.ForeignKey('Employe', db_column
='employe',
435 verbose_name
="Employé")
436 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
437 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3,
439 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
440 db_column
='organisme_bstg',
442 verbose_name
="Organisme",
443 help_text
="Si détaché (DET) ou \
444 mis à disposition (MAD), \
445 préciser l'organisme.",
446 null
=True, blank
=True)
449 remplacement
= models
.BooleanField(default
=False)
450 statut_residence
= models
.CharField(max_length
=10, default
='local',
451 verbose_name
="Statut", null
=True,
452 choices
=STATUT_RESIDENCE_CHOICES
)
455 classement
= models
.ForeignKey('Classement', db_column
='classement',
457 null
=True, blank
=True)
458 regime_travail
= models
.DecimalField(max_digits
=12, null
=True,
460 default
=REGIME_TRAVAIL_DEFAULT
,
461 verbose_name
="Régime de travail",
462 help_text
="% du temps complet")
463 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
464 decimal_places
=2, null
=True,
465 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
466 verbose_name
="Nb. heures par semaine")
468 # Occupation du Poste par cet Employe (anciennement "mandat")
469 date_debut
= models
.DateField(verbose_name
="Date de début d'occupation \
471 help_text
=HELP_TEXT_DATE
)
472 date_fin
= models
.DateField(verbose_name
="Date de fin d'occupation \
474 help_text
=HELP_TEXT_DATE
,
475 null
=True, blank
=True)
482 ordering
= ['employe__nom_affichage', 'employe__nom', 'poste__nom']
483 verbose_name
= "Dossier"
484 verbose_name_plural
= "Dossiers"
486 def __unicode__(self
):
487 poste
= self
.poste
.nom
488 if self
.employe
.genre
== 'F':
489 poste
= self
.poste
.nom_feminin
490 return u
'%s - %s' % (self
.employe
, poste
)
493 class Dossier(Dossier_
):
494 __doc__
= Dossier_
.__doc__
498 class Dossier(Dossier_
):
499 __doc__
= Dossier_
.__doc__
502 class DossierPiece(models
.Model
):
503 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
504 Ex.: Lettre de motivation.
506 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
508 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
509 fichier
= models
.FileField(verbose_name
="Fichier",
510 upload_to
=dossier_piece_dispatch
,
511 storage
=storage_prive
)
516 def __unicode__(self
):
517 return u
'%s' % (self
.nom
)
519 class DossierCommentaire(Commentaire
):
520 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
526 class RemunerationMixin(AUFMetadata
):
528 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
529 related_name
='%(app_label)s_%(class)s_remunerations')
530 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
532 verbose_name
="Type de rémunération")
533 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
534 db_column
='type_revalorisation',
536 verbose_name
="Type de revalorisation",
537 null
=True, blank
=True)
538 montant
= models
.FloatField(null
=True, blank
=True,
540 # Annuel (12 mois, 52 semaines, 364 jours?)
541 devise
= models
.ForeignKey('Devise', to_field
='id',
542 db_column
='devise', related_name
='+',
544 # commentaire = precision
545 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
546 # date_debut = anciennement date_effectif
547 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
548 verbose_name
="Date de début",
549 null
=True, blank
=True)
550 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
551 verbose_name
="Date de fin",
552 null
=True, blank
=True)
556 ordering
= ['type__nom', '-date_fin']
558 def __unicode__(self
):
559 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
561 class Remuneration_(RemunerationMixin
):
562 """Structure de rémunération (données budgétaires) en situation normale
563 pour un Dossier. Si un Evenement existe, utiliser la structure de
564 rémunération EvenementRemuneration de cet événement.
567 def montant_mois(self
):
568 return round(self
.montant
/ 12, 2)
570 def taux_devise(self
):
571 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
573 def montant_euro(self
):
574 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
576 def montant_euro_mois(self
):
577 return round(self
.montant_euro() / 12, 2)
579 def __unicode__(self
):
581 devise
= self
.devise
.code
584 return "%s %s" % (self
.montant
, devise
)
588 verbose_name
= "Rémunération"
589 verbose_name_plural
= "Rémunérations"
592 class Remuneration(Remuneration_
):
593 __doc__
= Remuneration_
.__doc__
598 class ContratManager(NoDeleteManager
):
599 def get_query_set(self
):
600 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
603 class Contrat(AUFMetadata
):
604 """Document juridique qui encadre la relation de travail d'un Employe
605 pour un Poste particulier. Pour un Dossier (qui documente cette
606 relation de travail) plusieurs contrats peuvent être associés.
609 objects
= ContratManager()
611 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
613 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
615 verbose_name
="Type de contrat")
616 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
617 verbose_name
="Date de début")
618 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
619 verbose_name
="Date de fin",
620 null
=True, blank
=True)
623 ordering
= ['dossier__employe__nom_affichage']
624 verbose_name
= "Contrat"
625 verbose_name_plural
= "Contrats"
627 def __unicode__(self
):
628 return u
'%s - %s' % (self
.dossier
, self
.id)
630 # TODO? class ContratPiece(models.Model):
635 class Evenement_(AUFMetadata
):
636 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
637 d'un Dossier qui vient altérer des informations normales liées à un Dossier
638 (ex.: la Remuneration).
640 Ex.: congé de maternité, maladie...
642 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
643 différent et une rémunération en conséquence. On souhaite toutefois
644 conserver le Dossier intact afin d'éviter une re-saisie des données lors
645 du retour à la normale.
647 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
649 nom
= models
.CharField(max_length
=255)
650 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
651 verbose_name
="Date de début")
652 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
653 verbose_name
="Date de fin",
654 null
=True, blank
=True)
659 verbose_name
= "Évènement"
660 verbose_name_plural
= "Évènements"
662 def __unicode__(self
):
663 return u
'%s' % (self
.nom
)
666 class Evenement(Evenement_
):
667 __doc__
= Evenement_
.__doc__
670 class EvenementRemuneration_(RemunerationMixin
):
671 """Structure de rémunération liée à un Evenement qui remplace
672 temporairement la Remuneration normale d'un Dossier, pour toute la durée
675 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
677 verbose_name
="Évènement")
678 # TODO : le champ dossier hérité de Remuneration doit être dérivé
679 # de l'Evenement associé
683 ordering
= ['evenement', 'type__nom', '-date_fin']
684 verbose_name
= "Évènement - rémunération"
685 verbose_name_plural
= "Évènements - rémunérations"
688 class EvenementRemuneration(EvenementRemuneration_
):
689 __doc__
= EvenementRemuneration_
.__doc__
695 class EvenementRemuneration(EvenementRemuneration_
):
696 __doc__
= EvenementRemuneration_
.__doc__
701 class FamilleEmploi(AUFMetadata
):
702 """Catégorie utilisée dans la gestion des Postes.
703 Catégorie supérieure à TypePoste.
705 nom
= models
.CharField(max_length
=255)
709 verbose_name
= "Famille d'emploi"
710 verbose_name_plural
= "Familles d'emploi"
712 def __unicode__(self
):
713 return u
'%s' % (self
.nom
)
715 class TypePoste(AUFMetadata
):
716 """Catégorie de Poste.
718 nom
= models
.CharField(max_length
=255)
719 nom_feminin
= models
.CharField(max_length
=255,
720 verbose_name
="Nom féminin")
722 is_responsable
= models
.BooleanField(default
=False,
723 verbose_name
="Poste de responsabilité")
724 famille_emploi
= models
.ForeignKey('FamilleEmploi',
725 db_column
='famille_emploi',
727 verbose_name
="Famille d'emploi")
731 verbose_name
= "Type de poste"
732 verbose_name_plural
= "Types de poste"
734 def __unicode__(self
):
735 return u
'%s' % (self
.nom
)
738 TYPE_PAIEMENT_CHOICES
= (
739 ('Régulier', 'Régulier'),
740 ('Ponctuel', 'Ponctuel'),
743 NATURE_REMUNERATION_CHOICES
= (
744 ('Accessoire', 'Accessoire'),
745 ('Charges', 'Charges'),
746 ('Indemnité', 'Indemnité'),
747 ('RAS', 'Rémunération autre source'),
748 ('Traitement', 'Traitement'),
751 class TypeRemuneration(AUFMetadata
):
752 """Catégorie de Remuneration.
754 nom
= models
.CharField(max_length
=255)
755 type_paiement
= models
.CharField(max_length
=30,
756 choices
=TYPE_PAIEMENT_CHOICES
,
757 verbose_name
="Type de paiement")
758 nature_remuneration
= models
.CharField(max_length
=30,
759 choices
=NATURE_REMUNERATION_CHOICES
,
760 verbose_name
="Nature de la rémunération")
764 verbose_name
= "Type de rémunération"
765 verbose_name_plural
= "Types de rémunération"
767 def __unicode__(self
):
768 return u
'%s' % (self
.nom
)
770 class TypeRevalorisation(AUFMetadata
):
771 """Justification du changement de la Remuneration.
772 (Actuellement utilisé dans aucun traitement informatique.)
774 nom
= models
.CharField(max_length
=255)
778 verbose_name
= "Type de revalorisation"
779 verbose_name_plural
= "Types de revalorisation"
781 def __unicode__(self
):
782 return u
'%s' % (self
.nom
)
784 class Service(AUFMetadata
):
785 """Unité administrative où les Postes sont rattachés.
787 nom
= models
.CharField(max_length
=255)
791 verbose_name
= "Service"
792 verbose_name_plural
= "Services"
794 def __unicode__(self
):
795 return u
'%s' % (self
.nom
)
798 TYPE_ORGANISME_CHOICES
= (
799 ('MAD', 'Mise à disposition'),
800 ('DET', 'Détachement'),
803 class OrganismeBstg(AUFMetadata
):
804 """Organisation d'où provient un Employe mis à disposition (MAD) de
805 ou détaché (DET) à l'AUF à titre gratuit.
807 (BSTG = bien et service à titre gratuit.)
809 nom
= models
.CharField(max_length
=255)
810 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
811 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
813 related_name
='organismes_bstg',
814 null
=True, blank
=True)
817 ordering
= ['type', 'nom']
818 verbose_name
= "Organisme BSTG"
819 verbose_name_plural
= "Organismes BSTG"
821 def __unicode__(self
):
822 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
824 class Statut(AUFMetadata
):
825 """Statut de l'Employe dans le cadre d'un Dossier particulier.
828 code
= models
.CharField(max_length
=25, unique
=True)
829 nom
= models
.CharField(max_length
=255)
833 verbose_name
= "Statut d'employé"
834 verbose_name_plural
= "Statuts d'employé"
836 def __unicode__(self
):
837 return u
'%s : %s' % (self
.code
, self
.nom
)
840 TYPE_CLASSEMENT_CHOICES
= (
842 ('T', 'T - Technicien'),
843 ('P', 'P - Professionel'),
845 ('D', 'D - Direction'),
846 ('SO', 'SO - Sans objet [expatriés]'),
847 ('HG', 'HG - Hors grille [direction]'),
851 class Classement_(AUFMetadata
):
852 """Éléments de classement de la
853 "Grille générique de classement hiérarchique".
855 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
856 classement dans la grille. Le classement donne le coefficient utilisé dans:
858 salaire de base = coefficient * valeur du point de l'Implantation du Poste
861 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
862 echelon
= models
.IntegerField(verbose_name
="Échelon")
863 degre
= models
.IntegerField(verbose_name
="Degré")
864 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient",
867 # annee # au lieu de date_debut et date_fin
868 commentaire
= models
.TextField(null
=True, blank
=True)
872 ordering
= ['type','echelon','degre','coefficient']
873 verbose_name
= "Classement"
874 verbose_name_plural
= "Classements"
876 def __unicode__(self
):
877 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
880 class Classement(Classement_
):
881 __doc__
= Classement_
.__doc__
884 class TauxChange_(AUFMetadata
):
885 """Taux de change de la devise vers l'euro (EUR)
886 pour chaque année budgétaire.
889 devise
= models
.ForeignKey('Devise', db_column
='devise',
891 annee
= models
.IntegerField(verbose_name
="Année")
892 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
896 ordering
= ['-annee', 'devise__code']
897 verbose_name
= "Taux de change"
898 verbose_name_plural
= "Taux de change"
900 def __unicode__(self
):
901 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
904 class TauxChange(TauxChange_
):
905 __doc__
= TauxChange_
.__doc__
908 class ValeurPoint_(AUFMetadata
):
909 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
910 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
911 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
913 salaire de base = coefficient * valeur du point de l'Implantation du Poste
915 valeur
= models
.FloatField(null
=True)
916 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
917 related_name
='+', default
=5)
918 implantation
= models
.ForeignKey(ref
.Implantation
,
919 db_column
='implantation',
920 related_name
='%(app_label)s_valeur_point')
922 annee
= models
.IntegerField()
925 ordering
= ['annee', 'implantation__nom']
928 verbose_name
= "Valeur du point"
929 verbose_name_plural
= "Valeurs du point"
931 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
932 def get_tauxchange_courant(self
):
934 Recherche le taux courant associé à la valeur d'un point.
935 Tous les taux de l'année courante sont chargés, pour optimiser un
936 affichage en liste. (On pourrait probablement améliorer le manager pour
937 lui greffer le taux courant sous forme de JOIN)
939 for tauxchange
in self
.tauxchange
:
940 if tauxchange
.implantation_id
== self
.implantation_id
:
944 def __unicode__(self
):
945 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
948 class ValeurPoint(ValeurPoint_
):
949 __doc__
= ValeurPoint_
.__doc__
952 class Devise(AUFMetadata
):
955 code
= models
.CharField(max_length
=10, unique
=True)
956 nom
= models
.CharField(max_length
=255)
960 verbose_name
= "Devise"
961 verbose_name_plural
= "Devises"
963 def __unicode__(self
):
964 return u
'%s - %s' % (self
.code
, self
.nom
)
966 class TypeContrat(AUFMetadata
):
969 nom
= models
.CharField(max_length
=255)
970 nom_long
= models
.CharField(max_length
=255)
974 verbose_name
= "Type de contrat"
975 verbose_name_plural
= "Types de contrat"
977 def __unicode__(self
):
978 return u
'%s' % (self
.nom
)
983 class ResponsableImplantation(AUFMetadata
):
984 """Le responsable d'une implantation.
985 Anciennement géré sur le Dossier du responsable.
987 employe
= models
.ForeignKey('Employe', db_column
='employe',
989 null
=True, blank
=True)
990 implantation
= models
.ForeignKey(ref
.Implantation
,
991 db_column
='implantation', related_name
='+',
994 def __unicode__(self
):
995 return u
'%s : %s' % (self
.implantation
, self
.employe
)
998 ordering
= ['implantation__nom']
999 verbose_name
= "Responsable d'implantation"
1000 verbose_name_plural
= "Responsables d'implantation"