Accès à page perso via actions en haut à droite
[auf_rh_dae.git] / project / dae / forms.py
CommitLineData
5d680e84 1# -*- encoding: utf-8 -*-
ce110fb9 2
072820fc 3from django.db.models import Q, Max
5d680e84 4from django import forms
36341125 5from django.forms.models import inlineformset_factory
e88caaf0 6from django.contrib.admin import widgets as admin_widgets
afc204bf 7from ajax_select.fields import AutoCompleteSelectField
8fa94e8b 8from auf.django.workflow.forms import WorkflowFormMixin
5d680e84 9from datamaster_modeles import models as ref
5d680e84 10from dae import models as dae
d8cfc3d5 11from utils import get_employe_from_user, is_user_dans_services_centraux
5d680e84 12from rh_v1 import models as rh
56589624 13from workflow import grp_drh, POSTE_ETATS_BOUTONS
f258e4e7
OL
14
15def _implantation_choices(obj, request):
16 # TRAITEMENT NORMAL
17 employe = get_employe_from_user(request.user)
18 # SERVICE
d8cfc3d5 19 if is_user_dans_services_centraux(request.user):
f258e4e7
OL
20 q = Q(**{ 'id' : employe.implantation_id })
21 # REGION
22 else:
23 q = Q(**{ 'region' : employe.implantation.region })
24
25 # TRAITEMENT DRH
26 if grp_drh in request.user.groups.all():
27 q = Q()
28 return [('', '----------')] + [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
29
30def _employe_choices(obj, request):
f258e4e7
OL
31 # TRAITEMENT NORMAL
32 employe = get_employe_from_user(request.user)
33 # SERVICE
d8cfc3d5 34 if is_user_dans_services_centraux(request.user):
072820fc
OL
35 q_dae_region_service = Q(poste__implantation=employe.implantation)
36 q_rh_region_service = Q(implantation1=employe.implantation) | Q(implantation2=employe.implantation)
f258e4e7
OL
37 # REGION
38 else:
072820fc
OL
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)
f258e4e7
OL
41 # TRAITEMENT DRH
42 if grp_drh in request.user.groups.all():
072820fc
OL
43 q_dae_region_service = Q()
44 q_rh_region_service = Q()
f258e4e7 45
072820fc
OL
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))
f258e4e7 53 id_copies = [p.id_rh_id for p in copies.all()]
072820fc
OL
54
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]
f258e4e7
OL
57 rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies)
58
59 def option_label(employe):
60 return "%s %s" % (employe.nom.upper(), employe.prenom.title())
61
62 return [('', 'Nouvel employé')] + \
63 sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies] +
64 [('rh-%s' % p.id, option_label(p)) for p in rhv1],
65 key=lambda t: t[1])
66
5d680e84 67
4bce4d24
OL
68def label_poste_display(poste):
69 """Formate un visuel pour un poste dans une liste déroulante"""
70 label = u"%s - %s [%s]" %(poste.type_poste, poste.type_poste.famille_emploi.nom, poste.id)
71 return label
9cb4de55
OL
72
73class PostePieceForm(inlineformset_factory(dae.Poste, dae.PostePiece)):
74 pass
75
76class DossierPieceForm(inlineformset_factory(dae.Dossier, dae.DossierPiece)):
77 pass
78
e2185d45 79class FinancementForm(inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)):
151e7bd0
OL
80 pass
81
03b395db
OL
82
83class DossierComparaisonForm(forms.ModelForm):
84
85 recherche = AutoCompleteSelectField('dossiers', required=False)
86 poste = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':'60'}))
87
88 class Model:
89 model = dae.DossierComparaison
90
03b395db
OL
91class DossierComparaisonForm(inlineformset_factory(dae.Dossier, dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm)):
92 pass
93
068d1462
OL
94class PosteComparaisonForm(forms.ModelForm):
95
96 recherche = AutoCompleteSelectField('postes', required=False)
97
98 class Model:
99 model = dae.PosteComparaison
100
101class PosteComparaisonForm(inlineformset_factory(dae.Poste, dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm)):
102 pass
103
0a085c42
OL
104class FlexibleRemunForm(forms.ModelForm):
105
106 montant_mensuel = forms.DecimalField(required=False)
107 montant = forms.DecimalField(required=True, label='Montant annuel')
108
109 class Meta:
110 model = dae.Remuneration
111
dc4b78a7
OL
112 def clean_devise(self):
113 devise = self.cleaned_data['devise']
114 liste_taux = devise.tauxchange_set.order_by('-annee').filter(implantation=self.instance.dossier.poste.implantation)
115 if len(liste_taux) == 0:
116 raise forms.ValidationError(u"La devise %s n'a pas de taux pour l'implantation %s" % (devise, self.instance.dossier.poste.implantation))
117 else:
118 return devise
119
120
0a085c42
OL
121class RemunForm(inlineformset_factory(dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm)):
122 pass
123
1b217058 124class PosteForm(forms.ModelForm):
5d680e84 125 """ Formulaire des postes. """
12c7f8a7 126
ea7adc69 127 # On ne propose que les services actifs
8899db43 128 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(actif=True), required=True)
ea7adc69 129
12c7f8a7
OL
130 responsable=AutoCompleteSelectField('responsables', required=True)
131 #responsable = forms.ModelChoiceField(
132 # queryset=rh.Poste.objects.select_related(depth=1))
133
134 # La liste des choix est laissée vide. Voir __init__ pour la raison.
135 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
136 choices=(), required=False)
ea7adc69 137
12c7f8a7
OL
138 valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
139 valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
3121c13c
OL
140
141
5d680e84
NC
142 class Meta:
143 model = dae.Poste
b15bf543 144 exclude = ('actif', )
c3be904d
OL
145 fields = ('type_intervention',
146 'poste', 'implantation', 'type_poste', 'service', 'nom',
154677c3 147 'responsable', 'local', 'expatrie', 'mise_a_disposition',
b15bf543 148 'appel', 'date_debut', 'date_fin',
5d680e84
NC
149 'regime_travail', 'regime_travail_nb_heure_semaine',
150 'classement_min', 'classement_max',
151 'valeur_point_min', 'valeur_point_max',
3d627bfd 152 'devise_min', 'devise_max',
5f61bccb
OL
153 'salaire_min', 'salaire_max',
154 'indemn_expat_min', 'indemn_expat_max',
155 'indemn_fct_min', 'indemn_fct_max',
156 'charges_patronales_min', 'charges_patronales_max',
5d680e84
NC
157 'autre_min', 'autre_max', 'devise_comparaison',
158 'comp_locale_min', 'comp_locale_max',
159 'comp_universite_min', 'comp_universite_max',
160 'comp_fonctionpub_min', 'comp_fonctionpub_max',
161 'comp_ong_min', 'comp_ong_max',
8fa94e8b 162 'comp_autre_min', 'comp_autre_max',
2e092e0c 163 'justification',
8fa94e8b 164 )
c3be904d
OL
165 widgets = dict(type_intervention=forms.RadioSelect(),
166 appel=forms.RadioSelect(),
3d627bfd 167 nom=forms.TextInput(attrs={'size': 60},),
e88caaf0
OL
168 date_debut=admin_widgets.AdminDateWidget(),
169 date_fin=admin_widgets.AdminDateWidget(),
2e092e0c 170 justification=forms.Textarea(attrs={'cols': 80},),
3d627bfd 171 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
172 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
173 )
5d680e84 174
c2458db6 175 def __init__(self, *args, **kwargs):
5d680e84
NC
176 """ Mise à jour dynamique du contenu du menu des postes.
177
178 Si on ne met le menu à jour de cette façon, à chaque instantiation du
179 formulaire, son contenu est mis en cache par le système et il ne
180 reflète pas les changements apportés par les ajouts, modifications,
181 etc...
182
139686f2
NC
183 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
184 car le "id" de chaque choix est spécial (voir _poste_choices).
185
5d680e84 186 """
c2458db6 187 request = kwargs.pop('request')
5d680e84 188 super(PosteForm, self).__init__(*args, **kwargs)
f258e4e7
OL
189 self.fields['poste'].choices = self._poste_choices(request)
190 self.fields['implantation'].choices = _implantation_choices(self, request)
5d680e84 191
cc3098d0
OL
192 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
193 if self.instance and self.instance.id is None:
194 dossiers = self.instance.get_dossiers()
195 if len(dossiers) > 0:
196 self.initial['service'] = dossiers[0].service_id
9508a5b8
OL
197 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
198
cc3098d0 199
f258e4e7 200 def _poste_choices(self, request):
5d680e84
NC
201 """ Menu déroulant pour les postes.
202
203 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
204 d'équivalent dans dae.
205
206 """
f258e4e7
OL
207 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
208 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
5d680e84 209 id_copies = [p.id_rh_id for p in copies.all()]
f258e4e7 210 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
139686f2
NC
211 # Optimisation de la requête
212 rhv1 = rhv1.select_related(depth=1)
5d680e84 213
98d51b59 214 return [('', 'Nouveau poste')] + \
4bce4d24
OL
215 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
216 [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
5d680e84 217 key=lambda t: t[1])
3ed49093 218
4dd75e7b
OL
219 def clean(self):
220 """
221 Validation conditionnelles de certains champs.
222 """
223 cleaned_data = self.cleaned_data
224
225 # Gestion de la mise à disposition
226 mise_a_disposition = cleaned_data.get("mise_a_disposition")
227 valeur_point_min = cleaned_data.get("valeur_point_min")
228 valeur_point_max = cleaned_data.get("valeur_point_max")
229 if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
230 msg = u"Ce champ est obligatoire."
231 self._errors["valeur_point_min"] = self.error_class([msg])
232 self._errors["valeur_point_max"] = self.error_class([msg])
233 raise forms.ValidationError("Les valeurs de point sont vides")
f42c6e20 234
f42c6e20
OL
235 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
236 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
237 self._errors["local"] = self.error_class([msg])
238 self._errors["expatrie"] = ''
239 raise forms.ValidationError(msg)
240
241
4dd75e7b
OL
242 return cleaned_data
243
244
245
494ff2be
NC
246 def save(self, *args, **kwargs):
247 kwargs2 = kwargs.copy()
248 kwargs2['commit'] = False
249 poste = super(PosteForm, self).save(*args, **kwargs2)
250 # id_rh
251 if 'commit' not in kwargs or kwargs['commit']:
252 poste.save()
253 return poste
254
3ed49093 255
139686f2
NC
256class ChoosePosteForm(forms.ModelForm):
257 class Meta:
258 model = dae.Poste
259 fields = ('poste',)
260
261 # La liste des choix est laissée vide. Voir PosteForm.__init__.
262 poste = forms.ChoiceField(choices=(), required=False)
263
4ee6d70a 264 def __init__(self, request=None, *args, **kwargs):
139686f2 265 super(ChoosePosteForm, self).__init__(*args, **kwargs)
4ee6d70a 266 self.fields['poste'].choices = self._poste_choices(request)
139686f2 267
4ee6d70a 268 def _poste_choices(self, request):
139686f2 269 """ Menu déroulant pour les postes. """
4ee6d70a
OL
270 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
271 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
139686f2
NC
272 id_copies = [p.id_rh_id for p in copies.all()]
273
98d51b59 274 return [('', '----------')] + \
139686f2
NC
275 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
276 key=lambda t: t[1])
277
278
139686f2
NC
279class EmployeForm(forms.ModelForm):
280 """ Formulaire des employés. """
281 class Meta:
282 model = dae.Employe
283 fields = ('employe', 'nom', 'prenom', 'genre')
284
285 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
286 employe = forms.ChoiceField(choices=(), required=False)
287
ac6235f6 288 def __init__(self, *args, **kwargs):
139686f2 289 """ Mise à jour dynamique du contenu du menu des employés. """
ac6235f6 290 request = kwargs.pop('request', None)
139686f2 291 super(EmployeForm, self).__init__(*args, **kwargs)
f258e4e7 292 self.fields['employe'].choices = _employe_choices(self, request)
139686f2 293
139686f2
NC
294
295
296class DossierForm(forms.ModelForm):
297 """ Formulaire des dossiers. """
298 class Meta:
b1baa306 299 exclude= ('etat', )
139686f2 300 model = dae.Dossier
4d25e2ba 301 widgets = dict(statut_residence=forms.RadioSelect(),
0e0aeb7e
OL
302 contrat_date_debut=admin_widgets.AdminDateWidget(),
303 contrat_date_fin=admin_widgets.AdminDateWidget(),
4d25e2ba 304 )
e6f52402 305
3799cafc 306WF_HELP_TEXT = ""
e0b93e3a 307
e6f52402 308class PosteWorkflowForm(WorkflowFormMixin):
56589624 309 bouton_libelles = POSTE_ETATS_BOUTONS
e6f52402
OL
310 class Meta:
311 fields = ('etat', )
312 model = dae.Poste
56589624 313
e0b93e3a
OL
314 def __init__(self, *args, **kwargs):
315 super(self.__class__, self).__init__(*args, **kwargs)
316 self.fields['etat'].help_text = WF_HELP_TEXT
317
318
e6f52402 319class DossierWorkflowForm(WorkflowFormMixin):
56589624 320 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
e6f52402
OL
321 class Meta:
322 fields = ('etat', )
323 model = dae.Dossier
e0b93e3a
OL
324
325 def __init__(self, *args, **kwargs):
326 super(self.__class__, self).__init__(*args, **kwargs)
327 self.fields['etat'].help_text = WF_HELP_TEXT
328