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