Fix for selection using proper SELECT
[auf_rh_dae.git] / project / dae / forms.py
CommitLineData
5d680e84 1# -*- encoding: utf-8 -*-
ce110fb9 2
bed0c4c9 3import datetime
5a1f75cb 4from django import forms
80be36aa 5from django.core.urlresolvers import reverse
2e672700 6from django.forms.models import BaseInlineFormSet
5a1f75cb 7from django.forms.models import inlineformset_factory, modelformset_factory
80be36aa
OL
8from django.db.models import Q, Max, Count
9from django.shortcuts import redirect
10from django.contrib.admin import widgets as admin_widgets
5a1f75cb 11
75f0e87b
DB
12from ajax_select.fields import AutoCompleteSelectField
13
14from auf.django.references import models as ref
15from auf.django.workflow.forms import WorkflowFormMixin
66fefd2f 16from auf.django.workflow.models import WorkflowCommentaire
75f0e87b 17
3383b2d1 18from project import groups
17c90428 19from project.rh import models as rh
17c90428 20from project.dae import models as dae
34950f36 21from project.dae.workflow import POSTE_ETATS_BOUTONS, POSTE_ETAT_FINALISE
1b31de9f 22
f258e4e7 23
2e672700
OL
24class BaseInlineFormSetWithInitial(BaseInlineFormSet):
25 """
26 Cette classe permet de fournir l'option initial aux inlineformsets.
27 Elle devient désuette en django 1.4.
28 """
29 def __init__(self, data=None, files=None, instance=None,
30 save_as_new=False, prefix=None, queryset=None, **kwargs):
31
32 self.initial_extra = kwargs.pop('initial', None)
33
34 from django.db.models.fields.related import RelatedObject
35 if instance is None:
36 self.instance = self.fk.rel.to()
37 else:
38 self.instance = instance
39 self.save_as_new = save_as_new
40 # is there a better way to get the object descriptor?
41 self.rel_name = RelatedObject(self.fk.rel.to, self.model, self.fk).get_accessor_name()
42 if queryset is None:
43 queryset = self.model._default_manager
44 qs = queryset.filter(**{self.fk.name: self.instance})
45 super(BaseInlineFormSetWithInitial, self).__init__(data, files, prefix=prefix,
46 queryset=qs, **kwargs)
47
48 def _construct_form(self, i, **kwargs):
49 if self.is_bound and i < self.initial_form_count():
50 # Import goes here instead of module-level because importing
51 # django.db has side effects.
52 from django.db import connections
53 pk_key = "%s-%s" % (self.add_prefix(i), self.model._meta.pk.name)
54 pk = self.data[pk_key]
55 pk_field = self.model._meta.pk
56 pk = pk_field.get_db_prep_lookup('exact', pk,
57 connection=connections[self.get_queryset().db])
58 if isinstance(pk, list):
59 pk = pk[0]
60 kwargs['instance'] = self._existing_object(pk)
61 if i < self.initial_form_count() and not kwargs.get('instance'):
62 kwargs['instance'] = self.get_queryset()[i]
63 if i >= self.initial_form_count() and self.initial_extra:
64 # Set initial values for extra forms
65 try:
66 kwargs['initial'] = self.initial_extra[i-self.initial_form_count()]
67 except IndexError:
68 pass
69
70 defaults = {'auto_id': self.auto_id, 'prefix': self.add_prefix(i)}
71 if self.is_bound:
72 defaults['data'] = self.data
73 defaults['files'] = self.files
74 if self.initial:
75 try:
76 defaults['initial'] = self.initial[i]
77 except IndexError:
78 pass
79 # Allow extra forms to be empty.
80 if i >= self.initial_form_count():
81 defaults['empty_permitted'] = True
82 defaults.update(kwargs)
83 form = self.form(**defaults)
84 self.add_fields(form, i)
85 return form
86
87
f258e4e7
OL
88def _implantation_choices(obj, request):
89 # TRAITEMENT NORMAL
3383b2d1 90 employe = groups.get_employe_from_user(request.user)
01971ac9 91 q = Q(**{'zone_administrative__in': groups.get_zones_from_user(request.user)})
f258e4e7
OL
92
93 # TRAITEMENT DRH
3383b2d1 94 user_groupes = [g.name for g in request.user.groups.all()]
713b824a
OL
95 if groups.DRH_NIVEAU_1 in user_groupes or \
96 groups.DRH_NIVEAU_2 in user_groupes:
f258e4e7 97 q = Q()
5a1f75cb
EMS
98 return [('', '----------')] + \
99 [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
100
f258e4e7
OL
101
102def _employe_choices(obj, request):
f258e4e7 103 # TRAITEMENT NORMAL
3383b2d1 104 employe = groups.get_employe_from_user(request.user)
b0cf30b8 105 q_dae_region_service = Q(
01971ac9
BS
106 poste__implantation__zone_administrative__in=(
107 groups.get_zones_from_user(request.user)
5a1f75cb 108 )
b0cf30b8
EMS
109 )
110 q_rh_region_service = Q(
01971ac9
BS
111 poste__implantation__zone_administrative__in=(
112 groups.get_zones_from_user(request.user)
5a1f75cb 113 )
b0cf30b8 114 )
f258e4e7 115 # TRAITEMENT DRH
3383b2d1 116 user_groupes = [g.name for g in request.user.groups.all()]
713b824a
OL
117 if groups.DRH_NIVEAU_1 in user_groupes or \
118 groups.DRH_NIVEAU_2 in user_groupes:
072820fc
OL
119 q_dae_region_service = Q()
120 q_rh_region_service = Q()
f258e4e7 121
5a1f75cb
EMS
122 # On filtre les employes avec les droits régionaux et on s'assure que
123 # c'est bien le dernier dossier en date pour sortir l'employe. On retient
124 # un employé qui travaille présentement dans la même région que le user
125 # connecté.
126 dossiers_regionaux_ids = [
127 d.id for d in dae.Dossier.objects.filter(q_dae_region_service)
128 ]
129 employes_ids = [
130 d['employe']
131 for d in dae.Dossier.objects
132 .values('employe')
133 .annotate(dernier_dossier=Max('id'))
134 if d['dernier_dossier'] in dossiers_regionaux_ids
135 ]
072820fc
OL
136 dae_employe = dae.Employe.objects.filter(id__in=employes_ids)
137 dae_ = dae_employe.filter(id_rh__isnull=True)
138 copies = dae_employe.filter(Q(id_rh__isnull=False))
f258e4e7 139 id_copies = [p.id_rh_id for p in copies.all()]
072820fc 140
5a1f75cb
EMS
141 dossiers_regionaux_ids = [
142 d.id for d in rh.Dossier.objects.filter(q_rh_region_service)
143 ]
144 employes_ids = [
145 d['employe']
146 for d in rh.Dossier.objects
147 .values('employe')
148 .annotate(dernier_dossier=Max('id'))
149 if d['dernier_dossier'] in dossiers_regionaux_ids
150 ]
151 rhv1 = rh.Employe.objects \
152 .filter(id__in=employes_ids) \
153 .exclude(id__in=id_copies)
154
155 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont
156 # pas de Dossier associés
67c15007
OL
157 employes_avec_dae = [d.employe_id for d in dae.Dossier.objects.all()]
158 employes_orphelins = dae.Employe.objects.exclude(id__in=employes_avec_dae)
159
0339920c
OL
160 def option_label(employe, extra=""):
161 if extra:
162 extra = " [%s]" % extra
163 return "%s %s %s" % (employe.nom.upper(), employe.prenom.title(), extra)
f258e4e7 164
0339920c
OL
165 lbl_rh = sorted([('rh-%s' % p.id, option_label(p, "existant dans rh")) for p in rhv1],
166 key=lambda t: t[1])
167 lbl_dae = sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins],
168 key=lambda t: t[1])
169 return [('', 'Nouvel employé')] + lbl_rh + lbl_dae
5a1f75cb 170
f258e4e7 171
4bce4d24
OL
172def label_poste_display(poste):
173 """Formate un visuel pour un poste dans une liste déroulante"""
23294f7d
OL
174 annee = ""
175 if poste.date_debut:
176 annee = poste.date_debut.year
9c1ff333
OL
177
178 nom = poste.nom
67ae0181 179 label = u"%s (%s) %s [%s]" % (
34950f36
OL
180 annee,
181 poste.implantation.nom_court,
182 nom,
67ae0181 183 #poste.type_poste.categorie_emploi.nom,
34950f36 184 poste.id,
93817ef3 185 )
4bce4d24 186 return label
9cb4de55 187
2e672700 188
874949f3 189PostePieceFormSet = inlineformset_factory(dae.Poste, dae.PostePiece,)
25086dcf 190DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
2e672700 191
874949f3
OL
192# Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
193# données de RH
194FinancementFormSetInitial = inlineformset_factory(
2e672700
OL
195 dae.Poste,
196 dae.PosteFinancement,
197 formset=BaseInlineFormSetWithInitial,
198 extra=2
5a1f75cb 199)
874949f3
OL
200FinancementFormSet = inlineformset_factory(
201 dae.Poste,
202 dae.PosteFinancement,
203 extra=2
204)
5a1f75cb 205
03b395db
OL
206
207class DossierComparaisonForm(forms.ModelForm):
11f22317 208
03b395db 209 recherche = AutoCompleteSelectField('dossiers', required=False)
5a1f75cb
EMS
210 poste = forms.CharField(
211 max_length=255, widget=forms.TextInput(attrs={'size': '60'})
212 )
03b395db 213
320d7584 214 class Meta:
03b395db 215 model = dae.DossierComparaison
320d7584 216 exclude = ('dossier',)
03b395db 217
320d7584
EMS
218DossierComparaisonFormSet = modelformset_factory(
219 dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
25086dcf 220)
03b395db 221
5a1f75cb 222
068d1462 223class PosteComparaisonForm(forms.ModelForm):
11f22317 224
e503e64d 225 recherche = AutoCompleteSelectField('dae_postes', required=False)
068d1462 226
320d7584 227 class Meta:
068d1462 228 model = dae.PosteComparaison
320d7584 229 exclude = ('poste',)
068d1462 230
874949f3
OL
231# Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
232# données de RH
233PosteComparaisonFormSetInitial = inlineformset_factory(
2e672700
OL
234 dae.Poste,
235 dae.PosteComparaison,
236 extra=3,
237 max_num=3,
238 form=PosteComparaisonForm,
2e672700 239 formset=BaseInlineFormSetWithInitial,
25086dcf 240)
874949f3
OL
241PosteComparaisonFormSet = inlineformset_factory(
242 dae.Poste,
243 dae.PosteComparaison,
244 extra=3,
245 max_num=3,
246 form=PosteComparaisonForm,
247)
068d1462 248
5a1f75cb 249
0a085c42
OL
250class FlexibleRemunForm(forms.ModelForm):
251
252 montant_mensuel = forms.DecimalField(required=False)
253 montant = forms.DecimalField(required=True, label='Montant annuel')
254
255 class Meta:
256 model = dae.Remuneration
257
dc4b78a7
OL
258 def clean_devise(self):
259 devise = self.cleaned_data['devise']
67173010
OL
260 if devise.code == 'EUR':
261 return devise
5a1f75cb
EMS
262 implantation = ref.Implantation.objects.get(
263 id=self.data['implantation']
264 )
2455f48d 265 liste_taux = devise.tauxchange_set.order_by('-annee')
dc4b78a7 266 if len(liste_taux) == 0:
5a1f75cb
EMS
267 raise forms.ValidationError(
268 u"La devise %s n'a pas de taux pour l'implantation %s" %
269 (devise, implantation)
270 )
dc4b78a7
OL
271 else:
272 return devise
273
25086dcf
EMS
274RemunForm = inlineformset_factory(
275 dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm
276)
0a085c42 277
5a1f75cb 278
1b217058 279class PosteForm(forms.ModelForm):
5d680e84 280 """ Formulaire des postes. """
12c7f8a7 281
ea7adc69 282 # On ne propose que les services actifs
5a1f75cb
EMS
283 service = forms.ModelChoiceField(
284 queryset=rh.Service.objects.all(), required=True
285 )
ea7adc69 286
5a1f75cb 287 responsable = AutoCompleteSelectField('responsables', required=True)
12c7f8a7
OL
288 #responsable = forms.ModelChoiceField(
289 # queryset=rh.Poste.objects.select_related(depth=1))
290
291 # La liste des choix est laissée vide. Voir __init__ pour la raison.
292 poste = forms.ChoiceField(label="Nouveau poste ou évolution du poste",
293 choices=(), required=False)
11f22317 294
5a1f75cb
EMS
295 valeur_point_min = forms.ModelChoiceField(
296 queryset=rh.ValeurPoint.actuelles.all(), required=False
297 )
298 valeur_point_max = forms.ModelChoiceField(
299 queryset=rh.ValeurPoint.actuelles.all(), required=False
300 )
11f22317 301
5d680e84
NC
302 class Meta:
303 model = dae.Poste
c3be904d
OL
304 fields = ('type_intervention',
305 'poste', 'implantation', 'type_poste', 'service', 'nom',
154677c3 306 'responsable', 'local', 'expatrie', 'mise_a_disposition',
b15bf543 307 'appel', 'date_debut', 'date_fin',
5d680e84
NC
308 'regime_travail', 'regime_travail_nb_heure_semaine',
309 'classement_min', 'classement_max',
310 'valeur_point_min', 'valeur_point_max',
3d627bfd 311 'devise_min', 'devise_max',
5f61bccb
OL
312 'salaire_min', 'salaire_max',
313 'indemn_expat_min', 'indemn_expat_max',
314 'indemn_fct_min', 'indemn_fct_max',
315 'charges_patronales_min', 'charges_patronales_max',
5d680e84
NC
316 'autre_min', 'autre_max', 'devise_comparaison',
317 'comp_locale_min', 'comp_locale_max',
318 'comp_universite_min', 'comp_universite_max',
319 'comp_fonctionpub_min', 'comp_fonctionpub_max',
320 'comp_ong_min', 'comp_ong_max',
8fa94e8b 321 'comp_autre_min', 'comp_autre_max',
2e092e0c 322 'justification',
8fa94e8b 323 )
c3be904d
OL
324 widgets = dict(type_intervention=forms.RadioSelect(),
325 appel=forms.RadioSelect(),
3d627bfd 326 nom=forms.TextInput(attrs={'size': 60},),
e88caaf0
OL
327 date_debut=admin_widgets.AdminDateWidget(),
328 date_fin=admin_widgets.AdminDateWidget(),
2e092e0c 329 justification=forms.Textarea(attrs={'cols': 80},),
3d627bfd 330 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
331 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
332 )
5d680e84 333
c2458db6 334 def __init__(self, *args, **kwargs):
5d680e84
NC
335 """ Mise à jour dynamique du contenu du menu des postes.
336
337 Si on ne met le menu à jour de cette façon, à chaque instantiation du
338 formulaire, son contenu est mis en cache par le système et il ne
339 reflète pas les changements apportés par les ajouts, modifications,
340 etc...
341
139686f2
NC
342 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
343 car le "id" de chaque choix est spécial (voir _poste_choices).
344
5d680e84 345 """
c2458db6 346 request = kwargs.pop('request')
5d680e84 347 super(PosteForm, self).__init__(*args, **kwargs)
f258e4e7 348 self.fields['poste'].choices = self._poste_choices(request)
9c1ff333 349
5a1f75cb
EMS
350 self.fields['implantation'].choices = \
351 _implantation_choices(self, request)
5d680e84 352
cc3098d0
OL
353 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
354 if self.instance and self.instance.id is None:
355 dossiers = self.instance.get_dossiers()
356 if len(dossiers) > 0:
09aa8374 357 self.initial['service'] = dossiers[0].poste.service
9508a5b8 358
f258e4e7 359 def _poste_choices(self, request):
5d680e84 360 """ Menu déroulant pour les postes.
9c1ff333 361 Constitué des postes de RH
5d680e84 362 """
9c1ff333
OL
363 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).all()
364 postes_rh = postes_rh.select_related(depth=1)
5d680e84 365
98d51b59 366 return [('', 'Nouveau poste')] + \
9c1ff333
OL
367 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
368 postes_rh],
5d680e84 369 key=lambda t: t[1])
3ed49093 370
4dd75e7b
OL
371 def clean(self):
372 """
373 Validation conditionnelles de certains champs.
374 """
5a1f75cb 375 cleaned_data = self.cleaned_data
4dd75e7b 376
5a1f75cb
EMS
377 if cleaned_data.get("local") is False \
378 and cleaned_data.get("expatrie") is False:
379 msg = "Le poste doit au moins être ouvert localement " \
380 "ou aux expatriés"
f42c6e20
OL
381 self._errors["local"] = self.error_class([msg])
382 self._errors["expatrie"] = ''
383 raise forms.ValidationError(msg)
f42c6e20 384
4dd75e7b
OL
385 return cleaned_data
386
3ed49093 387
34950f36 388class ChoosePosteForm(forms.Form):
139686f2 389 class Meta:
139686f2
NC
390 fields = ('poste',)
391
392 # La liste des choix est laissée vide. Voir PosteForm.__init__.
34950f36
OL
393 postes_dae = forms.ChoiceField(choices=(), required=False)
394 postes_rh = forms.ChoiceField(choices=(), required=False)
139686f2 395
4ee6d70a 396 def __init__(self, request=None, *args, **kwargs):
139686f2 397 super(ChoosePosteForm, self).__init__(*args, **kwargs)
34950f36
OL
398 self.fields['postes_dae'].choices = self._poste_dae_choices(request)
399 self.fields['postes_rh'].choices = self._poste_rh_choices(request)
139686f2 400
34950f36
OL
401 def _poste_dae_choices(self, request):
402 """ Menu déroulant pour les postes."""
403 postes_dae = dae.Poste.objects.ma_region_ou_service(request.user) \
404 .exclude(etat__in=(POSTE_ETAT_FINALISE, )) \
405 .annotate(num_dae=Count('dae_dossiers')) \
406 .filter(num_dae=0) \
67ae0181 407 .order_by('implantation', '-date_debut', )
139686f2 408
98d51b59 409 return [('', '----------')] + \
34950f36
OL
410 [('dae-%s' % p.id, label_poste_display(p)) for p in postes_dae]
411
412 def _poste_rh_choices(self, request):
413 """ Menu déroulant pour les postes."""
80be36aa 414 postes_dae = dae.Poste.objects.exclude(etat__in=(POSTE_ETAT_FINALISE, ))
bed0c4c9 415 today = datetime.date.today()
80be36aa 416 id_poste_dae_commences = [p.id_rh_id for p in postes_dae if p.id_rh is not None]
34950f36 417 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user) \
80be36aa 418 .exclude(id__in=id_poste_dae_commences) \
bed0c4c9
BS
419 .filter(Q(date_debut__lte=today) &
420 (Q(date_fin__gte=today) |
421 Q(date_fin__isnull=True))
422 ) \
67ae0181 423 .order_by('implantation', '-date_debut', )
34950f36
OL
424
425 return [('', '----------')] + \
426 [('rh-%s' % p.id, label_poste_display(p)) for p in postes_rh]
139686f2 427
80be36aa
OL
428 def clean(self):
429 cleaned_data = super(ChoosePosteForm, self).clean()
430 postes_dae = cleaned_data.get("postes_dae")
431 postes_rh = cleaned_data.get("postes_rh")
432 if (postes_dae is u"" and postes_rh is u"") or \
433 (postes_dae is not u"" and postes_rh is not u""):
434 raise forms.ValidationError("Choisissez un poste DAE ou un poste RH")
435 return cleaned_data
436
437 def redirect(self):
438 poste_dae_key = self.cleaned_data.get("postes_dae")
439 if poste_dae_key is not u"":
440 return redirect(reverse('embauche', args=(poste_dae_key,)))
441 poste_rh_key = self.cleaned_data.get("postes_rh")
442 if poste_rh_key is not u"":
67ae0181 443 return redirect("%s?creer_dossier_dae='M'" % reverse('poste', args=(poste_rh_key,)))
139686f2 444
139686f2
NC
445class EmployeForm(forms.ModelForm):
446 """ Formulaire des employés. """
447 class Meta:
448 model = dae.Employe
449 fields = ('employe', 'nom', 'prenom', 'genre')
450
451 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
452 employe = forms.ChoiceField(choices=(), required=False)
453
ac6235f6 454 def __init__(self, *args, **kwargs):
139686f2 455 """ Mise à jour dynamique du contenu du menu des employés. """
ac6235f6 456 request = kwargs.pop('request', None)
139686f2 457 super(EmployeForm, self).__init__(*args, **kwargs)
f258e4e7 458 self.fields['employe'].choices = _employe_choices(self, request)
139686f2 459
139686f2 460
139686f2
NC
461class DossierForm(forms.ModelForm):
462 """ Formulaire des dossiers. """
463 class Meta:
5a1f75cb 464 exclude = ('etat', 'employe', 'poste', 'date_debut',)
139686f2 465 model = dae.Dossier
4d25e2ba 466 widgets = dict(statut_residence=forms.RadioSelect(),
0e0aeb7e
OL
467 contrat_date_debut=admin_widgets.AdminDateWidget(),
468 contrat_date_fin=admin_widgets.AdminDateWidget(),
4d25e2ba 469 )
e6f52402 470
3799cafc 471WF_HELP_TEXT = ""
e0b93e3a 472
5a1f75cb 473
e6f52402 474class PosteWorkflowForm(WorkflowFormMixin):
56589624 475 bouton_libelles = POSTE_ETATS_BOUTONS
5a1f75cb 476
e6f52402
OL
477 class Meta:
478 fields = ('etat', )
479 model = dae.Poste
9536ea21 480
e0b93e3a 481 def __init__(self, *args, **kwargs):
e54b7d5d 482 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a
OL
483 self.fields['etat'].help_text = WF_HELP_TEXT
484
485
e6f52402 486class DossierWorkflowForm(WorkflowFormMixin):
5a1f75cb
EMS
487 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
488
e6f52402 489 class Meta:
9e40cfbe 490 fields = ('etat', )
e6f52402 491 model = dae.Dossier
e0b93e3a
OL
492
493 def __init__(self, *args, **kwargs):
e54b7d5d 494 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a 495 self.fields['etat'].help_text = WF_HELP_TEXT
e54b7d5d 496 self._etat_initial = self.instance.etat
e0b93e3a 497
e54b7d5d
EMS
498 def save(self):
499 super(DossierWorkflowForm, self).save()
500 poste = self.instance.poste
66fefd2f
OL
501
502 # créer le commentaire automatique pour le poste associé
503 commentaire = WorkflowCommentaire()
504 commentaire.content_object = poste
505 texte = u"Validation automatique à travers le dossier [%s] de %s\n%s" %(
506 self.instance.id,
507 self.instance,
508 self.data.get('commentaire', ''),
509 )
510 commentaire.texte = texte
511 commentaire.etat_initial = self.instance._etat_courant
512 commentaire.etat_final = self.instance.etat
513 commentaire.owner = self.request.user
514 commentaire.save()
515
516 # force l'état du poste
517 poste.etat = self.instance.etat
518 poste.save()
9536ea21 519
5a1f75cb 520
9536ea21
EMS
521class ContratForm(forms.ModelForm):
522
523 class Meta:
9dfa4296 524 fields = ('type_contrat', 'fichier', )
9536ea21
EMS
525 model = dae.Contrat
526
5a1f75cb 527
c3f0b49f
EMS
528class DAENumeriseeForm(forms.ModelForm):
529
530 class Meta:
531 model = dae.Dossier
532 fields = ('dae_numerisee',)
cbfd7bd4
EMS
533
534
535class DAEFinaliseesSearchForm(forms.Form):
536 q = forms.CharField(
537 label='Recherche', required=False,
538 widget=forms.TextInput(attrs={'size': 40})
539 )
540 importees = forms.ChoiceField(
541 label='Importation', required=False, choices=(
542 ('', ''),
543 ('oui', 'DAE importées seulement'),
544 ('non', 'DAE non-importées seulement'),
545 )
546 )