[#3068] Correction au calcul des postes vacants
[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
5db1c5a3
DB
133def 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)
a623d1b2
EMS
158 ).distinct()
159
5db1c5a3
DB
160 # employés sans contrat ou contrats échus
161 employes = rh.Employe.objects.filter(rh_dossiers__in=dossiers) \
162 .distinct().count()
a623d1b2 163
5db1c5a3
DB
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
98d6eb6c 199def rapports_masse_salariale(request):
fc62be5d
EMS
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(
fc62be5d 221 select={
471fd30f
EMS
222 'valeur_point': (
223 'SELECT valeur FROM rh_valeurpoint '
224 'WHERE annee = %d '
225 'AND implantation = ref_implantation.id' % annee
226 ),
227 'valeur_point_devise': (
228 'SELECT d.code '
229 'FROM rh_valeurpoint vp '
230 'INNER JOIN rh_devise d ON d.id = vp.devise '
231 'WHERE annee = %d '
232 'AND implantation = ref_implantation.id' % annee
233 )
fc62be5d 234 }
48c0abfe 235 )
fc62be5d
EMS
236 if region:
237 dossiers = dossiers.filter(poste__implantation__region=region)
238 if implantation:
239 dossiers = dossiers.filter(poste__implantation=implantation)
240
241 # Récupérer les rémunérations actives
242 remuns = rh.Remuneration.objects \
243 .actifs(annee=annee) \
244 .select_related('devise', 'type') \
245 .extra(
246 tables=['rh_tauxchange'],
247 where=[
248 'rh_tauxchange.annee = %s',
249 'rh_tauxchange.devise = rh_devise.id'
250 ],
251 params=[annee],
252 select={
253 'taux_change': 'rh_tauxchange.taux'
254 }
255 )
471fd30f
EMS
256 if region:
257 remuns = remuns.filter(dossier__poste__implantation__region=region)
258 if implantation:
259 remuns = remuns.filter(dossier__poste__implantation=implantation)
fc62be5d 260 remuns_par_dossier = defaultdict(list)
471fd30f 261 types_remun = set()
fc62be5d 262 for remun in remuns:
471fd30f 263 types_remun.add(remun.type_id)
fc62be5d
EMS
264 remuns_par_dossier[remun.dossier_id].append(remun)
265
266 # Récupérer les types de rémunération par nature
267 types_remun_par_nature = defaultdict(list)
268 for type in rh.TypeRemuneration.objects.all():
471fd30f
EMS
269 if type.id in types_remun:
270 types_remun_par_nature[type.nature_remuneration].append(type)
fc62be5d
EMS
271 titres_traitements = [
272 t.nom for t in types_remun_par_nature[u'Traitement']
273 ]
274 titres_indemnites = [
275 t.nom for t in types_remun_par_nature[u'Indemnité']
276 ]
277 titres_primes = [
278 t.nom for t in types_remun_par_nature[u'Accessoire']
279 ]
280 titres_charges = [
281 t.nom for t in types_remun_par_nature[u'Charges']
282 ]
df37184c 283
fc62be5d
EMS
284 # Boucler sur les dossiers et préprarer les lignes du rapport
285 lignes = []
286 masse_salariale_totale = 0
287 for dossier in dossiers:
288 debut_cette_annee = \
289 max(dossier.date_debut or debut_annee, debut_annee)
290 fin_cette_annee = \
291 min(dossier.date_fin or fin_annee, fin_annee)
292 jours = (fin_cette_annee - debut_cette_annee).days + 1
293
294 remuns = remuns_par_dossier[dossier.id]
295 devises = set(remun.devise.code for remun in remuns)
296 if len(devises) == 1:
297 devise = remuns[0].devise.code
298 montant_remun = lambda r: r.montant
299 taux_change = Decimal(str(remuns[0].taux_change))
300 else:
301 devise = 'EUR'
302 montant_remun = lambda r: (
303 r.montant * Decimal(str(r.taux_change))
48c0abfe 304 )
fc62be5d
EMS
305 taux_change = Decimal(1)
306
307 remuns_par_type = defaultdict(lambda: 0)
308 for remun in remuns:
ff9bc43d
EMS
309 if remun.type.nature_remuneration == u'Accessoire':
310 remuns_par_type[remun.type_id] += montant_remun(remun)
311 else:
312 remuns_par_type[remun.type_id] += (
313 montant_remun(remun) * ((
314 min(remun.date_fin or fin_annee, fin_annee) -
315 max(remun.date_debut or debut_annee, debut_annee)
a81bde86
EMS
316 ).days + 1) / jours_annee *
317 dossier.regime_travail / 100
ff9bc43d 318 ).quantize(TWOPLACES)
fc62be5d
EMS
319 traitements = [
320 remuns_par_type[type.id]
321 for type in types_remun_par_nature[u'Traitement']
322 ]
323 indemnites = [
324 remuns_par_type[type.id]
325 for type in types_remun_par_nature[u'Indemnité']
326 ]
327 primes = [
328 remuns_par_type[type.id]
329 for type in types_remun_par_nature[u'Accessoire']
330 ]
331 charges = [
332 remuns_par_type[type.id]
333 for type in types_remun_par_nature[u'Charges']
334 ]
335 masse_salariale = sum(remuns_par_type.values())
336 masse_salariale_eur = (
337 masse_salariale * taux_change
338 ).quantize(TWOPLACES)
339 masse_salariale_totale += masse_salariale_eur
340
341 if dossier.valeur_point and dossier.classement \
342 and dossier.classement.coefficient and dossier.regime_travail:
343 salaire_theorique = (Decimal(str(
344 dossier.valeur_point * dossier.classement.coefficient
345 )) * dossier.regime_travail / 100).quantize(TWOPLACES)
346 else:
347 salaire_theorique = None
348
349 lignes.append({
350 'dossier': dossier,
351 'poste': dossier.poste,
352 'date_debut': (
353 dossier.date_debut
354 if dossier.date_debut and dossier.date_debut.year == annee
355 else None
356 ),
357 'date_fin': (
358 dossier.date_fin
359 if dossier.date_fin and dossier.date_fin.year == annee
360 else None
361 ),
362 'jours': jours,
363 'devise': devise,
364 'valeur_point': dossier.valeur_point,
365 'valeur_point_devise': dossier.valeur_point_devise,
366 'regime_travail': dossier.regime_travail,
367 'local_expatrie': {
368 'local': 'L', 'expat': 'E'
369 }.get(dossier.statut_residence),
370 'salaire_bstg': remuns_par_type[2],
371 'salaire_bstg_eur': (
372 (remuns_par_type[2] * taux_change).quantize(TWOPLACES)
373 ),
374 'salaire_theorique': salaire_theorique,
375 'traitements': traitements,
376 'total_traitements': sum(traitements),
377 'indemnites': indemnites,
378 'total_indemnites': sum(indemnites),
379 'primes': primes,
380 'total_primes': sum(primes),
381 'charges': charges,
382 'total_charges': sum(charges),
383 'masse_salariale': masse_salariale,
384 'masse_salariale_eur': masse_salariale_eur,
385 })
00ca4d9f 386
fc62be5d
EMS
387 # Récupérer les postes actifs pour déterminer le nombre de jours
388 # vacants.
389 postes = list(
390 rh.Poste.objects.actifs(annee=annee)
391 .select_related(
392 'devise_max', 'valeur_point_max', 'classement_max'
00ca4d9f 393 )
fc62be5d
EMS
394 .extra(
395 tables=['rh_tauxchange'],
396 where=[
397 'rh_tauxchange.annee = %s',
398 'rh_tauxchange.devise = rh_devise.id'
399 ],
400 params=[annee],
401 select={
402 'taux_change': 'rh_tauxchange.taux'
403 }
48c0abfe 404 )
fc62be5d
EMS
405 )
406 postes_par_id = dict((poste.id, poste) for poste in postes)
407 jours_vacants_date = dict(
408 (poste.id, max(today, poste.date_debut or today))
409 for poste in postes
410 )
411 jours_vacants = defaultdict(lambda: 0)
412 for dossier in rh.Dossier.objects.actifs(annee=annee) \
413 .order_by('date_debut'):
414 if dossier.poste_id not in jours_vacants_date:
415 continue
416 derniere_date = jours_vacants_date[dossier.poste_id]
417 if dossier.date_debut is not None:
418 jours_vacants[dossier.poste_id] += max((
419 dossier.date_debut - derniere_date
420 ).days - 1, 0)
421 jours_vacants_date[dossier.poste_id] = max(
422 min(dossier.date_fin or fin_annee, fin_annee),
423 derniere_date
48c0abfe 424 )
24b5b57f
EMS
425 for poste_id, derniere_date in jours_vacants_date.iteritems():
426 jours_vacants[poste_id] += max((
427 min(postes_par_id[poste_id].date_fin or fin_annee, fin_annee) -
428 derniere_date
429 ).days, 0)
48c0abfe 430
fc62be5d
EMS
431 # Ajouter les lignes des postes vacants au rapport
432 for poste_id, jours in jours_vacants.iteritems():
433 if jours == 0:
434 continue
435 poste = postes_par_id[poste_id]
436 if poste.valeur_point_max and poste.classement_max \
437 and poste.classement_max.coefficient and poste.regime_travail:
438 salaire_theorique = (Decimal(str(
439 poste.valeur_point_max.valeur *
440 poste.classement_max.coefficient
441 )) * poste.regime_travail / 100).quantize(TWOPLACES)
442 else:
443 salaire_theorique = None
df37184c 444
fc62be5d
EMS
445 local_expatrie = '/'.join(
446 (['L'] if poste.local else []) +
447 (['E'] if poste.expatrie else [])
448 )
df37184c 449
fc62be5d
EMS
450 salaire = (
451 poste.salaire_max * jours / jours_annee *
452 poste.regime_travail / 100
453 ).quantize(TWOPLACES)
454 indemnites = (
455 poste.indemn_max * jours / jours_annee *
456 poste.regime_travail / 100
457 ).quantize(TWOPLACES)
458 charges = (
459 poste.autre_max * jours / jours_annee *
460 poste.regime_travail / 100
461 ).quantize(TWOPLACES)
462 masse_salariale = salaire + indemnites + charges
463 masse_salariale_eur = (
464 masse_salariale * Decimal(str(poste.taux_change))
465 ).quantize(TWOPLACES)
466 masse_salariale_totale += masse_salariale_eur
467
468 lignes.append({
469 'poste': poste,
470 'regime_travail': poste.regime_travail,
471 'local_expatrie': local_expatrie,
472 'jours': jours,
473 'devise': poste.devise_max and poste.devise_max.code,
474 'valeur_point': (
475 poste.valeur_point_max and poste.valeur_point_max.valeur
476 ),
477 'valeur_point_devise': (
478 poste.valeur_point_max and \
479 poste.valeur_point_max.devise.code
480 ),
481 'salaire_theorique': salaire_theorique,
24b5b57f
EMS
482 'traitements': [0] * len(titres_traitements),
483 'total_traitements': salaire,
484 'indemnites': [0] * len(titres_indemnites),
fc62be5d 485 'total_indemnites': indemnites,
24b5b57f
EMS
486 'primes': [0] * len(titres_primes),
487 'total_primes': 0,
488 'charges': [0] * len(titres_charges),
fc62be5d
EMS
489 'total_charges': charges,
490 'masse_salariale': masse_salariale,
491 'masse_salariale_eur': masse_salariale_eur
492 })
493 if 'ods' in request.GET:
494 doc = ods.masse_salariale(
495 lignes=lignes,
496 annee=annee,
497 titres_traitements=titres_traitements,
498 titres_indemnites=titres_indemnites,
499 titres_primes=titres_primes,
500 titres_charges=titres_charges,
501 masse_salariale_totale=masse_salariale_totale
502 )
503 response = HttpResponse(
504 mimetype='vnd.oasis.opendocument.spreadsheet'
505 )
506 response['Content-Disposition'] = 'filename=masse-salariale.ods'
507 doc.write(response)
508 return response
509 else:
510 return render(request, 'rh/rapports/masse_salariale.html', {
511 'form': form,
512 'titres_traitements': titres_traitements,
513 'titres_indemnites': titres_indemnites,
514 'titres_primes': titres_primes,
515 'titres_charges': titres_charges,
516 'masse_salariale_totale': masse_salariale_totale,
517 'lignes': lignes,
518 'annee': annee
519 })
520 return render(request, 'rh/rapports/masse_salariale.html', {
521 'form': form
522 })
98d6eb6c
JPC
523
524
525@login_required
526@drh_or_admin_required
857b5c24
JPC
527def rapports_postes_modelisation(request):
528 c = {}
529 data = []
3c1ba807 530
7bf28694
EMS
531 for categorie in rh.CategorieEmploi.objects.all():
532 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
3c1ba807
JPC
533 data_types = []
534 for t in types.all():
535 postes = rh.Poste.objects.filter(type_poste=t)
536 data_types.append({
537 'num_postes': postes.count(),
538 'postes': postes.all(),
7bf28694 539 'type': categorie,
857b5c24 540 })
3c1ba807 541
857b5c24 542 data.append({
7bf28694 543 'categorie': categorie,
3c1ba807 544 'nb_types': types.count(),
00ca4d9f
EMS
545 'types': data_types
546 })
857b5c24
JPC
547
548 c['data'] = data
549
890b80e7 550 return render(request, 'rh/rapports/postes_modelisation.html', c)
857b5c24
JPC
551
552
553@login_required
554@drh_or_admin_required
783e077a
JPC
555def rapports_postes_implantation(request):
556 c = {}
557 data = []
558 for r in ref.Region.objects.all():
559 implantations = []
560 for i in ref.Implantation.objects.filter(region=r):
561 implantations.append({
562 'implantation': i,
563 'postes': rh.Poste.objects.filter(implantation=i),
564 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
565 })
566 data.append({
567 'region': r,
568 'implantations': implantations
569 })
570
571 c['data'] = data
572
890b80e7 573 return render(request, 'rh/rapports/postes_implantation.html', c)
783e077a
JPC
574
575
576@login_required
577@drh_or_admin_required
578def rapports_postes_service(request):
579 c = {}
9988aafb
JPC
580 data = []
581 for s in rh.Service.objects.all():
582 postes = rh.Poste.objects.filter(service=s).all()
583 num_postes = rh.Poste.objects.filter(service=s).count()
22343fe7 584 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
783e077a 585
9988aafb 586 c['data'] = data
890b80e7 587 return render(request, 'rh/rapports/postes_service.html', c)
32373f2e 588
10a11035 589
32373f2e 590@region_protected(rh.Dossier)
3ebc0952 591def dossier_apercu(request, dossier_id):
baf9b78c 592 d = get_object_or_404(rh.Dossier, pk=dossier_id)
3ebc0952 593 c = {
838bc59d 594 'title': u"Dossier %s" % (d, ),
00ca4d9f
EMS
595 'is_popup': request.GET.get('_popup', False),
596 'dossier': d,
597 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
598 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
599 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
abf91905 600 'media_url': settings.PRIVE_MEDIA_URL,
3ebc0952 601 }
890b80e7 602 return render(request, 'admin/rh/dossier/apercu.html', c)
00ca4d9f 603
4a1f2ece 604
fbe60a9d
OL
605@region_protected(rh.Poste)
606def poste_apercu(request, poste_id):
c8cb18e3 607 p = get_object_or_404(rh.Poste, pk=poste_id)
fbe60a9d 608 c = {
c8cb18e3 609 'title': u"Poste %s" % (p, ),
00ca4d9f
EMS
610 'is_popup': request.GET.get('_popup', False),
611 'poste': p,
612 'financements': (
613 rh.PosteFinancement.objects.filter(poste=poste_id).all()
614 ),
615 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
616 'dossiers': (
617 rh.Dossier.objects.filter(poste=poste_id)
618 .order_by("-date_debut").all()
619 ),
620 'comparaisons': (
621 rh.PosteComparaison.objects.filter(poste=poste_id).all()
622 ),
623 'commentaires': (
624 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
625 ),
abf91905 626 'media_url': settings.PRIVE_MEDIA_URL,
fbe60a9d 627 }
890b80e7 628 return render(request, 'admin/rh/poste/apercu.html', c)
00ca4d9f 629
fbe60a9d 630
4a1f2ece
OL
631def employe_apercu(request, employe_id):
632 employe = get_object_or_404(rh.Employe, pk=employe_id)
fbe60a9d 633 user_groups = request.user.groups.all()
fbe60a9d
OL
634 dossiers = None
635
636 if request.user.is_superuser or \
637 grp_drh in user_groups:
638 q = Q(employe=employe)
639 if grp_correspondants_rh in user_groups:
00ca4d9f
EMS
640 regions = [
641 d.poste.implantation.region for d in employe.rh_dossiers.all()
642 ]
fbe60a9d 643 q = Q(employe=employe) & Q(implantation__region__in=regions)
22343fe7
OL
644
645 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
646
4a1f2ece 647 c = {
ce740bb5 648 'title': u"Employe %s" % (employe, ),
00ca4d9f
EMS
649 'is_popup': request.GET.get('_popup', False),
650 'employe': employe,
651 'dossiers': dossiers,
abf91905 652 'media_url': settings.PRIVE_MEDIA_URL,
4a1f2ece 653 }
890b80e7 654 return render(request, 'admin/rh/employe/apercu.html', c)
00ca4d9f 655
150d83ec 656
77bd83d1
JPC
657@login_required
658@drh_or_admin_required
56264a85 659def organigrammes_employe(request, id, level="all"):
08faf06e 660 poste = get_object_or_404(rh.Poste, pk=id)
00ca4d9f
EMS
661 dossiers_by_poste = dict(
662 (d.poste_id, d)
663 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
664 )
5c0f1778
JPC
665 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
666
667 e = dossiers_by_poste[poste.id].employe
668 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
58014aec 669 graph = pgv.AGraph()
5c0f1778 670
f187a10f 671 if rh.Poste.objects.filter(responsable=poste).count() > 0:
58014aec
JPC
672 postes_handle = [poste]
673 while postes_handle:
00ca4d9f
EMS
674 postes_handle = rh.Poste.objects.select_related('implantation') \
675 .filter(
676 Q(date_fin__gt=date.today()) | Q(date_fin=None),
677 Q(date_debut__lt=date.today()) | Q(date_debut=None),
678 responsable__in=postes_handle
679 ).exclude(supprime=True).exclude(responsable=None).all()
32373f2e 680
58014aec
JPC
681 for p in postes_handle:
682 if p.responsable_id != p.id:
00ca4d9f
EMS
683 graph.add_edge(
684 dossiers_by_poste[p.responsable_id].poste_id, p.id
685 )
13ad5ad5 686
f187a10f 687 else:
5c0f1778
JPC
688 graph.add_node(poste.id)
689
4afec2e1
JPC
690 if level != "all":
691 postes_niveau = [poste.id]
692 for niveau in range(int(level)):
00ca4d9f
EMS
693 postes_niveau = [
694 p.id for p in
695 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
696 ]
4afec2e1
JPC
697
698 while postes_niveau:
00ca4d9f
EMS
699 postes_niveau = [
700 p.id for p in
701 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
702 ]
4afec2e1
JPC
703 if postes_niveau:
704 for p in postes_niveau:
705 if graph.has_node(p):
4cc81304 706 graph.delete_node(p)
4afec2e1 707
58014aec 708 a = graph
4afec2e1 709 a.name = name.encode('ascii', 'xmlcharrefreplace')
5c0f1778
JPC
710
711 poste_remontant = poste
712 while poste_remontant.responsable_id:
713 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
714 poste_remontant = poste_remontant.responsable
715
716 rh_graph.bind_poste_to_graph(a, postes_by_id)
717 #a.graph_attr['normalize'] = True
718 #a.graph_attr['level'] = 2
719 a.layout(prog='dot')
720
721 svg = a.draw(format='svg')
722
723 c = {
724 'svg': svg
725 }
a251ac8d
JPC
726
727 if 'forcer' in request.GET:
890b80e7 728 response = HttpResponse(svg, content_type='image/svg+xml')
00ca4d9f
EMS
729 response['Content-Disposition'] = \
730 'attachment; filename=organigramme.svg'
a251ac8d
JPC
731 return response
732
890b80e7
DB
733 return render(request, 'rh/organigrammes/employe.html', c,
734 content_type="image/svg+xml"
00ca4d9f 735 )
5c0f1778
JPC
736
737
738@login_required
739@drh_or_admin_required
740def organigrammes_service(request, id):
5c0f1778 741 service = get_object_or_404(rh.Service, pk=id)
82af5c19
JPC
742 svg = rh_graph.organigramme_postes_cluster( \
743 cluster_filter={"service": service}, \
744 titre=u"Organigramme du service %s" % service.nom,
745 cluster_titre=service.nom)
5c0f1778 746
82af5c19
JPC
747 c = {
748 'svg': svg
749 }
5c0f1778 750
10a11035 751 return render(request, 'rh/organigrammes/vide.html', c,
890b80e7 752 content_type="image/svg+xml"
00ca4d9f
EMS
753 )
754
5c0f1778 755
82af5c19
JPC
756@login_required
757@drh_or_admin_required
758def organigrammes_implantation(request, id):
82af5c19
JPC
759 implantation = get_object_or_404(ref.Implantation, pk=id)
760 svg = rh_graph.organigramme_postes_cluster( \
761 cluster_filter={"implantation": implantation}, \
762 titre=u"Organigramme de l'implantation %s" % implantation.nom,
763 cluster_titre=implantation.nom)
5c0f1778
JPC
764
765 c = {
766 'svg': svg
767 }
768
10a11035 769 return render(request, 'rh/organigrammes/vide.html', c,
890b80e7 770 content_type="image/svg+xml"
00ca4d9f
EMS
771 )
772
5c0f1778 773
9da4c195
JPC
774@login_required
775@drh_or_admin_required
776def organigrammes_region(request, id):
9da4c195
JPC
777 region = get_object_or_404(ref.Region, pk=id)
778 svg = rh_graph.organigramme_postes_cluster( \
779 cluster_filter={"implantation__region": region}, \
780 titre=u"Organigramme du bureau de %s" % region.nom,
781 cluster_titre=region.nom)
782
783 c = {
784 'svg': svg
785 }
786
10a11035
EMS
787 return render(request,
788 'rh/organigrammes/vide.html', c,
890b80e7 789 content_type="image/svg+xml"
00ca4d9f 790 )