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