#1703
[auf_rh_dae.git] / project / dae / forms.py
1 # -*- encoding: utf-8 -*-
2
3 from django.db.models import Q, Max
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 # TRAITEMENT NORMAL
32 employe = get_employe_from_user(request.user)
33 # SERVICE
34 if is_user_dans_services_centraux(request.user):
35 q_dae_region_service = Q(poste__implantation=employe.implantation)
36 q_rh_region_service = Q(implantation1=employe.implantation) | Q(implantation2=employe.implantation)
37 # REGION
38 else:
39 q_dae_region_service = Q(poste__implantation__region=employe.implantation.region)
40 q_rh_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_dae_region_service = Q()
44 q_rh_region_service = Q()
45
46 # On filtre les employes avec les droits régionaux et on s'assure que c'est bien le dernier dossier en date pour sortir l'employe
47 # On retient un employé qui travaille présentement dans la même région que le user connecté.
48 dossiers_regionaux_ids = [d.id for d in dae.Dossier.objects.filter(q_dae_region_service)]
49 employes_ids = [d['employe'] for d in dae.Dossier.objects.values('employe').annotate(dernier_dossier=Max('id')) if d['dernier_dossier'] in dossiers_regionaux_ids]
50 dae_employe = dae.Employe.objects.filter(id__in=employes_ids)
51 dae_ = dae_employe.filter(id_rh__isnull=True)
52 copies = dae_employe.filter(Q(id_rh__isnull=False))
53 id_copies = [p.id_rh_id for p in copies.all()]
54
55 dossiers_regionaux_ids = [d.id for d in rh.Dossier.objects.filter(q_rh_region_service)]
56 employes_ids = [d['employe'] for d in rh.Dossier.objects.values('employe').annotate(dernier_dossier=Max('id')) if d['dernier_dossier'] in dossiers_regionaux_ids]
57 rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies)
58
59 def option_label(employe):
60 return "%s %s" % (employe.nom.upper(), employe.prenom.title())
61
62 return [('', 'Nouvel employé')] + \
63 sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies] +
64 [('rh-%s' % p.id, option_label(p)) for p in rhv1],
65 key=lambda t: t[1])
66
67
68 def label_poste_display(poste):
69 """Formate un visuel pour un poste dans une liste déroulante"""
70 label = u"%s - %s [%s]" %(poste.type_poste, poste.type_poste.famille_emploi.nom, poste.id)
71 return label
72
73 class PostePieceForm(inlineformset_factory(dae.Poste, dae.PostePiece)):
74 pass
75
76 class DossierPieceForm(inlineformset_factory(dae.Dossier, dae.DossierPiece)):
77 pass
78
79 class FinancementForm(inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)):
80 pass
81
82
83 class DossierComparaisonForm(forms.ModelForm):
84
85 recherche = AutoCompleteSelectField('dossiers', required=False)
86 poste = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':'60'}))
87
88 class Model:
89 model = dae.DossierComparaison
90
91 class DossierComparaisonForm(inlineformset_factory(dae.Dossier, dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm)):
92 pass
93
94 class PosteComparaisonForm(forms.ModelForm):
95
96 recherche = AutoCompleteSelectField('postes', required=False)
97
98 class Model:
99 model = dae.PosteComparaison
100
101 class PosteComparaisonForm(inlineformset_factory(dae.Poste, dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm)):
102 pass
103
104 class FlexibleRemunForm(forms.ModelForm):
105
106 montant_mensuel = forms.DecimalField(required=False)
107 montant = forms.DecimalField(required=True, label='Montant annuel')
108
109 class Meta:
110 model = dae.Remuneration
111
112 class RemunForm(inlineformset_factory(dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm)):
113 pass
114
115 class PosteForm(forms.ModelForm):
116 """ Formulaire des postes. """
117
118 # On ne propose que les services actifs
119 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(actif=True), required=True)
120
121 responsable=AutoCompleteSelectField('responsables', required=True)
122 #responsable = forms.ModelChoiceField(
123 # queryset=rh.Poste.objects.select_related(depth=1))
124
125 # La liste des choix est laissée vide. Voir __init__ pour la raison.
126 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
127 choices=(), required=False)
128
129 valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
130 valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
131
132
133 class Meta:
134 model = dae.Poste
135 exclude = ('actif', )
136 fields = ('type_intervention',
137 'poste', 'implantation', 'type_poste', 'service', 'nom',
138 'responsable', 'local', 'expatrie', 'mise_a_disposition',
139 'appel', 'date_debut', 'date_fin',
140 'regime_travail', 'regime_travail_nb_heure_semaine',
141 'classement_min', 'classement_max',
142 'valeur_point_min', 'valeur_point_max',
143 'devise_min', 'devise_max',
144 'salaire_min', 'salaire_max',
145 'indemn_expat_min', 'indemn_expat_max',
146 'indemn_fct_min', 'indemn_fct_max',
147 'charges_patronales_min', 'charges_patronales_max',
148 'autre_min', 'autre_max', 'devise_comparaison',
149 'comp_locale_min', 'comp_locale_max',
150 'comp_universite_min', 'comp_universite_max',
151 'comp_fonctionpub_min', 'comp_fonctionpub_max',
152 'comp_ong_min', 'comp_ong_max',
153 'comp_autre_min', 'comp_autre_max',
154 'justification',
155 )
156 widgets = dict(type_intervention=forms.RadioSelect(),
157 appel=forms.RadioSelect(),
158 nom=forms.TextInput(attrs={'size': 60},),
159 date_debut=admin_widgets.AdminDateWidget(),
160 date_fin=admin_widgets.AdminDateWidget(),
161 justification=forms.Textarea(attrs={'cols': 80},),
162 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
163 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
164 )
165
166 def __init__(self, *args, **kwargs):
167 """ Mise à jour dynamique du contenu du menu des postes.
168
169 Si on ne met le menu à jour de cette façon, à chaque instantiation du
170 formulaire, son contenu est mis en cache par le système et il ne
171 reflète pas les changements apportés par les ajouts, modifications,
172 etc...
173
174 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
175 car le "id" de chaque choix est spécial (voir _poste_choices).
176
177 """
178 request = kwargs.pop('request')
179 super(PosteForm, self).__init__(*args, **kwargs)
180 self.fields['poste'].choices = self._poste_choices(request)
181 self.fields['implantation'].choices = _implantation_choices(self, request)
182
183 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
184 if self.instance and self.instance.id is None:
185 dossiers = self.instance.get_dossiers()
186 if len(dossiers) > 0:
187 self.initial['service'] = dossiers[0].service_id
188 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
189
190
191 def _poste_choices(self, request):
192 """ Menu déroulant pour les postes.
193
194 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
195 d'équivalent dans dae.
196
197 """
198 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
199 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
200 id_copies = [p.id_rh_id for p in copies.all()]
201 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
202 # Optimisation de la requête
203 rhv1 = rhv1.select_related(depth=1)
204
205 return [('', 'Nouveau poste')] + \
206 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
207 [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
208 key=lambda t: t[1])
209
210 def clean(self):
211 """
212 Validation conditionnelles de certains champs.
213 """
214 cleaned_data = self.cleaned_data
215
216 # Gestion de la mise à disposition
217 mise_a_disposition = cleaned_data.get("mise_a_disposition")
218 valeur_point_min = cleaned_data.get("valeur_point_min")
219 valeur_point_max = cleaned_data.get("valeur_point_max")
220 if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
221 msg = u"Ce champ est obligatoire."
222 self._errors["valeur_point_min"] = self.error_class([msg])
223 self._errors["valeur_point_max"] = self.error_class([msg])
224 raise forms.ValidationError("Les valeurs de point sont vides")
225
226 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
227 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
228 self._errors["local"] = self.error_class([msg])
229 self._errors["expatrie"] = ''
230 raise forms.ValidationError(msg)
231
232
233 return cleaned_data
234
235
236
237 def save(self, *args, **kwargs):
238 kwargs2 = kwargs.copy()
239 kwargs2['commit'] = False
240 poste = super(PosteForm, self).save(*args, **kwargs2)
241 # id_rh
242 if 'commit' not in kwargs or kwargs['commit']:
243 poste.save()
244 return poste
245
246
247 class ChoosePosteForm(forms.ModelForm):
248 class Meta:
249 model = dae.Poste
250 fields = ('poste',)
251
252 # La liste des choix est laissée vide. Voir PosteForm.__init__.
253 poste = forms.ChoiceField(choices=(), required=False)
254
255 def __init__(self, request=None, *args, **kwargs):
256 super(ChoosePosteForm, self).__init__(*args, **kwargs)
257 self.fields['poste'].choices = self._poste_choices(request)
258
259 def _poste_choices(self, request):
260 """ Menu déroulant pour les postes. """
261 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
262 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
263 id_copies = [p.id_rh_id for p in copies.all()]
264
265 return [('', '----------')] + \
266 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
267 key=lambda t: t[1])
268
269
270 class EmployeForm(forms.ModelForm):
271 """ Formulaire des employés. """
272 class Meta:
273 model = dae.Employe
274 fields = ('employe', 'nom', 'prenom', 'genre')
275
276 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
277 employe = forms.ChoiceField(choices=(), required=False)
278
279 def __init__(self, *args, **kwargs):
280 """ Mise à jour dynamique du contenu du menu des employés. """
281 request = kwargs.pop('request', None)
282 super(EmployeForm, self).__init__(*args, **kwargs)
283 self.fields['employe'].choices = _employe_choices(self, request)
284
285
286
287 class DossierForm(forms.ModelForm):
288 """ Formulaire des dossiers. """
289 class Meta:
290 exclude= ('etat', )
291 model = dae.Dossier
292 widgets = dict(statut_residence=forms.RadioSelect(),
293 contrat_date_debut=admin_widgets.AdminDateWidget(),
294 contrat_date_fin=admin_widgets.AdminDateWidget(),
295 )
296
297 WF_HELP_TEXT = ""
298
299 class PosteWorkflowForm(WorkflowFormMixin):
300 bouton_libelles = POSTE_ETATS_BOUTONS
301 class Meta:
302 fields = ('etat', )
303 model = dae.Poste
304
305 def __init__(self, *args, **kwargs):
306 super(self.__class__, self).__init__(*args, **kwargs)
307 self.fields['etat'].help_text = WF_HELP_TEXT
308
309
310 class DossierWorkflowForm(WorkflowFormMixin):
311 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
312 class Meta:
313 fields = ('etat', )
314 model = dae.Dossier
315
316 def __init__(self, *args, **kwargs):
317 super(self.__class__, self).__init__(*args, **kwargs)
318 self.fields['etat'].help_text = WF_HELP_TEXT
319