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)
256 def taux_devise(self
):
257 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.implantation
)
258 if len(liste_taux
) == 0:
259 raise Exception(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.implantation
))
261 return liste_taux
[0].taux
263 def montant_euros(self
):
264 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
267 class PosteCommentaire(Commentaire
):
268 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
277 SITUATION_CHOICES
= (
278 ('C', 'Célibataire'),
283 class Employe(AUFMetadata
):
284 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
285 Dossiers qu'il occupe ou a occupé de Postes.
287 Cette classe aurait pu avantageusement s'appeler Personne car la notion
288 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
291 nom
= models
.CharField(max_length
=255)
292 prenom
= models
.CharField(max_length
=255, verbose_name
="Prénom")
293 nom_affichage
= models
.CharField(max_length
=255,
294 verbose_name
="Nom d'affichage",
295 null
=True, blank
=True)
296 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
297 db_column
='nationalite',
298 related_name
='employes_nationalite',
299 verbose_name
="Nationalité")
300 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
301 verbose_name
="Date de naissance",
302 null
=True, blank
=True)
303 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
306 situation_famille
= models
.CharField(max_length
=1,
307 choices
=SITUATION_CHOICES
,
308 verbose_name
="Situation familiale",
309 null
=True, blank
=True)
310 date_entree
= models
.DateField(verbose_name
="Date d'entrée à l'AUF",
311 help_text
=HELP_TEXT_DATE
,
312 null
=True, blank
=True)
315 tel_domicile
= models
.CharField(max_length
=255,
316 verbose_name
="Tél. domicile",
317 null
=True, blank
=True)
318 tel_cellulaire
= models
.CharField(max_length
=255,
319 verbose_name
="Tél. cellulaire",
320 null
=True, blank
=True)
321 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
322 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
323 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
324 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
325 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
326 related_name
='employes',
327 null
=True, blank
=True)
330 ordering
= ['nom_affichage','nom','prenom']
331 verbose_name
= "Employé"
332 verbose_name_plural
= "Employés"
334 def __unicode__(self
):
335 return u
'%s' % (self
.get_nom())
338 nom_affichage
= self
.nom_affichage
339 if not nom_affichage
:
340 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
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',
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__
507 class Dossier(Dossier_
):
508 __doc__
= Dossier_
.__doc__
511 class DossierPiece(models
.Model
):
512 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
513 Ex.: Lettre de motivation.
515 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
517 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
518 fichier
= models
.FileField(verbose_name
="Fichier",
519 upload_to
=dossier_piece_dispatch
,
520 storage
=storage_prive
)
525 def __unicode__(self
):
526 return u
'%s' % (self
.nom
)
528 class DossierCommentaire(Commentaire
):
529 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
532 class DossierComparaison(models
.Model
):
534 Photo d'une comparaison salariale au moment de l'embauche.
536 dossier
= models
.ForeignKey('Dossier', related_name
='comparaisons')
537 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True)
538 poste
= models
.CharField(max_length
=255, null
=True, blank
=True)
539 personne
= models
.CharField(max_length
=255, null
=True, blank
=True)
540 montant
= models
.IntegerField(null
=True)
541 devise
= models
.ForeignKey('Devise', default
=5, related_name
='+', null
=True, blank
=True)
543 def taux_devise(self
):
544 liste_taux
= self
.devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.dossier
.poste
.implantation
)
545 if len(liste_taux
) == 0:
546 raise Exception(u
"La devise %s n'a pas de taux pour l'implantation %s" % (self
.devise
, self
.dossier
.poste
.implantation
))
548 return liste_taux
[0].taux
550 def montant_euros(self
):
551 return round(float(self
.montant
) * float(self
.taux_devise()), 2)
556 class RemunerationMixin(AUFMetadata
):
558 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
559 related_name
='%(app_label)s_%(class)s_remunerations')
560 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
562 verbose_name
="Type de rémunération")
563 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
564 db_column
='type_revalorisation',
566 verbose_name
="Type de revalorisation",
567 null
=True, blank
=True)
568 montant
= models
.FloatField(null
=True, blank
=True,
570 # Annuel (12 mois, 52 semaines, 364 jours?)
571 devise
= models
.ForeignKey('Devise', to_field
='id',
572 db_column
='devise', related_name
='+',
574 # commentaire = precision
575 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
576 # date_debut = anciennement date_effectif
577 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
578 verbose_name
="Date de début",
579 null
=True, blank
=True)
580 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
581 verbose_name
="Date de fin",
582 null
=True, blank
=True)
586 ordering
= ['type__nom', '-date_fin']
588 def __unicode__(self
):
589 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
591 class Remuneration_(RemunerationMixin
):
592 """Structure de rémunération (données budgétaires) en situation normale
593 pour un Dossier. Si un Evenement existe, utiliser la structure de
594 rémunération EvenementRemuneration de cet événement.
597 def montant_mois(self
):
598 return round(self
.montant
/ 12, 2)
600 def taux_devise(self
):
601 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
603 def montant_euro(self
):
604 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
606 def montant_euro_mois(self
):
607 return round(self
.montant_euro() / 12, 2)
609 def __unicode__(self
):
611 devise
= self
.devise
.code
614 return "%s %s" % (self
.montant
, devise
)
618 verbose_name
= "Rémunération"
619 verbose_name_plural
= "Rémunérations"
622 class Remuneration(Remuneration_
):
623 __doc__
= Remuneration_
.__doc__
628 class ContratManager(NoDeleteManager
):
629 def get_query_set(self
):
630 return super(ContratManager
, self
).get_query_set().select_related('dossier', 'dossier__poste')
633 class Contrat(AUFMetadata
):
634 """Document juridique qui encadre la relation de travail d'un Employe
635 pour un Poste particulier. Pour un Dossier (qui documente cette
636 relation de travail) plusieurs contrats peuvent être associés.
639 objects
= ContratManager()
641 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
643 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
645 verbose_name
="Type de contrat")
646 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
647 verbose_name
="Date de début")
648 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
649 verbose_name
="Date de fin",
650 null
=True, blank
=True)
653 ordering
= ['dossier__employe__nom_affichage']
654 verbose_name
= "Contrat"
655 verbose_name_plural
= "Contrats"
657 def __unicode__(self
):
658 return u
'%s - %s' % (self
.dossier
, self
.id)
660 # TODO? class ContratPiece(models.Model):
665 class Evenement_(AUFMetadata
):
666 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
667 d'un Dossier qui vient altérer des informations normales liées à un Dossier
668 (ex.: la Remuneration).
670 Ex.: congé de maternité, maladie...
672 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
673 différent et une rémunération en conséquence. On souhaite toutefois
674 conserver le Dossier intact afin d'éviter une re-saisie des données lors
675 du retour à la normale.
677 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
679 nom
= models
.CharField(max_length
=255)
680 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
681 verbose_name
="Date de début")
682 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
683 verbose_name
="Date de fin",
684 null
=True, blank
=True)
689 verbose_name
= "Évènement"
690 verbose_name_plural
= "Évènements"
692 def __unicode__(self
):
693 return u
'%s' % (self
.nom
)
696 class Evenement(Evenement_
):
697 __doc__
= Evenement_
.__doc__
700 class EvenementRemuneration_(RemunerationMixin
):
701 """Structure de rémunération liée à un Evenement qui remplace
702 temporairement la Remuneration normale d'un Dossier, pour toute la durée
705 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
707 verbose_name
="Évènement")
708 # TODO : le champ dossier hérité de Remuneration doit être dérivé
709 # de l'Evenement associé
713 ordering
= ['evenement', 'type__nom', '-date_fin']
714 verbose_name
= "Évènement - rémunération"
715 verbose_name_plural
= "Évènements - rémunérations"
718 class EvenementRemuneration(EvenementRemuneration_
):
719 __doc__
= EvenementRemuneration_
.__doc__
725 class EvenementRemuneration(EvenementRemuneration_
):
726 __doc__
= EvenementRemuneration_
.__doc__
731 class FamilleEmploi(AUFMetadata
):
732 """Catégorie utilisée dans la gestion des Postes.
733 Catégorie supérieure à TypePoste.
735 nom
= models
.CharField(max_length
=255)
739 verbose_name
= "Famille d'emploi"
740 verbose_name_plural
= "Familles d'emploi"
742 def __unicode__(self
):
743 return u
'%s' % (self
.nom
)
745 class TypePoste(AUFMetadata
):
746 """Catégorie de Poste.
748 nom
= models
.CharField(max_length
=255)
749 nom_feminin
= models
.CharField(max_length
=255,
750 verbose_name
="Nom féminin")
752 is_responsable
= models
.BooleanField(default
=False,
753 verbose_name
="Poste de responsabilité")
754 famille_emploi
= models
.ForeignKey('FamilleEmploi',
755 db_column
='famille_emploi',
757 verbose_name
="Famille d'emploi")
761 verbose_name
= "Type de poste"
762 verbose_name_plural
= "Types de poste"
764 def __unicode__(self
):
765 return u
'%s' % (self
.nom
)
768 TYPE_PAIEMENT_CHOICES
= (
769 ('Régulier', 'Régulier'),
770 ('Ponctuel', 'Ponctuel'),
773 NATURE_REMUNERATION_CHOICES
= (
774 ('Accessoire', 'Accessoire'),
775 ('Charges', 'Charges'),
776 ('Indemnité', 'Indemnité'),
777 ('RAS', 'Rémunération autre source'),
778 ('Traitement', 'Traitement'),
781 class TypeRemuneration(AUFMetadata
):
782 """Catégorie de Remuneration.
784 nom
= models
.CharField(max_length
=255)
785 type_paiement
= models
.CharField(max_length
=30,
786 choices
=TYPE_PAIEMENT_CHOICES
,
787 verbose_name
="Type de paiement")
788 nature_remuneration
= models
.CharField(max_length
=30,
789 choices
=NATURE_REMUNERATION_CHOICES
,
790 verbose_name
="Nature de la rémunération")
794 verbose_name
= "Type de rémunération"
795 verbose_name_plural
= "Types de rémunération"
797 def __unicode__(self
):
798 return u
'%s' % (self
.nom
)
800 class TypeRevalorisation(AUFMetadata
):
801 """Justification du changement de la Remuneration.
802 (Actuellement utilisé dans aucun traitement informatique.)
804 nom
= models
.CharField(max_length
=255)
808 verbose_name
= "Type de revalorisation"
809 verbose_name_plural
= "Types de revalorisation"
811 def __unicode__(self
):
812 return u
'%s' % (self
.nom
)
814 class Service(AUFMetadata
):
815 """Unité administrative où les Postes sont rattachés.
817 nom
= models
.CharField(max_length
=255)
821 verbose_name
= "Service"
822 verbose_name_plural
= "Services"
824 def __unicode__(self
):
825 return u
'%s' % (self
.nom
)
828 TYPE_ORGANISME_CHOICES
= (
829 ('MAD', 'Mise à disposition'),
830 ('DET', 'Détachement'),
833 class OrganismeBstg(AUFMetadata
):
834 """Organisation d'où provient un Employe mis à disposition (MAD) de
835 ou détaché (DET) à l'AUF à titre gratuit.
837 (BSTG = bien et service à titre gratuit.)
839 nom
= models
.CharField(max_length
=255)
840 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
841 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
843 related_name
='organismes_bstg',
844 null
=True, blank
=True)
847 ordering
= ['type', 'nom']
848 verbose_name
= "Organisme BSTG"
849 verbose_name_plural
= "Organismes BSTG"
851 def __unicode__(self
):
852 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
854 class Statut(AUFMetadata
):
855 """Statut de l'Employe dans le cadre d'un Dossier particulier.
858 code
= models
.CharField(max_length
=25, unique
=True)
859 nom
= models
.CharField(max_length
=255)
863 verbose_name
= "Statut d'employé"
864 verbose_name_plural
= "Statuts d'employé"
866 def __unicode__(self
):
867 return u
'%s : %s' % (self
.code
, self
.nom
)
870 TYPE_CLASSEMENT_CHOICES
= (
872 ('T', 'T - Technicien'),
873 ('P', 'P - Professionel'),
875 ('D', 'D - Direction'),
876 ('SO', 'SO - Sans objet [expatriés]'),
877 ('HG', 'HG - Hors grille [direction]'),
881 class Classement_(AUFMetadata
):
882 """Éléments de classement de la
883 "Grille générique de classement hiérarchique".
885 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
886 classement dans la grille. Le classement donne le coefficient utilisé dans:
888 salaire de base = coefficient * valeur du point de l'Implantation du Poste
891 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
892 echelon
= models
.IntegerField(verbose_name
="Échelon")
893 degre
= models
.IntegerField(verbose_name
="Degré")
894 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient",
897 # annee # au lieu de date_debut et date_fin
898 commentaire
= models
.TextField(null
=True, blank
=True)
902 ordering
= ['type','echelon','degre','coefficient']
903 verbose_name
= "Classement"
904 verbose_name_plural
= "Classements"
906 def __unicode__(self
):
907 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
910 class Classement(Classement_
):
911 __doc__
= Classement_
.__doc__
914 class TauxChange_(AUFMetadata
):
915 """Taux de change de la devise vers l'euro (EUR)
916 pour chaque année budgétaire.
919 devise
= models
.ForeignKey('Devise', db_column
='devise',
921 annee
= models
.IntegerField(verbose_name
="Année")
922 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
926 ordering
= ['-annee', 'devise__code']
927 verbose_name
= "Taux de change"
928 verbose_name_plural
= "Taux de change"
930 def __unicode__(self
):
931 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
934 class TauxChange(TauxChange_
):
935 __doc__
= TauxChange_
.__doc__
937 class ValeurPointManager(NoDeleteManager
):
938 def get_query_set(self
):
939 return super(ValeurPointManager
, self
).get_query_set().select_related('devise', 'implantation')
942 class ValeurPoint_(AUFMetadata
):
943 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
944 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
945 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
947 salaire de base = coefficient * valeur du point de l'Implantation du Poste
950 objects
= ValeurPointManager()
952 valeur
= models
.FloatField(null
=True)
953 devise
= models
.ForeignKey('Devise', db_column
='devise', null
=True,
954 related_name
='+', default
=5)
955 implantation
= models
.ForeignKey(ref
.Implantation
,
956 db_column
='implantation',
957 related_name
='%(app_label)s_valeur_point')
959 annee
= models
.IntegerField()
962 ordering
= ['-annee', 'implantation__nom']
964 verbose_name
= "Valeur du point"
965 verbose_name_plural
= "Valeurs du point"
967 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
968 def get_tauxchange_courant(self
):
970 Recherche le taux courant associé à la valeur d'un point.
971 Tous les taux de l'année courante sont chargés, pour optimiser un
972 affichage en liste. (On pourrait probablement améliorer le manager pour
973 lui greffer le taux courant sous forme de JOIN)
975 for tauxchange
in self
.tauxchange
:
976 if tauxchange
.implantation_id
== self
.implantation_id
:
980 def __unicode__(self
):
981 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
984 class ValeurPoint(ValeurPoint_
):
985 __doc__
= ValeurPoint_
.__doc__
988 class Devise(AUFMetadata
):
991 code
= models
.CharField(max_length
=10, unique
=True)
992 nom
= models
.CharField(max_length
=255)
996 verbose_name
= "Devise"
997 verbose_name_plural
= "Devises"
999 def __unicode__(self
):
1000 return u
'%s - %s' % (self
.code
, self
.nom
)
1002 class TypeContrat(AUFMetadata
):
1005 nom
= models
.CharField(max_length
=255)
1006 nom_long
= models
.CharField(max_length
=255)
1010 verbose_name
= "Type de contrat"
1011 verbose_name_plural
= "Types de contrat"
1013 def __unicode__(self
):
1014 return u
'%s' % (self
.nom
)
1019 class ResponsableImplantation(AUFMetadata
):
1020 """Le responsable d'une implantation.
1021 Anciennement géré sur le Dossier du responsable.
1023 employe
= models
.ForeignKey('Employe', db_column
='employe',
1025 null
=True, blank
=True)
1026 implantation
= models
.ForeignKey(ref
.Implantation
,
1027 db_column
='implantation', related_name
='+',
1030 def __unicode__(self
):
1031 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1034 ordering
= ['implantation__nom']
1035 verbose_name
= "Responsable d'implantation"
1036 verbose_name_plural
= "Responsables d'implantation"