On the way there...
[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
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 def __init__(self, *a, **kw):
313 super(FlexibleRemunForm, self).__init__(*a, **kw)
314 for field in self.fields:
315 field = self.fields[field]
316 if not isinstance(field.widget, (
317 forms.widgets.HiddenInput,
318 forms.widgets.Select)):
319 field.widget = ReadOnlyWidget()
320 elif isinstance(field.widget, forms.widgets.Select):
321 field.widget = ReadOnlyChoiceWidget(choices=field.choices)
322
323
324 class GroupedInlineFormset(BaseInlineFormSet):
325
326 def set_groups(self,
327 groups,
328 group_accessor,
329 choice_overrides=[]):
330
331
332 # Create pre-defined groups.
333 self.groups = OrderedDict()
334 for group in groups:
335 self.groups[group[0]] = {
336 'name': group[1],
337 'key': group[0],
338 'forms': [],
339 }
340
341 # Assign each form to a group.
342 for form in self.forms:
343 if bool(form.initial):
344 grp = group_accessor(form)
345 if grp[0] not in self.groups:
346 self.groups[grp[0]] = {
347 'name': grp[1],
348 'key': grp[0],
349 'forms': [],
350 }
351 self.groups[grp[0]]['forms'].append(form)
352
353 # Add extra forms (n extra for each grop).
354 tmp_extras = []
355 for i in xrange(len(self.groups) * self.extra):
356 tmp_extras.insert(0, self._construct_form(i))
357
358 for g in self.groups:
359 for i in xrange(self.extra):
360 self.groups[g]['forms'].append(tmp_extras.pop())
361
362
363 # Override form choices with the data provided in
364 # choice_overrides
365 for key in choice_overrides:
366 for form in self.groups.get(key, {'forms': []})['forms']:
367 for field_key in choice_overrides[key]:
368 form.fields[field_key].choices = choice_overrides[
369 key][field_key]
370
371
372 # Create an iterable for easier access in template.
373 self.group_list = self.groups.values()
374
375 # def set_groups(self, group_accessor, groups, group_order=[]):
376 # """
377 # group_accessor: A function that will get the key and name from
378 # each form.
379 # group_order: list the group keys here in a list and
380 # GroupedInlineFormset.groups will be ordered (ordereddict) by
381 # the key sequence provided here. Any missing key from the
382 # sequence will
383 # """
384
385 # # Build group list.
386 # self.groups = OrderedDict()
387 # temp_groups = {}
388 # # self.groups_and_forms = []
389 # for form in self.forms:
390 # group_key, group_name = group_accessor(form)
391 # if not temp_groups.has_key(group_key):
392 # temp_groups[group_key] = {
393 # 'name': group_name,
394 # 'key': group_key,
395 # 'forms': [],
396 # }
397 # temp_groups[group_key]['forms'].append(form)
398
399 # for order_key in group_order:
400 # if temp_groups.has_key(order_key):
401 # self.groups[order_key] = temp_groups.pop(order_key)
402
403 # for key in temp_groups:
404 # self.groups[key] = temp_groups[key]
405
406 # del temp_groups
407
408 # self.group_list = self.groups.values()
409
410
411 def remun_formset_factory(parent_model,
412 model,
413 form=forms.ModelForm,
414 formset=GroupedInlineFormset,
415 fk_name=None,
416 fields=None,
417 exclude=None,
418 can_order=False,
419 can_delete=True,
420 extra=2,
421 max_num=None,
422 formfield_callback=None,
423 groups=None,
424 choice_overrides=[]):
425 trs = rh.TypeRemuneration.objects.all()
426 # extra = max_num = trs.count()
427 fk = _get_foreign_key(parent_model, model, fk_name=fk_name)
428 # enforce a max_num=1 when the foreign key to the parent model is unique.
429 if fk.unique:
430 max_num = 1
431 kwargs = {
432 'form': form,
433 'formfield_callback': formfield_callback,
434 'formset': formset,
435 'extra': extra,
436 'can_delete': can_delete,
437 'can_order': can_order,
438 'fields': fields,
439 'exclude': exclude,
440 'max_num': max_num,
441 }
442 FormSet = modelformset_factory(model, **kwargs)
443 FormSet.fk = fk
444
445 def grouper(form):
446 rtype = form.initial['type']
447 if not isinstance(rtype, rh.TypeRemuneration):
448 rtype = rh.TypeRemuneration.objects.get(id=rtype)
449 return (rtype.nature_remuneration,
450 rtype.nature_remuneration
451 )
452
453 def __init__(inst, *a, **kw):
454 super(FormSet, inst).__init__(*a, **kw)
455
456 # Warning, this constructor can be improved and made more
457 # efficient.. it's a bit of a hack that will help creating a
458 # "grouped" formset that works inside Django. Although the
459 # code makes several iterations, I've timed it at 1ms to
460 # for it's execution.
461
462 # # Set initial data.
463 # d_inst = kw.get('instance', None)
464
465 # Set initial for all form type.
466 # for form, tr in zip(inst.forms, trs):
467 # form.initial['type'] = tr
468
469 # # Set form grouping.
470 inst.set_groups(groups, grouper, choice_overrides)
471
472 # # Create groups list of choices for type field.
473 # inst.group_type_choices = {}
474 # for group_key in inst.groups:
475 # inst.group_type_choices[group_key] = []
476
477 # # Add choices based on form.initial which have been set above.
478 # for form in inst.forms:
479 # inst.group_type_choices[
480 # form.initial['type'].nature_remuneration].append(
481 # (form.initial['type'].id, form.initial['type'].nom)
482 # )
483
484 # # Finally... change the choices of each form in the formset:
485 # for form in inst.forms:
486 # form.fields['type'].choices = inst.group_type_choices[
487 # form.initial['type'].nature_remuneration]
488
489
490 FormSet.__init__ = __init__
491
492 return FormSet
493
494
495 ReadOnlyRemunForm = remun_formset_factory(
496 dae.Dossier,
497 dae.Remuneration,
498 form=ReadOnlyRemunForm,
499 groups = [
500 (u'Traitement', u'Traitement',),
501 (u'Indemnité', u'Indemnité',),
502 (u'Charges', u'Charges',),
503 (u'Accessoire', u'Accessoire',),
504 ]
505 )
506
507 null_choice = ('', '-' * 10)
508 RemunForm = remun_formset_factory(
509 dae.Dossier,
510 dae.Remuneration,
511 form=FlexibleRemunForm,
512 groups = [
513 (u'Traitement', u'Traitement',),
514 (u'Indemnité', u'Indemnité',),
515 (u'Charges', u'Charges',),
516 (u'Accessoire', u'Accessoire',),
517 ],
518 choice_overrides = {
519 u'Traitement': {
520 'type': [null_choice] + list(
521 rh.TypeRemuneration.objects.filter(
522 nature_remuneration=u'Traitement').values_list(
523 'id', 'nom')
524 )
525 },
526 u'Indemnité': {
527 'type': [null_choice] + list(
528 rh.TypeRemuneration.objects.filter(
529 nature_remuneration=u'Indemnité').values_list(
530 'id', 'nom')
531 )
532 },
533 u'Charges': {
534 'type': [null_choice] + list(
535 rh.TypeRemuneration.objects.filter(
536 nature_remuneration=u'Charges').values_list(
537 'id', 'nom')
538 )
539 },
540 u'Accessoire': {
541 'type': [null_choice] + list(
542 rh.TypeRemuneration.objects.filter(
543 nature_remuneration=u'Accessoire').values_list(
544 'id', 'nom')
545 )
546 },
547 }
548 )
549
550 RemunForm = ReadOnlyRemunForm
551
552
553 class PosteForm(forms.ModelForm):
554 """ Formulaire des postes. """
555
556 # On ne propose que les services actifs
557 service = forms.ModelChoiceField(
558 queryset=rh.Service.objects.all(), required=True
559 )
560
561 responsable = AutoCompleteSelectField('responsables', required=True)
562 #responsable = forms.ModelChoiceField(
563 # queryset=rh.Poste.objects.select_related(depth=1))
564
565 # La liste des choix est laissée vide. Voir __init__ pour la raison.
566 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
567 choices=(), required=False)
568
569 valeur_point_min = forms.ModelChoiceField(
570 queryset=rh.ValeurPoint.actuelles.all(), required=False
571 )
572 valeur_point_max = forms.ModelChoiceField(
573 queryset=rh.ValeurPoint.actuelles.all(), required=False
574 )
575
576 class Meta:
577 model = dae.Poste
578 fields = ('type_intervention',
579 'poste', 'implantation', 'type_poste', 'service', 'nom',
580 'responsable', 'local', 'expatrie', 'mise_a_disposition',
581 'appel', 'date_debut', 'date_fin',
582 'regime_travail', 'regime_travail_nb_heure_semaine',
583 'classement_min', 'classement_max',
584 'valeur_point_min', 'valeur_point_max',
585 'devise_min', 'devise_max',
586 'salaire_min', 'salaire_max',
587 'indemn_expat_min', 'indemn_expat_max',
588 'indemn_fct_min', 'indemn_fct_max',
589 'charges_patronales_min', 'charges_patronales_max',
590 'autre_min', 'autre_max', 'devise_comparaison',
591 'comp_locale_min', 'comp_locale_max',
592 'comp_universite_min', 'comp_universite_max',
593 'comp_fonctionpub_min', 'comp_fonctionpub_max',
594 'comp_ong_min', 'comp_ong_max',
595 'comp_autre_min', 'comp_autre_max',
596 'justification',
597 )
598 widgets = dict(type_intervention=forms.RadioSelect(),
599 appel=forms.RadioSelect(),
600 nom=forms.TextInput(attrs={'size': 60},),
601 date_debut=admin_widgets.AdminDateWidget(),
602 date_fin=admin_widgets.AdminDateWidget(),
603 justification=forms.Textarea(attrs={'cols': 80},),
604 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
605 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
606 )
607
608 def __init__(self, *args, **kwargs):
609 """ Mise à jour dynamique du contenu du menu des postes.
610
611 Si on ne met le menu à jour de cette façon, à chaque instantiation du
612 formulaire, son contenu est mis en cache par le système et il ne
613 reflète pas les changements apportés par les ajouts, modifications,
614 etc...
615
616 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
617 car le "id" de chaque choix est spécial (voir _poste_choices).
618
619 """
620 request = kwargs.pop('request')
621 super(PosteForm, self).__init__(*args, **kwargs)
622 self.fields['poste'].choices = self._poste_choices(request)
623
624 self.fields['implantation'].choices = \
625 _implantation_choices(self, request)
626
627 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
628 if self.instance and self.instance.id is None:
629 dossiers = self.instance.get_dossiers()
630 if len(dossiers) > 0:
631 self.initial['service'] = dossiers[0].poste.service
632
633 def _poste_choices(self, request):
634 """ Menu déroulant pour les postes.
635 Constitué des postes de RH
636 """
637 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).all()
638 postes_rh = postes_rh.select_related(depth=1)
639
640 return [('', 'Nouveau poste')] + \
641 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
642 postes_rh],
643 key=lambda t: t[1])
644
645 def clean(self):
646 """
647 Validation conditionnelles de certains champs.
648 """
649 cleaned_data = self.cleaned_data
650
651 if cleaned_data.get("local") is False \
652 and cleaned_data.get("expatrie") is False:
653 msg = "Le poste doit au moins être ouvert localement " \
654 "ou aux expatriés"
655 self._errors["local"] = self.error_class([msg])
656 self._errors["expatrie"] = ''
657 raise forms.ValidationError(msg)
658
659 return cleaned_data
660
661
662 class ChoosePosteForm(forms.Form):
663 class Meta:
664 fields = ('poste',)
665
666 # La liste des choix est laissée vide. Voir PosteForm.__init__.
667 postes_dae = forms.ChoiceField(choices=(), required=False)
668 postes_rh = forms.ChoiceField(choices=(), required=False)
669
670 def __init__(self, request=None, *args, **kwargs):
671 super(ChoosePosteForm, self).__init__(*args, **kwargs)
672 self.fields['postes_dae'].choices = self._poste_dae_choices(request)
673 self.fields['postes_rh'].choices = self._poste_rh_choices(request)
674
675 def _poste_dae_choices(self, request):
676 """ Menu déroulant pour les postes."""
677 postes_dae = dae.Poste.objects.ma_region_ou_service(request.user) \
678 .exclude(etat__in=(POSTE_ETAT_FINALISE, )) \
679 .annotate(num_dae=Count('dae_dossiers')) \
680 .filter(num_dae=0) \
681 .order_by('implantation', '-date_debut', )
682
683 return [('', '----------')] + \
684 [('dae-%s' % p.id, label_poste_display(p)) for p in postes_dae]
685
686 def _poste_rh_choices(self, request):
687 """ Menu déroulant pour les postes."""
688 postes_dae = dae.Poste.objects.exclude(etat__in=(POSTE_ETAT_FINALISE, ))
689 today = datetime.date.today()
690 id_poste_dae_commences = [p.id_rh_id for p in postes_dae if p.id_rh is not None]
691 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user) \
692 .exclude(id__in=id_poste_dae_commences) \
693 .filter(Q(date_debut__lte=today) &
694 (Q(date_fin__gte=today) |
695 Q(date_fin__isnull=True))
696 ) \
697 .order_by('implantation', '-date_debut', )
698
699 return [('', '----------')] + \
700 [('rh-%s' % p.id, label_poste_display(p)) for p in postes_rh]
701
702 def clean(self):
703 cleaned_data = super(ChoosePosteForm, self).clean()
704 postes_dae = cleaned_data.get("postes_dae")
705 postes_rh = cleaned_data.get("postes_rh")
706 if (postes_dae is u"" and postes_rh is u"") or \
707 (postes_dae is not u"" and postes_rh is not u""):
708 raise forms.ValidationError("Choisissez un poste DAE ou un poste RH")
709 return cleaned_data
710
711 def redirect(self):
712 poste_dae_key = self.cleaned_data.get("postes_dae")
713 if poste_dae_key is not u"":
714 return redirect(reverse('embauche', args=(poste_dae_key,)))
715 poste_rh_key = self.cleaned_data.get("postes_rh")
716 if poste_rh_key is not u"":
717 return redirect("%s?creer_dossier_dae='M'" % reverse('poste', args=(poste_rh_key,)))
718
719 class EmployeForm(forms.ModelForm):
720 """ Formulaire des employés. """
721 class Meta:
722 model = dae.Employe
723 fields = ('employe', 'nom', 'prenom', 'genre')
724
725 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
726 employe = forms.ChoiceField(choices=(), required=False)
727
728 def __init__(self, *args, **kwargs):
729 """ Mise à jour dynamique du contenu du menu des employés. """
730 request = kwargs.pop('request', None)
731 super(EmployeForm, self).__init__(*args, **kwargs)
732 self.fields['employe'].choices = _employe_choices(self, request)
733
734
735 class DossierForm(forms.ModelForm):
736 """ Formulaire des dossiers. """
737 class Meta:
738 exclude = ('etat', 'employe', 'poste', 'date_debut',)
739 model = dae.Dossier
740 widgets = dict(statut_residence=forms.RadioSelect(),
741 contrat_date_debut=admin_widgets.AdminDateWidget(),
742 contrat_date_fin=admin_widgets.AdminDateWidget(),
743 )
744
745 WF_HELP_TEXT = ""
746
747
748 class PosteWorkflowForm(WorkflowFormMixin):
749 bouton_libelles = POSTE_ETATS_BOUTONS
750
751 class Meta:
752 fields = ('etat', )
753 model = dae.Poste
754
755 def __init__(self, *args, **kwargs):
756 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
757 self.fields['etat'].help_text = WF_HELP_TEXT
758
759
760 class DossierWorkflowForm(WorkflowFormMixin):
761 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
762
763 class Meta:
764 fields = ('etat', )
765 model = dae.Dossier
766
767 def __init__(self, *args, **kwargs):
768 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
769 self.fields['etat'].help_text = WF_HELP_TEXT
770 self._etat_initial = self.instance.etat
771
772 def save(self):
773 super(DossierWorkflowForm, self).save()
774 poste = self.instance.poste
775
776 # créer le commentaire automatique pour le poste associé
777 commentaire = WorkflowCommentaire()
778 commentaire.content_object = poste
779 texte = u"Validation automatique à travers le dossier [%s] de %s\n%s" %(
780 self.instance.id,
781 self.instance,
782 self.data.get('commentaire', ''),
783 )
784 commentaire.texte = texte
785 commentaire.etat_initial = self.instance._etat_courant
786 commentaire.etat_final = self.instance.etat
787 commentaire.owner = self.request.user
788 commentaire.save()
789
790 # force l'état du poste
791 poste.etat = self.instance.etat
792 poste.save()
793
794
795 class ContratForm(forms.ModelForm):
796
797 class Meta:
798 fields = ('type_contrat', 'fichier', )
799 model = dae.Contrat
800
801
802 class DAENumeriseeForm(forms.ModelForm):
803
804 class Meta:
805 model = dae.Dossier
806 fields = ('dae_numerisee',)
807
808
809 class DAEFinaliseesSearchForm(forms.Form):
810 q = forms.CharField(
811 label='Recherche', required=False,
812 widget=forms.TextInput(attrs={'size': 40})
813 )
814 importees = forms.ChoiceField(
815 label='Importation', required=False, choices=(
816 ('', ''),
817 ('oui', 'DAE importées seulement'),
818 ('non', 'DAE non-importées seulement'),
819 )
820 )