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