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
,
173 mode
= request
.GET
.get('mode', None)
175 return render_to_response('dae/embauche_consulter.html', vars, RequestContext(request
))
177 return reponse_pdf('dae/embauche_pdf.html', vars)
179 return render_to_response('dae/embauche_pdf.html', vars, RequestContext(request
))
182 @dossier_dans_ma_region_ou_service
183 def embauche(request
, key
=None, dossier_id
=None):
184 """ Formulaire d'autorisation d'embauche. """
186 vars = dict(step
='poste', form
=ChoosePosteForm(request
=request
))
188 type_remun
= filtered_type_remun()
189 vars = dict(type_remun
=type_remun
)
190 source
, id = key
.split('-')
193 poste
= get_object_or_404(dae
.Poste
, pk
=id)
198 if request
.POST
['employe'] == '':
200 employe
= dae
.Employe()
202 employe_source
, id = request
.POST
['employe'].split('-')
203 if employe_source
== 'dae':
205 employe
= get_object_or_404(dae
.Employe
, pk
=id)
206 elif employe_source
== 'rh':
207 # Employé RH, on le copie dans DAE
208 e
= get_object_or_404(rh
.Employe
, pk
=id)
209 employe
= dae
.Employe(id_rh
=e
, prenom
=e
.prenom
, nom
=e
.nom
,
214 employe_form
= EmployeForm(request
.POST
, instance
=employe
, request
=request
)
217 if employe_form
.is_valid():
218 data
= dict(request
.POST
.items())
219 #with warnings.catch_warnings():
220 # warnings.simplefilter('ignore')
221 employe
= employe_form
.save()
222 data
['employe'] = 'dae-%s' % employe
.id
223 employe_form
= EmployeForm(data
, instance
=employe
, request
=request
)
226 dossier
= dae
.Dossier(poste
=poste
, employe
=employe
)
228 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
229 dossier_form
= DossierForm(request
.POST
, instance
=dossier
)
230 piecesForm
= DossierPieceForm(request
.POST
, request
.FILES
, instance
=dossier
)
231 justificationsNouveauForm
= JustificationNouvelEmployeForm(request
.POST
, instance
=dossier
)
232 justificationsAutreForm
= JustificationAutreEmployeForm(request
.POST
, instance
=dossier
)
233 dossiersComparaisonsForm
= DossierComparaisonForm(request
.POST
, instance
=dossier
)
235 if dossier_form
.is_valid() and piecesForm
.is_valid() and justificationsNouveauForm
.is_valid() and justificationsAutreForm
.is_valid() and dossiersComparaisonsForm
.is_valid():
236 dossier
= dossier_form
.save()
237 piecesForm
.instance
= dossier
239 justificationsNouveauForm
.instance
= dossier
240 justificationsNouveauForm
.save()
241 justificationsAutreForm
.instance
= dossier
242 justificationsAutreForm
.save()
243 dossiersComparaisonsForm
.instance
= dossier
244 dossiersComparaisonsForm
.save()
246 if not dossier
.remuneration_set
.all():
247 # Pré-peuplement des entrées de la section "coût
248 # global", à l'exclusion de "Indemnité de fonction"
249 for type in type_remun
.all():
250 dae
.Remuneration(dossier
=dossier
, type=type,
251 devise
=dossier
.devise
).save()
254 # Sauvegarde du coût global
255 cg_lines
= defaultdict(dict)
256 for k
, v
in request
.POST
.items():
257 if k
.startswith('cg-'):
258 prefix
, field_name
, cg_id
= k
.split('-')
259 cg_lines
[int(cg_id
)][unicode(field_name
)] = v
261 for r
in dossier
.remuneration_set
.all():
262 print 'trying %r' % r
264 if cg_lines
[r
.id]['montant'] == '':
267 for k
, v
in cg_lines
[r
.id].items():
271 messages
.add_message(request
, messages
.SUCCESS
, "Le dossier %s a été sauvegardé." % dossier
)
272 if request
.POST
.has_key('save'):
273 return redirect('embauche_consulter', dossier_id
=dossier
.id)
275 return redirect('embauche', key
=dossier
.poste
.key
, dossier_id
=dossier
.id)
278 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
281 dossier_form
= DossierForm(instance
=dossier
)
282 piecesForm
= DossierPieceForm(instance
=dossier
)
283 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
284 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
285 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
287 # Initialisation d'un formulaire vide
288 dossier_rh
= rh
.Dossier()
289 poste_rh
= poste
.id_rh
291 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
292 employe
= dossier
.employe
293 data
= dict(employe
='dae-%s' % employe
.id)
294 employe_form
= EmployeForm(initial
=data
, instance
=employe
, request
=request
)
296 dossier
= pre_filled_dossier(dossier_rh
, 'new', poste_rh
)
297 employe_form
= EmployeForm(request
=request
)
299 dossier_form
= DossierForm(instance
=dossier
)
300 piecesForm
= DossierPieceForm(instance
=dossier
)
301 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
302 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
303 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
305 vars = dict(step
='employe',
306 type_remun
=type_remun
,
309 piecesForm
=piecesForm
,
310 justificationsNouveauForm
=justificationsNouveauForm
,
311 justificationsAutreForm
=justificationsAutreForm
,
312 dossiersComparaisonsForm
=dossiersComparaisonsForm
,
313 forms
=dict(employe
=employe_form
, dossier
=dossier_form
, )
317 return render_to_response('dae/embauche.html', vars,
318 RequestContext(request
))
320 @dossier_dans_ma_region_ou_service
321 def embauches_liste(request
):
322 """ Liste des embauches. """
324 vars['embauches'] = dae
.Dossier
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
325 return render_to_response('dae/embauches_liste.html', vars, RequestContext(request
))
327 def employe(request
, key
):
328 """ Récupération AJAX de l'employé pour la page d'embauche. """
329 data
= dict(employe
=key
)
333 employe
= dae
.Employe()
336 source
, id = key
.split('-')
339 employe
= get_object_or_404(dae
.Employe
, pk
=id)
341 e
= get_object_or_404(rh
.Employe
, id=id)
342 # Initialisation avec les valeurs de l'employé de rh_v1
343 employe
= dae
.Employe(id_rh
=e
)
344 for field
in ('prenom', 'nom', 'genre'):
345 setattr(employe
, field
, getattr(e
, field
))
347 return HttpResponse(EmployeForm(initial
=data
, instance
=employe
, request
=request
).as_table())
349 ################################################################################
351 ################################################################################
353 @employe_dans_ma_region_ou_service
354 def dossier(request
, poste_key
, employe_key
):
355 """ Récupération AJAX du dossier pour la page d'embauche. """
358 poste_source
, poste_id
= poste_key
.split('-')
359 poste
= get_object_or_404(dae
.Poste
, pk
=poste_id
)
361 # Récupérer la devise de l'implantation lié au poste
362 implantation_devise
= poste
.get_default_devise()
363 data
.update({'devise' : implantation_devise
.id})
365 if poste
.id_rh_id
is not None:
366 poste_rh
= get_object_or_404(rh
.Poste
, pk
=poste
.id_rh_id
)
370 ##########################################################################################
372 ##########################################################################################
373 if employe_key
== '':
374 employe_source
= 'new'
376 dossier_rh
= rh
.Dossier()
377 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
379 ##########################################################################################
381 ##########################################################################################
382 if employe_key
.startswith('dae'):
383 employe_source
, employe_id
= employe_key
.split('-')
384 employe_dae
= get_object_or_404(dae
.Employe
, pk
=employe_id
)
386 # récupération de l'ancien dossier rh v1 pour l'employe DAE
388 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_dae
.id_rh_id
, mandat_date_fin
=None)
389 except (rh
.Dossier
.DoesNotExist
):
390 dossier_rh
= rh
.Dossier()
392 # on tente de récupérer le dossier DAE, au pire on le contruit en le
393 # prépoluant avec son dossier rh v1.
395 dossier
= dae
.Dossier
.objects
.get(employe
=employe_dae
, poste
=poste
)
396 except (dae
.Dossier
.DoesNotExist
):
397 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
398 employe
= employe_dae
.id_rh
399 ##########################################################################################
401 ##########################################################################################
402 if employe_key
.startswith('rh'):
403 employe_source
, employe_id
= employe_key
.split('-')
404 employe_rh
= get_object_or_404(rh
.Employe
, pk
=employe_id
)
406 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il n'en a pas,
407 # on en fournit un nouveau qui servira uniquement un créer un nouveau dossier DAE.
409 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_rh
, mandat_date_fin
=None)
410 except (rh
.Dossier
.DoesNotExist
):
411 dossier_rh
= rh
.Dossier()
412 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
415 dossier_form
= DossierForm(initial
=data
, instance
=dossier
)
416 vars = dict(form
=dossier_form
, poste
=poste
, employe
=employe
)
417 return render_to_response('dae/embauche-dossier.html', vars,
418 RequestContext(request
))
420 # @Cette fonction est appelée à partir de fonctions déjà sécurisée
421 def pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
):
422 dossier
= dae
.Dossier()
424 if employe_source
!= 'new' and dossier_rh
.id:
425 dossier
.statut_anterieur
= dossier_rh
.statut
427 # Certains dossiers ont un classement à zéro
428 if dossier_rh
.classement_id
> 0:
429 dossier
.classement_anterieur
= dossier_rh
.classement
431 # Récupération du salaire de base
432 remun
= dossier_rh
.remuneration_set
.filter(type=1)
434 dossier
.salaire_anterieur
= remun
[0].montant
436 # Récupération du titulaire précédent
438 dossiers
= rh
.Dossier
.objects
.order_by('-mandat_date_fin')
439 dossiers
= dossiers
.filter(poste1
=poste_rh
) | dossiers
.filter(poste2
=poste_rh
)
441 # Ce bloc ignore toutes les erreurs, car les données de rh
442 # manquantes peuvent en générer
445 titulaire
= d
.employe
446 dossier
.employe_anterieur
= titulaire
447 dossier
.classement_titulaire_anterieur
= d
.classement
448 dossier
.statut_titulaire_anterieur
= d
.statut
449 dossier
.salaire_titulaire_anterieur
= \
450 d
.remuneration_set
.all()[0].montant
453 # TODO: afficher l'info, les champs ne sont pas dans le
454 # modèle dae.Dossier: nom, prenom, classement, salaire
457 except (rh
.Dossier
.DoesNotExist
):
458 dossier_rh
= rh
.Dossier()
463 @dossier_dans_ma_region_ou_service
464 def dossier_resume(request
, dossier_id
=None):
467 output : devise, devise_code, taux_euro
470 dossier
= rh
.Dossier
.objects
.get(id=dossier_id
)
472 return HttpResponseGone("Ce dossier n'est pas accessible")
475 data
['personne'] = unicode(dossier
.employe
)
476 data
['implantation'] = dossier
.implantation1
.id
477 data
['poste'] = u
"%s %s" % (dossier
.poste1
.type_poste
.nom
, dossier
.complement1
)
478 data
['montant'] = dossier
.get_salaire()
479 salaire
= dossier
.get_dernier_salaire_remun()
480 if salaire
is not None:
481 data
['devise'] = dossier
.get_dernier_salaire_remun().devise
.id
482 data
['montant_euros'] = dossier
.get_dernier_salaire_remun().en_euros()
484 data
['devise'] = None
485 data
['montant_euros'] = 0
486 return HttpResponse(dumps(data
))
488 def liste_postes(request
):
490 input : implantation_id
491 output : JSON liste de valeur point
493 method
= request
.method
494 params
= getattr(request
, method
, [])
497 # Voir le code de _poste_choices dans forms.py
498 dae_
= dae
.Poste
.objects
.filter(actif
=True, id_rh__isnull
=True)
499 copies
= dae
.Poste
.objects
.exclude(id_rh__isnull
=True)
500 rh_postes_actifs
= rh
.Poste
.objects
.filter(actif
=True)
502 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
503 implantation_id
= params
.get('implantation_id')
504 dae_
= dae_
.filter(implantation__id
=implantation_id
)
505 copies
= copies
.filter(implantation__id
=implantation_id
)
506 rh_postes_actifs
= rh_postes_actifs
.filter(implantation__id
=implantation_id
)
508 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
509 rhv1
= rh_postes_actifs
.exclude(id__in
=id_copies
)
510 rhv1
= rhv1
.select_related(depth
=1)
512 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])
513 return HttpResponse(dumps(data
))
516 ################################################################################
517 # AJAX SECURITE non nécessaire
518 ################################################################################
519 def coefficient(request
):
524 method
= request
.method
525 params
= getattr(request
, method
, [])
527 if 'classement' in params
and params
.get('classement') is not u
"":
528 classement
= params
.get('classement')
529 classement
= rh
.Classement
.objects
.get(pk
=classement
)
530 data
['coefficient'] = classement
.coefficient
532 data
['coefficient'] = 0
533 return HttpResponse(dumps(data
))
539 output : devise, devise_code, taux_euro
541 method
= request
.method
542 params
= getattr(request
, method
, [])
544 if 'valeur_point' in params
and params
.get('valeur_point') is not u
"":
545 valeur_point
= params
.get('valeur_point')
546 valeur_point
= rh
.ValeurPoint
.objects
.get(pk
=valeur_point
)
547 annee
= valeur_point
.annee
548 implantation
= valeur_point
.implantation
549 taux
= rh
.TauxChange
.objects
.get(annee
=annee
,
550 implantation
=implantation
)
551 data
['devise'] = taux
.devise
.id
552 data
['valeur'] = valeur_point
.valeur
553 data
['devise_code'] = taux
.devise
.code
554 data
['taux_euro'] = taux
.taux
556 return HttpResponseGone("Vous devez choisir une valeur de point")
557 return HttpResponse(dumps(data
))
559 def devise_code(request
):
562 output : devise_code, taux_euro
564 method
= request
.method
565 params
= getattr(request
, method
, [])
567 if 'devise' in params
:
568 devise
= params
.get('devise')
569 devise
= rh
.Devise
.objects
.get(pk
=devise
)
570 annee
= date
.today().year
571 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=devise
)
573 return HttpResponseGone("Le taux n'est pas disponible")
574 data
['devise_code'] = devise
.code
575 data
['taux_euro'] = taux
[0].taux
576 return HttpResponse(dumps(data
))
578 def add_remun(request
, dossier
, type_remun
):
579 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier
)
580 type_remun
= get_object_or_404(rh
.TypeRemuneration
, pk
=type_remun
)
581 dae
.Remuneration(dossier
=dossier
, devise
=dossier
.devise
,
582 type=type_remun
).save()
584 return render_to_response('dae/embauche-remun.html', dict(dossier
=dossier
),
585 RequestContext(request
))
587 def salaire(request
, implantation
, devise
, classement
):
588 if not devise
or not classement
:
591 taux_impl
= rh
.TauxChange
.objects
.filter(implantation
=implantation
) \
593 taux
= rh
.TauxChange
.objects
.filter(devise
=devise
).order_by('-annee')
594 vp
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation
) \
596 if vp
.count() * taux
.count() * taux_impl
.count() == 0:
599 classement
= get_object_or_404(rh
.Classement
, pk
=classement
)
600 taux
, taux_impl
, vp
= taux
[0].taux
, taux_impl
[0].taux
, vp
[0].valeur
602 salaire_euro
= round(vp
* classement
.coefficient
* taux_impl
, 2)
603 data
= dict(salaire_euro
=salaire_euro
, taux
=taux
,
604 salaire_devise
=round(salaire_euro
/ taux
, 2))
606 return HttpResponse(dumps(data
))
608 def liste_valeurs_point(request
):
610 input : implantation_id
611 output : JSON liste de valeur point
613 method
= request
.method
614 params
= getattr(request
, method
, [])
616 annee_courante
= datetime
.datetime
.now().year
617 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
618 implantation_id
= params
.get('implantation_id')
619 objects
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation_id
, annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
621 objects
= rh
.ValeurPoint
.objects
.filter(annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
623 data
.append({'id' : o
.id, 'label' : o
.__unicode__(), })
624 return HttpResponse(dumps(data
))