Fix for salaire de base bug
[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
502 comparaisons = comparaisons_formset.save(commit=False)
503 for comparaison in comparaisons:
504 comparaison.dossier = dossier
505 comparaison.save()
506
507 for comparaison in zip(
508 comparaisons,
509 comparaisons_formset.forms):
510
511 cmp_dossier_id = comparaison[1].cleaned_data.get(
512 'cmp_dossier', None)
513 if not cmp_dossier_id:
514 continue
515 cmp_dossier_qs = rh.Dossier.objects.filter(id=cmp_dossier_id)
516 if not cmp_dossier_qs.count() > 0:
517 continue
518 rhdossier = rh.Dossier.objects.get(id=cmp_dossier_qs.get().id)
519
520
521 # Get all remunerations for a period of 1 year,
522 # going back from either: today (if cdossier has not
523 # yet ended), or from cdossier's date_fin.
524 cmp_date = min(rhdossier.date_fin or date.today(), date.today())
525 for remuneration in _filter_remunerations(
526 rhdossier.remunerations().order_by('-date_debut'),
527 only_traitement=False,
528 ):
529 dae.DossierComparaisonRemuneration.objects.create(
530 dossier_comparaison = comparaison[0],
531 type=remuneration.type,
532 type_revalorisation=remuneration.type_revalorisation,
533 montant=remuneration.montant,
534 devise=remuneration.devise,
535 commentaire=remuneration.commentaire,
536 date_debut=remuneration.date_debut,
537 date_fin=remuneration.date_fin,
538 )
539
540 messages.success(
541 request, "Le dossier %s a été sauvegardé." % dossier
542 )
543 if 'save' in request.POST:
544 return redirect('embauche_consulter', dossier_id=dossier.id)
545 else:
546 return redirect(
547 'embauche', key=dossier.poste.key, dossier_id=dossier.id
548 )
549
550 else:
551 messages.add_message(
552 request, messages.ERROR,
553 'Il y a des erreurs dans le formulaire.'
554 )
555
556 else:
557 # Initialisation d'un formulaire vide
558 if dossier_id:
559 dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
560 employe = dossier.employe
561 data = dict(employe='dae-%s' % employe.id)
562 employe_form = EmployeForm(
563 initial=data, instance=employe, request=request
564 )
565 else:
566 dossier_rh = rh.Dossier()
567 poste_rh = poste.id_rh
568 dossier = pre_filled_dossier(dossier_rh, 'new', poste_rh)
569 employe_form = EmployeForm(request=request)
570
571 dossier_form = DossierForm(instance=dossier)
572 piecesForm = DossierPieceForm(instance=dossier)
573 comparaisons_formset = DossierComparaisonFormSet(
574 queryset=dossier.dae_comparaisons.ma_region_ou_service(
575 request.user
576 )
577 )
578 remunForm = RemunForm(instance=dossier)
579
580 try:
581 comparaisons_internes = \
582 dossier.poste.dae_comparaisons_internes.ma_region_ou_service(
583 request.user
584 )
585 except dae.Poste.DoesNotExist:
586 comparaisons_internes = []
587
588 # Modify queryset so that it is limited to users' rights:
589 imp_qs = dae.ProxyImplantation.dae_manager.ma_region_ou_service(
590 request.user)
591
592 for cmp_form in comparaisons_formset.forms:
593 cmp_form.fields['implantation'].queryset = imp_qs
594
595
596 c = {
597 'type_remun': filtered_type_remun(),
598 'devises': devises(),
599 'poste': poste,
600 'dossier': dossier,
601 'piecesForm': piecesForm,
602 'remunForm': remunForm,
603 'comparaisons_formset': comparaisons_formset,
604 'forms': dict(employe=employe_form, dossier=dossier_form, ),
605 'comparaisons_internes': comparaisons_internes,
606 }
607 return render(request, 'dae/embauche.html', c)
608
609
610 @dae_groupe_requis
611 @dossier_dans_ma_region_ou_service
612 def embauches_liste(request):
613 """ Liste des embauches. """
614 content_type = ContentType.objects.get_for_model(dae.Dossier)
615 extra_select = {'derniere_validation': (
616 "SELECT MAX(date) FROM workflow_workflowcommentaire "
617 "WHERE content_type_id = '%s' AND object_id = dae_dossier.id" %
618 content_type.id
619 )}
620 embauches_a_traiter = dae.Dossier.objects \
621 .mes_choses_a_faire(request.user) \
622 .extra(select=extra_select).order_by('-id')
623 embauches_en_cours = dae.Dossier.objects \
624 .ma_region_ou_service(request.user) \
625 .extra(select=extra_select) \
626 .order_by('-id') \
627 .exclude(etat=DOSSIER_ETAT_FINALISE)
628 c = {
629 'embauches_a_traiter': embauches_a_traiter,
630 'embauches_en_cours': embauches_en_cours,
631 }
632 return render(request, 'dae/embauches_liste.html', c)
633
634
635 @dae_groupe_requis
636 def embauches_finalisees(request):
637 """Liste des embauches finalisées."""
638
639 ### POST
640
641 if request.method == 'POST':
642 if 'supprimer' in request.POST:
643 ids = request.POST.getlist('ids')
644 dossiers = dae.Dossier.objects.filter(id__in=ids)
645 count = dossiers.count()
646 if count > 0:
647 dossiers.delete()
648 messages.success(request, u'%d dossiers supprimés' % count)
649 return redirect(request.get_full_path())
650
651 ### GET
652
653 embauches = dae.Dossier.objects.ma_region_ou_service(request.user) \
654 .filter(etat=DOSSIER_ETAT_FINALISE)
655
656 # Recherche
657 search_form = DAEFinaliseesSearchForm(request.GET)
658 if search_form.is_valid():
659 q = search_form.cleaned_data.get('q').strip()
660 importees = search_form.cleaned_data.get('importees')
661 if q:
662 criteria = [
663 Q(**{
664 'poste__implantation__zone_administrative__nom__icontains':
665 word
666 }) |
667 Q(poste__implantation__zone_administrative__code=word) |
668 Q(poste__implantation__nom__icontains=word) |
669 Q(poste__nom__icontains=word) |
670 Q(employe__nom__icontains=word) |
671 Q(employe__prenom__icontains=word)
672 for word in q.split()
673 ]
674 embauches = embauches.filter(*criteria)
675 if importees == 'oui':
676 embauches = embauches.exclude(dossier_rh=None)
677 elif importees == 'non':
678 embauches = embauches.filter(dossier_rh=None)
679
680 # Tri
681 tri = request.GET.get('tri', None)
682 if tri and tri.startswith('-'):
683 dir = '-'
684 tri = tri[1:]
685 else:
686 dir = ''
687 if tri == 'region':
688 embauches = embauches.order_by(
689 dir + 'poste__implantation__zone_administrative__nom'
690 )
691 elif tri == 'implantation':
692 embauches = embauches.order_by(dir + 'poste__implantation__nom')
693 elif tri == 'poste':
694 embauches = embauches.order_by(dir + 'poste__nom')
695 elif tri == 'personne':
696 embauches = embauches.order_by(dir + 'employe__nom',
697 dir + 'employe__prenom')
698 elif tri == 'date_debut':
699 embauches = embauches.order_by(dir + 'debut_contrat')
700 elif tri == 'date_fin':
701 embauches = embauches.order_by(dir + 'fin_contrat')
702
703 # Pagination
704 paginator = Paginator(embauches, 20)
705 try:
706 page = paginator.page(request.GET.get('page', 1))
707 except InvalidPage:
708 page = paginator.page(1)
709
710 return render(request, 'dae/embauches_finalisees.html', {
711 'embauches': page,
712 'search_form': search_form,
713 'importer': in_drh_or_admin(request.user)
714 })
715
716
717 def employe(request, key):
718 """ Récupération AJAX de l'employé pour la page d'embauche. """
719 data = dict(employe=key)
720
721 if key == '':
722 # Nouvel employé
723 employe = dae.Employe()
724 else:
725 # Employé existant
726 source, id = key.split('-')
727
728 if source == 'dae':
729 employe = get_object_or_404(dae.Employe, pk=id)
730 elif source == 'rh':
731 e = get_object_or_404(rh.Employe, id=id)
732 # Initialisation avec les valeurs de l'employé de rh_v1
733 employe = dae.Employe(id_rh=e)
734 for field in ('prenom', 'nom', 'genre'):
735 setattr(employe, field, getattr(e, field))
736
737 return HttpResponse(
738 EmployeForm(initial=data, instance=employe, request=request).as_table()
739 )
740
741
742 ### CONTRATS
743
744 @dae_groupe_requis
745 @get_contrat
746 def contrat(request, contrat, filename):
747 return sendfile(request, contrat.fichier.path)
748
749
750 @dae_groupe_requis
751 @get_contrat
752 def contrat_supprimer(request, contrat):
753 if request.method == 'POST':
754 if 'oui' in request.POST:
755 contrat.delete()
756 return redirect('embauche_consulter', dossier_id=contrat.dossier.id)
757 c = {
758 'contrat': contrat,
759 }
760 return render(request, 'dae/contrat-supprimer.html', c)
761
762
763 @dae_groupe_requis
764 @dossier_dans_ma_region_ou_service
765 def embauche_ajouter_contrat(request, dossier_id=None):
766 dossier = get_object_or_404(dae.Dossier, pk=dossier_id)
767 if request.method == 'POST':
768 form = ContratForm(request.POST, request.FILES)
769 if form.is_valid():
770 contrat = form.save(commit=False)
771 contrat.dossier = dossier
772 contrat.date_debut = dossier.contrat_date_debut
773 contrat.date_fin = dossier.contrat_date_fin
774 contrat.save()
775 return redirect('embauche_consulter', dossier_id=dossier.id)
776 else:
777 form = ContratForm()
778
779 c = {
780 'form': form,
781 }
782 return render(request, 'dae/embauche-ajouter-contrat.html', c)
783
784
785 ### DAE NUMERISEE
786
787 @get_object(dae.Dossier, 'consulter')
788 def dae_numerisee(request, dossier):
789 return sendfile(request, dossier.dae_numerisee.path)
790
791
792 @get_object(dae.Dossier, 'modifier_dae_numerisee')
793 def dae_numerisee_modifier(request, dossier):
794 if request.method == 'POST':
795 form = DAENumeriseeForm(request.POST, request.FILES, instance=dossier)
796 if form.is_valid():
797 form.save()
798 return redirect('embauche_consulter', dossier_id=dossier.id)
799 else:
800 form = DAENumeriseeForm(instance=dossier)
801
802 c = {
803 'form': form,
804 }
805 return render(request, 'dae/dae_numerisee_modifier.html', c)
806
807
808 @get_object(dae.Dossier, 'modifier_dae_numerisee')
809 def dae_numerisee_supprimer(request, dossier):
810 if request.method == 'POST':
811 if 'oui' in request.POST:
812 dossier.dae_numerisee = None
813 dossier.save()
814 return redirect('embauche_consulter', dossier_id=dossier.id)
815 return render(request, 'dae/dae_numerisee_supprimer.html', {})
816
817
818 # AJAX SECURISE
819
820 @dae_groupe_requis
821 @employe_dans_ma_region_ou_service
822 def dossier(request, poste_key, employe_key):
823 """ Récupération AJAX du dossier pour la page d'embauche. """
824 data = dict()
825
826 poste_source, poste_id = poste_key.split('-')
827 poste = get_object_or_404(dae.Poste, pk=poste_id)
828
829 # Récupérer la devise de l'implantation lié au poste
830 implantation_devise = poste.get_default_devise()
831 data.update({'devise': implantation_devise})
832
833 if poste.id_rh_id is not None:
834 poste_rh = get_object_or_404(rh.Poste, pk=poste.id_rh_id)
835 else:
836 poste_rh = None
837
838 # NOUVEL EMPLOYE
839 if employe_key == '':
840 employe_source = 'new'
841 employe = None
842 dossier_rh = rh.Dossier()
843 dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh)
844
845 # EMPLOYE DAE
846 if employe_key.startswith('dae'):
847 employe_source, employe_id = employe_key.split('-')
848 employe_dae = get_object_or_404(dae.Employe, pk=employe_id)
849
850 # récupération de l'ancien dossier rh v1 pour l'employe DAE
851 try:
852 dossier_rh = rh.Dossier.objects.get(
853 employe=employe_dae.id_rh_id, date_fin=None
854 )
855 except (rh.Dossier.DoesNotExist):
856 dossier_rh = rh.Dossier()
857
858 # on tente de récupérer le dossier DAE, au pire on le contruit en le
859 # prépoluant avec son dossier rh v1.
860 try:
861 dossier = dae.Dossier.objects.get(employe=employe_dae, poste=poste)
862 except (dae.Dossier.DoesNotExist):
863 dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh)
864 employe = employe_dae.id_rh
865
866 # EMPLOYE RH v1
867 if employe_key.startswith('rh'):
868 employe_source, employe_id = employe_key.split('-')
869 employe_rh = get_object_or_404(rh.Employe, pk=employe_id)
870
871 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il
872 # n'en a pas, on en fournit un nouveau qui servira uniquement un
873 # créer un nouveau dossier DAE.
874 try:
875 dossier_rh = rh.Dossier.objects.get(
876 employe=employe_rh, date_fin=None
877 )
878 except (rh.Dossier.DoesNotExist):
879 dossier_rh = rh.Dossier()
880 dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh)
881 employe = employe_rh
882
883 dossier_form = DossierForm(initial=data, instance=dossier)
884 vars = dict(form=dossier_form, poste=poste, employe=employe)
885 return render(request, 'dae/embauche-dossier.html', vars)
886
887
888 # Cette fonction est appelée à partir de fonctions déjà sécurisée
889 def pre_filled_dossier(dossier_rh, employe_source, poste_rh):
890 dossier = dae.Dossier()
891
892 if employe_source != 'new' and dossier_rh.id:
893 dossier.statut_anterieur = dossier_rh.statut
894
895 # Certains dossiers ont un classement à zéro
896 if dossier_rh.classement_id > 0:
897 dossier.classement_anterieur = dossier_rh.classement
898
899 # Récupération du salaire de base
900 remun = dossier_rh.remunerations() \
901 .filter(type=1).order_by('-date_debut')
902 if remun:
903 dossier.salaire_anterieur = remun[0].montant
904 dossier.devise_anterieur = remun[0].devise
905
906 # Récupération du titulaire précédent
907 try:
908 dossiers = rh.Dossier.objects.order_by('-date_debut')
909 if poste_rh:
910 dossiers = dossiers.filter(poste=poste_rh)
911 else:
912 dossiers = rh.Dossier.objects.none()
913 if len(dossiers):
914 # Ce bloc ignore toutes les erreurs, car les données de rh
915 # manquantes peuvent en générer
916 d = dossiers[0]
917 try:
918 titulaire = d.employe
919 dossier.employe_anterieur = titulaire
920 dossier.classement_titulaire_anterieur = d.classement
921 dossier.statut_titulaire_anterieur = d.statut
922 remun = d.remunerations().filter(type=1) \
923 .order_by('-date_debut')[0]
924 dossier.salaire_titulaire_anterieur = remun.montant
925 dossier.devise_titulaire_anterieur = remun.devise
926 except:
927 pass
928 # TODO: afficher l'info, les champs ne sont pas dans le
929 # modèle dae.Dossier: nom, prenom, classement, salaire
930 pass
931
932 except (rh.Dossier.DoesNotExist):
933 dossier_rh = rh.Dossier()
934
935 return dossier
936
937
938 def _filter_remunerations(remun_qs, only_traitement=True):
939 """
940 Type de remun traitement derniers commencant a la meme date
941 """
942 # Toutes les rémunérations d'un dossier
943 remunerations = remun_qs
944
945 if only_traitement:
946 remunerations = [
947 r for r in remun_qs
948 if r.type.nature_remuneration == "Traitement"
949 ]
950
951 # On prend les dernières avec le postulat que les rémun à la même date
952 # constituent le dernier salaire
953 if len(remunerations) > 0:
954 date_debut = remunerations[0].date_debut
955
956 return [r for r in remunerations if r.date_debut == date_debut]
957
958
959
960 def _get_salaire_traitement(dossier):
961 data = {}
962
963 remunerations = _filter_remunerations(dossier.remunerations().order_by('-date_debut'))
964
965 montant = 0.0
966 montant_euros = 0.0
967 devise = None
968
969 # Les remun sont sensées être dans la même devise
970 for r in remunerations:
971 montant += float(r.montant)
972 montant_euros += r.montant_euros()
973 devise = r.devise.id
974
975 data['devise'] = devise
976 data['montant'] = montant
977 data['montant_euros'] = montant_euros
978 return data
979
980
981 @dae_groupe_requis
982 @vieux_dossier_dans_ma_region_ou_service
983 def dossier_resume(request, dossier_id=None):
984 try:
985 dossier = rh.Dossier.objects.get(id=dossier_id)
986 except:
987 return HttpResponseNotFound("Ce dossier n'est pas accessible")
988
989 data = {}
990 data['personne'] = unicode(dossier.employe)
991 if dossier.classement is not None:
992 data['classement'] = dossier.classement.id
993 if dossier.statut is not None:
994 data['statut'] = dossier.statut.id
995 data['implantation'] = dossier.poste.implantation.id
996 data['poste'] = dossier.poste.nom
997 data['d_id'] = dossier.id
998 data.update(_get_salaire_traitement(dossier))
999 return HttpResponse(dumps(data))
1000
1001
1002 @dae_groupe_requis
1003 @vieux_dossier_dans_ma_region_ou_service
1004 def poste_resume(request, dossier_id=None):
1005 """
1006 On travaille, en réalité sur le dossier mais on cache
1007 l'identité de la personne.
1008 """
1009 try:
1010 dossier = rh.Dossier.objects.get(id=dossier_id)
1011 except:
1012 return HttpResponseNotFound("Ce dossier n'est pas accessible")
1013
1014 data = {}
1015 data['implantation'] = dossier.poste.implantation.id
1016 data['poste'] = dossier.poste.nom
1017 data['statut'] = dossier.statut_id
1018 data['classement'] = dossier.classement_id
1019 data['d_id'] = dossier.id
1020 data.update(_get_salaire_traitement(dossier))
1021
1022 return HttpResponse(dumps(data))
1023
1024
1025 def liste_postes(request):
1026 """ Appel AJAX :
1027 input : implantation_id
1028 output : JSON liste de valeur point
1029 """
1030 method = request.method
1031 params = getattr(request, method, [])
1032 data = []
1033
1034 if 'implantation_id' in params \
1035 and params.get('implantation_id') is not u"":
1036 implantation_id = params.get('implantation_id')
1037 q = Q(implantation__id=implantation_id)
1038 else:
1039 q = Q()
1040
1041 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).filter(q)
1042 postes_rh = postes_rh.select_related(depth=1)
1043
1044 data = [('', 'Nouveau poste')] + \
1045 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
1046 postes_rh],
1047 key=lambda t: t[1])
1048 return HttpResponse(dumps(data))
1049
1050
1051 @login_required
1052 def dossier_piece(request, id, filename):
1053 """Téléchargement d'une pièce jointe à un poste."""
1054 piece = get_object_or_404(dae.DossierPiece, pk=id)
1055 if dae.Dossier.objects.ma_region_ou_service(request.user) \
1056 .filter(id=piece.dossier_id).exists():
1057 return sendfile(request, piece.fichier.path)
1058 else:
1059 return redirect_interdiction(request)
1060
1061
1062 # AJAX SECURITE non nécessaire
1063
1064 def coefficient(request):
1065 """ Appel AJAX :
1066 input : classement
1067 output : coefficient
1068 """
1069 method = request.method
1070 params = getattr(request, method, [])
1071 data = dict()
1072 if 'classement' in params and params.get('classement') is not u"":
1073 classement = params.get('classement')
1074 classement = rh.Classement.objects.get(pk=classement)
1075 data['coefficient'] = classement.coefficient
1076 else:
1077 data['coefficient'] = 0
1078 return HttpResponse(dumps(data))
1079
1080
1081 def devise(request):
1082 """ Appel AJAX :
1083 input : valeur_point
1084 output : devise, devise_code, taux_euro
1085 """
1086 method = request.method
1087 params = getattr(request, method, [])
1088 data = dict()
1089 if 'valeur_point' in params and params.get('valeur_point') is not u"":
1090 valeur_point = params.get('valeur_point')
1091 valeur_point = rh.ValeurPoint.objects.get(pk=valeur_point)
1092 annee = valeur_point.annee
1093 try:
1094 taux = rh.TauxChange.objects.get(
1095 annee=annee, devise=valeur_point.devise
1096 )
1097 except rh.TauxChange.DoesNotExist:
1098 return HttpResponseNotFound(
1099 u"Taux de change introuvable pour la devise %s "
1100 u"pour l'année %d" % (valeur_point.devise.code, annee)
1101 )
1102 except rh.TauxChange.MultipleObjectsReturned:
1103 return HttpResponseNotFound(
1104 u"Il existe plusieurs taux pour la devise %s "
1105 u"cette année-là : %s" % (valeur_point.devise.code, annee)
1106 )
1107
1108 data['devise'] = taux.devise.id
1109 data['valeur'] = valeur_point.valeur
1110 data['devise_code'] = taux.devise.code
1111 data['taux_euro'] = taux.taux
1112 else:
1113 return HttpResponseNotFound("Vous devez choisir une valeur de point")
1114 return HttpResponse(dumps(data))
1115
1116
1117 def devise_code(request):
1118 """ Appel AJAX :
1119 input : devise
1120 output : devise_code, taux_euro
1121 """
1122 method = request.method
1123 params = getattr(request, method, [])
1124 data = dict()
1125 if 'devise' in params:
1126 devise = params.get('devise')
1127 devise = rh.Devise.objects.get(pk=devise)
1128 annee = date.today().year
1129 taux = rh.TauxChange.objects.filter(annee=annee, devise=devise)
1130 if len(taux) == 0:
1131 return HttpResponseNotFound("Le taux n'est pas disponible")
1132 data['devise_code'] = devise.code
1133 data['taux_euro'] = taux[0].taux
1134 return HttpResponse(dumps(data))
1135
1136
1137 def add_remun(request, dossier, type_remun):
1138 dossier = get_object_or_404(dae.Dossier, pk=dossier)
1139 type_remun = get_object_or_404(rh.TypeRemuneration, pk=type_remun)
1140 dae.Remuneration(dossier=dossier, devise=dossier.devise,
1141 type=type_remun).save()
1142
1143 c = {
1144 'dossier': dossier,
1145 }
1146 return render(request, 'dae/embauche-remun.html', c)
1147
1148
1149 def salaire(request, implantation, devise, classement):
1150 if not devise or not classement:
1151 raise Http404
1152
1153 taux = rh.TauxChange.objects.filter(devise=devise).order_by('-annee')
1154 vp = rh.ValeurPoint.objects \
1155 .filter(implantation=implantation, devise=devise) \
1156 .order_by('-annee')
1157
1158 if vp.count() == 0:
1159 status = u"pas de valeur de point pour le couple \
1160 implantation/devise (%s/%s)" % (implantation, devise)
1161 return HttpResponse(dumps(dict(status=status)))
1162
1163 if taux.count() == 0:
1164 status = u"Pas de taux pour la devise %s" % devise
1165 return HttpResponse(dumps(dict(status=status)))
1166
1167 classement = get_object_or_404(rh.Classement, pk=classement)
1168 if classement.coefficient is None:
1169 raise Http404
1170 taux, vp = taux[0].taux, vp[0].valeur
1171
1172 salaire_euro = round(vp * classement.coefficient * taux, 2)
1173 data = dict(status='OK',
1174 salaire_euro=salaire_euro, taux=taux,
1175 salaire_devise=round(salaire_euro / taux, 2))
1176
1177 return HttpResponse(dumps(data))
1178
1179
1180 def liste_valeurs_point(request):
1181 """ Appel AJAX :
1182 input : implantation_id
1183 output : JSON liste de valeur point
1184 """
1185 method = request.method
1186 params = getattr(request, method, [])
1187 data = []
1188 annee_courante = datetime.now().year
1189 if 'implantation_id' in params \
1190 and params.get('implantation_id') is not u"":
1191 implantation_id = params.get('implantation_id')
1192 preselectionne = rh.ValeurPoint.objects \
1193 .filter(implantation=implantation_id, annee=annee_courante) \
1194 .order_by("-annee")
1195 for o in preselectionne:
1196 data.append({
1197 'id': o.id,
1198 'label': o.__unicode__(),
1199 'devise': o.devise_id,
1200 'suggestion': True
1201 })
1202 else:
1203 preselectionne = rh.ValeurPoint.objects.none()
1204
1205 liste_complete = rh.ValeurPoint.objects \
1206 .filter(annee__in=(annee_courante,)) \
1207 .order_by("-annee")
1208 for o in liste_complete.exclude(id__in=[p.id for p in preselectionne]):
1209 data.append({
1210 'id': o.id,
1211 'label': o.__unicode__(),
1212 'devise': o.devise_id,
1213 'suggestion': False
1214 })
1215 return HttpResponse(dumps(data, indent=4))