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