remove complement nom
[auf_rh_dae.git] / project / dae / forms.py
1 # -*- encoding: utf-8 -*-
2
3 from django.db.models import Q, Max
4 from django import forms
5 from django.forms.models import inlineformset_factory, modelformset_factory
6 from django.contrib.admin import widgets as admin_widgets
7 from ajax_select.fields import AutoCompleteSelectField
8 from auf.django.workflow.forms import WorkflowFormMixin
9 from auf.django.references import models as ref
10 from dae import models as dae
11 from utils import get_employe_from_user, is_user_dans_services_centraux
12 from rh import models as rh
13 from workflow import grp_drh, POSTE_ETATS_BOUTONS, DOSSIER_ETAT_FINALISE, POSTE_ETAT_FINALISE
14
15 def _implantation_choices(obj, request):
16 # TRAITEMENT NORMAL
17 employe = get_employe_from_user(request.user)
18 # SERVICE
19 if is_user_dans_services_centraux(request.user):
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
30 def _employe_choices(obj, request):
31 # TRAITEMENT NORMAL
32 employe = get_employe_from_user(request.user)
33 # SERVICE
34 if is_user_dans_services_centraux(request.user):
35 q_dae_region_service = Q(poste__implantation=employe.implantation)
36 q_rh_region_service = Q(poste__implantation=employe.implantation)
37 # REGION
38 else:
39 q_dae_region_service = Q(poste__implantation__region=employe.implantation.region)
40 q_rh_region_service = Q(poste__implantation__region=employe.implantation.region)
41 # TRAITEMENT DRH
42 if grp_drh in request.user.groups.all():
43 q_dae_region_service = Q()
44 q_rh_region_service = Q()
45
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))
53 id_copies = [p.id_rh_id for p in copies.all()]
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]
57 rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies)
58
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
64 def option_label(employe):
65 return "%s %s" % (employe.nom.upper(), employe.prenom.title())
66
67 return [('', 'Nouvel employé')] + \
68 sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins] +
69 [('rh-%s' % p.id, option_label(p)) for p in rhv1],
70 key=lambda t: t[1])
71
72 def 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
76
77 PostePieceForm = inlineformset_factory(dae.Poste, dae.PostePiece)
78 DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
79 FinancementForm = inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)
80
81 class 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 Meta:
87 model = dae.DossierComparaison
88 exclude = ('dossier',)
89
90 DossierComparaisonFormSet = modelformset_factory(
91 dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
92 )
93
94 class PosteComparaisonForm(forms.ModelForm):
95
96 recherche = AutoCompleteSelectField('dae_postes', required=False)
97
98 class Meta:
99 model = dae.PosteComparaison
100 exclude = ('poste',)
101
102 PosteComparaisonFormSet = modelformset_factory(
103 dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm
104 )
105
106 class 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
114 def clean_devise(self):
115 devise = self.cleaned_data['devise']
116 if devise.code == 'EUR':
117 return devise
118 implantation = ref.Implantation.objects.get(id=self.data['implantation'])
119 liste_taux = devise.tauxchange_set.order_by('-annee')
120 if len(liste_taux) == 0:
121 raise forms.ValidationError(u"La devise %s n'a pas de taux pour l'implantation %s" % (devise, implantation))
122 else:
123 return devise
124
125 RemunForm = inlineformset_factory(
126 dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm
127 )
128
129 class PosteForm(forms.ModelForm):
130 """ Formulaire des postes. """
131
132 # On ne propose que les services actifs
133 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(archive=False), required=True)
134
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)
142
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)
145
146
147 class Meta:
148 model = dae.Poste
149 fields = ('type_intervention',
150 'poste', 'implantation', 'type_poste', 'service', 'nom',
151 'responsable', 'local', 'expatrie', 'mise_a_disposition',
152 'appel', 'date_debut', 'date_fin',
153 'regime_travail', 'regime_travail_nb_heure_semaine',
154 'classement_min', 'classement_max',
155 'valeur_point_min', 'valeur_point_max',
156 'devise_min', 'devise_max',
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',
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',
166 'comp_autre_min', 'comp_autre_max',
167 'justification',
168 )
169 widgets = dict(type_intervention=forms.RadioSelect(),
170 appel=forms.RadioSelect(),
171 nom=forms.TextInput(attrs={'size': 60},),
172 date_debut=admin_widgets.AdminDateWidget(),
173 date_fin=admin_widgets.AdminDateWidget(),
174 justification=forms.Textarea(attrs={'cols': 80},),
175 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
176 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
177 )
178
179 def __init__(self, *args, **kwargs):
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
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
190 """
191 request = kwargs.pop('request')
192 super(PosteForm, self).__init__(*args, **kwargs)
193 self.fields['poste'].choices = self._poste_choices(request)
194 self.fields['implantation'].choices = _implantation_choices(self, request)
195
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:
200 self.initial['service'] = dossiers[0].poste.service
201
202
203 def _poste_choices(self, request):
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 """
210 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
211 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
212 id_copies = [p.id_rh_id for p in copies.all()]
213 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).exclude(id__in=id_copies)
214 # Optimisation de la requête
215 rhv1 = rhv1.select_related(depth=1)
216
217 return [('', 'Nouveau poste')] + \
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],
220 key=lambda t: t[1])
221
222 def clean(self):
223 """
224 Validation conditionnelles de certains champs.
225 """
226 cleaned_data = self.cleaned_data
227
228 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
229 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
230 self._errors["local"] = self.error_class([msg])
231 self._errors["expatrie"] = ''
232 raise forms.ValidationError(msg)
233
234 return cleaned_data
235
236
237
238 def save(self, *args, **kwargs):
239 kwargs2 = kwargs.copy()
240 kwargs2['commit'] = False
241 poste = super(PosteForm, self).save(*args, **kwargs2)
242 # id_rh
243 if 'commit' not in kwargs or kwargs['commit']:
244 poste.save()
245 return poste
246
247
248 class ChoosePosteForm(forms.ModelForm):
249 class Meta:
250 model = dae.Poste
251 fields = ('poste',)
252
253 # La liste des choix est laissée vide. Voir PosteForm.__init__.
254 poste = forms.ChoiceField(choices=(), required=False)
255
256 def __init__(self, request=None, *args, **kwargs):
257 super(ChoosePosteForm, self).__init__(*args, **kwargs)
258 self.fields['poste'].choices = self._poste_choices(request)
259
260 def _poste_choices(self, request):
261 """ Menu déroulant pour les postes. """
262 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
263 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
264 id_copies = [p.id_rh_id for p in copies.all()]
265
266 return [('', '----------')] + \
267 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
268 key=lambda t: t[1])
269
270
271 class EmployeForm(forms.ModelForm):
272 """ Formulaire des employés. """
273 class Meta:
274 model = dae.Employe
275 fields = ('employe', 'nom', 'prenom', 'genre')
276
277 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
278 employe = forms.ChoiceField(choices=(), required=False)
279
280 def __init__(self, *args, **kwargs):
281 """ Mise à jour dynamique du contenu du menu des employés. """
282 request = kwargs.pop('request', None)
283 super(EmployeForm, self).__init__(*args, **kwargs)
284 self.fields['employe'].choices = _employe_choices(self, request)
285
286
287 class DossierForm(forms.ModelForm):
288 """ Formulaire des dossiers. """
289 class Meta:
290 exclude= ('etat', 'employe', 'poste', 'date_debut', )
291 model = dae.Dossier
292 widgets = dict(statut_residence=forms.RadioSelect(),
293 contrat_date_debut=admin_widgets.AdminDateWidget(),
294 contrat_date_fin=admin_widgets.AdminDateWidget(),
295 )
296
297 WF_HELP_TEXT = ""
298
299 class PosteWorkflowForm(WorkflowFormMixin):
300 bouton_libelles = POSTE_ETATS_BOUTONS
301 class Meta:
302 fields = ('etat', )
303 model = dae.Poste
304
305 def __init__(self, *args, **kwargs):
306 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
307 self.fields['etat'].help_text = WF_HELP_TEXT
308
309
310 class DossierWorkflowForm(WorkflowFormMixin):
311 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
312 class Meta:
313 fields = ('etat', )
314 model = dae.Dossier
315
316 def __init__(self, *args, **kwargs):
317 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
318 self.fields['etat'].help_text = WF_HELP_TEXT
319 self._etat_initial = self.instance.etat
320
321 def save(self):
322 super(DossierWorkflowForm, self).save()
323 poste = self.instance.poste
324 if poste.etat == self._etat_initial:
325 poste.etat = self.instance.etat
326 poste.save()
327
328 class ContratForm(forms.ModelForm):
329
330 class Meta:
331 fields = ('type_contrat', )
332 model = dae.Contrat
333
334 class DAENumeriseeForm(forms.ModelForm):
335
336 class Meta:
337 model = dae.Dossier
338 fields = ('dae_numerisee',)
339
340 class DAEImportableForm(forms.Form):
341 qs_poste = dae.Poste.objects.filter(etat=POSTE_ETAT_FINALISE)
342 qs_dossier = dae.Dossier.objects.filter(etat=DOSSIER_ETAT_FINALISE)
343 poste = forms.ModelChoiceField(queryset=qs_poste, label="Poste finalisé", required=False)
344 dossier = forms.ModelChoiceField(queryset=qs_dossier, label="DAE finalisée", required=False)
345
346 def clean_poste(self):
347 poste = self.cleaned_data['poste']
348 if poste is not None and poste.est_importe():
349 raise forms.ValidationError("Ce poste a déjà été importé")
350 return poste
351
352 def clean_dossier(self):
353 dossier = self.cleaned_data['dossier']
354 if dossier is not None and not dossier.poste.est_importe():
355 raise forms.ValidationError("Le poste de ce dossier doit être importé avant de pouvoir importer le dossier.")
356 return dossier
357
358 def importer_poste(self):
359 poste = self.cleaned_data['poste']
360 if poste is not None and not poste.est_importe():
361 poste.importer()
362