1 # -=- encoding: utf-8 -=-
5 from django
.core
.files
.storage
import FileSystemStorage
6 from django
.db
import models
9 import datamaster_modeles
.models
as ref
13 HELP_TEXT_DATE
= "format: aaaa-mm-jj"
14 REGIME_TRAVAIL_DEFAULT
= 100.00
15 REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
= 35.00
19 storage_prive
= FileSystemStorage(settings
.PRIVE_MEDIA_ROOT
,
20 base_url
=settings
.PRIVE_MEDIA_URL
)
22 def poste_piece_dispatch(instance
, filename
):
23 path
= "poste/%s/%s" % (instance
.poste_id
, filename
)
26 def dossier_piece_dispatch(instance
, filename
):
27 path
= "dossier/%s/%s" % (instance
.dossier_id
, filename
)
31 class Metadata(models
.Model
):
33 Metadata.actif = flag remplaçant la suppression.
34 supprime == True : objet réputé supprimé.
36 actif
= models
.BooleanField(default
=True)
37 supprime
= models
.BooleanField(default
=False)
38 date_creation
= models
.DateField(auto_now_add
=True)
39 user_creation
= models
.ForeignKey('auth.User',
40 db_column
='user_creation', related_name
='+',
41 null
=True, blank
=True)
42 date_modification
= models
.DateField(auto_now
=True)
43 user_modification
= models
.ForeignKey('auth.User',
44 db_column
='user_modification', related_name
='+',
45 null
=True, blank
=True)
46 date_desactivation
= models
.DateField(null
=True, blank
=True)
47 user_desactivation
= models
.ForeignKey('auth.User',
48 db_column
='user_desactivation', related_name
='+',
49 null
=True, blank
=True)
54 class Commentaire(Metadata
):
55 texte
= models
.TextField()
56 owner
= models
.ForeignKey('auth.User', db_column
='owner', related_name
='+')
60 ordering
= ['-date_creation']
62 def __unicode__(self
):
63 return u
'%s' % (self
.texte
)
68 POSTE_APPEL_CHOICES
= (
69 ('interne', 'Interne'),
70 ('externe', 'Externe'),
73 class Poste_(Metadata
):
74 """Un Poste est un emploi (job) à combler dans une implantation.
75 Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
76 Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
79 nom
= models
.CharField(max_length
=255,
80 verbose_name
="Titre du poste", )
81 nom_feminin
= models
.CharField(max_length
=255,
82 verbose_name
="Titre du poste (au féminin)",
84 implantation
= models
.ForeignKey(ref
.Implantation
,
85 db_column
='implantation', related_name
='+')
86 type_poste
= models
.ForeignKey('TypePoste', db_column
='type_poste',
89 service
= models
.ForeignKey('Service', db_column
='service',
91 verbose_name
="Direction/Service/Pôle support",
92 default
=1) # default = Rectorat
93 responsable
= models
.ForeignKey('Poste', db_column
='responsable',
95 verbose_name
="Poste du responsable",
96 default
=149) # default = Recteur
99 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
100 default
=REGIME_TRAVAIL_DEFAULT
,
101 verbose_name
="Temps de travail",
102 help_text
="% du temps complet")
103 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
105 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
106 verbose_name
="Nb. heures par semaine")
109 local
= models
.BooleanField(verbose_name
="Local", default
=True,
111 expatrie
= models
.BooleanField(verbose_name
="Expatrié", default
=False,
113 mise_a_disposition
= models
.BooleanField(
114 verbose_name
="Mise à disposition",
116 appel
= models
.CharField(max_length
=10,
117 verbose_name
="Appel à candidature",
118 choices
=POSTE_APPEL_CHOICES
,
122 classement_min
= models
.ForeignKey('Classement',
123 db_column
='classement_min', related_name
='+',
124 null
=True, blank
=True)
125 classement_max
= models
.ForeignKey('Classement',
126 db_column
='classement_max', related_name
='+',
127 null
=True, blank
=True)
128 valeur_point_min
= models
.ForeignKey('ValeurPoint',
129 db_column
='valeur_point_min', related_name
='+',
130 null
=True, blank
=True)
131 valeur_point_max
= models
.ForeignKey('ValeurPoint',
132 db_column
='valeur_point_max', related_name
='+',
133 null
=True, blank
=True)
134 devise_min
= models
.ForeignKey('Devise', db_column
='devise_min',
135 related_name
='+', default
=5)
136 devise_max
= models
.ForeignKey('Devise', db_column
='devise_max',
137 related_name
='+', default
=5)
138 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
140 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
142 indemn_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
144 indemn_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
146 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
148 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
151 # Comparatifs de rémunération
152 devise_comparaison
= models
.ForeignKey('Devise',
153 db_column
='devise_comparaison',
156 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
157 null
=True, blank
=True)
158 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
159 null
=True, blank
=True)
160 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
161 null
=True, blank
=True)
162 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
163 null
=True, blank
=True)
164 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
165 null
=True, blank
=True)
166 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
167 null
=True, blank
=True)
168 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
169 null
=True, blank
=True)
170 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
171 null
=True, blank
=True)
172 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
173 null
=True, blank
=True)
174 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
175 null
=True, blank
=True)
178 justification
= models
.TextField(null
=True, blank
=True)
181 date_validation
= models
.DateTimeField(null
=True, blank
=True) # de dae
182 date_debut
= models
.DateField(verbose_name
="Date de début",
183 help_text
=HELP_TEXT_DATE
)
184 date_fin
= models
.DateField(verbose_name
="Date de fin",
185 help_text
=HELP_TEXT_DATE
,
186 null
=True, blank
=True)
190 ordering
= ['implantation__nom', 'nom']
191 verbose_name
= "Poste"
192 verbose_name_plural
= "Postes"
194 def __unicode__(self
):
195 representation
= u
'%s - %s [%s]' % (self
.implantation
, self
.nom
,
198 representation
= representation
+ u
' (vacant)'
199 return representation
202 # TODO : si existe un dossier actif pour ce poste, return False
203 # self.dossier_set.all() fonctionne pas
208 __doc__
= Poste_
.__doc__
212 __doc__
= Poste_
.__doc__
215 POSTE_FINANCEMENT_CHOICES
= (
216 ('A', 'A - Frais de personnel'),
217 ('B', 'B - Projet(s)-Titre(s)'),
222 class PosteFinancement_(models
.Model
):
223 """Pour un Poste, structure d'informations décrivant comment on prévoit
226 poste
= models
.ForeignKey('Poste', db_column
='poste',
227 related_name
='%(app_label)s_financements')
228 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
229 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
230 help_text
="ex.: 33.33 % (décimale avec point)")
231 commentaire
= models
.TextField(
232 help_text
="Spécifiez la source de financement.")
238 def __unicode__(self
):
239 return u
'%s : %s %' % (self
.type, self
.pourcentage
)
242 class PosteFinancement(PosteFinancement_
):
243 __doc__
= PosteFinancement_
.__doc__
246 class PostePiece(models
.Model
):
247 """Documents relatifs au Poste.
248 Ex.: Description de poste
250 poste
= models
.ForeignKey('Poste', db_column
='poste',
251 related_name
='pieces')
252 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
253 fichier
= models
.FileField(verbose_name
="Fichier",
254 upload_to
=poste_piece_dispatch
,
255 storage
=storage_prive
)
260 def __unicode__(self
):
261 return u
'%s' % (self
.nom
)
263 class PosteComparaison(models
.Model
):
265 De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
267 poste
= models
.ForeignKey('Poste', related_name
='comparaisons_internes')
268 implantation
= models
.ForeignKey(ref
.Implantation
, null
=True, blank
=True)
269 nom
= models
.CharField(verbose_name
="Poste", max_length
=255, null
=True, blank
=True)
270 montant
= models
.IntegerField(null
=True)
271 devise
= models
.ForeignKey("Devise", default
=5, related_name
='+', null
=True, blank
=True)
272 montant_euros
= models
.IntegerField(null
=True)
275 class PosteCommentaire(Commentaire
):
276 poste
= models
.ForeignKey('Poste', db_column
='poste', related_name
='+')
285 SITUATION_CHOICES
= (
286 ('C', 'Célibataire'),
291 class Employe(Metadata
):
292 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
293 Dossiers qu'il occupe ou a occupé de Postes.
295 Cette classe aurait pu avantageusement s'appeler Personne car la notion
296 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
299 nom
= models
.CharField(max_length
=255)
300 prenom
= models
.CharField(max_length
=255, verbose_name
="Prénom")
301 nom_affichage
= models
.CharField(max_length
=255,
302 verbose_name
="Nom d'affichage",
303 null
=True, blank
=True)
304 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
305 db_column
='nationalite',
306 related_name
='employes_nationalite',
307 verbose_name
="Nationalité")
308 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
309 verbose_name
="Date de naissance",
310 null
=True, blank
=True)
311 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
314 situation_famille
= models
.CharField(max_length
=1,
315 choices
=SITUATION_CHOICES
,
316 verbose_name
="Situation familiale",
317 null
=True, blank
=True)
318 date_entree
= models
.DateField(verbose_name
="Date d'entrée à l'AUF",
319 help_text
=HELP_TEXT_DATE
,
320 null
=True, blank
=True)
323 tel_domicile
= models
.CharField(max_length
=255,
324 verbose_name
="Tél. domicile",
325 null
=True, blank
=True)
326 tel_cellulaire
= models
.CharField(max_length
=255,
327 verbose_name
="Tél. cellulaire",
328 null
=True, blank
=True)
329 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
330 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
331 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
332 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
333 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
334 related_name
='employes',
335 null
=True, blank
=True)
338 ordering
= ['nom_affichage','nom','prenom']
339 verbose_name
= "Employé"
340 verbose_name_plural
= "Employés"
342 def __unicode__(self
):
343 return u
'%s' % (self
.get_nom())
346 nom_affichage
= self
.nom_affichage
347 if not nom_affichage
:
348 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
351 class EmployePiece(models
.Model
):
352 """Documents relatifs à un employé.
355 employe
= models
.ForeignKey('Employe', db_column
='employe',
357 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
358 fichier
= models
.FileField(verbose_name
="Fichier",
359 upload_to
=dossier_piece_dispatch
,
360 storage
=storage_prive
)
365 def __unicode__(self
):
366 return u
'%s' % (self
.nom
)
368 class EmployeCommentaire(Commentaire
):
369 employe
= models
.ForeignKey('Employe', db_column
='employe',
373 LIEN_PARENTE_CHOICES
= (
374 ('Conjoint', 'Conjoint'),
375 ('Conjointe', 'Conjointe'),
380 class AyantDroit(Metadata
):
381 """Personne en relation avec un Employe.
384 nom
= models
.CharField(max_length
=255)
385 prenom
= models
.CharField(max_length
=255,
386 verbose_name
="Prénom",)
387 nom_affichage
= models
.CharField(max_length
=255,
388 verbose_name
="Nom d'affichage",
389 null
=True, blank
=True)
390 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
391 db_column
='nationalite',
392 related_name
='ayantdroits_nationalite',
393 verbose_name
="Nationalité")
394 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
395 verbose_name
="Date de naissance",
396 null
=True, blank
=True)
397 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
400 employe
= models
.ForeignKey('Employe', db_column
='employe',
401 related_name
='ayantdroits',
402 verbose_name
="Employé")
403 lien_parente
= models
.CharField(max_length
=10,
404 choices
=LIEN_PARENTE_CHOICES
,
405 verbose_name
="Lien de parenté",
406 null
=True, blank
=True)
409 ordering
= ['nom_affichage']
410 verbose_name
= "Ayant droit"
411 verbose_name_plural
= "Ayants droit"
413 def __unicode__(self
):
414 return u
'%s' % (self
.get_nom())
417 nom_affichage
= self
.nom_affichage
418 if not nom_affichage
:
419 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
422 class AyantDroitCommentaire(Commentaire
):
423 ayant_droit
= models
.ForeignKey('AyantDroit', db_column
='ayant_droit',
429 STATUT_RESIDENCE_CHOICES
= (
431 ('expat', 'Expatrié'),
434 COMPTE_COMPTA_CHOICES
= (
440 class Dossier_(Metadata
):
441 """Le Dossier regroupe les informations relatives à l'occupation
442 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
445 Plusieurs Contrats peuvent être associés au Dossier.
446 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
447 lequel aucun Dossier n'existe est un poste vacant.
450 employe
= models
.ForeignKey('Employe', db_column
='employe',
452 verbose_name
="Employé")
453 poste
= models
.ForeignKey('Poste', db_column
='poste',
454 related_name
='+', editable
=False)
455 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3)
456 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
457 db_column
='organisme_bstg',
459 verbose_name
="Organisme",
460 help_text
="Si détaché (DET) ou \
461 mis à disposition (MAD), \
462 préciser l'organisme.",
463 null
=True, blank
=True)
466 remplacement
= models
.BooleanField(default
=False)
467 statut_residence
= models
.CharField(max_length
=10, default
='local',
468 verbose_name
="Statut",
469 choices
=STATUT_RESIDENCE_CHOICES
)
472 classement
= models
.ForeignKey('Classement', db_column
='classement',
474 null
=True, blank
=True)
475 regime_travail
= models
.DecimalField(max_digits
=12,
477 default
=REGIME_TRAVAIL_DEFAULT
,
478 verbose_name
="Régime de travail",
479 help_text
="% du temps complet")
480 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
482 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
483 verbose_name
="Nb. heures par semaine")
485 # Occupation du Poste par cet Employe (anciennement "mandat")
486 date_debut
= models
.DateField(verbose_name
="Date de début d'occupation \
488 help_text
=HELP_TEXT_DATE
)
489 date_fin
= models
.DateField(verbose_name
="Date de fin d'occupation \
491 help_text
=HELP_TEXT_DATE
,
492 null
=True, blank
=True)
499 ordering
= ['employe__nom_affichage', 'employe__nom', 'poste__nom']
500 verbose_name
= "Dossier"
501 verbose_name_plural
= "Dossiers"
503 def __unicode__(self
):
504 poste
= self
.poste
.nom
505 if self
.employe
.genre
== 'F':
506 poste
= self
.poste
.nom_feminin
507 return u
'%s - %s' % (self
.employe
, poste
)
510 class Dossier(Dossier_
):
511 __doc__
= Dossier_
.__doc__
515 class Dossier(Dossier_
):
516 __doc__
= Dossier_
.__doc__
519 class DossierPiece(models
.Model
):
520 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
521 Ex.: Lettre de motivation.
523 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
525 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
526 fichier
= models
.FileField(verbose_name
="Fichier",
527 upload_to
=dossier_piece_dispatch
,
528 storage
=storage_prive
)
533 def __unicode__(self
):
534 return u
'%s' % (self
.nom
)
536 class DossierCommentaire(Commentaire
):
537 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
543 class RemunerationMixin(Metadata
):
545 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
546 related_name
='%(app_label)s_%(class)s_remunerations')
547 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
549 verbose_name
="Type de rémunération")
550 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
551 db_column
='type_revalorisation',
553 verbose_name
="Type de revalorisation",
554 null
=True, blank
=True)
555 montant
= models
.FloatField(null
=True, blank
=True,
557 # Annuel (12 mois, 52 semaines, 364 jours?)
558 devise
= models
.ForeignKey('Devise', to_field
='id',
559 db_column
='devise', related_name
='+',
561 # commentaire = precision
562 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
563 # date_debut = anciennement date_effectif
564 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
565 verbose_name
="Date de début",
566 null
=True, blank
=True)
567 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
568 verbose_name
="Date de fin",
569 null
=True, blank
=True)
573 ordering
= ['type__nom', '-date_fin']
575 def __unicode__(self
):
576 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
578 class Remuneration_(RemunerationMixin
):
579 """Structure de rémunération (données budgétaires) en situation normale
580 pour un Dossier. Si un Evenement existe, utiliser la structure de
581 rémunération EvenementRemuneration de cet événement.
584 def montant_mois(self
):
585 return round(self
.montant
/ 12, 2)
587 def taux_devise(self
):
588 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
590 def montant_euro(self
):
591 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
593 def montant_euro_mois(self
):
594 return round(self
.montant_euro() / 12, 2)
596 def __unicode__(self
):
598 devise
= self
.devise
.code
601 return "%s %s" % (self
.montant
, devise
)
605 verbose_name
= "Rémunération"
606 verbose_name_plural
= "Rémunérations"
609 class Remuneration(Remuneration_
):
610 __doc__
= Remuneration_
.__doc__
615 class Contrat(Metadata
):
616 """Document juridique qui encadre la relation de travail d'un Employe
617 pour un Poste particulier. Pour un Dossier (qui documente cette
618 relation de travail) plusieurs contrats peuvent être associés.
620 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
622 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
624 verbose_name
="Type de contrat")
625 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
626 verbose_name
="Date de début")
627 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
628 verbose_name
="Date de fin",
629 null
=True, blank
=True)
632 ordering
= ['dossier__employe__nom_affichage']
633 verbose_name
= "Contrat"
634 verbose_name_plural
= "Contrats"
636 def __unicode__(self
):
637 return u
'%s - %s' % (self
.dossier
, self
.id)
639 # TODO? class ContratPiece(models.Model):
644 class Evenement_(Metadata
):
645 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
646 d'un Dossier qui vient altérer des informations normales liées à un Dossier
647 (ex.: la Remuneration).
649 Ex.: congé de maternité, maladie...
651 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
652 différent et une rémunération en conséquence. On souhaite toutefois
653 conserver le Dossier intact afin d'éviter une re-saisie des données lors
654 du retour à la normale.
656 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
658 nom
= models
.CharField(max_length
=255)
659 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
660 verbose_name
="Date de début")
661 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
662 verbose_name
="Date de fin",
663 null
=True, blank
=True)
668 verbose_name
= "Évènement"
669 verbose_name_plural
= "Évènements"
671 def __unicode__(self
):
672 return u
'%s' % (self
.nom
)
675 class Evenement(Evenement_
):
676 __doc__
= Evenement_
.__doc__
679 class EvenementRemuneration_(RemunerationMixin
):
680 """Structure de rémunération liée à un Evenement qui remplace
681 temporairement la Remuneration normale d'un Dossier, pour toute la durée
684 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
686 verbose_name
="Évènement")
687 # TODO : le champ dossier hérité de Remuneration doit être dérivé
688 # de l'Evenement associé
692 ordering
= ['evenement', 'type__nom', '-date_fin']
693 verbose_name
= "Évènement - rémunération"
694 verbose_name_plural
= "Évènements - rémunérations"
697 class EvenementRemuneration(EvenementRemuneration_
):
698 __doc__
= EvenementRemuneration_
.__doc__
704 class EvenementRemuneration(EvenementRemuneration_
):
705 __doc__
= EvenementRemuneration_
.__doc__
710 class FamilleEmploi(Metadata
):
711 """Catégorie utilisée dans la gestion des Postes.
712 Catégorie supérieure à TypePoste.
714 nom
= models
.CharField(max_length
=255)
718 verbose_name
= "Famille d'emploi"
719 verbose_name_plural
= "Familles d'emploi"
721 def __unicode__(self
):
722 return u
'%s' % (self
.nom
)
724 class TypePoste(Metadata
):
725 """Catégorie de Poste.
727 nom
= models
.CharField(max_length
=255)
728 nom_feminin
= models
.CharField(max_length
=255,
729 verbose_name
="Nom féminin")
731 is_responsable
= models
.BooleanField(default
=False,
732 verbose_name
="Poste de responsabilité")
733 famille_emploi
= models
.ForeignKey('FamilleEmploi',
734 db_column
='famille_emploi',
736 verbose_name
="Famille d'emploi")
740 verbose_name
= "Type de poste"
741 verbose_name_plural
= "Types de poste"
743 def __unicode__(self
):
744 return u
'%s' % (self
.nom
)
747 TYPE_PAIEMENT_CHOICES
= (
748 ('Régulier', 'Régulier'),
749 ('Ponctuel', 'Ponctuel'),
752 NATURE_REMUNERATION_CHOICES
= (
753 ('Accessoire', 'Accessoire'),
754 ('Charges', 'Charges'),
755 ('Indemnité', 'Indemnité'),
756 ('RAS', 'Rémunération autre source'),
757 ('Traitement', 'Traitement'),
760 class TypeRemuneration(Metadata
):
761 """Catégorie de Remuneration.
763 nom
= models
.CharField(max_length
=255)
764 type_paiement
= models
.CharField(max_length
=30,
765 choices
=TYPE_PAIEMENT_CHOICES
,
766 verbose_name
="Type de paiement")
767 nature_remuneration
= models
.CharField(max_length
=30,
768 choices
=NATURE_REMUNERATION_CHOICES
,
769 verbose_name
="Nature de la rémunération")
773 verbose_name
= "Type de rémunération"
774 verbose_name_plural
= "Types de rémunération"
776 def __unicode__(self
):
777 return u
'%s' % (self
.nom
)
779 class TypeRevalorisation(Metadata
):
780 """Justification du changement de la Remuneration.
781 (Actuellement utilisé dans aucun traitement informatique.)
783 nom
= models
.CharField(max_length
=255)
787 verbose_name
= "Type de revalorisation"
788 verbose_name_plural
= "Types de revalorisation"
790 def __unicode__(self
):
791 return u
'%s' % (self
.nom
)
793 class Service(Metadata
):
794 """Unité administrative où les Postes sont rattachés.
796 nom
= models
.CharField(max_length
=255)
800 verbose_name
= "Service"
801 verbose_name_plural
= "Services"
803 def __unicode__(self
):
804 return u
'%s' % (self
.nom
)
807 TYPE_ORGANISME_CHOICES
= (
808 ('MAD', 'Mise à disposition'),
809 ('DET', 'Détachement'),
812 class OrganismeBstg(Metadata
):
813 """Organisation d'où provient un Employe mis à disposition (MAD) de
814 ou détaché (DET) à l'AUF à titre gratuit.
816 (BSTG = bien et service à titre gratuit.)
818 nom
= models
.CharField(max_length
=255)
819 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
820 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
822 related_name
='organismes_bstg',
823 null
=True, blank
=True)
826 ordering
= ['type', 'nom']
827 verbose_name
= "Organisme BSTG"
828 verbose_name_plural
= "Organismes BSTG"
830 def __unicode__(self
):
831 return u
'%s (%s)' % (self
.nom
, self
.get_type_display())
833 class Statut(Metadata
):
834 """Statut de l'Employe dans le cadre d'un Dossier particulier.
837 code
= models
.CharField(max_length
=25, unique
=True)
838 nom
= models
.CharField(max_length
=255)
842 verbose_name
= "Statut d'employé"
843 verbose_name_plural
= "Statuts d'employé"
845 def __unicode__(self
):
846 return u
'%s : %s' % (self
.code
, self
.nom
)
849 TYPE_CLASSEMENT_CHOICES
= (
851 ('T', 'T - Technicien'),
852 ('P', 'P - Professionel'),
854 ('D', 'D - Direction'),
855 ('SO', 'SO - Sans objet [expatriés]'),
856 ('HG', 'HG - Hors grille [direction]'),
860 class Classement_(Metadata
):
861 """Éléments de classement de la
862 "Grille générique de classement hiérarchique".
864 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
865 classement dans la grille. Le classement donne le coefficient utilisé dans:
867 salaire de base = coefficient * valeur du point de l'Implantation du Poste
870 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
871 echelon
= models
.IntegerField(verbose_name
="Échelon")
872 degre
= models
.IntegerField(verbose_name
="Degré")
873 coefficient
= models
.FloatField(default
=0, verbose_name
="Coéfficient")
875 # annee # au lieu de date_debut et date_fin
876 commentaire
= models
.TextField(null
=True, blank
=True)
880 ordering
= ['type','echelon','degre','coefficient']
881 verbose_name
= "Classement"
882 verbose_name_plural
= "Classements"
884 def __unicode__(self
):
885 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
888 class Classement(Classement_
):
889 __doc__
= Classement_
.__doc__
892 class TauxChange_(Metadata
):
893 """Taux de change de la devise vers l'euro (EUR)
894 pour chaque année budgétaire.
897 devise
= models
.ForeignKey('Devise', db_column
='devise',
899 annee
= models
.IntegerField(verbose_name
="Année")
900 taux
= models
.FloatField(verbose_name
="Taux vers l'euro")
904 ordering
= ['-annee', 'devise__code']
905 verbose_name
= "Taux de change"
906 verbose_name_plural
= "Taux de change"
908 def __unicode__(self
):
909 return u
'%s : %s € (%s)' % (self
.devise
, self
.taux
, self
.annee
)
912 class TauxChange(TauxChange_
):
913 __doc__
= TauxChange_
.__doc__
916 class ValeurPoint_(Metadata
):
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
923 valeur
= models
.FloatField()
924 devise
= models
.ForeignKey('Devise', db_column
='devise',
925 related_name
='+', default
=5)
926 implantation
= models
.ForeignKey(ref
.Implantation
,
927 db_column
='implantation',
928 related_name
='%(app_label)s_valeur_point')
930 annee
= models
.IntegerField()
933 ordering
= ['annee', 'implantation__nom']
936 verbose_name
= "Valeur du point"
937 verbose_name_plural
= "Valeurs du point"
939 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
940 def get_tauxchange_courant(self
):
942 Recherche le taux courant associé à la valeur d'un point.
943 Tous les taux de l'année courante sont chargés, pour optimiser un
944 affichage en liste. (On pourrait probablement améliorer le manager pour
945 lui greffer le taux courant sous forme de JOIN)
947 for tauxchange
in self
.tauxchange
:
948 if tauxchange
.implantation_id
== self
.implantation_id
:
952 def __unicode__(self
):
953 return u
'%s %s (%s)' % (self
.valeur
, self
.devise
, self
.annee
)
956 class ValeurPoint(ValeurPoint_
):
957 __doc__
= ValeurPoint_
.__doc__
960 class Devise(Metadata
):
963 code
= models
.CharField(max_length
=10, unique
=True)
964 nom
= models
.CharField(max_length
=255)
968 verbose_name
= "Devise"
969 verbose_name_plural
= "Devises"
971 def __unicode__(self
):
972 return u
'%s - %s' % (self
.code
, self
.nom
)
974 class TypeContrat(Metadata
):
977 nom
= models
.CharField(max_length
=255)
978 nom_long
= models
.CharField(max_length
=255)
982 verbose_name
= "Type de contrat"
983 verbose_name_plural
= "Types de contrat"
985 def __unicode__(self
):
986 return u
'%s' % (self
.nom
)
991 class ResponsableImplantation(Metadata
):
992 """Le responsable d'une implantation.
993 Anciennement géré sur le Dossier du responsable.
995 employe
= models
.ForeignKey('Employe', db_column
='employe',
997 null
=True, blank
=True)
998 implantation
= models
.ForeignKey(ref
.Implantation
,
999 db_column
='implantation', related_name
='+',
1002 def __unicode__(self
):
1003 return u
'%s : %s' % (self
.implantation
, self
.employe
)
1006 ordering
= ['implantation__nom']
1007 verbose_name
= "Responsable d'implantation"
1008 verbose_name_plural
= "Responsables d'implantation"