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