start import DAE->RH
[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
bf6f2712 11import auf.django.references.models as ref
b4aeadf3 12from validators import validate_date_passee
16b1454e 13from managers import PosteManager, DossierManager, DossierComparaisonManager, PosteComparaisonManager
83b7692b 14
2d4d2fcf 15# Constantes
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 Poste_(AUFMetadata):
6e4600ef 57 """Un Poste est un emploi (job) à combler dans une implantation.
58 Un Poste peut être comblé par un Employe, auquel cas un Dossier est créé.
59 Si on veut recruter 2 jardiniers, 2 Postes distincts existent.
60 """
1f2979b8
OL
61
62 objects = PosteManager()
63
83b7692b 64 # Identification
2d4d2fcf 65 nom = models.CharField(max_length=255,
c1195471 66 verbose_name = u"Titre du poste", )
2d4d2fcf 67 nom_feminin = models.CharField(max_length=255,
c1195471 68 verbose_name = u"Titre du poste (au féminin)",
6e4600ef 69 null=True)
70 implantation = models.ForeignKey(ref.Implantation,
71 db_column='implantation', related_name='+')
72 type_poste = models.ForeignKey('TypePoste', db_column='type_poste',
73 related_name='+',
74 null=True)
8277a35b 75 service = models.ForeignKey('Service', db_column='service', null=True,
6e4600ef 76 related_name='+',
3f5cbabe 77 verbose_name = u"Direction/Service/Pôle support", )
6e4600ef 78 responsable = models.ForeignKey('Poste', db_column='responsable',
8277a35b 79 related_name='+', null=True,
3f5cbabe 80 verbose_name = u"Poste du responsable", )
83b7692b 81
82 # Contrat
83 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 84 default=REGIME_TRAVAIL_DEFAULT, null=True,
c1195471 85 verbose_name = u"Temps de travail",
8c1ae2b3 86 help_text="% du temps complet")
83b7692b 87 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
8277a35b 88 decimal_places=2, null=True,
2d4d2fcf 89 default=REGIME_TRAVAIL_NB_HEURE_SEMAINE_DEFAULT,
c1195471 90 verbose_name = u"Nb. heures par semaine")
83b7692b 91
92 # Recrutement
c1195471 93 local = models.NullBooleanField(verbose_name = u"Local", default=True,
8277a35b 94 null=True, blank=True)
c1195471 95 expatrie = models.NullBooleanField(verbose_name = u"Expatrié", default=False,
8277a35b
NC
96 null=True, blank=True)
97 mise_a_disposition = models.NullBooleanField(
c1195471 98 verbose_name = u"Mise à disposition",
8277a35b
NC
99 null=True, default=False)
100 appel = models.CharField(max_length=10, null=True,
c1195471 101 verbose_name = u"Appel à candidature",
6e4600ef 102 choices=POSTE_APPEL_CHOICES,
103 default='interne')
83b7692b 104
105 # Rémunération
6e4600ef 106 classement_min = models.ForeignKey('Classement',
107 db_column='classement_min', related_name='+',
108 null=True, blank=True)
109 classement_max = models.ForeignKey('Classement',
110 db_column='classement_max', related_name='+',
111 null=True, blank=True)
112 valeur_point_min = models.ForeignKey('ValeurPoint',
113 db_column='valeur_point_min', related_name='+',
114 null=True, blank=True)
115 valeur_point_max = models.ForeignKey('ValeurPoint',
116 db_column='valeur_point_max', related_name='+',
117 null=True, blank=True)
8277a35b 118 devise_min = models.ForeignKey('Devise', db_column='devise_min', null=True,
6e4600ef 119 related_name='+', default=5)
8277a35b 120 devise_max = models.ForeignKey('Devise', db_column='devise_max', null=True,
6e4600ef 121 related_name='+', default=5)
83b7692b 122 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 123 null=True, default=0)
83b7692b 124 salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 125 null=True, default=0)
83b7692b 126 indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 127 null=True, default=0)
83b7692b 128 indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 129 null=True, default=0)
83b7692b 130 autre_min = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 131 null=True, default=0)
83b7692b 132 autre_max = models.DecimalField(max_digits=12, decimal_places=2,
8277a35b 133 null=True, default=0)
83b7692b 134
135 # Comparatifs de rémunération
8277a35b 136 devise_comparaison = models.ForeignKey('Devise', null=True,
6e4600ef 137 db_column='devise_comparaison',
138 related_name='+',
139 default=5)
83b7692b 140 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 141 null=True, blank=True)
83b7692b 142 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 143 null=True, blank=True)
83b7692b 144 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 145 null=True, blank=True)
83b7692b 146 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 147 null=True, blank=True)
83b7692b 148 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 149 null=True, blank=True)
83b7692b 150 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 151 null=True, blank=True)
83b7692b 152 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 153 null=True, blank=True)
83b7692b 154 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 155 null=True, blank=True)
83b7692b 156 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 157 null=True, blank=True)
83b7692b 158 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
6e4600ef 159 null=True, blank=True)
83b7692b 160
161 # Justification
6e4600ef 162 justification = models.TextField(null=True, blank=True)
83b7692b 163
2d4d2fcf 164 # Autres Metadata
07b40eda 165 date_validation = models.DateTimeField(null=True, blank=True) # de dae
3f5f3898 166 date_debut = models.DateField(verbose_name=u"Date de début",
7332d8f9 167 null=True, blank=True)
3f5f3898 168 date_fin = models.DateField(verbose_name=u"Date de fin",
6e4600ef 169 null=True, blank=True)
170
171 class Meta:
37868f0b 172 abstract = True
6e4600ef 173 ordering = ['implantation__nom', 'nom']
c1195471
OL
174 verbose_name = u"Poste"
175 verbose_name_plural = u"Postes"
6e4600ef 176
83b7692b 177 def __unicode__(self):
8c1ae2b3 178 representation = u'%s - %s [%s]' % (self.implantation, self.nom,
179 self.id)
180 if self.is_vacant():
23102192 181 representation = representation + u' (VACANT)'
8c1ae2b3 182 return representation
183
184 def is_vacant(self):
23102192
DB
185 vacant = True
186 if self.occupe_par():
187 vacant = False
188 return vacant
189
190 def occupe_par(self):
191 """Retourne la liste d'employé occupant ce poste.
192 Généralement, retourne une liste d'un élément.
193 Si poste inoccupé, retourne liste vide.
194 """
16b1454e 195 return [d.employe for d in self.rh_dossiers.filter(actif=True, supprime=False) \
23102192 196 .exclude(date_fin__lt=date.today())]
83b7692b 197
aff1a4c6
PP
198 prefix_implantation = "implantation__region"
199 def get_regions(self):
200 return [self.implantation.region]
201
83b7692b 202
37868f0b
NC
203class Poste(Poste_):
204 __doc__ = Poste_.__doc__
205
206
769a0755
NC
207class Poste(Poste_):
208 __doc__ = Poste_.__doc__
209
210
83b7692b 211POSTE_FINANCEMENT_CHOICES = (
212 ('A', 'A - Frais de personnel'),
213 ('B', 'B - Projet(s)-Titre(s)'),
214 ('C', 'C - Autre')
215)
216
6e7c919b
NC
217
218class PosteFinancement_(models.Model):
6e4600ef 219 """Pour un Poste, structure d'informations décrivant comment on prévoit
220 financer ce Poste.
221 """
83b7692b 222 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
223 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
8c1ae2b3 224 help_text="ex.: 33.33 % (décimale avec point)")
83b7692b 225 commentaire = models.TextField(
8c1ae2b3 226 help_text="Spécifiez la source de financement.")
83b7692b 227
228 class Meta:
6e7c919b 229 abstract = True
83b7692b 230 ordering = ['type']
6e4600ef 231
232 def __unicode__(self):
233 return u'%s : %s %' % (self.type, self.pourcentage)
83b7692b 234
6e7c919b
NC
235
236class PosteFinancement(PosteFinancement_):
317ce433 237 poste = models.ForeignKey('Poste', db_column='poste', related_name='%(app_label)s_financements')
6e7c919b
NC
238
239
317ce433 240class PostePiece_(models.Model):
6e4600ef 241 """Documents relatifs au Poste.
7abc6d45 242 Ex.: Description de poste
243 """
c1195471
OL
244 nom = models.CharField(verbose_name = u"Nom", max_length=255)
245 fichier = models.FileField(verbose_name = u"Fichier",
83b7692b 246 upload_to=poste_piece_dispatch,
247 storage=storage_prive)
248
6e4600ef 249 class Meta:
317ce433 250 abstract = True
6e4600ef 251 ordering = ['nom']
252
253 def __unicode__(self):
254 return u'%s' % (self.nom)
255
317ce433
OL
256class PostePiece(PostePiece_):
257 poste = models.ForeignKey('Poste', db_column='poste', related_name='%(app_label)s_pieces')
258
259class PosteComparaison_(models.Model):
068d1462
OL
260 """
261 De la même manière qu'un dossier, un poste peut-être comparé à un autre poste.
262 """
16b1454e
OL
263 objects = PosteComparaisonManager()
264
0f0dacbb 265 implantation = models.ForeignKey(ref.Implantation, null=True, blank=True, related_name="+")
c1195471 266 nom = models.CharField(verbose_name = u"Poste", max_length=255, null=True, blank=True)
068d1462
OL
267 montant = models.IntegerField(null=True)
268 devise = models.ForeignKey("Devise", default=5, related_name='+', null=True, blank=True)
1d0f4eef 269
317ce433
OL
270 class Meta:
271 abstract = True
272
1d0f4eef
OL
273 def taux_devise(self):
274 liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.implantation)
275 if len(liste_taux) == 0:
276 raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.implantation))
277 else:
278 return liste_taux[0].taux
279
280 def montant_euros(self):
281 return round(float(self.montant) * float(self.taux_devise()), 2)
068d1462 282
317ce433
OL
283class PosteComparaison(PosteComparaison_):
284 poste = models.ForeignKey('Poste', related_name='%(app_label)s_comparaisons_internes')
068d1462 285
317ce433 286class PosteCommentaire_(Commentaire):
8c1ae2b3 287 poste = models.ForeignKey('Poste', db_column='poste', related_name='+')
83b7692b 288
317ce433
OL
289 class Meta:
290 abstract = True
291
292class PosteCommentaire(PosteCommentaire_):
293 pass
2d4d2fcf 294
83b7692b 295### EMPLOYÉ/PERSONNE
e9bbd6ba 296
297GENRE_CHOICES = (
298 ('M', 'Homme'),
299 ('F', 'Femme'),
300)
301SITUATION_CHOICES = (
302 ('C', 'Célibataire'),
303 ('F', 'Fiancé'),
304 ('M', 'Marié'),
305)
306
d6985a3a 307class Employe(AUFMetadata):
6e4600ef 308 """Personne occupant ou ayant occupé un Poste. Un Employe aura autant de
309 Dossiers qu'il occupe ou a occupé de Postes.
310
311 Cette classe aurait pu avantageusement s'appeler Personne car la notion
312 d'employé n'a pas de sens si aucun Dossier n'existe pour une personne.
313 """
9afaa55e 314 # Identification
e9bbd6ba 315 nom = models.CharField(max_length=255)
c1195471 316 prenom = models.CharField(max_length=255, verbose_name = u"Prénom")
6e4600ef 317 nom_affichage = models.CharField(max_length=255,
c1195471 318 verbose_name = u"Nom d'affichage",
6e4600ef 319 null=True, blank=True)
83b7692b 320 nationalite = models.ForeignKey(ref.Pays, to_field='code',
6e4600ef 321 db_column='nationalite',
8c1ae2b3 322 related_name='employes_nationalite',
c1195471 323 verbose_name = u"Nationalité")
a25e1d5c 324 date_naissance = models.DateField(verbose_name = u"Date de naissance",
b4aeadf3 325 validators=[validate_date_passee],
6e4600ef 326 null=True, blank=True)
2d4d2fcf 327 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
83b7692b 328
9afaa55e 329 # Infos personnelles
6e4600ef 330 situation_famille = models.CharField(max_length=1,
331 choices=SITUATION_CHOICES,
c1195471 332 verbose_name = u"Situation familiale",
6e4600ef 333 null=True, blank=True)
c1195471 334 date_entree = models.DateField(verbose_name = u"Date d'entrée à l'AUF",
7abc6d45 335 null=True, blank=True)
83b7692b 336
9afaa55e 337 # Coordonnées
8c1ae2b3 338 tel_domicile = models.CharField(max_length=255,
c1195471 339 verbose_name = u"Tél. domicile",
8c1ae2b3 340 null=True, blank=True)
341 tel_cellulaire = models.CharField(max_length=255,
c1195471 342 verbose_name = u"Tél. cellulaire",
8c1ae2b3 343 null=True, blank=True)
e9bbd6ba 344 adresse = models.CharField(max_length=255, null=True, blank=True)
e9bbd6ba 345 ville = models.CharField(max_length=255, null=True, blank=True)
346 province = models.CharField(max_length=255, null=True, blank=True)
347 code_postal = models.CharField(max_length=255, null=True, blank=True)
6e4600ef 348 pays = models.ForeignKey(ref.Pays, to_field='code', db_column='pays',
349 related_name='employes',
350 null=True, blank=True)
9afaa55e 351
6e4600ef 352 class Meta:
67666927 353 ordering = ['nom_affichage','nom','prenom']
c1195471
OL
354 verbose_name = u"Employé"
355 verbose_name_plural = u"Employés"
6e4600ef 356
9afaa55e 357 def __unicode__(self):
54773196 358 return u'%s [%s]' % (self.get_nom(), self.id)
8c1ae2b3 359
360 def get_nom(self):
ebc8eb32 361 nom_affichage = self.nom_affichage
362 if not nom_affichage:
363 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
8c1ae2b3 364 return nom_affichage
d04d084c 365
366 def civilite(self):
367 civilite = u''
368 if self.genre.upper() == u'M':
369 civilite = u'M.'
370 elif self.genre.upper() == u'F':
371 civilite = u'Mme'
372 return civilite
5ea6b5bb 373
374 def url_photo(self):
375 """Retourne l'URL du service retournant la photo de l'Employe.
376 Équivalent reverse url 'rh_photo' avec id en param.
377 """
378 from django.core.urlresolvers import reverse
379 return reverse('rh_photo', kwargs={'id':self.id})
35c0c2fe 380
c267f20c 381 def dossiers_passes(self):
382 today = date.today()
65f9fac8 383 dossiers_passes = self.dossiers.filter(date_fin__lt=today).order_by('-date_fin')
384 for d in dossiers_passes:
385 d.archive = True
386 return dossiers_passes
c267f20c 387
388 def dossiers_futurs(self):
389 today = date.today()
390 return self.dossiers.filter(date_debut__gt=today).order_by('-date_fin')
391
392 def dossiers_encours(self):
393 dossiers_p_f = self.dossiers_passes() | self.dossiers_futurs()
394 ids_dossiers_p_f = [d.id for d in dossiers_p_f]
65f9fac8 395 dossiers_encours = self.dossiers.exclude(id__in=ids_dossiers_p_f).order_by('-date_fin')
396
397 # TODO : supprimer ce code quand related_name fonctionnera ou d.remuneration_set
398 for d in dossiers_encours:
399 d.remunerations = Remuneration.objects.filter(dossier=d.id).order_by('-id')
400 return dossiers_encours
35c0c2fe 401
402 def postes_encours(self):
403 postes_encours = set()
404 for d in self.dossiers_encours():
405 postes_encours.add(d.poste)
406 return postes_encours
407
408 def poste_principal(self):
65f9fac8 409 """
410 Retourne le Poste du premier Dossier créé parmi les Dossiers en cours.
411 Idée derrière :
412 si on ajout d'autre Dossiers, c'est pour des Postes secondaires.
413 """
414 poste = Poste.objects.none()
415 try:
416 poste = self.dossiers_encours().order_by('date_debut')[0].poste
417 except:
418 pass
419 return poste
9afaa55e 420
aff1a4c6
PP
421 prefix_implantation = "dossiers__poste__implantation__region"
422 def get_regions(self):
423 regions = []
424 for d in self.dossiers.all():
425 regions.append(d.poste.implantation.region)
426 return regions
427
428
7abc6d45 429class EmployePiece(models.Model):
6e4600ef 430 """Documents relatifs à un employé.
7abc6d45 431 Ex.: CV...
432 """
f9e54d59 433 employe = models.ForeignKey('Employe', db_column='employe')
8c1ae2b3 434 nom = models.CharField(verbose_name="Nom", max_length=255)
435 fichier = models.FileField(verbose_name="Fichier",
5ea6b5bb 436 upload_to=employe_piece_dispatch,
7abc6d45 437 storage=storage_prive)
438
6e4600ef 439 class Meta:
440 ordering = ['nom']
f9e54d59
PP
441 verbose_name = u"Employé pièce"
442 verbose_name_plural = u"Employé pièces"
443
6e4600ef 444 def __unicode__(self):
445 return u'%s' % (self.nom)
446
07b40eda 447class EmployeCommentaire(Commentaire):
8c1ae2b3 448 employe = models.ForeignKey('Employe', db_column='employe',
6e4600ef 449 related_name='+')
9afaa55e 450
b343eb3d
PP
451 class Meta:
452 verbose_name = u"Employé commentaire"
453 verbose_name_plural = u"Employé commentaires"
454
2d4d2fcf 455
e9bbd6ba 456LIEN_PARENTE_CHOICES = (
457 ('Conjoint', 'Conjoint'),
458 ('Conjointe', 'Conjointe'),
459 ('Fille', 'Fille'),
460 ('Fils', 'Fils'),
461)
462
d6985a3a 463class AyantDroit(AUFMetadata):
6e4600ef 464 """Personne en relation avec un Employe.
465 """
9afaa55e 466 # Identification
e9bbd6ba 467 nom = models.CharField(max_length=255)
8c1ae2b3 468 prenom = models.CharField(max_length=255,
c1195471 469 verbose_name = u"Prénom",)
6e4600ef 470 nom_affichage = models.CharField(max_length=255,
c1195471 471 verbose_name = u"Nom d'affichage",
6e4600ef 472 null=True, blank=True)
2d4d2fcf 473 nationalite = models.ForeignKey(ref.Pays, to_field='code',
6e4600ef 474 db_column='nationalite',
8c1ae2b3 475 related_name='ayantdroits_nationalite',
c1195471 476 verbose_name = u"Nationalité")
a25e1d5c 477 date_naissance = models.DateField(verbose_name = u"Date de naissance",
25037368 478 validators=[validate_date_passee],
6e4600ef 479 null=True, blank=True)
2d4d2fcf 480 genre = models.CharField(max_length=1, choices=GENRE_CHOICES)
83b7692b 481
9afaa55e 482 # Relation
483 employe = models.ForeignKey('Employe', db_column='employe',
8c1ae2b3 484 related_name='ayantdroits',
c1195471 485 verbose_name = u"Employé")
6e4600ef 486 lien_parente = models.CharField(max_length=10,
487 choices=LIEN_PARENTE_CHOICES,
c1195471 488 verbose_name = u"Lien de parenté",
6e4600ef 489 null=True, blank=True)
490
491 class Meta:
492 ordering = ['nom_affichage']
c1195471
OL
493 verbose_name = u"Ayant droit"
494 verbose_name_plural = u"Ayants droit"
8c1ae2b3 495
6e4600ef 496 def __unicode__(self):
8c1ae2b3 497 return u'%s' % (self.get_nom())
498
499 def get_nom(self):
500 nom_affichage = self.nom_affichage
501 if not nom_affichage:
502 nom_affichage = u'%s %s' % (self.nom.upper(), self.prenom)
503 return nom_affichage
83b7692b 504
aff1a4c6
PP
505 prefix_implantation = "employe__dossiers__poste__implantation__region"
506 def get_regions(self):
507 regions = []
508 for d in self.employe.dossiers.all():
509 regions.append(d.poste.implantation.region)
510 return regions
511
512
07b40eda 513class AyantDroitCommentaire(Commentaire):
8c1ae2b3 514 ayant_droit = models.ForeignKey('AyantDroit', db_column='ayant_droit',
6e4600ef 515 related_name='+')
83b7692b 516
2d4d2fcf 517
83b7692b 518### DOSSIER
519
520STATUT_RESIDENCE_CHOICES = (
521 ('local', 'Local'),
522 ('expat', 'Expatrié'),
523)
524
525COMPTE_COMPTA_CHOICES = (
526 ('coda', 'CODA'),
527 ('scs', 'SCS'),
528 ('aucun', 'Aucun'),
529)
530
d6985a3a 531class Dossier_(AUFMetadata):
6e4600ef 532 """Le Dossier regroupe les informations relatives à l'occupation
533 d'un Poste par un Employe. Un seul Dossier existe par Poste occupé
534 par un Employe.
535
536 Plusieurs Contrats peuvent être associés au Dossier.
537 Une structure de Remuneration est rattachée au Dossier. Un Poste pour
538 lequel aucun Dossier n'existe est un poste vacant.
539 """
3f5cbabe
OL
540
541 objects = DossierManager()
542
63e17dff 543 # TODO: OneToOne ??
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 \
a25e1d5c 579 de poste",)
c1195471 580 date_fin = models.DateField(verbose_name = u"Date de fin d'occupation \
6e4600ef 581 de poste",
6e4600ef 582 null=True, blank=True)
e9bbd6ba 583
2d4d2fcf 584 # Comptes
585 # TODO?
83b7692b 586
6e4600ef 587 class Meta:
37868f0b 588 abstract = True
49449367 589 ordering = ['employe__nom', ]
3f5f3898 590 verbose_name = u"Dossier"
8c1ae2b3 591 verbose_name_plural = "Dossiers"
6e4600ef 592
65f9fac8 593 def salaire_theorique(self):
594 annee = date.today().year
595 coeff = self.classement.coefficient
596 implantation = self.poste.implantation
597 point = ValeurPoint.objects.get(implantation=implantation, annee=annee)
598
599 montant = coeff * point.valeur
600 devise = point.devise
601 return {'montant':montant, 'devise':devise}
602
83b7692b 603 def __unicode__(self):
8c1ae2b3 604 poste = self.poste.nom
605 if self.employe.genre == 'F':
606 poste = self.poste.nom_feminin
607 return u'%s - %s' % (self.employe, poste)
83b7692b 608
aff1a4c6
PP
609 prefix_implantation = "poste__implantation__region"
610 def get_regions(self):
611 return [self.poste.implantation.region]
612
37868f0b 613
3ebc0952
OL
614 def remunerations(self):
615 return self.rh_remuneration_remunerations.all().order_by('date_debut')
616
09aa8374
OL
617 def get_salaire(self):
618 try:
619 return [r for r in self.remunerations().order_by('-date_debut') if r.type_id == 1][0]
620 except:
621 return None
3ebc0952 622
37868f0b
NC
623class Dossier(Dossier_):
624 __doc__ = Dossier_.__doc__
16b1454e
OL
625 poste = models.ForeignKey('Poste', db_column='poste', related_name='%(app_label)s_dossiers')
626 employe = models.ForeignKey('Employe', db_column='employe',
627 related_name='%(app_label)s_dossiers',
628 verbose_name=u"Employé")
37868f0b
NC
629
630
83b7692b 631class DossierPiece(models.Model):
7abc6d45 632 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
633 Ex.: Lettre de motivation.
634 """
8c1ae2b3 635 dossier = models.ForeignKey('Dossier', db_column='dossier',
6e4600ef 636 related_name='+')
c1195471
OL
637 nom = models.CharField(verbose_name = u"Nom", max_length=255)
638 fichier = models.FileField(verbose_name = u"Fichier",
83b7692b 639 upload_to=dossier_piece_dispatch,
640 storage=storage_prive)
641
6e4600ef 642 class Meta:
643 ordering = ['nom']
644
645 def __unicode__(self):
646 return u'%s' % (self.nom)
647
07b40eda 648class DossierCommentaire(Commentaire):
8c1ae2b3 649 dossier = models.ForeignKey('Dossier', db_column='dossier',
6e4600ef 650 related_name='+')
83b7692b 651
1d0f4eef
OL
652class DossierComparaison(models.Model):
653 """
654 Photo d'une comparaison salariale au moment de l'embauche.
655 """
16b1454e
OL
656
657 objects = DossierComparaisonManager()
658
1d0f4eef 659 dossier = models.ForeignKey('Dossier', related_name='comparaisons')
8c6269cc 660 implantation = models.ForeignKey(ref.Implantation, related_name="+", null=True, blank=True)
1d0f4eef
OL
661 poste = models.CharField(max_length=255, null=True, blank=True)
662 personne = models.CharField(max_length=255, null=True, blank=True)
663 montant = models.IntegerField(null=True)
93494cf1 664 devise = models.ForeignKey('Devise', default=5, related_name='+', null=True, blank=True)
1d0f4eef
OL
665
666 def taux_devise(self):
667 liste_taux = self.devise.tauxchange_set.order_by('-annee').filter(implantation=self.dossier.poste.implantation)
668 if len(liste_taux) == 0:
669 raise Exception(u"La devise %s n'a pas de taux pour l'implantation %s" % (self.devise, self.dossier.poste.implantation))
670 else:
671 return liste_taux[0].taux
672
673 def montant_euros(self):
674 return round(float(self.montant) * float(self.taux_devise()), 2)
675
2d4d2fcf 676
07b40eda 677### RÉMUNÉRATION
e9bbd6ba 678
d6985a3a 679class RemunerationMixin(AUFMetadata):
9afaa55e 680 # Identification
6e7c919b
NC
681 dossier = models.ForeignKey('Dossier', db_column='dossier',
682 related_name='%(app_label)s_%(class)s_remunerations')
83b7692b 683 type = models.ForeignKey('TypeRemuneration', db_column='type',
8c1ae2b3 684 related_name='+',
c1195471 685 verbose_name = u"Type de rémunération")
7abc6d45 686 type_revalorisation = models.ForeignKey('TypeRevalorisation',
687 db_column='type_revalorisation',
6e4600ef 688 related_name='+',
c1195471 689 verbose_name = u"Type de revalorisation",
7abc6d45 690 null=True, blank=True)
50fa9bc1 691 montant = models.FloatField(null=True, blank=True,
6e4600ef 692 default=0)
2d4d2fcf 693 # Annuel (12 mois, 52 semaines, 364 jours?)
c589d980 694 devise = models.ForeignKey('Devise', to_field='id',
6e4600ef 695 db_column='devise', related_name='+',
696 default=5)
2d4d2fcf 697 # commentaire = precision
698 commentaire = models.CharField(max_length=255, null=True, blank=True)
699 # date_debut = anciennement date_effectif
a25e1d5c 700 date_debut = models.DateField(verbose_name = u"Date de début",
6e4600ef 701 null=True, blank=True)
a25e1d5c 702 date_fin = models.DateField(verbose_name = u"Date de fin",
6e4600ef 703 null=True, blank=True)
83b7692b 704
2d4d2fcf 705 class Meta:
706 abstract = True
6e4600ef 707 ordering = ['type__nom', '-date_fin']
708
709 def __unicode__(self):
710 return u'%s %s (%s)' % (self.montant, self.devise.code, self.type.nom)
2d4d2fcf 711
6e7c919b 712class Remuneration_(RemunerationMixin):
2d4d2fcf 713 """Structure de rémunération (données budgétaires) en situation normale
714 pour un Dossier. Si un Evenement existe, utiliser la structure de
715 rémunération EvenementRemuneration de cet événement.
716 """
83b7692b 717
718 def montant_mois(self):
719 return round(self.montant / 12, 2)
720
721 def taux_devise(self):
722 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
723
724 def montant_euro(self):
725 return round(float(self.montant) / float(self.taux_devise()), 2)
726
727 def montant_euro_mois(self):
728 return round(self.montant_euro() / 12, 2)
9afaa55e 729
730 def __unicode__(self):
731 try:
732 devise = self.devise.code
733 except:
734 devise = "???"
735 return "%s %s" % (self.montant, devise)
83b7692b 736
6e7c919b
NC
737 class Meta:
738 abstract = True
c1195471
OL
739 verbose_name = u"Rémunération"
740 verbose_name_plural = u"Rémunérations"
6e7c919b
NC
741
742
743class Remuneration(Remuneration_):
744 __doc__ = Remuneration_.__doc__
745
2d4d2fcf 746
747### CONTRATS
c41b7fcc
OL
748
749class ContratManager(NoDeleteManager):
750 def get_query_set(self):
751 return super(ContratManager, self).get_query_set().select_related('dossier', 'dossier__poste')
752
2d4d2fcf 753
d6985a3a 754class Contrat(AUFMetadata):
2d4d2fcf 755 """Document juridique qui encadre la relation de travail d'un Employe
756 pour un Poste particulier. Pour un Dossier (qui documente cette
757 relation de travail) plusieurs contrats peuvent être associés.
758 """
c41b7fcc
OL
759
760 objects = ContratManager()
761
6e4600ef 762 dossier = models.ForeignKey('Dossier', db_column='dossier',
65f9fac8 763 related_name='contrats')
6e4600ef 764 type_contrat = models.ForeignKey('TypeContrat', db_column='type_contrat',
8c1ae2b3 765 related_name='+',
c1195471 766 verbose_name = u"Type de contrat")
a25e1d5c
OL
767 date_debut = models.DateField(verbose_name = u"Date de début")
768 date_fin = models.DateField(verbose_name = u"Date de fin",
6e4600ef 769 null=True, blank=True)
770
771 class Meta:
772 ordering = ['dossier__employe__nom_affichage']
c1195471
OL
773 verbose_name = u"Contrat"
774 verbose_name_plural = u"Contrats"
6e4600ef 775
776 def __unicode__(self):
8c1ae2b3 777 return u'%s - %s' % (self.dossier, self.id)
6e4600ef 778
779# TODO? class ContratPiece(models.Model):
2d4d2fcf 780
781
782### ÉVÉNEMENTS
783
d6985a3a 784class Evenement_(AUFMetadata):
6e4600ef 785 """Un Evenement sert à déclarer une situation temporaire (exceptionnelle)
786 d'un Dossier qui vient altérer des informations normales liées à un Dossier
787 (ex.: la Remuneration).
788
789 Ex.: congé de maternité, maladie...
790
791 Lors de ces situations exceptionnelles, l'Employe a un régime de travail
792 différent et une rémunération en conséquence. On souhaite toutefois
793 conserver le Dossier intact afin d'éviter une re-saisie des données lors
794 du retour à la normale.
795 """
8c1ae2b3 796 dossier = models.ForeignKey('Dossier', db_column='dossier',
6e4600ef 797 related_name='+')
798 nom = models.CharField(max_length=255)
a25e1d5c
OL
799 date_debut = models.DateField(verbose_name = u"Date de début")
800 date_fin = models.DateField(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
a122050d 980 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 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 1070
09aa8374 1071 actuelles = ValeurPointManager()
701f3bea 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
9afaa55e 1088 def __unicode__(self):
8c1ae2b3 1089 return u'%s %s (%s)' % (self.valeur, self.devise, self.annee)
6e7c919b
NC
1090
1091
1092class ValeurPoint(ValeurPoint_):
1093 __doc__ = ValeurPoint_.__doc__
1094
e9bbd6ba 1095
d6985a3a 1096class Devise(AUFMetadata):
6e4600ef 1097 """Devise monétaire.
1098 """
e9bbd6ba 1099 code = models.CharField(max_length=10, unique=True)
1100 nom = models.CharField(max_length=255)
1101
6e4600ef 1102 class Meta:
1103 ordering = ['code']
c1195471
OL
1104 verbose_name = u"Devise"
1105 verbose_name_plural = u"Devises"
6e4600ef 1106
e9bbd6ba 1107 def __unicode__(self):
1108 return u'%s - %s' % (self.code, self.nom)
1109
d6985a3a 1110class TypeContrat(AUFMetadata):
6e4600ef 1111 """Type de contrat.
1112 """
e9bbd6ba 1113 nom = models.CharField(max_length=255)
6e4600ef 1114 nom_long = models.CharField(max_length=255)
49f9f116 1115
8c1ae2b3 1116 class Meta:
1117 ordering = ['nom']
c1195471
OL
1118 verbose_name = u"Type de contrat"
1119 verbose_name_plural = u"Types de contrat"
8c1ae2b3 1120
9afaa55e 1121 def __unicode__(self):
1122 return u'%s' % (self.nom)
30be56d5 1123
2d4d2fcf 1124
1125### AUTRES
1126
d6985a3a 1127class ResponsableImplantation(AUFMetadata):
30be56d5 1128 """Le responsable d'une implantation.
1129 Anciennement géré sur le Dossier du responsable.
1130 """
6e4600ef 1131 employe = models.ForeignKey('Employe', db_column='employe',
1132 related_name='+',
1133 null=True, blank=True)
1134 implantation = models.ForeignKey(ref.Implantation,
1135 db_column='implantation', related_name='+',
1136 unique=True)
30be56d5 1137
1138 def __unicode__(self):
1139 return u'%s : %s' % (self.implantation, self.employe)
1140
1141 class Meta:
1142 ordering = ['implantation__nom']
8c1ae2b3 1143 verbose_name = "Responsable d'implantation"
1144 verbose_name_plural = "Responsables d'implantation"