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