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