fix #1460
[auf_rh_dae.git] / project / dae / forms.py
1 # -*- encoding: utf-8 -*-
2
3 from django.db.models import Q
4 from django import forms
5 from django.forms.models import inlineformset_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 datamaster_modeles import models as ref
10 from dae import models as dae
11 from utils import get_employe_from_user, is_user_dans_service
12 from rh_v1 import models as rh
13 from workflow import grp_drh
14
15 def _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
30 def _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
62
63 def 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
67
68 class PostePieceForm(inlineformset_factory(dae.Poste, dae.PostePiece)):
69 pass
70
71 class DossierPieceForm(inlineformset_factory(dae.Dossier, dae.DossierPiece)):
72 pass
73
74 class FinancementForm(inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)):
75 pass
76
77
78 class 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
86 class DossierComparaisonForm(inlineformset_factory(dae.Dossier, dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm)):
87 pass
88
89 class 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
97 class RemunForm(inlineformset_factory(dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm)):
98 pass
99
100 class 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:
112 q_ids = [j.question.id for j in instance.justificationnouvelemploye_set.filter(dossier=instance)]
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
122 class 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:
134 q_ids = [j.question.id for j in instance.justificationautreemploye_set.filter(dossier=instance)]
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)
143
144 class PosteForm(forms.ModelForm):
145 """ Formulaire des postes. """
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)
157
158
159 class Meta:
160 model = dae.Poste
161 exclude = ('actif', )
162 fields = ('poste', 'implantation', 'type_poste', 'service', 'nom',
163 'responsable', 'local', 'expatrie', 'mise_a_disposition',
164 'appel', 'date_debut', 'date_fin',
165 'regime_travail', 'regime_travail_nb_heure_semaine',
166 'classement_min', 'classement_max',
167 'valeur_point_min', 'valeur_point_max',
168 'devise_min', 'devise_max',
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',
175 'comp_autre_min', 'comp_autre_max',
176 'justification',
177 )
178 widgets = dict(appel=forms.RadioSelect(),
179 nom=forms.TextInput(attrs={'size': 60},),
180 date_debut=admin_widgets.AdminDateWidget(),
181 date_fin=admin_widgets.AdminDateWidget(),
182 justification=forms.Textarea(attrs={'cols': 80},),
183 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
184 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
185 )
186
187 def __init__(self, *args, **kwargs):
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
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
198 """
199 request = kwargs.pop('request')
200 super(PosteForm, self).__init__(*args, **kwargs)
201 self.fields['poste'].choices = self._poste_choices(request)
202 self.fields['implantation'].choices = _implantation_choices(self, request)
203
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
209 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
210
211
212 def _poste_choices(self, request):
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 """
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)
221 id_copies = [p.id_rh_id for p in copies.all()]
222 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
223 # Optimisation de la requête
224 rhv1 = rhv1.select_related(depth=1)
225
226 return [('', 'Nouveau poste')] + \
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],
229 key=lambda t: t[1])
230
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")
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
255 return cleaned_data
256
257
258
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
268
269 class 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
277 def __init__(self, request=None, *args, **kwargs):
278 super(ChoosePosteForm, self).__init__(*args, **kwargs)
279 self.fields['poste'].choices = self._poste_choices(request)
280
281 def _poste_choices(self, request):
282 """ Menu déroulant pour les postes. """
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)
285 id_copies = [p.id_rh_id for p in copies.all()]
286
287 return [('', '----------')] + \
288 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
289 key=lambda t: t[1])
290
291
292 class 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
301 def __init__(self, *args, **kwargs):
302 """ Mise à jour dynamique du contenu du menu des employés. """
303 request = kwargs.pop('request', None)
304 super(EmployeForm, self).__init__(*args, **kwargs)
305 self.fields['employe'].choices = _employe_choices(self, request)
306
307
308
309 class DossierForm(forms.ModelForm):
310 """ Formulaire des dossiers. """
311 class Meta:
312 exclude= ('etat', )
313 model = dae.Dossier
314 widgets = dict(statut_residence=forms.RadioSelect(),
315 contrat_date_debut=admin_widgets.AdminDateWidget(),
316 contrat_date_fin=admin_widgets.AdminDateWidget(),
317 )
318
319 WF_HELP_TEXT = """Ce champs affiche par défaut l'étape de traitement de la demande.
320 La liste déroulante vous permet de sélectionner l'étape suivante."""
321 WF_HELP_TEXT = ""
322
323 class PosteWorkflowForm(WorkflowFormMixin):
324
325 class Meta:
326 fields = ('etat', )
327 model = dae.Poste
328
329 def __init__(self, *args, **kwargs):
330 super(self.__class__, self).__init__(*args, **kwargs)
331 self.fields['etat'].help_text = WF_HELP_TEXT
332
333
334 class DossierWorkflowForm(WorkflowFormMixin):
335
336 class Meta:
337 fields = ('etat', )
338 model = dae.Dossier
339
340 def __init__(self, *args, **kwargs):
341 super(self.__class__, self).__init__(*args, **kwargs)
342 self.fields['etat'].help_text = WF_HELP_TEXT
343