1 # -*- encoding: utf-8 -*-
5 from auf
.django
.workflow
.forms
import WorkflowFormMixin
6 from auf
.django
.references
import models
as ref
7 from ajax_select
.fields
import AutoCompleteSelectField
8 from django
import forms
9 from django
.contrib
.admin
import widgets
as admin_widgets
10 from django
.db
.models
import Q
, Max
11 from django
.forms
.models
import inlineformset_factory
, modelformset_factory
13 from project
.dae
import models
as dae
14 from project
.dae
.utils
import \
15 get_employe_from_user
, is_user_dans_services_centraux
16 from project
.rh
import models
as rh
17 from project
.dae
.workflow
import \
18 grp_drh
, POSTE_ETATS_BOUTONS
, DOSSIER_ETAT_FINALISE
, \
22 def _implantation_choices(obj
, request
):
24 employe
= get_employe_from_user(request
.user
)
26 if is_user_dans_services_centraux(request
.user
):
27 q
= Q(**{'id': employe
.implantation_id
})
30 q
= Q(**{'region': employe
.implantation
.region
})
33 if grp_drh
in request
.user
.groups
.all():
35 return [('', '----------')] + \
36 [(i
.id, unicode(i
), )for i
in ref
.Implantation
.objects
.filter(q
)]
39 def _employe_choices(obj
, request
):
41 employe
= get_employe_from_user(request
.user
)
43 if is_user_dans_services_centraux(request
.user
):
44 q_dae_region_service
= Q(poste__implantation
=employe
.implantation
)
45 q_rh_region_service
= Q(poste__implantation
=employe
.implantation
)
48 q_dae_region_service
= Q(
49 poste__implantation__region
=employe
.implantation
.region
51 q_rh_region_service
= Q(
52 poste__implantation__region
=employe
.implantation
.region
55 if grp_drh
in request
.user
.groups
.all():
56 q_dae_region_service
= Q()
57 q_rh_region_service
= Q()
59 # On filtre les employes avec les droits régionaux et on s'assure que
60 # c'est bien le dernier dossier en date pour sortir l'employe. On retient
61 # un employé qui travaille présentement dans la même région que le user
63 dossiers_regionaux_ids
= [
64 d
.id for d
in dae
.Dossier
.objects
.filter(q_dae_region_service
)
68 for d
in dae
.Dossier
.objects
70 .annotate(dernier_dossier
=Max('id'))
71 if d
['dernier_dossier'] in dossiers_regionaux_ids
73 dae_employe
= dae
.Employe
.objects
.filter(id__in
=employes_ids
)
74 dae_
= dae_employe
.filter(id_rh__isnull
=True)
75 copies
= dae_employe
.filter(Q(id_rh__isnull
=False))
76 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
78 dossiers_regionaux_ids
= [
79 d
.id for d
in rh
.Dossier
.objects
.filter(q_rh_region_service
)
83 for d
in rh
.Dossier
.objects
85 .annotate(dernier_dossier
=Max('id'))
86 if d
['dernier_dossier'] in dossiers_regionaux_ids
88 rhv1
= rh
.Employe
.objects \
89 .filter(id__in
=employes_ids
) \
90 .exclude(id__in
=id_copies
)
92 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont
93 # pas de Dossier associés
94 employes_avec_dae
= [d
.employe_id
for d
in dae
.Dossier
.objects
.all()]
95 employes_orphelins
= dae
.Employe
.objects
.exclude(id__in
=employes_avec_dae
)
97 def option_label(employe
):
98 return "%s %s" % (employe
.nom
.upper(), employe
.prenom
.title())
100 return [('', 'Nouvel employé')] + \
102 [('dae-%s' % p
.id, option_label(p
))
103 for p
in dae_ | copies | employes_orphelins
] +
104 [('rh-%s' % p
.id, option_label(p
)) for p
in rhv1
],
109 def label_poste_display(poste
):
110 """Formate un visuel pour un poste dans une liste déroulante"""
113 annee
= poste
.date_debut
.year
114 label
= u
"%s %s - %s [%s]" % (
115 annee
, poste
.type_poste
, poste
.type_poste
.famille_emploi
.nom
,
120 PostePieceForm
= inlineformset_factory(dae
.Poste
, dae
.PostePiece
)
121 DossierPieceForm
= inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)
122 FinancementForm
= inlineformset_factory(
123 dae
.Poste
, dae
.PosteFinancement
, extra
=2
127 class DossierComparaisonForm(forms
.ModelForm
):
129 recherche
= AutoCompleteSelectField('dossiers', required
=False)
130 poste
= forms
.CharField(
131 max_length
=255, widget
=forms
.TextInput(attrs
={'size': '60'})
135 model
= dae
.DossierComparaison
136 exclude
= ('dossier',)
138 DossierComparaisonFormSet
= modelformset_factory(
139 dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
143 class PosteComparaisonForm(forms
.ModelForm
):
145 recherche
= AutoCompleteSelectField('dae_postes', required
=False)
148 model
= dae
.PosteComparaison
151 PosteComparaisonFormSet
= modelformset_factory(
152 dae
.PosteComparaison
, extra
=3, max_num
=3, form
=PosteComparaisonForm
156 class FlexibleRemunForm(forms
.ModelForm
):
158 montant_mensuel
= forms
.DecimalField(required
=False)
159 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
162 model
= dae
.Remuneration
164 def clean_devise(self
):
165 devise
= self
.cleaned_data
['devise']
166 if devise
.code
== 'EUR':
168 implantation
= ref
.Implantation
.objects
.get(
169 id=self
.data
['implantation']
171 liste_taux
= devise
.tauxchange_set
.order_by('-annee')
172 if len(liste_taux
) == 0:
173 raise forms
.ValidationError(
174 u
"La devise %s n'a pas de taux pour l'implantation %s" %
175 (devise
, implantation
)
180 RemunForm
= inlineformset_factory(
181 dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
185 class PosteForm(forms
.ModelForm
):
186 """ Formulaire des postes. """
188 # On ne propose que les services actifs
189 service
= forms
.ModelChoiceField(
190 queryset
=rh
.Service
.objects
.all(), required
=True
193 responsable
= AutoCompleteSelectField('responsables', required
=True)
194 #responsable = forms.ModelChoiceField(
195 # queryset=rh.Poste.objects.select_related(depth=1))
197 # La liste des choix est laissée vide. Voir __init__ pour la raison.
198 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
199 choices
=(), required
=False)
201 valeur_point_min
= forms
.ModelChoiceField(
202 queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False
204 valeur_point_max
= forms
.ModelChoiceField(
205 queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False
210 fields
= ('type_intervention',
211 'poste', 'implantation', 'type_poste', 'service', 'nom',
212 'responsable', 'local', 'expatrie', 'mise_a_disposition',
213 'appel', 'date_debut', 'date_fin',
214 'regime_travail', 'regime_travail_nb_heure_semaine',
215 'classement_min', 'classement_max',
216 'valeur_point_min', 'valeur_point_max',
217 'devise_min', 'devise_max',
218 'salaire_min', 'salaire_max',
219 'indemn_expat_min', 'indemn_expat_max',
220 'indemn_fct_min', 'indemn_fct_max',
221 'charges_patronales_min', 'charges_patronales_max',
222 'autre_min', 'autre_max', 'devise_comparaison',
223 'comp_locale_min', 'comp_locale_max',
224 'comp_universite_min', 'comp_universite_max',
225 'comp_fonctionpub_min', 'comp_fonctionpub_max',
226 'comp_ong_min', 'comp_ong_max',
227 'comp_autre_min', 'comp_autre_max',
230 widgets
= dict(type_intervention
=forms
.RadioSelect(),
231 appel
=forms
.RadioSelect(),
232 nom
=forms
.TextInput(attrs
={'size': 60},),
233 date_debut
=admin_widgets
.AdminDateWidget(),
234 date_fin
=admin_widgets
.AdminDateWidget(),
235 justification
=forms
.Textarea(attrs
={'cols': 80},),
236 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
237 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
240 def __init__(self
, *args
, **kwargs
):
241 """ Mise à jour dynamique du contenu du menu des postes.
243 Si on ne met le menu à jour de cette façon, à chaque instantiation du
244 formulaire, son contenu est mis en cache par le système et il ne
245 reflète pas les changements apportés par les ajouts, modifications,
248 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
249 car le "id" de chaque choix est spécial (voir _poste_choices).
252 request
= kwargs
.pop('request')
253 super(PosteForm
, self
).__init__(*args
, **kwargs
)
254 self
.fields
['poste'].choices
= self
._poste_choices(request
)
255 self
.fields
['implantation'].choices
= \
256 _implantation_choices(self
, request
)
258 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
259 if self
.instance
and self
.instance
.id is None:
260 dossiers
= self
.instance
.get_dossiers()
261 if len(dossiers
) > 0:
262 self
.initial
['service'] = dossiers
[0].poste
.service
264 def _poste_choices(self
, request
):
265 """ Menu déroulant pour les postes.
267 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
268 d'équivalent dans dae.
271 copies
= dae
.Poste
.objects \
272 .ma_region_ou_service(request
.user
) \
273 .exclude(id_rh__isnull
=True) \
274 .filter(etat
=POSTE_ETAT_FINALISE
)
275 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
276 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
) \
277 .exclude(id__in
=id_copies
)
278 # Optimisation de la requête
279 rhv1
= rhv1
.select_related(depth
=1)
281 return [('', 'Nouveau poste')] + \
282 sorted([('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
287 Validation conditionnelles de certains champs.
289 cleaned_data
= self
.cleaned_data
291 if cleaned_data
.get("local") is False \
292 and cleaned_data
.get("expatrie") is False:
293 msg
= "Le poste doit au moins être ouvert localement " \
295 self
._errors
["local"] = self
.error_class([msg
])
296 self
._errors
["expatrie"] = ''
297 raise forms
.ValidationError(msg
)
301 def save(self
, *args
, **kwargs
):
302 kwargs2
= kwargs
.copy()
303 kwargs2
['commit'] = False
304 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
306 if 'commit' not in kwargs
or kwargs
['commit']:
308 poste
.date_creation
= datetime
.datetime
.now()
313 class ChoosePosteForm(forms
.ModelForm
):
318 # La liste des choix est laissée vide. Voir PosteForm.__init__.
319 poste
= forms
.ChoiceField(choices
=(), required
=False)
321 def __init__(self
, request
=None, *args
, **kwargs
):
322 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
323 self
.fields
['poste'].choices
= self
._poste_choices(request
)
325 def _poste_choices(self
, request
):
326 """ Menu déroulant pour les postes. """
327 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
) \
328 .filter(id_rh__isnull
=True)
329 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
) \
330 .exclude(id_rh__isnull
=True)
332 return [('', '----------')] + \
333 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
337 class EmployeForm(forms
.ModelForm
):
338 """ Formulaire des employés. """
341 fields
= ('employe', 'nom', 'prenom', 'genre')
343 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
344 employe
= forms
.ChoiceField(choices
=(), required
=False)
346 def __init__(self
, *args
, **kwargs
):
347 """ Mise à jour dynamique du contenu du menu des employés. """
348 request
= kwargs
.pop('request', None)
349 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
350 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
353 class DossierForm(forms
.ModelForm
):
354 """ Formulaire des dossiers. """
356 exclude
= ('etat', 'employe', 'poste', 'date_debut',)
358 widgets
= dict(statut_residence
=forms
.RadioSelect(),
359 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
360 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
363 def save(self
, *args
, **kwargs
):
364 dossier
= super(DossierForm
, self
).save(*args
, **kwargs
)
365 if dossier
.id is None:
366 dossier
.date_creation
= datetime
.datetime
.now()
373 class PosteWorkflowForm(WorkflowFormMixin
):
374 bouton_libelles
= POSTE_ETATS_BOUTONS
380 def __init__(self
, *args
, **kwargs
):
381 super(PosteWorkflowForm
, self
).__init__(*args
, **kwargs
)
382 self
.fields
['etat'].help_text
= WF_HELP_TEXT
385 class DossierWorkflowForm(WorkflowFormMixin
):
386 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
392 def __init__(self
, *args
, **kwargs
):
393 super(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
394 self
.fields
['etat'].help_text
= WF_HELP_TEXT
395 self
._etat_initial
= self
.instance
.etat
398 super(DossierWorkflowForm
, self
).save()
399 poste
= self
.instance
.poste
400 if poste
.etat
== self
._etat_initial
:
401 poste
.etat
= self
.instance
.etat
405 class ContratForm(forms
.ModelForm
):
408 fields
= ('type_contrat', 'fichier', )
412 class DAENumeriseeForm(forms
.ModelForm
):
416 fields
= ('dae_numerisee',)
419 class DAEFinaliseesSearchForm(forms
.Form
):
421 label
='Recherche', required
=False,
422 widget
=forms
.TextInput(attrs
={'size': 40})
424 importees
= forms
.ChoiceField(
425 label
='Importation', required
=False, choices
=(
427 ('oui', 'DAE importées seulement'),
428 ('non', 'DAE non-importées seulement'),