liste remove pending
[auf_rh_dae.git] / project / dae / forms.py
1 # -*- encoding: utf-8 -*-
2
3 import datetime
4 from django.db.models import Q, Max
5 from django import forms
6 from django.forms.models import inlineformset_factory, modelformset_factory
7 from django.contrib.admin import widgets as admin_widgets
8 from ajax_select.fields import AutoCompleteSelectField
9 from auf.django.workflow.forms import WorkflowFormMixin
10 from auf.django.references import models as ref
11 from dae import models as dae
12 from utils import get_employe_from_user, is_user_dans_services_centraux
13 from rh import models as rh
14 from workflow import grp_drh, POSTE_ETATS_BOUTONS, DOSSIER_ETAT_FINALISE, POSTE_ETAT_FINALISE
15
16 def _implantation_choices(obj, request):
17 # TRAITEMENT NORMAL
18 employe = get_employe_from_user(request.user)
19 # SERVICE
20 if is_user_dans_services_centraux(request.user):
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
31 def _employe_choices(obj, request):
32 # TRAITEMENT NORMAL
33 employe = get_employe_from_user(request.user)
34 # SERVICE
35 if is_user_dans_services_centraux(request.user):
36 q_dae_region_service = Q(poste__implantation=employe.implantation)
37 q_rh_region_service = Q(poste__implantation=employe.implantation)
38 # REGION
39 else:
40 q_dae_region_service = Q(poste__implantation__region=employe.implantation.region)
41 q_rh_region_service = Q(poste__implantation__region=employe.implantation.region)
42 # TRAITEMENT DRH
43 if grp_drh in request.user.groups.all():
44 q_dae_region_service = Q()
45 q_rh_region_service = Q()
46
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))
54 id_copies = [p.id_rh_id for p in copies.all()]
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]
58 rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies)
59
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
65 def option_label(employe):
66 return "%s %s" % (employe.nom.upper(), employe.prenom.title())
67
68 return [('', 'Nouvel employé')] + \
69 sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins] +
70 [('rh-%s' % p.id, option_label(p)) for p in rhv1],
71 key=lambda t: t[1])
72
73 def label_poste_display(poste):
74 """Formate un visuel pour un poste dans une liste déroulante"""
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)
79 return label
80
81 PostePieceForm = inlineformset_factory(dae.Poste, dae.PostePiece)
82 DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
83 FinancementForm = inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=2)
84
85 class DossierComparaisonForm(forms.ModelForm):
86
87 recherche = AutoCompleteSelectField('dossiers', required=False)
88 poste = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':'60'}))
89
90 class Meta:
91 model = dae.DossierComparaison
92 exclude = ('dossier',)
93
94 DossierComparaisonFormSet = modelformset_factory(
95 dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
96 )
97
98 class PosteComparaisonForm(forms.ModelForm):
99
100 recherche = AutoCompleteSelectField('dae_postes', required=False)
101
102 class Meta:
103 model = dae.PosteComparaison
104 exclude = ('poste',)
105
106 PosteComparaisonFormSet = modelformset_factory(
107 dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm
108 )
109
110 class 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
118 def clean_devise(self):
119 devise = self.cleaned_data['devise']
120 if devise.code == 'EUR':
121 return devise
122 implantation = ref.Implantation.objects.get(id=self.data['implantation'])
123 liste_taux = devise.tauxchange_set.order_by('-annee')
124 if len(liste_taux) == 0:
125 raise forms.ValidationError(u"La devise %s n'a pas de taux pour l'implantation %s" % (devise, implantation))
126 else:
127 return devise
128
129 RemunForm = inlineformset_factory(
130 dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm
131 )
132
133 class PosteForm(forms.ModelForm):
134 """ Formulaire des postes. """
135
136 # On ne propose que les services actifs
137 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(archive=False), required=True)
138
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)
146
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)
149
150
151 class Meta:
152 model = dae.Poste
153 fields = ('type_intervention',
154 'poste', 'implantation', 'type_poste', 'service', 'nom',
155 'responsable', 'local', 'expatrie', 'mise_a_disposition',
156 'appel', 'date_debut', 'date_fin',
157 'regime_travail', 'regime_travail_nb_heure_semaine',
158 'classement_min', 'classement_max',
159 'valeur_point_min', 'valeur_point_max',
160 'devise_min', 'devise_max',
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',
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',
170 'comp_autre_min', 'comp_autre_max',
171 'justification',
172 )
173 widgets = dict(type_intervention=forms.RadioSelect(),
174 appel=forms.RadioSelect(),
175 nom=forms.TextInput(attrs={'size': 60},),
176 date_debut=admin_widgets.AdminDateWidget(),
177 date_fin=admin_widgets.AdminDateWidget(),
178 justification=forms.Textarea(attrs={'cols': 80},),
179 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
180 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
181 )
182
183 def __init__(self, *args, **kwargs):
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
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
194 """
195 request = kwargs.pop('request')
196 super(PosteForm, self).__init__(*args, **kwargs)
197 self.fields['poste'].choices = self._poste_choices(request)
198 self.fields['implantation'].choices = _implantation_choices(self, request)
199
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:
204 self.initial['service'] = dossiers[0].poste.service
205
206
207 def _poste_choices(self, request):
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 """
214 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True).filter(etat=POSTE_ETAT_FINALISE)
215 id_copies = [p.id_rh_id for p in copies.all()]
216 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).exclude(id__in=id_copies)
217 # Optimisation de la requête
218 rhv1 = rhv1.select_related(depth=1)
219
220 return [('', 'Nouveau poste')] + \
221 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
222 key=lambda t: t[1])
223
224 def clean(self):
225 """
226 Validation conditionnelles de certains champs.
227 """
228 cleaned_data = self.cleaned_data
229
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)
235
236 return cleaned_data
237
238
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 if poste.id is None:
246 poste.date_creation = datetime.datetime.now()
247 poste.save()
248 return poste
249
250
251 class 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
259 def __init__(self, request=None, *args, **kwargs):
260 super(ChoosePosteForm, self).__init__(*args, **kwargs)
261 self.fields['poste'].choices = self._poste_choices(request)
262
263 def _poste_choices(self, request):
264 """ Menu déroulant pour les postes. """
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)
267
268 return [('', '----------')] + \
269 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
270 key=lambda t: t[1])
271
272
273 class 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
282 def __init__(self, *args, **kwargs):
283 """ Mise à jour dynamique du contenu du menu des employés. """
284 request = kwargs.pop('request', None)
285 super(EmployeForm, self).__init__(*args, **kwargs)
286 self.fields['employe'].choices = _employe_choices(self, request)
287
288
289 class DossierForm(forms.ModelForm):
290 """ Formulaire des dossiers. """
291 class Meta:
292 exclude= ('etat', 'employe', 'poste', 'date_debut', )
293 model = dae.Dossier
294 widgets = dict(statut_residence=forms.RadioSelect(),
295 contrat_date_debut=admin_widgets.AdminDateWidget(),
296 contrat_date_fin=admin_widgets.AdminDateWidget(),
297 )
298
299 def save(self, *args, **kwargs):
300 dossier = super(PosteForm, self).save(*args, **kwargs)
301 if dossier.id is None:
302 dossier.date_creation = datetime.datetime.now()
303 dossier.save()
304 return dossier
305
306 WF_HELP_TEXT = ""
307
308 class PosteWorkflowForm(WorkflowFormMixin):
309 bouton_libelles = POSTE_ETATS_BOUTONS
310 class Meta:
311 fields = ('etat', )
312 model = dae.Poste
313
314 def __init__(self, *args, **kwargs):
315 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
316 self.fields['etat'].help_text = WF_HELP_TEXT
317
318
319 class DossierWorkflowForm(WorkflowFormMixin):
320 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
321 class Meta:
322 fields = ('etat', )
323 model = dae.Dossier
324
325 def __init__(self, *args, **kwargs):
326 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
327 self.fields['etat'].help_text = WF_HELP_TEXT
328 self._etat_initial = self.instance.etat
329
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()
336
337 class ContratForm(forms.ModelForm):
338
339 class Meta:
340 fields = ('type_contrat', )
341 model = dae.Contrat
342
343 class DAENumeriseeForm(forms.ModelForm):
344
345 class Meta:
346 model = dae.Dossier
347 fields = ('dae_numerisee',)
348
349 class 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