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 def clean_devise(self
):
113 devise
= self
.cleaned_data
['devise']
114 liste_taux
= devise
.tauxchange_set
.order_by('-annee').filter(implantation
=self
.instance
.dossier
.poste
.implantation
)
115 if len(liste_taux
) == 0:
116 raise forms
.ValidationError(u
"La devise %s n'a pas de taux pour l'implantation %s" % (devise
, self
.instance
.dossier
.poste
.implantation
))
121 class RemunForm(inlineformset_factory(dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
)):
124 class PosteForm(forms
.ModelForm
):
125 """ Formulaire des postes. """
127 # On ne propose que les services actifs
128 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(actif
=True), required
=True)
130 responsable
=AutoCompleteSelectField('responsables', required
=True)
131 #responsable = forms.ModelChoiceField(
132 # queryset=rh.Poste.objects.select_related(depth=1))
134 # La liste des choix est laissée vide. Voir __init__ pour la raison.
135 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
136 choices
=(), required
=False)
138 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
139 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
144 exclude
= ('actif', )
145 fields
= ('type_intervention',
146 'poste', 'implantation', 'type_poste', 'service', 'nom',
147 'responsable', 'local', 'expatrie', 'mise_a_disposition',
148 'appel', 'date_debut', 'date_fin',
149 'regime_travail', 'regime_travail_nb_heure_semaine',
150 'classement_min', 'classement_max',
151 'valeur_point_min', 'valeur_point_max',
152 'devise_min', 'devise_max',
153 'salaire_min', 'salaire_max',
154 'indemn_expat_min', 'indemn_expat_max',
155 'indemn_fct_min', 'indemn_fct_max',
156 'charges_patronales_min', 'charges_patronales_max',
157 'autre_min', 'autre_max', 'devise_comparaison',
158 'comp_locale_min', 'comp_locale_max',
159 'comp_universite_min', 'comp_universite_max',
160 'comp_fonctionpub_min', 'comp_fonctionpub_max',
161 'comp_ong_min', 'comp_ong_max',
162 'comp_autre_min', 'comp_autre_max',
165 widgets
= dict(type_intervention
=forms
.RadioSelect(),
166 appel
=forms
.RadioSelect(),
167 nom
=forms
.TextInput(attrs
={'size': 60},),
168 date_debut
=admin_widgets
.AdminDateWidget(),
169 date_fin
=admin_widgets
.AdminDateWidget(),
170 justification
=forms
.Textarea(attrs
={'cols': 80},),
171 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
172 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
175 def __init__(self
, *args
, **kwargs
):
176 """ Mise à jour dynamique du contenu du menu des postes.
178 Si on ne met le menu à jour de cette façon, à chaque instantiation du
179 formulaire, son contenu est mis en cache par le système et il ne
180 reflète pas les changements apportés par les ajouts, modifications,
183 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
184 car le "id" de chaque choix est spécial (voir _poste_choices).
187 request
= kwargs
.pop('request')
188 super(PosteForm
, self
).__init__(*args
, **kwargs
)
189 self
.fields
['poste'].choices
= self
._poste_choices(request
)
190 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
192 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
193 if self
.instance
and self
.instance
.id is None:
194 dossiers
= self
.instance
.get_dossiers()
195 if len(dossiers
) > 0:
196 self
.initial
['service'] = dossiers
[0].service_id
197 self
.initial
['nom'] = "%s %s" % (self
.initial
['nom'], self
.instance
.get_complement_nom())
200 def _poste_choices(self
, request
):
201 """ Menu déroulant pour les postes.
203 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
204 d'équivalent dans dae.
207 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True, id_rh__isnull
=True)
208 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
209 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
210 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True).exclude(id__in
=id_copies
)
211 # Optimisation de la requête
212 rhv1
= rhv1
.select_related(depth
=1)
214 return [('', 'Nouveau poste')] + \
215 sorted([('dae-%s' % p
.id, label_poste_display(p
)) for p
in dae_ | copies
] +
216 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
221 Validation conditionnelles de certains champs.
223 cleaned_data
= self
.cleaned_data
225 # Gestion de la mise à disposition
226 mise_a_disposition
= cleaned_data
.get("mise_a_disposition")
227 valeur_point_min
= cleaned_data
.get("valeur_point_min")
228 valeur_point_max
= cleaned_data
.get("valeur_point_max")
229 if mise_a_disposition
is False and (valeur_point_min
is None or valeur_point_max
is None):
230 msg
= u
"Ce champ est obligatoire."
231 self
._errors
["valeur_point_min"] = self
.error_class([msg
])
232 self
._errors
["valeur_point_max"] = self
.error_class([msg
])
233 raise forms
.ValidationError("Les valeurs de point sont vides")
235 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
236 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
237 self
._errors
["local"] = self
.error_class([msg
])
238 self
._errors
["expatrie"] = ''
239 raise forms
.ValidationError(msg
)
246 def save(self
, *args
, **kwargs
):
247 kwargs2
= kwargs
.copy()
248 kwargs2
['commit'] = False
249 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
251 if 'commit' not in kwargs
or kwargs
['commit']:
256 class ChoosePosteForm(forms
.ModelForm
):
261 # La liste des choix est laissée vide. Voir PosteForm.__init__.
262 poste
= forms
.ChoiceField(choices
=(), required
=False)
264 def __init__(self
, request
=None, *args
, **kwargs
):
265 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
266 self
.fields
['poste'].choices
= self
._poste_choices(request
)
268 def _poste_choices(self
, request
):
269 """ Menu déroulant pour les postes. """
270 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
271 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
272 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
274 return [('', '----------')] + \
275 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
279 class EmployeForm(forms
.ModelForm
):
280 """ Formulaire des employés. """
283 fields
= ('employe', 'nom', 'prenom', 'genre')
285 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
286 employe
= forms
.ChoiceField(choices
=(), required
=False)
288 def __init__(self
, *args
, **kwargs
):
289 """ Mise à jour dynamique du contenu du menu des employés. """
290 request
= kwargs
.pop('request', None)
291 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
292 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
296 class DossierForm(forms
.ModelForm
):
297 """ Formulaire des dossiers. """
301 widgets
= dict(statut_residence
=forms
.RadioSelect(),
302 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
303 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
308 class PosteWorkflowForm(WorkflowFormMixin
):
309 bouton_libelles
= POSTE_ETATS_BOUTONS
314 def __init__(self
, *args
, **kwargs
):
315 super(self
.__class__
, self
).__init__(*args
, **kwargs
)
316 self
.fields
['etat'].help_text
= WF_HELP_TEXT
319 class DossierWorkflowForm(WorkflowFormMixin
):
320 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
325 def __init__(self
, *args
, **kwargs
):
326 super(self
.__class__
, self
).__init__(*args
, **kwargs
)
327 self
.fields
['etat'].help_text
= WF_HELP_TEXT