#1703
[auf_rh_dae.git] / project / dae / forms.py
CommitLineData
5d680e84 1# -*- encoding: utf-8 -*-
ce110fb9 2
cfd819ed 3from django.db.models import Q, Max
5d680e84 4from django import forms
36341125 5from django.forms.models import inlineformset_factory
e88caaf0 6from django.contrib.admin import widgets as admin_widgets
afc204bf 7from ajax_select.fields import AutoCompleteSelectField
8fa94e8b 8from auf.django.workflow.forms import WorkflowFormMixin
5d680e84 9from datamaster_modeles import models as ref
5d680e84 10from dae import models as dae
d8cfc3d5 11from utils import get_employe_from_user, is_user_dans_services_centraux
5d680e84 12from rh_v1 import models as rh
56589624 13from workflow import grp_drh, POSTE_ETATS_BOUTONS
f258e4e7
OL
14
15def _implantation_choices(obj, request):
16 # TRAITEMENT NORMAL
17 employe = get_employe_from_user(request.user)
18 # SERVICE
d8cfc3d5 19 if is_user_dans_services_centraux(request.user):
f258e4e7
OL
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
30def _employe_choices(obj, request):
f258e4e7
OL
31 # TRAITEMENT NORMAL
32 employe = get_employe_from_user(request.user)
33 # SERVICE
d8cfc3d5 34 if is_user_dans_services_centraux(request.user):
cfd819ed
OL
35 q_dae_region_service = Q(poste__implantation=employe.implantation)
36 q_rh_region_service = Q(implantation1=employe.implantation) | Q(implantation2=employe.implantation)
f258e4e7
OL
37 # REGION
38 else:
cfd819ed
OL
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)
f258e4e7
OL
41 # TRAITEMENT DRH
42 if grp_drh in request.user.groups.all():
cfd819ed
OL
43 q_dae_region_service = Q()
44 q_rh_region_service = Q()
f258e4e7 45
cfd819ed
OL
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))
f258e4e7 53 id_copies = [p.id_rh_id for p in copies.all()]
cfd819ed
OL
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]
f258e4e7
OL
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
5d680e84 67
4bce4d24
OL
68def 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
9cb4de55
OL
72
73class PostePieceForm(inlineformset_factory(dae.Poste, dae.PostePiece)):
74 pass
75
76class DossierPieceForm(inlineformset_factory(dae.Dossier, dae.DossierPiece)):
77 pass
78
e2185d45 79class FinancementForm(inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)):
151e7bd0
OL
80 pass
81
03b395db
OL
82
83class 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
03b395db
OL
91class DossierComparaisonForm(inlineformset_factory(dae.Dossier, dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm)):
92 pass
93
068d1462
OL
94class PosteComparaisonForm(forms.ModelForm):
95
96 recherche = AutoCompleteSelectField('postes', required=False)
97
98 class Model:
99 model = dae.PosteComparaison
100
101class PosteComparaisonForm(inlineformset_factory(dae.Poste, dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm)):
102 pass
103
0a085c42
OL
104class 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
112class RemunForm(inlineformset_factory(dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm)):
113 pass
114
1b217058 115class PosteForm(forms.ModelForm):
5d680e84 116 """ Formulaire des postes. """
12c7f8a7 117
ea7adc69 118 # On ne propose que les services actifs
8899db43 119 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(actif=True), required=True)
ea7adc69 120
12c7f8a7
OL
121 responsable=AutoCompleteSelectField('responsables', required=True)
122 #responsable = forms.ModelChoiceField(
123 # queryset=rh.Poste.objects.select_related(depth=1))
124
125 # La liste des choix est laissée vide. Voir __init__ pour la raison.
126 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
127 choices=(), required=False)
ea7adc69 128
12c7f8a7
OL
129 valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
130 valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
3121c13c
OL
131
132
5d680e84
NC
133 class Meta:
134 model = dae.Poste
b15bf543 135 exclude = ('actif', )
c3be904d
OL
136 fields = ('type_intervention',
137 'poste', 'implantation', 'type_poste', 'service', 'nom',
154677c3 138 'responsable', 'local', 'expatrie', 'mise_a_disposition',
b15bf543 139 'appel', 'date_debut', 'date_fin',
5d680e84
NC
140 'regime_travail', 'regime_travail_nb_heure_semaine',
141 'classement_min', 'classement_max',
142 'valeur_point_min', 'valeur_point_max',
3d627bfd 143 'devise_min', 'devise_max',
5f61bccb
OL
144 'salaire_min', 'salaire_max',
145 'indemn_expat_min', 'indemn_expat_max',
146 'indemn_fct_min', 'indemn_fct_max',
147 'charges_patronales_min', 'charges_patronales_max',
5d680e84
NC
148 'autre_min', 'autre_max', 'devise_comparaison',
149 'comp_locale_min', 'comp_locale_max',
150 'comp_universite_min', 'comp_universite_max',
151 'comp_fonctionpub_min', 'comp_fonctionpub_max',
152 'comp_ong_min', 'comp_ong_max',
8fa94e8b 153 'comp_autre_min', 'comp_autre_max',
2e092e0c 154 'justification',
8fa94e8b 155 )
c3be904d
OL
156 widgets = dict(type_intervention=forms.RadioSelect(),
157 appel=forms.RadioSelect(),
3d627bfd 158 nom=forms.TextInput(attrs={'size': 60},),
e88caaf0
OL
159 date_debut=admin_widgets.AdminDateWidget(),
160 date_fin=admin_widgets.AdminDateWidget(),
2e092e0c 161 justification=forms.Textarea(attrs={'cols': 80},),
3d627bfd 162 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
163 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
164 )
5d680e84 165
c2458db6 166 def __init__(self, *args, **kwargs):
5d680e84
NC
167 """ Mise à jour dynamique du contenu du menu des postes.
168
169 Si on ne met le menu à jour de cette façon, à chaque instantiation du
170 formulaire, son contenu est mis en cache par le système et il ne
171 reflète pas les changements apportés par les ajouts, modifications,
172 etc...
173
139686f2
NC
174 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
175 car le "id" de chaque choix est spécial (voir _poste_choices).
176
5d680e84 177 """
c2458db6 178 request = kwargs.pop('request')
5d680e84 179 super(PosteForm, self).__init__(*args, **kwargs)
f258e4e7
OL
180 self.fields['poste'].choices = self._poste_choices(request)
181 self.fields['implantation'].choices = _implantation_choices(self, request)
5d680e84 182
cc3098d0
OL
183 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
184 if self.instance and self.instance.id is None:
185 dossiers = self.instance.get_dossiers()
186 if len(dossiers) > 0:
187 self.initial['service'] = dossiers[0].service_id
9508a5b8
OL
188 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
189
cc3098d0 190
f258e4e7 191 def _poste_choices(self, request):
5d680e84
NC
192 """ Menu déroulant pour les postes.
193
194 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
195 d'équivalent dans dae.
196
197 """
f258e4e7
OL
198 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
199 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
5d680e84 200 id_copies = [p.id_rh_id for p in copies.all()]
f258e4e7 201 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
139686f2
NC
202 # Optimisation de la requête
203 rhv1 = rhv1.select_related(depth=1)
5d680e84 204
98d51b59 205 return [('', 'Nouveau poste')] + \
4bce4d24
OL
206 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
207 [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
5d680e84 208 key=lambda t: t[1])
3ed49093 209
4dd75e7b
OL
210 def clean(self):
211 """
212 Validation conditionnelles de certains champs.
213 """
214 cleaned_data = self.cleaned_data
215
216 # Gestion de la mise à disposition
217 mise_a_disposition = cleaned_data.get("mise_a_disposition")
218 valeur_point_min = cleaned_data.get("valeur_point_min")
219 valeur_point_max = cleaned_data.get("valeur_point_max")
220 if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
221 msg = u"Ce champ est obligatoire."
222 self._errors["valeur_point_min"] = self.error_class([msg])
223 self._errors["valeur_point_max"] = self.error_class([msg])
224 raise forms.ValidationError("Les valeurs de point sont vides")
f42c6e20 225
f42c6e20
OL
226 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
227 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
228 self._errors["local"] = self.error_class([msg])
229 self._errors["expatrie"] = ''
230 raise forms.ValidationError(msg)
231
232
4dd75e7b
OL
233 return cleaned_data
234
235
236
494ff2be
NC
237 def save(self, *args, **kwargs):
238 kwargs2 = kwargs.copy()
239 kwargs2['commit'] = False
240 poste = super(PosteForm, self).save(*args, **kwargs2)
241 # id_rh
242 if 'commit' not in kwargs or kwargs['commit']:
243 poste.save()
244 return poste
245
3ed49093 246
139686f2
NC
247class ChoosePosteForm(forms.ModelForm):
248 class Meta:
249 model = dae.Poste
250 fields = ('poste',)
251
252 # La liste des choix est laissée vide. Voir PosteForm.__init__.
253 poste = forms.ChoiceField(choices=(), required=False)
254
4ee6d70a 255 def __init__(self, request=None, *args, **kwargs):
139686f2 256 super(ChoosePosteForm, self).__init__(*args, **kwargs)
4ee6d70a 257 self.fields['poste'].choices = self._poste_choices(request)
139686f2 258
4ee6d70a 259 def _poste_choices(self, request):
139686f2 260 """ Menu déroulant pour les postes. """
4ee6d70a
OL
261 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
262 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
139686f2
NC
263 id_copies = [p.id_rh_id for p in copies.all()]
264
98d51b59 265 return [('', '----------')] + \
139686f2
NC
266 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
267 key=lambda t: t[1])
268
269
139686f2
NC
270class EmployeForm(forms.ModelForm):
271 """ Formulaire des employés. """
272 class Meta:
273 model = dae.Employe
274 fields = ('employe', 'nom', 'prenom', 'genre')
275
276 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
277 employe = forms.ChoiceField(choices=(), required=False)
278
ac6235f6 279 def __init__(self, *args, **kwargs):
139686f2 280 """ Mise à jour dynamique du contenu du menu des employés. """
ac6235f6 281 request = kwargs.pop('request', None)
139686f2 282 super(EmployeForm, self).__init__(*args, **kwargs)
f258e4e7 283 self.fields['employe'].choices = _employe_choices(self, request)
139686f2 284
139686f2
NC
285
286
287class DossierForm(forms.ModelForm):
288 """ Formulaire des dossiers. """
289 class Meta:
b1baa306 290 exclude= ('etat', )
139686f2 291 model = dae.Dossier
4d25e2ba 292 widgets = dict(statut_residence=forms.RadioSelect(),
0e0aeb7e
OL
293 contrat_date_debut=admin_widgets.AdminDateWidget(),
294 contrat_date_fin=admin_widgets.AdminDateWidget(),
4d25e2ba 295 )
e6f52402 296
3799cafc 297WF_HELP_TEXT = ""
e0b93e3a 298
e6f52402 299class PosteWorkflowForm(WorkflowFormMixin):
56589624 300 bouton_libelles = POSTE_ETATS_BOUTONS
e6f52402
OL
301 class Meta:
302 fields = ('etat', )
303 model = dae.Poste
56589624 304
e0b93e3a
OL
305 def __init__(self, *args, **kwargs):
306 super(self.__class__, self).__init__(*args, **kwargs)
307 self.fields['etat'].help_text = WF_HELP_TEXT
308
309
e6f52402 310class DossierWorkflowForm(WorkflowFormMixin):
56589624 311 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
e6f52402
OL
312 class Meta:
313 fields = ('etat', )
314 model = dae.Dossier
e0b93e3a
OL
315
316 def __init__(self, *args, **kwargs):
317 super(self.__class__, self).__init__(*args, **kwargs)
318 self.fields['etat'].help_text = WF_HELP_TEXT
319