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