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