rh.models progression refactoring...
[auf_rh_dae.git] / project / rh / models.py
1 # -=- encoding: utf-8 -=-
2
3 import datetime
4
5 from django.core.files.storage import FileSystemStorage
6 from django.db import models
7 import settings
8
9 import datamaster_modeles.models as ref
10
11
12 # Upload de fichiers
13 storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
14 base_url=settings.PRIVE_MEDIA_URL)
15
16 def poste_piece_dispatch(instance, filename):
17 path = "poste/%s/%s" % (instance.poste_id, filename)
18 return path
19
20 def dossier_piece_dispatch(instance, filename):
21 path = "dossier/%s/%s" % (instance.dossier_id, filename)
22 return path
23
24
25 ### POSTE
26
27 POSTE_APPEL_CHOICES = (
28 ('interne', 'Interne'),
29 ('externe', 'Externe'),
30 )
31
32 class Poste(models.Model):
33 # Identification
34 id = models.IntegerField(primary_key=True)
35 nom = models.CharField(verbose_name="Titre du poste", max_length=255)
36 # nom_feminin
37 implantation = models.ForeignKey(ref.Implantation)
38 type_poste = models.ForeignKey(rh.TypePoste, null=True, related_name='+')
39 service = models.ForeignKey(rh.Service, related_name='+',
40 verbose_name=u"Direction/Service/Pôle support")
41 responsable = models.ForeignKey(rh.Poste, related_name='+',
42 verbose_name="Poste du responsable")
43
44 # Contrat
45 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
46 default=100,
47 verbose_name="Temps de travail",
48 help_text="% du temps complet")
49 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
50 decimal_places=2,
51 default=35,
52 verbose_name="Nb. heures par semaine")
53
54 # Recrutement
55 local = models.BooleanField(verbose_name="Local", default=True, blank=True)
56 expatrie = models.BooleanField(verbose_name="Expatrié", default=False,
57 blank=True)
58
59 # TODO null?
60 mise_a_disposition = models.BooleanField(verbose_name="Mise à disposition")
61 appel = models.CharField(max_length=10, default='interne',
62 verbose_name="Appel à candidature",
63 choices=POSTE_APPEL_CHOICES)
64
65 # Rémunération
66 classement_min = models.ForeignKey(rh.Classement, related_name='+')
67 classement_max = models.ForeignKey(rh.Classement, related_name='+')
68 valeur_point_min = models.ForeignKey(rh.ValeurPoint, related_name='+',
69 blank=True, null=True)
70 valeur_point_max = models.ForeignKey(rh.ValeurPoint, related_name='+',
71 blank=True, null=True)
72 devise_min = models.ForeignKey(rh.Devise, default=5, related_name='+')
73 devise_max = models.ForeignKey(rh.Devise, default=5, related_name='+')
74 salaire_min = models.DecimalField(max_digits=12, decimal_places=2,
75 default=0)
76 salaire_max = models.DecimalField(max_digits=12, decimal_places=2,
77 default=0)
78 indemn_min = models.DecimalField(max_digits=12, decimal_places=2,
79 default=0)
80 indemn_max = models.DecimalField(max_digits=12, decimal_places=2,
81 default=0)
82 autre_min = models.DecimalField(max_digits=12, decimal_places=2,
83 default=0)
84 autre_max = models.DecimalField(max_digits=12, decimal_places=2,
85 default=0)
86
87 # Comparatifs de rémunération
88 devise_comparaison = models.ForeignKey(rh.Devise, related_name='+',
89 default=5)
90 comp_locale_min = models.DecimalField(max_digits=12, decimal_places=2,
91 null=True, blank=True)
92 comp_locale_max = models.DecimalField(max_digits=12, decimal_places=2,
93 null=True, blank=True)
94 comp_universite_min = models.DecimalField(max_digits=12, decimal_places=2,
95 null=True, blank=True)
96 comp_universite_max = models.DecimalField(max_digits=12, decimal_places=2,
97 null=True, blank=True)
98 comp_fonctionpub_min = models.DecimalField(max_digits=12, decimal_places=2,
99 null=True, blank=True)
100 comp_fonctionpub_max = models.DecimalField(max_digits=12, decimal_places=2,
101 null=True, blank=True)
102 comp_ong_min = models.DecimalField(max_digits=12, decimal_places=2,
103 null=True, blank=True)
104 comp_ong_max = models.DecimalField(max_digits=12, decimal_places=2,
105 null=True, blank=True)
106 comp_autre_min = models.DecimalField(max_digits=12, decimal_places=2,
107 null=True, blank=True)
108 comp_autre_max = models.DecimalField(max_digits=12, decimal_places=2,
109 null=True, blank=True)
110
111 # Justification
112 justification = models.TextField()
113
114 # Méta
115 date_validation = models.DateTimeField() # provenance : dae
116 date_creation = models.DateTimeField(auto_now_add=True)
117 date_modification = models.DateTimeField(auto_now=True)
118 date_debut = models.DateField(verbose_name="Date de début",
119 help_text="format: aaaa-mm-jj")
120 date_fin = models.DateField(null=True, blank=True,
121 verbose_name="Date de fin",
122 help_text="format: aaaa-mm-jj")
123 actif = models.BooleanField(default=True)
124
125 def __unicode__(self):
126 # gérer si poste est vacant ou non dans affichage
127 return u'%s - %s [%s]' % (self.implantation, self.nom, self.id)
128
129
130 POSTE_FINANCEMENT_CHOICES = (
131 ('A', 'A - Frais de personnel'),
132 ('B', 'B - Projet(s)-Titre(s)'),
133 ('C', 'C - Autre')
134 )
135
136 class PosteFinancement(models.Model):
137 poste = models.ForeignKey('Poste', related_name='financements')
138 type = models.CharField(max_length=1, choices=POSTE_FINANCEMENT_CHOICES)
139 pourcentage = models.DecimalField(max_digits=12, decimal_places=2,
140 help_text="ex.: 33.33 % (décimale avec point)")
141 commentaire = models.TextField(
142 help_text="Spécifiez la source de financement.")
143
144 class Meta:
145 ordering = ['type']
146
147 class PostePiece(models.Model):
148 """Documents relatifs au Poste
149 Ex.: Description de poste
150 """
151 poste = models.ForeignKey("Poste")
152 nom = models.CharField(verbose_name="Nom", max_length=255)
153 fichier = models.FileField(verbose_name="Fichier",
154 upload_to=poste_piece_dispatch,
155 storage=storage_prive)
156
157
158 ### EMPLOYÉ/PERSONNE
159
160 GENRE_CHOICES = (
161 ('M', 'Homme'),
162 ('F', 'Femme'),
163 )
164 SITUATION_CHOICES = (
165 ('C', 'Célibataire'),
166 ('F', 'Fiancé'),
167 ('M', 'Marié'),
168 )
169
170 class Employe(models.Model):
171 # Identification
172 id = models.IntegerField(primary_key=True)
173 nom = models.CharField(max_length=255)
174 prenom = models.CharField(max_length=255)
175 nationalite = models.ForeignKey(ref.Pays, to_field='code',
176 related_name='employes_nationalite',
177 db_column='nationalite')
178 date_naissance = models.DateField(null=True, blank=True)
179
180 # Infos personnelles
181 genre = models.CharField(max_length=1, null=True, blank=True,
182 choices=GENRE_CHOICES)
183 situation_famille = models.CharField(max_length=1, null=True, blank=True,
184 choices=SITUATION_CHOICES)
185 date_entree = models.DateField(verbose_name="Date d'entrée à l'AUF",
186 null=True, blank=True)
187
188 # Coordonnées
189 tel_domicile = models.CharField(max_length=255, null=True, blank=True)
190 tel_cellulaire = models.CharField(max_length=255, null=True, blank=True)
191 adresse = models.CharField(max_length=255, null=True, blank=True)
192 no_rue = models.CharField(max_length=255, null=True, blank=True)
193 ville = models.CharField(max_length=255, null=True, blank=True)
194 province = models.CharField(max_length=255, null=True, blank=True)
195 code_postal = models.CharField(max_length=255, null=True, blank=True)
196 pays = models.ForeignKey(ref.Pays, to_field='code',
197 null=True, blank=True,
198 related_name='employes', db_column='pays')
199
200 # Métas
201 date_creation = models.DateField(auto_now_add=True)
202 date_maj = models.DateField(auto_now=True) # date_modification
203 commentaire = models.TextField(null=True, blank=True)
204
205 def __unicode__(self):
206 return u'%s %s' % (self.prenom, self.nom)
207
208 class EmployePiece(models.Model):
209 """Documents relatifs à l'employé
210 Ex.: CV...
211 """
212 employe = models.ForeignKey("Employe")
213 nom = models.CharField(verbose_name="Nom", max_length=255)
214 fichier = models.FileField(verbose_name="Fichier",
215 upload_to=dossier_piece_dispatch,
216 storage=storage_prive)
217
218
219 LIEN_PARENTE_CHOICES = (
220 ('Conjoint', 'Conjoint'),
221 ('Conjointe', 'Conjointe'),
222 ('Fille', 'Fille'),
223 ('Fils', 'Fils'),
224 )
225
226 class AyantDroit(models.Model):
227 # Identification
228 id = models.IntegerField(primary_key=True)
229 nom = models.CharField(max_length=255)
230 prenom = models.CharField(max_length=255)
231 # nationalite facult
232 # date_naissance facultatif
233 # genre facultafif
234
235 # Relation
236 employe = models.ForeignKey('Employe', db_column='employe',
237 related_name='ayants_droit')
238 lien_parente = models.CharField(max_length=10, null=True, blank=True,
239 choices=LIEN_PARENTE_CHOICES)
240
241 # Méta
242 commentaire = models.TextField(null=True, blank=True)
243 actif = models.BooleanField()
244
245
246 ### DOSSIER
247
248 STATUT_RESIDENCE_CHOICES = (
249 ('local', 'Local'),
250 ('expat', 'Expatrié'),
251 )
252
253 COMPTE_COMPTA_CHOICES = (
254 ('coda', 'CODA'),
255 ('scs', 'SCS'),
256 ('aucun', 'Aucun'),
257 )
258
259 class Dossier(models.Model):
260 # Identification
261 id = models.IntegerField(primary_key=True)
262 #code = models.CharField(max_length=10, unique=True)
263 employe = models.ForeignKey('Employe', db_column='employe')
264 poste = models.ForeignKey('Poste', related_name='+', editable=False)
265 statut = models.ForeignKey('Statut', related_name='+') # blank=True, null=True ?
266 organisme_bstg = models.ForeignKey('OrganismeBstg',
267 null=True, blank=True,
268 verbose_name="Organisme",
269 help_text="Si détaché (DET) ou mis à disposition (MAD), \
270 préciser l'organisme.",
271 related_name='+')
272
273 # Recrutement
274 remplacement = models.BooleanField() # default False
275 statut_residence = models.CharField(max_length=10, default='local',
276 verbose_name="Statut",
277 choices=STATUT_RESIDENCE_CHOICES)
278
279 # Rémunération
280 classement = models.ForeignKey(rh.Classement, related_name='+')
281 regime_travail = models.DecimalField(max_digits=12, decimal_places=2,
282 verbose_name="Régime de travail",
283 help_text="% du temps complet") # default = 100
284 regime_travail_nb_heure_semaine = models.DecimalField(max_digits=12,
285 decimal_places=2, verbose_name="Nb. heures par semaine") # default = 35
286
287 # Occupation du Poste par cet Employe (anciennement "mandat")
288 date_debut = models.DateField(verbose_name="Date de début d'occupation de poste")
289 date_fin = models.DateField(verbose_name="Date de fin d'occupation de poste",
290 null=True, blank=True)
291
292 # Contrat
293 # m2m Contrat
294
295 # Méta
296 date_creation = models.DateTimeField(auto_now_add=True)
297 date_modification = models.DateField(auto_now=True)
298 commentaire = models.TextField(null=True, blank=True)
299
300 def __unicode__(self):
301 return u'%s - %s' % (self.poste.nom, self.employe)
302
303 class DossierPiece(models.Model):
304 """Documents relatifs au Dossier (à l'occupation de ce poste par employé).
305 Ex.: Lettre de motivation.
306 """
307 dossier = models.ForeignKey("Dossier")
308 nom = models.CharField(verbose_name="Nom", max_length=255)
309 fichier = models.FileField(verbose_name="Fichier",
310 upload_to=dossier_piece_dispatch,
311 storage=storage_prive)
312
313 class DossierCommentaire(models.Model):
314 dossier = models.ForeignKey("Dossier")
315 commentaire = models.TextField(null=True, blank=True)
316 # Méta qui quand...
317
318 ### RÉMUNÉRATION
319
320 class Remuneration(models.Model):
321 # Identification
322 id = models.IntegerField(primary_key=True)
323 dossier = models.ForeignKey('Dossier', db_column='dossier')
324 type = models.ForeignKey('TypeRemuneration', db_column='type',
325 related_name='+')
326 # TODO: what's that?
327 type_revalorisation = models.ForeignKey('TypeRevalorisation',
328 db_column='type_revalorisation',
329 null=True, blank=True)
330 montant = models.FloatField(null=True, blank=True) # Annuel (12 mois, 52 semaines, 364 jours)
331 devise = models.ForeignKey('Devise', to_field='code', db_column='devise')#,
332 #null=True, blank=True)
333 precision = models.CharField(max_length=255, null=True, blank=True) # precision = commentaire
334 date_debut = models.DateField(null=True, blank=True) # anciennement date_effectif
335 #date_fin = null=True
336
337 # Méta
338 date_creation = models.DateField(auto_now_add=True)
339 user_creation = models.IntegerField(null=True, blank=True) #User ou employé
340 # desactivation = models.BooleanField(null=True, blank=True) #
341 # date_desactivation = models.DateField(null=True, blank=True)
342 # user_desactivation = models.IntegerField(null=True, blank=True) #User ou employé
343 # annulation = models.BooleanField(null=True, blank=True)
344 # date_annulation = models.DateField(null=True, blank=True)
345 # user_annulation = models.IntegerField(null=True, blank=True) #User ou employé
346
347 def montant_mois(self):
348 return round(self.montant / 12, 2)
349
350 def taux_devise(self):
351 return self.devise.tauxchange_set.order_by('-annee').all()[0].taux
352
353 def montant_euro(self):
354 return round(float(self.montant) / float(self.taux_devise()), 2)
355
356 def montant_euro_mois(self):
357 return round(self.montant_euro() / 12, 2)
358
359 def __unicode__(self):
360 try:
361 devise = self.devise.code
362 except:
363 devise = "???"
364 return "%s %s" % (self.montant, devise)
365
366 class HistoriqueRemuneration(models.Model):
367 dossier = models.ForeignKey("Dossier")
368 # toujours pertinent si pour chaque Remuneration j'ai dd et df?
369
370
371 ### RÉFÉRENCES RH
372
373 class FamilleEmploi(models.Model):
374 # Identification
375 id = models.IntegerField(primary_key=True)
376 nom = models.CharField(max_length=255)
377 # Méta
378 actif = models.BooleanField()
379
380 class TypePoste(models.Model):
381 # Identification
382 id = models.IntegerField(primary_key=True)
383 nom = models.CharField(max_length=255)
384 nom_feminin = models.CharField(max_length=255)
385 #description = models.CharField(max_length=255)
386 is_responsable = models.BooleanField()
387 famille_emploi = models.ForeignKey('FamilleEmploi',
388 db_column='famille_emploi')
389 # Méta
390 date_modification = models.DateField(auto_now=True)
391 actif = models.BooleanField()
392
393 def __unicode__(self):
394 return u'%s' % self.nom
395
396
397 TYPE_PAIEMENT_CHOICES = (
398 ('Régulier', 'Régulier'),
399 ('Ponctuel', 'Ponctuel'),
400 )
401
402 NATURE_REMUNERATION_CHOICES = (
403 ('Accessoire', 'Accessoire'),
404 ('Charges', 'Charges'),
405 ('Indemnité', 'Indemnité'),
406 ('RAS', 'Rémunération autre source'),
407 ('Traitement', 'Traitement'),
408 )
409
410 class TypeRemuneration(models.Model):
411 # Identification
412 id = models.IntegerField(primary_key=True)
413 nom = models.CharField(max_length=255)
414 type_paiement = models.CharField(max_length=30,
415 choices=TYPE_PAIEMENT_CHOICES)
416 nature_remuneration = models.CharField(max_length=30,
417 choices=NATURE_REMUNERATION_CHOICES)
418 # Méta
419 actif = models.BooleanField()
420
421 def __unicode__(self):
422 return u'%s' % self.nom
423
424 class TypeRevalorisation(models.Model):
425 """Justification du changement de la Remuneration.
426 (Actuellement utilisé dans aucun traitement informatique)
427 """
428 # Identification
429 id = models.IntegerField(primary_key=True)
430 nom = models.CharField(max_length=255)
431 # Méta
432 actif = models.BooleanField()
433
434 class Service(models.Model):
435 # Identification
436 id = models.IntegerField(primary_key=True)
437 nom = models.CharField(max_length=255)
438 # Méta
439 actif = models.BooleanField()
440
441 def __unicode__(self):
442 return u'%s' % self.nom
443
444 class Meta:
445 ordering = ['nom']
446
447
448 TYPE_ORGANISME_CHOICES = (
449 ('MAD', 'Mise à disposition'),
450 ('DET', 'Détachement'),
451 )
452
453 class OrganismeBstg(models.Model):
454 # Identification
455 id = models.IntegerField(primary_key=True)
456 nom = models.CharField(max_length=255)
457 type = models.CharField(max_length=10, choices=TYPE_ORGANISME_CHOICES)
458 # pays
459
460 # Méta
461 actif = models.BooleanField()
462
463 def __unicode__(self):
464 return u'%s (%s)' % (self.nom, self.type)
465
466 class Meta:
467 ordering = ['type', 'nom']
468
469
470 CONTRAT_CATEGORIE_CHOICES= (
471 ('A', 'A'),
472 ('C', 'C'),
473 )
474
475 class Statut(models.Model):
476 # Identification
477 id = models.IntegerField(primary_key=True)
478 code = models.CharField(max_length=25, unique=True)
479 nom = models.CharField(max_length=255)
480 type_contrat_categorie = models.CharField(max_length=10,
481 choices=CONTRAT_CATEGORIE_CHOICES)
482 #CHOICES A, C (veut dire quoi?) voir TypeContrat.categorie
483 # A = AUF, C = Contractuel ???
484 # Méta
485 actif = models.BooleanField()
486
487 def __unicode__(self):
488 return u'%s : %s' % (self.code, self.nom)
489
490
491 TYPE_CLASSEMENT_CHOICES = (
492 ('S', 'S'),
493 ('T', 'T'),
494 )
495
496 class Classement(models.Model):
497 # Identification
498 id = models.IntegerField(primary_key=True)
499 type = models.CharField(max_length=10, choices=TYPE_CLASSEMENT_CHOICES)
500 echelon = models.IntegerField()
501 degre = models.IntegerField()
502 coefficient = models.FloatField()
503 # annee # au lieu de date_debut et date_fin
504 # Méta
505 commentaire = models.TextField(null=True, blank=True)
506 date_modification = models.DateField(auto_now=True)
507 actif = models.BooleanField()
508
509 def __unicode__(self):
510 return u'%s.%s.%s (%s)' % (self.type, self.echelon, self.degre,
511 self.coefficient)
512 class Meta:
513 ordering = ['type','echelon','degre','coefficient']
514
515 class TauxChange(models.Model):
516 """Taux de change de la devise vers l'euro (EUR)
517 pour cette année budgétaire.
518 """
519 # Identification
520 id = models.IntegerField(primary_key=True)
521 devise = models.ForeignKey('Devise', to_field='code', db_column='devise')
522 annee = models.IntegerField()
523 taux = models.FloatField()
524
525 class ValeurPoint(models.Model):
526 # Identification
527 id = models.IntegerField(primary_key=True)
528 valeur = models.FloatField()
529 # devise
530 implantation = models.ForeignKey(ref.Implantation,
531 db_column='implantation',
532 related_name='valeurs_point')
533 # Méta
534 annee = models.IntegerField()
535
536 # Stockage de tous les taux de change pour optimiser la recherche de la devise associée
537 annee_courante = datetime.datetime.now().year
538 tauxchange = TauxChange.objects.select_related('devise').filter(annee=annee_courante)
539
540 def __unicode__(self):
541 tx = self.get_tauxchange_courant()
542 if tx:
543 devise_code = tx.devise.code
544 else:
545 devise_code = "??"
546 return u'%s %s (%s-%s)' % (self.valeur, devise_code, self.implantation_id, self.annee)
547
548 class Meta:
549 ordering = ['valeur']
550
551 class Devise(models.Model):
552 id = models.IntegerField(primary_key=True)
553 code = models.CharField(max_length=10, unique=True)
554 nom = models.CharField(max_length=255)
555
556 def __unicode__(self):
557 return u'%s - %s' % (self.code, self.nom)
558
559 class TypeContrat(models.Model):
560 # Identification
561 id = models.IntegerField(primary_key=True)
562 nom = models.CharField(max_length=255)
563 nom_long = models.CharField(max_length=255) #description
564 categorie = models.CharField(max_length=10,
565 choices=CONTRAT_CATEGORIE_CHOICES) # A, C?
566 # Méta
567 actif = models.BooleanField()
568
569 def __unicode__(self):
570 return u'%s' % (self.nom)
571
572 class ResponsableImplantation(models.Model):
573 """Le responsable d'une implantation.
574 Anciennement géré sur le Dossier du responsable.
575 """
576 employe = models.ForeignKey('Employe', null=True, blank=True)
577 implantation = models.ForeignKey(ref.Implantation, unique=True)
578
579 def __unicode__(self):
580 return u'%s : %s' % (self.implantation, self.employe)
581
582 class Meta:
583 ordering = ['implantation__nom']
584
585 class Contrat(models.Model):
586 dossier = models.ForeignKey("Dossier") # 1 contrat peut être dans plusieurs dossier Dossier.contrat = m2m Contrat
587 type_contrat = models.ForeignKey('TypeContrat', related_name='+')
588 date_debut = models.DateField(help_text="format: aaaa-mm-jj")
589 date_fin = models.DateField(null=True, blank=True,
590 help_text="format: aaaa-mm-jj")
591
592 class Evenement(models.Models):
593 pass
594 # nom
595 # date_debut
596 # date_fin
597
598 class EvenementRemuneration(models.Model):
599 pass
600 # evenement
601 # structure de rémunération hérité de abstract ici