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