1 # -=- encoding: utf-8 -=-
4 from django
.core
.files
.storage
import FileSystemStorage
5 from django
.db
import models
7 import datamaster_modeles
.models
as ref
8 from rh_v1
import models
as rh
11 STATUT_RESIDENCE_CHOICES
= (
13 ('expat', 'Expatrié'),
16 POSTE_APPEL_CHOICES
= (
17 ('interne', 'Interne'),
18 ('externe', 'Externe'),
21 POSTE_STATUT_CHOICES
= (
22 ('MAD', 'Mise à disposition'),
23 ('DET', 'Détachement'),
27 storage_prive
= FileSystemStorage(settings
.PRIVE_MEDIA_ROOT
, base_url
=settings
.PRIVE_MEDIA_URL
)
29 def poste_piece_dispatch(instance
, filename
):
30 path
= "poste/%s/%s" % (instance
.poste
.id, filename
)
33 def dossier_piece_dispatch(instance
, filename
):
34 path
= "dossier/%s/%s" % (instance
.dossier
.id, filename
)
38 class PostePiece(models
.Model
):
39 poste
= models
.ForeignKey("Poste")
40 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
41 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
=poste_piece_dispatch
, storage
=storage_prive
)
43 class PosteManager(models
.Manager
):
45 Chargement de tous les objets FK existants sur chaque QuerySet.
47 def get_query_set(self
):
59 return super(PosteManager
, self
).get_query_set() \
60 .select_related(*fkeys
).all()
63 class Poste(models
.Model
):
65 id_rh
= models
.ForeignKey(rh
.Poste
, null
=True, related_name
='+',
67 verbose_name
="Mise à jour du poste")
68 nom
= models
.CharField(verbose_name
="Titre du poste", max_length
=255)
69 implantation
= models
.ForeignKey(ref
.Implantation
)
70 type_poste
= models
.ForeignKey(rh
.TypePoste
, null
=True, related_name
='+')
71 service
= models
.ForeignKey(rh
.Service
, related_name
='+',
72 verbose_name
=u
"Direction/Service/Pôle support")
73 responsable
= models
.ForeignKey(rh
.Poste
, related_name
='+',
74 verbose_name
="Poste du responsable")
76 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
78 verbose_name
="Temps de travail",
79 help_text
="% du temps complet")
80 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
83 verbose_name
="Nb. heures par semaine")
86 statut_residence
= models
.CharField(max_length
=10, default
='local',
87 verbose_name
="Statut",
88 choices
=STATUT_RESIDENCE_CHOICES
)
90 mise_a_disposition
= models
.BooleanField(verbose_name
="Mise à disposition")
91 appel
= models
.CharField(max_length
=10, default
='interne',
92 verbose_name
="Appel à candidature",
93 choices
=POSTE_APPEL_CHOICES
)
96 classement_min
= models
.ForeignKey(rh
.Classement
, related_name
='+')
97 classement_max
= models
.ForeignKey(rh
.Classement
, related_name
='+')
98 coefficient_min
= models
.FloatField(null
=True) # pour classement "hors grille"
99 coefficient_max
= models
.FloatField(null
=True) # pour classement "hors grille"
100 valeur_point_min
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+', blank
=True, null
=True)
101 valeur_point_max
= models
.ForeignKey(rh
.ValeurPoint
, related_name
='+', blank
=True, null
=True)
102 devise_min
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
103 devise_max
= models
.ForeignKey(rh
.Devise
, default
=5, related_name
='+')
104 salaire_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
106 salaire_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
108 indemn_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
110 indemn_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
112 autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
114 autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
117 # Comparatifs de rémunération
118 devise_comparaison
= models
.ForeignKey(rh
.Devise
, related_name
='+',
120 comp_locale_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
121 null
=True, blank
=True)
122 comp_locale_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
123 null
=True, blank
=True)
124 comp_universite_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
125 null
=True, blank
=True)
126 comp_universite_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
127 null
=True, blank
=True)
128 comp_fonctionpub_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
129 null
=True, blank
=True)
130 comp_fonctionpub_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
131 null
=True, blank
=True)
132 comp_ong_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
133 null
=True, blank
=True)
134 comp_ong_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
135 null
=True, blank
=True)
136 comp_autre_min
= models
.DecimalField(max_digits
=12, decimal_places
=2,
137 null
=True, blank
=True)
138 comp_autre_max
= models
.DecimalField(max_digits
=12, decimal_places
=2,
139 null
=True, blank
=True)
142 date_creation
= models
.DateTimeField(auto_now_add
=True)
143 date_modification
= models
.DateTimeField(auto_now
=True)
144 date_debut
= models
.DateField(verbose_name
="Date de début",
145 help_text
="format: aaaa-mm-jj")
146 date_fin
= models
.DateField(null
=True, blank
=True,
147 verbose_name
="Date de fin",
148 help_text
="format: aaaa-mm-jj")
149 actif
= models
.BooleanField(default
=True)
152 objects
= PosteManager()
156 Les vues sont montées selon une clef spéciale pour identifier la provenance du poste.
157 Cette méthode fournit un moyen de reconstruire cette clef afin de générer les URLs.
159 return "dae-%s" % self
.id
160 key
= property(_get_key
)
162 def get_dossiers(self
):
164 Liste tous les anciens dossiers liés à ce poste.
165 (Le nom de la relation sur le rh.Poste est mal choisi poste1 au lieu de dossier1)
166 Note1 : seulement le dosssier principal fait l'objet de la recherche.
167 Note2 : les dossiers sont retournés du plus récent au plus vieux. (Ce test est fait
168 en fonction du id, car les dates de création sont absentes de rh v1).
170 if self
.id_rh
is None:
172 postes
= [p
for p
in self
.id_rh
.poste1
.all()]
173 return sorted(postes
, key
=lambda poste
: poste
.id, reverse
=True)
175 def get_complement_nom(self
):
177 Inspecte les modèles rh v1 pour trouver dans le dernier dossier un complément de titre de poste.
179 dossiers
= self
.get_dossiers()
180 if len(dossiers
) > 0:
181 nom
= dossiers
[0].complement1
186 def get_employe(self
):
188 Inspecte les modèles rh v1 pour trouver l'employé du dernier dossier.
190 dossiers
= self
.get_dossiers()
191 if len(dossiers
) > 0:
192 return dossiers
[0].employe
196 def __unicode__(self
):
198 Cette fonction est consommatrice SQL car elle cherche les dossiers qui ont été liés à celui-ci.
200 complement_nom_poste
= self
.get_complement_nom()
201 if complement_nom_poste
is None:
202 complement_nom_poste
= ""
203 employe
= self
.get_employe()
211 complement_nom_poste
,
214 return u
'%s - %s (%s) [dae-%s %s %s]' % data
217 # Tester l'enregistrement car les models.py sont importés au complet
218 if not reversion
.is_registered(Poste
):
219 reversion
.register(Poste
)
222 POSTE_FINANCEMENT_CHOICES
= (
223 ('A', 'A - Frais de personnel'),
224 ('B', 'B - Projet(s)-Titre(s)'),
229 class PosteFinancement(models
.Model
):
230 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
231 help_text
="ex.: 12000.00 € (décimale avec point, devise = EUR)")
232 poste
= models
.ForeignKey('Poste', related_name
='financements')
233 type = models
.CharField(max_length
=1, choices
=POSTE_FINANCEMENT_CHOICES
)
234 pourcentage
= models
.DecimalField(max_digits
=12, decimal_places
=2,
235 help_text
="ex.: 33.33 % (décimale avec point)")
236 commentaire
= models
.TextField(
237 help_text
="Spécifiez la source de financement.")
248 class Employe(models
.Model
):
251 id_rh
= models
.ForeignKey(rh
.Employe
, null
=True, related_name
='+',
252 verbose_name
='Employé')
253 nom
= models
.CharField(max_length
=255)
254 prenom
= models
.CharField(max_length
=255, verbose_name
='Prénom')
255 genre
= models
.CharField(max_length
=1, choices
=GENRE_CHOICES
,
256 null
=True, blank
=True)
258 def __unicode__(self
):
259 return u
'%s %s' % (self
.prenom
, self
.nom
)
262 COMPTE_COMPTA_CHOICES
= (
268 class DossierPiece(models
.Model
):
269 dossier
= models
.ForeignKey("Dossier")
270 nom
= models
.CharField(verbose_name
="Nom", max_length
=255)
271 fichier
= models
.FileField(verbose_name
="Fichier", upload_to
=dossier_piece_dispatch
, storage
=storage_prive
)
273 class Dossier(models
.Model
):
276 employe
= models
.ForeignKey('Employe', related_name
='+', editable
=False)
277 poste
= models
.ForeignKey('Poste', related_name
='+', editable
=False)
278 statut
= models
.ForeignKey(rh
.Statut
, related_name
='+')
279 organisme_bstg
= models
.ForeignKey(rh
.OrganismeBstg
,
280 verbose_name
="Organisme",
281 help_text
="Si détaché (DET) ou mis à disposition (MAD), \
282 préciser l'organisme.",
285 # Données antérieures de l'employé
286 statut_anterieur
= models
.ForeignKey(
287 rh
.Statut
, related_name
='+', null
=True, blank
=True,
288 verbose_name
='Statut antérieur')
290 classement_anterieur
= models
.ForeignKey(
291 rh
.Classement
, related_name
='+', null
=True, blank
=True,
292 verbose_name
='Classement précédent')
293 salaire_anterieur
= models
.DecimalField(
294 max_digits
=12, decimal_places
=2, null
=True, default
=None,
295 blank
=True, verbose_name
='Salaire précédent')
297 # Données du titulaire précédent
298 employe_anterieur
= models
.ForeignKey(
299 rh
.Employe
, related_name
='+', null
=True, blank
=True,
300 verbose_name
='Employé précédent')
301 statut_titulaire_anterieur
= models
.ForeignKey(
302 rh
.Statut
, related_name
='+', null
=True, blank
=True,
303 verbose_name
='Statut titulaire précédent')
304 classement_titulaire_anterieur
= models
.ForeignKey(
305 rh
.Classement
, related_name
='+', null
=True, blank
=True,
306 verbose_name
='Classement titulaire précédent')
307 salaire_titulaire_anterieur
= models
.DecimalField(
308 max_digits
=12, decimal_places
=2, default
=None, null
=True,
309 blank
=True, verbose_name
='Salaire titulaire précédent')
312 remplacement
= models
.BooleanField()
313 statut_residence
= models
.CharField(max_length
=10, default
='local',
314 verbose_name
="Statut",
315 choices
=STATUT_RESIDENCE_CHOICES
)
318 classement
= models
.ForeignKey(rh
.Classement
, related_name
='+',
319 verbose_name
='Classement proposé')
320 salaire
= models
.DecimalField(max_digits
=12, decimal_places
=2,
321 verbose_name
='Salaire de base',
322 null
=True, default
=None)
323 devise
= models
.ForeignKey(rh
.Devise
, related_name
='+')
324 regime_travail
= models
.DecimalField(max_digits
=12, decimal_places
=2,
325 verbose_name
="Régime de travail",
326 help_text
="% du temps complet")
327 regime_travail_nb_heure_semaine
= models
.DecimalField(max_digits
=12,
328 decimal_places
=2, verbose_name
="Nb. heures par semaine")
331 type_contrat
= models
.ForeignKey(rh
.TypeContrat
, related_name
='+')
332 contrat_date_debut
= models
.DateField(help_text
="format: aaaa-mm-jj")
333 contrat_date_fin
= models
.DateField(help_text
="format: aaaa-mm-jj")
336 compte_compta
= models
.CharField(max_length
=10, default
='aucun',
337 verbose_name
=u
'Compte comptabilité',
338 choices
=COMPTE_COMPTA_CHOICES
)
339 compte_courriel
= models
.BooleanField()
342 class Remuneration(models
.Model
):
344 dossier
= models
.ForeignKey('Dossier', db_column
='dossier')
345 type = models
.ForeignKey(rh
.TypeRemuneration
, db_column
='type',
348 # type_revalorisation = models.ForeignKey('TypeRevalorisation',
349 # db_column='type_revalorisation')
350 montant
= models
.DecimalField(max_digits
=12, decimal_places
=2,
352 devise
= models
.ForeignKey(rh
.Devise
, to_field
='code',
353 db_column
='devise', related_name
='+')
354 precision
= models
.CharField(max_length
=255, null
=True, blank
=True)
355 # date_effective = models.DateField(null=True, blank=True)
356 # pourcentage = models.IntegerField(null=True, blank=True)
359 date_creation
= models
.DateField(auto_now_add
=True)
360 user_creation
= models
.IntegerField(null
=True, blank
=True)
361 # desactivation = models.BooleanField(default=False, blank=True)
362 # date_desactivation = models.DateField(null=True, blank=True)
363 # user_desactivation = models.IntegerField(null=True, blank=True)
364 # annulation = models.BooleanField(default=False, blank=True)
365 # date_annulation = models.DateField(null=True, blank=True)
366 # user_annulation = models.IntegerField(null=True, blank=True)
368 def montant_mois(self
):
369 return round(self
.montant
/ 12, 2)
371 def taux_devise(self
):
372 return self
.devise
.tauxchange_set
.order_by('-annee').all()[0].taux
374 def montant_euro(self
):
375 return round(float(self
.montant
) / float(self
.taux_devise()), 2)
377 def montant_euro_mois(self
):
378 return round(self
.montant_euro() / 12, 2)
381 class JustificationPoste(models
.Model
):
385 class JustificationEmploye(models
.Model
):
389 class DocumentPoste(models
.Model
):
393 class DocumentEmploye(models
.Model
):
397 class Validation(models
.Model
):
399 date
= models
.DateField()
401 # avis = ? (CHOICES?)
404 class ValidationPoste(models
.Model
):
405 poste
= models
.ForeignKey('Poste')
408 class ValidationEmploye(models
.Model
):
409 employe
= models
.ForeignKey('Employe')