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 def __unicode__(self
):
214 Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
216 complement_nom_poste
= self
.get_complement_nom()
217 if complement_nom_poste
is None:
218 complement_nom_poste
= ""
219 employe
= self
.get_employe()
227 complement_nom_poste
,
230 return u
'%s - %s (%s) [dae-%s %s %s]' % data
233 # Tester l'enregistrement car les models.py sont importés au complet
234 if not reversion
.is_registered(Poste
):
235 reversion
.register(Poste
)
238 POSTE_FINANCEMENT_CHOICES
= (
239 ('A', 'A - Frais de personnel'),
240 ('B', 'B - Projet(s)-Titre(s)'),
245 class PosteFinancement(models
.Model
):
246 poste
= models
.ForeignKey('Poste', related_name
='financements')
247 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
248 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
249 help_text
="ex.: 33.33 % (décimale avec point)")
250 commentaire
= models
.TextField(
251 help_text
="Spécifiez la source de financement.")
262 class Employe(models
.Model
):
265 id_rh
= models
.ForeignKey(rh
.Employe
, null
=True, related_name
='+',
266 verbose_name
='Employé')
267 nom
= models
.CharField(max_length
=255)
268 prenom
= models
.CharField(max_length
=255, verbose_name
='Prénom')
269 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
,
270 null
=True, blank
=True)
272 def __unicode__(self
):
273 return u
'%s %s' % (self
.prenom
, self
.nom
)
276 COMPTE_COMPTA_CHOICES
= (
282 class DossierPiece(models
.Model
):
283 dossier
= models
.ForeignKey("Dossier")
284 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
285 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
=dossier_piece_dispatch
, storage
=storage_prive
)
287 class Dossier(DossierWorkflow
, models
.Model
):
290 employe
= models
.ForeignKey('Employe', related_name
='+', editable
=False)
291 poste
= models
.ForeignKey('Poste', related_name
='+', editable
=False)
292 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+')
293 organisme_bstg
= models
.ForeignKey(rh
.OrganismeBstg
,
294 null
=True, blank
=True,
295 verbose_name
="Organisme",
296 help_text
="Si détaché (DET) ou mis à disposition (MAD), \
297 préciser l'organisme.",
299 organisme_bstg_autre
= models
.CharField(max_length
=255,
300 verbose_name
="Autre organisme",
301 help_text
="indiquer l'organisme ici s'il n'est pas dans la liste",
305 # Données antérieures de l'employé
306 statut_anterieur
= models
.ForeignKey(
307 rh
.Statut
, related_name
='+', null
=True, blank
=True,
308 verbose_name
='Statut antérieur')
309 classement_anterieur
= models
.ForeignKey(
310 rh
.Classement
, related_name
='+', null
=True, blank
=True,
311 verbose_name
='Classement précédent')
312 salaire_anterieur
= models
.DecimalField(
313 max_digits
=12, decimal_places
=2, null
=True, default
=None,
314 blank
=True, verbose_name
='Salaire précédent')
316 # Données du titulaire précédent
317 employe_anterieur
= models
.ForeignKey(
318 rh
.Employe
, related_name
='+', null
=True, blank
=True,
319 verbose_name
='Employé précédent')
320 statut_titulaire_anterieur
= models
.ForeignKey(
321 rh
.Statut
, related_name
='+', null
=True, blank
=True,
322 verbose_name
='Statut titulaire précédent')
323 classement_titulaire_anterieur
= models
.ForeignKey(
324 rh
.Classement
, related_name
='+', null
=True, blank
=True,
325 verbose_name
='Classement titulaire précédent')
326 salaire_titulaire_anterieur
= models
.DecimalField(
327 max_digits
=12, decimal_places
=2, default
=None, null
=True,
328 blank
=True, verbose_name
='Salaire titulaire précédent')
331 remplacement
= models
.BooleanField()
332 statut_residence
= models
.CharField(max_length
=10, default
='local',
333 verbose_name
="Statut",
334 choices
=STATUT_RESIDENCE_CHOICES
)
337 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+',
338 verbose_name
='Classement proposé')
339 salaire
= models
.DecimalField(max_digits
=12, decimal_places
=2,
340 verbose_name
='Salaire de base',
341 null
=True, default
=None)
342 devise
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
343 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
344 verbose_name
="Régime de travail",
345 help_text
="% du temps complet")
346 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
347 decimal_places
=2, verbose_name
="Nb. heures par semaine")
350 type_contrat
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
351 contrat_date_debut
= models
.DateField(help_text
="format: aaaa-mm-jj")
352 contrat_date_fin
= models
.DateField(null
=True, blank
=True,
353 help_text
="format: aaaa-mm-jj")
356 compte_compta
= models
.CharField(max_length
=10, default
='aucun',
357 verbose_name
=u
'Compte comptabilité',
358 choices
=COMPTE_COMPTA_CHOICES
)
359 compte_courriel
= models
.BooleanField()
362 date_creation
= models
.DateTimeField(auto_now_add
=True)
364 def __unicode__(self
):
365 return u
'%s - %s' % (self
.poste
.nom
, self
.employe
)
367 def get_couts_auf(self
):
369 On retire les MAD BSTG
371 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
not in (2, )]
373 def get_aides_auf(self
):
375 On récupère les MAD BSTG
377 return [r
for r
in self
.remuneration_set
.all() if r
.type_id
in (2, )]
379 # Tester l'enregistrement car les models.py sont importés au complet
380 if not reversion
.is_registered(Dossier
):
381 reversion
.register(Dossier
)
383 class Remuneration(models
.Model
):
385 dossier
= models
.ForeignKey('Dossier', db_column
='dossier')
386 type = models
.ForeignKey(rh
.TypeRemuneration
, db_column
='type',
389 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
390 # db_column='type_revalorisation')
391 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
393 devise
= models
.ForeignKey(rh
.Devise
, to_field
='code',
394 db_column
='devise', related_name
='+')
395 precision
= models
.CharField(max_length
=255, null
=True, blank
=True)
396 # date_effective = models.DateField(null=True, blank=True)
397 # pourcentage = models.IntegerField(null=True, blank=True)
400 date_creation
= models
.DateField(auto_now_add
=True)
401 user_creation
= models
.IntegerField(null
=True, blank
=True)
402 # desactivation = models.BooleanField(default=False, blank=True)
403 # date_desactivation = models.DateField(null=True, blank=True)
404 # user_desactivation = models.IntegerField(null=True, blank=True)
405 # annulation = models.BooleanField(default=False, blank=True)
406 # date_annulation = models.DateField(null=True, blank=True)
407 # user_annulation = models.IntegerField(null=True, blank=True)
409 def montant_mois(self
):
410 return round(self
.montant
/ 12, 2)
412 def taux_devise(self
):
413 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
415 def montant_euro(self
):
416 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
418 def montant_euro_mois(self
):
419 return round(self
.montant_euro() / 12, 2)
422 TYPE_JUSTIFICATIONS
= (
423 ('N', 'Nouvel employé'),
424 ('R', 'Renouvellement employé'),
427 class JustificationQuestion(models
.Model
):
428 question
= models
.CharField(max_length
=255)
429 type = models
.CharField(max_length
=255, choices
=TYPE_JUSTIFICATIONS
)
431 def __unicode__(self
,):
434 class JustificationNouvelEmploye(models
.Model
):
435 dossier
= models
.ForeignKey("Dossier")
436 question
= models
.ForeignKey("JustificationQuestion")
437 reponse
= models
.TextField()
439 class JustificationAutreEmploye(models
.Model
):
440 dossier
= models
.ForeignKey("Dossier")
441 question
= models
.ForeignKey("JustificationQuestion")
442 reponse
= models
.TextField()