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