1 # -*- encoding: utf-8 -*-
3 from django
.db
.models
import Q
, Max
4 from django
import forms
5 from django
.forms
.models
import inlineformset_factory
6 from django
.contrib
.admin
import widgets
as admin_widgets
7 from ajax_select
.fields
import AutoCompleteSelectField
8 from auf
.django
.workflow
.forms
import WorkflowFormMixin
9 from datamaster_modeles
import models
as ref
10 from dae
import models
as dae
11 from utils
import get_employe_from_user
, is_user_dans_services_centraux
12 from rh_v1
import models
as rh
13 from workflow
import grp_drh
, POSTE_ETATS_BOUTONS
15 def _implantation_choices(obj
, request
):
17 employe
= get_employe_from_user(request
.user
)
19 if is_user_dans_services_centraux(request
.user
):
20 q
= Q(**{ 'id' : employe
.implantation_id
})
23 q
= Q(**{ 'region' : employe
.implantation
.region
})
26 if grp_drh
in request
.user
.groups
.all():
28 return [('', '----------')] + [(i
.id, unicode(i
), )for i
in ref
.Implantation
.objects
.filter(q
)]
30 def _employe_choices(obj
, request
):
32 employe
= get_employe_from_user(request
.user
)
34 if is_user_dans_services_centraux(request
.user
):
35 q_dae_region_service
= Q(poste__implantation
=employe
.implantation
)
36 q_rh_region_service
= Q(implantation1
=employe
.implantation
) |
Q(implantation2
=employe
.implantation
)
39 q_dae_region_service
= Q(poste__implantation__region
=employe
.implantation
.region
)
40 q_rh_region_service
= Q(implantation1__region
=employe
.implantation
.region
) |
Q(implantation2__region
=employe
.implantation
.region
)
42 if grp_drh
in request
.user
.groups
.all():
43 q_dae_region_service
= Q()
44 q_rh_region_service
= Q()
46 # On filtre les employes avec les droits régionaux et on s'assure que c'est bien le dernier dossier en date pour sortir l'employe
47 # On retient un employé qui travaille présentement dans la même région que le user connecté.
48 dossiers_regionaux_ids
= [d
.id for d
in dae
.Dossier
.objects
.filter(q_dae_region_service
)]
49 employes_ids
= [d
['employe'] for d
in dae
.Dossier
.objects
.values('employe').annotate(dernier_dossier
=Max('id')) if d
['dernier_dossier'] in dossiers_regionaux_ids
]
50 dae_employe
= dae
.Employe
.objects
.filter(id__in
=employes_ids
)
51 dae_
= dae_employe
.filter(id_rh__isnull
=True)
52 copies
= dae_employe
.filter(Q(id_rh__isnull
=False))
53 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
55 dossiers_regionaux_ids
= [d
.id for d
in rh
.Dossier
.objects
.filter(q_rh_region_service
)]
56 employes_ids
= [d
['employe'] for d
in rh
.Dossier
.objects
.values('employe').annotate(dernier_dossier
=Max('id')) if d
['dernier_dossier'] in dossiers_regionaux_ids
]
57 rhv1
= rh
.Employe
.objects
.filter(id__in
=employes_ids
).exclude(id__in
=id_copies
)
59 def option_label(employe
):
60 return "%s %s" % (employe
.nom
.upper(), employe
.prenom
.title())
62 return [('', 'Nouvel employé')] + \
63 sorted([('dae-%s' % p
.id, option_label(p
)) for p
in dae_ | copies
] +
64 [('rh-%s' % p
.id, option_label(p
)) for p
in rhv1
],
68 def label_poste_display(poste
):
69 """Formate un visuel pour un poste dans une liste déroulante"""
70 label
= u
"%s - %s [%s]" %(poste
.type_poste
, poste
.type_poste
.famille_emploi
.nom
, poste
.id)
73 class PostePieceForm(inlineformset_factory(dae
.Poste
, dae
.PostePiece
)):
76 class DossierPieceForm(inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)):
79 class FinancementForm(inlineformset_factory(dae
.Poste
, dae
.PosteFinancement
, extra
=2)):
83 class DossierComparaisonForm(forms
.ModelForm
):
85 recherche
= AutoCompleteSelectField('dossiers', required
=False)
86 poste
= forms
.CharField(max_length
=255, widget
=forms
.TextInput(attrs
={'size':'60'}))
89 model
= dae
.DossierComparaison
91 class DossierComparaisonForm(inlineformset_factory(dae
.Dossier
, dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
)):
94 class PosteComparaisonForm(forms
.ModelForm
):
96 recherche
= AutoCompleteSelectField('postes', required
=False)
99 model
= dae
.PosteComparaison
101 class PosteComparaisonForm(inlineformset_factory(dae
.Poste
, dae
.PosteComparaison
, extra
=3, max_num
=3, form
=PosteComparaisonForm
)):
104 class FlexibleRemunForm(forms
.ModelForm
):
106 montant_mensuel
= forms
.DecimalField(required
=False)
107 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
110 model
= dae
.Remuneration
112 class RemunForm(inlineformset_factory(dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
)):
115 class PosteForm(forms
.ModelForm
):
116 """ Formulaire des postes. """
118 # On ne propose que les services actifs
119 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(actif
=True), required
=True)
121 responsable
=AutoCompleteSelectField('responsables', required
=True)
122 #responsable = forms.ModelChoiceField(
123 # queryset=rh.Poste.objects.select_related(depth=1))
125 # La liste des choix est laissée vide. Voir __init__ pour la raison.
126 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
127 choices
=(), required
=False)
129 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
130 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
135 exclude
= ('actif', )
136 fields
= ('type_intervention',
137 'poste', 'implantation', 'type_poste', 'service', 'nom',
138 'responsable', 'local', 'expatrie', 'mise_a_disposition',
139 'appel', 'date_debut', 'date_fin',
140 'regime_travail', 'regime_travail_nb_heure_semaine',
141 'classement_min', 'classement_max',
142 'valeur_point_min', 'valeur_point_max',
143 'devise_min', 'devise_max',
144 'salaire_min', 'salaire_max',
145 'indemn_expat_min', 'indemn_expat_max',
146 'indemn_fct_min', 'indemn_fct_max',
147 'charges_patronales_min', 'charges_patronales_max',
148 'autre_min', 'autre_max', 'devise_comparaison',
149 'comp_locale_min', 'comp_locale_max',
150 'comp_universite_min', 'comp_universite_max',
151 'comp_fonctionpub_min', 'comp_fonctionpub_max',
152 'comp_ong_min', 'comp_ong_max',
153 'comp_autre_min', 'comp_autre_max',
156 widgets
= dict(type_intervention
=forms
.RadioSelect(),
157 appel
=forms
.RadioSelect(),
158 nom
=forms
.TextInput(attrs
={'size': 60},),
159 date_debut
=admin_widgets
.AdminDateWidget(),
160 date_fin
=admin_widgets
.AdminDateWidget(),
161 justification
=forms
.Textarea(attrs
={'cols': 80},),
162 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
163 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
166 def __init__(self
, *args
, **kwargs
):
167 """ Mise à jour dynamique du contenu du menu des postes.
169 Si on ne met le menu à jour de cette façon, à chaque instantiation du
170 formulaire, son contenu est mis en cache par le système et il ne
171 reflète pas les changements apportés par les ajouts, modifications,
174 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
175 car le "id" de chaque choix est spécial (voir _poste_choices).
178 request
= kwargs
.pop('request')
179 super(PosteForm
, self
).__init__(*args
, **kwargs
)
180 self
.fields
['poste'].choices
= self
._poste_choices(request
)
181 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
183 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
184 if self
.instance
and self
.instance
.id is None:
185 dossiers
= self
.instance
.get_dossiers()
186 if len(dossiers
) > 0:
187 self
.initial
['service'] = dossiers
[0].service_id
188 self
.initial
['nom'] = "%s %s" % (self
.initial
['nom'], self
.instance
.get_complement_nom())
191 def _poste_choices(self
, request
):
192 """ Menu déroulant pour les postes.
194 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
195 d'équivalent dans dae.
198 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True, id_rh__isnull
=True)
199 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
200 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
201 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True).exclude(id__in
=id_copies
)
202 # Optimisation de la requête
203 rhv1
= rhv1
.select_related(depth
=1)
205 return [('', 'Nouveau poste')] + \
206 sorted([('dae-%s' % p
.id, label_poste_display(p
)) for p
in dae_ | copies
] +
207 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
212 Validation conditionnelles de certains champs.
214 cleaned_data
= self
.cleaned_data
216 # Gestion de la mise à disposition
217 mise_a_disposition
= cleaned_data
.get("mise_a_disposition")
218 valeur_point_min
= cleaned_data
.get("valeur_point_min")
219 valeur_point_max
= cleaned_data
.get("valeur_point_max")
220 if mise_a_disposition
is False and (valeur_point_min
is None or valeur_point_max
is None):
221 msg
= u
"Ce champ est obligatoire."
222 self
._errors
["valeur_point_min"] = self
.error_class([msg
])
223 self
._errors
["valeur_point_max"] = self
.error_class([msg
])
224 raise forms
.ValidationError("Les valeurs de point sont vides")
226 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
227 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
228 self
._errors
["local"] = self
.error_class([msg
])
229 self
._errors
["expatrie"] = ''
230 raise forms
.ValidationError(msg
)
237 def save(self
, *args
, **kwargs
):
238 kwargs2
= kwargs
.copy()
239 kwargs2
['commit'] = False
240 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
242 if 'commit' not in kwargs
or kwargs
['commit']:
247 class ChoosePosteForm(forms
.ModelForm
):
252 # La liste des choix est laissée vide. Voir PosteForm.__init__.
253 poste
= forms
.ChoiceField(choices
=(), required
=False)
255 def __init__(self
, request
=None, *args
, **kwargs
):
256 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
257 self
.fields
['poste'].choices
= self
._poste_choices(request
)
259 def _poste_choices(self
, request
):
260 """ Menu déroulant pour les postes. """
261 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
262 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
263 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
265 return [('', '----------')] + \
266 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
270 class EmployeForm(forms
.ModelForm
):
271 """ Formulaire des employés. """
274 fields
= ('employe', 'nom', 'prenom', 'genre')
276 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
277 employe
= forms
.ChoiceField(choices
=(), required
=False)
279 def __init__(self
, *args
, **kwargs
):
280 """ Mise à jour dynamique du contenu du menu des employés. """
281 request
= kwargs
.pop('request', None)
282 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
283 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
287 class DossierForm(forms
.ModelForm
):
288 """ Formulaire des dossiers. """
292 widgets
= dict(statut_residence
=forms
.RadioSelect(),
293 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
294 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
299 class PosteWorkflowForm(WorkflowFormMixin
):
300 bouton_libelles
= POSTE_ETATS_BOUTONS
305 def __init__(self
, *args
, **kwargs
):
306 super(self
.__class__
, self
).__init__(*args
, **kwargs
)
307 self
.fields
['etat'].help_text
= WF_HELP_TEXT
310 class DossierWorkflowForm(WorkflowFormMixin
):
311 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
316 def __init__(self
, *args
, **kwargs
):
317 super(self
.__class__
, self
).__init__(*args
, **kwargs
)
318 self
.fields
['etat'].help_text
= WF_HELP_TEXT