dep useless
[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
320d7584 5from django.forms.models import inlineformset_factory, modelformset_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
bf6f2712 9from auf.django.references import models as ref
5d680e84 10from dae import models as dae
d8cfc3d5 11from utils import get_employe_from_user, is_user_dans_services_centraux
09aa8374 12from rh import models as rh
317ce433 13from workflow import grp_drh, POSTE_ETATS_BOUTONS, DOSSIER_ETAT_FINALISE, POSTE_ETAT_FINALISE
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 35 q_dae_region_service = Q(poste__implantation=employe.implantation)
09aa8374 36 q_rh_region_service = Q(poste__implantation=employe.implantation)
f258e4e7
OL
37 # REGION
38 else:
072820fc 39 q_dae_region_service = Q(poste__implantation__region=employe.implantation.region)
09aa8374 40 q_rh_region_service = Q(poste__implantation__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"""
23294f7d
OL
74 annee = ""
75 if poste.date_debut:
76 annee = poste.date_debut.year
77 label = u"%s %s - %s [%s]" %(annee, poste.type_poste, poste.type_poste.famille_emploi.nom, poste.id)
4bce4d24 78 return label
9cb4de55 79
25086dcf
EMS
80PostePieceForm = inlineformset_factory(dae.Poste, dae.PostePiece)
81DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
82FinancementForm = inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)
03b395db
OL
83
84class DossierComparaisonForm(forms.ModelForm):
11f22317 85
03b395db
OL
86 recherche = AutoCompleteSelectField('dossiers', required=False)
87 poste = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':'60'}))
88
320d7584 89 class Meta:
03b395db 90 model = dae.DossierComparaison
320d7584 91 exclude = ('dossier',)
03b395db 92
320d7584
EMS
93DossierComparaisonFormSet = modelformset_factory(
94 dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
25086dcf 95)
03b395db 96
068d1462 97class PosteComparaisonForm(forms.ModelForm):
11f22317 98
e503e64d 99 recherche = AutoCompleteSelectField('dae_postes', required=False)
068d1462 100
320d7584 101 class Meta:
068d1462 102 model = dae.PosteComparaison
320d7584 103 exclude = ('poste',)
068d1462 104
320d7584
EMS
105PosteComparaisonFormSet = modelformset_factory(
106 dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm
25086dcf 107)
068d1462 108
0a085c42
OL
109class FlexibleRemunForm(forms.ModelForm):
110
111 montant_mensuel = forms.DecimalField(required=False)
112 montant = forms.DecimalField(required=True, label='Montant annuel')
113
114 class Meta:
115 model = dae.Remuneration
116
dc4b78a7
OL
117 def clean_devise(self):
118 devise = self.cleaned_data['devise']
67173010
OL
119 if devise.code == 'EUR':
120 return devise
a7186cbb 121 implantation = ref.Implantation.objects.get(id=self.data['implantation'])
2455f48d 122 liste_taux = devise.tauxchange_set.order_by('-annee')
dc4b78a7 123 if len(liste_taux) == 0:
a7186cbb 124 raise forms.ValidationError(u"La devise %s n'a pas de taux pour l'implantation %s" % (devise, implantation))
dc4b78a7
OL
125 else:
126 return devise
127
25086dcf
EMS
128RemunForm = inlineformset_factory(
129 dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm
130)
0a085c42 131
1b217058 132class PosteForm(forms.ModelForm):
5d680e84 133 """ Formulaire des postes. """
12c7f8a7 134
ea7adc69 135 # On ne propose que les services actifs
f614ca5c 136 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(archive=False), required=True)
ea7adc69 137
12c7f8a7
OL
138 responsable=AutoCompleteSelectField('responsables', required=True)
139 #responsable = forms.ModelChoiceField(
140 # queryset=rh.Poste.objects.select_related(depth=1))
141
142 # La liste des choix est laissée vide. Voir __init__ pour la raison.
143 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
144 choices=(), required=False)
11f22317 145
12c7f8a7
OL
146 valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
147 valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
11f22317
EMS
148
149
5d680e84
NC
150 class Meta:
151 model = dae.Poste
c3be904d
OL
152 fields = ('type_intervention',
153 'poste', 'implantation', 'type_poste', 'service', 'nom',
154677c3 154 'responsable', 'local', 'expatrie', 'mise_a_disposition',
b15bf543 155 'appel', 'date_debut', 'date_fin',
5d680e84
NC
156 'regime_travail', 'regime_travail_nb_heure_semaine',
157 'classement_min', 'classement_max',
158 'valeur_point_min', 'valeur_point_max',
3d627bfd 159 'devise_min', 'devise_max',
5f61bccb
OL
160 'salaire_min', 'salaire_max',
161 'indemn_expat_min', 'indemn_expat_max',
162 'indemn_fct_min', 'indemn_fct_max',
163 'charges_patronales_min', 'charges_patronales_max',
5d680e84
NC
164 'autre_min', 'autre_max', 'devise_comparaison',
165 'comp_locale_min', 'comp_locale_max',
166 'comp_universite_min', 'comp_universite_max',
167 'comp_fonctionpub_min', 'comp_fonctionpub_max',
168 'comp_ong_min', 'comp_ong_max',
8fa94e8b 169 'comp_autre_min', 'comp_autre_max',
2e092e0c 170 'justification',
8fa94e8b 171 )
c3be904d
OL
172 widgets = dict(type_intervention=forms.RadioSelect(),
173 appel=forms.RadioSelect(),
3d627bfd 174 nom=forms.TextInput(attrs={'size': 60},),
e88caaf0
OL
175 date_debut=admin_widgets.AdminDateWidget(),
176 date_fin=admin_widgets.AdminDateWidget(),
2e092e0c 177 justification=forms.Textarea(attrs={'cols': 80},),
3d627bfd 178 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
179 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
180 )
5d680e84 181
c2458db6 182 def __init__(self, *args, **kwargs):
5d680e84
NC
183 """ Mise à jour dynamique du contenu du menu des postes.
184
185 Si on ne met le menu à jour de cette façon, à chaque instantiation du
186 formulaire, son contenu est mis en cache par le système et il ne
187 reflète pas les changements apportés par les ajouts, modifications,
188 etc...
189
139686f2
NC
190 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
191 car le "id" de chaque choix est spécial (voir _poste_choices).
192
5d680e84 193 """
c2458db6 194 request = kwargs.pop('request')
5d680e84 195 super(PosteForm, self).__init__(*args, **kwargs)
f258e4e7
OL
196 self.fields['poste'].choices = self._poste_choices(request)
197 self.fields['implantation'].choices = _implantation_choices(self, request)
5d680e84 198
cc3098d0
OL
199 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
200 if self.instance and self.instance.id is None:
201 dossiers = self.instance.get_dossiers()
202 if len(dossiers) > 0:
09aa8374 203 self.initial['service'] = dossiers[0].poste.service
9508a5b8 204
cc3098d0 205
f258e4e7 206 def _poste_choices(self, request):
5d680e84
NC
207 """ Menu déroulant pour les postes.
208
209 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
210 d'équivalent dans dae.
211
212 """
f614ca5c 213 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
f258e4e7 214 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
5d680e84 215 id_copies = [p.id_rh_id for p in copies.all()]
f614ca5c 216 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).exclude(id__in=id_copies)
139686f2
NC
217 # Optimisation de la requête
218 rhv1 = rhv1.select_related(depth=1)
5d680e84 219
98d51b59 220 return [('', 'Nouveau poste')] + \
4bce4d24
OL
221 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
222 [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
5d680e84 223 key=lambda t: t[1])
3ed49093 224
4dd75e7b
OL
225 def clean(self):
226 """
227 Validation conditionnelles de certains champs.
228 """
229 cleaned_data = self.cleaned_data
230
f42c6e20
OL
231 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
232 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
233 self._errors["local"] = self.error_class([msg])
234 self._errors["expatrie"] = ''
235 raise forms.ValidationError(msg)
f42c6e20 236
4dd75e7b
OL
237 return cleaned_data
238
239
240
494ff2be
NC
241 def save(self, *args, **kwargs):
242 kwargs2 = kwargs.copy()
243 kwargs2['commit'] = False
244 poste = super(PosteForm, self).save(*args, **kwargs2)
245 # id_rh
246 if 'commit' not in kwargs or kwargs['commit']:
247 poste.save()
248 return poste
249
3ed49093 250
139686f2
NC
251class ChoosePosteForm(forms.ModelForm):
252 class Meta:
253 model = dae.Poste
254 fields = ('poste',)
255
256 # La liste des choix est laissée vide. Voir PosteForm.__init__.
257 poste = forms.ChoiceField(choices=(), required=False)
258
4ee6d70a 259 def __init__(self, request=None, *args, **kwargs):
139686f2 260 super(ChoosePosteForm, self).__init__(*args, **kwargs)
4ee6d70a 261 self.fields['poste'].choices = self._poste_choices(request)
139686f2 262
4ee6d70a 263 def _poste_choices(self, request):
139686f2 264 """ Menu déroulant pour les postes. """
4ee6d70a
OL
265 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
266 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
139686f2
NC
267 id_copies = [p.id_rh_id for p in copies.all()]
268
98d51b59 269 return [('', '----------')] + \
139686f2
NC
270 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
271 key=lambda t: t[1])
272
273
139686f2
NC
274class EmployeForm(forms.ModelForm):
275 """ Formulaire des employés. """
276 class Meta:
277 model = dae.Employe
278 fields = ('employe', 'nom', 'prenom', 'genre')
279
280 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
281 employe = forms.ChoiceField(choices=(), required=False)
282
ac6235f6 283 def __init__(self, *args, **kwargs):
139686f2 284 """ Mise à jour dynamique du contenu du menu des employés. """
ac6235f6 285 request = kwargs.pop('request', None)
139686f2 286 super(EmployeForm, self).__init__(*args, **kwargs)
f258e4e7 287 self.fields['employe'].choices = _employe_choices(self, request)
139686f2 288
139686f2 289
139686f2
NC
290class DossierForm(forms.ModelForm):
291 """ Formulaire des dossiers. """
292 class Meta:
16b1454e 293 exclude= ('etat', 'employe', 'poste', 'date_debut', )
139686f2 294 model = dae.Dossier
4d25e2ba 295 widgets = dict(statut_residence=forms.RadioSelect(),
0e0aeb7e
OL
296 contrat_date_debut=admin_widgets.AdminDateWidget(),
297 contrat_date_fin=admin_widgets.AdminDateWidget(),
4d25e2ba 298 )
e6f52402 299
3799cafc 300WF_HELP_TEXT = ""
e0b93e3a 301
e6f52402 302class PosteWorkflowForm(WorkflowFormMixin):
56589624 303 bouton_libelles = POSTE_ETATS_BOUTONS
e6f52402
OL
304 class Meta:
305 fields = ('etat', )
306 model = dae.Poste
9536ea21 307
e0b93e3a 308 def __init__(self, *args, **kwargs):
e54b7d5d 309 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a
OL
310 self.fields['etat'].help_text = WF_HELP_TEXT
311
312
e6f52402 313class DossierWorkflowForm(WorkflowFormMixin):
56589624 314 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
e6f52402 315 class Meta:
9e40cfbe 316 fields = ('etat', )
e6f52402 317 model = dae.Dossier
e0b93e3a
OL
318
319 def __init__(self, *args, **kwargs):
e54b7d5d 320 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a 321 self.fields['etat'].help_text = WF_HELP_TEXT
e54b7d5d 322 self._etat_initial = self.instance.etat
e0b93e3a 323
e54b7d5d
EMS
324 def save(self):
325 super(DossierWorkflowForm, self).save()
326 poste = self.instance.poste
327 if poste.etat == self._etat_initial:
328 poste.etat = self.instance.etat
329 poste.save()
9536ea21
EMS
330
331class ContratForm(forms.ModelForm):
332
333 class Meta:
a4125771 334 fields = ('type_contrat', )
9536ea21
EMS
335 model = dae.Contrat
336
c3f0b49f
EMS
337class DAENumeriseeForm(forms.ModelForm):
338
339 class Meta:
340 model = dae.Dossier
341 fields = ('dae_numerisee',)
317ce433
OL
342
343class DAEImportableForm(forms.Form):
344 qs_poste = dae.Poste.objects.filter(etat=POSTE_ETAT_FINALISE)
345 qs_dossier = dae.Dossier.objects.filter(etat=DOSSIER_ETAT_FINALISE)
346 poste = forms.ModelChoiceField(queryset=qs_poste, label="Poste finalisé", required=False)
347 dossier = forms.ModelChoiceField(queryset=qs_dossier, label="DAE finalisée", required=False)
348
349 def clean_poste(self):
350 poste = self.cleaned_data['poste']
351 if poste is not None and poste.est_importe():
352 raise forms.ValidationError("Ce poste a déjà été importé")
353 return poste
354
355 def clean_dossier(self):
356 dossier = self.cleaned_data['dossier']
357 if dossier is not None and not dossier.poste.est_importe():
358 raise forms.ValidationError("Le poste de ce dossier doit être importé avant de pouvoir importer le dossier.")
359 return dossier
360
361 def importer_poste(self):
362 poste = self.cleaned_data['poste']
363 if poste is not None and not poste.est_importe():
364 poste.importer()
365