remun clean
[auf_rh_dae.git] / project / dae / forms.py
1 # -*- encoding: utf-8 -*-
2
3 from django.db.models import Q
4 from django import forms
5 from django.forms.models import inlineformset_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 datamaster_modeles import models as ref
10 from dae import models as dae
11 from utils import get_employe_from_user, is_user_dans_service
12 from rh_v1 import models as rh
13 from workflow import grp_drh
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_service(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 q = Q(id_rh__isnull=True) & Q(id_rh__isnull=True)
32
33 # TRAITEMENT NORMAL
34 employe = get_employe_from_user(request.user)
35 # SERVICE
36 if is_user_dans_service(request.user):
37 q_region_service = Q(implantation1=employe.implantation) | Q(implantation2=employe.implantation)
38 # REGION
39 else:
40 q_region_service = Q(implantation1__region=employe.implantation.region) | Q(implantation2__region=employe.implantation.region)
41 # TRAITEMENT DRH
42 if grp_drh in request.user.groups.all():
43 q_region_service = Q()
44
45 # Construction de la liste des employés en puisant dans DAE (pas d'info) et dans rh_v1
46 # Pour le filtrage par région/service, on est obligé d'aller regarder le dossier rh_v1
47 # car l'information dans le modèle rh_v1.Employe n'existe pas.
48 dae_ = dae.Employe.objects.filter(id_rh__isnull=True)
49 copies = dae.Employe.objects.filter(Q(id_rh__isnull=False))
50 id_copies = [p.id_rh_id for p in copies.all()]
51 employes_ids = list(set([d.employe_id for d in rh.Dossier.objects.filter(q_region_service)]))
52 rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies)
53
54 def option_label(employe):
55 return "%s %s" % (employe.nom.upper(), employe.prenom.title())
56
57 return [('', 'Nouvel employé')] + \
58 sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies] +
59 [('rh-%s' % p.id, option_label(p)) for p in rhv1],
60 key=lambda t: t[1])
61
62
63 def label_poste_display(poste):
64 """Formate un visuel pour un poste dans une liste déroulante"""
65 label = u"%s - %s [%s]" %(poste.type_poste, poste.type_poste.famille_emploi.nom, poste.id)
66 return label
67
68 class PostePieceForm(inlineformset_factory(dae.Poste, dae.PostePiece)):
69 pass
70
71 class DossierPieceForm(inlineformset_factory(dae.Dossier, dae.DossierPiece)):
72 pass
73
74 class FinancementForm(inlineformset_factory(dae.Poste, dae.PosteFinancement, extra=1)):
75 pass
76
77
78 class DossierComparaisonForm(forms.ModelForm):
79
80 recherche = AutoCompleteSelectField('dossiers', required=False)
81 poste = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':'60'}))
82
83 class Model:
84 model = dae.DossierComparaison
85
86 class DossierComparaisonForm(inlineformset_factory(dae.Dossier, dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm)):
87 pass
88
89 class FlexibleRemunForm(forms.ModelForm):
90
91 montant_mensuel = forms.DecimalField(required=False)
92 montant = forms.DecimalField(required=True, label='Montant annuel')
93
94 class Meta:
95 model = dae.Remuneration
96
97 class RemunForm(inlineformset_factory(dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm)):
98 pass
99
100 class JustificationNouvelEmployeForm(inlineformset_factory(dae.Dossier,
101 dae.JustificationNouvelEmploye,
102 extra=0,
103 can_delete=False,
104 exclude=('question',))):
105 """
106 Formulaire de justification d'un nouvel employé.
107 Le dossier a besoin d'être enregistré une première fois afin de prépopuler les questions.
108 """
109 def __init__(self, *args, **kwargs):
110 instance = kwargs['instance']
111 if instance.id:
112 q_ids = [j.question.id for j in instance.justificationnouvelemploye_set.filter(dossier=instance)]
113 for q in dae.JustificationQuestion.objects.filter(type="N"):
114 if q.id in q_ids:
115 continue
116 j = dae.JustificationNouvelEmploye()
117 j.dossier = instance
118 j.question = q
119 j.save()
120 super(self.__class__, self).__init__(*args, **kwargs)
121
122 class JustificationAutreEmployeForm(inlineformset_factory(dae.Dossier,
123 dae.JustificationAutreEmploye,
124 extra=0,
125 can_delete=False,
126 exclude=('question',))):
127 """
128 Formulaire de justification d'un nouvel employé.
129 Le dossier a besoin d'être enregistré une première fois afin de prépopuler les questions.
130 """
131 def __init__(self, *args, **kwargs):
132 instance = kwargs['instance']
133 if instance.id:
134 q_ids = [j.question.id for j in instance.justificationautreemploye_set.filter(dossier=instance)]
135 for q in dae.JustificationQuestion.objects.filter(type="R"):
136 if q.id in q_ids:
137 continue
138 j = dae.JustificationAutreEmploye()
139 j.dossier = instance
140 j.question = q
141 j.save()
142 super(self.__class__, self).__init__(*args, **kwargs)
143
144 class PosteForm(forms.ModelForm):
145 """ Formulaire des postes. """
146
147
148 class Meta:
149 model = dae.Poste
150
151 fields = ('poste', 'implantation', 'type_poste', 'service', 'nom',
152 'responsable', 'local', 'expatrie', 'mise_a_disposition',
153 'appel', 'date_debut', 'date_fin', 'actif',
154 'regime_travail', 'regime_travail_nb_heure_semaine',
155 'classement_min', 'classement_max',
156 'valeur_point_min', 'valeur_point_max',
157 'devise_min', 'devise_max',
158 'salaire_min', 'salaire_max', 'indemn_min', 'indemn_max',
159 'autre_min', 'autre_max', 'devise_comparaison',
160 'comp_locale_min', 'comp_locale_max',
161 'comp_universite_min', 'comp_universite_max',
162 'comp_fonctionpub_min', 'comp_fonctionpub_max',
163 'comp_ong_min', 'comp_ong_max',
164 'comp_autre_min', 'comp_autre_max',
165 'justification',
166 )
167 widgets = dict(appel=forms.RadioSelect(),
168 nom=forms.TextInput(attrs={'size': 60},),
169 date_debut=admin_widgets.AdminDateWidget(),
170 date_fin=admin_widgets.AdminDateWidget(),
171 justification=forms.Textarea(attrs={'cols': 80},),
172 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
173 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
174 )
175
176 responsable=AutoCompleteSelectField('responsables', required=True)
177 #responsable = forms.ModelChoiceField(
178 # queryset=rh.Poste.objects.select_related(depth=1))
179
180 # La liste des choix est laissée vide. Voir __init__ pour la raison.
181 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
182 choices=(), required=False)
183
184 valeur_point_min = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
185 valeur_point_max = forms.ModelChoiceField(queryset=rh.ValeurPoint.actuelles.all(), required=False)
186
187 def __init__(self, *args, **kwargs):
188 """ Mise à jour dynamique du contenu du menu des postes.
189
190 Si on ne met le menu à jour de cette façon, à chaque instantiation du
191 formulaire, son contenu est mis en cache par le système et il ne
192 reflète pas les changements apportés par les ajouts, modifications,
193 etc...
194
195 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
196 car le "id" de chaque choix est spécial (voir _poste_choices).
197
198 """
199 request = kwargs.pop('request')
200 super(PosteForm, self).__init__(*args, **kwargs)
201 self.fields['poste'].choices = self._poste_choices(request)
202 self.fields['implantation'].choices = _implantation_choices(self, request)
203
204 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
205 if self.instance and self.instance.id is None:
206 dossiers = self.instance.get_dossiers()
207 if len(dossiers) > 0:
208 self.initial['service'] = dossiers[0].service_id
209 self.initial['nom'] = "%s %s" % (self.initial['nom'], self.instance.get_complement_nom())
210
211
212 def _poste_choices(self, request):
213 """ Menu déroulant pour les postes.
214
215 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
216 d'équivalent dans dae.
217
218 """
219 dae_ = dae.Poste.objects.ma_region_ou_service(request.user).filter(actif=True, id_rh__isnull=True)
220 copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True)
221 id_copies = [p.id_rh_id for p in copies.all()]
222 rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).filter(actif=True).exclude(id__in=id_copies)
223 # Optimisation de la requête
224 rhv1 = rhv1.select_related(depth=1)
225
226 return [('', 'Nouveau poste')] + \
227 sorted([('dae-%s' % p.id, label_poste_display(p)) for p in dae_ | copies] +
228 [('rh-%s' % p.id, label_poste_display(p)) for p in rhv1],
229 key=lambda t: t[1])
230
231 def clean(self):
232 """
233 Validation conditionnelles de certains champs.
234 """
235 cleaned_data = self.cleaned_data
236
237 # Gestion de la mise à disposition
238 mise_a_disposition = cleaned_data.get("mise_a_disposition")
239 valeur_point_min = cleaned_data.get("valeur_point_min")
240 valeur_point_max = cleaned_data.get("valeur_point_max")
241 if mise_a_disposition is False and (valeur_point_min is None or valeur_point_max is None):
242 msg = u"Ce champ est obligatoire."
243 self._errors["valeur_point_min"] = self.error_class([msg])
244 self._errors["valeur_point_max"] = self.error_class([msg])
245 raise forms.ValidationError("Les valeurs de point sont vides")
246
247 return cleaned_data
248
249
250
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
260
261 class 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
269 def __init__(self, request=None, *args, **kwargs):
270 super(ChoosePosteForm, self).__init__(*args, **kwargs)
271 self.fields['poste'].choices = self._poste_choices(request)
272
273 def _poste_choices(self, request):
274 """ Menu déroulant pour les postes. """
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)
277 id_copies = [p.id_rh_id for p in copies.all()]
278
279 return [('', '----------')] + \
280 sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies],
281 key=lambda t: t[1])
282
283
284 class 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
293 def __init__(self, *args, **kwargs):
294 """ Mise à jour dynamique du contenu du menu des employés. """
295 request = kwargs.pop('request', None)
296 super(EmployeForm, self).__init__(*args, **kwargs)
297 self.fields['employe'].choices = _employe_choices(self, request)
298
299
300
301 class DossierForm(forms.ModelForm):
302 """ Formulaire des dossiers. """
303 class Meta:
304 exclude= ('etat', )
305 model = dae.Dossier
306 widgets = dict(statut_residence=forms.RadioSelect(),
307 contrat_date_debut=admin_widgets.AdminDateWidget(),
308 contrat_date_fin=admin_widgets.AdminDateWidget(),
309 )
310
311 class PosteWorkflowForm(WorkflowFormMixin):
312
313 class Meta:
314 fields = ('etat', )
315 model = dae.Poste
316
317 class DossierWorkflowForm(WorkflowFormMixin):
318
319 class Meta:
320 fields = ('etat', )
321 model = dae.Dossier