On the way there...
[auf_rh_dae.git] / project / dae / forms.py
CommitLineData
5d680e84 1# -*- encoding: utf-8 -*-
ce110fb9 2
bed0c4c9 3import datetime
6bec5651 4from ordereddict import OrderedDict
5a1f75cb 5from django import forms
80be36aa 6from django.core.urlresolvers import reverse
661da766 7from django.core.exceptions import MultipleObjectsReturned
2e672700 8from django.forms.models import BaseInlineFormSet
4718c21c
BS
9from django.forms.models import (
10 inlineformset_factory,
11 modelformset_factory,
12 _get_foreign_key,
13 )
80be36aa
OL
14from django.db.models import Q, Max, Count
15from django.shortcuts import redirect
16from django.contrib.admin import widgets as admin_widgets
5a1f75cb 17
75f0e87b
DB
18from ajax_select.fields import AutoCompleteSelectField
19
20from auf.django.references import models as ref
21from auf.django.workflow.forms import WorkflowFormMixin
66fefd2f 22from auf.django.workflow.models import WorkflowCommentaire
75f0e87b 23
3383b2d1 24from project import groups
17c90428 25from project.rh import models as rh
17c90428 26from project.dae import models as dae
661da766 27from .widgets import ReadOnlyChoiceWidget, ReadOnlyWidget
34950f36 28from project.dae.workflow import POSTE_ETATS_BOUTONS, POSTE_ETAT_FINALISE
1b31de9f 29
f258e4e7 30
2e672700
OL
31class 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
f258e4e7
OL
95def _implantation_choices(obj, request):
96 # TRAITEMENT NORMAL
3383b2d1 97 employe = groups.get_employe_from_user(request.user)
b0cf30b8 98 q = Q(**{'zone_administrative': employe.implantation.zone_administrative})
f258e4e7
OL
99
100 # TRAITEMENT DRH
3383b2d1 101 user_groupes = [g.name for g in request.user.groups.all()]
713b824a
OL
102 if groups.DRH_NIVEAU_1 in user_groupes or \
103 groups.DRH_NIVEAU_2 in user_groupes:
f258e4e7 104 q = Q()
5a1f75cb
EMS
105 return [('', '----------')] + \
106 [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
107
f258e4e7
OL
108
109def _employe_choices(obj, request):
f258e4e7 110 # TRAITEMENT NORMAL
3383b2d1 111 employe = groups.get_employe_from_user(request.user)
b0cf30b8
EMS
112 q_dae_region_service = Q(
113 poste__implantation__zone_administrative=(
114 employe.implantation.zone_administrative
5a1f75cb 115 )
b0cf30b8
EMS
116 )
117 q_rh_region_service = Q(
118 poste__implantation__zone_administrative=(
119 employe.implantation.zone_administrative
5a1f75cb 120 )
b0cf30b8 121 )
f258e4e7 122 # TRAITEMENT DRH
3383b2d1 123 user_groupes = [g.name for g in request.user.groups.all()]
713b824a
OL
124 if groups.DRH_NIVEAU_1 in user_groupes or \
125 groups.DRH_NIVEAU_2 in user_groupes:
072820fc
OL
126 q_dae_region_service = Q()
127 q_rh_region_service = Q()
f258e4e7 128
5a1f75cb
EMS
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 ]
072820fc
OL
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))
f258e4e7 146 id_copies = [p.id_rh_id for p in copies.all()]
072820fc 147
5a1f75cb
EMS
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
67c15007
OL
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
0339920c
OL
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)
f258e4e7 171
0339920c
OL
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
5a1f75cb 177
f258e4e7 178
4bce4d24
OL
179def label_poste_display(poste):
180 """Formate un visuel pour un poste dans une liste déroulante"""
23294f7d
OL
181 annee = ""
182 if poste.date_debut:
183 annee = poste.date_debut.year
9c1ff333
OL
184
185 nom = poste.nom
67ae0181 186 label = u"%s (%s) %s [%s]" % (
34950f36
OL
187 annee,
188 poste.implantation.nom_court,
189 nom,
67ae0181 190 #poste.type_poste.categorie_emploi.nom,
34950f36 191 poste.id,
93817ef3 192 )
4bce4d24 193 return label
9cb4de55 194
2e672700 195
874949f3 196PostePieceFormSet = inlineformset_factory(dae.Poste, dae.PostePiece,)
25086dcf 197DossierPieceForm = inlineformset_factory(dae.Dossier, dae.DossierPiece)
2e672700 198
874949f3
OL
199# Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
200# données de RH
201FinancementFormSetInitial = inlineformset_factory(
2e672700
OL
202 dae.Poste,
203 dae.PosteFinancement,
204 formset=BaseInlineFormSetWithInitial,
205 extra=2
5a1f75cb 206)
874949f3
OL
207FinancementFormSet = inlineformset_factory(
208 dae.Poste,
209 dae.PosteFinancement,
210 extra=2
211)
5a1f75cb 212
03b395db
OL
213
214class DossierComparaisonForm(forms.ModelForm):
11f22317 215
03b395db 216 recherche = AutoCompleteSelectField('dossiers', required=False)
5a1f75cb
EMS
217 poste = forms.CharField(
218 max_length=255, widget=forms.TextInput(attrs={'size': '60'})
219 )
03b395db 220
320d7584 221 class Meta:
03b395db 222 model = dae.DossierComparaison
320d7584 223 exclude = ('dossier',)
03b395db 224
320d7584
EMS
225DossierComparaisonFormSet = modelformset_factory(
226 dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm
25086dcf 227)
03b395db 228
5a1f75cb 229
068d1462 230class PosteComparaisonForm(forms.ModelForm):
11f22317 231
e503e64d 232 recherche = AutoCompleteSelectField('dae_postes', required=False)
068d1462 233
320d7584 234 class Meta:
068d1462 235 model = dae.PosteComparaison
320d7584 236 exclude = ('poste',)
068d1462 237
874949f3
OL
238# Ce formset est utilisé dans le cas de la création de poste prépopulé avec les
239# données de RH
240PosteComparaisonFormSetInitial = inlineformset_factory(
2e672700
OL
241 dae.Poste,
242 dae.PosteComparaison,
243 extra=3,
244 max_num=3,
245 form=PosteComparaisonForm,
2e672700 246 formset=BaseInlineFormSetWithInitial,
25086dcf 247)
874949f3
OL
248PosteComparaisonFormSet = inlineformset_factory(
249 dae.Poste,
250 dae.PosteComparaison,
251 extra=3,
252 max_num=3,
253 form=PosteComparaisonForm,
254)
068d1462 255
5a1f75cb 256
0a085c42
OL
257class 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
4718c21c
BS
265 def __init__(self, *a, **kw):
266 super(FlexibleRemunForm, self).__init__(*a, **kw)
661da766 267 # self.fields['type'].widget = ReadOnlyChoiceWidget(choices=self.fields['type'].choices)
4718c21c 268
dc4b78a7
OL
269 def clean_devise(self):
270 devise = self.cleaned_data['devise']
67173010
OL
271 if devise.code == 'EUR':
272 return devise
5a1f75cb
EMS
273 implantation = ref.Implantation.objects.get(
274 id=self.data['implantation']
275 )
2455f48d 276 liste_taux = devise.tauxchange_set.order_by('-annee')
dc4b78a7 277 if len(liste_taux) == 0:
5a1f75cb
EMS
278 raise forms.ValidationError(
279 u"La devise %s n'a pas de taux pour l'implantation %s" %
280 (devise, implantation)
281 )
dc4b78a7
OL
282 else:
283 return devise
284
6bec5651
BS
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
4718c21c 310
661da766
BS
311class 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
4718c21c
BS
324class GroupedInlineFormset(BaseInlineFormSet):
325
661da766
BS
326 def set_groups(self,
327 groups,
328 group_accessor,
329 choice_overrides=[]):
330
4718c21c 331
661da766 332 # Create pre-defined groups.
6bec5651 333 self.groups = OrderedDict()
661da766
BS
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.
4718c21c 342 for form in self.forms:
661da766
BS
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())
6bec5651 361
661da766
BS
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.
4718c21c
BS
373 self.group_list = self.groups.values()
374
661da766
BS
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
4718c21c
BS
410
411def 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,
661da766 420 extra=2,
4718c21c 421 max_num=None,
6bec5651 422 formfield_callback=None,
661da766
BS
423 groups=None,
424 choice_overrides=[]):
4718c21c 425 trs = rh.TypeRemuneration.objects.all()
661da766 426 # extra = max_num = trs.count()
4718c21c
BS
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):
661da766
BS
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 )
4718c21c
BS
452
453 def __init__(inst, *a, **kw):
454 super(FormSet, inst).__init__(*a, **kw)
455
661da766
BS
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.
4718c21c 461
661da766
BS
462 # # Set initial data.
463 # d_inst = kw.get('instance', None)
4718c21c 464
661da766
BS
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
4718c21c
BS
490 FormSet.__init__ = __init__
491
492 return FormSet
493
494
661da766
BS
495ReadOnlyRemunForm = 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
507null_choice = ('', '-' * 10)
4718c21c 508RemunForm = remun_formset_factory(
6bec5651
BS
509 dae.Dossier,
510 dae.Remuneration,
511 form=FlexibleRemunForm,
661da766
BS
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 }
4718c21c 548 )
0a085c42 549
661da766
BS
550RemunForm = ReadOnlyRemunForm
551
5a1f75cb 552
1b217058 553class PosteForm(forms.ModelForm):
5d680e84 554 """ Formulaire des postes. """
12c7f8a7 555
ea7adc69 556 # On ne propose que les services actifs
5a1f75cb
EMS
557 service = forms.ModelChoiceField(
558 queryset=rh.Service.objects.all(), required=True
559 )
ea7adc69 560
5a1f75cb 561 responsable = AutoCompleteSelectField('responsables', required=True)
12c7f8a7
OL
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)
11f22317 568
5a1f75cb
EMS
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 )
11f22317 575
5d680e84
NC
576 class Meta:
577 model = dae.Poste
c3be904d
OL
578 fields = ('type_intervention',
579 'poste', 'implantation', 'type_poste', 'service', 'nom',
154677c3 580 'responsable', 'local', 'expatrie', 'mise_a_disposition',
b15bf543 581 'appel', 'date_debut', 'date_fin',
5d680e84
NC
582 'regime_travail', 'regime_travail_nb_heure_semaine',
583 'classement_min', 'classement_max',
584 'valeur_point_min', 'valeur_point_max',
3d627bfd 585 'devise_min', 'devise_max',
5f61bccb
OL
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',
5d680e84
NC
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',
8fa94e8b 595 'comp_autre_min', 'comp_autre_max',
2e092e0c 596 'justification',
8fa94e8b 597 )
c3be904d
OL
598 widgets = dict(type_intervention=forms.RadioSelect(),
599 appel=forms.RadioSelect(),
3d627bfd 600 nom=forms.TextInput(attrs={'size': 60},),
e88caaf0
OL
601 date_debut=admin_widgets.AdminDateWidget(),
602 date_fin=admin_widgets.AdminDateWidget(),
2e092e0c 603 justification=forms.Textarea(attrs={'cols': 80},),
3d627bfd 604 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
605 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
606 )
5d680e84 607
c2458db6 608 def __init__(self, *args, **kwargs):
5d680e84
NC
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
139686f2
NC
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
5d680e84 619 """
c2458db6 620 request = kwargs.pop('request')
5d680e84 621 super(PosteForm, self).__init__(*args, **kwargs)
f258e4e7 622 self.fields['poste'].choices = self._poste_choices(request)
9c1ff333 623
5a1f75cb
EMS
624 self.fields['implantation'].choices = \
625 _implantation_choices(self, request)
5d680e84 626
cc3098d0
OL
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:
09aa8374 631 self.initial['service'] = dossiers[0].poste.service
9508a5b8 632
f258e4e7 633 def _poste_choices(self, request):
5d680e84 634 """ Menu déroulant pour les postes.
9c1ff333 635 Constitué des postes de RH
5d680e84 636 """
9c1ff333
OL
637 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user).all()
638 postes_rh = postes_rh.select_related(depth=1)
5d680e84 639
98d51b59 640 return [('', 'Nouveau poste')] + \
9c1ff333
OL
641 sorted([('rh-%s' % p.id, label_poste_display(p)) for p in
642 postes_rh],
5d680e84 643 key=lambda t: t[1])
3ed49093 644
4dd75e7b
OL
645 def clean(self):
646 """
647 Validation conditionnelles de certains champs.
648 """
5a1f75cb 649 cleaned_data = self.cleaned_data
4dd75e7b 650
5a1f75cb
EMS
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"
f42c6e20
OL
655 self._errors["local"] = self.error_class([msg])
656 self._errors["expatrie"] = ''
657 raise forms.ValidationError(msg)
f42c6e20 658
4dd75e7b
OL
659 return cleaned_data
660
3ed49093 661
34950f36 662class ChoosePosteForm(forms.Form):
139686f2 663 class Meta:
139686f2
NC
664 fields = ('poste',)
665
666 # La liste des choix est laissée vide. Voir PosteForm.__init__.
34950f36
OL
667 postes_dae = forms.ChoiceField(choices=(), required=False)
668 postes_rh = forms.ChoiceField(choices=(), required=False)
139686f2 669
4ee6d70a 670 def __init__(self, request=None, *args, **kwargs):
139686f2 671 super(ChoosePosteForm, self).__init__(*args, **kwargs)
34950f36
OL
672 self.fields['postes_dae'].choices = self._poste_dae_choices(request)
673 self.fields['postes_rh'].choices = self._poste_rh_choices(request)
139686f2 674
34950f36
OL
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) \
67ae0181 681 .order_by('implantation', '-date_debut', )
139686f2 682
98d51b59 683 return [('', '----------')] + \
34950f36
OL
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."""
80be36aa 688 postes_dae = dae.Poste.objects.exclude(etat__in=(POSTE_ETAT_FINALISE, ))
bed0c4c9 689 today = datetime.date.today()
80be36aa 690 id_poste_dae_commences = [p.id_rh_id for p in postes_dae if p.id_rh is not None]
34950f36 691 postes_rh = rh.Poste.objects.ma_region_ou_service(request.user) \
80be36aa 692 .exclude(id__in=id_poste_dae_commences) \
bed0c4c9
BS
693 .filter(Q(date_debut__lte=today) &
694 (Q(date_fin__gte=today) |
695 Q(date_fin__isnull=True))
696 ) \
67ae0181 697 .order_by('implantation', '-date_debut', )
34950f36
OL
698
699 return [('', '----------')] + \
700 [('rh-%s' % p.id, label_poste_display(p)) for p in postes_rh]
139686f2 701
80be36aa
OL
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"":
67ae0181 717 return redirect("%s?creer_dossier_dae='M'" % reverse('poste', args=(poste_rh_key,)))
139686f2 718
139686f2
NC
719class 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
ac6235f6 728 def __init__(self, *args, **kwargs):
139686f2 729 """ Mise à jour dynamique du contenu du menu des employés. """
ac6235f6 730 request = kwargs.pop('request', None)
139686f2 731 super(EmployeForm, self).__init__(*args, **kwargs)
f258e4e7 732 self.fields['employe'].choices = _employe_choices(self, request)
139686f2 733
139686f2 734
139686f2
NC
735class DossierForm(forms.ModelForm):
736 """ Formulaire des dossiers. """
737 class Meta:
5a1f75cb 738 exclude = ('etat', 'employe', 'poste', 'date_debut',)
139686f2 739 model = dae.Dossier
4d25e2ba 740 widgets = dict(statut_residence=forms.RadioSelect(),
0e0aeb7e
OL
741 contrat_date_debut=admin_widgets.AdminDateWidget(),
742 contrat_date_fin=admin_widgets.AdminDateWidget(),
4d25e2ba 743 )
e6f52402 744
3799cafc 745WF_HELP_TEXT = ""
e0b93e3a 746
5a1f75cb 747
e6f52402 748class PosteWorkflowForm(WorkflowFormMixin):
56589624 749 bouton_libelles = POSTE_ETATS_BOUTONS
5a1f75cb 750
e6f52402
OL
751 class Meta:
752 fields = ('etat', )
753 model = dae.Poste
9536ea21 754
e0b93e3a 755 def __init__(self, *args, **kwargs):
e54b7d5d 756 super(PosteWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a
OL
757 self.fields['etat'].help_text = WF_HELP_TEXT
758
759
e6f52402 760class DossierWorkflowForm(WorkflowFormMixin):
5a1f75cb
EMS
761 bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste...
762
e6f52402 763 class Meta:
9e40cfbe 764 fields = ('etat', )
e6f52402 765 model = dae.Dossier
e0b93e3a
OL
766
767 def __init__(self, *args, **kwargs):
e54b7d5d 768 super(DossierWorkflowForm, self).__init__(*args, **kwargs)
e0b93e3a 769 self.fields['etat'].help_text = WF_HELP_TEXT
e54b7d5d 770 self._etat_initial = self.instance.etat
e0b93e3a 771
e54b7d5d
EMS
772 def save(self):
773 super(DossierWorkflowForm, self).save()
774 poste = self.instance.poste
66fefd2f
OL
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()
9536ea21 793
5a1f75cb 794
9536ea21
EMS
795class ContratForm(forms.ModelForm):
796
797 class Meta:
9dfa4296 798 fields = ('type_contrat', 'fichier', )
9536ea21
EMS
799 model = dae.Contrat
800
5a1f75cb 801
c3f0b49f
EMS
802class DAENumeriseeForm(forms.ModelForm):
803
804 class Meta:
805 model = dae.Dossier
806 fields = ('dae_numerisee',)
cbfd7bd4
EMS
807
808
809class 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 )