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