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