[#3127] Toujours calculer les primes complètes sur l'année dans le rapport de masse...
[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_masse_salariale(request):
134 form = MasseSalarialeForm(request.user, request.GET)
135 if 'annee' in request.GET and form.is_valid():
136 region = form.cleaned_data['region']
137 implantation = form.cleaned_data['implantation']
138 annee = form.cleaned_data['annee']
139 debut_annee = date(annee, 1, 1)
140 fin_annee = date(annee, 12, 31)
141 jours_annee = (fin_annee - debut_annee).days + 1
142 today = date.today()
143
144 # Récupérer les dossiers actifs
145 dossiers = rh.Dossier.objects \
146 .actifs(annee=annee) \
147 .select_related(
148 'poste', 'poste__implantation',
149 'poste__implantation__region',
150 'poste__implantation__adresse_physique_pays',
151 'employe', 'poste__type_poste', 'classement',
152 'statut', 'organisme_bstg'
153 ) \
154 .extra(
155 tables=['rh_valeurpoint', 'rh_devise'],
156 where=[
157 'rh_valeurpoint.annee = %s',
158 'rh_valeurpoint.implantation = ref_implantation.id',
159 'rh_devise.id = rh_valeurpoint.devise'
160 ],
161 params=[annee],
162 select={
163 'valeur_point': 'rh_valeurpoint.valeur',
164 'valeur_point_devise': 'rh_devise.code'
165 }
166 )
167 if region:
168 dossiers = dossiers.filter(poste__implantation__region=region)
169 if implantation:
170 dossiers = dossiers.filter(poste__implantation=implantation)
171
172 # Récupérer les rémunérations actives
173 remuns = rh.Remuneration.objects \
174 .actifs(annee=annee) \
175 .select_related('devise', 'type') \
176 .extra(
177 tables=['rh_tauxchange'],
178 where=[
179 'rh_tauxchange.annee = %s',
180 'rh_tauxchange.devise = rh_devise.id'
181 ],
182 params=[annee],
183 select={
184 'taux_change': 'rh_tauxchange.taux'
185 }
186 )
187 remuns_par_dossier = defaultdict(list)
188 for remun in remuns:
189 remuns_par_dossier[remun.dossier_id].append(remun)
190
191 # Récupérer les types de rémunération par nature
192 types_remun_par_nature = defaultdict(list)
193 for type in rh.TypeRemuneration.objects.all():
194 types_remun_par_nature[type.nature_remuneration].append(type)
195 titres_traitements = [
196 t.nom for t in types_remun_par_nature[u'Traitement']
197 ]
198 titres_indemnites = [
199 t.nom for t in types_remun_par_nature[u'Indemnité']
200 ]
201 titres_primes = [
202 t.nom for t in types_remun_par_nature[u'Accessoire']
203 ]
204 titres_charges = [
205 t.nom for t in types_remun_par_nature[u'Charges']
206 ]
207
208 # Boucler sur les dossiers et préprarer les lignes du rapport
209 lignes = []
210 masse_salariale_totale = 0
211 for dossier in dossiers:
212 debut_cette_annee = \
213 max(dossier.date_debut or debut_annee, debut_annee)
214 fin_cette_annee = \
215 min(dossier.date_fin or fin_annee, fin_annee)
216 jours = (fin_cette_annee - debut_cette_annee).days + 1
217
218 remuns = remuns_par_dossier[dossier.id]
219 devises = set(remun.devise.code for remun in remuns)
220 if len(devises) == 1:
221 devise = remuns[0].devise.code
222 montant_remun = lambda r: r.montant
223 taux_change = Decimal(str(remuns[0].taux_change))
224 else:
225 devise = 'EUR'
226 montant_remun = lambda r: (
227 r.montant * Decimal(str(r.taux_change))
228 )
229 taux_change = Decimal(1)
230
231 remuns_par_type = defaultdict(lambda: 0)
232 for remun in remuns:
233 if remun.type.nature_remuneration == u'Accessoire':
234 remuns_par_type[remun.type_id] += montant_remun(remun)
235 else:
236 remuns_par_type[remun.type_id] += (
237 montant_remun(remun) * ((
238 min(remun.date_fin or fin_annee, fin_annee) -
239 max(remun.date_debut or debut_annee, debut_annee)
240 ).days + 1) / jours_annee
241 ).quantize(TWOPLACES)
242 traitements = [
243 remuns_par_type[type.id]
244 for type in types_remun_par_nature[u'Traitement']
245 ]
246 indemnites = [
247 remuns_par_type[type.id]
248 for type in types_remun_par_nature[u'Indemnité']
249 ]
250 primes = [
251 remuns_par_type[type.id]
252 for type in types_remun_par_nature[u'Accessoire']
253 ]
254 charges = [
255 remuns_par_type[type.id]
256 for type in types_remun_par_nature[u'Charges']
257 ]
258 masse_salariale = sum(remuns_par_type.values())
259 masse_salariale_eur = (
260 masse_salariale * taux_change
261 ).quantize(TWOPLACES)
262 masse_salariale_totale += masse_salariale_eur
263
264 if dossier.valeur_point and dossier.classement \
265 and dossier.classement.coefficient and dossier.regime_travail:
266 salaire_theorique = (Decimal(str(
267 dossier.valeur_point * dossier.classement.coefficient
268 )) * dossier.regime_travail / 100).quantize(TWOPLACES)
269 else:
270 salaire_theorique = None
271
272 lignes.append({
273 'dossier': dossier,
274 'poste': dossier.poste,
275 'date_debut': (
276 dossier.date_debut
277 if dossier.date_debut and dossier.date_debut.year == annee
278 else None
279 ),
280 'date_fin': (
281 dossier.date_fin
282 if dossier.date_fin and dossier.date_fin.year == annee
283 else None
284 ),
285 'jours': jours,
286 'devise': devise,
287 'valeur_point': dossier.valeur_point,
288 'valeur_point_devise': dossier.valeur_point_devise,
289 'regime_travail': dossier.regime_travail,
290 'local_expatrie': {
291 'local': 'L', 'expat': 'E'
292 }.get(dossier.statut_residence),
293 'salaire_bstg': remuns_par_type[2],
294 'salaire_bstg_eur': (
295 (remuns_par_type[2] * taux_change).quantize(TWOPLACES)
296 ),
297 'salaire_theorique': salaire_theorique,
298 'traitements': traitements,
299 'total_traitements': sum(traitements),
300 'indemnites': indemnites,
301 'total_indemnites': sum(indemnites),
302 'primes': primes,
303 'total_primes': sum(primes),
304 'charges': charges,
305 'total_charges': sum(charges),
306 'masse_salariale': masse_salariale,
307 'masse_salariale_eur': masse_salariale_eur,
308 })
309
310 # Récupérer les postes actifs pour déterminer le nombre de jours
311 # vacants.
312 postes = list(
313 rh.Poste.objects.actifs(annee=annee)
314 .select_related(
315 'devise_max', 'valeur_point_max', 'classement_max'
316 )
317 .extra(
318 tables=['rh_tauxchange'],
319 where=[
320 'rh_tauxchange.annee = %s',
321 'rh_tauxchange.devise = rh_devise.id'
322 ],
323 params=[annee],
324 select={
325 'taux_change': 'rh_tauxchange.taux'
326 }
327 )
328 )
329 postes_par_id = dict((poste.id, poste) for poste in postes)
330 jours_vacants_date = dict(
331 (poste.id, max(today, poste.date_debut or today))
332 for poste in postes
333 )
334 jours_vacants = defaultdict(lambda: 0)
335 for dossier in rh.Dossier.objects.actifs(annee=annee) \
336 .order_by('date_debut'):
337 if dossier.poste_id not in jours_vacants_date:
338 continue
339 derniere_date = jours_vacants_date[dossier.poste_id]
340 if dossier.date_debut is not None:
341 jours_vacants[dossier.poste_id] += max((
342 dossier.date_debut - derniere_date
343 ).days - 1, 0)
344 jours_vacants_date[dossier.poste_id] = max(
345 min(dossier.date_fin or fin_annee, fin_annee),
346 derniere_date
347 )
348
349 # Ajouter les lignes des postes vacants au rapport
350 for poste_id, jours in jours_vacants.iteritems():
351 if jours == 0:
352 continue
353 poste = postes_par_id[poste_id]
354 if poste.valeur_point_max and poste.classement_max \
355 and poste.classement_max.coefficient and poste.regime_travail:
356 salaire_theorique = (Decimal(str(
357 poste.valeur_point_max.valeur *
358 poste.classement_max.coefficient
359 )) * poste.regime_travail / 100).quantize(TWOPLACES)
360 else:
361 salaire_theorique = None
362
363 local_expatrie = '/'.join(
364 (['L'] if poste.local else []) +
365 (['E'] if poste.expatrie else [])
366 )
367
368 salaire = (
369 poste.salaire_max * jours / jours_annee *
370 poste.regime_travail / 100
371 ).quantize(TWOPLACES)
372 indemnites = (
373 poste.indemn_max * jours / jours_annee *
374 poste.regime_travail / 100
375 ).quantize(TWOPLACES)
376 charges = (
377 poste.autre_max * jours / jours_annee *
378 poste.regime_travail / 100
379 ).quantize(TWOPLACES)
380 masse_salariale = salaire + indemnites + charges
381 masse_salariale_eur = (
382 masse_salariale * Decimal(str(poste.taux_change))
383 ).quantize(TWOPLACES)
384 masse_salariale_totale += masse_salariale_eur
385
386 lignes.append({
387 'poste': poste,
388 'regime_travail': poste.regime_travail,
389 'local_expatrie': local_expatrie,
390 'jours': jours,
391 'devise': poste.devise_max and poste.devise_max.code,
392 'valeur_point': (
393 poste.valeur_point_max and poste.valeur_point_max.valeur
394 ),
395 'valeur_point_devise': (
396 poste.valeur_point_max and \
397 poste.valeur_point_max.devise.code
398 ),
399 'salaire_theorique': salaire_theorique,
400 'salaire_de_base': salaire,
401 'total_indemnites': indemnites,
402 'total_charges': charges,
403 'masse_salariale': masse_salariale,
404 'masse_salariale_eur': masse_salariale_eur
405 })
406 if 'ods' in request.GET:
407 doc = ods.masse_salariale(
408 lignes=lignes,
409 annee=annee,
410 titres_traitements=titres_traitements,
411 titres_indemnites=titres_indemnites,
412 titres_primes=titres_primes,
413 titres_charges=titres_charges,
414 masse_salariale_totale=masse_salariale_totale
415 )
416 response = HttpResponse(
417 mimetype='vnd.oasis.opendocument.spreadsheet'
418 )
419 response['Content-Disposition'] = 'filename=masse-salariale.ods'
420 doc.write(response)
421 return response
422 else:
423 return render(request, 'rh/rapports/masse_salariale.html', {
424 'form': form,
425 'titres_traitements': titres_traitements,
426 'titres_indemnites': titres_indemnites,
427 'titres_primes': titres_primes,
428 'titres_charges': titres_charges,
429 'masse_salariale_totale': masse_salariale_totale,
430 'lignes': lignes,
431 'annee': annee
432 })
433 return render(request, 'rh/rapports/masse_salariale.html', {
434 'form': form
435 })
436
437
438 @login_required
439 @drh_or_admin_required
440 def rapports_employes_sans_contrat(request):
441 lookup_params = get_lookup_params(request)
442
443 # contrats échus
444 contrats_echus = rh.Contrat.objects.filter(
445 date_fin__lt=date.today()
446 ).filter(
447 **lookup_params
448 )
449
450 # dossiers en cours sans contrat
451 dossiers_sans_contrat = rh.Dossier.objects.filter(
452 Q(date_fin=None) | Q(date_fin__gt=date.today()),
453 ).exclude(
454 date_debut__gt=date.today()
455 ).filter(
456 rh_contrats__in=contrats_echus
457 )
458
459 # employés sans contrat
460 employes = rh.Employe.objects \
461 .filter(rh_dossiers__in=dossiers_sans_contrat).distinct()
462 if 'o' in request.GET:
463 employes = employes.order_by(
464 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
465 )
466
467 # affichage
468 headers = [
469 ("id", u"#"),
470 ("nom", u"Employé"),
471 ("dossier", u"Dossier : Poste"),
472 ("dossier_date_debut", u"Début dossier"),
473 ("dossier_date_fin", u"Fin dossier"),
474 ]
475 h = SortHeaders(
476 request, headers, order_field_type="ot", order_field="o",
477 not_sortable=('dossier', 'dossier_date_debut', 'dossier_date_fin')
478 )
479
480 c = {
481 'title': u'Rapport des employés sans contrat',
482 'employes': employes,
483 'dossiers_sans_contrat': dossiers_sans_contrat,
484 'headers': list(h.headers()),
485 }
486
487 return render(request, 'rh/rapports/employes_sans_contrat.html', c)
488
489
490 @login_required
491 @drh_or_admin_required
492 def rapports_postes_modelisation(request):
493 c = {}
494 data = []
495
496 for categorie in rh.CategorieEmploi.objects.all():
497 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
498 data_types = []
499 for t in types.all():
500 postes = rh.Poste.objects.filter(type_poste=t)
501 data_types.append({
502 'num_postes': postes.count(),
503 'postes': postes.all(),
504 'type': categorie,
505 })
506
507 data.append({
508 'categorie': categorie,
509 'nb_types': types.count(),
510 'types': data_types
511 })
512
513 c['data'] = data
514
515 return render(request, 'rh/rapports/postes_modelisation.html', c)
516
517
518 @login_required
519 @drh_or_admin_required
520 def rapports_postes_implantation(request):
521 c = {}
522 data = []
523 for r in ref.Region.objects.all():
524 implantations = []
525 for i in ref.Implantation.objects.filter(region=r):
526 implantations.append({
527 'implantation': i,
528 'postes': rh.Poste.objects.filter(implantation=i),
529 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
530 })
531 data.append({
532 'region': r,
533 'implantations': implantations
534 })
535
536 c['data'] = data
537
538 return render(request, 'rh/rapports/postes_implantation.html', c)
539
540
541 @login_required
542 @drh_or_admin_required
543 def rapports_postes_service(request):
544 c = {}
545 data = []
546 for s in rh.Service.objects.all():
547 postes = rh.Poste.objects.filter(service=s).all()
548 num_postes = rh.Poste.objects.filter(service=s).count()
549 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
550
551 c['data'] = data
552 return render(request, 'rh/rapports/postes_service.html', c)
553
554
555 @region_protected(rh.Dossier)
556 def dossier_apercu(request, dossier_id):
557 d = get_object_or_404(rh.Dossier, pk=dossier_id)
558 c = {
559 'title': u"Dossier %s" % (d, ),
560 'is_popup': request.GET.get('_popup', False),
561 'dossier': d,
562 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
563 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
564 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
565 'media_url': settings.PRIVE_MEDIA_URL,
566 }
567 return render(request, 'admin/rh/dossier/apercu.html', c)
568
569
570 @region_protected(rh.Poste)
571 def poste_apercu(request, poste_id):
572 p = get_object_or_404(rh.Poste, pk=poste_id)
573 c = {
574 'title': u"Poste %s" % (p, ),
575 'is_popup': request.GET.get('_popup', False),
576 'poste': p,
577 'financements': (
578 rh.PosteFinancement.objects.filter(poste=poste_id).all()
579 ),
580 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
581 'dossiers': (
582 rh.Dossier.objects.filter(poste=poste_id)
583 .order_by("-date_debut").all()
584 ),
585 'comparaisons': (
586 rh.PosteComparaison.objects.filter(poste=poste_id).all()
587 ),
588 'commentaires': (
589 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
590 ),
591 'media_url': settings.PRIVE_MEDIA_URL,
592 }
593 return render(request, 'admin/rh/poste/apercu.html', c)
594
595
596 def employe_apercu(request, employe_id):
597 employe = get_object_or_404(rh.Employe, pk=employe_id)
598 user_groups = request.user.groups.all()
599 dossiers = None
600
601 if request.user.is_superuser or \
602 grp_drh in user_groups:
603 q = Q(employe=employe)
604 if grp_correspondants_rh in user_groups:
605 regions = [
606 d.poste.implantation.region for d in employe.rh_dossiers.all()
607 ]
608 q = Q(employe=employe) & Q(implantation__region__in=regions)
609
610 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
611
612 c = {
613 'title': u"Employe %s" % (employe, ),
614 'is_popup': request.GET.get('_popup', False),
615 'employe': employe,
616 'dossiers': dossiers,
617 'media_url': settings.PRIVE_MEDIA_URL,
618 }
619 return render(request, 'admin/rh/employe/apercu.html', c)
620
621
622 @login_required
623 @drh_or_admin_required
624 def organigrammes_employe(request, id, level="all"):
625 poste = get_object_or_404(rh.Poste, pk=id)
626 dossiers_by_poste = dict(
627 (d.poste_id, d)
628 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
629 )
630 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
631
632 e = dossiers_by_poste[poste.id].employe
633 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
634 graph = pgv.AGraph()
635
636 if rh.Poste.objects.filter(responsable=poste).count() > 0:
637 postes_handle = [poste]
638 while postes_handle:
639 postes_handle = rh.Poste.objects.select_related('implantation') \
640 .filter(
641 Q(date_fin__gt=date.today()) | Q(date_fin=None),
642 Q(date_debut__lt=date.today()) | Q(date_debut=None),
643 responsable__in=postes_handle
644 ).exclude(supprime=True).exclude(responsable=None).all()
645
646 for p in postes_handle:
647 if p.responsable_id != p.id:
648 graph.add_edge(
649 dossiers_by_poste[p.responsable_id].poste_id, p.id
650 )
651
652 else:
653 graph.add_node(poste.id)
654
655 if level != "all":
656 postes_niveau = [poste.id]
657 for niveau in range(int(level)):
658 postes_niveau = [
659 p.id for p in
660 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
661 ]
662
663 while postes_niveau:
664 postes_niveau = [
665 p.id for p in
666 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
667 ]
668 if postes_niveau:
669 for p in postes_niveau:
670 if graph.has_node(p):
671 graph.delete_node(p)
672
673 a = graph
674 a.name = name.encode('ascii', 'xmlcharrefreplace')
675
676 poste_remontant = poste
677 while poste_remontant.responsable_id:
678 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
679 poste_remontant = poste_remontant.responsable
680
681 rh_graph.bind_poste_to_graph(a, postes_by_id)
682 #a.graph_attr['normalize'] = True
683 #a.graph_attr['level'] = 2
684 a.layout(prog='dot')
685
686 svg = a.draw(format='svg')
687
688 c = {
689 'svg': svg
690 }
691
692 if 'forcer' in request.GET:
693 response = HttpResponse(svg, content_type='image/svg+xml')
694 response['Content-Disposition'] = \
695 'attachment; filename=organigramme.svg'
696 return response
697
698 return render(request, 'rh/organigrammes/employe.html', c,
699 content_type="image/svg+xml"
700 )
701
702
703 @login_required
704 @drh_or_admin_required
705 def organigrammes_service(request, id):
706 service = get_object_or_404(rh.Service, pk=id)
707 svg = rh_graph.organigramme_postes_cluster( \
708 cluster_filter={"service": service}, \
709 titre=u"Organigramme du service %s" % service.nom,
710 cluster_titre=service.nom)
711
712 c = {
713 'svg': svg
714 }
715
716 return render(request, 'rh/organigrammes/vide.html', c,
717 content_type="image/svg+xml"
718 )
719
720
721 @login_required
722 @drh_or_admin_required
723 def organigrammes_implantation(request, id):
724 implantation = get_object_or_404(ref.Implantation, pk=id)
725 svg = rh_graph.organigramme_postes_cluster( \
726 cluster_filter={"implantation": implantation}, \
727 titre=u"Organigramme de l'implantation %s" % implantation.nom,
728 cluster_titre=implantation.nom)
729
730 c = {
731 'svg': svg
732 }
733
734 return render(request, 'rh/organigrammes/vide.html', c,
735 content_type="image/svg+xml"
736 )
737
738
739 @login_required
740 @drh_or_admin_required
741 def organigrammes_region(request, id):
742 region = get_object_or_404(ref.Region, pk=id)
743 svg = rh_graph.organigramme_postes_cluster( \
744 cluster_filter={"implantation__region": region}, \
745 titre=u"Organigramme du bureau de %s" % region.nom,
746 cluster_titre=region.nom)
747
748 c = {
749 'svg': svg
750 }
751
752 return render(request,
753 'rh/organigrammes/vide.html', c,
754 content_type="image/svg+xml"
755 )