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