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