1 # -=- encoding: utf-8 -=-
4 from django
.core
.files
.storage
import FileSystemStorage
5 from django
.db
import models
7 from workflow
import PosteWorkflow
, DossierWorkflow
8 import datamaster_modeles
.models
as ref
9 from rh_v1
import models
as rh
12 STATUT_RESIDENCE_CHOICES
= (
14 ('expat', 'Expatrié'),
17 POSTE_APPEL_CHOICES
= (
18 ('interne', 'Interne'),
19 ('externe', 'Externe'),
22 POSTE_STATUT_CHOICES
= (
23 ('MAD', 'Mise à disposition'),
24 ('DET', 'Détachement'),
28 storage_prive
= FileSystemStorage(settings
.PRIVE_MEDIA_ROOT
, base_url
=settings
.PRIVE_MEDIA_URL
)
30 def poste_piece_dispatch(instance
, filename
):
31 path
= "poste/%s/%s" % (instance
.poste_id
, filename
)
34 def dossier_piece_dispatch(instance
, filename
):
35 path
= "dossier/%s/%s" % (instance
.dossier_id
, filename
)
39 class PostePiece(models
.Model
):
40 poste
= models
.ForeignKey("Poste")
41 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
42 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
=poste_piece_dispatch
, storage
=storage_prive
)
44 class PosteManager(models
.Manager
):
46 Chargement de tous les objets FK existants sur chaque QuerySet.
48 def get_query_set(self
):
60 return super(PosteManager
, self
).get_query_set() \
61 .select_related(*fkeys
).all()
64 class Poste(PosteWorkflow
, models
.Model
):
66 id_rh
= models
.ForeignKey(rh
.Poste
, null
=True, related_name
='+',
68 verbose_name
="Mise à jour du poste")
69 nom
= models
.CharField(verbose_name
="Titre du poste", max_length
=255)
70 implantation
= models
.ForeignKey(ref
.Implantation
)
71 type_poste
= models
.ForeignKey(rh
.TypePoste
, null
=True, related_name
='+')
72 service
= models
.ForeignKey(rh
.Service
, related_name
='+',
73 verbose_name
=u
"Direction/Service/Pôle support")
74 responsable
= models
.ForeignKey(rh
.Poste
, related_name
='+',
75 verbose_name
="Poste du responsable")
77 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
79 verbose_name
="Temps de travail",
80 help_text
="% du temps complet")
81 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
84 verbose_name
="Nb. heures par semaine")
87 local
= models
.BooleanField(verbose_name
="Local", default
=True, blank
=True)
88 expatrie
= models
.BooleanField(verbose_name
="Expatrié", default
=False, blank
=True)
91 mise_a_disposition
= models
.BooleanField(verbose_name
="Mise à disposition")
92 appel
= models
.CharField(max_length
=10, default
='interne',
93 verbose_name
="Appel à candidature",
94 choices
=POSTE_APPEL_CHOICES
)
97 classement_min
= models
.ForeignKey(rh
.Classement
, related_name
='+')
98 classement_max
= models
.ForeignKey(rh
.Classement
, related_name
='+')
100 # En fait, les coefficient n'ont pas de valeur dans ces cas, les couts sont calculés
101 # et mis dans les coûts globals
102 #coefficient_min = models.FloatField(null=True) # pour classement "hors grille"
103 #coefficient_max = models.FloatField(null=True) # pour classement "hors grille"
105 valeur_point_min
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+', blank
=True, null
=True)
106 valeur_point_max
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+', blank
=True, null
=True)
107 devise_min
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
108 devise_max
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
109 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
111 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
113 indemn_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
115 indemn_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
117 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
119 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
122 # Comparatifs de rémunération
123 devise_comparaison
= models
.ForeignKey(rh
.Devise
, related_name
='+',
125 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
126 null
=True, blank
=True)
127 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
128 null
=True, blank
=True)
129 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
130 null
=True, blank
=True)
131 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
132 null
=True, blank
=True)
133 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
134 null
=True, blank
=True)
135 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
136 null
=True, blank
=True)
137 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
138 null
=True, blank
=True)
139 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
140 null
=True, blank
=True)
141 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
142 null
=True, blank
=True)
143 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
144 null
=True, blank
=True)
147 justification
= models
.TextField()
150 date_creation
= models
.DateTimeField(auto_now_add
=True)
151 date_modification
= models
.DateTimeField(auto_now
=True)
152 date_debut
= models
.DateField(verbose_name
="Date de début",
153 help_text
="format: aaaa-mm-jj")
154 date_fin
= models
.DateField(null
=True, blank
=True,
155 verbose_name
="Date de fin",
156 help_text
="format: aaaa-mm-jj")
157 actif
= models
.BooleanField(default
=True)
160 objects
= PosteManager()
164 Les vues sont montées selon une clef spéciale pour identifier la provenance du poste.
165 Cette méthode fournit un moyen de reconstruire cette clef afin de générer les URLs.
167 return "dae-%s" % self
.id
168 key
= property(_get_key
)
170 def get_dossiers(self
):
172 Liste tous les anciens dossiers liés à ce poste.
173 (Le nom de la relation sur le rh.Poste est mal choisi poste1 au lieu de dossier1)
174 Note1 : seulement le dosssier principal fait l'objet de la recherche.
175 Note2 : les dossiers sont retournés du plus récent au plus vieux. (Ce test est fait
176 en fonction du id, car les dates de création sont absentes de rh v1).
178 if self
.id_rh
is None:
180 postes
= [p
for p
in self
.id_rh
.poste1
.all()]
181 return sorted(postes
, key
=lambda poste
: poste
.id, reverse
=True)
183 def get_complement_nom(self
):
185 Inspecte les modèles rh v1 pour trouver dans le dernier dossier un complément de titre de poste.
187 dossiers
= self
.get_dossiers()
188 if len(dossiers
) > 0:
189 nom
= dossiers
[0].complement1
194 def get_employe(self
):
196 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
198 dossiers
= self
.get_dossiers()
199 if len(dossiers
) > 0:
200 return dossiers
[0].employe
204 def get_default_devise(self
):
205 """Récupère la devise par défaut en fonction de l'implantation (EUR autrement)"""
207 implantation_devise
= rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
)[0].devise
209 implantation_devise
= 5 # EUR
210 return implantation_devise
212 #####################
213 # Classement de poste
214 #####################
216 def get_couts_minimum(self
):
217 return (float)(self
.salaire_min
+ self
.indemn_min
+ self
.autre_min
)
219 def get_taux_minimum(self
):
221 return rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
, devise
=self
.devise_min
)[0].taux
225 def get_couts_minimum_euros(self
):
226 return self
.get_couts_minimum() * self
.get_taux_minimum()
228 def get_couts_maximum(self
):
229 return (float)(self
.salaire_max
+ self
.indemn_max
+ self
.autre_max
)
231 def get_taux_maximum(self
):
233 return rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
, devise
=self
.devise_max
)[0].taux
237 def get_couts_maximum_euros(self
):
238 return self
.get_couts_maximum() * self
.get_taux_maximum()
240 ######################
241 # Comparaison de poste
242 ######################
244 def get_taux_comparaison(self
):
246 return rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
, devise
=self
.devise_comparaison
)[0].taux
250 def get_comp_universite_min_euros(self
):
251 return (float)(self
.comp_universite_min
) * self
.get_taux_comparaison()
253 def get_comp_fonctionpub_min_euros(self
):
254 return (float)(self
.comp_fonctionpub_min
) * self
.get_taux_comparaison()
256 def get_comp_locale_min_euros(self
):
257 return (float)(self
.comp_locale_min
) * self
.get_taux_comparaison()
259 def get_comp_ong_min_euros(self
):
260 return (float)(self
.comp_ong_min
) * self
.get_taux_comparaison()
262 def get_comp_autre_min_euros(self
):
263 return (float)(self
.comp_autre_min
) * self
.get_taux_comparaison()
265 def get_comp_universite_max_euros(self
):
266 return (float)(self
.comp_universite_max
) * self
.get_taux_comparaison()
268 def get_comp_fonctionpub_max_euros(self
):
269 return (float)(self
.comp_fonctionpub_max
) * self
.get_taux_comparaison()
271 def get_comp_locale_max_euros(self
):
272 return (float)(self
.comp_locale_max
) * self
.get_taux_comparaison()
274 def get_comp_ong_max_euros(self
):
275 return (float)(self
.comp_ong_max
) * self
.get_taux_comparaison()
277 def get_comp_autre_max_euros(self
):
278 return (float)(self
.comp_autre_max
) * self
.get_taux_comparaison()
280 def __unicode__(self
):
282 Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
284 complement_nom_poste
= self
.get_complement_nom()
285 if complement_nom_poste
is None:
286 complement_nom_poste
= ""
287 employe
= self
.get_employe()
295 complement_nom_poste
,
298 return u
'%s - %s (%s) [dae-%s %s %s]' % data
301 # Tester l'enregistrement car les models.py sont importés au complet
302 if not reversion
.is_registered(Poste
):
303 reversion
.register(Poste
)
306 POSTE_FINANCEMENT_CHOICES
= (
307 ('A', 'A - Frais de personnel'),
308 ('B', 'B - Projet(s)-Titre(s)'),
313 class PosteFinancement(models
.Model
):
314 poste
= models
.ForeignKey('Poste', related_name
='financements')
315 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
316 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
317 help_text
="ex.: 33.33 % (décimale avec point)")
318 commentaire
= models
.TextField(
319 help_text
="Spécifiez la source de financement.")
324 def __unicode__(self
):
325 return u
"%s %s %s" % (self
.get_type_display(), self
.pourcentage
, self
.commentaire
)
333 class Employe(models
.Model
):
336 id_rh
= models
.ForeignKey(rh
.Employe
, null
=True, related_name
='+',
337 verbose_name
='Employé')
338 nom
= models
.CharField(max_length
=255)
339 prenom
= models
.CharField(max_length
=255, verbose_name
='Prénom')
340 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
,
341 null
=True, blank
=True)
343 def __unicode__(self
):
344 return u
'%s %s' % (self
.prenom
, self
.nom
)
347 COMPTE_COMPTA_CHOICES
= (
353 class DossierPiece(models
.Model
):
354 dossier
= models
.ForeignKey("Dossier")
355 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
356 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
=dossier_piece_dispatch
, storage
=storage_prive
)
358 class Dossier(DossierWorkflow
, models
.Model
):
361 employe
= models
.ForeignKey('Employe', related_name
='+', editable
=False)
362 poste
= models
.ForeignKey('Poste', related_name
='+', editable
=False)
363 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+')
364 organisme_bstg
= models
.ForeignKey(rh
.OrganismeBstg
,
365 null
=True, blank
=True,
366 verbose_name
="Organisme",
367 help_text
="Si détaché (DET) ou mis à disposition (MAD), \
368 préciser l'organisme.",
370 organisme_bstg_autre
= models
.CharField(max_length
=255,
371 verbose_name
="Autre organisme",
372 help_text
="indiquer l'organisme ici s'il n'est pas dans la liste",
376 # Données antérieures de l'employé
377 statut_anterieur
= models
.ForeignKey(
378 rh
.Statut
, related_name
='+', null
=True, blank
=True,
379 verbose_name
='Statut antérieur')
380 classement_anterieur
= models
.ForeignKey(
381 rh
.Classement
, related_name
='+', null
=True, blank
=True,
382 verbose_name
='Classement précédent')
383 salaire_anterieur
= models
.DecimalField(
384 max_digits
=12, decimal_places
=2, null
=True, default
=None,
385 blank
=True, verbose_name
='Salaire précédent')
387 # Données du titulaire précédent
388 employe_anterieur
= models
.ForeignKey(
389 rh
.Employe
, related_name
='+', null
=True, blank
=True,
390 verbose_name
='Employé précédent')
391 statut_titulaire_anterieur
= models
.ForeignKey(
392 rh
.Statut
, related_name
='+', null
=True, blank
=True,
393 verbose_name
='Statut titulaire précédent')
394 classement_titulaire_anterieur
= models
.ForeignKey(
395 rh
.Classement
, related_name
='+', null
=True, blank
=True,
396 verbose_name
='Classement titulaire précédent')
397 salaire_titulaire_anterieur
= models
.DecimalField(
398 max_digits
=12, decimal_places
=2, default
=None, null
=True,
399 blank
=True, verbose_name
='Salaire titulaire précédent')
402 remplacement
= models
.BooleanField()
403 statut_residence
= models
.CharField(max_length
=10, default
='local',
404 verbose_name
="Statut",
405 choices
=STATUT_RESIDENCE_CHOICES
)
408 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+',
409 verbose_name
='Classement proposé')
410 salaire
= models
.DecimalField(max_digits
=12, decimal_places
=2,
411 verbose_name
='Salaire de base',
412 null
=True, default
=None)
413 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
414 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
415 verbose_name
="Régime de travail",
416 help_text
="% du temps complet")
417 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
418 decimal_places
=2, verbose_name
="Nb. heures par semaine")
421 type_contrat
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
422 contrat_date_debut
= models
.DateField(help_text
="format: aaaa-mm-jj")
423 contrat_date_fin
= models
.DateField(null
=True, blank
=True,
424 help_text
="format: aaaa-mm-jj")
427 compte_compta
= models
.CharField(max_length
=10, default
='aucun',
428 verbose_name
=u
'Compte comptabilité',
429 choices
=COMPTE_COMPTA_CHOICES
)
430 compte_courriel
= models
.BooleanField()
433 date_creation
= models
.DateTimeField(auto_now_add
=True)
435 def __unicode__(self
):
436 return u
'%s - %s' % (self
.poste
.nom
, self
.employe
)
438 def get_salaire_euros(self
):
440 tx
= rh
.TauxChange
.objects
.filter(implantation
=self
.poste
.implantation
, devise
=self
.devise
)[0].taux
443 return (float)(tx
) * (float)(self
.salaire
)
445 def get_couts_auf(self
):
447 On retire les MAD BSTG
449 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
not in (2, )]
451 def get_aides_auf(self
):
453 On récupère les MAD BSTG
455 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in (2, )]
457 # Tester l'enregistrement car les models.py sont importés au complet
458 if not reversion
.is_registered(Dossier
):
459 reversion
.register(Dossier
)
461 class Remuneration(models
.Model
):
463 dossier
= models
.ForeignKey('Dossier', db_column
='dossier')
464 type = models
.ForeignKey(rh
.TypeRemuneration
, db_column
='type',
467 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
468 # db_column='type_revalorisation')
469 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
471 devise
= models
.ForeignKey(rh
.Devise
, to_field
='code',
472 db_column
='devise', related_name
='+')
473 precision
= models
.CharField(max_length
=255, null
=True, blank
=True)
474 # date_effective = models.DateField(null=True, blank=True)
475 # pourcentage = models.IntegerField(null=True, blank=True)
478 date_creation
= models
.DateField(auto_now_add
=True)
479 user_creation
= models
.IntegerField(null
=True, blank
=True)
480 # desactivation = models.BooleanField(default=False, blank=True)
481 # date_desactivation = models.DateField(null=True, blank=True)
482 # user_desactivation = models.IntegerField(null=True, blank=True)
483 # annulation = models.BooleanField(default=False, blank=True)
484 # date_annulation = models.DateField(null=True, blank=True)
485 # user_annulation = models.IntegerField(null=True, blank=True)
487 def montant_mois(self
):
488 return round(self
.montant
/ 12, 2)
490 def taux_devise(self
):
491 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
493 def montant_euro(self
):
494 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
496 def montant_euro_mois(self
):
497 return round(self
.montant_euro() / 12, 2)
500 TYPE_JUSTIFICATIONS
= (
501 ('N', 'Nouvel employé'),
502 ('R', 'Renouvellement employé'),
505 class JustificationQuestion(models
.Model
):
506 question
= models
.CharField(max_length
=255)
507 type = models
.CharField(max_length
=255, choices
=TYPE_JUSTIFICATIONS
)
509 def __unicode__(self
,):
512 class JustificationNouvelEmploye(models
.Model
):
513 dossier
= models
.ForeignKey("Dossier")
514 question
= models
.ForeignKey("JustificationQuestion")
515 reponse
= models
.TextField()
517 class JustificationAutreEmploye(models
.Model
):
518 dossier
= models
.ForeignKey("Dossier")
519 question
= models
.ForeignKey("JustificationQuestion")
520 reponse
= models
.TextField()