Changement de l'ordre des champs pour rapport rémun
[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
2d4d2fcf 448
e9bbd6ba 449LIEN_PARENTE_CHOICES = (
450 ('Conjoint', 'Conjoint'),
451 ('Conjointe', 'Conjointe'),
452 ('Fille', 'Fille'),
453 ('Fils', 'Fils'),
454)
455
d6985a3a 456class AyantDroit(AUFMetadata):
6e4600ef 457 """Personne en relation avec un Employe.
458 """
9afaa55e 459 # Identification
e9bbd6ba 460 nom = models.CharField(max_length=255)
8c1ae2b3 461 prenom = models.CharField(max_length=255,
c1195471 462 verbose_name = u"Prénom",)
6e4600ef 463 nom_affichage = models.CharField(max_length=255,
c1195471 464 verbose_name = u"Nom d'affichage",
6e4600ef 465 null=True, blank=True)
2d4d2fcf 466 nationalite = models.ForeignKey(ref.Pays, to_field='code',
6e4600ef 467 db_column='nationalite',
8c1ae2b3 468 related_name='ayantdroits_nationalite',
c1195471 469 verbose_name = u"Nationalité")
6e4600ef 470 date_naissance = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 471 verbose_name = u"Date de naissance",
25037368 472 validators=[validate_date_passee],
6e4600ef 473 null=True, blank=True)
2d4d2fcf 474 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
83b7692b 475
9afaa55e 476 # Relation
477 employe = models.ForeignKey('Employe', db_column='employe',
8c1ae2b3 478 related_name='ayantdroits',
c1195471 479 verbose_name = u"Employé")
6e4600ef 480 lien_parente = models.CharField(max_length=10,
481 choices=LIEN_PARENTE_CHOICES,
c1195471 482 verbose_name = u"Lien de parenté",
6e4600ef 483 null=True, blank=True)
484
485 class Meta:
486 ordering = ['nom_affichage']
c1195471
OL
487 verbose_name = u"Ayant droit"
488 verbose_name_plural = u"Ayants droit"
8c1ae2b3 489
6e4600ef 490 def __unicode__(self):
8c1ae2b3 491 return u'%s' % (self.get_nom())
492
493 def get_nom(self):
494 nom_affichage = self.nom_affichage
495 if not nom_affichage:
496 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
497 return nom_affichage
83b7692b 498
aff1a4c6
PP
499 prefix_implantation = "employe__dossiers__poste__implantation__region"
500 def get_regions(self):
501 regions = []
502 for d in self.employe.dossiers.all():
503 regions.append(d.poste.implantation.region)
504 return regions
505
506
07b40eda 507class AyantDroitCommentaire(Commentaire):
8c1ae2b3 508 ayant_droit = models.ForeignKey('AyantDroit', db_column='ayant_droit',
6e4600ef 509 related_name='+')
83b7692b 510
2d4d2fcf 511
83b7692b 512### DOSSIER
513
514STATUT_RESIDENCE_CHOICES = (
515 ('local', 'Local'),
516 ('expat', 'Expatrié'),
517)
518
519COMPTE_COMPTA_CHOICES = (
520 ('coda', 'CODA'),
521 ('scs', 'SCS'),
522 ('aucun', 'Aucun'),
523)
524
d6985a3a 525class Dossier_(AUFMetadata):
6e4600ef 526 """Le Dossier regroupe les informations relatives à l'occupation
527 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
528 par un Employe.
529
530 Plusieurs Contrats peuvent être associés au Dossier.
531 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
532 lequel aucun Dossier n'existe est un poste vacant.
533 """
83b7692b 534 # Identification
6e4600ef 535 employe = models.ForeignKey('Employe', db_column='employe',
d04d084c 536 related_name='dossiers',
bf4f5c77 537 verbose_name=u"Employé")
63e17dff
PP
538 # TODO: OneToOne ??
539 poste = models.ForeignKey('Poste', db_column='poste', related_name='dossiers')
8277a35b
NC
540 statut = models.ForeignKey('Statut', related_name='+', default=3,
541 null=True)
83b7692b 542 organisme_bstg = models.ForeignKey('OrganismeBstg',
6e4600ef 543 db_column='organisme_bstg',
544 related_name='+',
c1195471 545 verbose_name = u"Organisme",
8c1ae2b3 546 help_text="Si détaché (DET) ou \
6e4600ef 547 mis à disposition (MAD), \
548 préciser l'organisme.",
549 null=True, blank=True)
550
83b7692b 551 # Recrutement
2d4d2fcf 552 remplacement = models.BooleanField(default=False)
7c182958
PP
553 remplacement_de = models.ForeignKey('self', related_name='+',
554 null=True, blank=True)
83b7692b 555 statut_residence = models.CharField(max_length=10, default='local',
c1195471 556 verbose_name = u"Statut", null=True,
2d4d2fcf 557 choices=STATUT_RESIDENCE_CHOICES)
83b7692b 558
559 # Rémunération
6e4600ef 560 classement = models.ForeignKey('Classement', db_column='classement',
561 related_name='+',
562 null=True, blank=True)
8277a35b 563 regime_travail = models.DecimalField(max_digits=12, null=True,
2d4d2fcf 564 decimal_places=2,
565 default=REGIME_TRAVAIL_DEFAULT,
c1195471 566 verbose_name = u"Régime de travail",
8c1ae2b3 567 help_text="% du temps complet")
83b7692b 568 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
8277a35b 569 decimal_places=2, null=True,
2d4d2fcf 570 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
c1195471 571 verbose_name = u"Nb. heures par semaine")
7abc6d45 572
573 # Occupation du Poste par cet Employe (anciennement "mandat")
c1195471 574 date_debut = models.DateField(verbose_name = u"Date de début d'occupation \
6e4600ef 575 de poste",
576 help_text=HELP_TEXT_DATE)
c1195471 577 date_fin = models.DateField(verbose_name = u"Date de fin d'occupation \
6e4600ef 578 de poste",
2d4d2fcf 579 help_text=HELP_TEXT_DATE,
6e4600ef 580 null=True, blank=True)
e9bbd6ba 581
2d4d2fcf 582 # Comptes
583 # TODO?
83b7692b 584
6e4600ef 585 class Meta:
37868f0b 586 abstract = True
49449367 587 ordering = ['employe__nom', ]
3f5f3898 588 verbose_name = u"Dossier"
8c1ae2b3 589 verbose_name_plural = "Dossiers"
6e4600ef 590
65f9fac8 591 def salaire_theorique(self):
592 annee = date.today().year
593 coeff = self.classement.coefficient
594 implantation = self.poste.implantation
595 point = ValeurPoint.objects.get(implantation=implantation, annee=annee)
596
597 montant = coeff * point.valeur
598 devise = point.devise
599 return {'montant':montant, 'devise':devise}
600
83b7692b 601 def __unicode__(self):
8c1ae2b3 602 poste = self.poste.nom
603 if self.employe.genre == 'F':
604 poste = self.poste.nom_feminin
605 return u'%s - %s' % (self.employe, poste)
83b7692b 606
aff1a4c6
PP
607 prefix_implantation = "poste__implantation__region"
608 def get_regions(self):
609 return [self.poste.implantation.region]
610
37868f0b
NC
611
612class Dossier(Dossier_):
613 __doc__ = Dossier_.__doc__
614
615
64721a83
PP
616class DossierInactif(Dossier):
617 class Meta:
618 proxy = True
619 ordering = ['employe__nom', ]
620 verbose_name = u"Dossier inactif"
621 verbose_name_plural = u"Dossiers inactifs"
622
623
83b7692b 624class DossierPiece(models.Model):
7abc6d45 625 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
626 Ex.: Lettre de motivation.
627 """
8c1ae2b3 628 dossier = models.ForeignKey('Dossier', db_column='dossier',
6e4600ef 629 related_name='+')
c1195471
OL
630 nom = models.CharField(verbose_name = u"Nom", max_length=255)
631 fichier = models.FileField(verbose_name = u"Fichier",
83b7692b 632 upload_to=dossier_piece_dispatch,
633 storage=storage_prive)
634
6e4600ef 635 class Meta:
636 ordering = ['nom']
637
638 def __unicode__(self):
639 return u'%s' % (self.nom)
640
07b40eda 641class DossierCommentaire(Commentaire):
8c1ae2b3 642 dossier = models.ForeignKey('Dossier', db_column='dossier',
6e4600ef 643 related_name='+')
83b7692b 644
1d0f4eef
OL
645class DossierComparaison(models.Model):
646 """
647 Photo d'une comparaison salariale au moment de l'embauche.
648 """
649 dossier = models.ForeignKey('Dossier', related_name='comparaisons')
8c6269cc 650 implantation = models.ForeignKey(ref.Implantation, related_name="+", null=True, blank=True)
1d0f4eef
OL
651 poste = models.CharField(max_length=255, null=True, blank=True)
652 personne = models.CharField(max_length=255, null=True, blank=True)
653 montant = models.IntegerField(null=True)
93494cf1 654 devise = models.ForeignKey('Devise', default=5, related_name='+', null=True, blank=True)
1d0f4eef
OL
655
656 def taux_devise(self):
657 liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.dossier.poste.implantation)
658 if len(liste_taux) == 0:
659 raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.dossier.poste.implantation))
660 else:
661 return liste_taux[0].taux
662
663 def montant_euros(self):
664 return round(float(self.montant) * float(self.taux_devise()), 2)
665
2d4d2fcf 666
07b40eda 667### RÉMUNÉRATION
e9bbd6ba 668
d6985a3a 669class RemunerationMixin(AUFMetadata):
9afaa55e 670 # Identification
6e7c919b
NC
671 dossier = models.ForeignKey('Dossier', db_column='dossier',
672 related_name='%(app_label)s_%(class)s_remunerations')
83b7692b 673 type = models.ForeignKey('TypeRemuneration', db_column='type',
8c1ae2b3 674 related_name='+',
c1195471 675 verbose_name = u"Type de rémunération")
7abc6d45 676 type_revalorisation = models.ForeignKey('TypeRevalorisation',
677 db_column='type_revalorisation',
6e4600ef 678 related_name='+',
c1195471 679 verbose_name = u"Type de revalorisation",
7abc6d45 680 null=True, blank=True)
50fa9bc1 681 montant = models.FloatField(null=True, blank=True,
6e4600ef 682 default=0)
2d4d2fcf 683 # Annuel (12 mois, 52 semaines, 364 jours?)
c589d980 684 devise = models.ForeignKey('Devise', to_field='id',
6e4600ef 685 db_column='devise', related_name='+',
686 default=5)
2d4d2fcf 687 # commentaire = precision
688 commentaire = models.CharField(max_length=255, null=True, blank=True)
689 # date_debut = anciennement date_effectif
8c1ae2b3 690 date_debut = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 691 verbose_name = u"Date de début",
6e4600ef 692 null=True, blank=True)
693 date_fin = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 694 verbose_name = u"Date de fin",
6e4600ef 695 null=True, blank=True)
83b7692b 696
2d4d2fcf 697 class Meta:
698 abstract = True
6e4600ef 699 ordering = ['type__nom', '-date_fin']
700
701 def __unicode__(self):
702 return u'%s %s (%s)' % (self.montant, self.devise.code, self.type.nom)
2d4d2fcf 703
6e7c919b 704class Remuneration_(RemunerationMixin):
2d4d2fcf 705 """Structure de rémunération (données budgétaires) en situation normale
706 pour un Dossier. Si un Evenement existe, utiliser la structure de
707 rémunération EvenementRemuneration de cet événement.
708 """
83b7692b 709
710 def montant_mois(self):
711 return round(self.montant / 12, 2)
712
713 def taux_devise(self):
714 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
715
716 def montant_euro(self):
717 return round(float(self.montant) / float(self.taux_devise()), 2)
718
719 def montant_euro_mois(self):
720 return round(self.montant_euro() / 12, 2)
9afaa55e 721
722 def __unicode__(self):
723 try:
724 devise = self.devise.code
725 except:
726 devise = "???"
727 return "%s %s" % (self.montant, devise)
83b7692b 728
6e7c919b
NC
729 class Meta:
730 abstract = True
c1195471
OL
731 verbose_name = u"Rémunération"
732 verbose_name_plural = u"Rémunérations"
6e7c919b
NC
733
734
735class Remuneration(Remuneration_):
736 __doc__ = Remuneration_.__doc__
737
2d4d2fcf 738
739### CONTRATS
c41b7fcc
OL
740
741class ContratManager(NoDeleteManager):
742 def get_query_set(self):
743 return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste')
744
2d4d2fcf 745
d6985a3a 746class Contrat(AUFMetadata):
2d4d2fcf 747 """Document juridique qui encadre la relation de travail d'un Employe
748 pour un Poste particulier. Pour un Dossier (qui documente cette
749 relation de travail) plusieurs contrats peuvent être associés.
750 """
c41b7fcc
OL
751
752 objects = ContratManager()
753
6e4600ef 754 dossier = models.ForeignKey('Dossier', db_column='dossier',
65f9fac8 755 related_name='contrats')
6e4600ef 756 type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
8c1ae2b3 757 related_name='+',
c1195471 758 verbose_name = u"Type de contrat")
8c1ae2b3 759 date_debut = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 760 verbose_name = u"Date de début")
6e4600ef 761 date_fin = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 762 verbose_name = u"Date de fin",
6e4600ef 763 null=True, blank=True)
764
765 class Meta:
766 ordering = ['dossier__employe__nom_affichage']
c1195471
OL
767 verbose_name = u"Contrat"
768 verbose_name_plural = u"Contrats"
6e4600ef 769
770 def __unicode__(self):
8c1ae2b3 771 return u'%s - %s' % (self.dossier, self.id)
6e4600ef 772
773# TODO? class ContratPiece(models.Model):
2d4d2fcf 774
775
776### ÉVÉNEMENTS
777
d6985a3a 778class Evenement_(AUFMetadata):
6e4600ef 779 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
780 d'un Dossier qui vient altérer des informations normales liées à un Dossier
781 (ex.: la Remuneration).
782
783 Ex.: congé de maternité, maladie...
784
785 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
786 différent et une rémunération en conséquence. On souhaite toutefois
787 conserver le Dossier intact afin d'éviter une re-saisie des données lors
788 du retour à la normale.
789 """
8c1ae2b3 790 dossier = models.ForeignKey('Dossier', db_column='dossier',
6e4600ef 791 related_name='+')
792 nom = models.CharField(max_length=255)
8c1ae2b3 793 date_debut = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 794 verbose_name = u"Date de début")
8c1ae2b3 795 date_fin = models.DateField(help_text=HELP_TEXT_DATE,
c1195471 796 verbose_name = u"Date de fin",
6e4600ef 797 null=True, blank=True)
6e7c919b 798
6e4600ef 799 class Meta:
6e7c919b 800 abstract = True
6e4600ef 801 ordering = ['nom']
c1195471
OL
802 verbose_name = u"Évènement"
803 verbose_name_plural = u"Évènements"
6e4600ef 804
805 def __unicode__(self):
806 return u'%s' % (self.nom)
6e7c919b
NC
807
808
809class Evenement(Evenement_):
810 __doc__ = Evenement_.__doc__
811
2d4d2fcf 812
6e7c919b 813class EvenementRemuneration_(RemunerationMixin):
6e4600ef 814 """Structure de rémunération liée à un Evenement qui remplace
815 temporairement la Remuneration normale d'un Dossier, pour toute la durée
816 de l'Evenement.
817 """
818 evenement = models.ForeignKey("Evenement", db_column='evenement',
8c1ae2b3 819 related_name='+',
c1195471 820 verbose_name = u"Évènement")
8c1ae2b3 821 # TODO : le champ dossier hérité de Remuneration doit être dérivé
822 # de l'Evenement associé
83b7692b 823
6e7c919b
NC
824 class Meta:
825 abstract = True
8c1ae2b3 826 ordering = ['evenement', 'type__nom', '-date_fin']
c1195471
OL
827 verbose_name = u"Évènement - rémunération"
828 verbose_name_plural = u"Évènements - rémunérations"
6e7c919b
NC
829
830
831class EvenementRemuneration(EvenementRemuneration_):
832 __doc__ = EvenementRemuneration_.__doc__
83b7692b 833
f31ddfa0
NC
834 class Meta:
835 abstract = True
836
837
838class EvenementRemuneration(EvenementRemuneration_):
839 __doc__ = EvenementRemuneration_.__doc__
840
83b7692b 841
842### RÉFÉRENCES RH
843
d6985a3a 844class FamilleEmploi(AUFMetadata):
6e4600ef 845 """Catégorie utilisée dans la gestion des Postes.
846 Catégorie supérieure à TypePoste.
847 """
e9bbd6ba 848 nom = models.CharField(max_length=255)
6e4600ef 849
8c1ae2b3 850 class Meta:
851 ordering = ['nom']
c1195471
OL
852 verbose_name = u"Famille d'emploi"
853 verbose_name_plural = u"Familles d'emploi"
8c1ae2b3 854
6e4600ef 855 def __unicode__(self):
856 return u'%s' % (self.nom)
e9bbd6ba 857
d6985a3a 858class TypePoste(AUFMetadata):
6e4600ef 859 """Catégorie de Poste.
860 """
e9bbd6ba 861 nom = models.CharField(max_length=255)
8c1ae2b3 862 nom_feminin = models.CharField(max_length=255,
c1195471 863 verbose_name = u"Nom féminin")
6e4600ef 864
8c1ae2b3 865 is_responsable = models.BooleanField(default=False,
c1195471 866 verbose_name = u"Poste de responsabilité")
9afaa55e 867 famille_emploi = models.ForeignKey('FamilleEmploi',
6e4600ef 868 db_column='famille_emploi',
8c1ae2b3 869 related_name='+',
c1195471 870 verbose_name = u"Famille d'emploi")
e9bbd6ba 871
6e4600ef 872 class Meta:
873 ordering = ['nom']
c1195471
OL
874 verbose_name = u"Type de poste"
875 verbose_name_plural = u"Types de poste"
6e4600ef 876
e9bbd6ba 877 def __unicode__(self):
6e4600ef 878 return u'%s' % (self.nom)
e9bbd6ba 879
880
881TYPE_PAIEMENT_CHOICES = (
882 ('Régulier', 'Régulier'),
883 ('Ponctuel', 'Ponctuel'),
884)
885
886NATURE_REMUNERATION_CHOICES = (
887 ('Accessoire', 'Accessoire'),
888 ('Charges', 'Charges'),
889 ('Indemnité', 'Indemnité'),
7abc6d45 890 ('RAS', 'Rémunération autre source'),
e9bbd6ba 891 ('Traitement', 'Traitement'),
892)
893
d6985a3a 894class TypeRemuneration(AUFMetadata):
6e4600ef 895 """Catégorie de Remuneration.
896 """
e9bbd6ba 897 nom = models.CharField(max_length=255)
9afaa55e 898 type_paiement = models.CharField(max_length=30,
8c1ae2b3 899 choices=TYPE_PAIEMENT_CHOICES,
c1195471 900 verbose_name = u"Type de paiement")
9afaa55e 901 nature_remuneration = models.CharField(max_length=30,
8c1ae2b3 902 choices=NATURE_REMUNERATION_CHOICES,
c1195471 903 verbose_name = u"Nature de la rémunération")
8c1ae2b3 904
905 class Meta:
906 ordering = ['nom']
c1195471
OL
907 verbose_name = u"Type de rémunération"
908 verbose_name_plural = u"Types de rémunération"
9afaa55e 909
910 def __unicode__(self):
6e4600ef 911 return u'%s' % (self.nom)
e9bbd6ba 912
d6985a3a 913class TypeRevalorisation(AUFMetadata):
7abc6d45 914 """Justification du changement de la Remuneration.
6e4600ef 915 (Actuellement utilisé dans aucun traitement informatique.)
7abc6d45 916 """
e9bbd6ba 917 nom = models.CharField(max_length=255)
8c1ae2b3 918
919 class Meta:
920 ordering = ['nom']
c1195471
OL
921 verbose_name = u"Type de revalorisation"
922 verbose_name_plural = u"Types de revalorisation"
e9bbd6ba 923
924 def __unicode__(self):
6e4600ef 925 return u'%s' % (self.nom)
926
d6985a3a 927class Service(AUFMetadata):
6e4600ef 928 """Unité administrative où les Postes sont rattachés.
929 """
930 nom = models.CharField(max_length=255)
9afaa55e 931
932 class Meta:
933 ordering = ['nom']
c1195471
OL
934 verbose_name = u"Service"
935 verbose_name_plural = u"Services"
e9bbd6ba 936
6e4600ef 937 def __unicode__(self):
938 return u'%s' % (self.nom)
939
e9bbd6ba 940
941TYPE_ORGANISME_CHOICES = (
942 ('MAD', 'Mise à disposition'),
943 ('DET', 'Détachement'),
944)
945
d6985a3a 946class OrganismeBstg(AUFMetadata):
6e4600ef 947 """Organisation d'où provient un Employe mis à disposition (MAD) de
948 ou détaché (DET) à l'AUF à titre gratuit.
949
950 (BSTG = bien et service à titre gratuit.)
951 """
e9bbd6ba 952 nom = models.CharField(max_length=255)
953 type = models.CharField(max_length=10, choices=TYPE_ORGANISME_CHOICES)
6e4600ef 954 pays = models.ForeignKey(ref.Pays, to_field='code',
955 db_column='pays',
956 related_name='organismes_bstg',
957 null=True, blank=True)
9afaa55e 958
959 class Meta:
960 ordering = ['type', 'nom']
c1195471
OL
961 verbose_name = u"Organisme BSTG"
962 verbose_name_plural = u"Organismes BSTG"
9afaa55e 963
6e4600ef 964 def __unicode__(self):
8c1ae2b3 965 return u'%s (%s)' % (self.nom, self.get_type_display())
83b7692b 966
aff1a4c6
PP
967 prefix_implantation = "pays__region"
968 def get_regions(self):
969 return [self.pays.region]
970
971
d6985a3a 972class Statut(AUFMetadata):
6e4600ef 973 """Statut de l'Employe dans le cadre d'un Dossier particulier.
974 """
9afaa55e 975 # Identification
e9bbd6ba 976 code = models.CharField(max_length=25, unique=True)
977 nom = models.CharField(max_length=255)
e9bbd6ba 978
6e4600ef 979 class Meta:
980 ordering = ['code']
c1195471
OL
981 verbose_name = u"Statut d'employé"
982 verbose_name_plural = u"Statuts d'employé"
6e4600ef 983
9afaa55e 984 def __unicode__(self):
985 return u'%s : %s' % (self.code, self.nom)
986
83b7692b 987
e9bbd6ba 988TYPE_CLASSEMENT_CHOICES = (
6e4600ef 989 ('S', 'S -Soutien'),
990 ('T', 'T - Technicien'),
991 ('P', 'P - Professionel'),
992 ('C', 'C - Cadre'),
993 ('D', 'D - Direction'),
994 ('SO', 'SO - Sans objet [expatriés]'),
995 ('HG', 'HG - Hors grille [direction]'),
e9bbd6ba 996)
83b7692b 997
6e7c919b 998
d6985a3a 999class Classement_(AUFMetadata):
6e4600ef 1000 """Éléments de classement de la
1001 "Grille générique de classement hiérarchique".
1002
1003 Utile pour connaître, pour un Dossier, le salaire de base théorique lié au
1004 classement dans la grille. Le classement donne le coefficient utilisé dans:
1005
1006 salaire de base = coefficient * valeur du point de l'Implantation du Poste
1007 """
9afaa55e 1008 # Identification
e9bbd6ba 1009 type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
c1195471
OL
1010 echelon = models.IntegerField(verbose_name = u"Échelon")
1011 degre = models.IntegerField(verbose_name = u"Degré")
1012 coefficient = models.FloatField(default=0, verbose_name = u"Coéfficient",
8277a35b 1013 null=True)
9afaa55e 1014 # Méta
6e4600ef 1015 # annee # au lieu de date_debut et date_fin
1016 commentaire = models.TextField(null=True, blank=True)
1017
1018 class Meta:
6e7c919b 1019 abstract = True
6e4600ef 1020 ordering = ['type','echelon','degre','coefficient']
c1195471
OL
1021 verbose_name = u"Classement"
1022 verbose_name_plural = u"Classements"
e9bbd6ba 1023
1024 def __unicode__(self):
1025 return u'%s.%s.%s (%s)' % (self.type, self.echelon, self.degre,
1026 self.coefficient)
1027
6e7c919b
NC
1028class Classement(Classement_):
1029 __doc__ = Classement_.__doc__
1030
1031
d6985a3a 1032class TauxChange_(AUFMetadata):
7abc6d45 1033 """Taux de change de la devise vers l'euro (EUR)
6e4600ef 1034 pour chaque année budgétaire.
7abc6d45 1035 """
9afaa55e 1036 # Identification
8d3e2fff 1037 devise = models.ForeignKey('Devise', db_column='devise')
c1195471
OL
1038 annee = models.IntegerField(verbose_name = u"Année")
1039 taux = models.FloatField(verbose_name = u"Taux vers l'euro")
6e7c919b 1040
6e4600ef 1041 class Meta:
6e7c919b 1042 abstract = True
8c1ae2b3 1043 ordering = ['-annee', 'devise__code']
c1195471
OL
1044 verbose_name = u"Taux de change"
1045 verbose_name_plural = u"Taux de change"
6e4600ef 1046
1047 def __unicode__(self):
8c1ae2b3 1048 return u'%s : %s € (%s)' % (self.devise, self.taux, self.annee)
e9bbd6ba 1049
6e7c919b
NC
1050
1051class TauxChange(TauxChange_):
1052 __doc__ = TauxChange_.__doc__
1053
701f3bea
OL
1054class ValeurPointManager(NoDeleteManager):
1055 def get_query_set(self):
1056 return super(ValeurPointManager, self).get_query_set().select_related('devise', 'implantation')
1057
6e7c919b 1058
d6985a3a 1059class ValeurPoint_(AUFMetadata):
6e4600ef 1060 """Utile pour connaître, pour un Dossier, le salaire de base théorique lié
1061 au classement dans la grille. La ValeurPoint s'obtient par l'implantation
8c1ae2b3 1062 du Poste de ce Dossier : dossier.poste.implantation (pseudo code).
6e4600ef 1063
1064 salaire de base = coefficient * valeur du point de l'Implantation du Poste
1065 """
701f3bea
OL
1066
1067 objects = ValeurPointManager()
1068
8277a35b
NC
1069 valeur = models.FloatField(null=True)
1070 devise = models.ForeignKey('Devise', db_column='devise', null=True,
6e4600ef 1071 related_name='+', default=5)
83b7692b 1072 implantation = models.ForeignKey(ref.Implantation,
6e7c919b
NC
1073 db_column='implantation',
1074 related_name='%(app_label)s_valeur_point')
9afaa55e 1075 # Méta
e9bbd6ba 1076 annee = models.IntegerField()
9afaa55e 1077
6e4600ef 1078 class Meta:
701f3bea 1079 ordering = ['-annee', 'implantation__nom']
6e7c919b 1080 abstract = True
c1195471
OL
1081 verbose_name = u"Valeur du point"
1082 verbose_name_plural = u"Valeurs du point"
6e0bbb73 1083
e9d7483c 1084 # TODO : cette fonction n'était pas présente dans la branche dev, utilité?
ee23ecbc
NC
1085 def get_tauxchange_courant(self):
1086 """
1087 Recherche le taux courant associé à la valeur d'un point.
1088 Tous les taux de l'année courante sont chargés, pour optimiser un
1089 affichage en liste. (On pourrait probablement améliorer le manager pour
1090 lui greffer le taux courant sous forme de JOIN)
1091 """
1092 for tauxchange in self.tauxchange:
1093 if tauxchange.implantation_id == self.implantation_id:
1094 return tauxchange
1095 return None
1096
9afaa55e 1097 def __unicode__(self):
8c1ae2b3 1098 return u'%s %s (%s)' % (self.valeur, self.devise, self.annee)
6e7c919b
NC
1099
1100
1101class ValeurPoint(ValeurPoint_):
1102 __doc__ = ValeurPoint_.__doc__
1103
e9bbd6ba 1104
d6985a3a 1105class Devise(AUFMetadata):
6e4600ef 1106 """Devise monétaire.
1107 """
e9bbd6ba 1108 code = models.CharField(max_length=10, unique=True)
1109 nom = models.CharField(max_length=255)
1110
6e4600ef 1111 class Meta:
1112 ordering = ['code']
c1195471
OL
1113 verbose_name = u"Devise"
1114 verbose_name_plural = u"Devises"
6e4600ef 1115
e9bbd6ba 1116 def __unicode__(self):
1117 return u'%s - %s' % (self.code, self.nom)
1118
d6985a3a 1119class TypeContrat(AUFMetadata):
6e4600ef 1120 """Type de contrat.
1121 """
e9bbd6ba 1122 nom = models.CharField(max_length=255)
6e4600ef 1123 nom_long = models.CharField(max_length=255)
49f9f116 1124
8c1ae2b3 1125 class Meta:
1126 ordering = ['nom']
c1195471
OL
1127 verbose_name = u"Type de contrat"
1128 verbose_name_plural = u"Types de contrat"
8c1ae2b3 1129
9afaa55e 1130 def __unicode__(self):
1131 return u'%s' % (self.nom)
30be56d5 1132
2d4d2fcf 1133
1134### AUTRES
1135
d6985a3a 1136class ResponsableImplantation(AUFMetadata):
30be56d5 1137 """Le responsable d'une implantation.
1138 Anciennement géré sur le Dossier du responsable.
1139 """
6e4600ef 1140 employe = models.ForeignKey('Employe', db_column='employe',
1141 related_name='+',
1142 null=True, blank=True)
1143 implantation = models.ForeignKey(ref.Implantation,
1144 db_column='implantation', related_name='+',
1145 unique=True)
30be56d5 1146
1147 def __unicode__(self):
1148 return u'%s : %s' % (self.implantation, self.employe)
1149
1150 class Meta:
1151 ordering = ['implantation__nom']
8c1ae2b3 1152 verbose_name = "Responsable d'implantation"
1153 verbose_name_plural = "Responsables d'implantation"