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