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