5ad33bac0c59c62a4d2d3ec293a8535ad1c02737
[auf_rh_dae.git] / project / rh / views.py
1 # -*- encoding: utf-8 -*-
2
3 import urllib
4 from datetime import date
5 from itertools import izip
6 import StringIO
7
8 import pygraphviz as pgv
9
10 from django import forms
11 from django.conf import settings
12 from django.contrib.auth.decorators import login_required
13 from django.core.servers.basehttp import FileWrapper
14 from django.core.urlresolvers import reverse
15 from django.db.models import Q
16 from django.http import HttpResponse
17 from django.shortcuts import render, get_object_or_404
18
19 from auf.django.references import models as ref
20
21 from project import groups
22 from project.decorators import drh_or_admin_required, in_one_of_group
23 from project.decorators import redirect_interdiction
24 from project.decorators import region_protected
25 from project.groups import get_employe_from_user
26
27 from project.rh import models as rh
28 from project.rh import graph as rh_graph
29 from project.rh.change_list import RechercheTemporelle
30 from project.rh.lib import calc_remun, get_lookup_params
31 from project.rh.masse_salariale import MasseSalariale
32 from project.rh.templatetags.rapports import SortHeaders
33
34
35 @login_required
36 @drh_or_admin_required
37 def profil(request):
38 """Profil personnel de l'employé - éditable"""
39 employe = get_employe_from_user(user)
40 c = {
41 'user': request.user,
42 'employe': employe,
43 }
44 return render(request, 'rh/profil.html', c)
45
46
47 @login_required
48 @drh_or_admin_required
49 def employes_liste(request):
50 """Liste des employés."""
51 today = date.today()
52 employes = rh.Employe.objects \
53 .exclude(dossiers__date_debut__gt=today) \
54 .exclude(dossiers__date_fin__lt=today) \
55 .order_by('nom')
56 c = {
57 'user': request.user,
58 'employes': employes,
59 }
60 return render(request, 'rh/employes_liste.html', c)
61
62
63 @login_required
64 @drh_or_admin_required
65 def employe(request, id):
66 """Information publique sur un employé."""
67 try:
68 employe = rh.Employe.objects.get(pk=id)
69 except:
70 employe = rh.Employe.objects.none()
71 c = {
72 'user': request.user,
73 'employe': employe,
74 }
75 return render(request, 'rh/employe.html', c)
76
77
78 @login_required
79 @in_one_of_group((groups.grp_correspondants_rh,
80 groups.grp_administrateurs,
81 groups.grp_directeurs_bureau,
82 groups.grp_drh,
83 groups.grp_drh2))
84 def rapports_contrat(request):
85 if 'HTTP_REFERER' in request.META.keys():
86 referer = request.META['HTTP_REFERER']
87 referer = "/".join(referer.split('/')[3:])
88 referer = "/%s" % referer.split('?')[0]
89 if referer != reverse('rhr_contrats'):
90 params = request.GET.copy()
91 params.update({'statut': 'Actif'})
92 request.GET = params
93
94 lookup_params = get_lookup_params(request)
95
96 contrats = rh.Contrat.objects.select_related(
97 'dossier', 'dossier__poste', 'dossier__poste__implantation',
98 'type_contrat', 'dossier__employe'
99 )
100
101 cl = RechercheTemporelle(dict(request.GET.items()), rh.Contrat)
102 lookup_params = cl.purge_params(lookup_params)
103 q_temporel = cl.get_q_temporel(contrats)
104 q = Q(**lookup_params) & q_temporel
105 user_groups = request.user.groups.all()
106 if groups.grp_correspondants_rh in user_groups or\
107 groups.grp_administrateurs in user_groups or\
108 groups.grp_directeurs_bureau in user_groups:
109 employe = get_employe_from_user(request.user)
110 q = q & Q(dossier__poste__implantation__region=employe.implantation.region)
111
112 contrats = contrats.filter(q).exclude(dossier__employe__supprime=1)
113
114 if 'o' in request.GET:
115 contrats = contrats.order_by(
116 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
117 )
118
119 employes = set([c.dossier.employe_id for c in contrats])
120
121 headers = [
122 ("dossier__employe__id", u"# de l'employé"),
123 ("dossier__employe__nom", u"Employé"),
124 ("dossier__poste__nom", u"Poste"),
125 ("dossier__poste__implantation__region", u"Région"),
126 ("dossier__poste__implantation", u"Implantation"),
127 ("type_contrat__nom", u"Type de contrat"),
128 ("dossier__statut_residence", u"Statut de résidence"),
129 ("date_debut", u"Date début"),
130 ("date_fin", u"Date fin"),
131 ]
132 h = SortHeaders(request, headers, order_field_type="ot", order_field="o")
133
134 c = {
135 'cl': cl,
136 'title': 'Rapport des contrats',
137 'contrats': contrats,
138 'count': len(contrats),
139 'count_employe': len(employes),
140 'headers': list(h.headers()),
141 }
142
143 return render(request, 'rh/rapports/contrats.html', c)
144
145
146 @login_required
147 @in_one_of_group((groups.grp_correspondants_rh,
148 groups.grp_administrateurs,
149 groups.grp_directeurs_bureau,
150 groups.grp_drh,
151 groups.grp_drh2))
152 def rapports_masse_salariale(request):
153
154 class RechercheTemporelle(forms.Form):
155 remunerations = rh.Remuneration.objects.exclude(date_debut=None)
156 if len(remunerations) > 0:
157 annee_debut = remunerations.order_by('date_debut')[0].date_debut.year
158 else:
159 annee_debut = 1982
160 CHOICE_ANNEES = range(annee_debut, date.today().year + 1)
161
162 annee = forms.CharField(
163 initial=date.today().year,
164 widget=forms.Select(
165 choices=((a, a) for a in reversed(CHOICE_ANNEES))
166 )
167 )
168
169 user_groups = request.user.groups.all()
170 if groups.grp_correspondants_rh in user_groups or\
171 groups.grp_administrateurs in user_groups or\
172 groups.grp_directeurs_bureau in user_groups:
173 employe = get_employe_from_user(request.user)
174 regions = ref.Region.objects.filter(id=employe.implantation.region.id)
175 implantations = ref.Implantation.objects.filter(region=employe.implantation.region)
176 else:
177 regions = ref.Region.objects.all()
178 implantations = ref.Implantation.objects.all()
179
180 region = forms.CharField(
181 widget=forms.Select(choices=[('', '')] +
182 [(i.id, i) for i in regions]
183 )
184 )
185
186 implantation = forms.CharField(
187 widget=forms.Select(choices=[('', '')] +
188 [(i.id, i) for i in implantations]
189 )
190 )
191
192 #date_debut = forms.DateField(widget=adminwidgets.AdminDateWidget)
193 #date_fin = forms.DateField(widget=adminwidgets.AdminDateWidget)
194
195 form = RechercheTemporelle(request.GET)
196 get_filtre = [
197 (k, v) for k, v in request.GET.items()
198 if k not in ('date_debut', 'date_fin', 'implantation')
199 ]
200 query_string = urllib.urlencode(get_filtre)
201
202 date_debut = None
203 date_fin = None
204 if request.GET.get('annee', None):
205 date_debut = "01-01-%s" % request.GET.get('annee', None)
206 date_fin = "31-12-%s" % request.GET.get('annee', None)
207
208 implantation = request.GET.get('implantation')
209 region = request.GET.get('region')
210
211 custom_filter = {}
212 if implantation:
213 custom_filter['dossier__poste__implantation'] = implantation
214 if region:
215 custom_filter['dossier__poste__implantation__region'] = region
216
217 # on force la région dans tous les cas pour les membres de ce groupe
218 user_groups = request.user.groups.all()
219 if groups.grp_correspondants_rh in user_groups or\
220 groups.grp_administrateurs in user_groups or\
221 groups.grp_directeurs_bureau in user_groups:
222 employe = get_employe_from_user(request.user)
223 custom_filter['dossier__poste__implantation__region'] = employe.implantation.region.id
224
225
226 c = {
227 'title': 'Rapport de masse salariale',
228 'form': form,
229 'headers': [],
230 'query_string': query_string,
231 }
232 if date_debut or date_fin:
233 masse = MasseSalariale(date_debut, date_fin, custom_filter,
234 request.GET.get('ne_pas_grouper', False))
235 if masse.rapport:
236 if request.GET.get('ods'):
237 for h in (
238 h for h in masse.headers if 'background-color' in h[2]
239 ):
240 del h[2]['background-color']
241 masse.ods()
242 output = StringIO.StringIO()
243 masse.doc.save(output)
244 output.seek(0)
245
246 response = HttpResponse(
247 FileWrapper(output),
248 content_type=(
249 'application/vnd.oasis.opendocument.spreadsheet'
250 )
251 )
252 response['Content-Disposition'] = \
253 'attachment; filename=Masse Salariale %s.ods' % \
254 masse.annee
255 return response
256 else:
257 c['rapport'] = masse.rapport
258 c['header_keys'] = [h[0] for h in masse.headers]
259 #on enleve le background pour le header
260 for h in (
261 h for h in masse.headers if 'background-color' in h[2]
262 ):
263 h[2]['background'] = 'none'
264 h = SortHeaders(request, masse.headers, order_field_type="ot",
265 not_sortable=c['header_keys'], order_field="o")
266 c['headers'] = list(h.headers())
267 for key, nom, opts in masse.headers:
268 c['headers']
269 c['total'] = masse.grand_totaux[0]
270 c['total_euro'] = masse.grand_totaux[1]
271 c['colspan'] = len(c['header_keys']) - 1
272 get_filtre.append(('ods', True))
273 query_string = urllib.urlencode(get_filtre)
274 c['url_ods'] = "%s?%s" % (
275 reverse('rhr_masse_salariale'), query_string)
276
277 return render(request, 'rh/rapports/masse_salariale.html', c)
278
279
280 @login_required
281 @in_one_of_group((groups.grp_correspondants_rh,
282 groups.grp_administrateurs,
283 groups.grp_directeurs_bureau,
284 groups.grp_drh,
285 groups.grp_drh2))
286 def rapports_employes_sans_contrat(request):
287 lookup_params = get_lookup_params(request)
288
289 # régionalisation
290 user_groups = request.user.groups.all()
291 q_region = Q()
292 if groups.grp_correspondants_rh in user_groups or\
293 groups.grp_administrateurs in user_groups or\
294 groups.grp_directeurs_bureau in user_groups:
295 employe = get_employe_from_user(request.user)
296 q_region = Q(poste__implantation__region=employe.implantation.region)
297
298 # contrats échus
299 contrats_echus = rh.Contrat.objects.filter(
300 date_fin__lt=date.today()
301 ).filter(
302 **lookup_params
303 )
304
305 # dossiers en cours sans contrat
306 dossiers_sans_contrat = rh.Dossier.objects.filter(q_region & (
307 Q(date_fin=None) | Q(date_fin__gt=date.today())),
308 ).exclude(
309 date_debut__gt=date.today()
310 ).filter(
311 rh_contrats__in=contrats_echus
312 )
313
314 # employés sans contrat
315 employes = rh.Employe.objects.filter(rh_dossiers__in=dossiers_sans_contrat).distinct()
316 if 'o' in request.GET:
317 employes = employes.order_by(
318 ('-' if request.GET.get('ot') == "desc" else '') + request.GET['o']
319 )
320
321 # affichage
322 headers = [
323 ("id", u"# de l'employé"),
324 ("nom", u"Employé"),
325 ("dossier", u"Dossier : Poste"),
326 ("dossier_date_debut", u"Début occupation poste"),
327 ("dossier_date_fin", u"Fin occupation poste"),
328 ]
329 h = SortHeaders(
330 request, headers, order_field_type="ot", order_field="o",
331 not_sortable=('dossier','dossier_date_debut','dossier_date_fin',)
332 )
333
334 c = {
335 'title': u'Rapport des employés sans contrat',
336 'employes': employes,
337 'dossiers_sans_contrat':dossiers_sans_contrat,
338 'headers': list(h.headers()),
339 }
340
341 return render(request, 'rh/rapports/employes_sans_contrat.html', c)
342
343
344 @login_required
345 @drh_or_admin_required
346 def rapports_postes_modelisation(request):
347 c = {}
348 data = []
349
350 for categorie in rh.CategorieEmploi.objects.all():
351 types = rh.TypePoste.objects.filter(categorie_emploi=categorie)
352 data_types = []
353 for t in types.all():
354 postes = rh.Poste.objects.filter(type_poste=t)
355 data_types.append({
356 'num_postes': postes.count(),
357 'postes': postes.all(),
358 'type': categorie,
359 })
360
361 data.append({
362 'categorie': categorie,
363 'nb_types': types.count(),
364 'types': data_types
365 })
366
367 c['data'] = data
368
369 return render(request, 'rh/rapports/postes_modelisation.html', c)
370
371
372 @login_required
373 @drh_or_admin_required
374 def rapports_postes_implantation(request):
375 c = {}
376 data = []
377 for r in ref.Region.objects.all():
378 implantations = []
379 for i in ref.Implantation.objects.filter(region=r):
380 implantations.append({
381 'implantation': i,
382 'postes': rh.Poste.objects.filter(implantation=i),
383 'num_postes': rh.Poste.objects.filter(implantation=i).count(),
384 })
385 data.append({
386 'region': r,
387 'implantations': implantations
388 })
389
390 c['data'] = data
391
392 return render(request, 'rh/rapports/postes_implantation.html', c)
393
394
395 @login_required
396 @drh_or_admin_required
397 def rapports_postes_service(request):
398 c = {}
399 data = []
400 for s in rh.Service.objects.all():
401 postes = rh.Poste.objects.filter(service=s).all()
402 num_postes = rh.Poste.objects.filter(service=s).count()
403 data.append({'service': s, 'num_postes': num_postes, 'postes': postes})
404
405 c['data'] = data
406 return render(request, 'rh/rapports/postes_service.html', c)
407
408 @region_protected(rh.Dossier)
409 def dossier_apercu(request, dossier_id):
410 d = get_object_or_404(rh.Dossier, pk=dossier_id)
411 c = {
412 'title': u"Dossier %s" % (d, ),
413 'is_popup': request.GET.get('_popup', False),
414 'dossier': d,
415 'pieces': rh.DossierPiece.objects.filter(dossier__exact=d),
416 'contrats': rh.Contrat.objects.filter(dossier__exact=d),
417 'commentaires': rh.DossierCommentaire.objects.filter(dossier=d).all(),
418 'media_url': settings.PRIVE_MEDIA_URL,
419 }
420 return render(request, 'admin/rh/dossier/apercu.html', c)
421
422
423 @region_protected(rh.Poste)
424 def poste_apercu(request, poste_id):
425 p = get_object_or_404(rh.Poste, pk=poste_id)
426 c = {
427 'title': u"Poste %s" % (p, ),
428 'is_popup': request.GET.get('_popup', False),
429 'poste': p,
430 'financements': (
431 rh.PosteFinancement.objects.filter(poste=poste_id).all()
432 ),
433 'pieces': rh.PostePiece.objects.filter(poste=poste_id).all(),
434 'dossiers': (
435 rh.Dossier.objects.filter(poste=poste_id)
436 .order_by("-date_debut").all()
437 ),
438 'comparaisons': (
439 rh.PosteComparaison.objects.filter(poste=poste_id).all()
440 ),
441 'commentaires': (
442 rh.PosteCommentaire.objects.filter(poste=poste_id).all()
443 ),
444 'media_url': settings.PRIVE_MEDIA_URL,
445 }
446 return render(request, 'admin/rh/poste/apercu.html', c)
447
448 @region_protected(rh.Employe)
449 def employe_apercu(request, employe_id):
450 employe = get_object_or_404(rh.Employe, pk=employe_id)
451 user_groups = request.user.groups.all()
452 dossiers = None
453
454 if in_drh_or_admin(request.user):
455 q = Q(employe=employe)
456
457 if groups.grp_correspondants_rh in user_groups or\
458 groups.grp_administrateurs in user_groups or\
459 groups.grp_directeurs_bureau in user_groups:
460 employe_connecte = get_employe_from_user(request.user)
461 q = Q(employe=employe) & Q(poste__implantation__region=employe_connecte.implantation.region)
462
463 dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
464
465 c = {
466 'title': u"Employe %s" % (employe, ),
467 'is_popup': request.GET.get('_popup', False),
468 'employe': employe,
469 'dossiers': dossiers,
470 'media_url': settings.PRIVE_MEDIA_URL,
471 }
472 return render(request, 'admin/rh/employe/apercu.html', c)
473
474
475 @login_required
476 @in_one_of_group((groups.grp_correspondants_rh,
477 groups.grp_administrateurs,
478 groups.grp_directeurs_bureau,
479 groups.grp_drh,
480 groups.grp_drh2))
481 def organigrammes_employe(request, id, level="all"):
482
483 poste = get_object_or_404(rh.Poste, pk=id)
484 dossiers_by_poste = dict(
485 (d.poste_id, d)
486 for d in rh.Dossier.objects.select_related('employe', 'poste').all()
487 )
488 postes_by_id = dict((p.id, p) for p in rh.Poste.objects.all())
489
490 e = dossiers_by_poste[poste.id].employe
491 name = u"Organigramme de [%s] %s %s" % (e.id, e.nom.upper(), e.prenom)
492 graph = pgv.AGraph()
493
494 if rh.Poste.objects.filter(responsable=poste).count() > 0:
495 postes_handle = [poste]
496 while postes_handle:
497 postes_handle = rh.Poste.objects.select_related('implantation') \
498 .filter(
499 Q(date_fin__gt=date.today()) | Q(date_fin=None),
500 Q(date_debut__lt=date.today()) | Q(date_debut=None),
501 responsable__in=postes_handle
502 ).exclude(supprime=True).exclude(responsable=None).all()
503
504 for p in postes_handle:
505 if p.responsable_id != p.id:
506 graph.add_edge(
507 dossiers_by_poste[p.responsable_id].poste_id, p.id
508 )
509
510 else:
511 graph.add_node(poste.id)
512
513 if level != "all":
514 postes_niveau = [poste.id]
515 for niveau in range(int(level)):
516 postes_niveau = [
517 p.id for p in
518 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
519 ]
520
521 while postes_niveau:
522 postes_niveau = [
523 p.id for p in
524 rh.Poste.objects.filter(responsable__in=postes_niveau).all()
525 ]
526 if postes_niveau:
527 for p in postes_niveau:
528 if graph.has_node(p):
529 graph.delete_node(p)
530
531 a = graph
532 a.name = name.encode('ascii', 'xmlcharrefreplace')
533
534 poste_remontant = poste
535 while poste_remontant.responsable_id:
536 a.add_edge(poste_remontant.responsable_id, poste_remontant.id)
537 poste_remontant = poste_remontant.responsable
538
539 rh_graph.bind_poste_to_graph(a, postes_by_id)
540 #a.graph_attr['normalize'] = True
541 #a.graph_attr['level'] = 2
542 a.layout(prog='dot')
543
544 svg = a.draw(format='svg')
545
546 c = {
547 'svg': svg
548 }
549
550 if 'forcer' in request.GET:
551 response = HttpResponse(svg, content_type='image/svg+xml')
552 response['Content-Disposition'] = \
553 'attachment; filename=organigramme.svg'
554 return response
555
556 return render(request, 'rh/organigrammes/employe.html', c,
557 content_type="image/svg+xml"
558 )
559
560
561 @login_required
562 @in_one_of_group((groups.grp_correspondants_rh,
563 groups.grp_administrateurs,
564 groups.grp_directeurs_bureau,
565 groups.grp_drh,
566 groups.grp_drh2))
567 def organigrammes_service(request, id):
568
569 service = get_object_or_404(rh.Service, pk=id)
570 svg = rh_graph.organigramme_postes_cluster( \
571 cluster_filter={"service": service}, \
572 titre=u"Organigramme du service %s" % service.nom,
573 cluster_titre=service.nom)
574
575 c = {
576 'svg': svg
577 }
578
579 return render(request, 'rh/organigrammes/vide.html', c,
580 content_type="image/svg+xml"
581 )
582
583
584 @login_required
585 @in_one_of_group((groups.grp_correspondants_rh,
586 groups.grp_administrateurs,
587 groups.grp_directeurs_bureau,
588 groups.grp_drh,
589 groups.grp_drh2))
590 def organigrammes_implantation(request, id):
591
592 implantation = get_object_or_404(ref.Implantation, pk=id)
593 svg = rh_graph.organigramme_postes_cluster( \
594 cluster_filter={"implantation": implantation}, \
595 titre=u"Organigramme de l'implantation %s" % implantation.nom,
596 cluster_titre=implantation.nom)
597
598 c = {
599 'svg': svg
600 }
601
602 return render(request, 'rh/organigrammes/vide.html', c,
603 content_type="image/svg+xml"
604 )
605
606
607 @login_required
608 @in_one_of_group((groups.grp_correspondants_rh,
609 groups.grp_administrateurs,
610 groups.grp_directeurs_bureau,
611 groups.grp_drh,
612 groups.grp_drh2))
613 def organigrammes_region(request, id):
614
615 region = get_object_or_404(ref.Region, pk=id)
616 svg = rh_graph.organigramme_postes_cluster( \
617 cluster_filter={"implantation__region": region}, \
618 titre=u"Organigramme du bureau de %s" % region.nom,
619 cluster_titre=region.nom)
620
621 c = {
622 'svg': svg
623 }
624
625 return render(request,
626 'rh/organigrammes/vide.html', c,
627 content_type="image/svg+xml"
628 )