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