1 # -=- encoding: utf-8 -=-
4 from django
.core
.files
.storage
import FileSystemStorage
5 from django
.db
import models
7 from workflow
import PosteWorkflow
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 validation_bureau_regional
= models
.BooleanField(verbose_name
="Validation bureau régional")
151 validation_bureau_regional_date
= models
.DateField(blank
=True, null
=True)
152 validation_drh
= models
.BooleanField(verbose_name
="Validation DRH")
153 validation_drh_date
= models
.DateField(blank
=True, null
=True)
154 validation_secretaire_general
= models
.BooleanField(verbose_name
="Validation secrétaire général")
155 validation_secretaire_general_date
= models
.DateField(blank
=True, null
=True)
156 validation_recteur
= models
.BooleanField(verbose_name
="Validation recteur")
157 validation_recteur_date
= models
.DateField(blank
=True, null
=True)
160 date_creation
= models
.DateTimeField(auto_now_add
=True)
161 date_modification
= models
.DateTimeField(auto_now
=True)
162 date_debut
= models
.DateField(verbose_name
="Date de début",
163 help_text
="format: aaaa-mm-jj")
164 date_fin
= models
.DateField(null
=True, blank
=True,
165 verbose_name
="Date de fin",
166 help_text
="format: aaaa-mm-jj")
167 actif
= models
.BooleanField(default
=True)
170 objects
= PosteManager()
174 Les vues sont montées selon une clef spéciale pour identifier la provenance du poste.
175 Cette méthode fournit un moyen de reconstruire cette clef afin de générer les URLs.
177 return "dae-%s" % self
.id
178 key
= property(_get_key
)
180 def get_dossiers(self
):
182 Liste tous les anciens dossiers liés à ce poste.
183 (Le nom de la relation sur le rh.Poste est mal choisi poste1 au lieu de dossier1)
184 Note1 : seulement le dosssier principal fait l'objet de la recherche.
185 Note2 : les dossiers sont retournés du plus récent au plus vieux. (Ce test est fait
186 en fonction du id, car les dates de création sont absentes de rh v1).
188 if self
.id_rh
is None:
190 postes
= [p
for p
in self
.id_rh
.poste1
.all()]
191 return sorted(postes
, key
=lambda poste
: poste
.id, reverse
=True)
193 def get_complement_nom(self
):
195 Inspecte les modèles rh v1 pour trouver dans le dernier dossier un complément de titre de poste.
197 dossiers
= self
.get_dossiers()
198 if len(dossiers
) > 0:
199 nom
= dossiers
[0].complement1
204 def get_employe(self
):
206 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
208 dossiers
= self
.get_dossiers()
209 if len(dossiers
) > 0:
210 return dossiers
[0].employe
214 def get_default_devise(self
):
215 """Récupère la devise par défaut en fonction de l'implantation (EUR autrement)"""
217 implantation_devise
= rh
.TauxChange
.objects
.filter(implantation
=self
.implantation
)[0].devise
219 implantation_devise
= 5 # EUR
220 return implantation_devise
222 def __unicode__(self
):
224 Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
226 complement_nom_poste
= self
.get_complement_nom()
227 if complement_nom_poste
is None:
228 complement_nom_poste
= ""
229 employe
= self
.get_employe()
237 complement_nom_poste
,
240 return u
'%s - %s (%s) [dae-%s %s %s]' % data
243 # Tester l'enregistrement car les models.py sont importés au complet
244 if not reversion
.is_registered(Poste
):
245 reversion
.register(Poste
)
248 POSTE_FINANCEMENT_CHOICES
= (
249 ('A', 'A - Frais de personnel'),
250 ('B', 'B - Projet(s)-Titre(s)'),
255 class PosteFinancement(models
.Model
):
256 poste
= models
.ForeignKey('Poste', related_name
='financements')
257 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
258 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
259 help_text
="ex.: 33.33 % (décimale avec point)")
260 commentaire
= models
.TextField(
261 help_text
="Spécifiez la source de financement.")
272 class Employe(models
.Model
):
275 id_rh
= models
.ForeignKey(rh
.Employe
, null
=True, related_name
='+',
276 verbose_name
='Employé')
277 nom
= models
.CharField(max_length
=255)
278 prenom
= models
.CharField(max_length
=255, verbose_name
='Prénom')
279 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
,
280 null
=True, blank
=True)
282 def __unicode__(self
):
283 return u
'%s %s' % (self
.prenom
, self
.nom
)
286 COMPTE_COMPTA_CHOICES
= (
292 class DossierPiece(models
.Model
):
293 dossier
= models
.ForeignKey("Dossier")
294 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
295 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
=dossier_piece_dispatch
, storage
=storage_prive
)
297 class Dossier(models
.Model
):
300 employe
= models
.ForeignKey('Employe', related_name
='+', editable
=False)
301 poste
= models
.ForeignKey('Poste', related_name
='+', editable
=False)
302 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+')
303 organisme_bstg
= models
.ForeignKey(rh
.OrganismeBstg
,
304 null
=True, blank
=True,
305 verbose_name
="Organisme",
306 help_text
="Si détaché (DET) ou mis à disposition (MAD), \
307 préciser l'organisme.",
309 organisme_bstg_autre
= models
.CharField(max_length
=255,
310 verbose_name
="Autre organisme",
311 help_text
="indiquer l'organisme ici s'il n'est pas dans la liste",
315 # Données antérieures de l'employé
316 statut_anterieur
= models
.ForeignKey(
317 rh
.Statut
, related_name
='+', null
=True, blank
=True,
318 verbose_name
='Statut antérieur')
319 classement_anterieur
= models
.ForeignKey(
320 rh
.Classement
, related_name
='+', null
=True, blank
=True,
321 verbose_name
='Classement précédent')
322 salaire_anterieur
= models
.DecimalField(
323 max_digits
=12, decimal_places
=2, null
=True, default
=None,
324 blank
=True, verbose_name
='Salaire précédent')
326 # Données du titulaire précédent
327 employe_anterieur
= models
.ForeignKey(
328 rh
.Employe
, related_name
='+', null
=True, blank
=True,
329 verbose_name
='Employé précédent')
330 statut_titulaire_anterieur
= models
.ForeignKey(
331 rh
.Statut
, related_name
='+', null
=True, blank
=True,
332 verbose_name
='Statut titulaire précédent')
333 classement_titulaire_anterieur
= models
.ForeignKey(
334 rh
.Classement
, related_name
='+', null
=True, blank
=True,
335 verbose_name
='Classement titulaire précédent')
336 salaire_titulaire_anterieur
= models
.DecimalField(
337 max_digits
=12, decimal_places
=2, default
=None, null
=True,
338 blank
=True, verbose_name
='Salaire titulaire précédent')
341 remplacement
= models
.BooleanField()
342 statut_residence
= models
.CharField(max_length
=10, default
='local',
343 verbose_name
="Statut",
344 choices
=STATUT_RESIDENCE_CHOICES
)
347 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+',
348 verbose_name
='Classement proposé')
349 salaire
= models
.DecimalField(max_digits
=12, decimal_places
=2,
350 verbose_name
='Salaire de base',
351 null
=True, default
=None)
352 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
353 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
354 verbose_name
="Régime de travail",
355 help_text
="% du temps complet")
356 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
357 decimal_places
=2, verbose_name
="Nb. heures par semaine")
360 type_contrat
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
361 contrat_date_debut
= models
.DateField(help_text
="format: aaaa-mm-jj")
362 contrat_date_fin
= models
.DateField(null
=True, blank
=True,
363 help_text
="format: aaaa-mm-jj")
366 compte_compta
= models
.CharField(max_length
=10, default
='aucun',
367 verbose_name
=u
'Compte comptabilité',
368 choices
=COMPTE_COMPTA_CHOICES
)
369 compte_courriel
= models
.BooleanField()
372 date_creation
= models
.DateTimeField(auto_now_add
=True)
374 def __unicode__(self
):
375 return u
'%s - %s' % (self
.poste
.nom
, self
.employe
)
377 def get_couts_auf(self
):
379 On retire les MAD BSTG
381 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
not in (2, )]
383 def get_aides_auf(self
):
385 On récupère les MAD BSTG
387 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in (2, )]
389 # Tester l'enregistrement car les models.py sont importés au complet
390 if not reversion
.is_registered(Dossier
):
391 reversion
.register(Dossier
)
393 class Remuneration(models
.Model
):
395 dossier
= models
.ForeignKey('Dossier', db_column
='dossier')
396 type = models
.ForeignKey(rh
.TypeRemuneration
, db_column
='type',
399 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
400 # db_column='type_revalorisation')
401 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
403 devise
= models
.ForeignKey(rh
.Devise
, to_field
='code',
404 db_column
='devise', related_name
='+')
405 precision
= models
.CharField(max_length
=255, null
=True, blank
=True)
406 # date_effective = models.DateField(null=True, blank=True)
407 # pourcentage = models.IntegerField(null=True, blank=True)
410 date_creation
= models
.DateField(auto_now_add
=True)
411 user_creation
= models
.IntegerField(null
=True, blank
=True)
412 # desactivation = models.BooleanField(default=False, blank=True)
413 # date_desactivation = models.DateField(null=True, blank=True)
414 # user_desactivation = models.IntegerField(null=True, blank=True)
415 # annulation = models.BooleanField(default=False, blank=True)
416 # date_annulation = models.DateField(null=True, blank=True)
417 # user_annulation = models.IntegerField(null=True, blank=True)
419 def montant_mois(self
):
420 return round(self
.montant
/ 12, 2)
422 def taux_devise(self
):
423 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
425 def montant_euro(self
):
426 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
428 def montant_euro_mois(self
):
429 return round(self
.montant_euro() / 12, 2)
432 TYPE_JUSTIFICATIONS
= (
433 ('N', 'Nouvel employé'),
434 ('R', 'Renouvellement employé'),
437 class JustificationQuestion(models
.Model
):
438 question
= models
.CharField(max_length
=255)
439 type = models
.CharField(max_length
=255, choices
=TYPE_JUSTIFICATIONS
)
441 def __unicode__(self
,):
444 class JustificationNouvelEmploye(models
.Model
):
445 dossier
= models
.ForeignKey("Dossier")
446 question
= models
.ForeignKey("JustificationQuestion")
447 reponse
= models
.TextField()
449 class JustificationAutreEmploye(models
.Model
):
450 dossier
= models
.ForeignKey("Dossier")
451 question
= models
.ForeignKey("JustificationQuestion")
452 reponse
= models
.TextField()
455 class Validation(models
.Model
):
457 date
= models
.DateField()
459 # avis = ? (CHOICES?)
462 class ValidationPoste(models
.Model
):
463 poste
= models
.ForeignKey('Poste')
466 class ValidationEmploye(models
.Model
):
467 employe
= models
.ForeignKey('Employe')