X-Git-Url: http://git.auf.org/?p=auf_rh_dae.git;a=blobdiff_plain;f=project%2Fdae%2Fviews.py;h=7277d8b3c98435ccbcaf119010f0d20b6075e4f4;hp=295947eba63bb48b71e816f8b11b93fad498a2ae;hb=7e4113d91edc99778bf8bb281b58889563704362;hpb=2a5259fee6c472bfb0cde020ac9738cc84a0bb04;ds=sidebyside diff --git a/project/dae/views.py b/project/dae/views.py index 295947e..7277d8b 100644 --- a/project/dae/views.py +++ b/project/dae/views.py @@ -1,46 +1,52 @@ # -*- encoding: utf-8 -*- -import os -import datetime -import StringIO -from collections import defaultdict -from datetime import date -from simplejson import dumps -import warnings +from datetime import date, datetime +from dateutil.relativedelta import relativedelta -from django.core.exceptions import MultipleObjectsReturned -from django.core.urlresolvers import reverse -from django.core.paginator import Paginator, InvalidPage -from django.http import Http404, HttpResponse, HttpResponseGone -from django.shortcuts import redirect, render_to_response, get_object_or_404 -from django.views.static import serve -from django.template import Context, RequestContext -from django.template.loader import get_template +from auf.django.permissions.decorators import get_object +from auf.django.references import models as ref from django.contrib import messages -from django.conf import settings -from django.contrib.auth.decorators import login_required +from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.contenttypes.models import ContentType - -from reversion.models import Version - +from django.core.paginator import Paginator, InvalidPage +from django.db.models import Q, Count +from django.http import Http404, HttpResponse, HttpResponseNotFound +from django.shortcuts import redirect, render, get_object_or_404 from sendfile import sendfile +from simplejson import dumps -from auf.django.permissions.decorators import get_object - -from dae import models as dae -from dae.forms import * -from dae.workflow import DOSSIER_ETAT_FINALISE, DOSSIER_ETAT_REGION_FINALISATION, \ - DOSSIER_ETAT_DRH_FINALISATION, POSTE_ETAT_FINALISE -from dae.decorators import redirect_interdiction, dae_groupe_requis, \ +from project.dae import models as dae +from project.dae.decorators import \ + dae_groupe_requis, \ poste_dans_ma_region_ou_service, \ dossier_dans_ma_region_ou_service, \ vieux_dossier_dans_ma_region_ou_service, \ employe_dans_ma_region_ou_service, \ dossier_est_modifiable, \ poste_est_modifiable, get_contrat -from dae.mail import send_drh_finalisation_mail -from rh import models as rh - +from project.dae.forms import FinancementFormSet, FinancementFormSetInitial +from project.dae.forms import \ + PosteComparaisonFormSet, PosteComparaisonFormSetInitial +from project.dae.forms import \ + PosteWorkflowForm, PosteForm, PostePieceFormSet, \ + DossierWorkflowForm, ChoosePosteForm, \ + EmployeForm, DossierForm, DossierPieceForm, \ + DossierComparaisonFormSet, RemunForm, ContratForm, DAENumeriseeForm, \ + label_poste_display, DAEFinaliseesSearchForm, \ + remun_formset_factory, ReadOnlyRemunFormSet +from project.dae.mail import send_drh_finalisation_mail +from project.dae.workflow import \ + DOSSIER_ETAT_FINALISE, DOSSIER_ETAT_REGION_FINALISATION, \ + DOSSIER_ETAT_DRH_FINALISATION, POSTE_ETAT_FINALISE +from project.decorators import redirect_interdiction,\ + drh_or_admin_required,\ + in_drh_or_admin,\ + in_one_of_group +from project.rh import models as rh +from project import groups + + +# Helpers def devises(): liste = [] @@ -52,13 +58,14 @@ def devises(): data['taux_euro'] = 0 else: data['taux_euro'] = taux[0].taux - data['devise_code'] = d.code + data['devise_code'] = d.id liste.append(data) return liste + @dae_groupe_requis def index(request): - return render_to_response('dae/index.html', {}, RequestContext(request)) + return render(request, 'dae/index.html', {}) ### POSTE @@ -70,26 +77,54 @@ def poste_consulter(request, key): poste = get_object_or_404(dae.Poste, pk=id) if request.POST: - validationForm = PosteWorkflowForm(request.POST, instance=poste, request=request) + validationForm = PosteWorkflowForm( + request.POST, instance=poste, request=request + ) if validationForm.is_valid(): validationForm.save() - messages.add_message(request, messages.SUCCESS, "La validation a été enregistrée.") + messages.add_message( + request, messages.SUCCESS, "La validation a été enregistrée." + ) return redirect('dae_postes_liste') else: validationForm = PosteWorkflowForm(instance=poste, request=request) - comparaisons_internes = poste.dae_comparaisons_internes.ma_region_ou_service(request.user) - vars = { + comparaisons_internes = \ + poste.dae_comparaisons_internes.ma_region_ou_service(request.user) + return render(request, 'dae/poste_consulter.html', { + 'devises': devises(), 'poste': poste, 'validationForm': validationForm, - 'comparaisons_internes': comparaisons_internes - } + 'comparaisons_internes': comparaisons_internes, + 'importer': request.user.is_superuser, + }) + + +@user_passes_test(lambda u: u.is_superuser) +@drh_or_admin_required +def poste_importer(request, id): + poste_dae = get_object_or_404(dae.Poste, id=id) + if request.method == 'POST': + if 'confirmer' in request.POST: + poste_rh = poste_dae.importer_dans_rh() + return redirect('admin:rh_poste_change', poste_rh.id) + else: + return redirect('poste_consulter', 'dae-' + id) + else: + c = { + 'poste': poste_dae, + } + return render(request, 'dae/poste_importer.html', c) - return render_to_response('dae/poste_consulter.html', vars, RequestContext(request)) @dae_groupe_requis @poste_dans_ma_region_ou_service @poste_est_modifiable +@in_one_of_group((groups.CORRESPONDANT_RH, + groups.ADMINISTRATEURS, + groups.DIRECTEUR_DE_BUREAU, + groups.DRH_NIVEAU_1, + groups.DRH_NIVEAU_2)) def poste(request, key=None): """ Formulaire pour un poste. @@ -97,42 +132,84 @@ def poste(request, key=None): il est automatiquement copié dans dae. """ + if 'creer_dossier_dae' in request.GET: + creer_dossier_dae = True + else: + creer_dossier_dae = False + + def _dupliquer_poste(poste_dae, poste_rh): + """ + Recopie les fields d'un poste RH dans un poste DAE + avec ceux-ci précédemment crées + """ + exclus = ('id', 'supprime', 'date_creation', + 'user_creation', 'date_modification', + 'user_modification', ) + fields = [f for f in poste_rh._meta.fields if f.name not in exclus] + for field in fields: + setattr(poste_dae, field.name, getattr(poste_rh, field.name)) + return poste_dae + poste, data, vars = None, dict(), dict() - if key: - # Poste existant - data['poste'] = key + # Sans key, c'est un nouveau poste + if key is None: + new = True + else: + new = False + + # Type intervention + if 'type_intervention' in request.GET: + data['type_intervention'] = request.GET['type_intervention'] + if creer_dossier_dae: + data['type_intervention'] = request.GET['creer_dossier_dae'] + + # Poste existant + poste_rh = None + if not new: source, id = key.split('-') if source == 'dae': poste = get_object_or_404(dae.Poste, pk=id) + data['poste'] = key elif source == 'rh': - p = get_object_or_404(rh.Poste, pk=id) + poste_rh = get_object_or_404(rh.Poste, pk=id) + poste = dae.Poste(id_rh=poste_rh) # Initialisation avec les valeurs du poste de rh_v1 - poste = dae.Poste(id_rh=p, nom=p.type_poste.nom) - for field in ('implantation', 'type_poste', ): - setattr(poste, field, getattr(p, field)) + poste = _dupliquer_poste(poste, poste_rh) + data['poste'] = 'rh-' + str(poste.id_rh_id) + + # prépopuler pour la modification de poste + if poste_rh is not None: + FinancementForm = FinancementFormSetInitial + PosteComparaisonForm = PosteComparaisonFormSetInitial + + qs_financements = poste_rh.rh_financements.all() + qs_comparaisons = poste_rh.rh_comparaisons_internes.all() + financements = [{'type': f.type, 'pourcentage': f.pourcentage, + 'commentaire': f.commentaire} for f in qs_financements] + comparaisons = [{'implantation': c.implantation, 'nom': c.nom, + 'montant': c.montant, 'devise': c.devise} for c in qs_comparaisons] + # formulaires normaux, avec modifications des objects FK else: - # Nouveau poste - vars['new'] = True + FinancementForm = FinancementFormSet + PosteComparaisonForm = PosteComparaisonFormSet + financements = [] + comparaisons = [] if request.POST: data.update(dict(request.POST.items())) form = PosteForm(data, instance=poste, request=request) - financementForm = FinancementForm(request.POST, instance=poste) - piecesForm = PostePieceForm(request.POST, request.FILES, instance=poste) - if isinstance(poste, dae.Poste): - comparaisons_formset = PosteComparaisonFormSet( - request.POST, - queryset=poste.dae_comparaisons_internes.ma_region_ou_service(request.user) - ) - else: - comparaisons_formset = PosteComparaisonFormSet( + financementForm = FinancementForm(request.POST, instance=poste, ) + piecesForm = PostePieceFormSet( + request.POST, request.FILES, instance=poste + ) + comparaisons_formset = PosteComparaisonForm( request.POST, - queryset=dae.PosteComparaison.objects.none() + instance=poste, ) - if form.is_valid() and piecesForm.is_valid() and financementForm.is_valid() and \ - comparaisons_formset.is_valid(): + if form.is_valid() and piecesForm.is_valid() and \ + financementForm.is_valid() and comparaisons_formset.is_valid(): poste = form.save() piecesForm.instance = poste piecesForm.save() @@ -144,38 +221,100 @@ def poste(request, key=None): for comparaison in comparaisons: comparaison.poste = poste comparaison.save() + saved_cmps = [x.id for x in comparaisons] + + for cmp_f in comparaisons_formset.forms: + comparaison = (cmp_f.instance, cmp_f) + + cmp_dossier_id = comparaison[1].cleaned_data.get( + 'cmp_poste', None) + if (not cmp_dossier_id or + comparaison[0].id not in saved_cmps): + continue + cmp_dossier_qs = rh.Dossier.objects.filter(id=cmp_dossier_id) + if not cmp_dossier_qs.count() > 0: + continue + dossier = rh.Dossier.objects.get(id=cmp_dossier_qs.get().id) + + # Get all remunerations for a period of 1 year, + # going back from either: today (if dossier has not + # yet ended), or from dossier's date_fin. + cmp_date = min(dossier.date_fin or date.today(), date.today()) + for remuneration in _filter_remunerations( + dossier.remunerations().order_by('-date_debut'), + only_traitement=False, + ): + dae.PosteComparaisonRemuneration.objects.create( + poste_comparaison = comparaison[0], + type=remuneration.type, + type_revalorisation=remuneration.type_revalorisation, + montant=remuneration.montant, + devise=remuneration.devise, + commentaire=remuneration.commentaire, + date_debut=remuneration.date_debut, + date_fin=remuneration.date_fin, + ) + + # dans le cas d'une modification de poste de RH, on recopie les PJ + if poste_rh is not None: + for piece in poste_rh.rh_pieces.all(): + dae.PostePiece(poste=poste, nom=piece.nom, + fichier=piece.fichier).save() + messages.add_message( + request, messages.SUCCESS, + "Le poste %s a été sauvegardé." % poste + ) + if creer_dossier_dae: + return redirect('embauche', key='dae-%s' % poste.id) - messages.add_message(request, messages.SUCCESS, "Le poste %s a été sauvegardé." % poste) - if request.POST.has_key('save'): + if 'save' in request.POST: return redirect('poste_consulter', key='dae-%s' % poste.id) else: return redirect('poste', key='dae-%s' % poste.id) else: - messages.add_message(request, messages.ERROR, 'Il y a des erreurs dans le formulaire.') + messages.add_message( + request, messages.ERROR, + 'Il y a des erreurs dans le formulaire.' + ) else: # 'initial' évite la validation prémature lors d'une copie de poste de # rh_v1 vers dae. form = PosteForm(initial=data, instance=poste, request=request) - piecesForm = PostePieceForm(instance=poste) - financementForm = FinancementForm(instance=poste) - if isinstance(poste, dae.Poste): - comparaisons_formset = PosteComparaisonFormSet( - queryset=poste.dae_comparaisons_internes.ma_region_ou_service(request.user) + piecesForm = PostePieceFormSet(instance=poste) + + if poste_rh is not None: + financementForm = FinancementForm( + initial=financements, instance=poste ) - else: - comparaisons_formset = PosteComparaisonFormSet( - queryset=dae.PosteComparaison.objects.none() + comparaisons_formset = PosteComparaisonForm( + initial=comparaisons, + instance=poste, ) + # cas de la création d'un nouveau poste + else: + financementForm = FinancementForm(instance=poste) + comparaisons_formset = PosteComparaisonForm(instance=poste) + + + # Modify queryset so that it is limited to users' rights: + imp_qs = dae.ProxyImplantation.dae_manager.ma_region_ou_service( + request.user) + + for cmp_form in comparaisons_formset.forms: + cmp_form.fields['implantation'].queryset = imp_qs vars.update(dict( form=form, poste=poste, poste_key=key, piecesForm=piecesForm, financementForm=financementForm, - comparaisons_formset=comparaisons_formset + comparaisons_formset=comparaisons_formset, + poste_rh=poste_rh, + creer_dossier_dae=creer_dossier_dae, )) - return render_to_response('dae/poste.html', vars, RequestContext(request)) + return render(request, 'dae/poste.html', vars) + @dae_groupe_requis def postes_liste(request): @@ -183,22 +322,31 @@ def postes_liste(request): content_type = ContentType.objects.get_for_model(dae.Poste) extra_select = {'derniere_validation': ( "SELECT MAX(date) FROM workflow_workflowcommentaire " - "WHERE content_type_id = '%s' AND object_id = dae_poste.id" % content_type.id + "WHERE content_type_id = '%s' AND object_id = dae_poste.id" % + content_type.id )} postes_a_traiter = dae.Poste.objects.mes_choses_a_faire(request.user) \ - .extra(select=extra_select).order_by('-date_creation') + .annotate(num_dae=Count('dae_dossiers')) \ + .filter(num_dae=0) \ + .extra(select=extra_select).order_by('-id') postes_en_cours = dae.Poste.objects.ma_region_ou_service(request.user) \ - .extra(select=extra_select).filter(~Q(etat=POSTE_ETAT_FINALISE)).order_by('-date_creation') - return render_to_response('dae/postes_liste.html', { + .annotate(num_dae=Count('dae_dossiers')) \ + .filter(num_dae=0) \ + .extra(select=extra_select) \ + .filter(~Q(etat=POSTE_ETAT_FINALISE)) \ + .order_by('-id') + return render(request, 'dae/postes_liste.html', { 'postes_a_traiter': postes_a_traiter, 'postes_en_cours': postes_en_cours, - }, RequestContext(request)) + }) + @login_required def poste_piece(request, id, filename): """Téléchargement d'une pièce jointe à un poste.""" piece = get_object_or_404(dae.PostePiece, pk=id) - if dae.Poste.objects.ma_region_ou_service(request.user).filter(id=piece.poste_id).exists(): + if dae.Poste.objects.ma_region_ou_service(request.user) \ + .filter(id=piece.poste_id).exists(): return sendfile(request, piece.fichier.path) else: return redirect_interdiction(request) @@ -207,44 +355,81 @@ def poste_piece(request, id, filename): ### DOSSIER def filtered_type_remun(): - defaut = (2, 3, 8, 17) # salaire de base, indemnité de fonction, charges patronales + defaut = (2, 3, 8, 17) # salaire de base, indemnité de fonction, + # charges patronales return rh.TypeRemuneration.objects.filter(pk__in=defaut) + @dae_groupe_requis @dossier_dans_ma_region_ou_service -def embauche_consulter(request, dossier_id): +def embauche_consulter(request, dossier_id): dossier = get_object_or_404(dae.Dossier, pk=dossier_id) etat_precedent = dossier.etat if request.POST: - validationForm = DossierWorkflowForm(request.POST, instance=dossier, request=request) + validationForm = DossierWorkflowForm( + request.POST, instance=dossier, request=request + ) if validationForm.is_valid(): if etat_precedent == DOSSIER_ETAT_REGION_FINALISATION and \ - validationForm.cleaned_data['etat'] == DOSSIER_ETAT_DRH_FINALISATION: + validationForm.cleaned_data['etat'] == \ + DOSSIER_ETAT_DRH_FINALISATION: send_drh_finalisation_mail(request, dossier) validationForm.save() - messages.add_message(request, messages.SUCCESS, "La validation a été enregistrée.") + messages.add_message( + request, messages.SUCCESS, "La validation a été enregistrée." + ) return redirect('dae_embauches_liste') else: validationForm = DossierWorkflowForm(instance=dossier, request=request) - comparaisons_internes = dossier.poste.dae_comparaisons_internes.ma_region_ou_service(request.user) + comparaisons_internes = \ + dossier.poste.dae_comparaisons_internes.ma_region_ou_service( + request.user + ) comparaisons = dossier.dae_comparaisons.ma_region_ou_service(request.user) - vars = { + + return render(request, 'dae/embauche_consulter.html', { 'dossier': dossier, + 'devises': devises(), 'validationForm': validationForm, 'comparaisons_internes': comparaisons_internes, - 'comparaisons': comparaisons - } + 'comparaisons': comparaisons, + 'importer': request.user.is_superuser, + }) + + +@user_passes_test(lambda u: u.is_superuser) +@dae_groupe_requis +@dossier_dans_ma_region_ou_service +def embauche_importer(request, dossier_id=None): + dossier_dae = get_object_or_404(dae.Dossier, id=dossier_id) + if request.method == 'POST': + if 'confirmer' in request.POST: + dossier_rh = dossier_dae.importer_dans_rh() + return redirect('admin:rh_dossier_change', dossier_rh.id) + else: + return redirect('embauches_finalisees') + else: + c = { + 'dossier': dossier_dae, + } + return render(request, 'dae/embauche_importer.html', c) - mode = request.GET.get('mode', None) - return render_to_response('dae/embauche_consulter.html', vars, RequestContext(request)) @dae_groupe_requis def embauche_choisir_poste(request): - return render_to_response('dae/embauche-choisir-poste.html', { - 'form': ChoosePosteForm(request=request) - }, RequestContext(request)) + if request.POST: + form = ChoosePosteForm(data=request.POST, request=request) + if form.is_valid(): + return form.redirect() + else: + form = ChoosePosteForm(request=request) + c = { + 'form': form, + } + return render(request, 'dae/embauche-choisir-poste.html', c) + @dae_groupe_requis @dossier_dans_ma_region_ou_service @@ -275,7 +460,9 @@ def embauche(request, key=None, dossier_id=None): else: raise Http404 - employe_form = EmployeForm(request.POST, instance=employe, request=request) + employe_form = EmployeForm( + request.POST, instance=employe, request=request + ) if employe_form.is_valid(): data = dict(request.POST.items()) @@ -290,11 +477,16 @@ def embauche(request, key=None, dossier_id=None): dossier.employe = employe_form.instance dossier_form = DossierForm(request.POST, instance=dossier) - piecesForm = DossierPieceForm(request.POST, request.FILES, instance=dossier) + piecesForm = DossierPieceForm( + request.POST, request.FILES, instance=dossier + ) comparaisons_formset = DossierComparaisonFormSet( request.POST, - queryset=dossier.dae_comparaisons.ma_region_ou_service(request.user) + queryset=dossier.dae_comparaisons.ma_region_ou_service( + request.user + ), ) + remunForm = RemunForm(request.POST, instance=dossier) if employe_form.is_valid() and \ @@ -308,19 +500,61 @@ def embauche(request, key=None, dossier_id=None): remunForm.save() # Ne remplacer que les comparaisons de ma région + comparaisons = comparaisons_formset.save(commit=False) for comparaison in comparaisons: comparaison.dossier = dossier comparaison.save() - - messages.success(request, "Le dossier %s a été sauvegardé." % dossier) - if request.POST.has_key('save'): + saved_cmps = [x.id for x in comparaisons] + + for cmp_f in comparaisons_formset.forms: + comparaison = (cmp_f.instance, cmp_f) + + cmp_dossier_id = comparaison[1].cleaned_data.get( + 'cmp_dossier', None) + if (not cmp_dossier_id or + comparaison[0].id not in saved_cmps): + continue + cmp_dossier_qs = rh.Dossier.objects.filter(id=cmp_dossier_id) + if not cmp_dossier_qs.count() > 0: + continue + rhdossier = rh.Dossier.objects.get(id=cmp_dossier_qs.get().id) + + + # Get all remunerations for a period of 1 year, + # going back from either: today (if cdossier has not + # yet ended), or from cdossier's date_fin. + cmp_date = min(rhdossier.date_fin or date.today(), date.today()) + for remuneration in _filter_remunerations( + rhdossier.remunerations().order_by('-date_debut'), + only_traitement=False, + ): + dae.DossierComparaisonRemuneration.objects.create( + dossier_comparaison = comparaison[0], + type=remuneration.type, + type_revalorisation=remuneration.type_revalorisation, + montant=remuneration.montant, + devise=remuneration.devise, + commentaire=remuneration.commentaire, + date_debut=remuneration.date_debut, + date_fin=remuneration.date_fin, + ) + + messages.success( + request, "Le dossier %s a été sauvegardé." % dossier + ) + if 'save' in request.POST: return redirect('embauche_consulter', dossier_id=dossier.id) else: - return redirect('embauche', key=dossier.poste.key, dossier_id=dossier.id) + return redirect( + 'embauche', key=dossier.poste.key, dossier_id=dossier.id + ) else: - messages.add_message(request, messages.ERROR, 'Il y a des erreurs dans le formulaire.') + messages.add_message( + request, messages.ERROR, + 'Il y a des erreurs dans le formulaire.' + ) else: # Initialisation d'un formulaire vide @@ -328,7 +562,9 @@ def embauche(request, key=None, dossier_id=None): dossier = get_object_or_404(dae.Dossier, pk=dossier_id) employe = dossier.employe data = dict(employe='dae-%s' % employe.id) - employe_form = EmployeForm(initial=data, instance=employe, request=request) + employe_form = EmployeForm( + initial=data, instance=employe, request=request + ) else: dossier_rh = rh.Dossier() poste_rh = poste.id_rh @@ -338,18 +574,36 @@ def embauche(request, key=None, dossier_id=None): dossier_form = DossierForm(instance=dossier) piecesForm = DossierPieceForm(instance=dossier) comparaisons_formset = DossierComparaisonFormSet( - queryset=dossier.dae_comparaisons.ma_region_ou_service(request.user) + queryset=dossier.dae_comparaisons.ma_region_ou_service( + request.user + ) ) remunForm = RemunForm(instance=dossier) try: - comparaisons_internes = dossier.poste.dae_comparaisons_internes.ma_region_ou_service( - request.user - ) + comparaisons_internes = \ + dossier.poste.dae_comparaisons_internes.ma_region_ou_service( + request.user + ) except dae.Poste.DoesNotExist: comparaisons_internes = [] - return render_to_response('dae/embauche.html', { + # Modify queryset so that it is limited to users' rights: + imp_qs = dae.ProxyImplantation.dae_manager.ma_region_ou_service( + request.user) + + for cmp_form in comparaisons_formset.forms: + cmp_form.fields['implantation'].queryset = imp_qs + + # Comment out for now. + # if dossier and dossier.poste: + # dossier_form.fields['employe_anterieur'].queryset = ( + # dossier_form.fields['employe_anterieur'].queryset.filter( + # rh_dossiers__poste=dossier.poste, + # ).distinct() + # ) + + c = { 'type_remun': filtered_type_remun(), 'devises': devises(), 'poste': poste, @@ -358,8 +612,10 @@ def embauche(request, key=None, dossier_id=None): 'remunForm': remunForm, 'comparaisons_formset': comparaisons_formset, 'forms': dict(employe=employe_form, dossier=dossier_form, ), - 'comparaisons_internes': comparaisons_internes - }, RequestContext(request)) + 'comparaisons_internes': comparaisons_internes, + } + return render(request, 'dae/embauche.html', c) + @dae_groupe_requis @dossier_dans_ma_region_ou_service @@ -368,23 +624,69 @@ def embauches_liste(request): content_type = ContentType.objects.get_for_model(dae.Dossier) extra_select = {'derniere_validation': ( "SELECT MAX(date) FROM workflow_workflowcommentaire " - "WHERE content_type_id = '%s' AND object_id = dae_dossier.id" % content_type.id + "WHERE content_type_id = '%s' AND object_id = dae_dossier.id" % + content_type.id )} - embauches_a_traiter = dae.Dossier.objects.mes_choses_a_faire(request.user) \ - .extra(select=extra_select).order_by('-date_creation') - embauches_en_cours = dae.Dossier.objects.ma_region_ou_service(request.user) \ - .extra(select=extra_select).order_by('-date_creation').exclude(etat=DOSSIER_ETAT_FINALISE) - return render_to_response('dae/embauches_liste.html', { + embauches_a_traiter = dae.Dossier.objects \ + .mes_choses_a_faire(request.user) \ + .extra(select=extra_select).order_by('-id') + embauches_en_cours = dae.Dossier.objects \ + .ma_region_ou_service(request.user) \ + .extra(select=extra_select) \ + .order_by('-id') \ + .exclude(etat=DOSSIER_ETAT_FINALISE) + c = { 'embauches_a_traiter': embauches_a_traiter, 'embauches_en_cours': embauches_en_cours, - }, RequestContext(request)) + } + return render(request, 'dae/embauches_liste.html', c) + @dae_groupe_requis def embauches_finalisees(request): """Liste des embauches finalisées.""" + + ### POST + + if request.method == 'POST': + if 'supprimer' in request.POST: + ids = request.POST.getlist('ids') + dossiers = dae.Dossier.objects.filter(id__in=ids) + count = dossiers.count() + if count > 0: + dossiers.delete() + messages.success(request, u'%d dossiers supprimés' % count) + return redirect(request.get_full_path()) + + ### GET + embauches = dae.Dossier.objects.ma_region_ou_service(request.user) \ .filter(etat=DOSSIER_ETAT_FINALISE) + # Recherche + search_form = DAEFinaliseesSearchForm(request.GET) + if search_form.is_valid(): + q = search_form.cleaned_data.get('q').strip() + importees = search_form.cleaned_data.get('importees') + if q: + criteria = [ + Q(**{ + 'poste__implantation__zone_administrative__nom__icontains': + word + }) | + Q(poste__implantation__zone_administrative__code=word) | + Q(poste__implantation__nom__icontains=word) | + Q(poste__nom__icontains=word) | + Q(employe__nom__icontains=word) | + Q(employe__prenom__icontains=word) + for word in q.split() + ] + embauches = embauches.filter(*criteria) + if importees == 'oui': + embauches = embauches.exclude(dossier_rh=None) + elif importees == 'non': + embauches = embauches.filter(dossier_rh=None) + # Tri tri = request.GET.get('tri', None) if tri and tri.startswith('-'): @@ -393,7 +695,9 @@ def embauches_finalisees(request): else: dir = '' if tri == 'region': - embauches = embauches.order_by(dir + 'poste__implantation__region__nom') + embauches = embauches.order_by( + dir + 'poste__implantation__zone_administrative__nom' + ) elif tri == 'implantation': embauches = embauches.order_by(dir + 'poste__implantation__nom') elif tri == 'poste': @@ -413,9 +717,12 @@ def embauches_finalisees(request): except InvalidPage: page = paginator.page(1) - return render_to_response('dae/embauches_finalisees.html', { - 'embauches': page - }, RequestContext(request)) + return render(request, 'dae/embauches_finalisees.html', { + 'embauches': page, + 'search_form': search_form, + 'importer': in_drh_or_admin(request.user) + }) + def employe(request, key): """ Récupération AJAX de l'employé pour la page d'embauche. """ @@ -437,7 +744,10 @@ def employe(request, key): for field in ('prenom', 'nom', 'genre'): setattr(employe, field, getattr(e, field)) - return HttpResponse(EmployeForm(initial=data, instance=employe, request=request).as_table()) + return HttpResponse( + EmployeForm(initial=data, instance=employe, request=request).as_table() + ) + ### CONTRATS @@ -446,6 +756,7 @@ def employe(request, key): def contrat(request, contrat, filename): return sendfile(request, contrat.fichier.path) + @dae_groupe_requis @get_contrat def contrat_supprimer(request, contrat): @@ -453,9 +764,11 @@ def contrat_supprimer(request, contrat): if 'oui' in request.POST: contrat.delete() return redirect('embauche_consulter', dossier_id=contrat.dossier.id) - return render_to_response('dae/contrat-supprimer.html', { - 'contrat': contrat - }, RequestContext(request)) + c = { + 'contrat': contrat, + } + return render(request, 'dae/contrat-supprimer.html', c) + @dae_groupe_requis @dossier_dans_ma_region_ou_service @@ -466,13 +779,18 @@ def embauche_ajouter_contrat(request, dossier_id=None): if form.is_valid(): contrat = form.save(commit=False) contrat.dossier = dossier + contrat.date_debut = dossier.contrat_date_debut + contrat.date_fin = dossier.contrat_date_fin contrat.save() return redirect('embauche_consulter', dossier_id=dossier.id) else: form = ContratForm() - return render_to_response('dae/embauche-ajouter-contrat.html', { - 'form': form - }, RequestContext(request)) + + c = { + 'form': form, + } + return render(request, 'dae/embauche-ajouter-contrat.html', c) + ### DAE NUMERISEE @@ -480,6 +798,7 @@ def embauche_ajouter_contrat(request, dossier_id=None): def dae_numerisee(request, dossier): return sendfile(request, dossier.dae_numerisee.path) + @get_object(dae.Dossier, 'modifier_dae_numerisee') def dae_numerisee_modifier(request, dossier): if request.method == 'POST': @@ -489,9 +808,12 @@ def dae_numerisee_modifier(request, dossier): return redirect('embauche_consulter', dossier_id=dossier.id) else: form = DAENumeriseeForm(instance=dossier) - return render_to_response('dae/dae_numerisee_modifier.html', { - 'form': form - }, RequestContext(request)) + + c = { + 'form': form, + } + return render(request, 'dae/dae_numerisee_modifier.html', c) + @get_object(dae.Dossier, 'modifier_dae_numerisee') def dae_numerisee_supprimer(request, dossier): @@ -500,11 +822,11 @@ def dae_numerisee_supprimer(request, dossier): dossier.dae_numerisee = None dossier.save() return redirect('embauche_consulter', dossier_id=dossier.id) - return render_to_response('dae/dae_numerisee_supprimer.html', {}, RequestContext(request)) + return render(request, 'dae/dae_numerisee_supprimer.html', {}) + -################################################################################ # AJAX SECURISE -################################################################################ + @dae_groupe_requis @employe_dans_ma_region_ou_service def dossier(request, poste_key, employe_key): @@ -516,64 +838,64 @@ def dossier(request, poste_key, employe_key): # Récupérer la devise de l'implantation lié au poste implantation_devise = poste.get_default_devise() - data.update({'devise' : implantation_devise}) + data.update({'devise': implantation_devise}) if poste.id_rh_id is not None: poste_rh = get_object_or_404(rh.Poste, pk=poste.id_rh_id) else: poste_rh = None - ########################################################################################## # NOUVEL EMPLOYE - ########################################################################################## if employe_key == '': employe_source = 'new' employe = None dossier_rh = rh.Dossier() dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh) - ########################################################################################## # EMPLOYE DAE - ########################################################################################## if employe_key.startswith('dae'): - employe_source, employe_id = employe_key.split('-') - employe_dae = get_object_or_404(dae.Employe, pk=employe_id) - - # récupération de l'ancien dossier rh v1 pour l'employe DAE - try: - dossier_rh = rh.Dossier.objects.get(employe=employe_dae.id_rh_id, date_fin=None) - except (rh.Dossier.DoesNotExist): - dossier_rh = rh.Dossier() - - # on tente de récupérer le dossier DAE, au pire on le contruit en le - # prépoluant avec son dossier rh v1. - try: - dossier = dae.Dossier.objects.get(employe=employe_dae, poste=poste) - except (dae.Dossier.DoesNotExist): - dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh) - employe = employe_dae.id_rh - ########################################################################################## + employe_source, employe_id = employe_key.split('-') + employe_dae = get_object_or_404(dae.Employe, pk=employe_id) + + # récupération de l'ancien dossier rh v1 pour l'employe DAE + try: + dossier_rh = rh.Dossier.objects.get( + employe=employe_dae.id_rh_id, date_fin=None + ) + except (rh.Dossier.DoesNotExist): + dossier_rh = rh.Dossier() + + # on tente de récupérer le dossier DAE, au pire on le contruit en le + # prépoluant avec son dossier rh v1. + try: + dossier = dae.Dossier.objects.get(employe=employe_dae, poste=poste) + except (dae.Dossier.DoesNotExist): + dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh) + employe = employe_dae.id_rh + # EMPLOYE RH v1 - ########################################################################################## if employe_key.startswith('rh'): - employe_source, employe_id = employe_key.split('-') - employe_rh = get_object_or_404(rh.Employe, pk=employe_id) - - # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il n'en a pas, - # on en fournit un nouveau qui servira uniquement un créer un nouveau dossier DAE. - try: - dossier_rh = rh.Dossier.objects.get(employe=employe_rh, date_fin=None) - except (rh.Dossier.DoesNotExist): - dossier_rh = rh.Dossier() - dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh) - employe = employe_rh + employe_source, employe_id = employe_key.split('-') + employe_rh = get_object_or_404(rh.Employe, pk=employe_id) + + # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il + # n'en a pas, on en fournit un nouveau qui servira uniquement un + # créer un nouveau dossier DAE. + try: + dossier_rh = rh.Dossier.objects.get( + employe=employe_rh, date_fin=None + ) + except (rh.Dossier.DoesNotExist): + dossier_rh = rh.Dossier() + dossier = pre_filled_dossier(dossier_rh, employe_source, poste_rh) + employe = employe_rh dossier_form = DossierForm(initial=data, instance=dossier) vars = dict(form=dossier_form, poste=poste, employe=employe) - return render_to_response('dae/embauche-dossier.html', vars, - RequestContext(request)) + return render(request, 'dae/embauche-dossier.html', vars) -# @Cette fonction est appelée à partir de fonctions déjà sécurisée + +# Cette fonction est appelée à partir de fonctions déjà sécurisée def pre_filled_dossier(dossier_rh, employe_source, poste_rh): dossier = dae.Dossier() @@ -585,7 +907,8 @@ def pre_filled_dossier(dossier_rh, employe_source, poste_rh): dossier.classement_anterieur = dossier_rh.classement # Récupération du salaire de base - remun = dossier_rh.remunerations().filter(type=1).order_by('-date_debut') + remun = dossier_rh.remunerations() \ + .filter(type=1).order_by('-date_debut') if remun: dossier.salaire_anterieur = remun[0].montant dossier.devise_anterieur = remun[0].devise @@ -606,7 +929,8 @@ def pre_filled_dossier(dossier_rh, employe_source, poste_rh): dossier.employe_anterieur = titulaire dossier.classement_titulaire_anterieur = d.classement dossier.statut_titulaire_anterieur = d.statut - remun = d.remunerations().filter(type=1).order_by('-date_debut')[0] + remun = d.remunerations().filter(type=1) \ + .order_by('-date_debut')[0] dossier.salaire_titulaire_anterieur = remun.montant dossier.devise_titulaire_anterieur = remun.devise except: @@ -620,34 +944,71 @@ def pre_filled_dossier(dossier_rh, employe_source, poste_rh): return dossier + +def _filter_remunerations(remun_qs, only_traitement=True): + """ + Type de remun traitement derniers commencant a la meme date + """ + # Toutes les rémunérations d'un dossier + remunerations = remun_qs + + if only_traitement: + remunerations = [ + r for r in remun_qs + if r.type.nature_remuneration == "Traitement" + ] + + # On prend les dernières avec le postulat que les rémun à la même date + # constituent le dernier salaire + if len(remunerations) > 0: + date_debut = remunerations[0].date_debut + + return [r for r in remunerations if r.date_debut == date_debut] + + + +def _get_salaire_traitement(dossier): + data = {} + + remunerations = _filter_remunerations(dossier.remunerations().order_by('-date_debut')) + + montant = 0.0 + montant_euros = 0.0 + devise = None + + # Les remun sont sensées être dans la même devise + for r in remunerations: + montant += float(r.montant) + montant_euros += r.montant_euros() + devise = r.devise.id + + data['devise'] = devise + data['montant'] = montant + data['montant_euros'] = montant_euros + return data + + @dae_groupe_requis @vieux_dossier_dans_ma_region_ou_service def dossier_resume(request, dossier_id=None): try: dossier = rh.Dossier.objects.get(id=dossier_id) except: - return HttpResponseGone("Ce dossier n'est pas accessible") + return HttpResponseNotFound("Ce dossier n'est pas accessible") data = {} data['personne'] = unicode(dossier.employe) - data['classement'] = dossier.classement.id + if dossier.classement is not None: + data['classement'] = dossier.classement.id if dossier.statut is not None: data['statut'] = dossier.statut.id data['implantation'] = dossier.poste.implantation.id - data['poste'] = u"%s %s" % (dossier.poste.type_poste.nom, dossier.poste.nom) - salaire = dossier.get_salaire() - if salaire is not None: - data['montant'] = salaire.montant - else: - data['montant'] = None - if salaire is not None and salaire.devise is not None: - data['devise'] = salaire.devise.id - data['montant_euros'] = salaire.montant_euro() - else: - data['devise'] = None - data['montant_euros'] = 0 + data['poste'] = dossier.poste.nom + data['d_id'] = dossier.id + data.update(_get_salaire_traitement(dossier)) return HttpResponse(dumps(data)) + @dae_groupe_requis @vieux_dossier_dans_ma_region_ou_service def poste_resume(request, dossier_id=None): @@ -658,21 +1019,19 @@ def poste_resume(request, dossier_id=None): try: dossier = rh.Dossier.objects.get(id=dossier_id) except: - return HttpResponseGone("Ce dossier n'est pas accessible") + return HttpResponseNotFound("Ce dossier n'est pas accessible") - salaire = dossier.get_salaire() data = {} data['implantation'] = dossier.poste.implantation.id - data['poste'] = u"%s %s" % (dossier.poste.type_poste.nom, dossier.poste.nom) - data['montant'] = salaire.montant - if salaire is not None: - data['devise'] = salaire.devise.id - data['montant_euros'] = salaire.montant_euro() - else: - data['devise'] = None - data['montant_euros'] = 0 + data['poste'] = dossier.poste.nom + data['statut'] = dossier.statut_id + data['classement'] = dossier.classement_id + data['d_id'] = dossier.id + data.update(_get_salaire_traitement(dossier)) + return HttpResponse(dumps(data)) + def liste_postes(request): """ Appel AJAX : input : implantation_id @@ -682,52 +1041,36 @@ def liste_postes(request): params = getattr(request, method, []) data = [] - # Voir le code de _poste_choices dans forms.py - dae_ = dae.Poste.objects.actifs().filter(id_rh__isnull=True) - copies = dae.Poste.objects.exclude(id_rh__isnull=True) - rh_postes_actifs = rh.Poste.objects.actifs() - - if 'implantation_id' in params and params.get('implantation_id') is not u"": + if 'implantation_id' in params \ + and params.get('implantation_id') is not u"": implantation_id = params.get('implantation_id') - dae_ = dae_.filter(implantation__id=implantation_id) - copies = copies.filter(implantation__id=implantation_id) - rh_postes_actifs = rh_postes_actifs.filter(implantation__id=implantation_id) + q = Q(implantation__id=implantation_id) + else: + q = Q() - id_copies = [p.id_rh_id for p in copies.all()] - rhv1 = rh_postes_actifs.exclude(id__in=id_copies) - rhv1 = rhv1.select_related(depth=1) + postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).filter(q) + postes_rh = postes_rh.select_related(depth=1) - data = [('', 'Nouveau poste')] + sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] + [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1], key=lambda t: t[1]) + data = [('', 'Nouveau poste')] + \ + sorted([('rh-%s' % p.id, label_poste_display(p)) for p in + postes_rh], + key=lambda t: t[1]) return HttpResponse(dumps(data)) + @login_required def dossier_piece(request, id, filename): """Téléchargement d'une pièce jointe à un poste.""" piece = get_object_or_404(dae.DossierPiece, pk=id) - if dae.Dossier.objects.ma_region_ou_service(request.user).filter(id=piece.dossier_id).exists(): + if dae.Dossier.objects.ma_region_ou_service(request.user) \ + .filter(id=piece.dossier_id).exists(): return sendfile(request, piece.fichier.path) else: return redirect_interdiction(request) -@login_required -def importer_choix_dossier(request): - method = request.method - params = getattr(request, method, None) - if params: - form = DAEImportableForm(params) - if form.is_valid(): - form.importer_poste() - messages.add_message(request, messages.SUCCESS, "L'importation a réussie.") - else: - form = DAEImportableForm() - - vars = dict(form=form, ) - return render_to_response('admin/dae/importer_choix_dossier.html', vars, - RequestContext(request)) -################################################################################ # AJAX SECURITE non nécessaire -################################################################################ + def coefficient(request): """ Appel AJAX : input : classement @@ -758,19 +1101,29 @@ def devise(request): valeur_point = rh.ValeurPoint.objects.get(pk=valeur_point) annee = valeur_point.annee try: - taux = rh.TauxChange.objects.get(annee=annee, devise=valeur_point.devise) - except MultipleObjectsReturned: - return HttpResponseGone(u"Il existe plusieurs taux pour la devise %s cette année-là : %s" % \ - (valeur_point.devise.code, annee)) + taux = rh.TauxChange.objects.get( + annee=annee, devise=valeur_point.devise + ) + except rh.TauxChange.DoesNotExist: + return HttpResponseNotFound( + u"Taux de change introuvable pour la devise %s " + u"pour l'année %d" % (valeur_point.devise.code, annee) + ) + except rh.TauxChange.MultipleObjectsReturned: + return HttpResponseNotFound( + u"Il existe plusieurs taux pour la devise %s " + u"cette année-là : %s" % (valeur_point.devise.code, annee) + ) data['devise'] = taux.devise.id data['valeur'] = valeur_point.valeur data['devise_code'] = taux.devise.code data['taux_euro'] = taux.taux else: - return HttpResponseGone("Vous devez choisir une valeur de point") + return HttpResponseNotFound("Vous devez choisir une valeur de point") return HttpResponse(dumps(data)) + def devise_code(request): """ Appel AJAX : input : devise @@ -785,41 +1138,55 @@ def devise_code(request): annee = date.today().year taux = rh.TauxChange.objects.filter(annee=annee, devise=devise) if len(taux) == 0: - return HttpResponseGone("Le taux n'est pas disponible") + return HttpResponseNotFound("Le taux n'est pas disponible") data['devise_code'] = devise.code data['taux_euro'] = taux[0].taux return HttpResponse(dumps(data)) + def add_remun(request, dossier, type_remun): dossier = get_object_or_404(dae.Dossier, pk=dossier) type_remun = get_object_or_404(rh.TypeRemuneration, pk=type_remun) dae.Remuneration(dossier=dossier, devise=dossier.devise, type=type_remun).save() - return render_to_response('dae/embauche-remun.html', dict(dossier=dossier), - RequestContext(request)) + c = { + 'dossier': dossier, + } + return render(request, 'dae/embauche-remun.html', c) + def salaire(request, implantation, devise, classement): if not devise or not classement: raise Http404 - taux_impl = rh.TauxChange.objects.filter(implantation=implantation) \ - .order_by('-annee') taux = rh.TauxChange.objects.filter(devise=devise).order_by('-annee') - vp = rh.ValeurPoint.objects.filter(implantation=implantation) \ - .order_by('-annee') - if vp.count() * taux.count() * taux_impl.count() == 0: - raise Http404 + vp = rh.ValeurPoint.objects \ + .filter(implantation=implantation, devise=devise) \ + .order_by('-annee') + + if vp.count() == 0: + status = u"pas de valeur de point pour le couple \ +implantation/devise (%s/%s)" % (implantation, devise) + return HttpResponse(dumps(dict(status=status))) + + if taux.count() == 0: + status = u"Pas de taux pour la devise %s" % devise + return HttpResponse(dumps(dict(status=status))) classement = get_object_or_404(rh.Classement, pk=classement) - taux, taux_impl, vp = taux[0].taux, taux_impl[0].taux, vp[0].valeur + if classement.coefficient is None: + raise Http404 + taux, vp = taux[0].taux, vp[0].valeur - salaire_euro = round(vp * classement.coefficient * taux_impl, 2) - data = dict(salaire_euro=salaire_euro, taux=taux, + salaire_euro = round(vp * classement.coefficient * taux, 2) + data = dict(status='OK', + salaire_euro=salaire_euro, taux=taux, salaire_devise=round(salaire_euro / taux, 2)) return HttpResponse(dumps(data)) + def liste_valeurs_point(request): """ Appel AJAX : input : implantation_id @@ -828,16 +1195,31 @@ def liste_valeurs_point(request): method = request.method params = getattr(request, method, []) data = [] - annee_courante = datetime.datetime.now().year - if 'implantation_id' in params and params.get('implantation_id') is not u"": + annee_courante = datetime.now().year + if 'implantation_id' in params \ + and params.get('implantation_id') is not u"": implantation_id = params.get('implantation_id') - preselectionne = rh.ValeurPoint.objects.filter(implantation=implantation_id, annee__in=(annee_courante,)).order_by("-annee") + preselectionne = rh.ValeurPoint.objects \ + .filter(implantation=implantation_id, annee=annee_courante) \ + .order_by("-annee") for o in preselectionne: - data.append({'id' : o.id, 'label' : o.__unicode__(), 'devise' : o.devise_id, 'suggestion' : True}) + data.append({ + 'id': o.id, + 'label': o.__unicode__(), + 'devise': o.devise_id, + 'suggestion': True + }) else: preselectionne = rh.ValeurPoint.objects.none() - liste_complete = rh.ValeurPoint.objects.filter(annee__in=(annee_courante,)).order_by("-annee") + liste_complete = rh.ValeurPoint.objects \ + .filter(annee__in=(annee_courante,)) \ + .order_by("-annee") for o in liste_complete.exclude(id__in=[p.id for p in preselectionne]): - data.append({'id' : o.id, 'label' : o.__unicode__(), 'devise' : o.devise_id, 'suggestion' : False}) + data.append({ + 'id': o.id, + 'label': o.__unicode__(), + 'devise': o.devise_id, + 'suggestion': False + }) return HttpResponse(dumps(data, indent=4))