fix data
[auf_rh_dae.git] / project / rh / models.py
CommitLineData
e9bbd6ba 1# -=- encoding: utf-8 -=-
2
a4125771 3import datetime
c267f20c 4from datetime import date
5
83b7692b 6from django.core.files.storage import FileSystemStorage
49f9f116 7from django.db import models
d6985a3a 8from django.conf import settings
c267f20c 9
d6985a3a
OL
10from auf.django.metadata.models import AUFMetadata
11from auf.django.metadata.managers import NoDeleteManager
bf6f2712 12import auf.django.references.models as ref
b4aeadf3 13from validators import validate_date_passee
16b1454e 14from managers import PosteManager, DossierManager, DossierComparaisonManager, PosteComparaisonManager
83b7692b 15
a4125771
OL
16
17
18# Gruick hack pour déterminer d'ou provient l'instanciation d'une classe pour l'héritage.
19# Cela permet de faire du dynamic loading par app sans avoir à redéfinir dans DAE la FK
20def app_context():
21 import inspect;
22 models_stack = [s[1].split('/')[-2] for s in inspect.stack() if s[1].endswith('models.py')]
23 return models_stack[-1]
24
25
2d4d2fcf 26# Constantes
6e4600ef 27REGIME_TRAVAIL_DEFAULT = 100.00
28REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT = 35.00
2d4d2fcf 29
83b7692b 30# Upload de fichiers
31storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
32 base_url=settings.PRIVE_MEDIA_URL)
33
34def poste_piece_dispatch(instance, filename):
35 path = "poste/%s/%s" % (instance.poste_id, filename)
36 return path
37
38def dossier_piece_dispatch(instance, filename):
39 path = "dossier/%s/%s" % (instance.dossier_id, filename)
40 return path
41
5ea6b5bb 42def employe_piece_dispatch(instance, filename):
43 path = "employe/%s/%s" % (instance.employe_id, filename)
44 return path
45
5f5a4f06 46
d6985a3a 47class Commentaire(AUFMetadata):
2d4d2fcf 48 texte = models.TextField()
6e4600ef 49 owner = models.ForeignKey('auth.User', db_column='owner', related_name='+')
2d4d2fcf 50
51 class Meta:
52 abstract = True
6e4600ef 53 ordering = ['-date_creation']
54
55 def __unicode__(self):
56 return u'%s' % (self.texte)
07b40eda 57
83b7692b 58
59### POSTE
60
61POSTE_APPEL_CHOICES = (
62 ('interne', 'Interne'),
63 ('externe', 'Externe'),
64)
65
d6985a3a 66class Poste_(AUFMetadata):
6e4600ef 67 """Un Poste est un emploi (job) à combler dans une implantation.
68 Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
69 Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
70 """
1f2979b8
OL
71
72 objects = PosteManager()
73
83b7692b 74 # Identification
2d4d2fcf 75 nom = models.CharField(max_length=255,
c1195471 76 verbose_name = u"Titre du poste", )
2d4d2fcf 77 nom_feminin = models.CharField(max_length=255,
c1195471 78 verbose_name = u"Titre du poste (au féminin)",
6e4600ef 79 null=True)
80 implantation = models.ForeignKey(ref.Implantation,
81 db_column='implantation', related_name='+')
82 type_poste = models.ForeignKey('TypePoste', db_column='type_poste',
83 related_name='+',
84 null=True)
8277a35b 85 service = models.ForeignKey('Service', db_column='service', null=True,
6e4600ef 86 related_name='+',
3f5cbabe 87 verbose_name = u"Direction/Service/Pôle support", )
6e4600ef 88 responsable = models.ForeignKey('Poste', db_column='responsable',
8277a35b 89 related_name='+', null=True,
3f5cbabe 90 verbose_name = u"Poste du responsable", )
83b7692b 91
92 # Contrat
93 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 94 default=REGIME_TRAVAIL_DEFAULT, null=True,
c1195471 95 verbose_name = u"Temps de travail",
8c1ae2b3 96 help_text="% du temps complet")
83b7692b 97 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
8277a35b 98 decimal_places=2, null=True,
2d4d2fcf 99 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
c1195471 100 verbose_name = u"Nb. heures par semaine")
83b7692b 101
102 # Recrutement
c1195471 103 local = models.NullBooleanField(verbose_name = u"Local", default=True,
8277a35b 104 null=True, blank=True)
c1195471 105 expatrie = models.NullBooleanField(verbose_name = u"Expatrié", default=False,
8277a35b
NC
106 null=True, blank=True)
107 mise_a_disposition = models.NullBooleanField(
c1195471 108 verbose_name = u"Mise à disposition",
8277a35b
NC
109 null=True, default=False)
110 appel = models.CharField(max_length=10, null=True,
c1195471 111 verbose_name = u"Appel à candidature",
6e4600ef 112 choices=POSTE_APPEL_CHOICES,
113 default='interne')
83b7692b 114
115 # Rémunération
6e4600ef 116 classement_min = models.ForeignKey('Classement',
117 db_column='classement_min', related_name='+',
118 null=True, blank=True)
119 classement_max = models.ForeignKey('Classement',
120 db_column='classement_max', related_name='+',
121 null=True, blank=True)
122 valeur_point_min = models.ForeignKey('ValeurPoint',
123 db_column='valeur_point_min', related_name='+',
124 null=True, blank=True)
125 valeur_point_max = models.ForeignKey('ValeurPoint',
126 db_column='valeur_point_max', related_name='+',
127 null=True, blank=True)
8277a35b 128 devise_min = models.ForeignKey('Devise', db_column='devise_min', null=True,
6e4600ef 129 related_name='+', default=5)
8277a35b 130 devise_max = models.ForeignKey('Devise', db_column='devise_max', null=True,
6e4600ef 131 related_name='+', default=5)
83b7692b 132 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 133 null=True, default=0)
83b7692b 134 salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 135 null=True, default=0)
83b7692b 136 indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 137 null=True, default=0)
83b7692b 138 indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 139 null=True, default=0)
83b7692b 140 autre_min = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 141 null=True, default=0)
83b7692b 142 autre_max = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 143 null=True, default=0)
83b7692b 144
145 # Comparatifs de rémunération
8277a35b 146 devise_comparaison = models.ForeignKey('Devise', null=True,
6e4600ef 147 db_column='devise_comparaison',
148 related_name='+',
149 default=5)
83b7692b 150 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 151 null=True, blank=True)
83b7692b 152 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 153 null=True, blank=True)
83b7692b 154 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 155 null=True, blank=True)
83b7692b 156 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 157 null=True, blank=True)
83b7692b 158 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 159 null=True, blank=True)
83b7692b 160 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 161 null=True, blank=True)
83b7692b 162 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 163 null=True, blank=True)
83b7692b 164 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 165 null=True, blank=True)
83b7692b 166 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 167 null=True, blank=True)
83b7692b 168 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 169 null=True, blank=True)
83b7692b 170
171 # Justification
6e4600ef 172 justification = models.TextField(null=True, blank=True)
83b7692b 173
2d4d2fcf 174 # Autres Metadata
07b40eda 175 date_validation = models.DateTimeField(null=True, blank=True) # de dae
3f5f3898 176 date_debut = models.DateField(verbose_name=u"Date de début",
7332d8f9 177 null=True, blank=True)
3f5f3898 178 date_fin = models.DateField(verbose_name=u"Date de fin",
6e4600ef 179 null=True, blank=True)
180
181 class Meta:
37868f0b 182 abstract = True
6e4600ef 183 ordering = ['implantation__nom', 'nom']
c1195471
OL
184 verbose_name = u"Poste"
185 verbose_name_plural = u"Postes"
6e4600ef 186
83b7692b 187 def __unicode__(self):
8c1ae2b3 188 representation = u'%s - %s [%s]' % (self.implantation, self.nom,
189 self.id)
190 if self.is_vacant():
23102192 191 representation = representation + u' (VACANT)'
8c1ae2b3 192 return representation
193
194 def is_vacant(self):
23102192
DB
195 vacant = True
196 if self.occupe_par():
197 vacant = False
198 return vacant
199
200 def occupe_par(self):
201 """Retourne la liste d'employé occupant ce poste.
202 Généralement, retourne une liste d'un élément.
203 Si poste inoccupé, retourne liste vide.
204 """
16b1454e 205 return [d.employe for d in self.rh_dossiers.filter(actif=True, supprime=False) \
23102192 206 .exclude(date_fin__lt=date.today())]
83b7692b 207
aff1a4c6
PP
208 prefix_implantation = "implantation__region"
209 def get_regions(self):
210 return [self.implantation.region]
211
83b7692b 212
37868f0b
NC
213class Poste(Poste_):
214 __doc__ = Poste_.__doc__
215
216
769a0755
NC
217class Poste(Poste_):
218 __doc__ = Poste_.__doc__
219
220
83b7692b 221POSTE_FINANCEMENT_CHOICES = (
222 ('A', 'A - Frais de personnel'),
223 ('B', 'B - Projet(s)-Titre(s)'),
224 ('C', 'C - Autre')
225)
226
6e7c919b
NC
227
228class PosteFinancement_(models.Model):
6e4600ef 229 """Pour un Poste, structure d'informations décrivant comment on prévoit
230 financer ce Poste.
231 """
a4125771 232 poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='%(app_label)s_financements')
83b7692b 233 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
234 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
8c1ae2b3 235 help_text="ex.: 33.33 % (décimale avec point)")
83b7692b 236 commentaire = models.TextField(
8c1ae2b3 237 help_text="Spécifiez la source de financement.")
83b7692b 238
239 class Meta:
6e7c919b 240 abstract = True
83b7692b 241 ordering = ['type']
6e4600ef 242
243 def __unicode__(self):
244 return u'%s : %s %' % (self.type, self.pourcentage)
83b7692b 245
6e7c919b
NC
246
247class PosteFinancement(PosteFinancement_):
a4125771 248 pass
6e7c919b
NC
249
250
317ce433 251class PostePiece_(models.Model):
6e4600ef 252 """Documents relatifs au Poste.
7abc6d45 253 Ex.: Description de poste
254 """
a4125771 255 poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='%(app_label)s_pieces')
c1195471
OL
256 nom = models.CharField(verbose_name = u"Nom", max_length=255)
257 fichier = models.FileField(verbose_name = u"Fichier",
83b7692b 258 upload_to=poste_piece_dispatch,
259 storage=storage_prive)
260
6e4600ef 261 class Meta:
317ce433 262 abstract = True
6e4600ef 263 ordering = ['nom']
264
265 def __unicode__(self):
266 return u'%s' % (self.nom)
267
317ce433 268class PostePiece(PostePiece_):
a4125771 269 pass
317ce433
OL
270
271class PosteComparaison_(models.Model):
068d1462
OL
272 """
273 De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
274 """
a4125771 275 poste = models.ForeignKey('%s.Poste' % app_context(), related_name='%(app_label)s_comparaisons_internes')
16b1454e
OL
276 objects = PosteComparaisonManager()
277
0f0dacbb 278 implantation = models.ForeignKey(ref.Implantation, null=True, blank=True, related_name="+")
c1195471 279 nom = models.CharField(verbose_name = u"Poste", max_length=255, null=True, blank=True)
068d1462
OL
280 montant = models.IntegerField(null=True)
281 devise = models.ForeignKey("Devise", default=5, related_name='+', null=True, blank=True)
1d0f4eef 282
317ce433
OL
283 class Meta:
284 abstract = True
285
1d0f4eef 286 def taux_devise(self):
a4125771
OL
287 if self.devise.code == "EUR":
288 return 1
289 annee = self.poste.date_debut.year
290 taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise, annee=annee)]
291 taux = set(taux)
292 if len(taux) != 1:
293 raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
1d0f4eef 294 else:
a4125771 295 return list(taux)[0]
1d0f4eef
OL
296
297 def montant_euros(self):
298 return round(float(self.montant) * float(self.taux_devise()), 2)
068d1462 299
317ce433 300class PosteComparaison(PosteComparaison_):
a4125771 301 pass
068d1462 302
317ce433 303class PosteCommentaire_(Commentaire):
a4125771 304 poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='+')
83b7692b 305
317ce433
OL
306 class Meta:
307 abstract = True
308
309class PosteCommentaire(PosteCommentaire_):
310 pass
2d4d2fcf 311
83b7692b 312### EMPLOYÉ/PERSONNE
e9bbd6ba 313
314GENRE_CHOICES = (
315 ('M', 'Homme'),
316 ('F', 'Femme'),
317)
318SITUATION_CHOICES = (
319 ('C', 'Célibataire'),
320 ('F', 'Fiancé'),
321 ('M', 'Marié'),
322)
323
d6985a3a 324class Employe(AUFMetadata):
6e4600ef 325 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
326 Dossiers qu'il occupe ou a occupé de Postes.
327
328 Cette classe aurait pu avantageusement s'appeler Personne car la notion
329 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
330 """
9afaa55e 331 # Identification
e9bbd6ba 332 nom = models.CharField(max_length=255)
c1195471 333 prenom = models.CharField(max_length=255, verbose_name = u"Prénom")
6e4600ef 334 nom_affichage = models.CharField(max_length=255,
c1195471 335 verbose_name = u"Nom d'affichage",
6e4600ef 336 null=True, blank=True)
83b7692b 337 nationalite = models.ForeignKey(ref.Pays, to_field='code',
6e4600ef 338 db_column='nationalite',
8c1ae2b3 339 related_name='employes_nationalite',
c1195471 340 verbose_name = u"Nationalité")
a25e1d5c 341 date_naissance = models.DateField(verbose_name = u"Date de naissance",
b4aeadf3 342 validators=[validate_date_passee],
6e4600ef 343 null=True, blank=True)
2d4d2fcf 344 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
83b7692b 345
9afaa55e 346 # Infos personnelles
6e4600ef 347 situation_famille = models.CharField(max_length=1,
348 choices=SITUATION_CHOICES,
c1195471 349 verbose_name = u"Situation familiale",
6e4600ef 350 null=True, blank=True)
c1195471 351 date_entree = models.DateField(verbose_name = u"Date d'entrée à l'AUF",
7abc6d45 352 null=True, blank=True)
83b7692b 353
9afaa55e 354 # Coordonnées
8c1ae2b3 355 tel_domicile = models.CharField(max_length=255,
c1195471 356 verbose_name = u"Tél. domicile",
8c1ae2b3 357 null=True, blank=True)
358 tel_cellulaire = models.CharField(max_length=255,
c1195471 359 verbose_name = u"Tél. cellulaire",
8c1ae2b3 360 null=True, blank=True)
e9bbd6ba 361 adresse = models.CharField(max_length=255, null=True, blank=True)
e9bbd6ba 362 ville = models.CharField(max_length=255, null=True, blank=True)
363 province = models.CharField(max_length=255, null=True, blank=True)
364 code_postal = models.CharField(max_length=255, null=True, blank=True)
6e4600ef 365 pays = models.ForeignKey(ref.Pays, to_field='code', db_column='pays',
366 related_name='employes',
367 null=True, blank=True)
9afaa55e 368
6e4600ef 369 class Meta:
67666927 370 ordering = ['nom_affichage','nom','prenom']
c1195471
OL
371 verbose_name = u"Employé"
372 verbose_name_plural = u"Employés"
6e4600ef 373
9afaa55e 374 def __unicode__(self):
54773196 375 return u'%s [%s]' % (self.get_nom(), self.id)
8c1ae2b3 376
377 def get_nom(self):
ebc8eb32 378 nom_affichage = self.nom_affichage
379 if not nom_affichage:
380 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
8c1ae2b3 381 return nom_affichage
d04d084c 382
383 def civilite(self):
384 civilite = u''
385 if self.genre.upper() == u'M':
386 civilite = u'M.'
387 elif self.genre.upper() == u'F':
388 civilite = u'Mme'
389 return civilite
5ea6b5bb 390
391 def url_photo(self):
392 """Retourne l'URL du service retournant la photo de l'Employe.
393 Équivalent reverse url 'rh_photo' avec id en param.
394 """
395 from django.core.urlresolvers import reverse
396 return reverse('rh_photo', kwargs={'id':self.id})
35c0c2fe 397
c267f20c 398 def dossiers_passes(self):
399 today = date.today()
65f9fac8 400 dossiers_passes = self.dossiers.filter(date_fin__lt=today).order_by('-date_fin')
401 for d in dossiers_passes:
402 d.archive = True
403 return dossiers_passes
c267f20c 404
405 def dossiers_futurs(self):
406 today = date.today()
407 return self.dossiers.filter(date_debut__gt=today).order_by('-date_fin')
408
409 def dossiers_encours(self):
410 dossiers_p_f = self.dossiers_passes() | self.dossiers_futurs()
411 ids_dossiers_p_f = [d.id for d in dossiers_p_f]
65f9fac8 412 dossiers_encours = self.dossiers.exclude(id__in=ids_dossiers_p_f).order_by('-date_fin')
413
414 # TODO : supprimer ce code quand related_name fonctionnera ou d.remuneration_set
415 for d in dossiers_encours:
416 d.remunerations = Remuneration.objects.filter(dossier=d.id).order_by('-id')
417 return dossiers_encours
35c0c2fe 418
419 def postes_encours(self):
420 postes_encours = set()
421 for d in self.dossiers_encours():
422 postes_encours.add(d.poste)
423 return postes_encours
424
425 def poste_principal(self):
65f9fac8 426 """
427 Retourne le Poste du premier Dossier créé parmi les Dossiers en cours.
428 Idée derrière :
429 si on ajout d'autre Dossiers, c'est pour des Postes secondaires.
430 """
431 poste = Poste.objects.none()
432 try:
433 poste = self.dossiers_encours().order_by('date_debut')[0].poste
434 except:
435 pass
436 return poste
9afaa55e 437
aff1a4c6
PP
438 prefix_implantation = "dossiers__poste__implantation__region"
439 def get_regions(self):
440 regions = []
441 for d in self.dossiers.all():
442 regions.append(d.poste.implantation.region)
443 return regions
444
445
7abc6d45 446class EmployePiece(models.Model):
6e4600ef 447 """Documents relatifs à un employé.
7abc6d45 448 Ex.: CV...
449 """
f9e54d59 450 employe = models.ForeignKey('Employe', db_column='employe')
8c1ae2b3 451 nom = models.CharField(verbose_name="Nom", max_length=255)
452 fichier = models.FileField(verbose_name="Fichier",
5ea6b5bb 453 upload_to=employe_piece_dispatch,
7abc6d45 454 storage=storage_prive)
455
6e4600ef 456 class Meta:
457 ordering = ['nom']
f9e54d59
PP
458 verbose_name = u"Employé pièce"
459 verbose_name_plural = u"Employé pièces"
460
6e4600ef 461 def __unicode__(self):
462 return u'%s' % (self.nom)
463
07b40eda 464class EmployeCommentaire(Commentaire):
8c1ae2b3 465 employe = models.ForeignKey('Employe', db_column='employe',
6e4600ef 466 related_name='+')
9afaa55e 467
b343eb3d
PP
468 class Meta:
469 verbose_name = u"Employé commentaire"
470 verbose_name_plural = u"Employé commentaires"
471
2d4d2fcf 472
e9bbd6ba 473LIEN_PARENTE_CHOICES = (
474 ('Conjoint', 'Conjoint'),
475 ('Conjointe', 'Conjointe'),
476 ('Fille', 'Fille'),
477 ('Fils', 'Fils'),
478)
479
d6985a3a 480class AyantDroit(AUFMetadata):
6e4600ef 481 """Personne en relation avec un Employe.
482 """
9afaa55e 483 # Identification
e9bbd6ba 484 nom = models.CharField(max_length=255)
8c1ae2b3 485 prenom = models.CharField(max_length=255,
c1195471 486 verbose_name = u"Prénom",)
6e4600ef 487 nom_affichage = models.CharField(max_length=255,
c1195471 488 verbose_name = u"Nom d'affichage",
6e4600ef 489 null=True, blank=True)
2d4d2fcf 490 nationalite = models.ForeignKey(ref.Pays, to_field='code',
6e4600ef 491 db_column='nationalite',
8c1ae2b3 492 related_name='ayantdroits_nationalite',
c1195471 493 verbose_name = u"Nationalité")
a25e1d5c 494 date_naissance = models.DateField(verbose_name = u"Date de naissance",
25037368 495 validators=[validate_date_passee],
6e4600ef 496 null=True, blank=True)
2d4d2fcf 497 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
83b7692b 498
9afaa55e 499 # Relation
500 employe = models.ForeignKey('Employe', db_column='employe',
8c1ae2b3 501 related_name='ayantdroits',
c1195471 502 verbose_name = u"Employé")
6e4600ef 503 lien_parente = models.CharField(max_length=10,
504 choices=LIEN_PARENTE_CHOICES,
c1195471 505 verbose_name = u"Lien de parenté",
6e4600ef 506 null=True, blank=True)
507
508 class Meta:
509 ordering = ['nom_affichage']
c1195471
OL
510 verbose_name = u"Ayant droit"
511 verbose_name_plural = u"Ayants droit"
8c1ae2b3 512
6e4600ef 513 def __unicode__(self):
8c1ae2b3 514 return u'%s' % (self.get_nom())
515
516 def get_nom(self):
517 nom_affichage = self.nom_affichage
518 if not nom_affichage:
519 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
520 return nom_affichage
83b7692b 521
aff1a4c6
PP
522 prefix_implantation = "employe__dossiers__poste__implantation__region"
523 def get_regions(self):
524 regions = []
525 for d in self.employe.dossiers.all():
526 regions.append(d.poste.implantation.region)
527 return regions
528
529
07b40eda 530class AyantDroitCommentaire(Commentaire):
8c1ae2b3 531 ayant_droit = models.ForeignKey('AyantDroit', db_column='ayant_droit',
6e4600ef 532 related_name='+')
83b7692b 533
2d4d2fcf 534
83b7692b 535### DOSSIER
536
537STATUT_RESIDENCE_CHOICES = (
538 ('local', 'Local'),
539 ('expat', 'Expatrié'),
540)
541
542COMPTE_COMPTA_CHOICES = (
543 ('coda', 'CODA'),
544 ('scs', 'SCS'),
545 ('aucun', 'Aucun'),
546)
547
d6985a3a 548class Dossier_(AUFMetadata):
6e4600ef 549 """Le Dossier regroupe les informations relatives à l'occupation
550 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
551 par un Employe.
552
553 Plusieurs Contrats peuvent être associés au Dossier.
554 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
555 lequel aucun Dossier n'existe est un poste vacant.
556 """
3f5cbabe
OL
557
558 objects = DossierManager()
559
63e17dff 560 # TODO: OneToOne ??
8277a35b
NC
561 statut = models.ForeignKey('Statut', related_name='+', default=3,
562 null=True)
83b7692b 563 organisme_bstg = models.ForeignKey('OrganismeBstg',
6e4600ef 564 db_column='organisme_bstg',
565 related_name='+',
c1195471 566 verbose_name = u"Organisme",
8c1ae2b3 567 help_text="Si détaché (DET) ou \
6e4600ef 568 mis à disposition (MAD), \
569 préciser l'organisme.",
570 null=True, blank=True)
571
83b7692b 572 # Recrutement
2d4d2fcf 573 remplacement = models.BooleanField(default=False)
7c182958
PP
574 remplacement_de = models.ForeignKey('self', related_name='+',
575 null=True, blank=True)
83b7692b 576 statut_residence = models.CharField(max_length=10, default='local',
c1195471 577 verbose_name = u"Statut", null=True,
2d4d2fcf 578 choices=STATUT_RESIDENCE_CHOICES)
83b7692b 579
580 # Rémunération
6e4600ef 581 classement = models.ForeignKey('Classement', db_column='classement',
582 related_name='+',
583 null=True, blank=True)
8277a35b 584 regime_travail = models.DecimalField(max_digits=12, null=True,
2d4d2fcf 585 decimal_places=2,
586 default=REGIME_TRAVAIL_DEFAULT,
c1195471 587 verbose_name = u"Régime de travail",
8c1ae2b3 588 help_text="% du temps complet")
83b7692b 589 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
8277a35b 590 decimal_places=2, null=True,
2d4d2fcf 591 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
c1195471 592 verbose_name = u"Nb. heures par semaine")
7abc6d45 593
594 # Occupation du Poste par cet Employe (anciennement "mandat")
c1195471 595 date_debut = models.DateField(verbose_name = u"Date de début d'occupation \
a25e1d5c 596 de poste",)
c1195471 597 date_fin = models.DateField(verbose_name = u"Date de fin d'occupation \
6e4600ef 598 de poste",
6e4600ef 599 null=True, blank=True)
e9bbd6ba 600
2d4d2fcf 601 # Comptes
602 # TODO?
83b7692b 603
6e4600ef 604 class Meta:
37868f0b 605 abstract = True
49449367 606 ordering = ['employe__nom', ]
3f5f3898 607 verbose_name = u"Dossier"
8c1ae2b3 608 verbose_name_plural = "Dossiers"
6e4600ef 609
65f9fac8 610 def salaire_theorique(self):
611 annee = date.today().year
612 coeff = self.classement.coefficient
613 implantation = self.poste.implantation
614 point = ValeurPoint.objects.get(implantation=implantation, annee=annee)
615
616 montant = coeff * point.valeur
617 devise = point.devise
618 return {'montant':montant, 'devise':devise}
619
83b7692b 620 def __unicode__(self):
8c1ae2b3 621 poste = self.poste.nom
622 if self.employe.genre == 'F':
623 poste = self.poste.nom_feminin
624 return u'%s - %s' % (self.employe, poste)
83b7692b 625
aff1a4c6
PP
626 prefix_implantation = "poste__implantation__region"
627 def get_regions(self):
628 return [self.poste.implantation.region]
629
37868f0b 630
3ebc0952 631 def remunerations(self):
a4125771 632 return self.rh_remunerations.all().order_by('date_debut')
3ebc0952 633
09aa8374
OL
634 def get_salaire(self):
635 try:
636 return [r for r in self.remunerations().order_by('-date_debut') if r.type_id == 1][0]
637 except:
638 return None
3ebc0952 639
37868f0b
NC
640class Dossier(Dossier_):
641 __doc__ = Dossier_.__doc__
a4125771 642 poste = models.ForeignKey('%s.Poste' % app_context(), db_column='poste', related_name='%(app_label)s_dossiers')
16b1454e
OL
643 employe = models.ForeignKey('Employe', db_column='employe',
644 related_name='%(app_label)s_dossiers',
645 verbose_name=u"Employé")
37868f0b
NC
646
647
fc917340 648class DossierPiece_(models.Model):
7abc6d45 649 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
650 Ex.: Lettre de motivation.
651 """
a4125771 652 dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='+')
c1195471
OL
653 nom = models.CharField(verbose_name = u"Nom", max_length=255)
654 fichier = models.FileField(verbose_name = u"Fichier",
83b7692b 655 upload_to=dossier_piece_dispatch,
656 storage=storage_prive)
657
6e4600ef 658 class Meta:
fc917340 659 abstract = True
6e4600ef 660 ordering = ['nom']
661
662 def __unicode__(self):
663 return u'%s' % (self.nom)
664
fc917340 665class DossierPiece(DossierPiece_):
a4125771 666 pass
fc917340
OL
667
668class DossierCommentaire_(Commentaire):
a4125771 669 dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='+')
fc917340
OL
670 class Meta:
671 abstract = True
672
673class DossierCommentaire(DossierCommentaire_):
a4125771 674 pass
fc917340
OL
675
676class DossierComparaison_(models.Model):
1d0f4eef
OL
677 """
678 Photo d'une comparaison salariale au moment de l'embauche.
679 """
a4125771 680 dossier = models.ForeignKey('%s.Dossier' % app_context(), related_name='%(app_label)s_comparaisons')
16b1454e
OL
681 objects = DossierComparaisonManager()
682
8c6269cc 683 implantation = models.ForeignKey(ref.Implantation, related_name="+", null=True, blank=True)
1d0f4eef
OL
684 poste = models.CharField(max_length=255, null=True, blank=True)
685 personne = models.CharField(max_length=255, null=True, blank=True)
686 montant = models.IntegerField(null=True)
93494cf1 687 devise = models.ForeignKey('Devise', default=5, related_name='+', null=True, blank=True)
1d0f4eef 688
fc917340
OL
689 class Meta:
690 abstract = True
691
1d0f4eef 692 def taux_devise(self):
a4125771
OL
693 annee = self.dossier.poste.date_debut.year
694 taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise, annee=annee)]
695 taux = set(taux)
696 if len(taux) != 1:
697 raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
1d0f4eef 698 else:
a4125771 699 return list(taux)[0]
1d0f4eef
OL
700
701 def montant_euros(self):
702 return round(float(self.montant) * float(self.taux_devise()), 2)
703
fc917340 704class DossierComparaison(DossierComparaison_):
a4125771 705 pass
2d4d2fcf 706
07b40eda 707### RÉMUNÉRATION
e9bbd6ba 708
d6985a3a 709class RemunerationMixin(AUFMetadata):
a4125771 710 dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='%(app_label)s_remunerations')
9afaa55e 711 # Identification
83b7692b 712 type = models.ForeignKey('TypeRemuneration', db_column='type',
8c1ae2b3 713 related_name='+',
c1195471 714 verbose_name = u"Type de rémunération")
7abc6d45 715 type_revalorisation = models.ForeignKey('TypeRevalorisation',
716 db_column='type_revalorisation',
6e4600ef 717 related_name='+',
c1195471 718 verbose_name = u"Type de revalorisation",
7abc6d45 719 null=True, blank=True)
50fa9bc1 720 montant = models.FloatField(null=True, blank=True,
6e4600ef 721 default=0)
2d4d2fcf 722 # Annuel (12 mois, 52 semaines, 364 jours?)
a4125771 723 devise = models.ForeignKey('Devise', db_column='devise', related_name='+', default=5)
2d4d2fcf 724 # commentaire = precision
725 commentaire = models.CharField(max_length=255, null=True, blank=True)
726 # date_debut = anciennement date_effectif
a25e1d5c 727 date_debut = models.DateField(verbose_name = u"Date de début",
6e4600ef 728 null=True, blank=True)
a25e1d5c 729 date_fin = models.DateField(verbose_name = u"Date de fin",
6e4600ef 730 null=True, blank=True)
83b7692b 731
2d4d2fcf 732 class Meta:
733 abstract = True
6e4600ef 734 ordering = ['type__nom', '-date_fin']
735
736 def __unicode__(self):
737 return u'%s %s (%s)' % (self.montant, self.devise.code, self.type.nom)
2d4d2fcf 738
6e7c919b 739class Remuneration_(RemunerationMixin):
2d4d2fcf 740 """Structure de rémunération (données budgétaires) en situation normale
741 pour un Dossier. Si un Evenement existe, utiliser la structure de
742 rémunération EvenementRemuneration de cet événement.
743 """
83b7692b 744
745 def montant_mois(self):
746 return round(self.montant / 12, 2)
747
748 def taux_devise(self):
a4125771
OL
749 if self.devise.code == "EUR":
750 return 1
751
752 annee = datetime.datetime.now().year
753 if self.date_debut is not None:
754 annee = self.date_debut.year
755 if self.dossier.poste.date_debut is not None:
756 annee = self.dossier.poste.date_debut
757
758 taux = [tc.taux for tc in TauxChange.objects.filter(devise=self.devise_id, annee=annee)]
759 taux = set(taux)
760 if len(taux) != 1:
761 raise Exception(u"Le taux de la devise %s n'a pas n'existe pas pour %s ou il existe plusieurs taux pour la même année" % (self.devise.id, annee))
762 else:
763 return list(taux)[0]
83b7692b 764
765 def montant_euro(self):
a4125771 766 return round(float(self.montant) * float(self.taux_devise()), 2)
83b7692b 767
768 def montant_euro_mois(self):
769 return round(self.montant_euro() / 12, 2)
9afaa55e 770
771 def __unicode__(self):
772 try:
773 devise = self.devise.code
774 except:
775 devise = "???"
776 return "%s %s" % (self.montant, devise)
83b7692b 777
6e7c919b
NC
778 class Meta:
779 abstract = True
c1195471
OL
780 verbose_name = u"Rémunération"
781 verbose_name_plural = u"Rémunérations"
6e7c919b
NC
782
783
784class Remuneration(Remuneration_):
a4125771 785 pass
6e7c919b 786
2d4d2fcf 787
788### CONTRATS
c41b7fcc
OL
789
790class ContratManager(NoDeleteManager):
791 def get_query_set(self):
792 return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste')
793
2d4d2fcf 794
fc917340 795class Contrat_(AUFMetadata):
2d4d2fcf 796 """Document juridique qui encadre la relation de travail d'un Employe
797 pour un Poste particulier. Pour un Dossier (qui documente cette
798 relation de travail) plusieurs contrats peuvent être associés.
799 """
a4125771 800 dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier', related_name='%(app_label)s_contrats')
c41b7fcc
OL
801 objects = ContratManager()
802
6e4600ef 803 type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
8c1ae2b3 804 related_name='+',
c1195471 805 verbose_name = u"Type de contrat")
a25e1d5c
OL
806 date_debut = models.DateField(verbose_name = u"Date de début")
807 date_fin = models.DateField(verbose_name = u"Date de fin",
6e4600ef 808 null=True, blank=True)
809
810 class Meta:
fc917340 811 abstract = True
6e4600ef 812 ordering = ['dossier__employe__nom_affichage']
c1195471
OL
813 verbose_name = u"Contrat"
814 verbose_name_plural = u"Contrats"
6e4600ef 815
816 def __unicode__(self):
8c1ae2b3 817 return u'%s - %s' % (self.dossier, self.id)
fc917340
OL
818
819class Contrat(Contrat_):
a4125771 820 pass
6e4600ef 821
2d4d2fcf 822
823### ÉVÉNEMENTS
824
a4125771
OL
825#class Evenement_(AUFMetadata):
826# """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
827# d'un Dossier qui vient altérer des informations normales liées à un Dossier
828# (ex.: la Remuneration).
829#
830# Ex.: congé de maternité, maladie...
831#
832# Lors de ces situations exceptionnelles, l'Employe a un régime de travail
833# différent et une rémunération en conséquence. On souhaite toutefois
834# conserver le Dossier intact afin d'éviter une re-saisie des données lors
835# du retour à la normale.
836# """
837# dossier = models.ForeignKey('%s.Dossier' % app_context(), db_column='dossier',
838# related_name='+')
839# nom = models.CharField(max_length=255)
840# date_debut = models.DateField(verbose_name = u"Date de début")
841# date_fin = models.DateField(verbose_name = u"Date de fin",
842# null=True, blank=True)
843#
844# class Meta:
845# abstract = True
846# ordering = ['nom']
847# verbose_name = u"Évènement"
848# verbose_name_plural = u"Évènements"
849#
850# def __unicode__(self):
851# return u'%s' % (self.nom)
852#
853#
854#class Evenement(Evenement_):
855# __doc__ = Evenement_.__doc__
856#
857#
858#class EvenementRemuneration_(RemunerationMixin):
859# """Structure de rémunération liée à un Evenement qui remplace
860# temporairement la Remuneration normale d'un Dossier, pour toute la durée
861# de l'Evenement.
862# """
863# evenement = models.ForeignKey("Evenement", db_column='evenement',
864# related_name='+',
865# verbose_name = u"Évènement")
866# # TODO : le champ dossier hérité de Remuneration doit être dérivé
867# # de l'Evenement associé
868#
869# class Meta:
870# abstract = True
871# ordering = ['evenement', 'type__nom', '-date_fin']
872# verbose_name = u"Évènement - rémunération"
873# verbose_name_plural = u"Évènements - rémunérations"
874#
875#
876#class EvenementRemuneration(EvenementRemuneration_):
877# __doc__ = EvenementRemuneration_.__doc__
878#
879# class Meta:
880# abstract = True
881#
882#
883#class EvenementRemuneration(EvenementRemuneration_):
884# __doc__ = EvenementRemuneration_.__doc__
f31ddfa0 885
83b7692b 886
887### RÉFÉRENCES RH
888
d6985a3a 889class FamilleEmploi(AUFMetadata):
6e4600ef 890 """Catégorie utilisée dans la gestion des Postes.
891 Catégorie supérieure à TypePoste.
892 """
e9bbd6ba 893 nom = models.CharField(max_length=255)
6e4600ef 894
8c1ae2b3 895 class Meta:
896 ordering = ['nom']
c1195471
OL
897 verbose_name = u"Famille d'emploi"
898 verbose_name_plural = u"Familles d'emploi"
8c1ae2b3 899
6e4600ef 900 def __unicode__(self):
901 return u'%s' % (self.nom)
e9bbd6ba 902
d6985a3a 903class TypePoste(AUFMetadata):
6e4600ef 904 """Catégorie de Poste.
905 """
e9bbd6ba 906 nom = models.CharField(max_length=255)
8c1ae2b3 907 nom_feminin = models.CharField(max_length=255,
c1195471 908 verbose_name = u"Nom féminin")
6e4600ef 909
8c1ae2b3 910 is_responsable = models.BooleanField(default=False,
c1195471 911 verbose_name = u"Poste de responsabilité")
9afaa55e 912 famille_emploi = models.ForeignKey('FamilleEmploi',
6e4600ef 913 db_column='famille_emploi',
8c1ae2b3 914 related_name='+',
c1195471 915 verbose_name = u"Famille d'emploi")
e9bbd6ba 916
6e4600ef 917 class Meta:
918 ordering = ['nom']
c1195471
OL
919 verbose_name = u"Type de poste"
920 verbose_name_plural = u"Types de poste"
6e4600ef 921
e9bbd6ba 922 def __unicode__(self):
6e4600ef 923 return u'%s' % (self.nom)
e9bbd6ba 924
925
926TYPE_PAIEMENT_CHOICES = (
927 ('Régulier', 'Régulier'),
928 ('Ponctuel', 'Ponctuel'),
929)
930
931NATURE_REMUNERATION_CHOICES = (
932 ('Accessoire', 'Accessoire'),
933 ('Charges', 'Charges'),
934 ('Indemnité', 'Indemnité'),
7abc6d45 935 ('RAS', 'Rémunération autre source'),
e9bbd6ba 936 ('Traitement', 'Traitement'),
937)
938
d6985a3a 939class TypeRemuneration(AUFMetadata):
6e4600ef 940 """Catégorie de Remuneration.
941 """
e9bbd6ba 942 nom = models.CharField(max_length=255)
9afaa55e 943 type_paiement = models.CharField(max_length=30,
8c1ae2b3 944 choices=TYPE_PAIEMENT_CHOICES,
c1195471 945 verbose_name = u"Type de paiement")
9afaa55e 946 nature_remuneration = models.CharField(max_length=30,
8c1ae2b3 947 choices=NATURE_REMUNERATION_CHOICES,
c1195471 948 verbose_name = u"Nature de la rémunération")
8c1ae2b3 949
950 class Meta:
951 ordering = ['nom']
c1195471
OL
952 verbose_name = u"Type de rémunération"
953 verbose_name_plural = u"Types de rémunération"
9afaa55e 954
955 def __unicode__(self):
6e4600ef 956 return u'%s' % (self.nom)
e9bbd6ba 957
d6985a3a 958class TypeRevalorisation(AUFMetadata):
7abc6d45 959 """Justification du changement de la Remuneration.
6e4600ef 960 (Actuellement utilisé dans aucun traitement informatique.)
7abc6d45 961 """
e9bbd6ba 962 nom = models.CharField(max_length=255)
8c1ae2b3 963
964 class Meta:
965 ordering = ['nom']
c1195471
OL
966 verbose_name = u"Type de revalorisation"
967 verbose_name_plural = u"Types de revalorisation"
e9bbd6ba 968
969 def __unicode__(self):
6e4600ef 970 return u'%s' % (self.nom)
971
d6985a3a 972class Service(AUFMetadata):
6e4600ef 973 """Unité administrative où les Postes sont rattachés.
974 """
975 nom = models.CharField(max_length=255)
9afaa55e 976
977 class Meta:
978 ordering = ['nom']
c1195471
OL
979 verbose_name = u"Service"
980 verbose_name_plural = u"Services"
e9bbd6ba 981
6e4600ef 982 def __unicode__(self):
983 return u'%s' % (self.nom)
984
e9bbd6ba 985
986TYPE_ORGANISME_CHOICES = (
987 ('MAD', 'Mise à disposition'),
988 ('DET', 'Détachement'),
989)
990
d6985a3a 991class OrganismeBstg(AUFMetadata):
6e4600ef 992 """Organisation d'où provient un Employe mis à disposition (MAD) de
993 ou détaché (DET) à l'AUF à titre gratuit.
994
995 (BSTG = bien et service à titre gratuit.)
996 """
e9bbd6ba 997 nom = models.CharField(max_length=255)
998 type = models.CharField(max_length=10, choices=TYPE_ORGANISME_CHOICES)
6e4600ef 999 pays = models.ForeignKey(ref.Pays, to_field='code',
1000 db_column='pays',
1001 related_name='organismes_bstg',
1002 null=True, blank=True)
9afaa55e 1003
1004 class Meta:
1005 ordering = ['type', 'nom']
c1195471
OL
1006 verbose_name = u"Organisme BSTG"
1007 verbose_name_plural = u"Organismes BSTG"
9afaa55e 1008
6e4600ef 1009 def __unicode__(self):
8c1ae2b3 1010 return u'%s (%s)' % (self.nom, self.get_type_display())
83b7692b 1011
aff1a4c6
PP
1012 prefix_implantation = "pays__region"
1013 def get_regions(self):
1014 return [self.pays.region]
1015
1016
d6985a3a 1017class Statut(AUFMetadata):
6e4600ef 1018 """Statut de l'Employe dans le cadre d'un Dossier particulier.
1019 """
9afaa55e 1020 # Identification
a122050d 1021 code = models.CharField(max_length=25, unique=True, help_text="Saisir un code court mais lisible pour ce statut : le code est utilisé pour associer les statuts aux autres données tout en demeurant plus lisible qu'un identifiant numérique.")
e9bbd6ba 1022 nom = models.CharField(max_length=255)
e9bbd6ba 1023
6e4600ef 1024 class Meta:
1025 ordering = ['code']
c1195471
OL
1026 verbose_name = u"Statut d'employé"
1027 verbose_name_plural = u"Statuts d'employé"
6e4600ef 1028
9afaa55e 1029 def __unicode__(self):
1030 return u'%s : %s' % (self.code, self.nom)
1031
83b7692b 1032
e9bbd6ba 1033TYPE_CLASSEMENT_CHOICES = (
6e4600ef 1034 ('S', 'S -Soutien'),
1035 ('T', 'T - Technicien'),
1036 ('P', 'P - Professionel'),
1037 ('C', 'C - Cadre'),
1038 ('D', 'D - Direction'),
1039 ('SO', 'SO - Sans objet [expatriés]'),
1040 ('HG', 'HG - Hors grille [direction]'),
e9bbd6ba 1041)
83b7692b 1042
6e7c919b 1043
d6985a3a 1044class Classement_(AUFMetadata):
6e4600ef 1045 """Éléments de classement de la
1046 "Grille générique de classement hiérarchique".
1047
1048 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
1049 classement dans la grille. Le classement donne le coefficient utilisé dans:
1050
1051 salaire de base = coefficient * valeur du point de l'Implantation du Poste
1052 """
9afaa55e 1053 # Identification
e9bbd6ba 1054 type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
c1195471
OL
1055 echelon = models.IntegerField(verbose_name = u"Échelon")
1056 degre = models.IntegerField(verbose_name = u"Degré")
1057 coefficient = models.FloatField(default=0, verbose_name = u"Coéfficient",
8277a35b 1058 null=True)
9afaa55e 1059 # Méta
6e4600ef 1060 # annee # au lieu de date_debut et date_fin
1061 commentaire = models.TextField(null=True, blank=True)
1062
1063 class Meta:
6e7c919b 1064 abstract = True
6e4600ef 1065 ordering = ['type','echelon','degre','coefficient']
c1195471
OL
1066 verbose_name = u"Classement"
1067 verbose_name_plural = u"Classements"
e9bbd6ba 1068
1069 def __unicode__(self):
1070 return u'%s.%s.%s (%s)' % (self.type, self.echelon, self.degre,
1071 self.coefficient)
1072
6e7c919b
NC
1073class Classement(Classement_):
1074 __doc__ = Classement_.__doc__
1075
1076
d6985a3a 1077class TauxChange_(AUFMetadata):
7abc6d45 1078 """Taux de change de la devise vers l'euro (EUR)
6e4600ef 1079 pour chaque année budgétaire.
7abc6d45 1080 """
9afaa55e 1081 # Identification
8d3e2fff 1082 devise = models.ForeignKey('Devise', db_column='devise')
c1195471
OL
1083 annee = models.IntegerField(verbose_name = u"Année")
1084 taux = models.FloatField(verbose_name = u"Taux vers l'euro")
6e7c919b 1085
6e4600ef 1086 class Meta:
6e7c919b 1087 abstract = True
8c1ae2b3 1088 ordering = ['-annee', 'devise__code']
c1195471
OL
1089 verbose_name = u"Taux de change"
1090 verbose_name_plural = u"Taux de change"
6e4600ef 1091
1092 def __unicode__(self):
8c1ae2b3 1093 return u'%s : %s € (%s)' % (self.devise, self.taux, self.annee)
e9bbd6ba 1094
6e7c919b
NC
1095
1096class TauxChange(TauxChange_):
1097 __doc__ = TauxChange_.__doc__
1098
701f3bea
OL
1099class ValeurPointManager(NoDeleteManager):
1100 def get_query_set(self):
1101 return super(ValeurPointManager, self).get_query_set().select_related('devise', 'implantation')
1102
6e7c919b 1103
d6985a3a 1104class ValeurPoint_(AUFMetadata):
6e4600ef 1105 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
1106 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
8c1ae2b3 1107 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
6e4600ef 1108
1109 salaire de base = coefficient * valeur du point de l'Implantation du Poste
1110 """
701f3bea 1111
09aa8374 1112 actuelles = ValeurPointManager()
701f3bea 1113
8277a35b
NC
1114 valeur = models.FloatField(null=True)
1115 devise = models.ForeignKey('Devise', db_column='devise', null=True,
6e4600ef 1116 related_name='+', default=5)
83b7692b 1117 implantation = models.ForeignKey(ref.Implantation,
6e7c919b
NC
1118 db_column='implantation',
1119 related_name='%(app_label)s_valeur_point')
9afaa55e 1120 # Méta
e9bbd6ba 1121 annee = models.IntegerField()
9afaa55e 1122
6e4600ef 1123 class Meta:
701f3bea 1124 ordering = ['-annee', 'implantation__nom']
6e7c919b 1125 abstract = True
c1195471
OL
1126 verbose_name = u"Valeur du point"
1127 verbose_name_plural = u"Valeurs du point"
6e0bbb73 1128
9afaa55e 1129 def __unicode__(self):
8c1ae2b3 1130 return u'%s %s (%s)' % (self.valeur, self.devise, self.annee)
6e7c919b
NC
1131
1132
1133class ValeurPoint(ValeurPoint_):
1134 __doc__ = ValeurPoint_.__doc__
1135
e9bbd6ba 1136
d6985a3a 1137class Devise(AUFMetadata):
6e4600ef 1138 """Devise monétaire.
1139 """
e9bbd6ba 1140 code = models.CharField(max_length=10, unique=True)
1141 nom = models.CharField(max_length=255)
1142
6e4600ef 1143 class Meta:
1144 ordering = ['code']
c1195471
OL
1145 verbose_name = u"Devise"
1146 verbose_name_plural = u"Devises"
6e4600ef 1147
e9bbd6ba 1148 def __unicode__(self):
1149 return u'%s - %s' % (self.code, self.nom)
1150
d6985a3a 1151class TypeContrat(AUFMetadata):
6e4600ef 1152 """Type de contrat.
1153 """
e9bbd6ba 1154 nom = models.CharField(max_length=255)
6e4600ef 1155 nom_long = models.CharField(max_length=255)
49f9f116 1156
8c1ae2b3 1157 class Meta:
1158 ordering = ['nom']
c1195471
OL
1159 verbose_name = u"Type de contrat"
1160 verbose_name_plural = u"Types de contrat"
8c1ae2b3 1161
9afaa55e 1162 def __unicode__(self):
1163 return u'%s' % (self.nom)
30be56d5 1164
2d4d2fcf 1165
1166### AUTRES
1167
d6985a3a 1168class ResponsableImplantation(AUFMetadata):
30be56d5 1169 """Le responsable d'une implantation.
1170 Anciennement géré sur le Dossier du responsable.
1171 """
6e4600ef 1172 employe = models.ForeignKey('Employe', db_column='employe',
1173 related_name='+',
1174 null=True, blank=True)
1175 implantation = models.ForeignKey(ref.Implantation,
1176 db_column='implantation', related_name='+',
1177 unique=True)
30be56d5 1178
1179 def __unicode__(self):
1180 return u'%s : %s' % (self.implantation, self.employe)
1181
1182 class Meta:
1183 ordering = ['implantation__nom']
8c1ae2b3 1184 verbose_name = "Responsable d'implantation"
1185 verbose_name_plural = "Responsables d'implantation"