fix #1466
[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 PosteForm(forms.ModelForm):
101 """ Formulaire des postes. """
102
103 responsable=AutoCompleteSelectField('responsables', required=True)
104 #responsable = forms.ModelChoiceField(
105 # queryset=rh.Poste.objects.select_related(depth=1))
106
107 # La liste des choix est laissée vide. Voir __init__ pour la raison.
108 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
109 choices=(), required=False)
110
111 valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
112 valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
113
114
115 class Meta:
116 model = dae.Poste
117 exclude = ('actif', )
118 fields = ('poste', 'implantation', 'type_poste', 'service', 'nom',
119 'responsable', 'local', 'expatrie', 'mise_a_disposition',
120 'appel', 'date_debut', 'date_fin',
121 'regime_travail', 'regime_travail_nb_heure_semaine',
122 'classement_min', 'classement_max',
123 'valeur_point_min', 'valeur_point_max',
124 'devise_min', 'devise_max',
125 'salaire_min', 'salaire_max', 'indemn_min', 'indemn_max',
126 'autre_min', 'autre_max', 'devise_comparaison',
127 'comp_locale_min', 'comp_locale_max',
128 'comp_universite_min', 'comp_universite_max',
129 'comp_fonctionpub_min', 'comp_fonctionpub_max',
130 'comp_ong_min', 'comp_ong_max',
131 'comp_autre_min', 'comp_autre_max',
132 'justification',
133 )
134 widgets = dict(appel=forms.RadioSelect(),
135 nom=forms.TextInput(attrs={'size': 60},),
136 date_debut=admin_widgets.AdminDateWidget(),
137 date_fin=admin_widgets.AdminDateWidget(),
138 justification=forms.Textarea(attrs={'cols': 80},),
139 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
140 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
141 )
142
143 def __init__(self, *args, **kwargs):
144 """ Mise à jour dynamique du contenu du menu des postes.
145
146 Si on ne met le menu à jour de cette façon, à chaque instantiation du
147 formulaire, son contenu est mis en cache par le système et il ne
148 reflète pas les changements apportés par les ajouts, modifications,
149 etc...
150
151 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
152 car le "id" de chaque choix est spécial (voir _poste_choices).
153
154 """
155 request = kwargs.pop('request')
156 super(PosteForm, self).__init__(*args, **kwargs)
157 self.fields['poste'].choices = self._poste_choices(request)
158 self.fields['implantation'].choices = _implantation_choices(self, request)
159
160 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
161 if self.instance and self.instance.id is None:
162 dossiers = self.instance.get_dossiers()
163 if len(dossiers) > 0:
164 self.initial['service'] = dossiers[0].service_id
165 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
166
167
168 def _poste_choices(self, request):
169 """ Menu déroulant pour les postes.
170
171 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
172 d'équivalent dans dae.
173
174 """
175 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
176 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
177 id_copies = [p.id_rh_id for p in copies.all()]
178 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
179 # Optimisation de la requête
180 rhv1 = rhv1.select_related(depth=1)
181
182 return [('', 'Nouveau poste')] + \
183 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
184 [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
185 key=lambda t: t[1])
186
187 def clean(self):
188 """
189 Validation conditionnelles de certains champs.
190 """
191 cleaned_data = self.cleaned_data
192
193 # Gestion de la mise à disposition
194 mise_a_disposition = cleaned_data.get("mise_a_disposition")
195 valeur_point_min = cleaned_data.get("valeur_point_min")
196 valeur_point_max = cleaned_data.get("valeur_point_max")
197 if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
198 msg = u"Ce champ est obligatoire."
199 self._errors["valeur_point_min"] = self.error_class([msg])
200 self._errors["valeur_point_max"] = self.error_class([msg])
201 raise forms.ValidationError("Les valeurs de point sont vides")
202
203 print cleaned_data.get("local")
204 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
205 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
206 self._errors["local"] = self.error_class([msg])
207 self._errors["expatrie"] = ''
208 raise forms.ValidationError(msg)
209
210
211 return cleaned_data
212
213
214
215 def save(self, *args, **kwargs):
216 kwargs2 = kwargs.copy()
217 kwargs2['commit'] = False
218 poste = super(PosteForm, self).save(*args, **kwargs2)
219 # id_rh
220 if 'commit' not in kwargs or kwargs['commit']:
221 poste.save()
222 return poste
223
224
225 class ChoosePosteForm(forms.ModelForm):
226 class Meta:
227 model = dae.Poste
228 fields = ('poste',)
229
230 # La liste des choix est laissée vide. Voir PosteForm.__init__.
231 poste = forms.ChoiceField(choices=(), required=False)
232
233 def __init__(self, request=None, *args, **kwargs):
234 super(ChoosePosteForm, self).__init__(*args, **kwargs)
235 self.fields['poste'].choices = self._poste_choices(request)
236
237 def _poste_choices(self, request):
238 """ Menu déroulant pour les postes. """
239 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
240 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
241 id_copies = [p.id_rh_id for p in copies.all()]
242
243 return [('', '----------')] + \
244 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
245 key=lambda t: t[1])
246
247
248 class EmployeForm(forms.ModelForm):
249 """ Formulaire des employés. """
250 class Meta:
251 model = dae.Employe
252 fields = ('employe', 'nom', 'prenom', 'genre')
253
254 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
255 employe = forms.ChoiceField(choices=(), required=False)
256
257 def __init__(self, *args, **kwargs):
258 """ Mise à jour dynamique du contenu du menu des employés. """
259 request = kwargs.pop('request', None)
260 super(EmployeForm, self).__init__(*args, **kwargs)
261 self.fields['employe'].choices = _employe_choices(self, request)
262
263
264
265 class DossierForm(forms.ModelForm):
266 """ Formulaire des dossiers. """
267 class Meta:
268 exclude= ('etat', )
269 model = dae.Dossier
270 widgets = dict(statut_residence=forms.RadioSelect(),
271 contrat_date_debut=admin_widgets.AdminDateWidget(),
272 contrat_date_fin=admin_widgets.AdminDateWidget(),
273 )
274
275 WF_HELP_TEXT = """Ce champs affiche par défaut l'étape de traitement de la demande.
276 La liste déroulante vous permet de sélectionner l'étape suivante."""
277 WF_HELP_TEXT = ""
278
279 class PosteWorkflowForm(WorkflowFormMixin):
280
281 class Meta:
282 fields = ('etat', )
283 model = dae.Poste
284
285 def __init__(self, *args, **kwargs):
286 super(self.__class__, self).__init__(*args, **kwargs)
287 self.fields['etat'].help_text = WF_HELP_TEXT
288
289
290 class DossierWorkflowForm(WorkflowFormMixin):
291
292 class Meta:
293 fields = ('etat', )
294 model = dae.Dossier
295
296 def __init__(self, *args, **kwargs):
297 super(self.__class__, self).__init__(*args, **kwargs)
298 self.fields['etat'].help_text = WF_HELP_TEXT
299