[#2393] DAE numérisée
[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
5d680e84 9from datamaster_modeles import models as ref
5d680e84 10from dae import models as dae
d8cfc3d5 11from utils import get_employe_from_user, is_user_dans_services_centraux
5d680e84 12from rh_v1 import models as rh
56589624 13from workflow import grp_drh, POSTE_ETATS_BOUTONS
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
OL
35 q_dae_region_service = Q(poste__implantation=employe.implantation)
36 q_rh_region_service = Q(implantation1=employe.implantation) | Q(implantation2=employe.implantation)
f258e4e7
OL
37 # REGION
38 else:
072820fc
OL
39 q_dae_region_service = Q(poste__implantation__region=employe.implantation.region)
40 q_rh_region_service = Q(implantation1__region=employe.implantation.region) | Q(implantation2__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):
82
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
OL
94class PosteComparaisonForm(forms.ModelForm):
95
96 recherche = AutoCompleteSelectField('postes', required=False)
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
8899db43 133 service = forms.ModelChoiceField(queryset=rh.Service.objects.filter(actif=True), 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)
ea7adc69 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)
3121c13c
OL
145
146
5d680e84
NC
147 class Meta:
148 model = dae.Poste
b15bf543 149 exclude = ('actif', )
c3be904d
OL
150 fields = ('type_intervention',
151 'poste', 'implantation', 'type_poste', 'service', 'nom',
154677c3 152 'responsable', 'local', 'expatrie', 'mise_a_disposition',
b15bf543 153 'appel', 'date_debut', 'date_fin',
5d680e84
NC
154 'regime_travail', 'regime_travail_nb_heure_semaine',
155 'classement_min', 'classement_max',
156 'valeur_point_min', 'valeur_point_max',
3d627bfd 157 'devise_min', 'devise_max',
5f61bccb
OL
158 'salaire_min', 'salaire_max',
159 'indemn_expat_min', 'indemn_expat_max',
160 'indemn_fct_min', 'indemn_fct_max',
161 'charges_patronales_min', 'charges_patronales_max',
5d680e84
NC
162 'autre_min', 'autre_max', 'devise_comparaison',
163 'comp_locale_min', 'comp_locale_max',
164 'comp_universite_min', 'comp_universite_max',
165 'comp_fonctionpub_min', 'comp_fonctionpub_max',
166 'comp_ong_min', 'comp_ong_max',
8fa94e8b 167 'comp_autre_min', 'comp_autre_max',
2e092e0c 168 'justification',
8fa94e8b 169 )
c3be904d
OL
170 widgets = dict(type_intervention=forms.RadioSelect(),
171 appel=forms.RadioSelect(),
3d627bfd 172 nom=forms.TextInput(attrs={'size': 60},),
e88caaf0
OL
173 date_debut=admin_widgets.AdminDateWidget(),
174 date_fin=admin_widgets.AdminDateWidget(),
2e092e0c 175 justification=forms.Textarea(attrs={'cols': 80},),
3d627bfd 176 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
177 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
178 )
5d680e84 179
c2458db6 180 def __init__(self, *args, **kwargs):
5d680e84
NC
181 """ Mise à jour dynamique du contenu du menu des postes.
182
183 Si on ne met le menu à jour de cette façon, à chaque instantiation du
184 formulaire, son contenu est mis en cache par le système et il ne
185 reflète pas les changements apportés par les ajouts, modifications,
186 etc...
187
139686f2
NC
188 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
189 car le "id" de chaque choix est spécial (voir _poste_choices).
190
5d680e84 191 """
c2458db6 192 request = kwargs.pop('request')
5d680e84 193 super(PosteForm, self).__init__(*args, **kwargs)
f258e4e7
OL
194 self.fields['poste'].choices = self._poste_choices(request)
195 self.fields['implantation'].choices = _implantation_choices(self, request)
5d680e84 196
cc3098d0
OL
197 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
198 if self.instance and self.instance.id is None:
199 dossiers = self.instance.get_dossiers()
200 if len(dossiers) > 0:
201 self.initial['service'] = dossiers[0].service_id
9508a5b8
OL
202 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
203
cc3098d0 204
f258e4e7 205 def _poste_choices(self, request):
5d680e84
NC
206 """ Menu déroulant pour les postes.
207
208 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
209 d'équivalent dans dae.
210
211 """
f258e4e7
OL
212 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
213 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
5d680e84 214 id_copies = [p.id_rh_id for p in copies.all()]
f258e4e7 215 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
139686f2
NC
216 # Optimisation de la requête
217 rhv1 = rhv1.select_related(depth=1)
5d680e84 218
98d51b59 219 return [('', 'Nouveau poste')] + \
4bce4d24
OL
220 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
221 [('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
230 # Gestion de la mise à disposition
231 mise_a_disposition = cleaned_data.get("mise_a_disposition")
232 valeur_point_min = cleaned_data.get("valeur_point_min")
233 valeur_point_max = cleaned_data.get("valeur_point_max")
234 if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
235 msg = u"Ce champ est obligatoire."
236 self._errors["valeur_point_min"] = self.error_class([msg])
237 self._errors["valeur_point_max"] = self.error_class([msg])
238 raise forms.ValidationError("Les valeurs de point sont vides")
f42c6e20 239
f42c6e20
OL
240 if cleaned_data.get("local") is False and cleaned_data.get("expatrie") is False:
241 msg = "Le poste doit au moins être ouvert localement ou aux expatriés"
242 self._errors["local"] = self.error_class([msg])
243 self._errors["expatrie"] = ''
244 raise forms.ValidationError(msg)
245
246
4dd75e7b
OL
247 return cleaned_data
248
249
250
494ff2be
NC
251 def save(self, *args, **kwargs):
252 kwargs2 = kwargs.copy()
253 kwargs2['commit'] = False
254 poste = super(PosteForm, self).save(*args, **kwargs2)
255 # id_rh
256 if 'commit' not in kwargs or kwargs['commit']:
257 poste.save()
258 return poste
259
3ed49093 260
139686f2
NC
261class ChoosePosteForm(forms.ModelForm):
262 class Meta:
263 model = dae.Poste
264 fields = ('poste',)
265
266 # La liste des choix est laissée vide. Voir PosteForm.__init__.
267 poste = forms.ChoiceField(choices=(), required=False)
268
4ee6d70a 269 def __init__(self, request=None, *args, **kwargs):
139686f2 270 super(ChoosePosteForm, self).__init__(*args, **kwargs)
4ee6d70a 271 self.fields['poste'].choices = self._poste_choices(request)
139686f2 272
4ee6d70a 273 def _poste_choices(self, request):
139686f2 274 """ Menu déroulant pour les postes. """
4ee6d70a
OL
275 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(id_rh__isnull=True)
276 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
139686f2
NC
277 id_copies = [p.id_rh_id for p in copies.all()]
278
98d51b59 279 return [('', '----------')] + \
139686f2
NC
280 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
281 key=lambda t: t[1])
282
283
139686f2
NC
284class EmployeForm(forms.ModelForm):
285 """ Formulaire des employés. """
286 class Meta:
287 model = dae.Employe
288 fields = ('employe', 'nom', 'prenom', 'genre')
289
290 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
291 employe = forms.ChoiceField(choices=(), required=False)
292
ac6235f6 293 def __init__(self, *args, **kwargs):
139686f2 294 """ Mise à jour dynamique du contenu du menu des employés. """
ac6235f6 295 request = kwargs.pop('request', None)
139686f2 296 super(EmployeForm, self).__init__(*args, **kwargs)
f258e4e7 297 self.fields['employe'].choices = _employe_choices(self, request)
139686f2 298
139686f2 299
139686f2
NC
300class DossierForm(forms.ModelForm):
301 """ Formulaire des dossiers. """
302 class Meta:
b1baa306 303 exclude= ('etat', )
139686f2 304 model = dae.Dossier
4d25e2ba 305 widgets = dict(statut_residence=forms.RadioSelect(),
0e0aeb7e
OL
306 contrat_date_debut=admin_widgets.AdminDateWidget(),
307 contrat_date_fin=admin_widgets.AdminDateWidget(),
4d25e2ba 308 )
e6f52402 309
3799cafc 310WF_HELP_TEXT = ""
e0b93e3a 311
e6f52402 312class PosteWorkflowForm(WorkflowFormMixin):
56589624 313 bouton_libelles = POSTE_ETATS_BOUTONS
e6f52402
OL
314 class Meta:
315 fields = ('etat', )
316 model = dae.Poste
9536ea21 317
e0b93e3a 318 def __init__(self, *args, **kwargs):
e54b7d5d 319 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a
OL
320 self.fields['etat'].help_text = WF_HELP_TEXT
321
322
e6f52402 323class DossierWorkflowForm(WorkflowFormMixin):
56589624 324 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
e6f52402 325 class Meta:
9e40cfbe 326 fields = ('etat', )
e6f52402 327 model = dae.Dossier
e0b93e3a
OL
328
329 def __init__(self, *args, **kwargs):
e54b7d5d 330 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a 331 self.fields['etat'].help_text = WF_HELP_TEXT
e54b7d5d 332 self._etat_initial = self.instance.etat
e0b93e3a 333
e54b7d5d
EMS
334 def save(self):
335 super(DossierWorkflowForm, self).save()
336 poste = self.instance.poste
337 if poste.etat == self._etat_initial:
338 poste.etat = self.instance.etat
339 poste.save()
9536ea21
EMS
340
341class ContratForm(forms.ModelForm):
342
343 class Meta:
344 fields = ('type', 'fichier')
345 model = dae.Contrat
346
c3f0b49f
EMS
347class DAENumeriseeForm(forms.ModelForm):
348
349 class Meta:
350 model = dae.Dossier
351 fields = ('dae_numerisee',)