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
20 import ho
.pisa
as pisa
22 from project
.dae
import models
as dae
23 from project
.rh_v1
import models
as rh
25 from decorators
import dae_groupe_requis
, \
26 poste_dans_ma_region_ou_service
, \
27 dossier_dans_ma_region_ou_service
, \
28 employe_dans_ma_region_ou_service
33 def reponse_pdf(template_src
, context_dict
):
35 Générer une réponse HTTP avec un PDF
38 for f
in ('css/pdf.css', 'css/dae.css'):
39 css_file
= os
.path
.join(settings
.MEDIA_ROOT
, f
)
40 css
+= open(css_file
, 'r').read()
41 context_dict
['css'] = css
43 template
= get_template(template_src
)
44 context
= Context(context_dict
)
45 html
= template
.render(context
)
46 result
= StringIO
.StringIO()
47 pdf
= pisa
.pisaDocument(html
, result
, encoding
='UTF-8')
49 return HttpResponse(result
.getvalue(), mimetype
='application/pdf')
50 return HttpResponse("impossible de générer le pdf! %s" % html
)
55 return render_to_response('dae/index.html', {}, RequestContext(request
))
58 @poste_dans_ma_region_ou_service
59 def poste_consulter(request
, key
):
60 source
, id = key
.split('-')
61 poste
= get_object_or_404(dae
.Poste
, pk
=id)
64 validationForm
= PosteWorkflowForm(request
.POST
, instance
=poste
, request
=request
)
65 if validationForm
.is_valid():
67 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
68 return redirect('poste_consulter', key
=key
)
70 validationForm
= PosteWorkflowForm(instance
=poste
, request
=request
)
72 vars = {'poste' : poste
, 'validationForm' : validationForm
, }
75 mode
= request
.GET
.get('mode', None)
77 return render_to_response('dae/poste_consulter.html', vars, RequestContext(request
))
79 return reponse_pdf('dae/poste_pdf.html', vars)
81 return render_to_response('dae/poste_pdf.html', vars, RequestContext(request
))
85 @poste_dans_ma_region_ou_service
86 def poste(request
, key
=None):
87 """ Formulaire pour un poste.
89 Permet de créer ou modifier un poste. Si le poste n'existe que dans rh_v1
90 il est automatiquement copié dans dae.
93 poste
, data
, vars = None, dict(), dict()
98 source
, id = key
.split('-')
101 poste
= get_object_or_404(dae
.Poste
, pk
=id)
103 p
= get_object_or_404(rh
.Poste
, pk
=id)
104 # Initialisation avec les valeurs du poste de rh_v1
105 poste
= dae
.Poste(id_rh
=p
, nom
=p
.type_poste
.nom
)
106 for field
in ('implantation', 'type_poste', 'actif'):
107 setattr(poste
, field
, getattr(p
, field
))
113 data
.update(dict(request
.POST
.items()))
114 form
= PosteForm(data
, instance
=poste
, request
=request
)
115 financementForm
= FinancementForm(request
.POST
, instance
=poste
)
116 piecesForm
= PostePieceForm(request
.POST
, request
.FILES
, instance
=poste
)
117 if form
.is_valid() and piecesForm
.is_valid() and financementForm
.is_valid():
119 piecesForm
.instance
= poste
121 financementForm
.instance
= poste
122 financementForm
.save()
123 messages
.add_message(request
, messages
.SUCCESS
, "Le poste %s a été sauvegardé." % poste
)
124 if request
.POST
.has_key('save'):
125 return redirect('poste_consulter', key
='dae-%s' % poste
.id)
127 return redirect('poste', key
='dae-%s' % poste
.id)
130 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
133 # 'initial' évite la validation prémature lors d'une copie de poste de
135 form
= PosteForm(initial
=data
, instance
=poste
, request
=request
)
136 piecesForm
= PostePieceForm(instance
=poste
)
137 financementForm
= FinancementForm(instance
=poste
)
139 vars.update(dict(form
=form
, poste
=poste
, poste_key
=key
, piecesForm
=piecesForm
, financementForm
=financementForm
))
141 return render_to_response('dae/poste.html', vars, RequestContext(request
))
144 def postes_liste(request
):
145 """ Liste des postes. """
147 vars['postes'] = dae
.Poste
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
148 return render_to_response('dae/postes_liste.html', vars, RequestContext(request
))
150 def filtered_type_remun():
151 # Exclusion de "Indemnité de fonction" des types de rémun utilisés
152 return rh
.TypeRemuneration
.objects
.exclude(pk
=7)
155 @dossier_dans_ma_region_ou_service
156 def embauche_consulter(request
, dossier_id
):
157 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
160 validationForm
= DossierWorkflowForm(request
.POST
, instance
=dossier
, request
=request
)
161 if validationForm
.is_valid():
162 validationForm
.save()
163 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
164 return redirect('embauche_consulter', dossier_id
=dossier
.id)
166 validationForm
= DossierWorkflowForm(instance
=dossier
, request
=request
)
170 'validationForm' : validationForm
,
172 return render_to_response('dae/embauche_consulter.html', vars, RequestContext(request
))
175 @dossier_dans_ma_region_ou_service
176 def embauche(request
, key
=None, dossier_id
=None):
177 """ Formulaire d'autorisation d'embauche. """
179 vars = dict(step
='poste', form
=ChoosePosteForm(request
=request
))
181 type_remun
= filtered_type_remun()
182 vars = dict(type_remun
=type_remun
)
183 source
, id = key
.split('-')
186 poste
= get_object_or_404(dae
.Poste
, pk
=id)
191 if request
.POST
['employe'] == '':
193 employe
= dae
.Employe()
195 employe_source
, id = request
.POST
['employe'].split('-')
196 if employe_source
== 'dae':
198 employe
= get_object_or_404(dae
.Employe
, pk
=id)
199 elif employe_source
== 'rh':
200 # Employé RH, on le copie dans DAE
201 e
= get_object_or_404(rh
.Employe
, pk
=id)
202 employe
= dae
.Employe(id_rh
=e
, prenom
=e
.prenom
, nom
=e
.nom
,
207 employe_form
= EmployeForm(request
.POST
, instance
=employe
, request
=request
)
210 if employe_form
.is_valid():
211 data
= dict(request
.POST
.items())
212 #with warnings.catch_warnings():
213 # warnings.simplefilter('ignore')
214 employe
= employe_form
.save()
215 data
['employe'] = 'dae-%s' % employe
.id
216 employe_form
= EmployeForm(data
, instance
=employe
, request
=request
)
219 dossier
= dae
.Dossier(poste
=poste
, employe
=employe
)
221 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
222 dossier_form
= DossierForm(request
.POST
, instance
=dossier
)
223 piecesForm
= DossierPieceForm(request
.POST
, request
.FILES
, instance
=dossier
)
224 justificationsNouveauForm
= JustificationNouvelEmployeForm(request
.POST
, instance
=dossier
)
225 justificationsAutreForm
= JustificationAutreEmployeForm(request
.POST
, instance
=dossier
)
226 dossiersComparaisonsForm
= DossierComparaisonForm(request
.POST
, instance
=dossier
)
228 if dossier_form
.is_valid() and piecesForm
.is_valid() and justificationsNouveauForm
.is_valid() and justificationsAutreForm
.is_valid() and dossiersComparaisonsForm
.is_valid():
229 dossier
= dossier_form
.save()
230 piecesForm
.instance
= dossier
232 justificationsNouveauForm
.instance
= dossier
233 justificationsNouveauForm
.save()
234 justificationsAutreForm
.instance
= dossier
235 justificationsAutreForm
.save()
236 dossiersComparaisonsForm
.instance
= dossier
237 dossiersComparaisonsForm
.save()
239 if not dossier
.remuneration_set
.all():
240 # Pré-peuplement des entrées de la section "coût
241 # global", à l'exclusion de "Indemnité de fonction"
242 for type in type_remun
.all():
243 dae
.Remuneration(dossier
=dossier
, type=type,
244 devise
=dossier
.devise
).save()
247 # Sauvegarde du coût global
248 cg_lines
= defaultdict(dict)
249 for k
, v
in request
.POST
.items():
250 if k
.startswith('cg-'):
251 prefix
, field_name
, cg_id
= k
.split('-')
252 cg_lines
[int(cg_id
)][unicode(field_name
)] = v
254 for r
in dossier
.remuneration_set
.all():
255 print 'trying %r' % r
257 if cg_lines
[r
.id]['montant'] == '':
260 for k
, v
in cg_lines
[r
.id].items():
264 messages
.add_message(request
, messages
.SUCCESS
, "Le dossier %s a été sauvegardé." % dossier
)
265 if request
.POST
.has_key('save'):
266 return redirect('embauche_consulter', dossier_id
=dossier
.id)
268 return redirect('embauche', key
=dossier
.poste
.key
, dossier_id
=dossier
.id)
271 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
274 dossier_form
= DossierForm(instance
=dossier
)
275 piecesForm
= DossierPieceForm(instance
=dossier
)
276 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
277 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
278 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
280 # Initialisation d'un formulaire vide
281 dossier_rh
= rh
.Dossier()
282 poste_rh
= poste
.id_rh
284 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
285 employe
= dossier
.employe
286 data
= dict(employe
='dae-%s' % employe
.id)
287 employe_form
= EmployeForm(initial
=data
, instance
=employe
, request
=request
)
289 dossier
= pre_filled_dossier(dossier_rh
, 'new', poste_rh
)
290 employe_form
= EmployeForm(request
=request
)
292 dossier_form
= DossierForm(instance
=dossier
)
293 piecesForm
= DossierPieceForm(instance
=dossier
)
294 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
295 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
296 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
298 vars = dict(step
='employe',
299 type_remun
=type_remun
,
302 piecesForm
=piecesForm
,
303 justificationsNouveauForm
=justificationsNouveauForm
,
304 justificationsAutreForm
=justificationsAutreForm
,
305 dossiersComparaisonsForm
=dossiersComparaisonsForm
,
306 forms
=dict(employe
=employe_form
, dossier
=dossier_form
, )
310 return render_to_response('dae/embauche.html', vars,
311 RequestContext(request
))
313 @dossier_dans_ma_region_ou_service
314 def embauches_liste(request
):
315 """ Liste des embauches. """
317 vars['embauches'] = dae
.Dossier
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
318 return render_to_response('dae/embauches_liste.html', vars, RequestContext(request
))
320 def employe(request
, key
):
321 """ Récupération AJAX de l'employé pour la page d'embauche. """
322 data
= dict(employe
=key
)
326 employe
= dae
.Employe()
329 source
, id = key
.split('-')
332 employe
= get_object_or_404(dae
.Employe
, pk
=id)
334 e
= get_object_or_404(rh
.Employe
, id=id)
335 # Initialisation avec les valeurs de l'employé de rh_v1
336 employe
= dae
.Employe(id_rh
=e
)
337 for field
in ('prenom', 'nom', 'genre'):
338 setattr(employe
, field
, getattr(e
, field
))
340 return HttpResponse(EmployeForm(initial
=data
, instance
=employe
, request
=request
).as_table())
342 ################################################################################
344 ################################################################################
346 @employe_dans_ma_region_ou_service
347 def dossier(request
, poste_key
, employe_key
):
348 """ Récupération AJAX du dossier pour la page d'embauche. """
351 poste_source
, poste_id
= poste_key
.split('-')
352 poste
= get_object_or_404(dae
.Poste
, pk
=poste_id
)
354 # Récupérer la devise de l'implantation lié au poste
355 implantation_devise
= poste
.get_default_devise()
356 data
.update({'devise' : implantation_devise
.id})
358 if poste
.id_rh_id
is not None:
359 poste_rh
= get_object_or_404(rh
.Poste
, pk
=poste
.id_rh_id
)
363 ##########################################################################################
365 ##########################################################################################
366 if employe_key
== '':
367 employe_source
= 'new'
369 dossier_rh
= rh
.Dossier()
370 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
372 ##########################################################################################
374 ##########################################################################################
375 if employe_key
.startswith('dae'):
376 employe_source
, employe_id
= employe_key
.split('-')
377 employe_dae
= get_object_or_404(dae
.Employe
, pk
=employe_id
)
379 # récupération de l'ancien dossier rh v1 pour l'employe DAE
381 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_dae
.id_rh_id
, mandat_date_fin
=None)
382 except (rh
.Dossier
.DoesNotExist
):
383 dossier_rh
= rh
.Dossier()
385 # on tente de récupérer le dossier DAE, au pire on le contruit en le
386 # prépoluant avec son dossier rh v1.
388 dossier
= dae
.Dossier
.objects
.get(employe
=employe_dae
, poste
=poste
)
389 except (dae
.Dossier
.DoesNotExist
):
390 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
391 employe
= employe_dae
.id_rh
392 ##########################################################################################
394 ##########################################################################################
395 if employe_key
.startswith('rh'):
396 employe_source
, employe_id
= employe_key
.split('-')
397 employe_rh
= get_object_or_404(rh
.Employe
, pk
=employe_id
)
399 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il n'en a pas,
400 # on en fournit un nouveau qui servira uniquement un créer un nouveau dossier DAE.
402 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_rh
, mandat_date_fin
=None)
403 except (rh
.Dossier
.DoesNotExist
):
404 dossier_rh
= rh
.Dossier()
405 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
408 dossier_form
= DossierForm(initial
=data
, instance
=dossier
)
409 vars = dict(form
=dossier_form
, poste
=poste
, employe
=employe
)
410 return render_to_response('dae/embauche-dossier.html', vars,
411 RequestContext(request
))
413 # @Cette fonction est appelée à partir de fonctions déjà sécurisée
414 def pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
):
415 dossier
= dae
.Dossier()
417 if employe_source
!= 'new' and dossier_rh
.id:
418 dossier
.statut_anterieur
= dossier_rh
.statut
420 # Certains dossiers ont un classement à zéro
421 if dossier_rh
.classement_id
> 0:
422 dossier
.classement_anterieur
= dossier_rh
.classement
424 # Récupération du salaire de base
425 remun
= dossier_rh
.remuneration_set
.filter(type=1)
427 dossier
.salaire_anterieur
= remun
[0].montant
429 # Récupération du titulaire précédent
431 dossiers
= rh
.Dossier
.objects
.order_by('-mandat_date_fin')
432 dossiers
= dossiers
.filter(poste1
=poste_rh
) | dossiers
.filter(poste2
=poste_rh
)
434 # Ce bloc ignore toutes les erreurs, car les données de rh
435 # manquantes peuvent en générer
438 titulaire
= d
.employe
439 dossier
.employe_anterieur
= titulaire
440 dossier
.classement_titulaire_anterieur
= d
.classement
441 dossier
.statut_titulaire_anterieur
= d
.statut
442 dossier
.salaire_titulaire_anterieur
= \
443 d
.remuneration_set
.all()[0].montant
446 # TODO: afficher l'info, les champs ne sont pas dans le
447 # modèle dae.Dossier: nom, prenom, classement, salaire
450 except (rh
.Dossier
.DoesNotExist
):
451 dossier_rh
= rh
.Dossier()
456 @dossier_dans_ma_region_ou_service
457 def dossier_resume(request
, dossier_id
=None):
460 output : devise, devise_code, taux_euro
463 dossier
= rh
.Dossier
.objects
.get(id=dossier_id
)
465 return HttpResponseGone("Ce dossier n'est pas accessible")
468 data
['personne'] = unicode(dossier
.employe
)
469 data
['implantation'] = dossier
.implantation1
.id
470 data
['poste'] = u
"%s %s" % (dossier
.poste1
.type_poste
.nom
, dossier
.complement1
)
471 data
['montant'] = dossier
.get_salaire()
472 salaire
= dossier
.get_dernier_salaire_remun()
473 if salaire
is not None:
474 data
['devise'] = dossier
.get_dernier_salaire_remun().devise
.id
475 data
['montant_euros'] = dossier
.get_dernier_salaire_remun().en_euros()
477 data
['devise'] = None
478 data
['montant_euros'] = 0
479 return HttpResponse(dumps(data
))
481 def liste_postes(request
):
483 input : implantation_id
484 output : JSON liste de valeur point
486 method
= request
.method
487 params
= getattr(request
, method
, [])
490 # Voir le code de _poste_choices dans forms.py
491 dae_
= dae
.Poste
.objects
.filter(actif
=True, id_rh__isnull
=True)
492 copies
= dae
.Poste
.objects
.exclude(id_rh__isnull
=True)
493 rh_postes_actifs
= rh
.Poste
.objects
.filter(actif
=True)
495 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
496 implantation_id
= params
.get('implantation_id')
497 dae_
= dae_
.filter(implantation__id
=implantation_id
)
498 copies
= copies
.filter(implantation__id
=implantation_id
)
499 rh_postes_actifs
= rh_postes_actifs
.filter(implantation__id
=implantation_id
)
501 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
502 rhv1
= rh_postes_actifs
.exclude(id__in
=id_copies
)
503 rhv1
= rhv1
.select_related(depth
=1)
505 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])
506 return HttpResponse(dumps(data
))
509 ################################################################################
510 # AJAX SECURITE non nécessaire
511 ################################################################################
512 def coefficient(request
):
517 method
= request
.method
518 params
= getattr(request
, method
, [])
520 if 'classement' in params
and params
.get('classement') is not u
"":
521 classement
= params
.get('classement')
522 classement
= rh
.Classement
.objects
.get(pk
=classement
)
523 data
['coefficient'] = classement
.coefficient
525 data
['coefficient'] = 0
526 return HttpResponse(dumps(data
))
532 output : devise, devise_code, taux_euro
534 method
= request
.method
535 params
= getattr(request
, method
, [])
537 if 'valeur_point' in params
and params
.get('valeur_point') is not u
"":
538 valeur_point
= params
.get('valeur_point')
539 valeur_point
= rh
.ValeurPoint
.objects
.get(pk
=valeur_point
)
540 annee
= valeur_point
.annee
541 implantation
= valeur_point
.implantation
542 taux
= rh
.TauxChange
.objects
.get(annee
=annee
,
543 implantation
=implantation
)
544 data
['devise'] = taux
.devise
.id
545 data
['valeur'] = valeur_point
.valeur
546 data
['devise_code'] = taux
.devise
.code
547 data
['taux_euro'] = taux
.taux
549 return HttpResponseGone("Vous devez choisir une valeur de point")
550 return HttpResponse(dumps(data
))
552 def devise_code(request
):
555 output : devise_code, taux_euro
557 method
= request
.method
558 params
= getattr(request
, method
, [])
560 if 'devise' in params
:
561 devise
= params
.get('devise')
562 devise
= rh
.Devise
.objects
.get(pk
=devise
)
563 annee
= date
.today().year
564 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=devise
)
566 return HttpResponseGone("Le taux n'est pas disponible")
567 data
['devise_code'] = devise
.code
568 data
['taux_euro'] = taux
[0].taux
569 return HttpResponse(dumps(data
))
571 def add_remun(request
, dossier
, type_remun
):
572 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier
)
573 type_remun
= get_object_or_404(rh
.TypeRemuneration
, pk
=type_remun
)
574 dae
.Remuneration(dossier
=dossier
, devise
=dossier
.devise
,
575 type=type_remun
).save()
577 return render_to_response('dae/embauche-remun.html', dict(dossier
=dossier
),
578 RequestContext(request
))
580 def salaire(request
, implantation
, devise
, classement
):
581 if not devise
or not classement
:
584 taux_impl
= rh
.TauxChange
.objects
.filter(implantation
=implantation
) \
586 taux
= rh
.TauxChange
.objects
.filter(devise
=devise
).order_by('-annee')
587 vp
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation
) \
589 if vp
.count() * taux
.count() * taux_impl
.count() == 0:
592 classement
= get_object_or_404(rh
.Classement
, pk
=classement
)
593 taux
, taux_impl
, vp
= taux
[0].taux
, taux_impl
[0].taux
, vp
[0].valeur
595 salaire_euro
= round(vp
* classement
.coefficient
* taux_impl
, 2)
596 data
= dict(salaire_euro
=salaire_euro
, taux
=taux
,
597 salaire_devise
=round(salaire_euro
/ taux
, 2))
599 return HttpResponse(dumps(data
))
601 def liste_valeurs_point(request
):
603 input : implantation_id
604 output : JSON liste de valeur point
606 method
= request
.method
607 params
= getattr(request
, method
, [])
609 annee_courante
= datetime
.datetime
.now().year
610 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
611 implantation_id
= params
.get('implantation_id')
612 objects
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation_id
, annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
614 objects
= rh
.ValeurPoint
.objects
.filter(annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
616 data
.append({'id' : o
.id, 'label' : o
.__unicode__(), })
617 return HttpResponse(dumps(data
))