fix for 500 error
[auf_rh_dae.git] / project / dae / views.py
1 # -*- encoding: utf-8 -*-
2
3 from datetime import date, datetime
4 from dateutil.relativedelta import relativedelta
5
6 from auf.django.permissions.decorators import get_object
7 from auf.django.references import models as ref
8 from django.contrib import messages
9 from django.contrib.auth.decorators import login_required, user_passes_test
10 from django.contrib.contenttypes.models import ContentType
11 from django.core.paginator import Paginator, InvalidPage
12 from django.db.models import Q, Count
13 from django.http import Http404, HttpResponse, HttpResponseNotFound
14 from django.shortcuts import redirect, render, get_object_or_404
15 from sendfile import sendfile
16 from simplejson import dumps
17
18 from project.dae import models as dae
19 from project.dae.decorators import \
20 dae_groupe_requis, \
21 poste_dans_ma_region_ou_service, \
22 dossier_dans_ma_region_ou_service, \
23 vieux_dossier_dans_ma_region_ou_service, \
24 employe_dans_ma_region_ou_service, \
25 dossier_est_modifiable, \
26 poste_est_modifiable, get_contrat
27 from project.dae.forms import FinancementFormSet, FinancementFormSetInitial
28 from project.dae.forms import \
29 PosteComparaisonFormSet, PosteComparaisonFormSetInitial
30 from project.dae.forms import \
31 PosteWorkflowForm, PosteForm, PostePieceFormSet, \
32 DossierWorkflowForm, ChoosePosteForm, \
33 EmployeForm, DossierForm, DossierPieceForm, \
34 DossierComparaisonFormSet, RemunForm, ContratForm, DAENumeriseeForm, \
35 label_poste_display, DAEFinaliseesSearchForm, \
36 remun_formset_factory, ReadOnlyRemunFormSet
37 from project.dae.mail import send_drh_finalisation_mail
38 from project.dae.workflow import \
39 DOSSIER_ETAT_FINALISE, DOSSIER_ETAT_REGION_FINALISATION, \
40 DOSSIER_ETAT_DRH_FINALISATION, POSTE_ETAT_FINALISE
41 from project.decorators import redirect_interdiction,\
42 drh_or_admin_required,\
43 in_drh_or_admin,\
44 in_one_of_group
45 from project.rh import models as rh
46 from project import groups
47
48
49 # Helpers
50
51 def devises():
52 liste = []
53 for d in rh.Devise.objects.all():
54 annee = date.today().year
55 taux = rh.TauxChange.objects.filter(annee=annee, devise=d)
56 data = {}
57 if len(taux) == 0:
58 data['taux_euro'] = 0
59 else:
60 data['taux_euro'] = taux[0].taux
61 data['devise_code'] = d.id
62 liste.append(data)
63 return liste
64
65
66 @dae_groupe_requis
67 def index(request):
68 return render(request, 'dae/index.html', {})
69
70
71 ### POSTE
72
73 @dae_groupe_requis
74 @poste_dans_ma_region_ou_service
75 def poste_consulter(request, key):
76 source, id = key.split('-')
77 poste = get_object_or_404(dae.Poste, pk=id)
78
79 if request.POST:
80 validationForm = PosteWorkflowForm(
81 request.POST, instance=poste, request=request
82 )
83 if validationForm.is_valid():
84 validationForm.save()
85 messages.add_message(
86 request, messages.SUCCESS, "La validation a été enregistrée."
87 )
88 return redirect('dae_postes_liste')
89 else:
90 validationForm = PosteWorkflowForm(instance=poste, request=request)
91
92 comparaisons_internes = \
93 poste.dae_comparaisons_internes.ma_region_ou_service(request.user)
94 return render(request, 'dae/poste_consulter.html', {
95 'devises': devises(),
96 'poste': poste,
97 'validationForm': validationForm,
98 'comparaisons_internes': comparaisons_internes,
99 'importer': request.user.is_superuser,
100 })
101
102
103 @user_passes_test(lambda u: u.is_superuser)
104 @drh_or_admin_required
105 def poste_importer(request, id):
106 poste_dae = get_object_or_404(dae.Poste, id=id)
107 if request.method == 'POST':
108 if 'confirmer' in request.POST:
109 poste_rh = poste_dae.importer_dans_rh()
110 return redirect('admin:rh_poste_change', poste_rh.id)
111 else:
112 return redirect('poste_consulter', 'dae-' + id)
113 else:
114 c = {
115 'poste': poste_dae,
116 }
117 return render(request, 'dae/poste_importer.html', c)
118
119
120 @dae_groupe_requis
121 @poste_dans_ma_region_ou_service
122 @poste_est_modifiable
123 @in_one_of_group((groups.CORRESPONDANT_RH,
124 groups.ADMINISTRATEURS,
125 groups.DIRECTEUR_DE_BUREAU,
126 groups.DRH_NIVEAU_1,
127 groups.DRH_NIVEAU_2))
128 def poste(request, key=None):
129 """ Formulaire pour un poste.
130
131 Permet de créer ou modifier un poste. Si le poste n'existe que dans rh_v1
132 il est automatiquement copié dans dae.
133
134 """
135 if 'creer_dossier_dae' in request.GET:
136 creer_dossier_dae = True
137 else:
138 creer_dossier_dae = False
139
140 def _dupliquer_poste(poste_dae, poste_rh):
141 """
142 Recopie les fields d'un poste RH dans un poste DAE
143 avec ceux-ci précédemment crées
144 """
145 exclus = ('id', 'supprime', 'date_creation',
146 'user_creation', 'date_modification',
147 'user_modification', )
148 fields = [f for f in poste_rh._meta.fields if f.name not in exclus]
149 for field in fields:
150 setattr(poste_dae, field.name, getattr(poste_rh, field.name))
151 return poste_dae
152
153 poste, data, vars = None, dict(), dict()
154
155 # Sans key, c'est un nouveau poste
156 if key is None:
157 new = True
158 else:
159 new = False
160
161 # Type intervention
162 if 'type_intervention' in request.GET:
163 data['type_intervention'] = request.GET['type_intervention']
164 if creer_dossier_dae:
165 data['type_intervention'] = request.GET['creer_dossier_dae']
166
167 # Poste existant
168 poste_rh = None
169 if not new:
170 source, id = key.split('-')
171
172 if source == 'dae':
173 poste = get_object_or_404(dae.Poste, pk=id)
174 data['poste'] = key
175 elif source == 'rh':
176 poste_rh = get_object_or_404(rh.Poste, pk=id)
177 poste = dae.Poste(id_rh=poste_rh)
178 # Initialisation avec les valeurs du poste de rh_v1
179 poste = _dupliquer_poste(poste, poste_rh)
180 data['poste'] = 'rh-' + str(poste.id_rh_id)
181
182 # prépopuler pour la modification de poste
183 if poste_rh is not None:
184 FinancementForm = FinancementFormSetInitial
185 PosteComparaisonForm = PosteComparaisonFormSetInitial
186
187 qs_financements = poste_rh.rh_financements.all()
188 qs_comparaisons = poste_rh.rh_comparaisons_internes.all()
189 financements = [{'type': f.type, 'pourcentage': f.pourcentage,
190 'commentaire': f.commentaire} for f in qs_financements]
191 comparaisons = [{'implantation': c.implantation, 'nom': c.nom,
192 'montant': c.montant, 'devise': c.devise} for c in qs_comparaisons]
193 # formulaires normaux, avec modifications des objects FK
194 else:
195 FinancementForm = FinancementFormSet
196 PosteComparaisonForm = PosteComparaisonFormSet
197 financements = []
198 comparaisons = []
199
200 if request.POST:
201 data.update(dict(request.POST.items()))
202 form = PosteForm(data, instance=poste, request=request)
203 financementForm = FinancementForm(request.POST, instance=poste, )
204 piecesForm = PostePieceFormSet(
205 request.POST, request.FILES, instance=poste
206 )
207 comparaisons_formset = PosteComparaisonForm(
208 request.POST,
209 instance=poste,
210 )
211 if form.is_valid() and piecesForm.is_valid() and \
212 financementForm.is_valid() and comparaisons_formset.is_valid():
213 poste = form.save()
214 piecesForm.instance = poste
215 piecesForm.save()
216 financementForm.instance = poste
217 financementForm.save()
218
219 # Ne remplacer que les comparaisons de ma région
220 comparaisons = comparaisons_formset.save(commit=False)
221 for comparaison in comparaisons:
222 comparaison.poste = poste
223 comparaison.save()
224
225 for comparaison in zip(
226 comparaisons,
227 comparaisons_formset.forms):
228 cmp_dossier_id = comparaison[1].cleaned_data.get(
229 'cmp_poste', None)
230 if not cmp_dossier_id:
231 continue
232 cmp_dossier_qs = rh.Dossier.objects.filter(id=cmp_dossier_id)
233 if not cmp_dossier_qs.count() > 0:
234 continue
235 dossier = rh.Dossier.objects.get(id=cmp_dossier_qs.get().id)
236
237 # Get all remunerations for a period of 1 year,
238 # going back from either: today (if dossier has not
239 # yet ended), or from dossier's date_fin.
240 cmp_date = min(dossier.date_fin or date.today(), date.today())
241 for remuneration in _filter_remunerations(
242 dossier.remunerations().order_by('-date_debut'),
243 only_traitement=False,
244 ):
245 dae.PosteComparaisonRemuneration.objects.create(
246 poste_comparaison = comparaison[0],
247 type=remuneration.type,
248 type_revalorisation=remuneration.type_revalorisation,
249 montant=remuneration.montant,
250 devise=remuneration.devise,
251 commentaire=remuneration.commentaire,
252 date_debut=remuneration.date_debut,
253 date_fin=remuneration.date_fin,
254 )
255
256 # dans le cas d'une modification de poste de RH, on recopie les PJ
257 if poste_rh is not None:
258 for piece in poste_rh.rh_pieces.all():
259 dae.PostePiece(poste=poste, nom=piece.nom,
260 fichier=piece.fichier).save()
261 messages.add_message(
262 request, messages.SUCCESS,
263 "Le poste %s a été sauvegardé." % poste
264 )
265 if creer_dossier_dae:
266 return redirect('embauche', key='dae-%s' % poste.id)
267
268 if 'save' in request.POST:
269 return redirect('poste_consulter', key='dae-%s' % poste.id)
270 else:
271 return redirect('poste', key='dae-%s' % poste.id)
272
273 else:
274 messages.add_message(
275 request, messages.ERROR,
276 'Il y a des erreurs dans le formulaire.'
277 )
278
279 else:
280 # 'initial' évite la validation prémature lors d'une copie de poste de
281 # rh_v1 vers dae.
282 form = PosteForm(initial=data, instance=poste, request=request)
283 piecesForm = PostePieceFormSet(instance=poste)
284
285 if poste_rh is not None:
286 financementForm = FinancementForm(
287 initial=financements, instance=poste
288 )
289 comparaisons_formset = PosteComparaisonForm(
290 initial=comparaisons,
291 instance=poste,
292 )
293 # cas de la création d'un nouveau poste
294 else:
295 financementForm = FinancementForm(instance=poste)
296 comparaisons_formset = PosteComparaisonForm(instance=poste)
297
298
299 # Modify queryset so that it is limited to users' rights:
300 imp_qs = dae.ProxyImplantation.dae_manager.ma_region_ou_service(
301 request.user)
302
303 for cmp_form in comparaisons_formset.forms:
304 cmp_form.fields['implantation'].queryset = imp_qs
305
306 vars.update(dict(
307 form=form, poste=poste, poste_key=key, piecesForm=piecesForm,
308 financementForm=financementForm,
309 comparaisons_formset=comparaisons_formset,
310 poste_rh=poste_rh,
311 creer_dossier_dae=creer_dossier_dae,
312 ))
313
314 return render(request, 'dae/poste.html', vars)
315
316
317 @dae_groupe_requis
318 def postes_liste(request):
319 """ Liste des postes. """
320 content_type = ContentType.objects.get_for_model(dae.Poste)
321 extra_select = {'derniere_validation': (
322 "SELECT MAX(date) FROM workflow_workflowcommentaire "
323 "WHERE content_type_id = '%s' AND object_id = dae_poste.id" %
324 content_type.id
325 )}
326 postes_a_traiter = dae.Poste.objects.mes_choses_a_faire(request.user) \
327 .annotate(num_dae=Count('dae_dossiers')) \
328 .filter(num_dae=0) \
329 .extra(select=extra_select).order_by('-id')
330 postes_en_cours = dae.Poste.objects.ma_region_ou_service(request.user) \
331 .annotate(num_dae=Count('dae_dossiers')) \
332 .filter(num_dae=0) \
333 .extra(select=extra_select) \
334 .filter(~Q(etat=POSTE_ETAT_FINALISE)) \
335 .order_by('-id')
336 return render(request, 'dae/postes_liste.html', {
337 'postes_a_traiter': postes_a_traiter,
338 'postes_en_cours': postes_en_cours,
339 })
340
341
342 @login_required
343 def poste_piece(request, id, filename):
344 """Téléchargement d'une pièce jointe à un poste."""
345 piece = get_object_or_404(dae.PostePiece, pk=id)
346 if dae.Poste.objects.ma_region_ou_service(request.user) \
347 .filter(id=piece.poste_id).exists():
348 return sendfile(request, piece.fichier.path)
349 else:
350 return redirect_interdiction(request)
351
352
353 ### DOSSIER
354
355 def filtered_type_remun():
356 defaut = (2, 3, 8, 17) # salaire de base, indemnité de fonction,
357 # charges patronales
358 return rh.TypeRemuneration.objects.filter(pk__in=defaut)
359
360
361 @dae_groupe_requis
362 @dossier_dans_ma_region_ou_service
363 def embauche_consulter(request, dossier_id):
364 dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
365 etat_precedent = dossier.etat
366
367 if request.POST:
368 validationForm = DossierWorkflowForm(
369 request.POST, instance=dossier, request=request
370 )
371 if validationForm.is_valid():
372 if etat_precedent == DOSSIER_ETAT_REGION_FINALISATION and \
373 validationForm.cleaned_data['etat'] == \
374 DOSSIER_ETAT_DRH_FINALISATION:
375 send_drh_finalisation_mail(request, dossier)
376 validationForm.save()
377 messages.add_message(
378 request, messages.SUCCESS, "La validation a été enregistrée."
379 )
380 return redirect('dae_embauches_liste')
381 else:
382 validationForm = DossierWorkflowForm(instance=dossier, request=request)
383
384 comparaisons_internes = \
385 dossier.poste.dae_comparaisons_internes.ma_region_ou_service(
386 request.user
387 )
388 comparaisons = dossier.dae_comparaisons.ma_region_ou_service(request.user)
389
390 return render(request, 'dae/embauche_consulter.html', {
391 'dossier': dossier,
392 'devises': devises(),
393 'validationForm': validationForm,
394 'comparaisons_internes': comparaisons_internes,
395 'comparaisons': comparaisons,
396 'importer': request.user.is_superuser,
397 })
398
399
400 @user_passes_test(lambda u: u.is_superuser)
401 @dae_groupe_requis
402 @dossier_dans_ma_region_ou_service
403 def embauche_importer(request, dossier_id=None):
404 dossier_dae = get_object_or_404(dae.Dossier, id=dossier_id)
405 if request.method == 'POST':
406 if 'confirmer' in request.POST:
407 dossier_rh = dossier_dae.importer_dans_rh()
408 return redirect('admin:rh_dossier_change', dossier_rh.id)
409 else:
410 return redirect('embauches_finalisees')
411 else:
412 c = {
413 'dossier': dossier_dae,
414 }
415 return render(request, 'dae/embauche_importer.html', c)
416
417
418 @dae_groupe_requis
419 def embauche_choisir_poste(request):
420 if request.POST:
421 form = ChoosePosteForm(data=request.POST, request=request)
422 if form.is_valid():
423 return form.redirect()
424 else:
425 form = ChoosePosteForm(request=request)
426 c = {
427 'form': form,
428 }
429 return render(request, 'dae/embauche-choisir-poste.html', c)
430
431
432 @dae_groupe_requis
433 @dossier_dans_ma_region_ou_service
434 @dossier_est_modifiable
435 def embauche(request, key=None, dossier_id=None):
436 """ Formulaire d'autorisation d'embauche. """
437
438 # Récupérer ou créer un poste et un dossier
439 source, id = key.split('-')
440 if source != 'dae':
441 return Http404
442 poste = get_object_or_404(dae.Poste, pk=id)
443
444 if request.POST:
445 if request.POST['employe'] == '':
446 # Nouvel employé
447 employe = dae.Employe()
448 else:
449 employe_source, id = request.POST['employe'].split('-')
450 if employe_source == 'dae':
451 # Employé DAE
452 employe = get_object_or_404(dae.Employe, pk=id)
453 elif employe_source == 'rh':
454 # Employé RH, on le copie dans DAE
455 e = get_object_or_404(rh.Employe, pk=id)
456 employe = dae.Employe(id_rh=e, prenom=e.prenom, nom=e.nom,
457 genre=e.genre)
458 else:
459 raise Http404
460
461 employe_form = EmployeForm(
462 request.POST, instance=employe, request=request
463 )
464
465 if employe_form.is_valid():
466 data = dict(request.POST.items())
467 employe = employe_form.save()
468 data['employe'] = 'dae-%s' % employe.id
469 employe_form = EmployeForm(data, instance=employe, request=request)
470
471 if not dossier_id:
472 dossier = dae.Dossier(poste=poste, employe=employe)
473 else:
474 dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
475 dossier.employe = employe_form.instance
476
477 dossier_form = DossierForm(request.POST, instance=dossier)
478 piecesForm = DossierPieceForm(
479 request.POST, request.FILES, instance=dossier
480 )
481 comparaisons_formset = DossierComparaisonFormSet(
482 request.POST,
483 queryset=dossier.dae_comparaisons.ma_region_ou_service(
484 request.user
485 )
486 )
487
488 remunForm = RemunForm(request.POST, instance=dossier)
489
490 if employe_form.is_valid() and \
491 dossier_form.is_valid() and \
492 piecesForm.is_valid() and \
493 comparaisons_formset.is_valid() and \
494 remunForm.is_valid():
495 employe.save()
496 dossier_form.save()
497 piecesForm.save()
498 remunForm.save()
499
500 # Ne remplacer que les comparaisons de ma région
501 comparaisons = comparaisons_formset.save(commit=False)
502 for comparaison in comparaisons:
503 comparaison.dossier = dossier
504 comparaison.save()
505
506 for comparaison in zip(
507 comparaisons,
508 comparaisons_formset.forms):
509
510 cmp_dossier_id = comparaison[1].cleaned_data.get(
511 'cmp_dossier', None)
512 if not cmp_dossier_id:
513 continue
514 cmp_dossier_qs = rh.Dossier.objects.filter(id=cmp_dossier_id)
515 if not cmp_dossier_qs.count() > 0:
516 continue
517 rhdossier = rh.Dossier.objects.get(id=cmp_dossier_qs.get().id)
518
519
520 # Get all remunerations for a period of 1 year,
521 # going back from either: today (if cdossier has not
522 # yet ended), or from cdossier's date_fin.
523 cmp_date = min(rhdossier.date_fin or date.today(), date.today())
524 for remuneration in _filter_remunerations(
525 rhdossier.remunerations().order_by('-date_debut'),
526 only_traitement=False,
527 ):
528 dae.DossierComparaisonRemuneration.objects.create(
529 dossier_comparaison = comparaison[0],
530 type=remuneration.type,
531 type_revalorisation=remuneration.type_revalorisation,
532 montant=remuneration.montant,
533 devise=remuneration.devise,
534 commentaire=remuneration.commentaire,
535 date_debut=remuneration.date_debut,
536 date_fin=remuneration.date_fin,
537 )
538
539 messages.success(
540 request, "Le dossier %s a été sauvegardé." % dossier
541 )
542 if 'save' in request.POST:
543 return redirect('embauche_consulter', dossier_id=dossier.id)
544 else:
545 return redirect(
546 'embauche', key=dossier.poste.key, dossier_id=dossier.id
547 )
548
549 else:
550 messages.add_message(
551 request, messages.ERROR,
552 'Il y a des erreurs dans le formulaire.'
553 )
554
555 else:
556 # Initialisation d'un formulaire vide
557 if dossier_id:
558 dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
559 employe = dossier.employe
560 data = dict(employe='dae-%s' % employe.id)
561 employe_form = EmployeForm(
562 initial=data, instance=employe, request=request
563 )
564 else:
565 dossier_rh = rh.Dossier()
566 poste_rh = poste.id_rh
567 dossier = pre_filled_dossier(dossier_rh, 'new', poste_rh)
568 employe_form = EmployeForm(request=request)
569
570 dossier_form = DossierForm(instance=dossier)
571 piecesForm = DossierPieceForm(instance=dossier)
572 comparaisons_formset = DossierComparaisonFormSet(
573 queryset=dossier.dae_comparaisons.ma_region_ou_service(
574 request.user
575 )
576 )
577 remunForm = RemunForm(instance=dossier)
578
579 try:
580 comparaisons_internes = \
581 dossier.poste.dae_comparaisons_internes.ma_region_ou_service(
582 request.user
583 )
584 except dae.Poste.DoesNotExist:
585 comparaisons_internes = []
586
587 # Modify queryset so that it is limited to users' rights:
588 imp_qs = dae.ProxyImplantation.dae_manager.ma_region_ou_service(
589 request.user)
590
591 for cmp_form in comparaisons_formset.forms:
592 cmp_form.fields['implantation'].queryset = imp_qs
593
594
595 c = {
596 'type_remun': filtered_type_remun(),
597 'devises': devises(),
598 'poste': poste,
599 'dossier': dossier,
600 'piecesForm': piecesForm,
601 'remunForm': remunForm,
602 'comparaisons_formset': comparaisons_formset,
603 'forms': dict(employe=employe_form, dossier=dossier_form, ),
604 'comparaisons_internes': comparaisons_internes,
605 }
606 return render(request, 'dae/embauche.html', c)
607
608
609 @dae_groupe_requis
610 @dossier_dans_ma_region_ou_service
611 def embauches_liste(request):
612 """ Liste des embauches. """
613 content_type = ContentType.objects.get_for_model(dae.Dossier)
614 extra_select = {'derniere_validation': (
615 "SELECT MAX(date) FROM workflow_workflowcommentaire "
616 "WHERE content_type_id = '%s' AND object_id = dae_dossier.id" %
617 content_type.id
618 )}
619 embauches_a_traiter = dae.Dossier.objects \
620 .mes_choses_a_faire(request.user) \
621 .extra(select=extra_select).order_by('-id')
622 embauches_en_cours = dae.Dossier.objects \
623 .ma_region_ou_service(request.user) \
624 .extra(select=extra_select) \
625 .order_by('-id') \
626 .exclude(etat=DOSSIER_ETAT_FINALISE)
627 c = {
628 'embauches_a_traiter': embauches_a_traiter,
629 'embauches_en_cours': embauches_en_cours,
630 }
631 return render(request, 'dae/embauches_liste.html', c)
632
633
634 @dae_groupe_requis
635 def embauches_finalisees(request):
636 """Liste des embauches finalisées."""
637
638 ### POST
639
640 if request.method == 'POST':
641 if 'supprimer' in request.POST:
642 ids = request.POST.getlist('ids')
643 dossiers = dae.Dossier.objects.filter(id__in=ids)
644 count = dossiers.count()
645 if count > 0:
646 dossiers.delete()
647 messages.success(request, u'%d dossiers supprimés' % count)
648 return redirect(request.get_full_path())
649
650 ### GET
651
652 embauches = dae.Dossier.objects.ma_region_ou_service(request.user) \
653 .filter(etat=DOSSIER_ETAT_FINALISE)
654
655 # Recherche
656 search_form = DAEFinaliseesSearchForm(request.GET)
657 if search_form.is_valid():
658 q = search_form.cleaned_data.get('q').strip()
659 importees = search_form.cleaned_data.get('importees')
660 if q:
661 criteria = [
662 Q(**{
663 'poste__implantation__zone_administrative__nom__icontains':
664 word
665 }) |
666 Q(poste__implantation__zone_administrative__code=word) |
667 Q(poste__implantation__nom__icontains=word) |
668 Q(poste__nom__icontains=word) |
669 Q(employe__nom__icontains=word) |
670 Q(employe__prenom__icontains=word)
671 for word in q.split()
672 ]
673 embauches = embauches.filter(*criteria)
674 if importees == 'oui':
675 embauches = embauches.exclude(dossier_rh=None)
676 elif importees == 'non':
677 embauches = embauches.filter(dossier_rh=None)
678
679 # Tri
680 tri = request.GET.get('tri', None)
681 if tri and tri.startswith('-'):
682 dir = '-'
683 tri = tri[1:]
684 else:
685 dir = ''
686 if tri == 'region':
687 embauches = embauches.order_by(
688 dir + 'poste__implantation__zone_administrative__nom'
689 )
690 elif tri == 'implantation':
691 embauches = embauches.order_by(dir + 'poste__implantation__nom')
692 elif tri == 'poste':
693 embauches = embauches.order_by(dir + 'poste__nom')
694 elif tri == 'personne':
695 embauches = embauches.order_by(dir + 'employe__nom',
696 dir + 'employe__prenom')
697 elif tri == 'date_debut':
698 embauches = embauches.order_by(dir + 'debut_contrat')
699 elif tri == 'date_fin':
700 embauches = embauches.order_by(dir + 'fin_contrat')
701
702 # Pagination
703 paginator = Paginator(embauches, 20)
704 try:
705 page = paginator.page(request.GET.get('page', 1))
706 except InvalidPage:
707 page = paginator.page(1)
708
709 return render(request, 'dae/embauches_finalisees.html', {
710 'embauches': page,
711 'search_form': search_form,
712 'importer': in_drh_or_admin(request.user)
713 })
714
715
716 def employe(request, key):
717 """ Récupération AJAX de l'employé pour la page d'embauche. """
718 data = dict(employe=key)
719
720 if key == '':
721 # Nouvel employé
722 employe = dae.Employe()
723 else:
724 # Employé existant
725 source, id = key.split('-')
726
727 if source == 'dae':
728 employe = get_object_or_404(dae.Employe, pk=id)
729 elif source == 'rh':
730 e = get_object_or_404(rh.Employe, id=id)
731 # Initialisation avec les valeurs de l'employé de rh_v1
732 employe = dae.Employe(id_rh=e)
733 for field in ('prenom', 'nom', 'genre'):
734 setattr(employe, field, getattr(e, field))
735
736 return HttpResponse(
737 EmployeForm(initial=data, instance=employe, request=request).as_table()
738 )
739
740
741 ### CONTRATS
742
743 @dae_groupe_requis
744 @get_contrat
745 def contrat(request, contrat, filename):
746 return sendfile(request, contrat.fichier.path)
747
748
749 @dae_groupe_requis
750 @get_contrat
751 def contrat_supprimer(request, contrat):
752 if request.method == 'POST':
753 if 'oui' in request.POST:
754 contrat.delete()
755 return redirect('embauche_consulter', dossier_id=contrat.dossier.id)
756 c = {
757 'contrat': contrat,
758 }
759 return render(request, 'dae/contrat-supprimer.html', c)
760
761
762 @dae_groupe_requis
763 @dossier_dans_ma_region_ou_service
764 def embauche_ajouter_contrat(request, dossier_id=None):
765 dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
766 if request.method == 'POST':
767 form = ContratForm(request.POST, request.FILES)
768 if form.is_valid():
769 contrat = form.save(commit=False)
770 contrat.dossier = dossier
771 contrat.date_debut = dossier.contrat_date_debut
772 contrat.date_fin = dossier.contrat_date_fin
773 contrat.save()
774 return redirect('embauche_consulter', dossier_id=dossier.id)
775 else:
776 form = ContratForm()
777
778 c = {
779 'form': form,
780 }
781 return render(request, 'dae/embauche-ajouter-contrat.html', c)
782
783
784 ### DAE NUMERISEE
785
786 @get_object(dae.Dossier, 'consulter')
787 def dae_numerisee(request, dossier):
788 return sendfile(request, dossier.dae_numerisee.path)
789
790
791 @get_object(dae.Dossier, 'modifier_dae_numerisee')
792 def dae_numerisee_modifier(request, dossier):
793 if request.method == 'POST':
794 form = DAENumeriseeForm(request.POST, request.FILES, instance=dossier)
795 if form.is_valid():
796 form.save()
797 return redirect('embauche_consulter', dossier_id=dossier.id)
798 else:
799 form = DAENumeriseeForm(instance=dossier)
800
801 c = {
802 'form': form,
803 }
804 return render(request, 'dae/dae_numerisee_modifier.html', c)
805
806
807 @get_object(dae.Dossier, 'modifier_dae_numerisee')
808 def dae_numerisee_supprimer(request, dossier):
809 if request.method == 'POST':
810 if 'oui' in request.POST:
811 dossier.dae_numerisee = None
812 dossier.save()
813 return redirect('embauche_consulter', dossier_id=dossier.id)
814 return render(request, 'dae/dae_numerisee_supprimer.html', {})
815
816
817 # AJAX SECURISE
818
819 @dae_groupe_requis
820 @employe_dans_ma_region_ou_service
821 def dossier(request, poste_key, employe_key):
822 """ Récupération AJAX du dossier pour la page d'embauche. """
823 data = dict()
824
825 poste_source, poste_id = poste_key.split('-')
826 poste = get_object_or_404(dae.Poste, pk=poste_id)
827
828 # Récupérer la devise de l'implantation lié au poste
829 implantation_devise = poste.get_default_devise()
830 data.update({'devise': implantation_devise})
831
832 if poste.id_rh_id is not None:
833 poste_rh = get_object_or_404(rh.Poste, pk=poste.id_rh_id)
834 else:
835 poste_rh = None
836
837 # NOUVEL EMPLOYE
838 if employe_key == '':
839 employe_source = 'new'
840 employe = None
841 dossier_rh = rh.Dossier()
842 dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh)
843
844 # EMPLOYE DAE
845 if employe_key.startswith('dae'):
846 employe_source, employe_id = employe_key.split('-')
847 employe_dae = get_object_or_404(dae.Employe, pk=employe_id)
848
849 # récupération de l'ancien dossier rh v1 pour l'employe DAE
850 try:
851 dossier_rh = rh.Dossier.objects.get(
852 employe=employe_dae.id_rh_id, date_fin=None
853 )
854 except (rh.Dossier.DoesNotExist):
855 dossier_rh = rh.Dossier()
856
857 # on tente de récupérer le dossier DAE, au pire on le contruit en le
858 # prépoluant avec son dossier rh v1.
859 try:
860 dossier = dae.Dossier.objects.get(employe=employe_dae, poste=poste)
861 except (dae.Dossier.DoesNotExist):
862 dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh)
863 employe = employe_dae.id_rh
864
865 # EMPLOYE RH v1
866 if employe_key.startswith('rh'):
867 employe_source, employe_id = employe_key.split('-')
868 employe_rh = get_object_or_404(rh.Employe, pk=employe_id)
869
870 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il
871 # n'en a pas, on en fournit un nouveau qui servira uniquement un
872 # créer un nouveau dossier DAE.
873 try:
874 dossier_rh = rh.Dossier.objects.get(
875 employe=employe_rh, date_fin=None
876 )
877 except (rh.Dossier.DoesNotExist):
878 dossier_rh = rh.Dossier()
879 dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh)
880 employe = employe_rh
881
882 dossier_form = DossierForm(initial=data, instance=dossier)
883 vars = dict(form=dossier_form, poste=poste, employe=employe)
884 return render(request, 'dae/embauche-dossier.html', vars)
885
886
887 # Cette fonction est appelée à partir de fonctions déjà sécurisée
888 def pre_filled_dossier(dossier_rh, employe_source, poste_rh):
889 dossier = dae.Dossier()
890
891 if employe_source != 'new' and dossier_rh.id:
892 dossier.statut_anterieur = dossier_rh.statut
893
894 # Certains dossiers ont un classement à zéro
895 if dossier_rh.classement_id > 0:
896 dossier.classement_anterieur = dossier_rh.classement
897
898 # Récupération du salaire de base
899 remun = dossier_rh.remunerations() \
900 .filter(type=1).order_by('-date_debut')
901 if remun:
902 dossier.salaire_anterieur = remun[0].montant
903 dossier.devise_anterieur = remun[0].devise
904
905 # Récupération du titulaire précédent
906 try:
907 dossiers = rh.Dossier.objects.order_by('-date_debut')
908 if poste_rh:
909 dossiers = dossiers.filter(poste=poste_rh)
910 else:
911 dossiers = rh.Dossier.objects.none()
912 if len(dossiers):
913 # Ce bloc ignore toutes les erreurs, car les données de rh
914 # manquantes peuvent en générer
915 d = dossiers[0]
916 try:
917 titulaire = d.employe
918 dossier.employe_anterieur = titulaire
919 dossier.classement_titulaire_anterieur = d.classement
920 dossier.statut_titulaire_anterieur = d.statut
921 remun = d.remunerations().filter(type=1) \
922 .order_by('-date_debut')[0]
923 dossier.salaire_titulaire_anterieur = remun.montant
924 dossier.devise_titulaire_anterieur = remun.devise
925 except:
926 pass
927 # TODO: afficher l'info, les champs ne sont pas dans le
928 # modèle dae.Dossier: nom, prenom, classement, salaire
929 pass
930
931 except (rh.Dossier.DoesNotExist):
932 dossier_rh = rh.Dossier()
933
934 return dossier
935
936
937 def _filter_remunerations(remun_qs, only_traitement=True):
938 """
939 Type de remun traitement derniers commencant a la meme date
940 """
941 # Toutes les rémunérations d'un dossier
942 remunerations = remun_qs
943
944 if only_traitement:
945 remunerations = [
946 r for r in remun_qs
947 if r.type.nature_remuneration == "Traitement"
948 ]
949
950 # On prend les dernières avec le postulat que les rémun à la même date
951 # constituent le dernier salaire
952 if len(remunerations) > 0:
953 date_debut = remunerations[0].date_debut
954
955 return [r for r in remunerations if r.date_debut == date_debut]
956
957
958
959 def _get_salaire_traitement(dossier):
960 data = {}
961
962 remunerations = _filter_remunerations(dossier.remunerations().order_by('-date_debut'))
963
964 montant = 0.0
965 montant_euros = 0.0
966 devise = None
967
968 # Les remun sont sensées être dans la même devise
969 for r in remunerations:
970 montant += float(r.montant)
971 montant_euros += r.montant_euros()
972 devise = r.devise.id
973
974 data['devise'] = devise
975 data['montant'] = montant
976 data['montant_euros'] = montant_euros
977 return data
978
979
980 @dae_groupe_requis
981 @vieux_dossier_dans_ma_region_ou_service
982 def dossier_resume(request, dossier_id=None):
983 try:
984 dossier = rh.Dossier.objects.get(id=dossier_id)
985 except:
986 return HttpResponseNotFound("Ce dossier n'est pas accessible")
987
988 data = {}
989 data['personne'] = unicode(dossier.employe)
990 if dossier.classement is not None:
991 data['classement'] = dossier.classement.id
992 if dossier.statut is not None:
993 data['statut'] = dossier.statut.id
994 data['implantation'] = dossier.poste.implantation.id
995 data['poste'] = dossier.poste.nom
996 data['d_id'] = dossier.id
997 data.update(_get_salaire_traitement(dossier))
998 return HttpResponse(dumps(data))
999
1000
1001 @dae_groupe_requis
1002 @vieux_dossier_dans_ma_region_ou_service
1003 def poste_resume(request, dossier_id=None):
1004 """
1005 On travaille, en réalité sur le dossier mais on cache
1006 l'identité de la personne.
1007 """
1008 try:
1009 dossier = rh.Dossier.objects.get(id=dossier_id)
1010 except:
1011 return HttpResponseNotFound("Ce dossier n'est pas accessible")
1012
1013 data = {}
1014 data['implantation'] = dossier.poste.implantation.id
1015 data['poste'] = dossier.poste.nom
1016 data['statut'] = dossier.statut_id
1017 data['classement'] = dossier.classement_id
1018 data['d_id'] = dossier.id
1019 data.update(_get_salaire_traitement(dossier))
1020
1021 return HttpResponse(dumps(data))
1022
1023
1024 def liste_postes(request):
1025 """ Appel AJAX :
1026 input : implantation_id
1027 output : JSON liste de valeur point
1028 """
1029 method = request.method
1030 params = getattr(request, method, [])
1031 data = []
1032
1033 if 'implantation_id' in params \
1034 and params.get('implantation_id') is not u"":
1035 implantation_id = params.get('implantation_id')
1036 q = Q(implantation__id=implantation_id)
1037 else:
1038 q = Q()
1039
1040 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).filter(q)
1041 postes_rh = postes_rh.select_related(depth=1)
1042
1043 data = [('', 'Nouveau poste')] + \
1044 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
1045 postes_rh],
1046 key=lambda t: t[1])
1047 return HttpResponse(dumps(data))
1048
1049
1050 @login_required
1051 def dossier_piece(request, id, filename):
1052 """Téléchargement d'une pièce jointe à un poste."""
1053 piece = get_object_or_404(dae.DossierPiece, pk=id)
1054 if dae.Dossier.objects.ma_region_ou_service(request.user) \
1055 .filter(id=piece.dossier_id).exists():
1056 return sendfile(request, piece.fichier.path)
1057 else:
1058 return redirect_interdiction(request)
1059
1060
1061 # AJAX SECURITE non nécessaire
1062
1063 def coefficient(request):
1064 """ Appel AJAX :
1065 input : classement
1066 output : coefficient
1067 """
1068 method = request.method
1069 params = getattr(request, method, [])
1070 data = dict()
1071 if 'classement' in params and params.get('classement') is not u"":
1072 classement = params.get('classement')
1073 classement = rh.Classement.objects.get(pk=classement)
1074 data['coefficient'] = classement.coefficient
1075 else:
1076 data['coefficient'] = 0
1077 return HttpResponse(dumps(data))
1078
1079
1080 def devise(request):
1081 """ Appel AJAX :
1082 input : valeur_point
1083 output : devise, devise_code, taux_euro
1084 """
1085 method = request.method
1086 params = getattr(request, method, [])
1087 data = dict()
1088 if 'valeur_point' in params and params.get('valeur_point') is not u"":
1089 valeur_point = params.get('valeur_point')
1090 valeur_point = rh.ValeurPoint.objects.get(pk=valeur_point)
1091 annee = valeur_point.annee
1092 try:
1093 taux = rh.TauxChange.objects.get(
1094 annee=annee, devise=valeur_point.devise
1095 )
1096 except rh.TauxChange.DoesNotExist:
1097 return HttpResponseNotFound(
1098 u"Taux de change introuvable pour la devise %s "
1099 u"pour l'année %d" % (valeur_point.devise.code, annee)
1100 )
1101 except rh.TauxChange.MultipleObjectsReturned:
1102 return HttpResponseNotFound(
1103 u"Il existe plusieurs taux pour la devise %s "
1104 u"cette année-là : %s" % (valeur_point.devise.code, annee)
1105 )
1106
1107 data['devise'] = taux.devise.id
1108 data['valeur'] = valeur_point.valeur
1109 data['devise_code'] = taux.devise.code
1110 data['taux_euro'] = taux.taux
1111 else:
1112 return HttpResponseNotFound("Vous devez choisir une valeur de point")
1113 return HttpResponse(dumps(data))
1114
1115
1116 def devise_code(request):
1117 """ Appel AJAX :
1118 input : devise
1119 output : devise_code, taux_euro
1120 """
1121 method = request.method
1122 params = getattr(request, method, [])
1123 data = dict()
1124 if 'devise' in params:
1125 devise = params.get('devise')
1126 devise = rh.Devise.objects.get(pk=devise)
1127 annee = date.today().year
1128 taux = rh.TauxChange.objects.filter(annee=annee, devise=devise)
1129 if len(taux) == 0:
1130 return HttpResponseNotFound("Le taux n'est pas disponible")
1131 data['devise_code'] = devise.code
1132 data['taux_euro'] = taux[0].taux
1133 return HttpResponse(dumps(data))
1134
1135
1136 def add_remun(request, dossier, type_remun):
1137 dossier = get_object_or_404(dae.Dossier, pk=dossier)
1138 type_remun = get_object_or_404(rh.TypeRemuneration, pk=type_remun)
1139 dae.Remuneration(dossier=dossier, devise=dossier.devise,
1140 type=type_remun).save()
1141
1142 c = {
1143 'dossier': dossier,
1144 }
1145 return render(request, 'dae/embauche-remun.html', c)
1146
1147
1148 def salaire(request, implantation, devise, classement):
1149 if not devise or not classement:
1150 raise Http404
1151
1152 taux = rh.TauxChange.objects.filter(devise=devise).order_by('-annee')
1153 vp = rh.ValeurPoint.objects \
1154 .filter(implantation=implantation, devise=devise) \
1155 .order_by('-annee')
1156
1157 if vp.count() == 0:
1158 status = u"pas de valeur de point pour le couple \
1159 implantation/devise (%s/%s)" % (implantation, devise)
1160 return HttpResponse(dumps(dict(status=status)))
1161
1162 if taux.count() == 0:
1163 status = u"Pas de taux pour la devise %s" % devise
1164 return HttpResponse(dumps(dict(status=status)))
1165
1166 classement = get_object_or_404(rh.Classement, pk=classement)
1167 if classement.coefficient is None:
1168 raise Http404
1169 taux, vp = taux[0].taux, vp[0].valeur
1170
1171 salaire_euro = round(vp * classement.coefficient * taux, 2)
1172 data = dict(status='OK',
1173 salaire_euro=salaire_euro, taux=taux,
1174 salaire_devise=round(salaire_euro / taux, 2))
1175
1176 return HttpResponse(dumps(data))
1177
1178
1179 def liste_valeurs_point(request):
1180 """ Appel AJAX :
1181 input : implantation_id
1182 output : JSON liste de valeur point
1183 """
1184 method = request.method
1185 params = getattr(request, method, [])
1186 data = []
1187 annee_courante = datetime.now().year
1188 if 'implantation_id' in params \
1189 and params.get('implantation_id') is not u"":
1190 implantation_id = params.get('implantation_id')
1191 preselectionne = rh.ValeurPoint.objects \
1192 .filter(implantation=implantation_id, annee=annee_courante) \
1193 .order_by("-annee")
1194 for o in preselectionne:
1195 data.append({
1196 'id': o.id,
1197 'label': o.__unicode__(),
1198 'devise': o.devise_id,
1199 'suggestion': True
1200 })
1201 else:
1202 preselectionne = rh.ValeurPoint.objects.none()
1203
1204 liste_complete = rh.ValeurPoint.objects \
1205 .filter(annee__in=(annee_courante,)) \
1206 .order_by("-annee")
1207 for o in liste_complete.exclude(id__in=[p.id for p in preselectionne]):
1208 data.append({
1209 'id': o.id,
1210 'label': o.__unicode__(),
1211 'devise': o.devise_id,
1212 'suggestion': False
1213 })
1214 return HttpResponse(dumps(data, indent=4))