Done
[auf_rh_dae.git] / project / rh / views.py
CommitLineData
e9bbd6ba 1# -*- encoding: utf-8 -*-
22343fe7 2
fc62be5d 3from collections import defaultdict
c9122f1d 4from datetime import date
fc62be5d 5from decimal import Decimal
9b0a8554 6
9f55f18a 7import pygraphviz as pgv
edbc9e37 8from reversion.models import Revision
f80b83c6 9
d104b0ae 10from auf.django.references import models as ref
4ba84959 11from django.conf import settings
e90b19fd 12from django.contrib.auth.decorators import login_required
dcd1b959 13from django.core.urlresolvers import reverse
fbe60a9d 14from django.db.models import Q
4ba84959 15from django.http import HttpResponse
edbc9e37 16from django.shortcuts import render, get_object_or_404, redirect
8e26a99b 17
018c8eaf 18from project.decorators import drh_or_admin_required
ae99002a 19from project.decorators import region_protected
afd3be54 20from project.groups import get_employe_from_user
c3550a05
OL
21from project import groups
22from project.decorators import in_one_of_group, in_drh_or_admin
e9bbd6ba 23
fc62be5d 24from project.rh import ods
4ba84959 25from project.rh import graph as rh_graph
fc62be5d 26from project.rh import models as rh
890b80e7 27from project.rh.change_list import RechercheTemporelle
fc62be5d 28from project.rh.forms import MasseSalarialeForm
d104b0ae 29from project.rh.lib import get_lookup_params
890b80e7 30from project.rh.templatetags.rapports import SortHeaders
edbc9e37 31from project.rh.historique import get_active_revisions, TodoForm
9817fed5 32
fc62be5d
EMS
33TWOPLACES = Decimal('0.01')
34
22343fe7 35
6219cf26
BS
36def devises():
37 liste = []
38 for d in rh.Devise.objects.all():
39 annee = date.today().year
40 taux = rh.TauxChange.objects.filter(annee=annee, devise=d)
41 data = {}
42 if len(taux) == 0:
43 data['taux_euro'] = 0
44 else:
45 data['taux_euro'] = taux[0].taux
46 data['devise_code'] = d.id
47 liste.append(data)
48 return liste
49
50
c9122f1d 51@login_required
d9a82745 52@drh_or_admin_required
c9122f1d 53def profil(request):
54 """Profil personnel de l'employé - éditable"""
d104b0ae 55 employe = get_employe_from_user(request.user)
7d0c2206 56 c = {
8b3f6ff5
DB
57 'user': request.user,
58 'employe': employe,
7d0c2206 59 }
890b80e7 60 return render(request, 'rh/profil.html', c)
22343fe7
OL
61
62
3411ac33 63@login_required
d9a82745 64@drh_or_admin_required
a9faef67 65def employes_liste(request):
66 """Liste des employés."""
5ea6b5bb 67 today = date.today()
68 employes = rh.Employe.objects \
5ea6b5bb 69 .exclude(dossiers__date_debut__gt=today) \
70 .exclude(dossiers__date_fin__lt=today) \
71 .order_by('nom')
a9faef67 72 c = {
22343fe7
OL
73 'user': request.user,
74 'employes': employes,
a9faef67 75 }
890b80e7 76 return render(request, 'rh/employes_liste.html', c)
22343fe7
OL
77
78
3411ac33 79@login_required
d9a82745 80@drh_or_admin_required
a9faef67 81def employe(request, id):
82 """Information publique sur un employé."""
83 try:
84 employe = rh.Employe.objects.get(pk=id)
85 except:
86 employe = rh.Employe.objects.none()
87 c = {
22343fe7
OL
88 'user': request.user,
89 'employe': employe,
a9faef67 90 }
890b80e7 91 return render(request, 'rh/employe.html', c)
63e17dff
PP
92
93
08faf06e 94@login_required
3383b2d1
OL
95@in_one_of_group((groups.CORRESPONDANT_RH,
96 groups.ADMINISTRATEURS,
97 groups.DIRECTEUR_DE_BUREAU,
98 groups.DRH_NIVEAU_1,
99 groups.DRH_NIVEAU_2))
f2d65e83 100def rapports_contrat(request):
dd18f6e3 101 # statut default = actif?
dcd1b959 102 if 'HTTP_REFERER' in request.META.keys():
58348377
EMS
103 referer = request.META['HTTP_REFERER']
104 referer = "/".join(referer.split('/')[3:])
105 referer = "/%s" % referer.split('?')[0]
106 if referer != reverse('rhr_contrats'):
107 params = request.GET.copy()
108 params.update({'statut': 'Actif'})
109 request.GET = params
f2d65e83 110
00ca4d9f
EMS
111 contrats = rh.Contrat.objects.select_related(
112 'dossier', 'dossier__poste', 'dossier__poste__implantation',
113 'type_contrat', 'dossier__employe'
114 )
22343fe7 115
dd18f6e3 116 # filters et recherche temporelle
6bee05ff 117 cl = RechercheTemporelle(dict(request.GET.items()), rh.Contrat)
dd18f6e3 118 lookup_params = get_lookup_params(request)
f0f6b03e 119 lookup_params = cl.purge_params(lookup_params)
dcd1b959
OL
120 q_temporel = cl.get_q_temporel(contrats)
121 q = Q(**lookup_params) & q_temporel
45066657 122 contrats = contrats.filter(q)
3383b2d1
OL
123 user_groups = [g.name for g in request.user.groups.all()]
124 if groups.CORRESPONDANT_RH in user_groups or\
125 groups.ADMINISTRATEURS in user_groups or\
126 groups.DIRECTEUR_DE_BUREAU in user_groups:
f96ec25c 127 employe = get_employe_from_user(request.user)
b0cf30b8
EMS
128 q = q & Q(
129 dossier__poste__implantation__zone_administrative=(
130 employe.implantation.zone_administrative
131 )
132 )
f96ec25c 133
c3550a05 134 contrats = contrats.filter(q)
f0f6b03e 135
dd18f6e3 136 # tri
65e02621 137 if 'o' in request.GET:
00ca4d9f
EMS
138 contrats = contrats.order_by(
139 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
140 )
f2d65e83 141
dd18f6e3 142 # affichage
8ee135b7 143 employes = set([c.dossier.employe_id for c in contrats])
65e02621 144 headers = [
dd18f6e3 145 ("dossier__employe__id", u"#"),
d50a5e5d 146 ("dossier__employe__nom", u"Employé"),
dd18f6e3
DB
147 ("dossier", u"Dossier : Poste"),
148 ("type_contrat__nom", u"Contrat"),
149 ("date_debut", u"Début contrat"),
150 ("date_fin", u"Fin contrat"),
151 ("dossier__statut_residence", u"Statut"),
b0cf30b8 152 ("dossier__poste__implantation__zone_administrative", u"Zone administrative"),
d50a5e5d 153 ("dossier__poste__implantation", u"Implantation"),
65e02621 154 ]
dd18f6e3
DB
155 h = SortHeaders(
156 request, headers, order_field_type="ot", order_field="o",
157 not_sortable=('dossier',)
158 )
65e02621 159
f2d65e83 160 c = {
00ca4d9f 161 'cl': cl,
f2d65e83
PP
162 'title': 'Rapport des contrats',
163 'contrats': contrats,
00ca4d9f 164 'count_employe': len(employes),
65e02621 165 'headers': list(h.headers()),
f2d65e83 166 }
890b80e7 167 return render(request, 'rh/rapports/contrats.html', c)
e2c0b1ac
PP
168
169
02c1b3dc 170@login_required
3383b2d1
OL
171@in_one_of_group((groups.CORRESPONDANT_RH,
172 groups.ADMINISTRATEURS,
173 groups.DIRECTEUR_DE_BUREAU,
174 groups.DRH_NIVEAU_1,
175 groups.DRH_NIVEAU_2))
6b7cf654 176def rapports_employes_sans_contrat(request):
5db1c5a3
DB
177 """
178 Employé sans contrat = a un Dossier qui n'a pas de Contrat associé
179 Employé avec contrat échu = a un Dossier avec un Contrat se terminant hier
180 au plus tard... (aujourd'hui = ok, pas date de fin = illimité donc ok)
181 """
1a7b9e36 182 lookup_params = get_lookup_params(request)
c8b22fd1 183
82c5e37d 184 # régionalisation
3383b2d1 185 user_groups = [g.name for g in request.user.groups.all()]
3383b2d1
OL
186 if groups.CORRESPONDANT_RH in user_groups or\
187 groups.ADMINISTRATEURS in user_groups or\
188 groups.DIRECTEUR_DE_BUREAU in user_groups:
219ae355 189 employe = get_employe_from_user(request.user)
b0cf30b8
EMS
190 lookup_params['poste__implantation__zone_administrative'] = \
191 employe.implantation.zone_administrative
219ae355 192
a20fa2a4 193 dossiers = rh.Dossier.objects.sans_contrats_ou_echus(**lookup_params)
a20fa2a4
OL
194 dossiers_ids = [d.id for d in dossiers]
195 employes = rh.Employe.objects.filter(id__in=dossiers_ids).distinct().count()
a623d1b2 196
5db1c5a3 197 # tri
c8b22fd1 198 if 'o' in request.GET:
5db1c5a3 199 dossiers = dossiers.order_by(
00ca4d9f
EMS
200 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
201 )
c8b22fd1 202
6b7cf654 203 # affichage
c8b22fd1 204 headers = [
5db1c5a3
DB
205 ("employe__id", u"#"),
206 ("employe__nom", u"Employé"),
1a7b9e36 207 ("dossier", u"Dossier : Poste"),
5db1c5a3
DB
208 ("rh_contrats__type_contrat", u"Contrat"),
209 ("rh_contrats__date_debut", u"Début contrat"),
210 ("rh_contrats__date_fin", u"Fin contrat"),
211 ("statut_residence", u"Statut"),
b0cf30b8 212 ("poste__implantation__zone_administrative__code", u"Zone administrative"),
5db1c5a3 213 ("poste__implantation__nom", u"Implantation"),
c8b22fd1 214 ]
00ca4d9f
EMS
215 h = SortHeaders(
216 request, headers, order_field_type="ot", order_field="o",
5db1c5a3 217 not_sortable=('dossier',)
00ca4d9f 218 )
c8b22fd1
JPC
219
220 c = {
5db1c5a3 221 'title': u'Rapport des employés sans contrat ou contrat échu',
c8b22fd1 222 'employes': employes,
5db1c5a3 223 'dossiers': dossiers,
c8b22fd1
JPC
224 'headers': list(h.headers()),
225 }
226
890b80e7 227 return render(request, 'rh/rapports/employes_sans_contrat.html', c)
00ca4d9f 228
c8b22fd1
JPC
229
230@login_required
3383b2d1
OL
231@in_one_of_group((groups.CORRESPONDANT_RH,
232 groups.ADMINISTRATEURS,
233 groups.DIRECTEUR_DE_BUREAU,
234 groups.DRH_NIVEAU_1,
235 groups.DRH_NIVEAU_2))
98d6eb6c 236def rapports_masse_salariale(request):
fc62be5d
EMS
237 form = MasseSalarialeForm(request.user, request.GET)
238 if 'annee' in request.GET and form.is_valid():
b0cf30b8 239 zone_administrative = form.cleaned_data['zone_administrative']
fc62be5d
EMS
240 implantation = form.cleaned_data['implantation']
241 annee = form.cleaned_data['annee']
242 debut_annee = date(annee, 1, 1)
243 fin_annee = date(annee, 12, 31)
244 jours_annee = (fin_annee - debut_annee).days + 1
245 today = date.today()
246
247 # Récupérer les dossiers actifs
248 dossiers = rh.Dossier.objects \
249 .actifs(annee=annee) \
250 .select_related(
251 'poste', 'poste__implantation',
b0cf30b8 252 'poste__implantation__zone_administrative',
fc62be5d
EMS
253 'poste__implantation__adresse_physique_pays',
254 'employe', 'poste__type_poste', 'classement',
255 'statut', 'organisme_bstg'
256 ) \
257 .extra(
fc62be5d 258 select={
471fd30f
EMS
259 'valeur_point': (
260 'SELECT valeur FROM rh_valeurpoint '
261 'WHERE annee = %d '
262 'AND implantation = ref_implantation.id' % annee
263 ),
264 'valeur_point_devise': (
265 'SELECT d.code '
266 'FROM rh_valeurpoint vp '
267 'INNER JOIN rh_devise d ON d.id = vp.devise '
268 'WHERE annee = %d '
269 'AND implantation = ref_implantation.id' % annee
270 )
fc62be5d 271 }
48c0abfe 272 )
b0cf30b8
EMS
273 if zone_administrative:
274 dossiers = dossiers.filter(poste__implantation__zone_administrative=zone_administrative)
fc62be5d
EMS
275 if implantation:
276 dossiers = dossiers.filter(poste__implantation=implantation)
277
278 # Récupérer les rémunérations actives
a6ed66f9 279 remun_actives = rh.Remuneration.objects \
fc62be5d 280 .actifs(annee=annee) \
a6ed66f9
OL
281 .select_related('devise', 'type')
282
283 remuns = remun_actives.extra(
fc62be5d
EMS
284 tables=['rh_tauxchange'],
285 where=[
e92cf771
EMS
286 'rh_tauxchange.devise = rh_devise.id',
287 'rh_tauxchange.annee = '
288 '(SELECT MAX(annee) FROM rh_tauxchange '
289 'WHERE devise = rh_devise.id '
290 'AND annee <= %s)',
fc62be5d
EMS
291 ],
292 params=[annee],
293 select={
294 'taux_change': 'rh_tauxchange.taux'
295 }
296 )
a6ed66f9
OL
297
298 if len(remun_actives) != len(remuns):
299 raise rh.RemunIntegrityException("Toutes les remunerations ne disposent pas d'un "
300 "taux de change pour l'année %d" % annee)
301
b0cf30b8
EMS
302 if zone_administrative:
303 remuns = remuns.filter(dossier__poste__implantation__zone_administrative=zone_administrative)
471fd30f
EMS
304 if implantation:
305 remuns = remuns.filter(dossier__poste__implantation=implantation)
fc62be5d 306 remuns_par_dossier = defaultdict(list)
471fd30f 307 types_remun = set()
fc62be5d 308 for remun in remuns:
471fd30f 309 types_remun.add(remun.type_id)
fc62be5d
EMS
310 remuns_par_dossier[remun.dossier_id].append(remun)
311
312 # Récupérer les types de rémunération par nature
313 types_remun_par_nature = defaultdict(list)
314 for type in rh.TypeRemuneration.objects.all():
471fd30f
EMS
315 if type.id in types_remun:
316 types_remun_par_nature[type.nature_remuneration].append(type)
fc62be5d
EMS
317 titres_traitements = [
318 t.nom for t in types_remun_par_nature[u'Traitement']
319 ]
320 titres_indemnites = [
321 t.nom for t in types_remun_par_nature[u'Indemnité']
322 ]
323 titres_primes = [
324 t.nom for t in types_remun_par_nature[u'Accessoire']
325 ]
326 titres_charges = [
327 t.nom for t in types_remun_par_nature[u'Charges']
328 ]
df37184c 329
fc62be5d
EMS
330 # Boucler sur les dossiers et préprarer les lignes du rapport
331 lignes = []
332 masse_salariale_totale = 0
333 for dossier in dossiers:
334 debut_cette_annee = \
335 max(dossier.date_debut or debut_annee, debut_annee)
336 fin_cette_annee = \
337 min(dossier.date_fin or fin_annee, fin_annee)
338 jours = (fin_cette_annee - debut_cette_annee).days + 1
339
340 remuns = remuns_par_dossier[dossier.id]
341 devises = set(remun.devise.code for remun in remuns)
342 if len(devises) == 1:
343 devise = remuns[0].devise.code
344 montant_remun = lambda r: r.montant
345 taux_change = Decimal(str(remuns[0].taux_change))
346 else:
347 devise = 'EUR'
348 montant_remun = lambda r: (
349 r.montant * Decimal(str(r.taux_change))
48c0abfe 350 )
fc62be5d
EMS
351 taux_change = Decimal(1)
352
353 remuns_par_type = defaultdict(lambda: 0)
354 for remun in remuns:
ff9bc43d
EMS
355 if remun.type.nature_remuneration == u'Accessoire':
356 remuns_par_type[remun.type_id] += montant_remun(remun)
357 else:
358 remuns_par_type[remun.type_id] += (
359 montant_remun(remun) * ((
360 min(remun.date_fin or fin_annee, fin_annee) -
361 max(remun.date_debut or debut_annee, debut_annee)
a81bde86
EMS
362 ).days + 1) / jours_annee *
363 dossier.regime_travail / 100
ff9bc43d 364 ).quantize(TWOPLACES)
fc62be5d
EMS
365 traitements = [
366 remuns_par_type[type.id]
367 for type in types_remun_par_nature[u'Traitement']
368 ]
369 indemnites = [
370 remuns_par_type[type.id]
371 for type in types_remun_par_nature[u'Indemnité']
372 ]
373 primes = [
374 remuns_par_type[type.id]
375 for type in types_remun_par_nature[u'Accessoire']
376 ]
377 charges = [
378 remuns_par_type[type.id]
379 for type in types_remun_par_nature[u'Charges']
380 ]
381 masse_salariale = sum(remuns_par_type.values())
382 masse_salariale_eur = (
383 masse_salariale * taux_change
384 ).quantize(TWOPLACES)
385 masse_salariale_totale += masse_salariale_eur
386
387 if dossier.valeur_point and dossier.classement \
388 and dossier.classement.coefficient and dossier.regime_travail:
389 salaire_theorique = (Decimal(str(
390 dossier.valeur_point * dossier.classement.coefficient
391 )) * dossier.regime_travail / 100).quantize(TWOPLACES)
392 else:
393 salaire_theorique = None
394
395 lignes.append({
396 'dossier': dossier,
397 'poste': dossier.poste,
398 'date_debut': (
399 dossier.date_debut
400 if dossier.date_debut and dossier.date_debut.year == annee
401 else None
402 ),
403 'date_fin': (
404 dossier.date_fin
405 if dossier.date_fin and dossier.date_fin.year == annee
406 else None
407 ),
408 'jours': jours,
409 'devise': devise,
410 'valeur_point': dossier.valeur_point,
411 'valeur_point_devise': dossier.valeur_point_devise,
412 'regime_travail': dossier.regime_travail,
413 'local_expatrie': {
414 'local': 'L', 'expat': 'E'
415 }.get(dossier.statut_residence),
416 'salaire_bstg': remuns_par_type[2],
417 'salaire_bstg_eur': (
418 (remuns_par_type[2] * taux_change).quantize(TWOPLACES)
419 ),
420 'salaire_theorique': salaire_theorique,
421 'traitements': traitements,
422 'total_traitements': sum(traitements),
423 'indemnites': indemnites,
424 'total_indemnites': sum(indemnites),
425 'primes': primes,
426 'total_primes': sum(primes),
427 'charges': charges,
428 'total_charges': sum(charges),
429 'masse_salariale': masse_salariale,
430 'masse_salariale_eur': masse_salariale_eur,
431 })
00ca4d9f 432
fc62be5d
EMS
433 # Récupérer les postes actifs pour déterminer le nombre de jours
434 # vacants.
50e2340a
EMS
435 postes = rh.Poste.objects.actifs(annee=annee) \
436 .select_related(
437 'devise_max', 'valeur_point_max', 'classement_max'
438 ) \
439 .extra(
440 tables=['rh_tauxchange'],
441 where=[
442 'rh_tauxchange.annee = %s',
443 'rh_tauxchange.devise = rh_devise.id'
444 ],
445 params=[annee],
446 select={
447 'taux_change': 'rh_tauxchange.taux'
448 }
449 )
b0cf30b8
EMS
450 if zone_administrative:
451 postes = postes.filter(implantation__zone_administrative=zone_administrative)
50e2340a
EMS
452 if implantation:
453 postes = postes.filter(implantation=implantation)
454 postes = list(postes)
fc62be5d
EMS
455 postes_par_id = dict((poste.id, poste) for poste in postes)
456 jours_vacants_date = dict(
457 (poste.id, max(today, poste.date_debut or today))
458 for poste in postes
459 )
460 jours_vacants = defaultdict(lambda: 0)
461 for dossier in rh.Dossier.objects.actifs(annee=annee) \
462 .order_by('date_debut'):
463 if dossier.poste_id not in jours_vacants_date:
464 continue
465 derniere_date = jours_vacants_date[dossier.poste_id]
466 if dossier.date_debut is not None:
467 jours_vacants[dossier.poste_id] += max((
468 dossier.date_debut - derniere_date
469 ).days - 1, 0)
470 jours_vacants_date[dossier.poste_id] = max(
471 min(dossier.date_fin or fin_annee, fin_annee),
472 derniere_date
48c0abfe 473 )
24b5b57f
EMS
474 for poste_id, derniere_date in jours_vacants_date.iteritems():
475 jours_vacants[poste_id] += max((
476 min(postes_par_id[poste_id].date_fin or fin_annee, fin_annee) -
477 derniere_date
478 ).days, 0)
48c0abfe 479
fc62be5d
EMS
480 # Ajouter les lignes des postes vacants au rapport
481 for poste_id, jours in jours_vacants.iteritems():
482 if jours == 0:
483 continue
484 poste = postes_par_id[poste_id]
485 if poste.valeur_point_max and poste.classement_max \
486 and poste.classement_max.coefficient and poste.regime_travail:
487 salaire_theorique = (Decimal(str(
488 poste.valeur_point_max.valeur *
489 poste.classement_max.coefficient
490 )) * poste.regime_travail / 100).quantize(TWOPLACES)
491 else:
492 salaire_theorique = None
df37184c 493
fc62be5d
EMS
494 local_expatrie = '/'.join(
495 (['L'] if poste.local else []) +
496 (['E'] if poste.expatrie else [])
497 )
df37184c 498
fc62be5d
EMS
499 salaire = (
500 poste.salaire_max * jours / jours_annee *
501 poste.regime_travail / 100
502 ).quantize(TWOPLACES)
503 indemnites = (
504 poste.indemn_max * jours / jours_annee *
505 poste.regime_travail / 100
506 ).quantize(TWOPLACES)
507 charges = (
508 poste.autre_max * jours / jours_annee *
509 poste.regime_travail / 100
510 ).quantize(TWOPLACES)
511 masse_salariale = salaire + indemnites + charges
512 masse_salariale_eur = (
513 masse_salariale * Decimal(str(poste.taux_change))
514 ).quantize(TWOPLACES)
515 masse_salariale_totale += masse_salariale_eur
516
517 lignes.append({
518 'poste': poste,
519 'regime_travail': poste.regime_travail,
520 'local_expatrie': local_expatrie,
521 'jours': jours,
522 'devise': poste.devise_max and poste.devise_max.code,
523 'valeur_point': (
524 poste.valeur_point_max and poste.valeur_point_max.valeur
525 ),
526 'valeur_point_devise': (
527 poste.valeur_point_max and \
528 poste.valeur_point_max.devise.code
529 ),
530 'salaire_theorique': salaire_theorique,
24b5b57f
EMS
531 'traitements': [0] * len(titres_traitements),
532 'total_traitements': salaire,
533 'indemnites': [0] * len(titres_indemnites),
fc62be5d 534 'total_indemnites': indemnites,
24b5b57f
EMS
535 'primes': [0] * len(titres_primes),
536 'total_primes': 0,
537 'charges': [0] * len(titres_charges),
fc62be5d
EMS
538 'total_charges': charges,
539 'masse_salariale': masse_salariale,
540 'masse_salariale_eur': masse_salariale_eur
541 })
542 if 'ods' in request.GET:
543 doc = ods.masse_salariale(
544 lignes=lignes,
545 annee=annee,
546 titres_traitements=titres_traitements,
547 titres_indemnites=titres_indemnites,
548 titres_primes=titres_primes,
549 titres_charges=titres_charges,
550 masse_salariale_totale=masse_salariale_totale
551 )
552 response = HttpResponse(
553 mimetype='vnd.oasis.opendocument.spreadsheet'
554 )
555 response['Content-Disposition'] = 'filename=masse-salariale.ods'
556 doc.write(response)
557 return response
558 else:
559 return render(request, 'rh/rapports/masse_salariale.html', {
560 'form': form,
561 'titres_traitements': titres_traitements,
562 'titres_indemnites': titres_indemnites,
563 'titres_primes': titres_primes,
564 'titres_charges': titres_charges,
565 'masse_salariale_totale': masse_salariale_totale,
566 'lignes': lignes,
567 'annee': annee
568 })
569 return render(request, 'rh/rapports/masse_salariale.html', {
570 'form': form
571 })
98d6eb6c
JPC
572
573
574@login_required
575@drh_or_admin_required
857b5c24
JPC
576def rapports_postes_modelisation(request):
577 c = {}
578 data = []
3c1ba807 579
7bf28694
EMS
580 for categorie in rh.CategorieEmploi.objects.all():
581 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
3c1ba807
JPC
582 data_types = []
583 for t in types.all():
584 postes = rh.Poste.objects.filter(type_poste=t)
585 data_types.append({
586 'num_postes': postes.count(),
587 'postes': postes.all(),
7bf28694 588 'type': categorie,
857b5c24 589 })
3c1ba807 590
857b5c24 591 data.append({
7bf28694 592 'categorie': categorie,
3c1ba807 593 'nb_types': types.count(),
00ca4d9f
EMS
594 'types': data_types
595 })
857b5c24
JPC
596
597 c['data'] = data
598
890b80e7 599 return render(request, 'rh/rapports/postes_modelisation.html', c)
857b5c24
JPC
600
601
602@login_required
603@drh_or_admin_required
783e077a
JPC
604def rapports_postes_implantation(request):
605 c = {}
606 data = []
607 for r in ref.Region.objects.all():
608 implantations = []
609 for i in ref.Implantation.objects.filter(region=r):
610 implantations.append({
611 'implantation': i,
612 'postes': rh.Poste.objects.filter(implantation=i),
613 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
614 })
615 data.append({
616 'region': r,
617 'implantations': implantations
618 })
619
620 c['data'] = data
621
890b80e7 622 return render(request, 'rh/rapports/postes_implantation.html', c)
783e077a
JPC
623
624
625@login_required
626@drh_or_admin_required
627def rapports_postes_service(request):
628 c = {}
9988aafb
JPC
629 data = []
630 for s in rh.Service.objects.all():
631 postes = rh.Poste.objects.filter(service=s).all()
632 num_postes = rh.Poste.objects.filter(service=s).count()
22343fe7 633 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
783e077a 634
9988aafb 635 c['data'] = data
890b80e7 636 return render(request, 'rh/rapports/postes_service.html', c)
32373f2e 637
d104b0ae 638
32373f2e 639@region_protected(rh.Dossier)
3ebc0952 640def dossier_apercu(request, dossier_id):
baf9b78c 641 d = get_object_or_404(rh.Dossier, pk=dossier_id)
6219cf26
BS
642 annees = [
643 x.year for x in d.remunerations().values_list(
644 'date_debut', flat=True) if x is not None]
645 min_y, max_y = (
646 (min(annees), max(annees)) if len(annees)
647 else (date.today().year, date.today().year)
648 )
649 annees = reversed(range(min_y, max_y + 1))
650
3ebc0952 651 c = {
838bc59d 652 'title': u"Dossier %s" % (d, ),
00ca4d9f 653 'is_popup': request.GET.get('_popup', False),
6219cf26 654 'devises': devises(),
00ca4d9f 655 'dossier': d,
6219cf26 656 'annees': annees,
00ca4d9f
EMS
657 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
658 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
659 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
abf91905 660 'media_url': settings.PRIVE_MEDIA_URL,
3ebc0952 661 }
890b80e7 662 return render(request, 'admin/rh/dossier/apercu.html', c)
00ca4d9f 663
4a1f2ece 664
fbe60a9d
OL
665@region_protected(rh.Poste)
666def poste_apercu(request, poste_id):
c8cb18e3 667 p = get_object_or_404(rh.Poste, pk=poste_id)
fbe60a9d 668 c = {
c8cb18e3 669 'title': u"Poste %s" % (p, ),
00ca4d9f
EMS
670 'is_popup': request.GET.get('_popup', False),
671 'poste': p,
672 'financements': (
673 rh.PosteFinancement.objects.filter(poste=poste_id).all()
674 ),
675 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
676 'dossiers': (
677 rh.Dossier.objects.filter(poste=poste_id)
678 .order_by("-date_debut").all()
679 ),
680 'comparaisons': (
681 rh.PosteComparaison.objects.filter(poste=poste_id).all()
682 ),
683 'commentaires': (
684 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
685 ),
abf91905 686 'media_url': settings.PRIVE_MEDIA_URL,
fbe60a9d 687 }
890b80e7 688 return render(request, 'admin/rh/poste/apercu.html', c)
00ca4d9f 689
3383b2d1
OL
690@login_required
691@in_one_of_group((groups.CORRESPONDANT_RH,
692 groups.ADMINISTRATEURS,
693 groups.DIRECTEUR_DE_BUREAU,
694 groups.DRH_NIVEAU_1,
695 groups.DRH_NIVEAU_2))
4a1f2ece
OL
696def employe_apercu(request, employe_id):
697 employe = get_object_or_404(rh.Employe, pk=employe_id)
3383b2d1 698 user_groups = [g.name for g in request.user.groups.all()]
fbe60a9d
OL
699 dossiers = None
700
cd4c1b65 701 if in_drh_or_admin(request.user):
fbe60a9d 702 q = Q(employe=employe)
22343fe7 703
3383b2d1
OL
704 if groups.CORRESPONDANT_RH in user_groups or\
705 groups.ADMINISTRATEURS in user_groups or\
706 groups.DIRECTEUR_DE_BUREAU in user_groups:
bcc0c52d 707 employe_connecte = get_employe_from_user(request.user)
b0cf30b8
EMS
708 q = Q(employe=employe) & Q(
709 poste__implantation__zone_administrative=(
710 employe_connecte.implantation.zone_administrative
711 )
712 )
713
22343fe7
OL
714 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
715
4a1f2ece 716 c = {
ce740bb5 717 'title': u"Employe %s" % (employe, ),
00ca4d9f
EMS
718 'is_popup': request.GET.get('_popup', False),
719 'employe': employe,
720 'dossiers': dossiers,
abf91905 721 'media_url': settings.PRIVE_MEDIA_URL,
4a1f2ece 722 }
890b80e7 723 return render(request, 'admin/rh/employe/apercu.html', c)
00ca4d9f 724
150d83ec 725
77bd83d1 726@login_required
3383b2d1
OL
727@in_one_of_group((groups.CORRESPONDANT_RH,
728 groups.ADMINISTRATEURS,
729 groups.DIRECTEUR_DE_BUREAU,
730 groups.DRH_NIVEAU_1,
731 groups.DRH_NIVEAU_2))
56264a85 732def organigrammes_employe(request, id, level="all"):
08faf06e 733 poste = get_object_or_404(rh.Poste, pk=id)
00ca4d9f
EMS
734 dossiers_by_poste = dict(
735 (d.poste_id, d)
736 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
737 )
5c0f1778
JPC
738 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
739
740 e = dossiers_by_poste[poste.id].employe
741 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
58014aec 742 graph = pgv.AGraph()
5c0f1778 743
f187a10f 744 if rh.Poste.objects.filter(responsable=poste).count() > 0:
58014aec
JPC
745 postes_handle = [poste]
746 while postes_handle:
00ca4d9f
EMS
747 postes_handle = rh.Poste.objects.select_related('implantation') \
748 .filter(
749 Q(date_fin__gt=date.today()) | Q(date_fin=None),
750 Q(date_debut__lt=date.today()) | Q(date_debut=None),
751 responsable__in=postes_handle
c3550a05 752 ).exclude(responsable=None).all()
32373f2e 753
58014aec
JPC
754 for p in postes_handle:
755 if p.responsable_id != p.id:
00ca4d9f
EMS
756 graph.add_edge(
757 dossiers_by_poste[p.responsable_id].poste_id, p.id
758 )
13ad5ad5 759
f187a10f 760 else:
5c0f1778
JPC
761 graph.add_node(poste.id)
762
4afec2e1
JPC
763 if level != "all":
764 postes_niveau = [poste.id]
765 for niveau in range(int(level)):
00ca4d9f
EMS
766 postes_niveau = [
767 p.id for p in
768 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
769 ]
4afec2e1
JPC
770
771 while postes_niveau:
00ca4d9f
EMS
772 postes_niveau = [
773 p.id for p in
774 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
775 ]
4afec2e1
JPC
776 if postes_niveau:
777 for p in postes_niveau:
778 if graph.has_node(p):
4cc81304 779 graph.delete_node(p)
4afec2e1 780
58014aec 781 a = graph
4afec2e1 782 a.name = name.encode('ascii', 'xmlcharrefreplace')
5c0f1778
JPC
783
784 poste_remontant = poste
785 while poste_remontant.responsable_id:
786 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
787 poste_remontant = poste_remontant.responsable
788
7000b7b3 789 rh_graph.bind_poste_to_graph(request.user, a, postes_by_id)
5c0f1778
JPC
790 #a.graph_attr['normalize'] = True
791 #a.graph_attr['level'] = 2
792 a.layout(prog='dot')
793
794 svg = a.draw(format='svg')
795
796 c = {
797 'svg': svg
798 }
a251ac8d
JPC
799
800 if 'forcer' in request.GET:
890b80e7 801 response = HttpResponse(svg, content_type='image/svg+xml')
00ca4d9f
EMS
802 response['Content-Disposition'] = \
803 'attachment; filename=organigramme.svg'
a251ac8d
JPC
804 return response
805
890b80e7
DB
806 return render(request, 'rh/organigrammes/employe.html', c,
807 content_type="image/svg+xml"
00ca4d9f 808 )
5c0f1778
JPC
809
810
811@login_required
3383b2d1
OL
812@in_one_of_group((groups.CORRESPONDANT_RH,
813 groups.ADMINISTRATEURS,
814 groups.DIRECTEUR_DE_BUREAU,
815 groups.DRH_NIVEAU_1,
816 groups.DRH_NIVEAU_2))
5c0f1778 817def organigrammes_service(request, id):
5c0f1778 818 service = get_object_or_404(rh.Service, pk=id)
82af5c19 819 svg = rh_graph.organigramme_postes_cluster( \
7000b7b3 820 request.user, \
82af5c19
JPC
821 cluster_filter={"service": service}, \
822 titre=u"Organigramme du service %s" % service.nom,
823 cluster_titre=service.nom)
5c0f1778 824
d104b0ae 825 return render(request, 'rh/organigrammes/vide.html', {
82af5c19 826 'svg': svg
d104b0ae 827 }, content_type="image/svg+xml")
00ca4d9f 828
5c0f1778 829
82af5c19 830@login_required
3383b2d1
OL
831@in_one_of_group((groups.CORRESPONDANT_RH,
832 groups.ADMINISTRATEURS,
833 groups.DIRECTEUR_DE_BUREAU,
834 groups.DRH_NIVEAU_1,
835 groups.DRH_NIVEAU_2))
82af5c19 836def organigrammes_implantation(request, id):
82af5c19
JPC
837 implantation = get_object_or_404(ref.Implantation, pk=id)
838 svg = rh_graph.organigramme_postes_cluster( \
7000b7b3 839 request.user, \
82af5c19
JPC
840 cluster_filter={"implantation": implantation}, \
841 titre=u"Organigramme de l'implantation %s" % implantation.nom,
842 cluster_titre=implantation.nom)
d104b0ae 843 return render(request, 'rh/organigrammes/vide.html', {
5c0f1778 844 'svg': svg
d104b0ae 845 }, content_type="image/svg+xml")
00ca4d9f 846
5c0f1778 847
9da4c195 848@login_required
3383b2d1
OL
849@in_one_of_group((groups.CORRESPONDANT_RH,
850 groups.ADMINISTRATEURS,
851 groups.DIRECTEUR_DE_BUREAU,
852 groups.DRH_NIVEAU_1,
853 groups.DRH_NIVEAU_2))
9da4c195 854def organigrammes_region(request, id):
9da4c195
JPC
855 region = get_object_or_404(ref.Region, pk=id)
856 svg = rh_graph.organigramme_postes_cluster( \
7000b7b3 857 request.user, \
9da4c195
JPC
858 cluster_filter={"implantation__region": region}, \
859 titre=u"Organigramme du bureau de %s" % region.nom,
860 cluster_titre=region.nom)
861
d104b0ae 862 return render(request, 'rh/organigrammes/vide.html', {
9da4c195 863 'svg': svg
d104b0ae 864 }, content_type="image/svg+xml")
8e26a99b
OL
865
866
867@login_required
868@drh_or_admin_required
869def historique_des_modifications(request,):
870
871 from django.core.paginator import Paginator
872
edbc9e37 873 revisions = get_active_revisions()
45c66f19 874 paginator = Paginator(revisions, 50)
8e26a99b
OL
875 page = request.GET.get('page')
876 try:
877 page_revisions = paginator.page(page)
878 except:
879 page_revisions = paginator.page(1)
880
edbc9e37
OL
881 results = page_revisions.object_list
882 if request.POST:
883 form = TodoForm(request.POST, revisions=results)
884 if form.is_valid():
885 for id in form.cleaned_data['items']:
886 revision = Revision.objects.get(id=id)
887 rh.ModificationTraite(revision=revision).save()
888 return redirect(reverse('rhr_historique_des_modifications'))
889 else:
8e26a99b 890
edbc9e37
OL
891 form = TodoForm(revisions=results)
892
8e26a99b 893 c = {
edbc9e37 894 'headers': ('Date', 'Auteur', 'Type', 'Objet', 'Historique',
d61cd885 895 'Commentaire', ),
8e26a99b
OL
896 'revisions': results,
897 'page': page_revisions,
edbc9e37 898 'form': form,
8e26a99b
OL
899 }
900
901 return render(request, 'rh/rapports/historique_des_modifications.html', c)