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