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
, \
29 dossier_est_modifiable
, \
35 def reponse_pdf(template_src
, context_dict
):
37 Générer une réponse HTTP avec un PDF
40 for f
in ('css/pdf.css', 'css/dae.css'):
41 css_file
= os
.path
.join(settings
.MEDIA_ROOT
, f
)
42 css
+= open(css_file
, 'r').read()
43 context_dict
['css'] = css
45 template
= get_template(template_src
)
46 context
= Context(context_dict
)
47 html
= template
.render(context
)
48 result
= StringIO
.StringIO()
49 pdf
= pisa
.pisaDocument(html
, result
, encoding
='UTF-8')
51 return HttpResponse(result
.getvalue(), mimetype
='application/pdf')
52 return HttpResponse("impossible de générer le pdf! %s" % html
)
57 return render_to_response('dae/index.html', {}, RequestContext(request
))
60 @poste_dans_ma_region_ou_service
61 def poste_consulter(request
, key
):
62 source
, id = key
.split('-')
63 poste
= get_object_or_404(dae
.Poste
, pk
=id)
66 validationForm
= PosteWorkflowForm(request
.POST
, instance
=poste
, request
=request
)
67 if validationForm
.is_valid():
69 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
70 return redirect('poste_consulter', key
=key
)
72 validationForm
= PosteWorkflowForm(instance
=poste
, request
=request
)
74 vars = {'poste' : poste
, 'validationForm' : validationForm
, }
77 mode
= request
.GET
.get('mode', None)
79 return render_to_response('dae/poste_consulter.html', vars, RequestContext(request
))
81 return reponse_pdf('dae/poste_pdf.html', vars)
83 return render_to_response('dae/poste_pdf.html', vars, RequestContext(request
))
87 @poste_dans_ma_region_ou_service
89 def poste(request
, key
=None):
90 """ Formulaire pour un poste.
92 Permet de créer ou modifier un poste. Si le poste n'existe que dans rh_v1
93 il est automatiquement copié dans dae.
96 poste
, data
, vars = None, dict(), dict()
101 source
, id = key
.split('-')
104 poste
= get_object_or_404(dae
.Poste
, pk
=id)
106 p
= get_object_or_404(rh
.Poste
, pk
=id)
107 # Initialisation avec les valeurs du poste de rh_v1
108 poste
= dae
.Poste(id_rh
=p
, nom
=p
.type_poste
.nom
)
109 for field
in ('implantation', 'type_poste', 'actif'):
110 setattr(poste
, field
, getattr(p
, field
))
116 data
.update(dict(request
.POST
.items()))
117 form
= PosteForm(data
, instance
=poste
, request
=request
)
118 financementForm
= FinancementForm(request
.POST
, instance
=poste
)
119 piecesForm
= PostePieceForm(request
.POST
, request
.FILES
, instance
=poste
)
120 if form
.is_valid() and piecesForm
.is_valid() and financementForm
.is_valid():
122 piecesForm
.instance
= poste
124 financementForm
.instance
= poste
125 financementForm
.save()
126 messages
.add_message(request
, messages
.SUCCESS
, "Le poste %s a été sauvegardé." % poste
)
127 if request
.POST
.has_key('save'):
128 return redirect('poste_consulter', key
='dae-%s' % poste
.id)
130 return redirect('poste', key
='dae-%s' % poste
.id)
133 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
136 # 'initial' évite la validation prémature lors d'une copie de poste de
138 form
= PosteForm(initial
=data
, instance
=poste
, request
=request
)
139 piecesForm
= PostePieceForm(instance
=poste
)
140 financementForm
= FinancementForm(instance
=poste
)
142 vars.update(dict(form
=form
, poste
=poste
, poste_key
=key
, piecesForm
=piecesForm
, financementForm
=financementForm
))
144 return render_to_response('dae/poste.html', vars, RequestContext(request
))
147 def postes_liste(request
):
148 """ Liste des postes. """
150 vars['postes'] = dae
.Poste
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
151 return render_to_response('dae/postes_liste.html', vars, RequestContext(request
))
153 def filtered_type_remun():
154 # Exclusion de "Indemnité de fonction" des types de rémun utilisés
155 return rh
.TypeRemuneration
.objects
.exclude(pk
=7)
158 @dossier_dans_ma_region_ou_service
159 def embauche_consulter(request
, dossier_id
):
160 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
163 validationForm
= DossierWorkflowForm(request
.POST
, instance
=dossier
, request
=request
)
164 if validationForm
.is_valid():
165 validationForm
.save()
166 messages
.add_message(request
, messages
.SUCCESS
, "La validation a été enregistrée.")
167 return redirect('embauche_consulter', dossier_id
=dossier
.id)
169 validationForm
= DossierWorkflowForm(instance
=dossier
, request
=request
)
173 'validationForm' : validationForm
,
176 mode
= request
.GET
.get('mode', None)
178 return render_to_response('dae/embauche_consulter.html', vars, RequestContext(request
))
180 return reponse_pdf('dae/embauche_pdf.html', vars)
182 return render_to_response('dae/embauche_pdf.html', vars, RequestContext(request
))
185 @dossier_dans_ma_region_ou_service
186 @dossier_est_modifiable
187 def embauche(request
, key
=None, dossier_id
=None):
188 """ Formulaire d'autorisation d'embauche. """
190 vars = dict(step
='poste', form
=ChoosePosteForm(request
=request
))
192 type_remun
= filtered_type_remun()
193 vars = dict(type_remun
=type_remun
)
194 source
, id = key
.split('-')
197 poste
= get_object_or_404(dae
.Poste
, pk
=id)
202 if request
.POST
['employe'] == '':
204 employe
= dae
.Employe()
206 employe_source
, id = request
.POST
['employe'].split('-')
207 if employe_source
== 'dae':
209 employe
= get_object_or_404(dae
.Employe
, pk
=id)
210 elif employe_source
== 'rh':
211 # Employé RH, on le copie dans DAE
212 e
= get_object_or_404(rh
.Employe
, pk
=id)
213 employe
= dae
.Employe(id_rh
=e
, prenom
=e
.prenom
, nom
=e
.nom
,
218 employe_form
= EmployeForm(request
.POST
, instance
=employe
, request
=request
)
221 if employe_form
.is_valid():
222 data
= dict(request
.POST
.items())
223 #with warnings.catch_warnings():
224 # warnings.simplefilter('ignore')
225 employe
= employe_form
.save()
226 data
['employe'] = 'dae-%s' % employe
.id
227 employe_form
= EmployeForm(data
, instance
=employe
, request
=request
)
230 dossier
= dae
.Dossier(poste
=poste
, employe
=employe
)
232 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
233 dossier_form
= DossierForm(request
.POST
, instance
=dossier
)
234 piecesForm
= DossierPieceForm(request
.POST
, request
.FILES
, instance
=dossier
)
235 justificationsNouveauForm
= JustificationNouvelEmployeForm(request
.POST
, instance
=dossier
)
236 justificationsAutreForm
= JustificationAutreEmployeForm(request
.POST
, instance
=dossier
)
237 dossiersComparaisonsForm
= DossierComparaisonForm(request
.POST
, instance
=dossier
)
239 if dossier_form
.is_valid() and piecesForm
.is_valid() and justificationsNouveauForm
.is_valid() and justificationsAutreForm
.is_valid() and dossiersComparaisonsForm
.is_valid():
240 dossier
= dossier_form
.save()
241 piecesForm
.instance
= dossier
243 justificationsNouveauForm
.instance
= dossier
244 justificationsNouveauForm
.save()
245 justificationsAutreForm
.instance
= dossier
246 justificationsAutreForm
.save()
247 dossiersComparaisonsForm
.instance
= dossier
248 dossiersComparaisonsForm
.save()
250 if not dossier
.remuneration_set
.all():
251 # Pré-peuplement des entrées de la section "coût
252 # global", à l'exclusion de "Indemnité de fonction"
253 for type in type_remun
.all():
254 dae
.Remuneration(dossier
=dossier
, type=type,
255 devise
=dossier
.devise
).save()
258 # Sauvegarde du coût global
259 cg_lines
= defaultdict(dict)
260 for k
, v
in request
.POST
.items():
261 if k
.startswith('cg-'):
262 prefix
, field_name
, cg_id
= k
.split('-')
263 cg_lines
[int(cg_id
)][unicode(field_name
)] = v
265 for r
in dossier
.remuneration_set
.all():
266 print 'trying %r' % r
268 if cg_lines
[r
.id]['montant'] == '':
271 for k
, v
in cg_lines
[r
.id].items():
275 messages
.add_message(request
, messages
.SUCCESS
, "Le dossier %s a été sauvegardé." % dossier
)
276 if request
.POST
.has_key('save'):
277 return redirect('embauche_consulter', dossier_id
=dossier
.id)
279 return redirect('embauche', key
=dossier
.poste
.key
, dossier_id
=dossier
.id)
282 messages
.add_message(request
, messages
.ERROR
, 'Il y a des erreurs dans le formulaire.')
285 dossier_form
= DossierForm(instance
=dossier
)
286 piecesForm
= DossierPieceForm(instance
=dossier
)
287 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
288 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
289 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
291 # Initialisation d'un formulaire vide
292 dossier_rh
= rh
.Dossier()
293 poste_rh
= poste
.id_rh
295 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier_id
)
296 employe
= dossier
.employe
297 data
= dict(employe
='dae-%s' % employe
.id)
298 employe_form
= EmployeForm(initial
=data
, instance
=employe
, request
=request
)
300 dossier
= pre_filled_dossier(dossier_rh
, 'new', poste_rh
)
301 employe_form
= EmployeForm(request
=request
)
303 dossier_form
= DossierForm(instance
=dossier
)
304 piecesForm
= DossierPieceForm(instance
=dossier
)
305 justificationsNouveauForm
= JustificationNouvelEmployeForm(instance
=dossier
)
306 justificationsAutreForm
= JustificationAutreEmployeForm(instance
=dossier
)
307 dossiersComparaisonsForm
= DossierComparaisonForm(instance
=dossier
)
309 vars = dict(step
='employe',
310 type_remun
=type_remun
,
313 piecesForm
=piecesForm
,
314 justificationsNouveauForm
=justificationsNouveauForm
,
315 justificationsAutreForm
=justificationsAutreForm
,
316 dossiersComparaisonsForm
=dossiersComparaisonsForm
,
317 forms
=dict(employe
=employe_form
, dossier
=dossier_form
, )
321 return render_to_response('dae/embauche.html', vars,
322 RequestContext(request
))
324 @dossier_dans_ma_region_ou_service
325 def embauches_liste(request
):
326 """ Liste des embauches. """
328 vars['embauches'] = dae
.Dossier
.objects
.ma_region_ou_service(request
.user
).order_by('-date_creation')
329 return render_to_response('dae/embauches_liste.html', vars, RequestContext(request
))
331 def employe(request
, key
):
332 """ Récupération AJAX de l'employé pour la page d'embauche. """
333 data
= dict(employe
=key
)
337 employe
= dae
.Employe()
340 source
, id = key
.split('-')
343 employe
= get_object_or_404(dae
.Employe
, pk
=id)
345 e
= get_object_or_404(rh
.Employe
, id=id)
346 # Initialisation avec les valeurs de l'employé de rh_v1
347 employe
= dae
.Employe(id_rh
=e
)
348 for field
in ('prenom', 'nom', 'genre'):
349 setattr(employe
, field
, getattr(e
, field
))
351 return HttpResponse(EmployeForm(initial
=data
, instance
=employe
, request
=request
).as_table())
353 ################################################################################
355 ################################################################################
357 @employe_dans_ma_region_ou_service
358 def dossier(request
, poste_key
, employe_key
):
359 """ Récupération AJAX du dossier pour la page d'embauche. """
362 poste_source
, poste_id
= poste_key
.split('-')
363 poste
= get_object_or_404(dae
.Poste
, pk
=poste_id
)
365 # Récupérer la devise de l'implantation lié au poste
366 implantation_devise
= poste
.get_default_devise()
367 data
.update({'devise' : implantation_devise
.id})
369 if poste
.id_rh_id
is not None:
370 poste_rh
= get_object_or_404(rh
.Poste
, pk
=poste
.id_rh_id
)
374 ##########################################################################################
376 ##########################################################################################
377 if employe_key
== '':
378 employe_source
= 'new'
380 dossier_rh
= rh
.Dossier()
381 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
383 ##########################################################################################
385 ##########################################################################################
386 if employe_key
.startswith('dae'):
387 employe_source
, employe_id
= employe_key
.split('-')
388 employe_dae
= get_object_or_404(dae
.Employe
, pk
=employe_id
)
390 # récupération de l'ancien dossier rh v1 pour l'employe DAE
392 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_dae
.id_rh_id
, mandat_date_fin
=None)
393 except (rh
.Dossier
.DoesNotExist
):
394 dossier_rh
= rh
.Dossier()
396 # on tente de récupérer le dossier DAE, au pire on le contruit en le
397 # prépoluant avec son dossier rh v1.
399 dossier
= dae
.Dossier
.objects
.get(employe
=employe_dae
, poste
=poste
)
400 except (dae
.Dossier
.DoesNotExist
):
401 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
402 employe
= employe_dae
.id_rh
403 ##########################################################################################
405 ##########################################################################################
406 if employe_key
.startswith('rh'):
407 employe_source
, employe_id
= employe_key
.split('-')
408 employe_rh
= get_object_or_404(rh
.Employe
, pk
=employe_id
)
410 # récupération de l'ancien dossier rh v1 pour l'employe rh v1, s'il n'en a pas,
411 # on en fournit un nouveau qui servira uniquement un créer un nouveau dossier DAE.
413 dossier_rh
= rh
.Dossier
.objects
.get(employe
=employe_rh
, mandat_date_fin
=None)
414 except (rh
.Dossier
.DoesNotExist
):
415 dossier_rh
= rh
.Dossier()
416 dossier
= pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
)
419 dossier_form
= DossierForm(initial
=data
, instance
=dossier
)
420 vars = dict(form
=dossier_form
, poste
=poste
, employe
=employe
)
421 return render_to_response('dae/embauche-dossier.html', vars,
422 RequestContext(request
))
424 # @Cette fonction est appelée à partir de fonctions déjà sécurisée
425 def pre_filled_dossier(dossier_rh
, employe_source
, poste_rh
):
426 dossier
= dae
.Dossier()
428 if employe_source
!= 'new' and dossier_rh
.id:
429 dossier
.statut_anterieur
= dossier_rh
.statut
431 # Certains dossiers ont un classement à zéro
432 if dossier_rh
.classement_id
> 0:
433 dossier
.classement_anterieur
= dossier_rh
.classement
435 # Récupération du salaire de base
436 remun
= dossier_rh
.remuneration_set
.filter(type=1)
438 dossier
.salaire_anterieur
= remun
[0].montant
440 # Récupération du titulaire précédent
442 dossiers
= rh
.Dossier
.objects
.order_by('-mandat_date_fin')
443 dossiers
= dossiers
.filter(poste1
=poste_rh
) | dossiers
.filter(poste2
=poste_rh
)
445 # Ce bloc ignore toutes les erreurs, car les données de rh
446 # manquantes peuvent en générer
449 titulaire
= d
.employe
450 dossier
.employe_anterieur
= titulaire
451 dossier
.classement_titulaire_anterieur
= d
.classement
452 dossier
.statut_titulaire_anterieur
= d
.statut
453 dossier
.salaire_titulaire_anterieur
= \
454 d
.remuneration_set
.all()[0].montant
457 # TODO: afficher l'info, les champs ne sont pas dans le
458 # modèle dae.Dossier: nom, prenom, classement, salaire
461 except (rh
.Dossier
.DoesNotExist
):
462 dossier_rh
= rh
.Dossier()
467 @dossier_dans_ma_region_ou_service
468 def dossier_resume(request
, dossier_id
=None):
471 output : devise, devise_code, taux_euro
474 dossier
= rh
.Dossier
.objects
.get(id=dossier_id
)
476 return HttpResponseGone("Ce dossier n'est pas accessible")
479 data
['personne'] = unicode(dossier
.employe
)
480 data
['implantation'] = dossier
.implantation1
.id
481 data
['poste'] = u
"%s %s" % (dossier
.poste1
.type_poste
.nom
, dossier
.complement1
)
482 data
['montant'] = dossier
.get_salaire()
483 salaire
= dossier
.get_dernier_salaire_remun()
484 if salaire
is not None:
485 data
['devise'] = dossier
.get_dernier_salaire_remun().devise
.id
486 data
['montant_euros'] = dossier
.get_dernier_salaire_remun().en_euros()
488 data
['devise'] = None
489 data
['montant_euros'] = 0
490 return HttpResponse(dumps(data
))
492 def liste_postes(request
):
494 input : implantation_id
495 output : JSON liste de valeur point
497 method
= request
.method
498 params
= getattr(request
, method
, [])
501 # Voir le code de _poste_choices dans forms.py
502 dae_
= dae
.Poste
.objects
.filter(actif
=True, id_rh__isnull
=True)
503 copies
= dae
.Poste
.objects
.exclude(id_rh__isnull
=True)
504 rh_postes_actifs
= rh
.Poste
.objects
.filter(actif
=True)
506 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
507 implantation_id
= params
.get('implantation_id')
508 dae_
= dae_
.filter(implantation__id
=implantation_id
)
509 copies
= copies
.filter(implantation__id
=implantation_id
)
510 rh_postes_actifs
= rh_postes_actifs
.filter(implantation__id
=implantation_id
)
512 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
513 rhv1
= rh_postes_actifs
.exclude(id__in
=id_copies
)
514 rhv1
= rhv1
.select_related(depth
=1)
516 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])
517 return HttpResponse(dumps(data
))
520 ################################################################################
521 # AJAX SECURITE non nécessaire
522 ################################################################################
523 def coefficient(request
):
528 method
= request
.method
529 params
= getattr(request
, method
, [])
531 if 'classement' in params
and params
.get('classement') is not u
"":
532 classement
= params
.get('classement')
533 classement
= rh
.Classement
.objects
.get(pk
=classement
)
534 data
['coefficient'] = classement
.coefficient
536 data
['coefficient'] = 0
537 return HttpResponse(dumps(data
))
543 output : devise, devise_code, taux_euro
545 method
= request
.method
546 params
= getattr(request
, method
, [])
548 if 'valeur_point' in params
and params
.get('valeur_point') is not u
"":
549 valeur_point
= params
.get('valeur_point')
550 valeur_point
= rh
.ValeurPoint
.objects
.get(pk
=valeur_point
)
551 annee
= valeur_point
.annee
552 implantation
= valeur_point
.implantation
553 taux
= rh
.TauxChange
.objects
.get(annee
=annee
,
554 implantation
=implantation
)
555 data
['devise'] = taux
.devise
.id
556 data
['valeur'] = valeur_point
.valeur
557 data
['devise_code'] = taux
.devise
.code
558 data
['taux_euro'] = taux
.taux
560 return HttpResponseGone("Vous devez choisir une valeur de point")
561 return HttpResponse(dumps(data
))
563 def devise_code(request
):
566 output : devise_code, taux_euro
568 method
= request
.method
569 params
= getattr(request
, method
, [])
571 if 'devise' in params
:
572 devise
= params
.get('devise')
573 devise
= rh
.Devise
.objects
.get(pk
=devise
)
574 annee
= date
.today().year
575 taux
= rh
.TauxChange
.objects
.filter(annee
=annee
, devise
=devise
)
577 return HttpResponseGone("Le taux n'est pas disponible")
578 data
['devise_code'] = devise
.code
579 data
['taux_euro'] = taux
[0].taux
580 return HttpResponse(dumps(data
))
582 def add_remun(request
, dossier
, type_remun
):
583 dossier
= get_object_or_404(dae
.Dossier
, pk
=dossier
)
584 type_remun
= get_object_or_404(rh
.TypeRemuneration
, pk
=type_remun
)
585 dae
.Remuneration(dossier
=dossier
, devise
=dossier
.devise
,
586 type=type_remun
).save()
588 return render_to_response('dae/embauche-remun.html', dict(dossier
=dossier
),
589 RequestContext(request
))
591 def salaire(request
, implantation
, devise
, classement
):
592 if not devise
or not classement
:
595 taux_impl
= rh
.TauxChange
.objects
.filter(implantation
=implantation
) \
597 taux
= rh
.TauxChange
.objects
.filter(devise
=devise
).order_by('-annee')
598 vp
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation
) \
600 if vp
.count() * taux
.count() * taux_impl
.count() == 0:
603 classement
= get_object_or_404(rh
.Classement
, pk
=classement
)
604 taux
, taux_impl
, vp
= taux
[0].taux
, taux_impl
[0].taux
, vp
[0].valeur
606 salaire_euro
= round(vp
* classement
.coefficient
* taux_impl
, 2)
607 data
= dict(salaire_euro
=salaire_euro
, taux
=taux
,
608 salaire_devise
=round(salaire_euro
/ taux
, 2))
610 return HttpResponse(dumps(data
))
612 def liste_valeurs_point(request
):
614 input : implantation_id
615 output : JSON liste de valeur point
617 method
= request
.method
618 params
= getattr(request
, method
, [])
620 annee_courante
= datetime
.datetime
.now().year
621 if 'implantation_id' in params
and params
.get('implantation_id') is not u
"":
622 implantation_id
= params
.get('implantation_id')
623 objects
= rh
.ValeurPoint
.objects
.filter(implantation
=implantation_id
, annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
625 objects
= rh
.ValeurPoint
.objects
.filter(annee__in
=(annee_courante
-1, annee_courante
)).order_by("-annee")
627 data
.append({'id' : o
.id, 'label' : o
.__unicode__(), })
628 return HttpResponse(dumps(data
))