upgrade auf.django.admingroup
[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 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
899 if poste_rh:
900 dossier_form.fields['employe_anterieur'].queryset = (
901 dossier_form.fields['employe_anterieur'].queryset.filter(
902 rh_dossiers__poste__pk=poste_rh.pk,
903 ).distinct()
904 )
905
906 vars = dict(form=dossier_form, poste=poste, employe=employe)
907 return render(request, 'dae/embauche-dossier.html', vars)
908
909
910 # Cette fonction est appelée à partir de fonctions déjà sécurisée
911 def pre_filled_dossier(dossier_rh, employe_source, poste_rh):
912 dossier = dae.Dossier()
913
914 if employe_source != 'new' and dossier_rh.id:
915 dossier.statut_anterieur = dossier_rh.statut
916
917 # Certains dossiers ont un classement à zéro
918 if dossier_rh.classement_id > 0:
919 dossier.classement_anterieur = dossier_rh.classement
920
921 # Récupération du salaire de base
922 remun = dossier_rh.remunerations() \
923 .filter(type=1).order_by('-date_debut')
924 if remun:
925 dossier.salaire_anterieur = remun[0].montant
926 dossier.devise_anterieur = remun[0].devise
927
928 # Récupération du titulaire précédent
929 try:
930 dossiers = rh.Dossier.objects.order_by('-date_debut')
931 if poste_rh:
932 dossiers = dossiers.filter(poste=poste_rh)
933 else:
934 dossiers = rh.Dossier.objects.none()
935 if len(dossiers):
936 # Ce bloc ignore toutes les erreurs, car les données de rh
937 # manquantes peuvent en générer
938 d = dossiers[0]
939 try:
940 titulaire = d.employe
941 dossier.employe_anterieur = titulaire
942 dossier.classement_titulaire_anterieur = d.classement
943 dossier.statut_titulaire_anterieur = d.statut
944 remun = d.remunerations().filter(type=1) \
945 .order_by('-date_debut')[0]
946 dossier.salaire_titulaire_anterieur = remun.montant
947 dossier.devise_titulaire_anterieur = remun.devise
948 except:
949 pass
950 # TODO: afficher l'info, les champs ne sont pas dans le
951 # modèle dae.Dossier: nom, prenom, classement, salaire
952 pass
953
954 except (rh.Dossier.DoesNotExist):
955 dossier_rh = rh.Dossier()
956
957 return dossier
958
959
960 def _filter_remunerations(remun_qs, only_traitement=True):
961 """
962 Type de remun traitement derniers commencant a la meme date
963 """
964 # Toutes les rémunérations d'un dossier
965 remunerations = remun_qs
966
967 if only_traitement:
968 remunerations = [
969 r for r in remun_qs
970 if r.type.nature_remuneration == "Traitement"
971 ]
972
973 # On prend les dernières avec le postulat que les rémun à la même date
974 # constituent le dernier salaire
975 if len(remunerations) > 0:
976 date_debut = remunerations[0].date_debut
977
978 return [r for r in remunerations if r.date_debut == date_debut]
979
980
981
982 def _get_salaire_traitement(dossier):
983 data = {}
984
985 remunerations = _filter_remunerations(dossier.remunerations().order_by('-date_debut'))
986
987 montant = 0.0
988 montant_euros = 0.0
989 devise = None
990
991 # Les remun sont sensées être dans la même devise
992 for r in remunerations:
993 montant += float(r.montant)
994 montant_euros += r.montant_euros()
995 devise = r.devise.id
996
997 data['devise'] = devise
998 data['montant'] = montant
999 data['montant_euros'] = montant_euros
1000 return data
1001
1002
1003 @dae_groupe_requis
1004 @vieux_dossier_dans_ma_region_ou_service
1005 def dossier_resume(request, dossier_id=None):
1006 try:
1007 dossier = rh.Dossier.objects.get(id=dossier_id)
1008 except:
1009 return HttpResponseNotFound("Ce dossier n'est pas accessible")
1010
1011 data = {}
1012 data['personne'] = unicode(dossier.employe)
1013 if dossier.classement is not None:
1014 data['classement'] = dossier.classement.id
1015 if dossier.statut is not None:
1016 data['statut'] = dossier.statut.id
1017 data['implantation'] = dossier.poste.implantation.id
1018 data['poste'] = dossier.poste.nom
1019 data['d_id'] = dossier.id
1020 data.update(_get_salaire_traitement(dossier))
1021 return HttpResponse(dumps(data))
1022
1023
1024 @dae_groupe_requis
1025 @vieux_dossier_dans_ma_region_ou_service
1026 def poste_resume(request, dossier_id=None):
1027 """
1028 On travaille, en réalité sur le dossier mais on cache
1029 l'identité de la personne.
1030 """
1031 try:
1032 dossier = rh.Dossier.objects.get(id=dossier_id)
1033 except:
1034 return HttpResponseNotFound("Ce dossier n'est pas accessible")
1035
1036 data = {}
1037 data['implantation'] = dossier.poste.implantation.id
1038 data['poste'] = dossier.poste.nom
1039 data['statut'] = dossier.statut_id
1040 data['classement'] = dossier.classement_id
1041 data['d_id'] = dossier.id
1042 data.update(_get_salaire_traitement(dossier))
1043
1044 return HttpResponse(dumps(data))
1045
1046
1047 def liste_postes(request):
1048 """ Appel AJAX :
1049 input : implantation_id
1050 output : JSON liste de valeur point
1051 """
1052 method = request.method
1053 params = getattr(request, method, [])
1054 data = []
1055
1056 if 'implantation_id' in params \
1057 and params.get('implantation_id') is not u"":
1058 implantation_id = params.get('implantation_id')
1059 q = Q(implantation__id=implantation_id)
1060 else:
1061 q = Q()
1062
1063 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).filter(q)
1064 postes_rh = postes_rh.select_related(depth=1)
1065
1066 data = [('', 'Nouveau poste')] + \
1067 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
1068 postes_rh],
1069 key=lambda t: t[1])
1070 return HttpResponse(dumps(data))
1071
1072
1073 @login_required
1074 def dossier_piece(request, id, filename):
1075 """Téléchargement d'une pièce jointe à un poste."""
1076 piece = get_object_or_404(dae.DossierPiece, pk=id)
1077 if dae.Dossier.objects.ma_region_ou_service(request.user) \
1078 .filter(id=piece.dossier_id).exists():
1079 return sendfile(request, piece.fichier.path)
1080 else:
1081 return redirect_interdiction(request)
1082
1083
1084 # AJAX SECURITE non nécessaire
1085
1086 def coefficient(request):
1087 """ Appel AJAX :
1088 input : classement
1089 output : coefficient
1090 """
1091 method = request.method
1092 params = getattr(request, method, [])
1093 data = dict()
1094 if 'classement' in params and params.get('classement') is not u"":
1095 classement = params.get('classement')
1096 classement = rh.Classement.objects.get(pk=classement)
1097 data['coefficient'] = classement.coefficient
1098 else:
1099 data['coefficient'] = 0
1100 return HttpResponse(dumps(data))
1101
1102
1103 def devise(request):
1104 """ Appel AJAX :
1105 input : valeur_point
1106 output : devise, devise_code, taux_euro
1107 """
1108 method = request.method
1109 params = getattr(request, method, [])
1110 data = dict()
1111 if 'valeur_point' in params and params.get('valeur_point') is not u"":
1112 valeur_point = params.get('valeur_point')
1113 valeur_point = rh.ValeurPoint.objects.get(pk=valeur_point)
1114 annee = valeur_point.annee
1115 try:
1116 taux = rh.TauxChange.objects.get(
1117 annee=annee, devise=valeur_point.devise
1118 )
1119 except rh.TauxChange.DoesNotExist:
1120 return HttpResponseNotFound(
1121 u"Taux de change introuvable pour la devise %s "
1122 u"pour l'année %d" % (valeur_point.devise.code, annee)
1123 )
1124 except rh.TauxChange.MultipleObjectsReturned:
1125 return HttpResponseNotFound(
1126 u"Il existe plusieurs taux pour la devise %s "
1127 u"cette année-là : %s" % (valeur_point.devise.code, annee)
1128 )
1129
1130 data['devise'] = taux.devise.id
1131 data['valeur'] = valeur_point.valeur
1132 data['devise_code'] = taux.devise.code
1133 data['taux_euro'] = taux.taux
1134 else:
1135 return HttpResponseNotFound("Vous devez choisir une valeur de point")
1136 return HttpResponse(dumps(data))
1137
1138
1139 def devise_code(request):
1140 """ Appel AJAX :
1141 input : devise
1142 output : devise_code, taux_euro
1143 """
1144 method = request.method
1145 params = getattr(request, method, [])
1146 data = dict()
1147 if 'devise' in params:
1148 devise = params.get('devise')
1149 devise = rh.Devise.objects.get(pk=devise)
1150 annee = date.today().year
1151 taux = rh.TauxChange.objects.filter(annee=annee, devise=devise)
1152 if len(taux) == 0:
1153 return HttpResponseNotFound("Le taux n'est pas disponible")
1154 data['devise_code'] = devise.code
1155 data['taux_euro'] = taux[0].taux
1156 return HttpResponse(dumps(data))
1157
1158
1159 def add_remun(request, dossier, type_remun):
1160 dossier = get_object_or_404(dae.Dossier, pk=dossier)
1161 type_remun = get_object_or_404(rh.TypeRemuneration, pk=type_remun)
1162 dae.Remuneration(dossier=dossier, devise=dossier.devise,
1163 type=type_remun).save()
1164
1165 c = {
1166 'dossier': dossier,
1167 }
1168 return render(request, 'dae/embauche-remun.html', c)
1169
1170
1171 def salaire(request, implantation, devise, classement):
1172 if not devise or not classement:
1173 raise Http404
1174
1175 taux = rh.TauxChange.objects.filter(devise=devise).order_by('-annee')
1176 vp = rh.ValeurPoint.objects \
1177 .filter(implantation=implantation, devise=devise) \
1178 .order_by('-annee')
1179
1180 if vp.count() == 0:
1181 status = u"pas de valeur de point pour le couple \
1182 implantation/devise (%s/%s)" % (implantation, devise)
1183 return HttpResponse(dumps(dict(status=status)))
1184
1185 if taux.count() == 0:
1186 status = u"Pas de taux pour la devise %s" % devise
1187 return HttpResponse(dumps(dict(status=status)))
1188
1189 classement = get_object_or_404(rh.Classement, pk=classement)
1190 if classement.coefficient is None:
1191 raise Http404
1192 taux, vp = taux[0].taux, vp[0].valeur
1193
1194 salaire_euro = round(vp * classement.coefficient * taux, 2)
1195 data = dict(status='OK',
1196 salaire_euro=salaire_euro, taux=taux,
1197 salaire_devise=round(salaire_euro / taux, 2))
1198
1199 return HttpResponse(dumps(data))
1200
1201
1202 def liste_valeurs_point(request):
1203 """ Appel AJAX :
1204 input : implantation_id
1205 output : JSON liste de valeur point
1206 """
1207 method = request.method
1208 params = getattr(request, method, [])
1209 data = []
1210 annee_courante = datetime.now().year
1211 if 'implantation_id' in params \
1212 and params.get('implantation_id') is not u"":
1213 implantation_id = params.get('implantation_id')
1214 preselectionne = rh.ValeurPoint.objects \
1215 .filter(implantation=implantation_id, annee=annee_courante) \
1216 .order_by("-annee")
1217 for o in preselectionne:
1218 data.append({
1219 'id': o.id,
1220 'label': o.__unicode__(),
1221 'devise': o.devise_id,
1222 'suggestion': True
1223 })
1224 else:
1225 preselectionne = rh.ValeurPoint.objects.none()
1226
1227 liste_complete = rh.ValeurPoint.objects \
1228 .filter(annee__in=(annee_courante,)) \
1229 .order_by("-annee")
1230 for o in liste_complete.exclude(id__in=[p.id for p in preselectionne]):
1231 data.append({
1232 'id': o.id,
1233 'label': o.__unicode__(),
1234 'devise': o.devise_id,
1235 'suggestion': False
1236 })
1237 return HttpResponse(dumps(data, indent=4))