[#3068] [#3069] [#3133] Réécriture du rapport de masse salariale
[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
fc62be5d 8from auf.django.references import models as ref
4ba84959 9from django.conf import settings
e90b19fd 10from django.contrib.auth.decorators import login_required
dcd1b959 11from django.core.urlresolvers import reverse
fbe60a9d 12from django.db.models import Q
4ba84959 13from django.http import HttpResponse
890b80e7 14from django.shortcuts import render, get_object_or_404
e9bbd6ba 15
018c8eaf 16from project.decorators import drh_or_admin_required
ae99002a 17from project.decorators import region_protected
fc62be5d
EMS
18from project.groups import \
19 get_employe_from_user, grp_drh, grp_correspondants_rh
20from project.rh import ods
4ba84959 21from project.rh import graph as rh_graph
fc62be5d 22from project.rh import models as rh
890b80e7 23from project.rh.change_list import RechercheTemporelle
fc62be5d 24from project.rh.forms import MasseSalarialeForm
10a11035 25from project.rh.lib import get_lookup_params
890b80e7 26from project.rh.templatetags.rapports import SortHeaders
9817fed5 27
fc62be5d
EMS
28TWOPLACES = Decimal('0.01')
29
22343fe7 30
c9122f1d 31@login_required
32def profil(request):
33 """Profil personnel de l'employé - éditable"""
10a11035 34 employe = get_employe_from_user(request.user)
7d0c2206 35 c = {
8b3f6ff5
DB
36 'user': request.user,
37 'employe': employe,
7d0c2206 38 }
890b80e7 39 return render(request, 'rh/profil.html', c)
22343fe7
OL
40
41
3411ac33 42@login_required
a9faef67 43def employes_liste(request):
44 """Liste des employés."""
5ea6b5bb 45 today = date.today()
46 employes = rh.Employe.objects \
5ea6b5bb 47 .exclude(dossiers__date_debut__gt=today) \
48 .exclude(dossiers__date_fin__lt=today) \
49 .order_by('nom')
a9faef67 50 c = {
22343fe7
OL
51 'user': request.user,
52 'employes': employes,
a9faef67 53 }
890b80e7 54 return render(request, 'rh/employes_liste.html', c)
22343fe7
OL
55
56
3411ac33 57@login_required
a9faef67 58def 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 = {
22343fe7
OL
65 'user': request.user,
66 'employe': employe,
a9faef67 67 }
890b80e7 68 return render(request, 'rh/employe.html', c)
63e17dff
PP
69
70
08faf06e
JPC
71@login_required
72@drh_or_admin_required
f2d65e83 73def rapports_contrat(request):
dd18f6e3 74 # statut default = actif?
dcd1b959 75 if 'HTTP_REFERER' in request.META.keys():
58348377
EMS
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
f2d65e83 83
00ca4d9f
EMS
84 contrats = rh.Contrat.objects.select_related(
85 'dossier', 'dossier__poste', 'dossier__poste__implantation',
86 'type_contrat', 'dossier__employe'
87 )
22343fe7 88
dd18f6e3 89 # filters et recherche temporelle
6bee05ff 90 cl = RechercheTemporelle(dict(request.GET.items()), rh.Contrat)
dd18f6e3 91 lookup_params = get_lookup_params(request)
f0f6b03e 92 lookup_params = cl.purge_params(lookup_params)
dcd1b959
OL
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)
f0f6b03e 96
dd18f6e3 97 # tri
65e02621 98 if 'o' in request.GET:
00ca4d9f
EMS
99 contrats = contrats.order_by(
100 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
101 )
f2d65e83 102
dd18f6e3 103 # affichage
8ee135b7 104 employes = set([c.dossier.employe_id for c in contrats])
65e02621 105 headers = [
dd18f6e3 106 ("dossier__employe__id", u"#"),
d50a5e5d 107 ("dossier__employe__nom", u"Employé"),
dd18f6e3
DB
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"),
d50a5e5d
OL
113 ("dossier__poste__implantation__region", u"Région"),
114 ("dossier__poste__implantation", u"Implantation"),
65e02621 115 ]
dd18f6e3
DB
116 h = SortHeaders(
117 request, headers, order_field_type="ot", order_field="o",
118 not_sortable=('dossier',)
119 )
65e02621 120
f2d65e83 121 c = {
00ca4d9f 122 'cl': cl,
f2d65e83
PP
123 'title': 'Rapport des contrats',
124 'contrats': contrats,
00ca4d9f 125 'count_employe': len(employes),
65e02621 126 'headers': list(h.headers()),
f2d65e83 127 }
890b80e7 128 return render(request, 'rh/rapports/contrats.html', c)
e2c0b1ac
PP
129
130
02c1b3dc
JPC
131@login_required
132@drh_or_admin_required
98d6eb6c 133def rapports_masse_salariale(request):
fc62be5d
EMS
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 }
48c0abfe 166 )
fc62be5d
EMS
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))
48c0abfe 228 )
fc62be5d
EMS
229 taux_change = Decimal(1)
230
231 remuns_par_type = defaultdict(lambda: 0)
232 for remun in remuns:
233 remuns_par_type[remun.type_id] += (
234 montant_remun(remun) * ((
235 min(remun.date_fin or fin_annee, fin_annee) -
236 max(remun.date_debut or debut_annee, debut_annee)
237 ).days + 1) / jours_annee
238 ).quantize(TWOPLACES)
239 traitements = [
240 remuns_par_type[type.id]
241 for type in types_remun_par_nature[u'Traitement']
242 ]
243 indemnites = [
244 remuns_par_type[type.id]
245 for type in types_remun_par_nature[u'Indemnité']
246 ]
247 primes = [
248 remuns_par_type[type.id]
249 for type in types_remun_par_nature[u'Accessoire']
250 ]
251 charges = [
252 remuns_par_type[type.id]
253 for type in types_remun_par_nature[u'Charges']
254 ]
255 masse_salariale = sum(remuns_par_type.values())
256 masse_salariale_eur = (
257 masse_salariale * taux_change
258 ).quantize(TWOPLACES)
259 masse_salariale_totale += masse_salariale_eur
260
261 if dossier.valeur_point and dossier.classement \
262 and dossier.classement.coefficient and dossier.regime_travail:
263 salaire_theorique = (Decimal(str(
264 dossier.valeur_point * dossier.classement.coefficient
265 )) * dossier.regime_travail / 100).quantize(TWOPLACES)
266 else:
267 salaire_theorique = None
268
269 lignes.append({
270 'dossier': dossier,
271 'poste': dossier.poste,
272 'date_debut': (
273 dossier.date_debut
274 if dossier.date_debut and dossier.date_debut.year == annee
275 else None
276 ),
277 'date_fin': (
278 dossier.date_fin
279 if dossier.date_fin and dossier.date_fin.year == annee
280 else None
281 ),
282 'jours': jours,
283 'devise': devise,
284 'valeur_point': dossier.valeur_point,
285 'valeur_point_devise': dossier.valeur_point_devise,
286 'regime_travail': dossier.regime_travail,
287 'local_expatrie': {
288 'local': 'L', 'expat': 'E'
289 }.get(dossier.statut_residence),
290 'salaire_bstg': remuns_par_type[2],
291 'salaire_bstg_eur': (
292 (remuns_par_type[2] * taux_change).quantize(TWOPLACES)
293 ),
294 'salaire_theorique': salaire_theorique,
295 'traitements': traitements,
296 'total_traitements': sum(traitements),
297 'indemnites': indemnites,
298 'total_indemnites': sum(indemnites),
299 'primes': primes,
300 'total_primes': sum(primes),
301 'charges': charges,
302 'total_charges': sum(charges),
303 'masse_salariale': masse_salariale,
304 'masse_salariale_eur': masse_salariale_eur,
305 })
306
307 # Récupérer les postes actifs pour déterminer le nombre de jours
308 # vacants.
309 postes = list(
310 rh.Poste.objects.actifs(annee=annee)
311 .select_related(
312 'devise_max', 'valeur_point_max', 'classement_max'
313 )
314 .extra(
315 tables=['rh_tauxchange'],
316 where=[
317 'rh_tauxchange.annee = %s',
318 'rh_tauxchange.devise = rh_devise.id'
319 ],
320 params=[annee],
321 select={
322 'taux_change': 'rh_tauxchange.taux'
323 }
324 )
325 )
326 postes_par_id = dict((poste.id, poste) for poste in postes)
327 jours_vacants_date = dict(
328 (poste.id, max(today, poste.date_debut or today))
329 for poste in postes
330 )
331 jours_vacants = defaultdict(lambda: 0)
332 for dossier in rh.Dossier.objects.actifs(annee=annee) \
333 .order_by('date_debut'):
334 if dossier.poste_id not in jours_vacants_date:
335 continue
336 derniere_date = jours_vacants_date[dossier.poste_id]
337 if dossier.date_debut is not None:
338 jours_vacants[dossier.poste_id] += max((
339 dossier.date_debut - derniere_date
340 ).days - 1, 0)
341 jours_vacants_date[dossier.poste_id] = max(
342 min(dossier.date_fin or fin_annee, fin_annee),
343 derniere_date
48c0abfe
JPC
344 )
345
fc62be5d
EMS
346 # Ajouter les lignes des postes vacants au rapport
347 for poste_id, jours in jours_vacants.iteritems():
348 if jours == 0:
349 continue
350 poste = postes_par_id[poste_id]
351 if poste.valeur_point_max and poste.classement_max \
352 and poste.classement_max.coefficient and poste.regime_travail:
353 salaire_theorique = (Decimal(str(
354 poste.valeur_point_max.valeur *
355 poste.classement_max.coefficient
356 )) * poste.regime_travail / 100).quantize(TWOPLACES)
357 else:
358 salaire_theorique = None
df37184c 359
fc62be5d
EMS
360 local_expatrie = '/'.join(
361 (['L'] if poste.local else []) +
362 (['E'] if poste.expatrie else [])
363 )
df37184c 364
fc62be5d
EMS
365 salaire = (
366 poste.salaire_max * jours / jours_annee *
367 poste.regime_travail / 100
368 ).quantize(TWOPLACES)
369 indemnites = (
370 poste.indemn_max * jours / jours_annee *
371 poste.regime_travail / 100
372 ).quantize(TWOPLACES)
373 charges = (
374 poste.autre_max * jours / jours_annee *
375 poste.regime_travail / 100
376 ).quantize(TWOPLACES)
377 masse_salariale = salaire + indemnites + charges
378 masse_salariale_eur = (
379 masse_salariale * Decimal(str(poste.taux_change))
380 ).quantize(TWOPLACES)
381 masse_salariale_totale += masse_salariale_eur
382
383 lignes.append({
384 'poste': poste,
385 'regime_travail': poste.regime_travail,
386 'local_expatrie': local_expatrie,
387 'jours': jours,
388 'devise': poste.devise_max and poste.devise_max.code,
389 'valeur_point': (
390 poste.valeur_point_max and poste.valeur_point_max.valeur
391 ),
392 'valeur_point_devise': (
393 poste.valeur_point_max and \
394 poste.valeur_point_max.devise.code
395 ),
396 'salaire_theorique': salaire_theorique,
397 'salaire_de_base': salaire,
398 'total_indemnites': indemnites,
399 'total_charges': charges,
400 'masse_salariale': masse_salariale,
401 'masse_salariale_eur': masse_salariale_eur
402 })
403 if 'ods' in request.GET:
404 doc = ods.masse_salariale(
405 lignes=lignes,
406 annee=annee,
407 titres_traitements=titres_traitements,
408 titres_indemnites=titres_indemnites,
409 titres_primes=titres_primes,
410 titres_charges=titres_charges,
411 masse_salariale_totale=masse_salariale_totale
412 )
413 response = HttpResponse(
414 mimetype='vnd.oasis.opendocument.spreadsheet'
415 )
416 response['Content-Disposition'] = 'filename=masse-salariale.ods'
417 doc.write(response)
418 return response
419 else:
420 return render(request, 'rh/rapports/masse_salariale.html', {
421 'form': form,
422 'titres_traitements': titres_traitements,
423 'titres_indemnites': titres_indemnites,
424 'titres_primes': titres_primes,
425 'titres_charges': titres_charges,
426 'masse_salariale_totale': masse_salariale_totale,
427 'lignes': lignes,
428 'annee': annee
429 })
430 return render(request, 'rh/rapports/masse_salariale.html', {
431 'form': form
432 })
98d6eb6c
JPC
433
434
435@login_required
436@drh_or_admin_required
6b7cf654 437def rapports_employes_sans_contrat(request):
1a7b9e36 438 lookup_params = get_lookup_params(request)
c8b22fd1 439
1a7b9e36
DB
440 # contrats échus
441 contrats_echus = rh.Contrat.objects.filter(
442 date_fin__lt=date.today()
443 ).filter(
444 **lookup_params
445 )
446
447 # dossiers en cours sans contrat
448 dossiers_sans_contrat = rh.Dossier.objects.filter(
449 Q(date_fin=None) | Q(date_fin__gt=date.today()),
450 ).exclude(
451 date_debut__gt=date.today()
452 ).filter(
453 rh_contrats__in=contrats_echus
454 )
10a11035 455
1a7b9e36 456 # employés sans contrat
10a11035
EMS
457 employes = rh.Employe.objects \
458 .filter(rh_dossiers__in=dossiers_sans_contrat).distinct()
c8b22fd1 459 if 'o' in request.GET:
1a7b9e36 460 employes = employes.order_by(
00ca4d9f
EMS
461 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
462 )
c8b22fd1 463
6b7cf654 464 # affichage
c8b22fd1 465 headers = [
dd18f6e3 466 ("id", u"#"),
1a7b9e36
DB
467 ("nom", u"Employé"),
468 ("dossier", u"Dossier : Poste"),
dd18f6e3
DB
469 ("dossier_date_debut", u"Début dossier"),
470 ("dossier_date_fin", u"Fin dossier"),
c8b22fd1 471 ]
00ca4d9f
EMS
472 h = SortHeaders(
473 request, headers, order_field_type="ot", order_field="o",
10a11035 474 not_sortable=('dossier', 'dossier_date_debut', 'dossier_date_fin')
00ca4d9f 475 )
c8b22fd1
JPC
476
477 c = {
478 'title': u'Rapport des employés sans contrat',
479 'employes': employes,
10a11035 480 'dossiers_sans_contrat': dossiers_sans_contrat,
c8b22fd1
JPC
481 'headers': list(h.headers()),
482 }
483
890b80e7 484 return render(request, 'rh/rapports/employes_sans_contrat.html', c)
00ca4d9f 485
c8b22fd1
JPC
486
487@login_required
488@drh_or_admin_required
857b5c24
JPC
489def rapports_postes_modelisation(request):
490 c = {}
491 data = []
3c1ba807 492
7bf28694
EMS
493 for categorie in rh.CategorieEmploi.objects.all():
494 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
3c1ba807
JPC
495 data_types = []
496 for t in types.all():
497 postes = rh.Poste.objects.filter(type_poste=t)
498 data_types.append({
499 'num_postes': postes.count(),
500 'postes': postes.all(),
7bf28694 501 'type': categorie,
857b5c24 502 })
3c1ba807 503
857b5c24 504 data.append({
7bf28694 505 'categorie': categorie,
3c1ba807 506 'nb_types': types.count(),
00ca4d9f
EMS
507 'types': data_types
508 })
857b5c24
JPC
509
510 c['data'] = data
511
890b80e7 512 return render(request, 'rh/rapports/postes_modelisation.html', c)
857b5c24
JPC
513
514
515@login_required
516@drh_or_admin_required
783e077a
JPC
517def rapports_postes_implantation(request):
518 c = {}
519 data = []
520 for r in ref.Region.objects.all():
521 implantations = []
522 for i in ref.Implantation.objects.filter(region=r):
523 implantations.append({
524 'implantation': i,
525 'postes': rh.Poste.objects.filter(implantation=i),
526 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
527 })
528 data.append({
529 'region': r,
530 'implantations': implantations
531 })
532
533 c['data'] = data
534
890b80e7 535 return render(request, 'rh/rapports/postes_implantation.html', c)
783e077a
JPC
536
537
538@login_required
539@drh_or_admin_required
540def rapports_postes_service(request):
541 c = {}
9988aafb
JPC
542 data = []
543 for s in rh.Service.objects.all():
544 postes = rh.Poste.objects.filter(service=s).all()
545 num_postes = rh.Poste.objects.filter(service=s).count()
22343fe7 546 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
783e077a 547
9988aafb 548 c['data'] = data
890b80e7 549 return render(request, 'rh/rapports/postes_service.html', c)
32373f2e 550
10a11035 551
32373f2e 552@region_protected(rh.Dossier)
3ebc0952 553def dossier_apercu(request, dossier_id):
baf9b78c 554 d = get_object_or_404(rh.Dossier, pk=dossier_id)
3ebc0952 555 c = {
838bc59d 556 'title': u"Dossier %s" % (d, ),
00ca4d9f
EMS
557 'is_popup': request.GET.get('_popup', False),
558 'dossier': d,
559 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
560 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
561 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
abf91905 562 'media_url': settings.PRIVE_MEDIA_URL,
3ebc0952 563 }
890b80e7 564 return render(request, 'admin/rh/dossier/apercu.html', c)
00ca4d9f 565
4a1f2ece 566
fbe60a9d
OL
567@region_protected(rh.Poste)
568def poste_apercu(request, poste_id):
c8cb18e3 569 p = get_object_or_404(rh.Poste, pk=poste_id)
fbe60a9d 570 c = {
c8cb18e3 571 'title': u"Poste %s" % (p, ),
00ca4d9f
EMS
572 'is_popup': request.GET.get('_popup', False),
573 'poste': p,
574 'financements': (
575 rh.PosteFinancement.objects.filter(poste=poste_id).all()
576 ),
577 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
578 'dossiers': (
579 rh.Dossier.objects.filter(poste=poste_id)
580 .order_by("-date_debut").all()
581 ),
582 'comparaisons': (
583 rh.PosteComparaison.objects.filter(poste=poste_id).all()
584 ),
585 'commentaires': (
586 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
587 ),
abf91905 588 'media_url': settings.PRIVE_MEDIA_URL,
fbe60a9d 589 }
890b80e7 590 return render(request, 'admin/rh/poste/apercu.html', c)
00ca4d9f 591
fbe60a9d 592
4a1f2ece
OL
593def employe_apercu(request, employe_id):
594 employe = get_object_or_404(rh.Employe, pk=employe_id)
fbe60a9d 595 user_groups = request.user.groups.all()
fbe60a9d
OL
596 dossiers = None
597
598 if request.user.is_superuser or \
599 grp_drh in user_groups:
600 q = Q(employe=employe)
601 if grp_correspondants_rh in user_groups:
00ca4d9f
EMS
602 regions = [
603 d.poste.implantation.region for d in employe.rh_dossiers.all()
604 ]
fbe60a9d 605 q = Q(employe=employe) & Q(implantation__region__in=regions)
22343fe7
OL
606
607 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
608
4a1f2ece 609 c = {
ce740bb5 610 'title': u"Employe %s" % (employe, ),
00ca4d9f
EMS
611 'is_popup': request.GET.get('_popup', False),
612 'employe': employe,
613 'dossiers': dossiers,
abf91905 614 'media_url': settings.PRIVE_MEDIA_URL,
4a1f2ece 615 }
890b80e7 616 return render(request, 'admin/rh/employe/apercu.html', c)
00ca4d9f 617
150d83ec 618
77bd83d1
JPC
619@login_required
620@drh_or_admin_required
56264a85 621def organigrammes_employe(request, id, level="all"):
08faf06e 622 poste = get_object_or_404(rh.Poste, pk=id)
00ca4d9f
EMS
623 dossiers_by_poste = dict(
624 (d.poste_id, d)
625 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
626 )
5c0f1778
JPC
627 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
628
629 e = dossiers_by_poste[poste.id].employe
630 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
58014aec 631 graph = pgv.AGraph()
5c0f1778 632
f187a10f 633 if rh.Poste.objects.filter(responsable=poste).count() > 0:
58014aec
JPC
634 postes_handle = [poste]
635 while postes_handle:
00ca4d9f
EMS
636 postes_handle = rh.Poste.objects.select_related('implantation') \
637 .filter(
638 Q(date_fin__gt=date.today()) | Q(date_fin=None),
639 Q(date_debut__lt=date.today()) | Q(date_debut=None),
640 responsable__in=postes_handle
641 ).exclude(supprime=True).exclude(responsable=None).all()
32373f2e 642
58014aec
JPC
643 for p in postes_handle:
644 if p.responsable_id != p.id:
00ca4d9f
EMS
645 graph.add_edge(
646 dossiers_by_poste[p.responsable_id].poste_id, p.id
647 )
13ad5ad5 648
f187a10f 649 else:
5c0f1778
JPC
650 graph.add_node(poste.id)
651
4afec2e1
JPC
652 if level != "all":
653 postes_niveau = [poste.id]
654 for niveau in range(int(level)):
00ca4d9f
EMS
655 postes_niveau = [
656 p.id for p in
657 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
658 ]
4afec2e1
JPC
659
660 while postes_niveau:
00ca4d9f
EMS
661 postes_niveau = [
662 p.id for p in
663 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
664 ]
4afec2e1
JPC
665 if postes_niveau:
666 for p in postes_niveau:
667 if graph.has_node(p):
4cc81304 668 graph.delete_node(p)
4afec2e1 669
58014aec 670 a = graph
4afec2e1 671 a.name = name.encode('ascii', 'xmlcharrefreplace')
5c0f1778
JPC
672
673 poste_remontant = poste
674 while poste_remontant.responsable_id:
675 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
676 poste_remontant = poste_remontant.responsable
677
678 rh_graph.bind_poste_to_graph(a, postes_by_id)
679 #a.graph_attr['normalize'] = True
680 #a.graph_attr['level'] = 2
681 a.layout(prog='dot')
682
683 svg = a.draw(format='svg')
684
685 c = {
686 'svg': svg
687 }
a251ac8d
JPC
688
689 if 'forcer' in request.GET:
890b80e7 690 response = HttpResponse(svg, content_type='image/svg+xml')
00ca4d9f
EMS
691 response['Content-Disposition'] = \
692 'attachment; filename=organigramme.svg'
a251ac8d
JPC
693 return response
694
890b80e7
DB
695 return render(request, 'rh/organigrammes/employe.html', c,
696 content_type="image/svg+xml"
00ca4d9f 697 )
5c0f1778
JPC
698
699
700@login_required
701@drh_or_admin_required
702def organigrammes_service(request, id):
5c0f1778 703 service = get_object_or_404(rh.Service, pk=id)
82af5c19
JPC
704 svg = rh_graph.organigramme_postes_cluster( \
705 cluster_filter={"service": service}, \
706 titre=u"Organigramme du service %s" % service.nom,
707 cluster_titre=service.nom)
5c0f1778 708
82af5c19
JPC
709 c = {
710 'svg': svg
711 }
5c0f1778 712
10a11035 713 return render(request, 'rh/organigrammes/vide.html', c,
890b80e7 714 content_type="image/svg+xml"
00ca4d9f
EMS
715 )
716
5c0f1778 717
82af5c19
JPC
718@login_required
719@drh_or_admin_required
720def organigrammes_implantation(request, id):
82af5c19
JPC
721 implantation = get_object_or_404(ref.Implantation, pk=id)
722 svg = rh_graph.organigramme_postes_cluster( \
723 cluster_filter={"implantation": implantation}, \
724 titre=u"Organigramme de l'implantation %s" % implantation.nom,
725 cluster_titre=implantation.nom)
5c0f1778
JPC
726
727 c = {
728 'svg': svg
729 }
730
10a11035 731 return render(request, 'rh/organigrammes/vide.html', c,
890b80e7 732 content_type="image/svg+xml"
00ca4d9f
EMS
733 )
734
5c0f1778 735
9da4c195
JPC
736@login_required
737@drh_or_admin_required
738def organigrammes_region(request, id):
9da4c195
JPC
739 region = get_object_or_404(ref.Region, pk=id)
740 svg = rh_graph.organigramme_postes_cluster( \
741 cluster_filter={"implantation__region": region}, \
742 titre=u"Organigramme du bureau de %s" % region.nom,
743 cluster_titre=region.nom)
744
745 c = {
746 'svg': svg
747 }
748
10a11035
EMS
749 return render(request,
750 'rh/organigrammes/vide.html', c,
890b80e7 751 content_type="image/svg+xml"
00ca4d9f 752 )