1 # -*- encoding: utf-8 -*-
6 from collections
import defaultdict
7 from datetime
import date
8 from simplejson
import dumps
11 from django
.core
.urlresolvers
import reverse
12 from django
.http
import Http404
, HttpResponse
, HttpResponseGone
13 from django
.shortcuts
import redirect
, render_to_response
, get_object_or_404
14 from django
.template
import Context
, RequestContext
15 from django
.template
.loader
import get_template
16 from django
.contrib
import messages
17 from django
.conf
import settings
19 from reversion
.models
import Version
21 from project
.dae
import models
as dae
22 from project
.rh_v1
import models
as rh
24 from decorators
import dae_groupe_requis
, \
25 poste_dans_ma_region_ou_service
, \
26 dossier_dans_ma_region_ou_service
, \
27 vieux_dossier_dans_ma_region_ou_service
, \
28 employe_dans_ma_region_ou_service
, \
29 dossier_est_modifiable
, \
35 for d
in rh
.Devise
.objects
.all():
36 annee
= date
.today().year
37 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=d
)
42 data
['taux_euro'] = taux
[0].taux
43 data
['devise_code'] = d
.code
47 def reponse_pdf(template_src
, context_dict
):
49 Générer une réponse HTTP avec un PDF
51 import ho
.pisa
as pisa
54 for f
in ('css/pdf.css', 'css/dae.css'):
55 css_file
= os
.path
.join(settings
.MEDIA_ROOT
, f
)
56 css
+= open(css_file
, 'r').read()
57 context_dict
['css'] = css
59 template
= get_template(template_src
)
60 context
= Context(context_dict
)
61 html
= template
.render(context
)
62 result
= StringIO
.StringIO()
63 pdf
= pisa
.pisaDocument(html
, result
, encoding
='UTF-8')
65 return HttpResponse(result
.getvalue(), mimetype
='application/pdf')
66 return HttpResponse("impossible de générer le pdf! %s" % html
)
71 return render_to_response('dae/index.html', {}, RequestContext(request
))
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)
80 validationForm
= PosteWorkflowForm(request
.POST
, instance
=poste
, request
=request
)
81 if validationForm
.is_valid():
83 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
84 return redirect('dae_postes_liste')
86 validationForm
= PosteWorkflowForm(instance
=poste
, request
=request
)
88 vars = {'poste' : poste
, 'validationForm' : validationForm
, }
91 mode
= request
.GET
.get('mode', None)
93 return render_to_response('dae/poste_consulter.html', vars, RequestContext(request
))
95 return reponse_pdf('dae/poste_pdf.html', vars)
97 return render_to_response('dae/poste_pdf.html', vars, RequestContext(request
))
101 @poste_dans_ma_region_ou_service
102 @poste_est_modifiable
103 def poste(request
, key
=None):
104 """ Formulaire pour un poste.
106 Permet de créer ou modifier un poste. Si le poste n'existe que dans rh_v1
107 il est automatiquement copié dans dae.
110 poste
, data
, vars = None, dict(), dict()
115 source
, id = key
.split('-')
118 poste
= get_object_or_404(dae
.Poste
, pk
=id)
120 p
= get_object_or_404(rh
.Poste
, pk
=id)
121 # Initialisation avec les valeurs du poste de rh_v1
122 poste
= dae
.Poste(id_rh
=p
, nom
=p
.type_poste
.nom
)
123 for field
in ('implantation', 'type_poste', 'actif'):
124 setattr(poste
, field
, getattr(p
, field
))
130 data
.update(dict(request
.POST
.items()))
131 form
= PosteForm(data
, instance
=poste
, request
=request
)
132 financementForm
= FinancementForm(request
.POST
, instance
=poste
)
133 piecesForm
= PostePieceForm(request
.POST
, request
.FILES
, instance
=poste
)
134 if form
.is_valid() and piecesForm
.is_valid() and financementForm
.is_valid():
136 piecesForm
.instance
= poste
138 financementForm
.instance
= poste
139 financementForm
.save()
140 messages
.add_message(request
, messages
.SUCCESS
, "Le poste %s a été sauvegardé." % poste
)
141 if request
.POST
.has_key('save'):
142 return redirect('poste_consulter', key
='dae-%s' % poste
.id)
144 return redirect('poste', key
='dae-%s' % poste
.id)
147 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
150 # 'initial' évite la validation prémature lors d'une copie de poste de
152 form
= PosteForm(initial
=data
, instance
=poste
, request
=request
)
153 piecesForm
= PostePieceForm(instance
=poste
)
154 financementForm
= FinancementForm(instance
=poste
)
156 vars.update(dict(form
=form
, poste
=poste
, poste_key
=key
, piecesForm
=piecesForm
, financementForm
=financementForm
))
158 return render_to_response('dae/poste.html', vars, RequestContext(request
))
161 def postes_liste(request
):
162 """ Liste des postes. """
164 vars['postes_a_traiter'] = dae
.Poste
.objects
.mes_choses_a_faire(request
.user
).filter(pourvu
=False).order_by('-date_creation')
165 vars['postes_vacants'] = dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(pourvu
=False).order_by('-date_creation')
166 vars['postes_pourvus'] = dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(pourvu
=True).order_by('-date_creation')
167 return render_to_response('dae/postes_liste.html', vars, RequestContext(request
))
169 def filtered_type_remun():
170 defaut
= (2, 3, 8, 17) # salaire de base, indemnité de fonction, charges patronales
171 return rh
.TypeRemuneration
.objects
.filter(pk__in
=defaut
)
174 @dossier_dans_ma_region_ou_service
175 def embauche_consulter(request
, dossier_id
):
176 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
179 validationForm
= DossierWorkflowForm(request
.POST
, instance
=dossier
, request
=request
)
180 if validationForm
.is_valid():
181 validationForm
.save()
182 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
183 return redirect('dae_embauches_liste')
185 validationForm
= DossierWorkflowForm(instance
=dossier
, request
=request
)
189 'validationForm' : validationForm
,
192 mode
= request
.GET
.get('mode', None)
194 return render_to_response('dae/embauche_consulter.html', vars, RequestContext(request
))
196 return reponse_pdf('dae/embauche_pdf.html', vars)
198 return render_to_response('dae/embauche_pdf.html', vars, RequestContext(request
))
201 @dossier_dans_ma_region_ou_service
202 @dossier_est_modifiable
203 def embauche(request
, key
=None, dossier_id
=None):
204 """ Formulaire d'autorisation d'embauche. """
206 vars = dict(step
='poste', form
=ChoosePosteForm(request
=request
))
208 type_remun
= filtered_type_remun()
209 vars = dict(type_remun
=type_remun
)
210 source
, id = key
.split('-')
213 poste
= get_object_or_404(dae
.Poste
, pk
=id)
218 if request
.POST
['employe'] == '':
220 employe
= dae
.Employe()
222 employe_source
, id = request
.POST
['employe'].split('-')
223 if employe_source
== 'dae':
225 employe
= get_object_or_404(dae
.Employe
, pk
=id)
226 elif employe_source
== 'rh':
227 # Employé RH, on le copie dans DAE
228 e
= get_object_or_404(rh
.Employe
, pk
=id)
229 employe
= dae
.Employe(id_rh
=e
, prenom
=e
.prenom
, nom
=e
.nom
,
234 employe_form
= EmployeForm(request
.POST
, instance
=employe
, request
=request
)
237 if employe_form
.is_valid():
238 data
= dict(request
.POST
.items())
239 #with warnings.catch_warnings():
240 # warnings.simplefilter('ignore')
241 employe
= employe_form
.save()
242 data
['employe'] = 'dae-%s' % employe
.id
243 employe_form
= EmployeForm(data
, instance
=employe
, request
=request
)
246 dossier
= dae
.Dossier(poste
=poste
, employe
=employe
)
248 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
249 dossier_form
= DossierForm(request
.POST
, instance
=dossier
)
250 piecesForm
= DossierPieceForm(request
.POST
, request
.FILES
, instance
=dossier
)
251 dossiersComparaisonsForm
= DossierComparaisonForm(request
.POST
, instance
=dossier
)
252 remunForm
= RemunForm(request
.POST
, instance
=dossier
)
253 if dossier_form
.is_valid() and \
254 piecesForm
.is_valid() and \
255 dossiersComparaisonsForm
.is_valid() and \
256 remunForm
.is_valid():
258 dossier
= dossier_form
.save()
259 piecesForm
.instance
= dossier
261 dossiersComparaisonsForm
.instance
= dossier
262 dossiersComparaisonsForm
.save()
263 remunForm
.instance
= dossier
266 #if not dossier.remuneration_set.all():
267 # # Pré-peuplement des entrées de la section "coût
268 # # global", à l'exclusion de "Indemnité de fonction"
269 # for type in type_remun.all():
270 # dae.Remuneration(dossier=dossier, type=type,
271 # devise=dossier.devise).save()
274 # # Sauvegarde du coût global
275 # cg_lines = defaultdict(dict)
276 # for k, v in request.POST.items():
277 # if k.startswith('cg-'):
278 # prefix, field_name, cg_id = k.split('-')
279 # cg_lines[int(cg_id)][unicode(field_name)] = v
281 # for r in dossier.remuneration_set.all():
282 # print 'trying %r' % r
283 # if r.id in cg_lines:
284 # if cg_lines[r.id]['montant'] == '':
287 # for k, v in cg_lines[r.id].items():
291 messages
.add_message(request
, messages
.SUCCESS
, "Le dossier %s a été sauvegardé." % dossier
)
292 if request
.POST
.has_key('save'):
293 return redirect('embauche_consulter', dossier_id
=dossier
.id)
295 return redirect('embauche', key
=dossier
.poste
.key
, dossier_id
=dossier
.id)
298 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
301 dossier_form
= DossierForm(instance
=dossier
)
302 piecesForm
= DossierPieceForm(instance
=dossier
)
303 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
304 remunForm
= RemunForm(instance
=dossier
)
306 # Initialisation d'un formulaire vide
307 dossier_rh
= rh
.Dossier()
308 poste_rh
= poste
.id_rh
310 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
311 employe
= dossier
.employe
312 data
= dict(employe
='dae-%s' % employe
.id)
313 employe_form
= EmployeForm(initial
=data
, instance
=employe
, request
=request
)
315 dossier
= pre_filled_dossier(dossier_rh
, 'new', poste_rh
)
316 employe_form
= EmployeForm(request
=request
)
318 dossier_form
= DossierForm(instance
=dossier
)
319 piecesForm
= DossierPieceForm(instance
=dossier
)
320 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
321 remunForm
= RemunForm(instance
=dossier
)
323 vars = dict(step
='employe',
324 type_remun
=type_remun
,
328 piecesForm
=piecesForm
,
330 dossiersComparaisonsForm
=dossiersComparaisonsForm
,
331 forms
=dict(employe
=employe_form
, dossier
=dossier_form
, )
335 return render_to_response('dae/embauche.html', vars,
336 RequestContext(request
))
338 @dossier_dans_ma_region_ou_service
339 def embauches_liste(request
):
340 """ Liste des embauches. """
342 vars['embauches_a_traiter'] = dae
.Dossier
.objects
.mes_choses_a_faire(request
.user
).filter(poste__pourvu
=False).order_by('-date_creation')
343 vars['embauches_en_cours'] = dae
.Dossier
.objects
.ma_region_ou_service(request
.user
).filter(poste__pourvu
=False).order_by('-date_creation')
344 vars['embauches_non_retenues'] = dae
.Dossier
.objects
.ma_region_ou_service(request
.user
).filter(poste__pourvu
=True).order_by('-date_creation')
345 return render_to_response('dae/embauches_liste.html', vars, RequestContext(request
))
347 def employe(request
, key
):
348 """ Récupération AJAX de l'employé pour la page d'embauche. """
349 data
= dict(employe
=key
)
353 employe
= dae
.Employe()
356 source
, id = key
.split('-')
359 employe
= get_object_or_404(dae
.Employe
, pk
=id)
361 e
= get_object_or_404(rh
.Employe
, id=id)
362 # Initialisation avec les valeurs de l'employé de rh_v1
363 employe
= dae
.Employe(id_rh
=e
)
364 for field
in ('prenom', 'nom', 'genre'):
365 setattr(employe
, field
, getattr(e
, field
))
367 return HttpResponse(EmployeForm(initial
=data
, instance
=employe
, request
=request
).as_table())
369 ################################################################################
371 ################################################################################
373 @employe_dans_ma_region_ou_service
374 def dossier(request
, poste_key
, employe_key
):
375 """ Récupération AJAX du dossier pour la page d'embauche. """
378 poste_source
, poste_id
= poste_key
.split('-')
379 poste
= get_object_or_404(dae
.Poste
, pk
=poste_id
)
381 # Récupérer la devise de l'implantation lié au poste
382 implantation_devise
= poste
.get_default_devise()
383 data
.update({'devise' : implantation_devise
.id})
385 if poste
.id_rh_id
is not None:
386 poste_rh
= get_object_or_404(rh
.Poste
, pk
=poste
.id_rh_id
)
390 ##########################################################################################
392 ##########################################################################################
393 if employe_key
== '':
394 employe_source
= 'new'
396 dossier_rh
= rh
.Dossier()
397 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
399 ##########################################################################################
401 ##########################################################################################
402 if employe_key
.startswith('dae'):
403 employe_source
, employe_id
= employe_key
.split('-')
404 employe_dae
= get_object_or_404(dae
.Employe
, pk
=employe_id
)
406 # récupération de l'ancien dossier rh v1 pour l'employe DAE
408 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_dae
.id_rh_id
, mandat_date_fin
=None)
409 except (rh
.Dossier
.DoesNotExist
):
410 dossier_rh
= rh
.Dossier()
412 # on tente de récupérer le dossier DAE, au pire on le contruit en le
413 # prépoluant avec son dossier rh v1.
415 dossier
= dae
.Dossier
.objects
.get(employe
=employe_dae
, poste
=poste
)
416 except (dae
.Dossier
.DoesNotExist
):
417 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
418 employe
= employe_dae
.id_rh
419 ##########################################################################################
421 ##########################################################################################
422 if employe_key
.startswith('rh'):
423 employe_source
, employe_id
= employe_key
.split('-')
424 employe_rh
= get_object_or_404(rh
.Employe
, pk
=employe_id
)
426 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il n'en a pas,
427 # on en fournit un nouveau qui servira uniquement un créer un nouveau dossier DAE.
429 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_rh
, mandat_date_fin
=None)
430 except (rh
.Dossier
.DoesNotExist
):
431 dossier_rh
= rh
.Dossier()
432 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
435 dossier_form
= DossierForm(initial
=data
, instance
=dossier
)
436 vars = dict(form
=dossier_form
, poste
=poste
, employe
=employe
)
437 return render_to_response('dae/embauche-dossier.html', vars,
438 RequestContext(request
))
440 # @Cette fonction est appelée à partir de fonctions déjà sécurisée
441 def pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
):
442 dossier
= dae
.Dossier()
444 if employe_source
!= 'new' and dossier_rh
.id:
445 dossier
.statut_anterieur
= dossier_rh
.statut
447 # Certains dossiers ont un classement à zéro
448 if dossier_rh
.classement_id
> 0:
449 dossier
.classement_anterieur
= dossier_rh
.classement
451 # Récupération du salaire de base
452 remun
= dossier_rh
.remuneration_set
.filter(type=1).order_by('-date_effective')
454 dossier
.salaire_anterieur
= remun
[0].montant
456 # Récupération du titulaire précédent
458 dossiers
= rh
.Dossier
.objects
.order_by('-mandat_date_fin')
459 dossiers
= dossiers
.filter(poste1
=poste_rh
) | dossiers
.filter(poste2
=poste_rh
)
461 # Ce bloc ignore toutes les erreurs, car les données de rh
462 # manquantes peuvent en générer
465 titulaire
= d
.employe
466 dossier
.employe_anterieur
= titulaire
467 dossier
.classement_titulaire_anterieur
= d
.classement
468 dossier
.statut_titulaire_anterieur
= d
.statut
469 dossier
.salaire_titulaire_anterieur
= d
.remuneration_set
.filter(type=1).order_by('-date_effective')[0].montant
472 # TODO: afficher l'info, les champs ne sont pas dans le
473 # modèle dae.Dossier: nom, prenom, classement, salaire
476 except (rh
.Dossier
.DoesNotExist
):
477 dossier_rh
= rh
.Dossier()
482 @vieux_dossier_dans_ma_region_ou_service
483 def dossier_resume(request
, dossier_id
=None):
486 output : devise, devise_code, taux_euro
489 dossier
= rh
.Dossier
.objects
.get(id=dossier_id
)
491 return HttpResponseGone("Ce dossier n'est pas accessible")
494 data
['personne'] = unicode(dossier
.employe
)
495 data
['implantation'] = dossier
.implantation1
.id
496 data
['poste'] = u
"%s %s" % (dossier
.poste1
.type_poste
.nom
, dossier
.complement1
)
497 data
['montant'] = dossier
.get_salaire()
498 salaire
= dossier
.get_dernier_salaire_remun()
499 if salaire
is not None:
500 data
['devise'] = salaire
.devise
.id
501 data
['montant_euros'] = salaire
.en_euros()
503 data
['devise'] = None
504 data
['montant_euros'] = 0
505 return HttpResponse(dumps(data
))
507 def liste_postes(request
):
509 input : implantation_id
510 output : JSON liste de valeur point
512 method
= request
.method
513 params
= getattr(request
, method
, [])
516 # Voir le code de _poste_choices dans forms.py
517 dae_
= dae
.Poste
.objects
.filter(actif
=True, id_rh__isnull
=True)
518 copies
= dae
.Poste
.objects
.exclude(id_rh__isnull
=True)
519 rh_postes_actifs
= rh
.Poste
.objects
.filter(actif
=True)
521 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
522 implantation_id
= params
.get('implantation_id')
523 dae_
= dae_
.filter(implantation__id
=implantation_id
)
524 copies
= copies
.filter(implantation__id
=implantation_id
)
525 rh_postes_actifs
= rh_postes_actifs
.filter(implantation__id
=implantation_id
)
527 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
528 rhv1
= rh_postes_actifs
.exclude(id__in
=id_copies
)
529 rhv1
= rhv1
.select_related(depth
=1)
531 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])
532 return HttpResponse(dumps(data
))
535 ################################################################################
536 # AJAX SECURITE non nécessaire
537 ################################################################################
538 def coefficient(request
):
543 method
= request
.method
544 params
= getattr(request
, method
, [])
546 if 'classement' in params
and params
.get('classement') is not u
"":
547 classement
= params
.get('classement')
548 classement
= rh
.Classement
.objects
.get(pk
=classement
)
549 data
['coefficient'] = classement
.coefficient
551 data
['coefficient'] = 0
552 return HttpResponse(dumps(data
))
558 output : devise, devise_code, taux_euro
560 method
= request
.method
561 params
= getattr(request
, method
, [])
563 if 'valeur_point' in params
and params
.get('valeur_point') is not u
"":
564 valeur_point
= params
.get('valeur_point')
565 valeur_point
= rh
.ValeurPoint
.objects
.get(pk
=valeur_point
)
566 annee
= valeur_point
.annee
567 implantation
= valeur_point
.implantation
568 taux
= rh
.TauxChange
.objects
.get(annee
=annee
,
569 implantation
=implantation
)
570 data
['devise'] = taux
.devise
.id
571 data
['valeur'] = valeur_point
.valeur
572 data
['devise_code'] = taux
.devise
.code
573 data
['taux_euro'] = taux
.taux
575 return HttpResponseGone("Vous devez choisir une valeur de point")
576 return HttpResponse(dumps(data
))
578 def devise_code(request
):
581 output : devise_code, taux_euro
583 method
= request
.method
584 params
= getattr(request
, method
, [])
586 if 'devise' in params
:
587 devise
= params
.get('devise')
588 devise
= rh
.Devise
.objects
.get(pk
=devise
)
589 annee
= date
.today().year
590 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=devise
)
592 return HttpResponseGone("Le taux n'est pas disponible")
593 data
['devise_code'] = devise
.code
594 data
['taux_euro'] = taux
[0].taux
595 return HttpResponse(dumps(data
))
597 def add_remun(request
, dossier
, type_remun
):
598 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier
)
599 type_remun
= get_object_or_404(rh
.TypeRemuneration
, pk
=type_remun
)
600 dae
.Remuneration(dossier
=dossier
, devise
=dossier
.devise
,
601 type=type_remun
).save()
603 return render_to_response('dae/embauche-remun.html', dict(dossier
=dossier
),
604 RequestContext(request
))
606 def salaire(request
, implantation
, devise
, classement
):
607 if not devise
or not classement
:
610 taux_impl
= rh
.TauxChange
.objects
.filter(implantation
=implantation
) \
612 taux
= rh
.TauxChange
.objects
.filter(devise
=devise
).order_by('-annee')
613 vp
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation
) \
615 if vp
.count() * taux
.count() * taux_impl
.count() == 0:
618 classement
= get_object_or_404(rh
.Classement
, pk
=classement
)
619 taux
, taux_impl
, vp
= taux
[0].taux
, taux_impl
[0].taux
, vp
[0].valeur
621 salaire_euro
= round(vp
* classement
.coefficient
* taux_impl
, 2)
622 data
= dict(salaire_euro
=salaire_euro
, taux
=taux
,
623 salaire_devise
=round(salaire_euro
/ taux
, 2))
625 return HttpResponse(dumps(data
))
627 def liste_valeurs_point(request
):
629 input : implantation_id
630 output : JSON liste de valeur point
632 method
= request
.method
633 params
= getattr(request
, method
, [])
635 annee_courante
= datetime
.datetime
.now().year
636 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
637 implantation_id
= params
.get('implantation_id')
638 objects
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation_id
, annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
640 objects
= rh
.ValeurPoint
.objects
.filter(annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
642 data
.append({'id' : o
.id, 'label' : o
.__unicode__(), })
643 return HttpResponse(dumps(data
))