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