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 employe_dans_ma_region_ou_service
, \
28 dossier_est_modifiable
, \
34 for d
in rh
.Devise
.objects
.all():
35 annee
= date
.today().year
36 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=d
)
41 data
['taux_euro'] = taux
[0].taux
42 data
['devise_code'] = d
.code
46 def reponse_pdf(template_src
, context_dict
):
48 Générer une réponse HTTP avec un PDF
50 import ho
.pisa
as pisa
53 for f
in ('css/pdf.css', 'css/dae.css'):
54 css_file
= os
.path
.join(settings
.MEDIA_ROOT
, f
)
55 css
+= open(css_file
, 'r').read()
56 context_dict
['css'] = css
58 template
= get_template(template_src
)
59 context
= Context(context_dict
)
60 html
= template
.render(context
)
61 result
= StringIO
.StringIO()
62 pdf
= pisa
.pisaDocument(html
, result
, encoding
='UTF-8')
64 return HttpResponse(result
.getvalue(), mimetype
='application/pdf')
65 return HttpResponse("impossible de générer le pdf! %s" % html
)
70 return render_to_response('dae/index.html', {}, RequestContext(request
))
73 @poste_dans_ma_region_ou_service
74 def poste_consulter(request
, key
):
75 source
, id = key
.split('-')
76 poste
= get_object_or_404(dae
.Poste
, pk
=id)
79 validationForm
= PosteWorkflowForm(request
.POST
, instance
=poste
, request
=request
)
80 if validationForm
.is_valid():
82 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
83 return redirect('poste_consulter', key
=key
)
85 validationForm
= PosteWorkflowForm(instance
=poste
, request
=request
)
87 vars = {'poste' : poste
, 'validationForm' : validationForm
, }
90 mode
= request
.GET
.get('mode', None)
92 return render_to_response('dae/poste_consulter.html', vars, RequestContext(request
))
94 return reponse_pdf('dae/poste_pdf.html', vars)
96 return render_to_response('dae/poste_pdf.html', vars, RequestContext(request
))
100 @poste_dans_ma_region_ou_service
101 @poste_est_modifiable
102 def poste(request
, key
=None):
103 """ Formulaire pour un poste.
105 Permet de créer ou modifier un poste. Si le poste n'existe que dans rh_v1
106 il est automatiquement copié dans dae.
109 poste
, data
, vars = None, dict(), dict()
114 source
, id = key
.split('-')
117 poste
= get_object_or_404(dae
.Poste
, pk
=id)
119 p
= get_object_or_404(rh
.Poste
, pk
=id)
120 # Initialisation avec les valeurs du poste de rh_v1
121 poste
= dae
.Poste(id_rh
=p
, nom
=p
.type_poste
.nom
)
122 for field
in ('implantation', 'type_poste', 'actif'):
123 setattr(poste
, field
, getattr(p
, field
))
129 data
.update(dict(request
.POST
.items()))
130 form
= PosteForm(data
, instance
=poste
, request
=request
)
131 financementForm
= FinancementForm(request
.POST
, instance
=poste
)
132 piecesForm
= PostePieceForm(request
.POST
, request
.FILES
, instance
=poste
)
133 if form
.is_valid() and piecesForm
.is_valid() and financementForm
.is_valid():
135 piecesForm
.instance
= poste
137 financementForm
.instance
= poste
138 financementForm
.save()
139 messages
.add_message(request
, messages
.SUCCESS
, "Le poste %s a été sauvegardé." % poste
)
140 if request
.POST
.has_key('save'):
141 return redirect('poste_consulter', key
='dae-%s' % poste
.id)
143 return redirect('poste', key
='dae-%s' % poste
.id)
146 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
149 # 'initial' évite la validation prémature lors d'une copie de poste de
151 form
= PosteForm(initial
=data
, instance
=poste
, request
=request
)
152 piecesForm
= PostePieceForm(instance
=poste
)
153 financementForm
= FinancementForm(instance
=poste
)
155 vars.update(dict(form
=form
, poste
=poste
, poste_key
=key
, piecesForm
=piecesForm
, financementForm
=financementForm
))
157 return render_to_response('dae/poste.html', vars, RequestContext(request
))
160 def postes_liste(request
):
161 """ Liste des postes. """
163 vars['postes'] = dae
.Poste
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
164 return render_to_response('dae/postes_liste.html', vars, RequestContext(request
))
166 def filtered_type_remun():
167 defaut
= (2, 3, 8, 17) # salaire de base, indemnité de fonction, charges patronales
168 return rh
.TypeRemuneration
.objects
.filter(pk__in
=defaut
)
171 @dossier_dans_ma_region_ou_service
172 def embauche_consulter(request
, dossier_id
):
173 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
176 validationForm
= DossierWorkflowForm(request
.POST
, instance
=dossier
, request
=request
)
177 if validationForm
.is_valid():
178 validationForm
.save()
179 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
180 return redirect('embauche_consulter', dossier_id
=dossier
.id)
182 validationForm
= DossierWorkflowForm(instance
=dossier
, request
=request
)
186 'validationForm' : validationForm
,
189 mode
= request
.GET
.get('mode', None)
191 return render_to_response('dae/embauche_consulter.html', vars, RequestContext(request
))
193 return reponse_pdf('dae/embauche_pdf.html', vars)
195 return render_to_response('dae/embauche_pdf.html', vars, RequestContext(request
))
198 @dossier_dans_ma_region_ou_service
199 @dossier_est_modifiable
200 def embauche(request
, key
=None, dossier_id
=None):
201 """ Formulaire d'autorisation d'embauche. """
203 vars = dict(step
='poste', form
=ChoosePosteForm(request
=request
))
205 type_remun
= filtered_type_remun()
206 vars = dict(type_remun
=type_remun
)
207 source
, id = key
.split('-')
210 poste
= get_object_or_404(dae
.Poste
, pk
=id)
215 if request
.POST
['employe'] == '':
217 employe
= dae
.Employe()
219 employe_source
, id = request
.POST
['employe'].split('-')
220 if employe_source
== 'dae':
222 employe
= get_object_or_404(dae
.Employe
, pk
=id)
223 elif employe_source
== 'rh':
224 # Employé RH, on le copie dans DAE
225 e
= get_object_or_404(rh
.Employe
, pk
=id)
226 employe
= dae
.Employe(id_rh
=e
, prenom
=e
.prenom
, nom
=e
.nom
,
231 employe_form
= EmployeForm(request
.POST
, instance
=employe
, request
=request
)
234 if employe_form
.is_valid():
235 data
= dict(request
.POST
.items())
236 #with warnings.catch_warnings():
237 # warnings.simplefilter('ignore')
238 employe
= employe_form
.save()
239 data
['employe'] = 'dae-%s' % employe
.id
240 employe_form
= EmployeForm(data
, instance
=employe
, request
=request
)
243 dossier
= dae
.Dossier(poste
=poste
, employe
=employe
)
245 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
246 dossier_form
= DossierForm(request
.POST
, instance
=dossier
)
247 piecesForm
= DossierPieceForm(request
.POST
, request
.FILES
, instance
=dossier
)
248 justificationsNouveauForm
= JustificationNouvelEmployeForm(request
.POST
, instance
=dossier
)
249 justificationsAutreForm
= JustificationAutreEmployeForm(request
.POST
, instance
=dossier
)
250 dossiersComparaisonsForm
= DossierComparaisonForm(request
.POST
, instance
=dossier
)
251 remunForm
= RemunForm(request
.POST
, instance
=dossier
)
252 print remunForm
.errors
253 if dossier_form
.is_valid() and \
254 piecesForm
.is_valid() and \
255 justificationsNouveauForm
.is_valid() and \
256 justificationsAutreForm
.is_valid() and \
257 dossiersComparaisonsForm
.is_valid() and \
258 remunForm
.is_valid():
260 dossier
= dossier_form
.save()
261 piecesForm
.instance
= dossier
263 justificationsNouveauForm
.instance
= dossier
264 justificationsNouveauForm
.save()
265 justificationsAutreForm
.instance
= dossier
266 justificationsAutreForm
.save()
267 dossiersComparaisonsForm
.instance
= dossier
268 dossiersComparaisonsForm
.save()
269 remunForm
.instance
= dossier
272 #if not dossier.remuneration_set.all():
273 # # Pré-peuplement des entrées de la section "coût
274 # # global", à l'exclusion de "Indemnité de fonction"
275 # for type in type_remun.all():
276 # dae.Remuneration(dossier=dossier, type=type,
277 # devise=dossier.devise).save()
280 # # Sauvegarde du coût global
281 # cg_lines = defaultdict(dict)
282 # for k, v in request.POST.items():
283 # if k.startswith('cg-'):
284 # prefix, field_name, cg_id = k.split('-')
285 # cg_lines[int(cg_id)][unicode(field_name)] = v
287 # for r in dossier.remuneration_set.all():
288 # print 'trying %r' % r
289 # if r.id in cg_lines:
290 # if cg_lines[r.id]['montant'] == '':
293 # for k, v in cg_lines[r.id].items():
297 messages
.add_message(request
, messages
.SUCCESS
, "Le dossier %s a été sauvegardé." % dossier
)
298 if request
.POST
.has_key('save'):
299 return redirect('embauche_consulter', dossier_id
=dossier
.id)
301 return redirect('embauche', key
=dossier
.poste
.key
, dossier_id
=dossier
.id)
304 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
307 dossier_form
= DossierForm(instance
=dossier
)
308 piecesForm
= DossierPieceForm(instance
=dossier
)
309 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
310 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
311 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
312 remunForm
= RemunForm(instance
=dossier
)
314 # Initialisation d'un formulaire vide
315 dossier_rh
= rh
.Dossier()
316 poste_rh
= poste
.id_rh
318 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
319 employe
= dossier
.employe
320 data
= dict(employe
='dae-%s' % employe
.id)
321 employe_form
= EmployeForm(initial
=data
, instance
=employe
, request
=request
)
323 dossier
= pre_filled_dossier(dossier_rh
, 'new', poste_rh
)
324 employe_form
= EmployeForm(request
=request
)
326 dossier_form
= DossierForm(instance
=dossier
)
327 piecesForm
= DossierPieceForm(instance
=dossier
)
328 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
329 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
330 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
331 remunForm
= RemunForm(instance
=dossier
)
333 vars = dict(step
='employe',
334 type_remun
=type_remun
,
338 piecesForm
=piecesForm
,
340 justificationsNouveauForm
=justificationsNouveauForm
,
341 justificationsAutreForm
=justificationsAutreForm
,
342 dossiersComparaisonsForm
=dossiersComparaisonsForm
,
343 forms
=dict(employe
=employe_form
, dossier
=dossier_form
, )
347 return render_to_response('dae/embauche.html', vars,
348 RequestContext(request
))
350 @dossier_dans_ma_region_ou_service
351 def embauches_liste(request
):
352 """ Liste des embauches. """
354 vars['embauches'] = dae
.Dossier
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
355 return render_to_response('dae/embauches_liste.html', vars, RequestContext(request
))
357 def employe(request
, key
):
358 """ Récupération AJAX de l'employé pour la page d'embauche. """
359 data
= dict(employe
=key
)
363 employe
= dae
.Employe()
366 source
, id = key
.split('-')
369 employe
= get_object_or_404(dae
.Employe
, pk
=id)
371 e
= get_object_or_404(rh
.Employe
, id=id)
372 # Initialisation avec les valeurs de l'employé de rh_v1
373 employe
= dae
.Employe(id_rh
=e
)
374 for field
in ('prenom', 'nom', 'genre'):
375 setattr(employe
, field
, getattr(e
, field
))
377 return HttpResponse(EmployeForm(initial
=data
, instance
=employe
, request
=request
).as_table())
379 ################################################################################
381 ################################################################################
383 @employe_dans_ma_region_ou_service
384 def dossier(request
, poste_key
, employe_key
):
385 """ Récupération AJAX du dossier pour la page d'embauche. """
388 poste_source
, poste_id
= poste_key
.split('-')
389 poste
= get_object_or_404(dae
.Poste
, pk
=poste_id
)
391 # Récupérer la devise de l'implantation lié au poste
392 implantation_devise
= poste
.get_default_devise()
393 data
.update({'devise' : implantation_devise
.id})
395 if poste
.id_rh_id
is not None:
396 poste_rh
= get_object_or_404(rh
.Poste
, pk
=poste
.id_rh_id
)
400 ##########################################################################################
402 ##########################################################################################
403 if employe_key
== '':
404 employe_source
= 'new'
406 dossier_rh
= rh
.Dossier()
407 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
409 ##########################################################################################
411 ##########################################################################################
412 if employe_key
.startswith('dae'):
413 employe_source
, employe_id
= employe_key
.split('-')
414 employe_dae
= get_object_or_404(dae
.Employe
, pk
=employe_id
)
416 # récupération de l'ancien dossier rh v1 pour l'employe DAE
418 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_dae
.id_rh_id
, mandat_date_fin
=None)
419 except (rh
.Dossier
.DoesNotExist
):
420 dossier_rh
= rh
.Dossier()
422 # on tente de récupérer le dossier DAE, au pire on le contruit en le
423 # prépoluant avec son dossier rh v1.
425 dossier
= dae
.Dossier
.objects
.get(employe
=employe_dae
, poste
=poste
)
426 except (dae
.Dossier
.DoesNotExist
):
427 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
428 employe
= employe_dae
.id_rh
429 ##########################################################################################
431 ##########################################################################################
432 if employe_key
.startswith('rh'):
433 employe_source
, employe_id
= employe_key
.split('-')
434 employe_rh
= get_object_or_404(rh
.Employe
, pk
=employe_id
)
436 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il n'en a pas,
437 # on en fournit un nouveau qui servira uniquement un créer un nouveau dossier DAE.
439 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_rh
, mandat_date_fin
=None)
440 except (rh
.Dossier
.DoesNotExist
):
441 dossier_rh
= rh
.Dossier()
442 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
445 dossier_form
= DossierForm(initial
=data
, instance
=dossier
)
446 vars = dict(form
=dossier_form
, poste
=poste
, employe
=employe
)
447 return render_to_response('dae/embauche-dossier.html', vars,
448 RequestContext(request
))
450 # @Cette fonction est appelée à partir de fonctions déjà sécurisée
451 def pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
):
452 dossier
= dae
.Dossier()
454 if employe_source
!= 'new' and dossier_rh
.id:
455 dossier
.statut_anterieur
= dossier_rh
.statut
457 # Certains dossiers ont un classement à zéro
458 if dossier_rh
.classement_id
> 0:
459 dossier
.classement_anterieur
= dossier_rh
.classement
461 # Récupération du salaire de base
462 remun
= dossier_rh
.remuneration_set
.filter(type=1)
464 dossier
.salaire_anterieur
= remun
[0].montant
466 # Récupération du titulaire précédent
468 dossiers
= rh
.Dossier
.objects
.order_by('-mandat_date_fin')
469 dossiers
= dossiers
.filter(poste1
=poste_rh
) | dossiers
.filter(poste2
=poste_rh
)
471 # Ce bloc ignore toutes les erreurs, car les données de rh
472 # manquantes peuvent en générer
475 titulaire
= d
.employe
476 dossier
.employe_anterieur
= titulaire
477 dossier
.classement_titulaire_anterieur
= d
.classement
478 dossier
.statut_titulaire_anterieur
= d
.statut
479 dossier
.salaire_titulaire_anterieur
= \
480 d
.remuneration_set
.all()[0].montant
483 # TODO: afficher l'info, les champs ne sont pas dans le
484 # modèle dae.Dossier: nom, prenom, classement, salaire
487 except (rh
.Dossier
.DoesNotExist
):
488 dossier_rh
= rh
.Dossier()
493 @dossier_dans_ma_region_ou_service
494 def dossier_resume(request
, dossier_id
=None):
497 output : devise, devise_code, taux_euro
500 dossier
= rh
.Dossier
.objects
.get(id=dossier_id
)
502 return HttpResponseGone("Ce dossier n'est pas accessible")
505 data
['personne'] = unicode(dossier
.employe
)
506 data
['implantation'] = dossier
.implantation1
.id
507 data
['poste'] = u
"%s %s" % (dossier
.poste1
.type_poste
.nom
, dossier
.complement1
)
508 data
['montant'] = dossier
.get_salaire()
509 salaire
= dossier
.get_dernier_salaire_remun()
510 if salaire
is not None:
511 data
['devise'] = salaire
.devise
.id
512 data
['montant_euros'] = salaire
.en_euros()
514 data
['devise'] = None
515 data
['montant_euros'] = 0
516 return HttpResponse(dumps(data
))
518 def liste_postes(request
):
520 input : implantation_id
521 output : JSON liste de valeur point
523 method
= request
.method
524 params
= getattr(request
, method
, [])
527 # Voir le code de _poste_choices dans forms.py
528 dae_
= dae
.Poste
.objects
.filter(actif
=True, id_rh__isnull
=True)
529 copies
= dae
.Poste
.objects
.exclude(id_rh__isnull
=True)
530 rh_postes_actifs
= rh
.Poste
.objects
.filter(actif
=True)
532 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
533 implantation_id
= params
.get('implantation_id')
534 dae_
= dae_
.filter(implantation__id
=implantation_id
)
535 copies
= copies
.filter(implantation__id
=implantation_id
)
536 rh_postes_actifs
= rh_postes_actifs
.filter(implantation__id
=implantation_id
)
538 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
539 rhv1
= rh_postes_actifs
.exclude(id__in
=id_copies
)
540 rhv1
= rhv1
.select_related(depth
=1)
542 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])
543 return HttpResponse(dumps(data
))
546 ################################################################################
547 # AJAX SECURITE non nécessaire
548 ################################################################################
549 def coefficient(request
):
554 method
= request
.method
555 params
= getattr(request
, method
, [])
557 if 'classement' in params
and params
.get('classement') is not u
"":
558 classement
= params
.get('classement')
559 classement
= rh
.Classement
.objects
.get(pk
=classement
)
560 data
['coefficient'] = classement
.coefficient
562 data
['coefficient'] = 0
563 return HttpResponse(dumps(data
))
569 output : devise, devise_code, taux_euro
571 method
= request
.method
572 params
= getattr(request
, method
, [])
574 if 'valeur_point' in params
and params
.get('valeur_point') is not u
"":
575 valeur_point
= params
.get('valeur_point')
576 valeur_point
= rh
.ValeurPoint
.objects
.get(pk
=valeur_point
)
577 annee
= valeur_point
.annee
578 implantation
= valeur_point
.implantation
579 taux
= rh
.TauxChange
.objects
.get(annee
=annee
,
580 implantation
=implantation
)
581 data
['devise'] = taux
.devise
.id
582 data
['valeur'] = valeur_point
.valeur
583 data
['devise_code'] = taux
.devise
.code
584 data
['taux_euro'] = taux
.taux
586 return HttpResponseGone("Vous devez choisir une valeur de point")
587 return HttpResponse(dumps(data
))
589 def devise_code(request
):
592 output : devise_code, taux_euro
594 method
= request
.method
595 params
= getattr(request
, method
, [])
597 if 'devise' in params
:
598 devise
= params
.get('devise')
599 devise
= rh
.Devise
.objects
.get(pk
=devise
)
600 annee
= date
.today().year
601 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=devise
)
603 return HttpResponseGone("Le taux n'est pas disponible")
604 data
['devise_code'] = devise
.code
605 data
['taux_euro'] = taux
[0].taux
606 return HttpResponse(dumps(data
))
608 def add_remun(request
, dossier
, type_remun
):
609 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier
)
610 type_remun
= get_object_or_404(rh
.TypeRemuneration
, pk
=type_remun
)
611 dae
.Remuneration(dossier
=dossier
, devise
=dossier
.devise
,
612 type=type_remun
).save()
614 return render_to_response('dae/embauche-remun.html', dict(dossier
=dossier
),
615 RequestContext(request
))
617 def salaire(request
, implantation
, devise
, classement
):
618 if not devise
or not classement
:
621 taux_impl
= rh
.TauxChange
.objects
.filter(implantation
=implantation
) \
623 taux
= rh
.TauxChange
.objects
.filter(devise
=devise
).order_by('-annee')
624 vp
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation
) \
626 if vp
.count() * taux
.count() * taux_impl
.count() == 0:
629 classement
= get_object_or_404(rh
.Classement
, pk
=classement
)
630 taux
, taux_impl
, vp
= taux
[0].taux
, taux_impl
[0].taux
, vp
[0].valeur
632 salaire_euro
= round(vp
* classement
.coefficient
* taux_impl
, 2)
633 data
= dict(salaire_euro
=salaire_euro
, taux
=taux
,
634 salaire_devise
=round(salaire_euro
/ taux
, 2))
636 return HttpResponse(dumps(data
))
638 def liste_valeurs_point(request
):
640 input : implantation_id
641 output : JSON liste de valeur point
643 method
= request
.method
644 params
= getattr(request
, method
, [])
646 annee_courante
= datetime
.datetime
.now().year
647 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
648 implantation_id
= params
.get('implantation_id')
649 objects
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation_id
, annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
651 objects
= rh
.ValeurPoint
.objects
.filter(annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
653 data
.append({'id' : o
.id, 'label' : o
.__unicode__(), })
654 return HttpResponse(dumps(data
))