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