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