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