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