1 # -*- encoding: utf-8 -*-
4 from ordereddict
import OrderedDict
5 from dateutil
.relativedelta
import relativedelta
6 from django
import forms
7 from django
.core
.urlresolvers
import reverse
8 from django
.core
.exceptions
import MultipleObjectsReturned
9 from django
.forms
.formsets
import TOTAL_FORM_COUNT
10 from django
.forms
.models
import BaseInlineFormSet
11 from django
.forms
.models
import (
12 inlineformset_factory
,
16 from django
.db
.models
import Q
, Max
, Count
17 from django
.shortcuts
import redirect
18 from django
.contrib
.admin
import widgets
as admin_widgets
20 from ajax_select
.fields
import AutoCompleteSelectField
22 from auf
.django
.references
import models
as ref
23 from auf
.django
.workflow
.forms
import WorkflowFormMixin
24 from auf
.django
.workflow
.models
import WorkflowCommentaire
26 from project
import groups
27 from project
.rh
import models
as rh
28 from project
.dae
import models
as dae
29 from .widgets
import ReadOnlyChoiceWidget
, ReadOnlyWidget
30 from project
.dae
.workflow
import POSTE_ETATS_BOUTONS
, POSTE_ETAT_FINALISE
33 def filtered_archived_fields_form_factory(*fields
):
35 Retourne un model form
37 class FilterArchivedFields(object):
38 def __init__(self
, *a
, **kw
):
39 super(FilterArchivedFields
, self
).__init__(*a
, **kw
)
41 self
.fields
[f
].queryset
= (
42 self
.fields
[f
]._queryset
.filter(archive
=False)
44 return FilterArchivedFields
47 class BaseInlineFormSetWithInitial(BaseInlineFormSet
):
49 Cette classe permet de fournir l'option initial aux inlineformsets.
50 Elle devient désuette en django 1.4.
52 def __init__(self
, data
=None, files
=None, instance
=None,
53 save_as_new
=False, prefix
=None, queryset
=None, **kwargs
):
55 self
.initial_extra
= kwargs
.pop('initial', None)
57 from django
.db
.models
.fields
.related
import RelatedObject
59 self
.instance
= self
.fk
.rel
.to()
61 self
.instance
= instance
62 self
.save_as_new
= save_as_new
63 # is there a better way to get the object descriptor?
64 self
.rel_name
= RelatedObject(self
.fk
.rel
.to
, self
.model
, self
.fk
).get_accessor_name()
66 queryset
= self
.model
._default_manager
67 qs
= queryset
.filter(**{self
.fk
.name
: self
.instance
})
68 super(BaseInlineFormSetWithInitial
, self
).__init__(data
, files
, prefix
=prefix
,
69 queryset
=qs
, **kwargs
)
71 def _construct_form(self
, i
, **kwargs
):
72 if self
.is_bound
and i
< self
.initial_form_count():
73 # Import goes here instead of module-level because importing
74 # django.db has side effects.
75 from django
.db
import connections
76 pk_key
= "%s-%s" % (self
.add_prefix(i
), self
.model
._meta
.pk
.name
)
77 pk
= self
.data
[pk_key
]
78 pk_field
= self
.model
._meta
.pk
79 pk
= pk_field
.get_db_prep_lookup('exact', pk
,
80 connection
=connections
[self
.get_queryset().db
])
81 if isinstance(pk
, list):
83 kwargs
['instance'] = self
._existing_object(pk
)
84 if i
< self
.initial_form_count() and not kwargs
.get('instance'):
85 kwargs
['instance'] = self
.get_queryset()[i
]
86 if i
>= self
.initial_form_count() and self
.initial_extra
:
87 # Set initial values for extra forms
89 kwargs
['initial'] = self
.initial_extra
[i
-self
.initial_form_count()]
93 defaults
= {'auto_id': self
.auto_id
, 'prefix': self
.add_prefix(i
)}
95 defaults
['data'] = self
.data
96 defaults
['files'] = self
.files
99 defaults
['initial'] = self
.initial
[i
]
102 # Allow extra forms to be empty.
103 if i
>= self
.initial_form_count():
104 defaults
['empty_permitted'] = True
105 defaults
.update(kwargs
)
106 form
= self
.form(**defaults
)
107 self
.add_fields(form
, i
)
111 def _implantation_choices(obj
, request
):
113 employe
= groups
.get_employe_from_user(request
.user
)
114 q
= Q(**{'zone_administrative__in': groups
.get_zones_from_user(request
.user
)})
117 user_groupes
= [g
.name
for g
in request
.user
.groups
.all()]
118 if groups
.DRH_NIVEAU_1
in user_groupes
or \
119 groups
.DRH_NIVEAU_2
in user_groupes
:
121 return [('', '----------')] + \
122 [(i
.id, unicode(i
), )for i
in ref
.Implantation
.objects
.filter(q
)]
125 def _employe_choices(obj
, request
):
127 employe
= groups
.get_employe_from_user(request
.user
)
128 q_dae_region_service
= Q(
129 poste__implantation__zone_administrative__in
=(
130 groups
.get_zones_from_user(request
.user
)
133 q_rh_region_service
= Q(
134 poste__implantation__zone_administrative__in
=(
135 groups
.get_zones_from_user(request
.user
)
139 user_groupes
= [g
.name
for g
in request
.user
.groups
.all()]
140 if groups
.DRH_NIVEAU_1
in user_groupes
or \
141 groups
.DRH_NIVEAU_2
in user_groupes
:
142 q_dae_region_service
= Q()
143 q_rh_region_service
= Q()
145 # On filtre les employes avec les droits régionaux et on s'assure que
146 # c'est bien le dernier dossier en date pour sortir l'employe. On retient
147 # un employé qui travaille présentement dans la même région que le user
149 dossiers_regionaux_ids
= [
150 d
.id for d
in dae
.Dossier
.objects
.filter(q_dae_region_service
)
154 for d
in dae
.Dossier
.objects
156 .annotate(dernier_dossier
=Max('id'))
157 if d
['dernier_dossier'] in dossiers_regionaux_ids
159 dae_employe
= dae
.Employe
.objects
.filter(id__in
=employes_ids
)
160 dae_
= dae_employe
.filter(id_rh__isnull
=True)
161 copies
= dae_employe
.filter(Q(id_rh__isnull
=False))
162 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
164 dossiers_regionaux_ids
= [
165 d
.id for d
in rh
.Dossier
.objects
.filter(q_rh_region_service
)
169 for d
in rh
.Dossier
.objects
171 .annotate(dernier_dossier
=Max('id'))
172 if d
['dernier_dossier'] in dossiers_regionaux_ids
174 rhv1
= rh
.Employe
.objects \
175 .filter(id__in
=employes_ids
) \
176 .exclude(id__in
=id_copies
)
178 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont
179 # pas de Dossier associés
180 employes_avec_dae
= [d
.employe_id
for d
in dae
.Dossier
.objects
.all()]
181 employes_orphelins
= dae
.Employe
.objects
.exclude(id__in
=employes_avec_dae
)
183 def option_label(employe
, extra
=""):
185 extra
= " [%s]" % extra
186 return "%s %s %s" % (employe
.nom
.upper(), employe
.prenom
.title(), extra
)
188 lbl_rh
= sorted([('rh-%s' % p
.id, option_label(p
, "existant dans rh")) for p
in rhv1
],
190 lbl_dae
= sorted([('dae-%s' % p
.id, option_label(p
)) for p
in dae_ | copies | employes_orphelins
],
192 return [('', 'Nouvel employé')] + lbl_rh
+ lbl_dae
195 def label_poste_display(poste
):
196 """Formate un visuel pour un poste dans une liste déroulante"""
199 annee
= poste
.date_debut
.year
202 label
= u
"%s (%s) %s [%s]" % (
204 poste
.implantation
.nom_court
,
206 #poste.type_poste.categorie_emploi.nom,
212 PostePieceFormSet
= inlineformset_factory(dae
.Poste
, dae
.PostePiece
,)
213 DossierPieceForm
= inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)
215 # Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
217 FinancementFormSetInitial
= inlineformset_factory(
219 dae
.PosteFinancement
,
220 formset
=BaseInlineFormSetWithInitial
,
223 FinancementFormSet
= inlineformset_factory(
225 dae
.PosteFinancement
,
230 class DossierComparaisonForm(
231 filtered_archived_fields_form_factory(
236 recherche
= AutoCompleteSelectField('dossiers', required
=False)
237 poste
= forms
.CharField(
238 max_length
=255, widget
=forms
.TextInput(attrs
={'size': '60'})
240 cmp_dossier
= forms
.IntegerField(
241 widget
=forms
.widgets
.HiddenInput
,
246 model
= dae
.DossierComparaison
247 exclude
= ('dossier',)
249 DossierComparaisonFormSet
= modelformset_factory(
250 dae
.DossierComparaison
, extra
=3, max_num
=3,
251 form
=DossierComparaisonForm
, can_delete
=True,
255 class PosteComparaisonForm(
256 filtered_archived_fields_form_factory('classement'),
259 recherche
= AutoCompleteSelectField('dae_postes', required
=False)
261 cmp_poste
= forms
.IntegerField(
262 widget
=forms
.widgets
.HiddenInput
,
267 model
= dae
.PosteComparaison
270 # Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
272 PosteComparaisonFormSetInitial
= inlineformset_factory(
274 dae
.PosteComparaison
,
277 form
=PosteComparaisonForm
,
278 formset
=BaseInlineFormSetWithInitial
,
280 PosteComparaisonFormSet
= inlineformset_factory(
282 dae
.PosteComparaison
,
285 form
=PosteComparaisonForm
,
289 class FlexibleRemunForm(
290 filtered_archived_fields_form_factory(
294 # Utilisé dans templats.
295 montant_mensuel
= forms
.DecimalField(required
=False)
296 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
299 model
= dae
.Remuneration
301 def __init__(self
, *a
, **kw
):
302 super(FlexibleRemunForm
, self
).__init__(*a
, **kw
)
303 # self.fields['type'].widget = ReadOnlyChoiceWidget(choices=self.fields['type'].choices)
305 def clean_devise(self
):
306 devise
= self
.cleaned_data
['devise']
307 if devise
.code
== 'EUR':
309 implantation
= ref
.Implantation
.objects
.get(
310 id=self
.data
['implantation']
312 liste_taux
= devise
.tauxchange_set
.order_by('-annee')
313 if len(liste_taux
) == 0:
314 raise forms
.ValidationError(
315 u
"La devise %s n'a pas de taux pour l'implantation %s" %
316 (devise
, implantation
)
321 def has_changed(self
):
323 Modification de has_changed pour qu'il ignore les montant a 0
327 changed_data
= self
.changed_data
329 # Type is set in hidden fields, it shouldn't be changed by the
330 # user; ignore when checking if data has changed.
331 if 'type' in changed_data
:
332 changed_data
.pop(changed_data
.index('type'))
334 # Montant is set to 0 in javascript, ifnore 'montant' data if
337 # Generer le key tel qu'identifié dans self.data:
338 montant_key
= '-'.join((self
.prefix
, 'montant'))
340 if ('montant' in changed_data
and
341 self
.data
.get(montant_key
, '0') == '0'):
342 changed_data
.pop(changed_data
.index('montant'))
344 return bool(changed_data
)
347 class ReadOnlyRemunForm(FlexibleRemunForm
):
348 # Utilisé dans templats.
350 def __init__(self
, *a
, **kw
):
351 super (ReadOnlyRemunForm
, self
).__init__(*a
, **kw
)
352 for field
in self
.fields
:
353 field
= self
.fields
[field
]
354 if not isinstance(field
.widget
, (
355 forms
.widgets
.HiddenInput
,
356 forms
.widgets
.Select
)):
357 field
.widget
= ReadOnlyWidget()
358 elif isinstance(field
.widget
, forms
.widgets
.Select
):
359 field
.widget
= ReadOnlyChoiceWidget(choices
=field
.choices
)
362 class GroupedInlineFormset(BaseInlineFormSet
):
364 def total_form_count(self
):
365 """Returns the total number of forms in this FormSet."""
367 return self
.management_form
.cleaned_data
[TOTAL_FORM_COUNT
]
369 i_count
= self
.initial_form_count()
370 return i_count
+ self
.extra
* len(self
._groups
)
375 choice_overrides
=[]):
378 # Create pre-defined groups.
379 self
.groups
= OrderedDict()
381 self
.groups
[group
[0]] = {
387 # Assign each form to a group.
389 for form
in self
.forms
:
390 if bool(form
.initial
):
391 grp
= group_accessor(form
)
392 if grp
[0] not in self
.groups
:
393 self
.groups
[grp
[0]] = {
398 self
.groups
[grp
[0]]['forms'].append(form
)
400 ungrouped_forms
.append(form
)
403 # Distribuer les extras de django dans les groupes, et ajouter
404 # des extras pour les groupes en nécessitant.
405 f_count
= len(self
.forms
)
406 for g
in self
.groups
:
407 for i
in xrange(f_count
, f_count
+ self
.extra
):
408 if len(ungrouped_forms
) == 0:
411 if len(ungrouped_forms
) > 0:
412 new_form
= ungrouped_forms
.pop()
414 new_form
= self
._construct_form(i
)
415 self
.forms
.append(new_form
)
417 self
.groups
[g
]['forms'].append(new_form
)
420 # Override form choices with the data provided in
422 for key
in choice_overrides
:
423 for form
in self
.groups
.get(key
, {'forms': []})['forms']:
424 for field_key
in choice_overrides
[key
]:
425 form
.fields
[field_key
].choices
= choice_overrides
[
429 # Create an iterable for easier access in template.
430 self
.group_list
= self
.groups
.values()
433 def remun_formset_factory(parent_model
,
435 form
=forms
.ModelForm
,
436 formset
=GroupedInlineFormset
,
445 formfield_callback
=None,
447 choice_overrides
=[]):
448 trs
= rh
.TypeRemuneration
.objects
.all()
449 # extra = max_num = trs.count()
450 fk
= _get_foreign_key(parent_model
, model
, fk_name
=fk_name
)
451 # enforce a max_num=1 when the foreign key to the parent model is unique.
456 'formfield_callback': formfield_callback
,
459 'can_delete': can_delete
,
460 'can_order': can_order
,
465 FormSet
= modelformset_factory(model
, **kwargs
)
467 FormSet
.read_only
= read_only
470 rtype
= form
.initial
['type']
471 if not isinstance(rtype
, rh
.TypeRemuneration
):
472 rtype
= rh
.TypeRemuneration
.objects
.get(id=rtype
)
473 return (rtype
.nature_remuneration
,
474 rtype
.nature_remuneration
477 def __init__(inst
, *a
, **kw
):
478 inst
._groups
= groups
479 super(inst
.__class__
, inst
).__init__(*a
, **kw
)
480 inst
.set_groups(groups
, grouper
, choice_overrides
)
482 FormSet
.__init__
= __init__
487 def remun_formset_factory_factory(
489 parent_model
=dae
.Dossier
,
490 model
=dae
.Remuneration
,
491 exclude_archived
=False):
493 Don't we love factory factories?
496 null_choice
= ('', '-' * 10)
497 extras
= 2 if not read_only
else 0
498 can_delete
= False if read_only
else True
499 form_class
= ReadOnlyRemunForm
if read_only
else FlexibleRemunForm
501 choice_override_extra_q
= {}
504 choice_override_extra_q
.update({
508 return remun_formset_factory(
513 can_delete
=can_delete
,
515 groups
= rh
.NATURE_REMUNERATION_CHOICES
,
518 'type': [null_choice
] + list(
519 rh
.TypeRemuneration
.objects
.filter(
520 nature_remuneration
=u
'Traitement',
521 **choice_override_extra_q
).values_list(
526 'type': [null_choice
] + list(
527 rh
.TypeRemuneration
.objects
.filter(
528 nature_remuneration
=u
'Indemnité',
529 **choice_override_extra_q
).values_list(
534 'type': [null_choice
] + list(
535 rh
.TypeRemuneration
.objects
.filter(
536 nature_remuneration
=u
'Charges',
537 **choice_override_extra_q
).values_list(
542 'type': [null_choice
] + list(
543 rh
.TypeRemuneration
.objects
.filter(
544 nature_remuneration
=u
'Accessoire',
545 **choice_override_extra_q
).values_list(
550 'type': [null_choice
] + list(
551 rh
.TypeRemuneration
.objects
.filter(
552 nature_remuneration
=u
'RAS',
553 **choice_override_extra_q
).values_list(
561 RemunForm
= remun_formset_factory_factory(
563 parent_model
=dae
.Dossier
,
564 model
=dae
.Remuneration
,
565 exclude_archived
=True,
568 ReadOnlyRemunFormSet
= remun_formset_factory_factory(
570 parent_model
=dae
.Dossier
,
571 model
=dae
.Remuneration
,
574 PosteCompReadOnlyRemunFormSet
= remun_formset_factory_factory(
576 parent_model
=dae
.PosteComparaison
,
577 model
=dae
.PosteComparaisonRemuneration
,
580 DossierCompReadOnlyRemunFormSet
= remun_formset_factory_factory(
582 parent_model
=dae
.DossierComparaison
,
583 model
=dae
.DossierComparaisonRemuneration
,
585 RHReadOnlyRemunFormSet
= remun_formset_factory_factory(
587 parent_model
=rh
.Dossier
,
588 model
=rh
.Remuneration
,
591 class PosteForm(filtered_archived_fields_form_factory(
595 """ Formulaire des postes. """
597 # On ne propose que les services actifs
598 service
= forms
.ModelChoiceField(
599 queryset
=rh
.Service
.objects
.all(), required
=True
602 responsable
= AutoCompleteSelectField('responsables', required
=True)
603 #responsable = forms.ModelChoiceField(
604 # queryset=rh.Poste.objects.select_related(depth=1))
606 # La liste des choix est laissée vide. Voir __init__ pour la raison.
607 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
608 choices
=(), required
=False)
610 valeur_point_min
= forms
.ModelChoiceField(
611 queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False
613 valeur_point_max
= forms
.ModelChoiceField(
614 queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False
619 fields
= ('type_intervention',
620 'poste', 'implantation', 'type_poste', 'service', 'nom',
621 'responsable', 'local', 'expatrie', 'mise_a_disposition',
622 'appel', 'date_debut', 'date_fin',
623 'regime_travail', 'regime_travail_nb_heure_semaine',
624 'classement_min', 'classement_max',
625 'valeur_point_min', 'valeur_point_max',
626 'devise_min', 'devise_max',
627 'salaire_min', 'salaire_max',
628 'indemn_expat_min', 'indemn_expat_max',
629 'indemn_fct_min', 'indemn_fct_max',
630 'charges_patronales_min', 'charges_patronales_max',
631 'autre_min', 'autre_max', 'devise_comparaison',
632 'comp_locale_min', 'comp_locale_max',
633 'comp_universite_min', 'comp_universite_max',
634 'comp_fonctionpub_min', 'comp_fonctionpub_max',
635 'comp_ong_min', 'comp_ong_max',
636 'comp_autre_min', 'comp_autre_max',
639 widgets
= dict(type_intervention
=forms
.RadioSelect(),
640 appel
=forms
.RadioSelect(),
641 nom
=forms
.TextInput(attrs
={'size': 60},),
642 date_debut
=admin_widgets
.AdminDateWidget(),
643 date_fin
=admin_widgets
.AdminDateWidget(),
644 justification
=forms
.Textarea(attrs
={'cols': 80},),
645 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
646 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
649 def __init__(self
, *args
, **kwargs
):
650 """ Mise à jour dynamique du contenu du menu des postes.
652 Si on ne met le menu à jour de cette façon, à chaque instantiation du
653 formulaire, son contenu est mis en cache par le système et il ne
654 reflète pas les changements apportés par les ajouts, modifications,
657 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
658 car le "id" de chaque choix est spécial (voir _poste_choices).
661 request
= kwargs
.pop('request')
662 super(PosteForm
, self
).__init__(*args
, **kwargs
)
663 self
.fields
['poste'].choices
= self
._poste_choices(request
)
665 self
.fields
['implantation'].choices
= \
666 _implantation_choices(self
, request
)
668 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
669 if self
.instance
and self
.instance
.id is None:
670 dossiers
= self
.instance
.get_dossiers()
671 if len(dossiers
) > 0:
672 self
.initial
['service'] = dossiers
[0].poste
.service
674 def _poste_choices(self
, request
):
675 """ Menu déroulant pour les postes.
676 Constitué des postes de RH
678 postes_rh
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).all()
679 postes_rh
= postes_rh
.select_related(depth
=1)
681 return [('', 'Nouveau poste')] + \
682 sorted([('rh-%s' % p
.id, label_poste_display(p
)) for p
in
688 Validation conditionnelles de certains champs.
690 cleaned_data
= self
.cleaned_data
692 if cleaned_data
.get("local") is False \
693 and cleaned_data
.get("expatrie") is False:
694 msg
= "Le poste doit au moins être ouvert localement " \
696 self
._errors
["local"] = self
.error_class([msg
])
697 self
._errors
["expatrie"] = ''
698 raise forms
.ValidationError(msg
)
703 class ChoosePosteForm(forms
.Form
):
707 # La liste des choix est laissée vide. Voir PosteForm.__init__.
708 postes_dae
= forms
.ChoiceField(choices
=(), required
=False)
709 postes_rh
= forms
.ChoiceField(choices
=(), required
=False)
711 def __init__(self
, request
=None, *args
, **kwargs
):
712 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
713 self
.fields
['postes_dae'].choices
= self
._poste_dae_choices(request
)
714 self
.fields
['postes_rh'].choices
= self
._poste_rh_choices(request
)
716 def _poste_dae_choices(self
, request
):
717 """ Menu déroulant pour les postes."""
718 postes_dae
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
) \
719 .exclude(etat__in
=(POSTE_ETAT_FINALISE
, )) \
720 .annotate(num_dae
=Count('dae_dossiers')) \
722 .order_by('implantation', '-date_debut', )
724 return [('', '----------')] + \
725 [('dae-%s' % p
.id, label_poste_display(p
)) for p
in postes_dae
]
727 def _poste_rh_choices(self
, request
):
728 """ Menu déroulant pour les postes."""
729 postes_dae
= dae
.Poste
.objects
.exclude(etat__in
=(POSTE_ETAT_FINALISE
, ))
730 today
= datetime
.date
.today()
731 id_poste_dae_commences
= [p
.id_rh_id
for p
in postes_dae
if p
.id_rh
is not None]
732 postes_rh
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
) \
733 .exclude(id__in
=id_poste_dae_commences
) \
734 .filter(Q(date_debut__lte
=today
) &
735 (Q(date_fin__gte
=today
) |
736 Q(date_fin__isnull
=True))
738 .order_by('implantation', '-date_debut', )
740 return [('', '----------')] + \
741 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in postes_rh
]
744 cleaned_data
= super(ChoosePosteForm
, self
).clean()
745 postes_dae
= cleaned_data
.get("postes_dae")
746 postes_rh
= cleaned_data
.get("postes_rh")
747 if (postes_dae
is u
"" and postes_rh
is u
"") or \
748 (postes_dae
is not u
"" and postes_rh
is not u
""):
749 raise forms
.ValidationError("Choisissez un poste DAE ou un poste RH")
753 poste_dae_key
= self
.cleaned_data
.get("postes_dae")
754 if poste_dae_key
is not u
"":
755 return redirect(reverse('embauche', args
=(poste_dae_key
,)))
756 poste_rh_key
= self
.cleaned_data
.get("postes_rh")
757 if poste_rh_key
is not u
"":
758 return redirect("%s?creer_dossier_dae='M'" % reverse('poste', args
=(poste_rh_key
,)))
760 class EmployeForm(forms
.ModelForm
):
761 """ Formulaire des employés. """
764 fields
= ('employe', 'nom', 'prenom', 'genre')
766 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
767 employe
= forms
.ChoiceField(choices
=(), required
=False)
769 def __init__(self
, *args
, **kwargs
):
770 """ Mise à jour dynamique du contenu du menu des employés. """
771 request
= kwargs
.pop('request', None)
772 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
773 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
777 filtered_archived_fields_form_factory(
779 'classement_anterieur',
780 'classement_titulaire_anterieur',
783 """ Formulaire des dossiers. """
786 exclude
= ('etat', 'employe', 'poste', 'date_debut',)
788 widgets
= dict(statut_residence
=forms
.RadioSelect(),
789 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
790 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
796 class PosteWorkflowForm(WorkflowFormMixin
):
797 bouton_libelles
= POSTE_ETATS_BOUTONS
803 def __init__(self
, *args
, **kwargs
):
804 super(PosteWorkflowForm
, self
).__init__(*args
, **kwargs
)
805 self
.fields
['etat'].help_text
= WF_HELP_TEXT
808 class DossierWorkflowForm(WorkflowFormMixin
):
809 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
815 def __init__(self
, *args
, **kwargs
):
816 super(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
817 self
.fields
['etat'].help_text
= WF_HELP_TEXT
818 self
._etat_initial
= self
.instance
.etat
821 super(DossierWorkflowForm
, self
).save()
822 poste
= self
.instance
.poste
824 if poste
.etat
== self
._etat_initial
:
825 poste
.etat
= self
.instance
.etat
828 # créer le commentaire automatique pour le poste associé
829 commentaire
= WorkflowCommentaire()
830 commentaire
.content_object
= poste
831 texte
= u
"Validation automatique à travers le dossier [%s] de %s\n%s" %(
834 self
.data
.get('commentaire', ''),
836 commentaire
.texte
= texte
837 commentaire
.etat_initial
= self
.instance
._etat_courant
838 commentaire
.etat_final
= self
.instance
.etat
839 commentaire
.owner
= self
.request
.user
843 class ContratForm(forms
.ModelForm
):
846 fields
= ('type_contrat', 'fichier', )
850 class DAENumeriseeForm(forms
.ModelForm
):
854 fields
= ('dae_numerisee',)
857 class DAEFinaliseesSearchForm(forms
.Form
):
859 label
='Recherche', required
=False,
860 widget
=forms
.TextInput(attrs
={'size': 40})
862 importees
= forms
.ChoiceField(
863 label
='Importation', required
=False, choices
=(
865 ('oui', 'DAE importées seulement'),
866 ('non', 'DAE non-importées seulement'),