Merge branch 'masse-salariale' into dev
[auf_rh_dae.git] / project / rh / views.py
1 # -*- encoding: utf-8 -*-
2
3 from collections import defaultdict
4 from datetime import date
5 from decimal import Decimal
6
7 import pygraphviz as pgv
8 from auf.django.references import models as ref
9 from django.conf import settings
10 from django.contrib.auth.decorators import login_required
11 from django.core.urlresolvers import reverse
12 from django.db.models import Q
13 from django.http import HttpResponse
14 from django.shortcuts import render, get_object_or_404
15
16 from project.decorators import drh_or_admin_required
17 from project.decorators import region_protected
18 from project.groups import \
19 get_employe_from_user, grp_drh, grp_correspondants_rh
20 from project.rh import ods
21 from project.rh import graph as rh_graph
22 from project.rh import models as rh
23 from project.rh.change_list import RechercheTemporelle
24 from project.rh.forms import MasseSalarialeForm
25 from project.rh.lib import get_lookup_params
26 from project.rh.templatetags.rapports import SortHeaders
27
28 TWOPLACES = Decimal('0.01')
29
30
31 @login_required
32 def profil(request):
33 """Profil personnel de l'employé - éditable"""
34 employe = get_employe_from_user(request.user)
35 c = {
36 'user': request.user,
37 'employe': employe,
38 }
39 return render(request, 'rh/profil.html', c)
40
41
42 @login_required
43 def employes_liste(request):
44 """Liste des employés."""
45 today = date.today()
46 employes = rh.Employe.objects \
47 .exclude(dossiers__date_debut__gt=today) \
48 .exclude(dossiers__date_fin__lt=today) \
49 .order_by('nom')
50 c = {
51 'user': request.user,
52 'employes': employes,
53 }
54 return render(request, 'rh/employes_liste.html', c)
55
56
57 @login_required
58 def employe(request, id):
59 """Information publique sur un employé."""
60 try:
61 employe = rh.Employe.objects.get(pk=id)
62 except:
63 employe = rh.Employe.objects.none()
64 c = {
65 'user': request.user,
66 'employe': employe,
67 }
68 return render(request, 'rh/employe.html', c)
69
70
71 @login_required
72 @drh_or_admin_required
73 def rapports_contrat(request):
74 # statut default = actif?
75 if 'HTTP_REFERER' in request.META.keys():
76 referer = request.META['HTTP_REFERER']
77 referer = "/".join(referer.split('/')[3:])
78 referer = "/%s" % referer.split('?')[0]
79 if referer != reverse('rhr_contrats'):
80 params = request.GET.copy()
81 params.update({'statut': 'Actif'})
82 request.GET = params
83
84 contrats = rh.Contrat.objects.select_related(
85 'dossier', 'dossier__poste', 'dossier__poste__implantation',
86 'type_contrat', 'dossier__employe'
87 )
88
89 # filters et recherche temporelle
90 cl = RechercheTemporelle(dict(request.GET.items()), rh.Contrat)
91 lookup_params = get_lookup_params(request)
92 lookup_params = cl.purge_params(lookup_params)
93 q_temporel = cl.get_q_temporel(contrats)
94 q = Q(**lookup_params) & q_temporel
95 contrats = contrats.filter(q).exclude(dossier__employe__supprime=1)
96
97 # tri
98 if 'o' in request.GET:
99 contrats = contrats.order_by(
100 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
101 )
102
103 # affichage
104 employes = set([c.dossier.employe_id for c in contrats])
105 headers = [
106 ("dossier__employe__id", u"#"),
107 ("dossier__employe__nom", u"Employé"),
108 ("dossier", u"Dossier : Poste"),
109 ("type_contrat__nom", u"Contrat"),
110 ("date_debut", u"Début contrat"),
111 ("date_fin", u"Fin contrat"),
112 ("dossier__statut_residence", u"Statut"),
113 ("dossier__poste__implantation__region", u"Région"),
114 ("dossier__poste__implantation", u"Implantation"),
115 ]
116 h = SortHeaders(
117 request, headers, order_field_type="ot", order_field="o",
118 not_sortable=('dossier',)
119 )
120
121 c = {
122 'cl': cl,
123 'title': 'Rapport des contrats',
124 'contrats': contrats,
125 'count_employe': len(employes),
126 'headers': list(h.headers()),
127 }
128 return render(request, 'rh/rapports/contrats.html', c)
129
130
131 @login_required
132 @drh_or_admin_required
133 def rapports_employes_sans_contrat(request):
134 """
135 Employé sans contrat = a un Dossier qui n'a pas de Contrat associé
136 Employé avec contrat échu = a un Dossier avec un Contrat se terminant hier
137 au plus tard... (aujourd'hui = ok, pas date de fin = illimité donc ok)
138 """
139 lookup_params = get_lookup_params(request)
140
141 # contrats échus
142 contrats_echus = rh.Contrat.objects.filter(
143 date_fin__lt=date.today()
144 ).filter(
145 **lookup_params
146 )
147
148 # dossiers en cours sans contrat ou contrats échus
149 dossiers = rh.Dossier.objects.filter(
150 Q(date_debut=None) | Q(date_debut__lte=date.today()),
151 ).filter(
152 Q(date_fin=None) | Q(date_fin__gte=date.today()),
153 ).exclude(
154 date_debut__gt=date.today()
155 ).filter(
156 # sans contrat | contrat échu
157 Q(rh_contrats=None) | Q(rh_contrats__in=contrats_echus)
158 ).distinct()
159
160 # employés sans contrat ou contrats échus
161 employes = rh.Employe.objects.filter(rh_dossiers__in=dossiers) \
162 .distinct().count()
163
164 # tri
165 if 'o' in request.GET:
166 dossiers = dossiers.order_by(
167 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
168 )
169
170 # affichage
171 headers = [
172 ("employe__id", u"#"),
173 ("employe__nom", u"Employé"),
174 ("dossier", u"Dossier : Poste"),
175 ("rh_contrats__type_contrat", u"Contrat"),
176 ("rh_contrats__date_debut", u"Début contrat"),
177 ("rh_contrats__date_fin", u"Fin contrat"),
178 ("statut_residence", u"Statut"),
179 ("poste__implantation__region__code", u"Région"),
180 ("poste__implantation__nom", u"Implantation"),
181 ]
182 h = SortHeaders(
183 request, headers, order_field_type="ot", order_field="o",
184 not_sortable=('dossier',)
185 )
186
187 c = {
188 'title': u'Rapport des employés sans contrat ou contrat échu',
189 'employes': employes,
190 'dossiers': dossiers,
191 'headers': list(h.headers()),
192 }
193
194 return render(request, 'rh/rapports/employes_sans_contrat.html', c)
195
196
197 @login_required
198 @drh_or_admin_required
199 def rapports_masse_salariale(request):
200 form = MasseSalarialeForm(request.user, request.GET)
201 if 'annee' in request.GET and form.is_valid():
202 region = form.cleaned_data['region']
203 implantation = form.cleaned_data['implantation']
204 annee = form.cleaned_data['annee']
205 debut_annee = date(annee, 1, 1)
206 fin_annee = date(annee, 12, 31)
207 jours_annee = (fin_annee - debut_annee).days + 1
208 today = date.today()
209
210 # Récupérer les dossiers actifs
211 dossiers = rh.Dossier.objects \
212 .actifs(annee=annee) \
213 .select_related(
214 'poste', 'poste__implantation',
215 'poste__implantation__region',
216 'poste__implantation__adresse_physique_pays',
217 'employe', 'poste__type_poste', 'classement',
218 'statut', 'organisme_bstg'
219 ) \
220 .extra(
221 tables=['rh_valeurpoint', 'rh_devise'],
222 where=[
223 'rh_valeurpoint.annee = %s',
224 'rh_valeurpoint.implantation = ref_implantation.id',
225 'rh_devise.id = rh_valeurpoint.devise'
226 ],
227 params=[annee],
228 select={
229 'valeur_point': 'rh_valeurpoint.valeur',
230 'valeur_point_devise': 'rh_devise.code'
231 }
232 )
233 if region:
234 dossiers = dossiers.filter(poste__implantation__region=region)
235 if implantation:
236 dossiers = dossiers.filter(poste__implantation=implantation)
237
238 # Récupérer les rémunérations actives
239 remuns = rh.Remuneration.objects \
240 .actifs(annee=annee) \
241 .select_related('devise', 'type') \
242 .extra(
243 tables=['rh_tauxchange'],
244 where=[
245 'rh_tauxchange.annee = %s',
246 'rh_tauxchange.devise = rh_devise.id'
247 ],
248 params=[annee],
249 select={
250 'taux_change': 'rh_tauxchange.taux'
251 }
252 )
253 remuns_par_dossier = defaultdict(list)
254 for remun in remuns:
255 remuns_par_dossier[remun.dossier_id].append(remun)
256
257 # Récupérer les types de rémunération par nature
258 types_remun_par_nature = defaultdict(list)
259 for type in rh.TypeRemuneration.objects.all():
260 types_remun_par_nature[type.nature_remuneration].append(type)
261 titres_traitements = [
262 t.nom for t in types_remun_par_nature[u'Traitement']
263 ]
264 titres_indemnites = [
265 t.nom for t in types_remun_par_nature[u'Indemnité']
266 ]
267 titres_primes = [
268 t.nom for t in types_remun_par_nature[u'Accessoire']
269 ]
270 titres_charges = [
271 t.nom for t in types_remun_par_nature[u'Charges']
272 ]
273
274 # Boucler sur les dossiers et préprarer les lignes du rapport
275 lignes = []
276 masse_salariale_totale = 0
277 for dossier in dossiers:
278 debut_cette_annee = \
279 max(dossier.date_debut or debut_annee, debut_annee)
280 fin_cette_annee = \
281 min(dossier.date_fin or fin_annee, fin_annee)
282 jours = (fin_cette_annee - debut_cette_annee).days + 1
283
284 remuns = remuns_par_dossier[dossier.id]
285 devises = set(remun.devise.code for remun in remuns)
286 if len(devises) == 1:
287 devise = remuns[0].devise.code
288 montant_remun = lambda r: r.montant
289 taux_change = Decimal(str(remuns[0].taux_change))
290 else:
291 devise = 'EUR'
292 montant_remun = lambda r: (
293 r.montant * Decimal(str(r.taux_change))
294 )
295 taux_change = Decimal(1)
296
297 remuns_par_type = defaultdict(lambda: 0)
298 for remun in remuns:
299 if remun.type.nature_remuneration == u'Accessoire':
300 remuns_par_type[remun.type_id] += montant_remun(remun)
301 else:
302 remuns_par_type[remun.type_id] += (
303 montant_remun(remun) * ((
304 min(remun.date_fin or fin_annee, fin_annee) -
305 max(remun.date_debut or debut_annee, debut_annee)
306 ).days + 1) / jours_annee
307 ).quantize(TWOPLACES)
308 traitements = [
309 remuns_par_type[type.id]
310 for type in types_remun_par_nature[u'Traitement']
311 ]
312 indemnites = [
313 remuns_par_type[type.id]
314 for type in types_remun_par_nature[u'Indemnité']
315 ]
316 primes = [
317 remuns_par_type[type.id]
318 for type in types_remun_par_nature[u'Accessoire']
319 ]
320 charges = [
321 remuns_par_type[type.id]
322 for type in types_remun_par_nature[u'Charges']
323 ]
324 masse_salariale = sum(remuns_par_type.values())
325 masse_salariale_eur = (
326 masse_salariale * taux_change
327 ).quantize(TWOPLACES)
328 masse_salariale_totale += masse_salariale_eur
329
330 if dossier.valeur_point and dossier.classement \
331 and dossier.classement.coefficient and dossier.regime_travail:
332 salaire_theorique = (Decimal(str(
333 dossier.valeur_point * dossier.classement.coefficient
334 )) * dossier.regime_travail / 100).quantize(TWOPLACES)
335 else:
336 salaire_theorique = None
337
338 lignes.append({
339 'dossier': dossier,
340 'poste': dossier.poste,
341 'date_debut': (
342 dossier.date_debut
343 if dossier.date_debut and dossier.date_debut.year == annee
344 else None
345 ),
346 'date_fin': (
347 dossier.date_fin
348 if dossier.date_fin and dossier.date_fin.year == annee
349 else None
350 ),
351 'jours': jours,
352 'devise': devise,
353 'valeur_point': dossier.valeur_point,
354 'valeur_point_devise': dossier.valeur_point_devise,
355 'regime_travail': dossier.regime_travail,
356 'local_expatrie': {
357 'local': 'L', 'expat': 'E'
358 }.get(dossier.statut_residence),
359 'salaire_bstg': remuns_par_type[2],
360 'salaire_bstg_eur': (
361 (remuns_par_type[2] * taux_change).quantize(TWOPLACES)
362 ),
363 'salaire_theorique': salaire_theorique,
364 'traitements': traitements,
365 'total_traitements': sum(traitements),
366 'indemnites': indemnites,
367 'total_indemnites': sum(indemnites),
368 'primes': primes,
369 'total_primes': sum(primes),
370 'charges': charges,
371 'total_charges': sum(charges),
372 'masse_salariale': masse_salariale,
373 'masse_salariale_eur': masse_salariale_eur,
374 })
375
376 # Récupérer les postes actifs pour déterminer le nombre de jours
377 # vacants.
378 postes = list(
379 rh.Poste.objects.actifs(annee=annee)
380 .select_related(
381 'devise_max', 'valeur_point_max', 'classement_max'
382 )
383 .extra(
384 tables=['rh_tauxchange'],
385 where=[
386 'rh_tauxchange.annee = %s',
387 'rh_tauxchange.devise = rh_devise.id'
388 ],
389 params=[annee],
390 select={
391 'taux_change': 'rh_tauxchange.taux'
392 }
393 )
394 )
395 postes_par_id = dict((poste.id, poste) for poste in postes)
396 jours_vacants_date = dict(
397 (poste.id, max(today, poste.date_debut or today))
398 for poste in postes
399 )
400 jours_vacants = defaultdict(lambda: 0)
401 for dossier in rh.Dossier.objects.actifs(annee=annee) \
402 .order_by('date_debut'):
403 if dossier.poste_id not in jours_vacants_date:
404 continue
405 derniere_date = jours_vacants_date[dossier.poste_id]
406 if dossier.date_debut is not None:
407 jours_vacants[dossier.poste_id] += max((
408 dossier.date_debut - derniere_date
409 ).days - 1, 0)
410 jours_vacants_date[dossier.poste_id] = max(
411 min(dossier.date_fin or fin_annee, fin_annee),
412 derniere_date
413 )
414
415 # Ajouter les lignes des postes vacants au rapport
416 for poste_id, jours in jours_vacants.iteritems():
417 if jours == 0:
418 continue
419 poste = postes_par_id[poste_id]
420 if poste.valeur_point_max and poste.classement_max \
421 and poste.classement_max.coefficient and poste.regime_travail:
422 salaire_theorique = (Decimal(str(
423 poste.valeur_point_max.valeur *
424 poste.classement_max.coefficient
425 )) * poste.regime_travail / 100).quantize(TWOPLACES)
426 else:
427 salaire_theorique = None
428
429 local_expatrie = '/'.join(
430 (['L'] if poste.local else []) +
431 (['E'] if poste.expatrie else [])
432 )
433
434 salaire = (
435 poste.salaire_max * jours / jours_annee *
436 poste.regime_travail / 100
437 ).quantize(TWOPLACES)
438 indemnites = (
439 poste.indemn_max * jours / jours_annee *
440 poste.regime_travail / 100
441 ).quantize(TWOPLACES)
442 charges = (
443 poste.autre_max * jours / jours_annee *
444 poste.regime_travail / 100
445 ).quantize(TWOPLACES)
446 masse_salariale = salaire + indemnites + charges
447 masse_salariale_eur = (
448 masse_salariale * Decimal(str(poste.taux_change))
449 ).quantize(TWOPLACES)
450 masse_salariale_totale += masse_salariale_eur
451
452 lignes.append({
453 'poste': poste,
454 'regime_travail': poste.regime_travail,
455 'local_expatrie': local_expatrie,
456 'jours': jours,
457 'devise': poste.devise_max and poste.devise_max.code,
458 'valeur_point': (
459 poste.valeur_point_max and poste.valeur_point_max.valeur
460 ),
461 'valeur_point_devise': (
462 poste.valeur_point_max and \
463 poste.valeur_point_max.devise.code
464 ),
465 'salaire_theorique': salaire_theorique,
466 'salaire_de_base': salaire,
467 'total_indemnites': indemnites,
468 'total_charges': charges,
469 'masse_salariale': masse_salariale,
470 'masse_salariale_eur': masse_salariale_eur
471 })
472 if 'ods' in request.GET:
473 doc = ods.masse_salariale(
474 lignes=lignes,
475 annee=annee,
476 titres_traitements=titres_traitements,
477 titres_indemnites=titres_indemnites,
478 titres_primes=titres_primes,
479 titres_charges=titres_charges,
480 masse_salariale_totale=masse_salariale_totale
481 )
482 response = HttpResponse(
483 mimetype='vnd.oasis.opendocument.spreadsheet'
484 )
485 response['Content-Disposition'] = 'filename=masse-salariale.ods'
486 doc.write(response)
487 return response
488 else:
489 return render(request, 'rh/rapports/masse_salariale.html', {
490 'form': form,
491 'titres_traitements': titres_traitements,
492 'titres_indemnites': titres_indemnites,
493 'titres_primes': titres_primes,
494 'titres_charges': titres_charges,
495 'masse_salariale_totale': masse_salariale_totale,
496 'lignes': lignes,
497 'annee': annee
498 })
499 return render(request, 'rh/rapports/masse_salariale.html', {
500 'form': form
501 })
502
503
504 @login_required
505 @drh_or_admin_required
506 def rapports_postes_modelisation(request):
507 c = {}
508 data = []
509
510 for categorie in rh.CategorieEmploi.objects.all():
511 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
512 data_types = []
513 for t in types.all():
514 postes = rh.Poste.objects.filter(type_poste=t)
515 data_types.append({
516 'num_postes': postes.count(),
517 'postes': postes.all(),
518 'type': categorie,
519 })
520
521 data.append({
522 'categorie': categorie,
523 'nb_types': types.count(),
524 'types': data_types
525 })
526
527 c['data'] = data
528
529 return render(request, 'rh/rapports/postes_modelisation.html', c)
530
531
532 @login_required
533 @drh_or_admin_required
534 def rapports_postes_implantation(request):
535 c = {}
536 data = []
537 for r in ref.Region.objects.all():
538 implantations = []
539 for i in ref.Implantation.objects.filter(region=r):
540 implantations.append({
541 'implantation': i,
542 'postes': rh.Poste.objects.filter(implantation=i),
543 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
544 })
545 data.append({
546 'region': r,
547 'implantations': implantations
548 })
549
550 c['data'] = data
551
552 return render(request, 'rh/rapports/postes_implantation.html', c)
553
554
555 @login_required
556 @drh_or_admin_required
557 def rapports_postes_service(request):
558 c = {}
559 data = []
560 for s in rh.Service.objects.all():
561 postes = rh.Poste.objects.filter(service=s).all()
562 num_postes = rh.Poste.objects.filter(service=s).count()
563 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
564
565 c['data'] = data
566 return render(request, 'rh/rapports/postes_service.html', c)
567
568
569 @region_protected(rh.Dossier)
570 def dossier_apercu(request, dossier_id):
571 d = get_object_or_404(rh.Dossier, pk=dossier_id)
572 c = {
573 'title': u"Dossier %s" % (d, ),
574 'is_popup': request.GET.get('_popup', False),
575 'dossier': d,
576 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
577 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
578 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
579 'media_url': settings.PRIVE_MEDIA_URL,
580 }
581 return render(request, 'admin/rh/dossier/apercu.html', c)
582
583
584 @region_protected(rh.Poste)
585 def poste_apercu(request, poste_id):
586 p = get_object_or_404(rh.Poste, pk=poste_id)
587 c = {
588 'title': u"Poste %s" % (p, ),
589 'is_popup': request.GET.get('_popup', False),
590 'poste': p,
591 'financements': (
592 rh.PosteFinancement.objects.filter(poste=poste_id).all()
593 ),
594 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
595 'dossiers': (
596 rh.Dossier.objects.filter(poste=poste_id)
597 .order_by("-date_debut").all()
598 ),
599 'comparaisons': (
600 rh.PosteComparaison.objects.filter(poste=poste_id).all()
601 ),
602 'commentaires': (
603 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
604 ),
605 'media_url': settings.PRIVE_MEDIA_URL,
606 }
607 return render(request, 'admin/rh/poste/apercu.html', c)
608
609
610 def employe_apercu(request, employe_id):
611 employe = get_object_or_404(rh.Employe, pk=employe_id)
612 user_groups = request.user.groups.all()
613 dossiers = None
614
615 if request.user.is_superuser or \
616 grp_drh in user_groups:
617 q = Q(employe=employe)
618 if grp_correspondants_rh in user_groups:
619 regions = [
620 d.poste.implantation.region for d in employe.rh_dossiers.all()
621 ]
622 q = Q(employe=employe) & Q(implantation__region__in=regions)
623
624 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
625
626 c = {
627 'title': u"Employe %s" % (employe, ),
628 'is_popup': request.GET.get('_popup', False),
629 'employe': employe,
630 'dossiers': dossiers,
631 'media_url': settings.PRIVE_MEDIA_URL,
632 }
633 return render(request, 'admin/rh/employe/apercu.html', c)
634
635
636 @login_required
637 @drh_or_admin_required
638 def organigrammes_employe(request, id, level="all"):
639 poste = get_object_or_404(rh.Poste, pk=id)
640 dossiers_by_poste = dict(
641 (d.poste_id, d)
642 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
643 )
644 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
645
646 e = dossiers_by_poste[poste.id].employe
647 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
648 graph = pgv.AGraph()
649
650 if rh.Poste.objects.filter(responsable=poste).count() > 0:
651 postes_handle = [poste]
652 while postes_handle:
653 postes_handle = rh.Poste.objects.select_related('implantation') \
654 .filter(
655 Q(date_fin__gt=date.today()) | Q(date_fin=None),
656 Q(date_debut__lt=date.today()) | Q(date_debut=None),
657 responsable__in=postes_handle
658 ).exclude(supprime=True).exclude(responsable=None).all()
659
660 for p in postes_handle:
661 if p.responsable_id != p.id:
662 graph.add_edge(
663 dossiers_by_poste[p.responsable_id].poste_id, p.id
664 )
665
666 else:
667 graph.add_node(poste.id)
668
669 if level != "all":
670 postes_niveau = [poste.id]
671 for niveau in range(int(level)):
672 postes_niveau = [
673 p.id for p in
674 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
675 ]
676
677 while postes_niveau:
678 postes_niveau = [
679 p.id for p in
680 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
681 ]
682 if postes_niveau:
683 for p in postes_niveau:
684 if graph.has_node(p):
685 graph.delete_node(p)
686
687 a = graph
688 a.name = name.encode('ascii', 'xmlcharrefreplace')
689
690 poste_remontant = poste
691 while poste_remontant.responsable_id:
692 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
693 poste_remontant = poste_remontant.responsable
694
695 rh_graph.bind_poste_to_graph(a, postes_by_id)
696 #a.graph_attr['normalize'] = True
697 #a.graph_attr['level'] = 2
698 a.layout(prog='dot')
699
700 svg = a.draw(format='svg')
701
702 c = {
703 'svg': svg
704 }
705
706 if 'forcer' in request.GET:
707 response = HttpResponse(svg, content_type='image/svg+xml')
708 response['Content-Disposition'] = \
709 'attachment; filename=organigramme.svg'
710 return response
711
712 return render(request, 'rh/organigrammes/employe.html', c,
713 content_type="image/svg+xml"
714 )
715
716
717 @login_required
718 @drh_or_admin_required
719 def organigrammes_service(request, id):
720 service = get_object_or_404(rh.Service, pk=id)
721 svg = rh_graph.organigramme_postes_cluster( \
722 cluster_filter={"service": service}, \
723 titre=u"Organigramme du service %s" % service.nom,
724 cluster_titre=service.nom)
725
726 c = {
727 'svg': svg
728 }
729
730 return render(request, 'rh/organigrammes/vide.html', c,
731 content_type="image/svg+xml"
732 )
733
734
735 @login_required
736 @drh_or_admin_required
737 def organigrammes_implantation(request, id):
738 implantation = get_object_or_404(ref.Implantation, pk=id)
739 svg = rh_graph.organigramme_postes_cluster( \
740 cluster_filter={"implantation": implantation}, \
741 titre=u"Organigramme de l'implantation %s" % implantation.nom,
742 cluster_titre=implantation.nom)
743
744 c = {
745 'svg': svg
746 }
747
748 return render(request, 'rh/organigrammes/vide.html', c,
749 content_type="image/svg+xml"
750 )
751
752
753 @login_required
754 @drh_or_admin_required
755 def organigrammes_region(request, id):
756 region = get_object_or_404(ref.Region, pk=id)
757 svg = rh_graph.organigramme_postes_cluster( \
758 cluster_filter={"implantation__region": region}, \
759 titre=u"Organigramme du bureau de %s" % region.nom,
760 cluster_titre=region.nom)
761
762 c = {
763 'svg': svg
764 }
765
766 return render(request,
767 'rh/organigrammes/vide.html', c,
768 content_type="image/svg+xml"
769 )