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