1 # -*- encoding: utf-8 -*-
3 from django
.db
.models
import Q
, Max
4 from django
import forms
5 from django
.forms
.models
import inlineformset_factory
, modelformset_factory
6 from django
.contrib
.admin
import widgets
as admin_widgets
7 from ajax_select
.fields
import AutoCompleteSelectField
8 from auf
.django
.workflow
.forms
import WorkflowFormMixin
9 from auf
.django
.references
import models
as ref
10 from dae
import models
as dae
11 from utils
import get_employe_from_user
, is_user_dans_services_centraux
12 from rh_v1
import models
as rh
13 from workflow
import grp_drh
, POSTE_ETATS_BOUTONS
15 def _implantation_choices(obj
, request
):
17 employe
= get_employe_from_user(request
.user
)
19 if is_user_dans_services_centraux(request
.user
):
20 q
= Q(**{ 'id' : employe
.implantation_id
})
23 q
= Q(**{ 'region' : employe
.implantation
.region
})
26 if grp_drh
in request
.user
.groups
.all():
28 return [('', '----------')] + [(i
.id, unicode(i
), )for i
in ref
.Implantation
.objects
.filter(q
)]
30 def _employe_choices(obj
, request
):
32 employe
= get_employe_from_user(request
.user
)
34 if is_user_dans_services_centraux(request
.user
):
35 q_dae_region_service
= Q(poste__implantation
=employe
.implantation
)
36 q_rh_region_service
= Q(implantation1
=employe
.implantation
) |
Q(implantation2
=employe
.implantation
)
39 q_dae_region_service
= Q(poste__implantation__region
=employe
.implantation
.region
)
40 q_rh_region_service
= Q(implantation1__region
=employe
.implantation
.region
) |
Q(implantation2__region
=employe
.implantation
.region
)
42 if grp_drh
in request
.user
.groups
.all():
43 q_dae_region_service
= Q()
44 q_rh_region_service
= Q()
46 # On filtre les employes avec les droits régionaux et on s'assure que c'est bien le dernier dossier en date pour sortir l'employe
47 # On retient un employé qui travaille présentement dans la même région que le user connecté.
48 dossiers_regionaux_ids
= [d
.id for d
in dae
.Dossier
.objects
.filter(q_dae_region_service
)]
49 employes_ids
= [d
['employe'] for d
in dae
.Dossier
.objects
.values('employe').annotate(dernier_dossier
=Max('id')) if d
['dernier_dossier'] in dossiers_regionaux_ids
]
50 dae_employe
= dae
.Employe
.objects
.filter(id__in
=employes_ids
)
51 dae_
= dae_employe
.filter(id_rh__isnull
=True)
52 copies
= dae_employe
.filter(Q(id_rh__isnull
=False))
53 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
55 dossiers_regionaux_ids
= [d
.id for d
in rh
.Dossier
.objects
.filter(q_rh_region_service
)]
56 employes_ids
= [d
['employe'] for d
in rh
.Dossier
.objects
.values('employe').annotate(dernier_dossier
=Max('id')) if d
['dernier_dossier'] in dossiers_regionaux_ids
]
57 rhv1
= rh
.Employe
.objects
.filter(id__in
=employes_ids
).exclude(id__in
=id_copies
)
59 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont pas de Dossier associés
60 employes_avec_dae
= [d
.employe_id
for d
in dae
.Dossier
.objects
.all()]
61 employes_orphelins
= dae
.Employe
.objects
.exclude(id__in
=employes_avec_dae
)
64 def option_label(employe
):
65 return "%s %s" % (employe
.nom
.upper(), employe
.prenom
.title())
67 return [('', 'Nouvel employé')] + \
68 sorted([('dae-%s' % p
.id, option_label(p
)) for p
in dae_ | copies | employes_orphelins
] +
69 [('rh-%s' % p
.id, option_label(p
)) for p
in rhv1
],
72 def label_poste_display(poste
):
73 """Formate un visuel pour un poste dans une liste déroulante"""
74 label
= u
"%s - %s [%s]" %(poste
.type_poste
, poste
.type_poste
.famille_emploi
.nom
, poste
.id)
77 PostePieceForm
= inlineformset_factory(dae
.Poste
, dae
.PostePiece
)
78 DossierPieceForm
= inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)
79 FinancementForm
= inlineformset_factory(dae
.Poste
, dae
.PosteFinancement
, extra
=2)
81 class DossierComparaisonForm(forms
.ModelForm
):
83 recherche
= AutoCompleteSelectField('dossiers', required
=False)
84 poste
= forms
.CharField(max_length
=255, widget
=forms
.TextInput(attrs
={'size':'60'}))
87 model
= dae
.DossierComparaison
88 exclude
= ('dossier',)
90 DossierComparaisonFormSet
= modelformset_factory(
91 dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
94 class PosteComparaisonForm(forms
.ModelForm
):
96 recherche
= AutoCompleteSelectField('postes', required
=False)
99 model
= dae
.PosteComparaison
102 PosteComparaisonFormSet
= modelformset_factory(
103 dae
.PosteComparaison
, extra
=3, max_num
=3, form
=PosteComparaisonForm
106 class FlexibleRemunForm(forms
.ModelForm
):
108 montant_mensuel
= forms
.DecimalField(required
=False)
109 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
112 model
= dae
.Remuneration
114 def clean_devise(self
):
115 devise
= self
.cleaned_data
['devise']
116 if devise
.code
== 'EUR':
118 implantation
= ref
.Implantation
.objects
.get(id=self
.data
['implantation'])
119 liste_taux
= devise
.tauxchange_set
.order_by('-annee')
120 if len(liste_taux
) == 0:
121 raise forms
.ValidationError(u
"La devise %s n'a pas de taux pour l'implantation %s" % (devise
, implantation
))
125 RemunForm
= inlineformset_factory(
126 dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
129 class PosteForm(forms
.ModelForm
):
130 """ Formulaire des postes. """
132 # On ne propose que les services actifs
133 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(actif
=True), required
=True)
135 responsable
=AutoCompleteSelectField('responsables', required
=True)
136 #responsable = forms.ModelChoiceField(
137 # queryset=rh.Poste.objects.select_related(depth=1))
139 # La liste des choix est laissée vide. Voir __init__ pour la raison.
140 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
141 choices
=(), required
=False)
143 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
144 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
149 exclude
= ('actif', )
150 fields
= ('type_intervention',
151 'poste', 'implantation', 'type_poste', 'service', 'nom',
152 'responsable', 'local', 'expatrie', 'mise_a_disposition',
153 'appel', 'date_debut', 'date_fin',
154 'regime_travail', 'regime_travail_nb_heure_semaine',
155 'classement_min', 'classement_max',
156 'valeur_point_min', 'valeur_point_max',
157 'devise_min', 'devise_max',
158 'salaire_min', 'salaire_max',
159 'indemn_expat_min', 'indemn_expat_max',
160 'indemn_fct_min', 'indemn_fct_max',
161 'charges_patronales_min', 'charges_patronales_max',
162 'autre_min', 'autre_max', 'devise_comparaison',
163 'comp_locale_min', 'comp_locale_max',
164 'comp_universite_min', 'comp_universite_max',
165 'comp_fonctionpub_min', 'comp_fonctionpub_max',
166 'comp_ong_min', 'comp_ong_max',
167 'comp_autre_min', 'comp_autre_max',
170 widgets
= dict(type_intervention
=forms
.RadioSelect(),
171 appel
=forms
.RadioSelect(),
172 nom
=forms
.TextInput(attrs
={'size': 60},),
173 date_debut
=admin_widgets
.AdminDateWidget(),
174 date_fin
=admin_widgets
.AdminDateWidget(),
175 justification
=forms
.Textarea(attrs
={'cols': 80},),
176 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
177 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
180 def __init__(self
, *args
, **kwargs
):
181 """ Mise à jour dynamique du contenu du menu des postes.
183 Si on ne met le menu à jour de cette façon, à chaque instantiation du
184 formulaire, son contenu est mis en cache par le système et il ne
185 reflète pas les changements apportés par les ajouts, modifications,
188 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
189 car le "id" de chaque choix est spécial (voir _poste_choices).
192 request
= kwargs
.pop('request')
193 super(PosteForm
, self
).__init__(*args
, **kwargs
)
194 self
.fields
['poste'].choices
= self
._poste_choices(request
)
195 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
197 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
198 if self
.instance
and self
.instance
.id is None:
199 dossiers
= self
.instance
.get_dossiers()
200 if len(dossiers
) > 0:
201 self
.initial
['service'] = dossiers
[0].service_id
202 self
.initial
['nom'] = "%s %s" % (self
.initial
['nom'], self
.instance
.get_complement_nom())
205 def _poste_choices(self
, request
):
206 """ Menu déroulant pour les postes.
208 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
209 d'équivalent dans dae.
212 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True, id_rh__isnull
=True)
213 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
214 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
215 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True).exclude(id__in
=id_copies
)
216 # Optimisation de la requête
217 rhv1
= rhv1
.select_related(depth
=1)
219 return [('', 'Nouveau poste')] + \
220 sorted([('dae-%s' % p
.id, label_poste_display(p
)) for p
in dae_ | copies
] +
221 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
226 Validation conditionnelles de certains champs.
228 cleaned_data
= self
.cleaned_data
230 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
231 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
232 self
._errors
["local"] = self
.error_class([msg
])
233 self
._errors
["expatrie"] = ''
234 raise forms
.ValidationError(msg
)
240 def save(self
, *args
, **kwargs
):
241 kwargs2
= kwargs
.copy()
242 kwargs2
['commit'] = False
243 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
245 if 'commit' not in kwargs
or kwargs
['commit']:
250 class ChoosePosteForm(forms
.ModelForm
):
255 # La liste des choix est laissée vide. Voir PosteForm.__init__.
256 poste
= forms
.ChoiceField(choices
=(), required
=False)
258 def __init__(self
, request
=None, *args
, **kwargs
):
259 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
260 self
.fields
['poste'].choices
= self
._poste_choices(request
)
262 def _poste_choices(self
, request
):
263 """ Menu déroulant pour les postes. """
264 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
265 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
266 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
268 return [('', '----------')] + \
269 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
273 class EmployeForm(forms
.ModelForm
):
274 """ Formulaire des employés. """
277 fields
= ('employe', 'nom', 'prenom', 'genre')
279 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
280 employe
= forms
.ChoiceField(choices
=(), required
=False)
282 def __init__(self
, *args
, **kwargs
):
283 """ Mise à jour dynamique du contenu du menu des employés. """
284 request
= kwargs
.pop('request', None)
285 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
286 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
289 class DossierForm(forms
.ModelForm
):
290 """ Formulaire des dossiers. """
294 widgets
= dict(statut_residence
=forms
.RadioSelect(),
295 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
296 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
301 class PosteWorkflowForm(WorkflowFormMixin
):
302 bouton_libelles
= POSTE_ETATS_BOUTONS
307 def __init__(self
, *args
, **kwargs
):
308 super(PosteWorkflowForm
, self
).__init__(*args
, **kwargs
)
309 self
.fields
['etat'].help_text
= WF_HELP_TEXT
312 class DossierWorkflowForm(WorkflowFormMixin
):
313 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
318 def __init__(self
, *args
, **kwargs
):
319 super(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
320 self
.fields
['etat'].help_text
= WF_HELP_TEXT
321 self
._etat_initial
= self
.instance
.etat
324 super(DossierWorkflowForm
, self
).save()
325 poste
= self
.instance
.poste
326 if poste
.etat
== self
._etat_initial
:
327 poste
.etat
= self
.instance
.etat
330 class ContratForm(forms
.ModelForm
):
333 fields
= ('type', 'fichier')
336 class DAENumeriseeForm(forms
.ModelForm
):
340 fields
= ('dae_numerisee',)