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