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
= u
"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 actif == False : objet réputé supprimé.
36 actif
= models
.BooleanField(default
=True)
37 date_creation
= models
.DateField(auto_now_add
=True)
38 user_creation
= models
.ForeignKey('auth.User',
39 db_column
='user_creation', related_name
='+',
40 null
=True, blank
=True)
41 date_modification
= models
.DateField(auto_now
=True)
42 user_modification
= models
.ForeignKey('auth.User',
43 db_column
='user_modification', related_name
='+',
44 null
=True, blank
=True)
45 date_desactivation
= models
.DateField(null
=True, blank
=True)
46 user_desactivation
= models
.ForeignKey('auth.User',
47 db_column
='user_desactivation', related_name
='+',
48 null
=True, blank
=True)
53 class Commentaire(Metadata
):
54 texte
= models
.TextField()
55 owner
= models
.ForeignKey('auth.User', db_column
='owner', related_name
='+')
59 ordering
= ['-date_creation']
61 def __unicode__(self
):
62 return u
'%s' % (self
.texte
)
67 POSTE_APPEL_CHOICES
= (
68 ('interne', 'Interne'),
69 ('externe', 'Externe'),
72 class Poste_(Metadata
):
73 """Un Poste est un emploi (job) à combler dans une implantation.
74 Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
75 Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
78 nom
= models
.CharField(max_length
=255,
79 verbose_name
=u
"Titre du poste", )
80 nom_feminin
= models
.CharField(max_length
=255,
81 verbose_name
=u
"Titre du poste (au féminin)",
83 implantation
= models
.ForeignKey(ref
.Implantation
,
84 db_column
='implantation', related_name
='+')
85 type_poste
= models
.ForeignKey('TypePoste', db_column
='type_poste',
88 service
= models
.ForeignKey('Service', db_column
='service',
90 verbose_name
=u
"Direction/Service/Pôle support",
91 default
=1) # default = Rectorat
92 responsable
= models
.ForeignKey('Poste', db_column
='responsable',
94 verbose_name
=u
"Poste du responsable",
95 default
=149) # default = Recteur
98 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
99 default
=REGIME_TRAVAIL_DEFAULT
,
100 verbose_name
=u
"Temps de travail",
101 help_text
=u
"% du temps complet")
102 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
104 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
105 verbose_name
=u
"Nb. heures par semaine")
108 local
= models
.BooleanField(verbose_name
=u
"Local", default
=True,
110 expatrie
= models
.BooleanField(verbose_name
=u
"Expatrié", default
=False,
112 mise_a_disposition
= models
.BooleanField(
113 verbose_name
=u
"Mise à disposition",
115 appel
= models
.CharField(max_length
=10,
116 verbose_name
=u
"Appel à candidature",
117 choices
=POSTE_APPEL_CHOICES
,
121 classement_min
= models
.ForeignKey('Classement',
122 db_column
='classement_min', related_name
='+',
123 null
=True, blank
=True)
124 classement_max
= models
.ForeignKey('Classement',
125 db_column
='classement_max', related_name
='+',
126 null
=True, blank
=True)
127 valeur_point_min
= models
.ForeignKey('ValeurPoint',
128 db_column
='valeur_point_min', related_name
='+',
129 null
=True, blank
=True)
130 valeur_point_max
= models
.ForeignKey('ValeurPoint',
131 db_column
='valeur_point_max', related_name
='+',
132 null
=True, blank
=True)
133 devise_min
= models
.ForeignKey('Devise', db_column
='devise_min',
134 related_name
='+', default
=5)
135 devise_max
= models
.ForeignKey('Devise', db_column
='devise_max',
136 related_name
='+', default
=5)
137 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
139 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
141 indemn_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
143 indemn_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
145 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
147 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
150 # Comparatifs de rémunération
151 devise_comparaison
= models
.ForeignKey('Devise',
152 db_column
='devise_comparaison',
155 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
156 null
=True, blank
=True)
157 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
158 null
=True, blank
=True)
159 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
160 null
=True, blank
=True)
161 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
162 null
=True, blank
=True)
163 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
164 null
=True, blank
=True)
165 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
166 null
=True, blank
=True)
167 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
168 null
=True, blank
=True)
169 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
170 null
=True, blank
=True)
171 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
172 null
=True, blank
=True)
173 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
174 null
=True, blank
=True)
177 justification
= models
.TextField(null
=True, blank
=True)
180 date_validation
= models
.DateTimeField(null
=True, blank
=True) # de dae
181 date_debut
= models
.DateField(verbose_name
=u
"Date de début",
182 help_text
=HELP_TEXT_DATE
)
183 date_fin
= models
.DateField(verbose_name
=u
"Date de fin",
184 help_text
=HELP_TEXT_DATE
,
185 null
=True, blank
=True)
189 ordering
= ['implantation__nom', 'nom']
191 def __unicode__(self
):
192 # TODO : gérer si poste est vacant ou non dans affichage
193 # TODO : gérer le nom_feminin (autre méthode appelée par __unicode__ ?)
194 return u
'%s - %s [%s]' % (self
.implantation
, self
.nom
, self
.id)
198 __doc__
= Poste_
.__doc__
201 POSTE_FINANCEMENT_CHOICES
= (
202 ('A', 'A - Frais de personnel'),
203 ('B', 'B - Projet(s)-Titre(s)'),
208 class PosteFinancement_(models
.Model
):
209 """Pour un Poste, structure d'informations décrivant comment on prévoit
212 poste
= models
.ForeignKey('Poste', db_column
='poste',
213 related_name
='%(app_label)s_financements')
214 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
215 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
216 help_text
=u
"ex.: 33.33 % (décimale avec point)")
217 commentaire
= models
.TextField(
218 help_text
=u
"Spécifiez la source de financement.")
224 def __unicode__(self
):
225 return u
'%s : %s %' % (self
.type, self
.pourcentage
)
228 class PosteFinancement(PosteFinancement_
):
229 __doc__
= PosteFinancement_
.__doc__
232 class PostePiece(models
.Model
):
233 """Documents relatifs au Poste.
234 Ex.: Description de poste
236 poste
= models
.ForeignKey("Poste", db_column
='poste',
237 related_name
='pieces')
238 nom
= models
.CharField(verbose_name
=u
"Nom", max_length
=255)
239 fichier
= models
.FileField(verbose_name
=u
"Fichier",
240 upload_to
=poste_piece_dispatch
,
241 storage
=storage_prive
)
246 def __unicode__(self
):
247 return u
'%s' % (self
.nom
)
249 class PosteCommentaire(Commentaire
):
250 poste
= models
.ForeignKey("Poste", db_column
='poste', related_name
='+')
259 SITUATION_CHOICES
= (
260 ('C', 'Célibataire'),
265 class Employe(Metadata
):
266 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
267 Dossiers qu'il occupe ou a occupé de Postes.
269 Cette classe aurait pu avantageusement s'appeler Personne car la notion
270 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
273 nom
= models
.CharField(max_length
=255)
274 prenom
= models
.CharField(max_length
=255, verbose_name
=u
"Prénom")
275 # TODO : nom_affichage doit être obligatoire, pas nom et prenom
276 nom_affichage
= models
.CharField(max_length
=255,
277 verbose_name
=u
"Nom d'affichage",
278 null
=True, blank
=True)
279 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
280 db_column
='nationalite',
281 related_name
='employes_nationalite')
282 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
283 null
=True, blank
=True)
284 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
287 situation_famille
= models
.CharField(max_length
=1,
288 choices
=SITUATION_CHOICES
,
289 null
=True, blank
=True)
290 date_entree
= models
.DateField(verbose_name
=u
"Date d'entrée à l'AUF",
291 help_text
=HELP_TEXT_DATE
,
292 null
=True, blank
=True)
295 tel_domicile
= models
.CharField(max_length
=255, null
=True, blank
=True)
296 tel_cellulaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
297 adresse
= models
.CharField(max_length
=255, null
=True, blank
=True)
298 ville
= models
.CharField(max_length
=255, null
=True, blank
=True)
299 province
= models
.CharField(max_length
=255, null
=True, blank
=True)
300 code_postal
= models
.CharField(max_length
=255, null
=True, blank
=True)
301 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code', db_column
='pays',
302 related_name
='employes',
303 null
=True, blank
=True)
306 ordering
= ['nom_affichage','nom']
308 def __unicode__(self
):
309 # TODO : gérer nom d'affichage
310 nom_affichage
= self
.nom_affichage
311 if not nom_affichage
:
312 nom_affichage
= u
'%s %s' % (self
.nom
.upper(), self
.prenom
)
313 return u
'%s' % (nom_affichage
)
315 class EmployePiece(models
.Model
):
316 """Documents relatifs à un employé.
319 employe
= models
.ForeignKey("Employe", db_column
='employe',
321 nom
= models
.CharField(verbose_name
=u
"Nom", max_length
=255)
322 fichier
= models
.FileField(verbose_name
=u
"Fichier",
323 upload_to
=dossier_piece_dispatch
,
324 storage
=storage_prive
)
329 def __unicode__(self
):
330 return u
'%s' % (self
.nom
)
332 class EmployeCommentaire(Commentaire
):
333 employe
= models
.ForeignKey("Employe", db_column
='employe',
337 LIEN_PARENTE_CHOICES
= (
338 ('Conjoint', 'Conjoint'),
339 ('Conjointe', 'Conjointe'),
344 class AyantDroit(Metadata
):
345 """Personne en relation avec un Employe.
348 nom
= models
.CharField(max_length
=255)
349 prenom
= models
.CharField(max_length
=255)
350 # TODO : nom_affichage doit être obligatoire, pas nom et prenom
351 nom_affichage
= models
.CharField(max_length
=255,
352 verbose_name
=u
"Nom d'affichage",
353 null
=True, blank
=True)
354 nationalite
= models
.ForeignKey(ref
.Pays
, to_field
='code',
355 db_column
='nationalite',
356 related_name
='ayantdroits_nationalite')
357 date_naissance
= models
.DateField(help_text
=HELP_TEXT_DATE
,
358 null
=True, blank
=True)
359 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
)
362 employe
= models
.ForeignKey('Employe', db_column
='employe',
363 related_name
='ayantdroits')
364 lien_parente
= models
.CharField(max_length
=10,
365 choices
=LIEN_PARENTE_CHOICES
,
366 null
=True, blank
=True)
369 ordering
= ['nom_affichage']
370 def __unicode__(self
):
371 # TODO : gérer nom d'affichage
372 return u
'%s %s' % (self
.prenom
, self
.nom
.upper())
374 class AyantDroitCommentaire(Commentaire
):
375 ayant_droit
= models
.ForeignKey("AyantDroit", db_column
='ayant_droit',
381 STATUT_RESIDENCE_CHOICES
= (
383 ('expat', 'Expatrié'),
386 COMPTE_COMPTA_CHOICES
= (
393 class Dossier_(Metadata
):
394 """Le Dossier regroupe les informations relatives à l'occupation
395 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
398 Plusieurs Contrats peuvent être associés au Dossier.
399 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
400 lequel aucun Dossier n'existe est un poste vacant.
403 employe
= models
.ForeignKey('Employe', db_column
='employe',
405 poste
= models
.ForeignKey('Poste', db_column
='poste',
406 related_name
='+', editable
=False)
407 statut
= models
.ForeignKey('Statut', related_name
='+', default
=3)
408 organisme_bstg
= models
.ForeignKey('OrganismeBstg',
409 db_column
='organisme_bstg',
411 verbose_name
=u
"Organisme",
412 help_text
=u
"Si détaché (DET) ou \
413 mis à disposition (MAD), \
414 préciser l'organisme.",
415 null
=True, blank
=True)
418 remplacement
= models
.BooleanField(default
=False)
419 statut_residence
= models
.CharField(max_length
=10, default
='local',
420 verbose_name
=u
"Statut",
421 choices
=STATUT_RESIDENCE_CHOICES
)
424 classement
= models
.ForeignKey('Classement', db_column
='classement',
426 null
=True, blank
=True)
427 regime_travail
= models
.DecimalField(max_digits
=12,
429 default
=REGIME_TRAVAIL_DEFAULT
,
430 verbose_name
=u
"Régime de travail",
431 help_text
=u
"% du temps complet")
432 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
434 default
=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT
,
435 verbose_name
=u
"Nb. heures par semaine")
437 # Occupation du Poste par cet Employe (anciennement "mandat")
438 date_debut
= models
.DateField(verbose_name
=u
"Date de début d'occupation \
440 help_text
=HELP_TEXT_DATE
)
441 date_fin
= models
.DateField(verbose_name
=u
"Date de fin d'occupation \
443 help_text
=HELP_TEXT_DATE
,
444 null
=True, blank
=True)
451 ordering
= ['poste__nom', 'employe__nom_affichage']
453 def __unicode__(self
):
454 return u
'%s - %s' % (self
.poste
.nom
, self
.employe
)
457 class Dossier(Dossier_
):
458 __doc__
= Dossier_
.__doc__
461 class DossierPiece(models
.Model
):
462 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
463 Ex.: Lettre de motivation.
465 dossier
= models
.ForeignKey("Dossier", db_column
='dossier',
467 nom
= models
.CharField(verbose_name
=u
"Nom", max_length
=255)
468 fichier
= models
.FileField(verbose_name
=u
"Fichier",
469 upload_to
=dossier_piece_dispatch
,
470 storage
=storage_prive
)
475 def __unicode__(self
):
476 return u
'%s' % (self
.nom
)
478 class DossierCommentaire(Commentaire
):
479 dossier
= models
.ForeignKey("Dossier", db_column
='dossier',
485 class RemunerationMixin(Metadata
):
487 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
488 related_name
='%(app_label)s_%(class)s_remunerations')
489 type = models
.ForeignKey('TypeRemuneration', db_column
='type',
491 type_revalorisation
= models
.ForeignKey('TypeRevalorisation',
492 db_column
='type_revalorisation',
494 null
=True, blank
=True)
495 montant
= models
.FloatField(null
=True, blank
=True,
497 # Annuel (12 mois, 52 semaines, 364 jours?)
498 devise
= models
.ForeignKey('Devise', to_field
='id',
499 db_column
='devise', related_name
='+',
501 # commentaire = precision
502 commentaire
= models
.CharField(max_length
=255, null
=True, blank
=True)
503 # date_debut = anciennement date_effectif
504 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
,
505 null
=True, blank
=True)
506 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
507 null
=True, blank
=True)
511 ordering
= ['type__nom', '-date_fin']
513 def __unicode__(self
):
514 return u
'%s %s (%s)' % (self
.montant
, self
.devise
.code
, self
.type.nom
)
516 class Remuneration_(RemunerationMixin
):
517 """Structure de rémunération (données budgétaires) en situation normale
518 pour un Dossier. Si un Evenement existe, utiliser la structure de
519 rémunération EvenementRemuneration de cet événement.
522 def montant_mois(self
):
523 return round(self
.montant
/ 12, 2)
525 def taux_devise(self
):
526 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
528 def montant_euro(self
):
529 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
531 def montant_euro_mois(self
):
532 return round(self
.montant_euro() / 12, 2)
534 def __unicode__(self
):
536 devise
= self
.devise
.code
539 return "%s %s" % (self
.montant
, devise
)
545 class Remuneration(Remuneration_
):
546 __doc__
= Remuneration_
.__doc__
551 class Contrat(Metadata
):
552 """Document juridique qui encadre la relation de travail d'un Employe
553 pour un Poste particulier. Pour un Dossier (qui documente cette
554 relation de travail) plusieurs contrats peuvent être associés.
556 dossier
= models
.ForeignKey('Dossier', db_column
='dossier',
558 type_contrat
= models
.ForeignKey('TypeContrat', db_column
='type_contrat',
560 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
)
561 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
562 null
=True, blank
=True)
565 ordering
= ['dossier__employe__nom_affichage']
567 def __unicode__(self
):
568 return u
'%s - %s' % (self
.dossier
.employe
.nom_affichage
, self
.id)
570 # TODO? class ContratPiece(models.Model):
575 class Evenement_(Metadata
):
576 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
577 d'un Dossier qui vient altérer des informations normales liées à un Dossier
578 (ex.: la Remuneration).
580 Ex.: congé de maternité, maladie...
582 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
583 différent et une rémunération en conséquence. On souhaite toutefois
584 conserver le Dossier intact afin d'éviter une re-saisie des données lors
585 du retour à la normale.
587 dossier
= models
.ForeignKey("Dossier", db_column
='dossier',
589 nom
= models
.CharField(max_length
=255)
590 date_debut
= models
.DateField(help_text
=HELP_TEXT_DATE
)
591 date_fin
= models
.DateField(help_text
=HELP_TEXT_DATE
,
592 null
=True, blank
=True)
598 def __unicode__(self
):
599 return u
'%s' % (self
.nom
)
602 class Evenement(Evenement_
):
603 __doc__
= Evenement_
.__doc__
606 class EvenementRemuneration_(RemunerationMixin
):
607 """Structure de rémunération liée à un Evenement qui remplace
608 temporairement la Remuneration normale d'un Dossier, pour toute la durée
611 evenement
= models
.ForeignKey("Evenement", db_column
='evenement',
618 class EvenementRemuneration(EvenementRemuneration_
):
619 __doc__
= EvenementRemuneration_
.__doc__
624 class FamilleEmploi(Metadata
):
625 """Catégorie utilisée dans la gestion des Postes.
626 Catégorie supérieure à TypePoste.
628 nom
= models
.CharField(max_length
=255)
630 def __unicode__(self
):
631 return u
'%s' % (self
.nom
)
633 class TypePoste(Metadata
):
634 """Catégorie de Poste.
636 nom
= models
.CharField(max_length
=255)
637 nom_feminin
= models
.CharField(max_length
=255)
639 is_responsable
= models
.BooleanField(default
=False)
640 famille_emploi
= models
.ForeignKey('FamilleEmploi',
641 db_column
='famille_emploi',
647 def __unicode__(self
):
648 # TODO : gérer nom féminin
649 return u
'%s' % (self
.nom
)
652 TYPE_PAIEMENT_CHOICES
= (
653 ('Régulier', 'Régulier'),
654 ('Ponctuel', 'Ponctuel'),
657 NATURE_REMUNERATION_CHOICES
= (
658 ('Accessoire', 'Accessoire'),
659 ('Charges', 'Charges'),
660 ('Indemnité', 'Indemnité'),
661 ('RAS', 'Rémunération autre source'),
662 ('Traitement', 'Traitement'),
665 class TypeRemuneration(Metadata
):
666 """Catégorie de Remuneration.
668 nom
= models
.CharField(max_length
=255)
669 type_paiement
= models
.CharField(max_length
=30,
670 choices
=TYPE_PAIEMENT_CHOICES
)
671 nature_remuneration
= models
.CharField(max_length
=30,
672 choices
=NATURE_REMUNERATION_CHOICES
)
674 def __unicode__(self
):
675 return u
'%s' % (self
.nom
)
677 class TypeRevalorisation(Metadata
):
678 """Justification du changement de la Remuneration.
679 (Actuellement utilisé dans aucun traitement informatique.)
681 nom
= models
.CharField(max_length
=255)
683 def __unicode__(self
):
684 return u
'%s' % (self
.nom
)
686 class Service(Metadata
):
687 """Unité administrative où les Postes sont rattachés.
689 nom
= models
.CharField(max_length
=255)
694 def __unicode__(self
):
695 return u
'%s' % (self
.nom
)
698 TYPE_ORGANISME_CHOICES
= (
699 ('MAD', 'Mise à disposition'),
700 ('DET', 'Détachement'),
703 class OrganismeBstg(Metadata
):
704 """Organisation d'où provient un Employe mis à disposition (MAD) de
705 ou détaché (DET) à l'AUF à titre gratuit.
707 (BSTG = bien et service à titre gratuit.)
709 nom
= models
.CharField(max_length
=255)
710 type = models
.CharField(max_length
=10, choices
=TYPE_ORGANISME_CHOICES
)
711 pays
= models
.ForeignKey(ref
.Pays
, to_field
='code',
713 related_name
='organismes_bstg',
714 null
=True, blank
=True)
717 ordering
= ['type', 'nom']
719 def __unicode__(self
):
720 return u
'%s (%s)' % (self
.nom
, self
.type)
722 class Statut(Metadata
):
723 """Statut de l'Employe dans le cadre d'un Dossier particulier.
726 code
= models
.CharField(max_length
=25, unique
=True)
727 nom
= models
.CharField(max_length
=255)
732 def __unicode__(self
):
733 return u
'%s : %s' % (self
.code
, self
.nom
)
736 TYPE_CLASSEMENT_CHOICES
= (
738 ('T', 'T - Technicien'),
739 ('P', 'P - Professionel'),
741 ('D', 'D - Direction'),
742 ('SO', 'SO - Sans objet [expatriés]'),
743 ('HG', 'HG - Hors grille [direction]'),
747 class Classement_(Metadata
):
748 """Éléments de classement de la
749 "Grille générique de classement hiérarchique".
751 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
752 classement dans la grille. Le classement donne le coefficient utilisé dans:
754 salaire de base = coefficient * valeur du point de l'Implantation du Poste
757 type = models
.CharField(max_length
=10, choices
=TYPE_CLASSEMENT_CHOICES
)
758 echelon
= models
.IntegerField()
759 degre
= models
.IntegerField()
760 coefficient
= models
.FloatField(default
=0)
762 # annee # au lieu de date_debut et date_fin
763 commentaire
= models
.TextField(null
=True, blank
=True)
767 ordering
= ['type','echelon','degre','coefficient']
769 def __unicode__(self
):
770 return u
'%s.%s.%s (%s)' % (self
.type, self
.echelon
, self
.degre
,
773 class Classement(Classement_
):
774 __doc__
= Classement_
.__doc__
777 class TauxChange_(Metadata
):
778 """Taux de change de la devise vers l'euro (EUR)
779 pour chaque année budgétaire.
782 devise
= models
.ForeignKey('Devise', to_field
='code', db_column
='devise',
784 annee
= models
.IntegerField()
785 taux
= models
.FloatField()
789 ordering
= ['annee', 'devise__code']
791 def __unicode__(self
):
792 return u
'%s : %s €' % (self
.devise
.code
, self
.taux
)
795 class TauxChange(TauxChange_
):
796 __doc__
= TauxChange_
.__doc__
799 class ValeurPoint_(Metadata
):
800 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
801 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
802 du POste de ce Dossier : dossier.poste.implantation (pseudo code).
804 salaire de base = coefficient * valeur du point de l'Implantation du Poste
806 valeur
= models
.FloatField()
807 devise
= models
.ForeignKey('Devise', db_column
='devise',
808 related_name
='+', default
=5)
809 implantation
= models
.ForeignKey(ref
.Implantation
,
810 db_column
='implantation',
811 related_name
='%(app_label)s_valeur_point')
813 annee
= models
.IntegerField()
815 # Stockage de tous les taux de change
816 # pour optimiser la recherche de la devise associée
817 annee_courante
= datetime
.datetime
.now().year
818 tauxchange
= TauxChange
.objects
.select_related('devise') \
819 .filter(annee
=annee_courante
)
823 ordering
= ['valeur']
825 def get_tauxchange_courant(self
):
827 Recherche le taux courant associé à la valeur d'un point.
828 Tous les taux de l'année courante sont chargés, pour optimiser un
829 affichage en liste. (On pourrait probablement améliorer le manager pour
830 lui greffer le taux courant sous forme de JOIN)
832 for tauxchange
in self
.tauxchange
:
833 if tauxchange
.implantation_id
== self
.implantation_id
:
837 def __unicode__(self
):
838 tx
= self
.get_tauxchange_courant()
840 devise_code
= tx
.devise
.code
843 return u
'%s %s (%s-%s)' % (self
.valeur
, devise_code
,
844 self
.implantation_id
, self
.annee
)
847 class ValeurPoint(ValeurPoint_
):
848 __doc__
= ValeurPoint_
.__doc__
851 class Devise(Metadata
):
854 code
= models
.CharField(max_length
=10, unique
=True)
855 nom
= models
.CharField(max_length
=255)
860 def __unicode__(self
):
861 return u
'%s - %s' % (self
.code
, self
.nom
)
863 class TypeContrat(Metadata
):
866 nom
= models
.CharField(max_length
=255)
867 nom_long
= models
.CharField(max_length
=255)
869 def __unicode__(self
):
870 return u
'%s' % (self
.nom
)
875 class ResponsableImplantation(Metadata
):
876 """Le responsable d'une implantation.
877 Anciennement géré sur le Dossier du responsable.
879 employe
= models
.ForeignKey('Employe', db_column
='employe',
881 null
=True, blank
=True)
882 implantation
= models
.ForeignKey(ref
.Implantation
,
883 db_column
='implantation', related_name
='+',
886 def __unicode__(self
):
887 return u
'%s : %s' % (self
.implantation
, self
.employe
)
890 ordering
= ['implantation__nom']