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