Done forms
[auf_rh_dae.git] / project / dae / forms.py
1 # -*- encoding: utf-8 -*-
2
3 import datetime
4 from ordereddict import OrderedDict
5 from django import forms
6 from django.core.urlresolvers import reverse
7 from django.core.exceptions import MultipleObjectsReturned
8 from django.forms.models import BaseInlineFormSet
9 from django.forms.models import (
10 inlineformset_factory,
11 modelformset_factory,
12 _get_foreign_key,
13 )
14 from django.db.models import Q, Max, Count
15 from django.shortcuts import redirect
16 from django.contrib.admin import widgets as admin_widgets
17
18 from ajax_select.fields import AutoCompleteSelectField
19
20 from auf.django.references import models as ref
21 from auf.django.workflow.forms import WorkflowFormMixin
22 from auf.django.workflow.models import WorkflowCommentaire
23
24 from project import groups
25 from project.rh import models as rh
26 from project.dae import models as dae
27 from .widgets import ReadOnlyChoiceWidget, ReadOnlyWidget
28 from project.dae.workflow import POSTE_ETATS_BOUTONS, POSTE_ETAT_FINALISE
29
30
31 class BaseInlineFormSetWithInitial(BaseInlineFormSet):
32 """
33 Cette classe permet de fournir l'option initial aux inlineformsets.
34 Elle devient désuette en django 1.4.
35 """
36 def __init__(self, data=None, files=None, instance=None,
37 save_as_new=False, prefix=None, queryset=None, **kwargs):
38
39 self.initial_extra = kwargs.pop('initial', None)
40
41 from django.db.models.fields.related import RelatedObject
42 if instance is None:
43 self.instance = self.fk.rel.to()
44 else:
45 self.instance = instance
46 self.save_as_new = save_as_new
47 # is there a better way to get the object descriptor?
48 self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
49 if queryset is None:
50 queryset = self.model._default_manager
51 qs = queryset.filter(**{self.fk.name: self.instance})
52 super(BaseInlineFormSetWithInitial, self).__init__(data, files, prefix=prefix,
53 queryset=qs, **kwargs)
54
55 def _construct_form(self, i, **kwargs):
56 if self.is_bound and i < self.initial_form_count():
57 # Import goes here instead of module-level because importing
58 # django.db has side effects.
59 from django.db import connections
60 pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
61 pk = self.data[pk_key]
62 pk_field = self.model._meta.pk
63 pk = pk_field.get_db_prep_lookup('exact', pk,
64 connection=connections[self.get_queryset().db])
65 if isinstance(pk, list):
66 pk = pk[0]
67 kwargs['instance'] = self._existing_object(pk)
68 if i < self.initial_form_count() and not kwargs.get('instance'):
69 kwargs['instance'] = self.get_queryset()[i]
70 if i >= self.initial_form_count() and self.initial_extra:
71 # Set initial values for extra forms
72 try:
73 kwargs['initial'] = self.initial_extra[i-self.initial_form_count()]
74 except IndexError:
75 pass
76
77 defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
78 if self.is_bound:
79 defaults['data'] = self.data
80 defaults['files'] = self.files
81 if self.initial:
82 try:
83 defaults['initial'] = self.initial[i]
84 except IndexError:
85 pass
86 # Allow extra forms to be empty.
87 if i >= self.initial_form_count():
88 defaults['empty_permitted'] = True
89 defaults.update(kwargs)
90 form = self.form(**defaults)
91 self.add_fields(form, i)
92 return form
93
94
95 def _implantation_choices(obj, request):
96 # TRAITEMENT NORMAL
97 employe = groups.get_employe_from_user(request.user)
98 q = Q(**{'zone_administrative': employe.implantation.zone_administrative})
99
100 # TRAITEMENT DRH
101 user_groupes = [g.name for g in request.user.groups.all()]
102 if groups.DRH_NIVEAU_1 in user_groupes or \
103 groups.DRH_NIVEAU_2 in user_groupes:
104 q = Q()
105 return [('', '----------')] + \
106 [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
107
108
109 def _employe_choices(obj, request):
110 # TRAITEMENT NORMAL
111 employe = groups.get_employe_from_user(request.user)
112 q_dae_region_service = Q(
113 poste__implantation__zone_administrative=(
114 employe.implantation.zone_administrative
115 )
116 )
117 q_rh_region_service = Q(
118 poste__implantation__zone_administrative=(
119 employe.implantation.zone_administrative
120 )
121 )
122 # TRAITEMENT DRH
123 user_groupes = [g.name for g in request.user.groups.all()]
124 if groups.DRH_NIVEAU_1 in user_groupes or \
125 groups.DRH_NIVEAU_2 in user_groupes:
126 q_dae_region_service = Q()
127 q_rh_region_service = Q()
128
129 # On filtre les employes avec les droits régionaux et on s'assure que
130 # c'est bien le dernier dossier en date pour sortir l'employe. On retient
131 # un employé qui travaille présentement dans la même région que le user
132 # connecté.
133 dossiers_regionaux_ids = [
134 d.id for d in dae.Dossier.objects.filter(q_dae_region_service)
135 ]
136 employes_ids = [
137 d['employe']
138 for d in dae.Dossier.objects
139 .values('employe')
140 .annotate(dernier_dossier=Max('id'))
141 if d['dernier_dossier'] in dossiers_regionaux_ids
142 ]
143 dae_employe = dae.Employe.objects.filter(id__in=employes_ids)
144 dae_ = dae_employe.filter(id_rh__isnull=True)
145 copies = dae_employe.filter(Q(id_rh__isnull=False))
146 id_copies = [p.id_rh_id for p in copies.all()]
147
148 dossiers_regionaux_ids = [
149 d.id for d in rh.Dossier.objects.filter(q_rh_region_service)
150 ]
151 employes_ids = [
152 d['employe']
153 for d in rh.Dossier.objects
154 .values('employe')
155 .annotate(dernier_dossier=Max('id'))
156 if d['dernier_dossier'] in dossiers_regionaux_ids
157 ]
158 rhv1 = rh.Employe.objects \
159 .filter(id__in=employes_ids) \
160 .exclude(id__in=id_copies)
161
162 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont
163 # pas de Dossier associés
164 employes_avec_dae = [d.employe_id for d in dae.Dossier.objects.all()]
165 employes_orphelins = dae.Employe.objects.exclude(id__in=employes_avec_dae)
166
167 def option_label(employe, extra=""):
168 if extra:
169 extra = " [%s]" % extra
170 return "%s %s %s" % (employe.nom.upper(), employe.prenom.title(), extra)
171
172 lbl_rh = sorted([('rh-%s' % p.id, option_label(p, "existant dans rh")) for p in rhv1],
173 key=lambda t: t[1])
174 lbl_dae = sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins],
175 key=lambda t: t[1])
176 return [('', 'Nouvel employé')] + lbl_rh + lbl_dae
177
178
179 def label_poste_display(poste):
180 """Formate un visuel pour un poste dans une liste déroulante"""
181 annee = ""
182 if poste.date_debut:
183 annee = poste.date_debut.year
184
185 nom = poste.nom
186 label = u"%s (%s) %s [%s]" % (
187 annee,
188 poste.implantation.nom_court,
189 nom,
190 #poste.type_poste.categorie_emploi.nom,
191 poste.id,
192 )
193 return label
194
195
196 PostePieceFormSet = inlineformset_factory(dae.Poste, dae.PostePiece,)
197 DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
198
199 # Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
200 # données de RH
201 FinancementFormSetInitial = inlineformset_factory(
202 dae.Poste,
203 dae.PosteFinancement,
204 formset=BaseInlineFormSetWithInitial,
205 extra=2
206 )
207 FinancementFormSet = inlineformset_factory(
208 dae.Poste,
209 dae.PosteFinancement,
210 extra=2
211 )
212
213
214 class DossierComparaisonForm(forms.ModelForm):
215
216 recherche = AutoCompleteSelectField('dossiers', required=False)
217 poste = forms.CharField(
218 max_length=255, widget=forms.TextInput(attrs={'size': '60'})
219 )
220
221 class Meta:
222 model = dae.DossierComparaison
223 exclude = ('dossier',)
224
225 DossierComparaisonFormSet = modelformset_factory(
226 dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
227 )
228
229
230 class PosteComparaisonForm(forms.ModelForm):
231
232 recherche = AutoCompleteSelectField('dae_postes', required=False)
233
234 class Meta:
235 model = dae.PosteComparaison
236 exclude = ('poste',)
237
238 # Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
239 # données de RH
240 PosteComparaisonFormSetInitial = inlineformset_factory(
241 dae.Poste,
242 dae.PosteComparaison,
243 extra=3,
244 max_num=3,
245 form=PosteComparaisonForm,
246 formset=BaseInlineFormSetWithInitial,
247 )
248 PosteComparaisonFormSet = inlineformset_factory(
249 dae.Poste,
250 dae.PosteComparaison,
251 extra=3,
252 max_num=3,
253 form=PosteComparaisonForm,
254 )
255
256
257 class FlexibleRemunForm(forms.ModelForm):
258 # Utilisé dans templats.
259 montant_mensuel = forms.DecimalField(required=False)
260 montant = forms.DecimalField(required=True, label='Montant annuel')
261
262 class Meta:
263 model = dae.Remuneration
264
265 def __init__(self, *a, **kw):
266 super(FlexibleRemunForm, self).__init__(*a, **kw)
267 # self.fields['type'].widget = ReadOnlyChoiceWidget(choices=self.fields['type'].choices)
268
269 def clean_devise(self):
270 devise = self.cleaned_data['devise']
271 if devise.code == 'EUR':
272 return devise
273 implantation = ref.Implantation.objects.get(
274 id=self.data['implantation']
275 )
276 liste_taux = devise.tauxchange_set.order_by('-annee')
277 if len(liste_taux) == 0:
278 raise forms.ValidationError(
279 u"La devise %s n'a pas de taux pour l'implantation %s" %
280 (devise, implantation)
281 )
282 else:
283 return devise
284
285 def has_changed(self):
286 """
287 Modification de has_changed pour qu'il ignore les montant a 0
288 et les 'types'.
289 """
290
291 changed_data = self.changed_data
292
293 # Type is set in hidden fields, it shouldn't be changed by the
294 # user; ignore when checking if data has changed.
295 if 'type' in changed_data:
296 changed_data.pop(changed_data.index('type'))
297
298 # Montant is set to 0 in javascript, ifnore 'montant' data if
299 # its value is 0.
300
301 # Generer le key tel qu'identifié dans self.data:
302 montant_key = '-'.join((self.prefix, 'montant'))
303
304 if ('montant' in changed_data and
305 self.data.get(montant_key, '0') == '0'):
306 changed_data.pop(changed_data.index('montant'))
307
308 return bool(changed_data)
309
310
311 class ReadOnlyRemunForm(FlexibleRemunForm):
312 # Utilisé dans templats.
313
314 def __init__(self, *a, **kw):
315 super (ReadOnlyRemunForm, self).__init__(*a, **kw)
316 for field in self.fields:
317 field = self.fields[field]
318 if not isinstance(field.widget, (
319 forms.widgets.HiddenInput,
320 forms.widgets.Select)):
321 field.widget = ReadOnlyWidget()
322 elif isinstance(field.widget, forms.widgets.Select):
323 field.widget = ReadOnlyChoiceWidget(choices=field.choices)
324
325
326 class GroupedInlineFormset(BaseInlineFormSet):
327
328 def set_groups(self,
329 groups,
330 group_accessor,
331 choice_overrides=[]):
332
333
334 # Create pre-defined groups.
335 self.groups = OrderedDict()
336 for group in groups:
337 self.groups[group[0]] = {
338 'name': group[1],
339 'key': group[0],
340 'forms': [],
341 }
342
343 # Assign each form to a group.
344 for form in self.forms:
345 if bool(form.initial):
346 grp = group_accessor(form)
347 if grp[0] not in self.groups:
348 self.groups[grp[0]] = {
349 'name': grp[1],
350 'key': grp[0],
351 'forms': [],
352 }
353 self.groups[grp[0]]['forms'].append(form)
354
355 # Add extra forms (n extra for each grop).
356 tmp_extras = []
357 for i in xrange(len(self.groups) * self.extra):
358 tmp_extras.insert(0,
359 self._construct_form(self.initial_form_count() + i))
360
361 for g in self.groups:
362 for i in xrange(self.extra):
363 self.groups[g]['forms'].append(tmp_extras.pop())
364
365
366 # Override form choices with the data provided in
367 # choice_overrides
368 for key in choice_overrides:
369 for form in self.groups.get(key, {'forms': []})['forms']:
370 for field_key in choice_overrides[key]:
371 form.fields[field_key].choices = choice_overrides[
372 key][field_key]
373
374
375 # Create an iterable for easier access in template.
376 self.group_list = self.groups.values()
377
378 # def set_groups(self, group_accessor, groups, group_order=[]):
379 # """
380 # group_accessor: A function that will get the key and name from
381 # each form.
382 # group_order: list the group keys here in a list and
383 # GroupedInlineFormset.groups will be ordered (ordereddict) by
384 # the key sequence provided here. Any missing key from the
385 # sequence will
386 # """
387
388 # # Build group list.
389 # self.groups = OrderedDict()
390 # temp_groups = {}
391 # # self.groups_and_forms = []
392 # for form in self.forms:
393 # group_key, group_name = group_accessor(form)
394 # if not temp_groups.has_key(group_key):
395 # temp_groups[group_key] = {
396 # 'name': group_name,
397 # 'key': group_key,
398 # 'forms': [],
399 # }
400 # temp_groups[group_key]['forms'].append(form)
401
402 # for order_key in group_order:
403 # if temp_groups.has_key(order_key):
404 # self.groups[order_key] = temp_groups.pop(order_key)
405
406 # for key in temp_groups:
407 # self.groups[key] = temp_groups[key]
408
409 # del temp_groups
410
411 # self.group_list = self.groups.values()
412
413
414 def remun_formset_factory(parent_model,
415 model,
416 form=forms.ModelForm,
417 formset=GroupedInlineFormset,
418 fk_name=None,
419 fields=None,
420 exclude=None,
421 can_order=False,
422 can_delete=True,
423 read_only=False,
424 extra=2,
425 max_num=None,
426 formfield_callback=None,
427 groups=None,
428 choice_overrides=[]):
429 trs = rh.TypeRemuneration.objects.all()
430 # extra = max_num = trs.count()
431 fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
432 # enforce a max_num=1 when the foreign key to the parent model is unique.
433 if fk.unique:
434 max_num = 1
435 kwargs = {
436 'form': form,
437 'formfield_callback': formfield_callback,
438 'formset': formset,
439 'extra': extra,
440 'can_delete': can_delete,
441 'can_order': can_order,
442 'fields': fields,
443 'exclude': exclude,
444 'max_num': max_num,
445 }
446 FormSet = modelformset_factory(model, **kwargs)
447 FormSet.fk = fk
448 FormSet.read_only = read_only
449
450 def grouper(form):
451 rtype = form.initial['type']
452 if not isinstance(rtype, rh.TypeRemuneration):
453 rtype = rh.TypeRemuneration.objects.get(id=rtype)
454 return (rtype.nature_remuneration,
455 rtype.nature_remuneration
456 )
457
458
459
460 # Monkey patch FormSet.
461 def __init__(inst, *a, **kw):
462 super(inst.__class__, inst).__init__(*a, **kw)
463 inst.set_groups(groups, grouper, choice_overrides)
464
465 FormSet.__init__ = __init__
466
467 return FormSet
468
469
470 def remun_formset_factory_factory(read_only=False):
471 """
472 Don't we love factory factories?
473 """
474
475 null_choice = ('', '-' * 10)
476 extras = 2 if not read_only else 0
477 can_delete = False if read_only else True
478 form_class = ReadOnlyRemunForm if read_only else FlexibleRemunForm
479
480 return remun_formset_factory(
481 dae.Dossier,
482 dae.Remuneration,
483 form=form_class,
484 extra=extras,
485 can_delete=can_delete,
486 read_only=read_only,
487 groups = [
488 (u'Traitement', u'Traitement',),
489 (u'Indemnité', u'Indemnité',),
490 (u'Charges', u'Charges',),
491 (u'Accessoire', u'Accessoire',),
492 (u'RAS', u'Rémunération autre source',),
493 ],
494 choice_overrides = {
495 u'Traitement': {
496 'type': [null_choice] + list(
497 rh.TypeRemuneration.objects.filter(
498 nature_remuneration=u'Traitement').values_list(
499 'id', 'nom')
500 )
501 },
502 u'Indemnité': {
503 'type': [null_choice] + list(
504 rh.TypeRemuneration.objects.filter(
505 nature_remuneration=u'Indemnité').values_list(
506 'id', 'nom')
507 )
508 },
509 u'Charges': {
510 'type': [null_choice] + list(
511 rh.TypeRemuneration.objects.filter(
512 nature_remuneration=u'Charges').values_list(
513 'id', 'nom')
514 )
515 },
516 u'Accessoire': {
517 'type': [null_choice] + list(
518 rh.TypeRemuneration.objects.filter(
519 nature_remuneration=u'Accessoire').values_list(
520 'id', 'nom')
521 )
522 },
523 u'RAS': {
524 'type': [null_choice] + list(
525 rh.TypeRemuneration.objects.filter(
526 nature_remuneration=u'RAS').values_list(
527 'id', 'nom')
528 )
529 },
530 }
531 )
532
533 RemunForm = remun_formset_factory_factory(read_only=False)
534 ReadOnlyRemunForm = remun_formset_factory_factory(read_only=True)
535
536
537 class PosteForm(forms.ModelForm):
538 """ Formulaire des postes. """
539
540 # On ne propose que les services actifs
541 service = forms.ModelChoiceField(
542 queryset=rh.Service.objects.all(), required=True
543 )
544
545 responsable = AutoCompleteSelectField('responsables', required=True)
546 #responsable = forms.ModelChoiceField(
547 # queryset=rh.Poste.objects.select_related(depth=1))
548
549 # La liste des choix est laissée vide. Voir __init__ pour la raison.
550 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
551 choices=(), required=False)
552
553 valeur_point_min = forms.ModelChoiceField(
554 queryset=rh.ValeurPoint.actuelles.all(), required=False
555 )
556 valeur_point_max = forms.ModelChoiceField(
557 queryset=rh.ValeurPoint.actuelles.all(), required=False
558 )
559
560 class Meta:
561 model = dae.Poste
562 fields = ('type_intervention',
563 'poste', 'implantation', 'type_poste', 'service', 'nom',
564 'responsable', 'local', 'expatrie', 'mise_a_disposition',
565 'appel', 'date_debut', 'date_fin',
566 'regime_travail', 'regime_travail_nb_heure_semaine',
567 'classement_min', 'classement_max',
568 'valeur_point_min', 'valeur_point_max',
569 'devise_min', 'devise_max',
570 'salaire_min', 'salaire_max',
571 'indemn_expat_min', 'indemn_expat_max',
572 'indemn_fct_min', 'indemn_fct_max',
573 'charges_patronales_min', 'charges_patronales_max',
574 'autre_min', 'autre_max', 'devise_comparaison',
575 'comp_locale_min', 'comp_locale_max',
576 'comp_universite_min', 'comp_universite_max',
577 'comp_fonctionpub_min', 'comp_fonctionpub_max',
578 'comp_ong_min', 'comp_ong_max',
579 'comp_autre_min', 'comp_autre_max',
580 'justification',
581 )
582 widgets = dict(type_intervention=forms.RadioSelect(),
583 appel=forms.RadioSelect(),
584 nom=forms.TextInput(attrs={'size': 60},),
585 date_debut=admin_widgets.AdminDateWidget(),
586 date_fin=admin_widgets.AdminDateWidget(),
587 justification=forms.Textarea(attrs={'cols': 80},),
588 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
589 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
590 )
591
592 def __init__(self, *args, **kwargs):
593 """ Mise à jour dynamique du contenu du menu des postes.
594
595 Si on ne met le menu à jour de cette façon, à chaque instantiation du
596 formulaire, son contenu est mis en cache par le système et il ne
597 reflète pas les changements apportés par les ajouts, modifications,
598 etc...
599
600 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
601 car le "id" de chaque choix est spécial (voir _poste_choices).
602
603 """
604 request = kwargs.pop('request')
605 super(PosteForm, self).__init__(*args, **kwargs)
606 self.fields['poste'].choices = self._poste_choices(request)
607
608 self.fields['implantation'].choices = \
609 _implantation_choices(self, request)
610
611 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
612 if self.instance and self.instance.id is None:
613 dossiers = self.instance.get_dossiers()
614 if len(dossiers) > 0:
615 self.initial['service'] = dossiers[0].poste.service
616
617 def _poste_choices(self, request):
618 """ Menu déroulant pour les postes.
619 Constitué des postes de RH
620 """
621 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).all()
622 postes_rh = postes_rh.select_related(depth=1)
623
624 return [('', 'Nouveau poste')] + \
625 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
626 postes_rh],
627 key=lambda t: t[1])
628
629 def clean(self):
630 """
631 Validation conditionnelles de certains champs.
632 """
633 cleaned_data = self.cleaned_data
634
635 if cleaned_data.get("local") is False \
636 and cleaned_data.get("expatrie") is False:
637 msg = "Le poste doit au moins être ouvert localement " \
638 "ou aux expatriés"
639 self._errors["local"] = self.error_class([msg])
640 self._errors["expatrie"] = ''
641 raise forms.ValidationError(msg)
642
643 return cleaned_data
644
645
646 class ChoosePosteForm(forms.Form):
647 class Meta:
648 fields = ('poste',)
649
650 # La liste des choix est laissée vide. Voir PosteForm.__init__.
651 postes_dae = forms.ChoiceField(choices=(), required=False)
652 postes_rh = forms.ChoiceField(choices=(), required=False)
653
654 def __init__(self, request=None, *args, **kwargs):
655 super(ChoosePosteForm, self).__init__(*args, **kwargs)
656 self.fields['postes_dae'].choices = self._poste_dae_choices(request)
657 self.fields['postes_rh'].choices = self._poste_rh_choices(request)
658
659 def _poste_dae_choices(self, request):
660 """ Menu déroulant pour les postes."""
661 postes_dae = dae.Poste.objects.ma_region_ou_service(request.user) \
662 .exclude(etat__in=(POSTE_ETAT_FINALISE, )) \
663 .annotate(num_dae=Count('dae_dossiers')) \
664 .filter(num_dae=0) \
665 .order_by('implantation', '-date_debut', )
666
667 return [('', '----------')] + \
668 [('dae-%s' % p.id, label_poste_display(p)) for p in postes_dae]
669
670 def _poste_rh_choices(self, request):
671 """ Menu déroulant pour les postes."""
672 postes_dae = dae.Poste.objects.exclude(etat__in=(POSTE_ETAT_FINALISE, ))
673 today = datetime.date.today()
674 id_poste_dae_commences = [p.id_rh_id for p in postes_dae if p.id_rh is not None]
675 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user) \
676 .exclude(id__in=id_poste_dae_commences) \
677 .filter(Q(date_debut__lte=today) &
678 (Q(date_fin__gte=today) |
679 Q(date_fin__isnull=True))
680 ) \
681 .order_by('implantation', '-date_debut', )
682
683 return [('', '----------')] + \
684 [('rh-%s' % p.id, label_poste_display(p)) for p in postes_rh]
685
686 def clean(self):
687 cleaned_data = super(ChoosePosteForm, self).clean()
688 postes_dae = cleaned_data.get("postes_dae")
689 postes_rh = cleaned_data.get("postes_rh")
690 if (postes_dae is u"" and postes_rh is u"") or \
691 (postes_dae is not u"" and postes_rh is not u""):
692 raise forms.ValidationError("Choisissez un poste DAE ou un poste RH")
693 return cleaned_data
694
695 def redirect(self):
696 poste_dae_key = self.cleaned_data.get("postes_dae")
697 if poste_dae_key is not u"":
698 return redirect(reverse('embauche', args=(poste_dae_key,)))
699 poste_rh_key = self.cleaned_data.get("postes_rh")
700 if poste_rh_key is not u"":
701 return redirect("%s?creer_dossier_dae='M'" % reverse('poste', args=(poste_rh_key,)))
702
703 class EmployeForm(forms.ModelForm):
704 """ Formulaire des employés. """
705 class Meta:
706 model = dae.Employe
707 fields = ('employe', 'nom', 'prenom', 'genre')
708
709 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
710 employe = forms.ChoiceField(choices=(), required=False)
711
712 def __init__(self, *args, **kwargs):
713 """ Mise à jour dynamique du contenu du menu des employés. """
714 request = kwargs.pop('request', None)
715 super(EmployeForm, self).__init__(*args, **kwargs)
716 self.fields['employe'].choices = _employe_choices(self, request)
717
718
719 class DossierForm(forms.ModelForm):
720 """ Formulaire des dossiers. """
721 class Meta:
722 exclude = ('etat', 'employe', 'poste', 'date_debut',)
723 model = dae.Dossier
724 widgets = dict(statut_residence=forms.RadioSelect(),
725 contrat_date_debut=admin_widgets.AdminDateWidget(),
726 contrat_date_fin=admin_widgets.AdminDateWidget(),
727 )
728
729 WF_HELP_TEXT = ""
730
731
732 class PosteWorkflowForm(WorkflowFormMixin):
733 bouton_libelles = POSTE_ETATS_BOUTONS
734
735 class Meta:
736 fields = ('etat', )
737 model = dae.Poste
738
739 def __init__(self, *args, **kwargs):
740 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
741 self.fields['etat'].help_text = WF_HELP_TEXT
742
743
744 class DossierWorkflowForm(WorkflowFormMixin):
745 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
746
747 class Meta:
748 fields = ('etat', )
749 model = dae.Dossier
750
751 def __init__(self, *args, **kwargs):
752 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
753 self.fields['etat'].help_text = WF_HELP_TEXT
754 self._etat_initial = self.instance.etat
755
756 def save(self):
757 super(DossierWorkflowForm, self).save()
758 poste = self.instance.poste
759
760 # créer le commentaire automatique pour le poste associé
761 commentaire = WorkflowCommentaire()
762 commentaire.content_object = poste
763 texte = u"Validation automatique à travers le dossier [%s] de %s\n%s" %(
764 self.instance.id,
765 self.instance,
766 self.data.get('commentaire', ''),
767 )
768 commentaire.texte = texte
769 commentaire.etat_initial = self.instance._etat_courant
770 commentaire.etat_final = self.instance.etat
771 commentaire.owner = self.request.user
772 commentaire.save()
773
774 # force l'état du poste
775 poste.etat = self.instance.etat
776 poste.save()
777
778
779 class ContratForm(forms.ModelForm):
780
781 class Meta:
782 fields = ('type_contrat', 'fichier', )
783 model = dae.Contrat
784
785
786 class DAENumeriseeForm(forms.ModelForm):
787
788 class Meta:
789 model = dae.Dossier
790 fields = ('dae_numerisee',)
791
792
793 class DAEFinaliseesSearchForm(forms.Form):
794 q = forms.CharField(
795 label='Recherche', required=False,
796 widget=forms.TextInput(attrs={'size': 40})
797 )
798 importees = forms.ChoiceField(
799 label='Importation', required=False, choices=(
800 ('', ''),
801 ('oui', 'DAE importées seulement'),
802 ('non', 'DAE non-importées seulement'),
803 )
804 )