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