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 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont pas de Dossier associés
60 employes_avec_dae
= [d
.employe_id
for d
in dae
.Dossier
.objects
.all()]
61 employes_orphelins
= dae
.Employe
.objects
.exclude(id__in
=employes_avec_dae
)
64 def option_label(employe
):
65 return "%s %s" % (employe
.nom
.upper(), employe
.prenom
.title())
67 return [('', 'Nouvel employé')] + \
68 sorted([('dae-%s' % p
.id, option_label(p
)) for p
in dae_ | copies | employes_orphelins
] +
69 [('rh-%s' % p
.id, option_label(p
)) for p
in rhv1
],
72 def label_poste_display(poste
):
73 """Formate un visuel pour un poste dans une liste déroulante"""
74 label
= u
"%s - %s [%s]" %(poste
.type_poste
, poste
.type_poste
.famille_emploi
.nom
, poste
.id)
77 PostePieceForm
= inlineformset_factory(dae
.Poste
, dae
.PostePiece
)
78 DossierPieceForm
= inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)
79 FinancementForm
= inlineformset_factory(dae
.Poste
, dae
.PosteFinancement
, extra
=2)
81 class DossierComparaisonForm(forms
.ModelForm
):
83 recherche
= AutoCompleteSelectField('dossiers', required
=False)
84 poste
= forms
.CharField(max_length
=255, widget
=forms
.TextInput(attrs
={'size':'60'}))
87 model
= dae
.DossierComparaison
89 DossierComparaisonForm
= inlineformset_factory(
90 dae
.Dossier
, dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
93 class PosteComparaisonForm(forms
.ModelForm
):
95 recherche
= AutoCompleteSelectField('postes', required
=False)
98 model
= dae
.PosteComparaison
100 PosteComparaisonForm
= inlineformset_factory(
101 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 if devise
.code
== 'EUR':
116 implantation
= ref
.Implantation
.objects
.get(id=self
.data
['implantation'])
117 liste_taux
= devise
.tauxchange_set
.order_by('-annee').filter(implantation
=implantation
)
118 if len(liste_taux
) == 0:
119 raise forms
.ValidationError(u
"La devise %s n'a pas de taux pour l'implantation %s" % (devise
, implantation
))
123 RemunForm
= inlineformset_factory(
124 dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
127 class PosteForm(forms
.ModelForm
):
128 """ Formulaire des postes. """
130 # On ne propose que les services actifs
131 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(actif
=True), required
=True)
133 responsable
=AutoCompleteSelectField('responsables', required
=True)
134 #responsable = forms.ModelChoiceField(
135 # queryset=rh.Poste.objects.select_related(depth=1))
137 # La liste des choix est laissée vide. Voir __init__ pour la raison.
138 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
139 choices
=(), required
=False)
141 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
142 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
147 exclude
= ('actif', )
148 fields
= ('type_intervention',
149 'poste', 'implantation', 'type_poste', 'service', 'nom',
150 'responsable', 'local', 'expatrie', 'mise_a_disposition',
151 'appel', 'date_debut', 'date_fin',
152 'regime_travail', 'regime_travail_nb_heure_semaine',
153 'classement_min', 'classement_max',
154 'valeur_point_min', 'valeur_point_max',
155 'devise_min', 'devise_max',
156 'salaire_min', 'salaire_max',
157 'indemn_expat_min', 'indemn_expat_max',
158 'indemn_fct_min', 'indemn_fct_max',
159 'charges_patronales_min', 'charges_patronales_max',
160 'autre_min', 'autre_max', 'devise_comparaison',
161 'comp_locale_min', 'comp_locale_max',
162 'comp_universite_min', 'comp_universite_max',
163 'comp_fonctionpub_min', 'comp_fonctionpub_max',
164 'comp_ong_min', 'comp_ong_max',
165 'comp_autre_min', 'comp_autre_max',
168 widgets
= dict(type_intervention
=forms
.RadioSelect(),
169 appel
=forms
.RadioSelect(),
170 nom
=forms
.TextInput(attrs
={'size': 60},),
171 date_debut
=admin_widgets
.AdminDateWidget(),
172 date_fin
=admin_widgets
.AdminDateWidget(),
173 justification
=forms
.Textarea(attrs
={'cols': 80},),
174 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
175 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
178 def __init__(self
, *args
, **kwargs
):
179 """ Mise à jour dynamique du contenu du menu des postes.
181 Si on ne met le menu à jour de cette façon, à chaque instantiation du
182 formulaire, son contenu est mis en cache par le système et il ne
183 reflète pas les changements apportés par les ajouts, modifications,
186 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
187 car le "id" de chaque choix est spécial (voir _poste_choices).
190 request
= kwargs
.pop('request')
191 super(PosteForm
, self
).__init__(*args
, **kwargs
)
192 self
.fields
['poste'].choices
= self
._poste_choices(request
)
193 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
195 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
196 if self
.instance
and self
.instance
.id is None:
197 dossiers
= self
.instance
.get_dossiers()
198 if len(dossiers
) > 0:
199 self
.initial
['service'] = dossiers
[0].service_id
200 self
.initial
['nom'] = "%s %s" % (self
.initial
['nom'], self
.instance
.get_complement_nom())
203 def _poste_choices(self
, request
):
204 """ Menu déroulant pour les postes.
206 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
207 d'équivalent dans dae.
210 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True, id_rh__isnull
=True)
211 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
212 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
213 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True).exclude(id__in
=id_copies
)
214 # Optimisation de la requête
215 rhv1
= rhv1
.select_related(depth
=1)
217 return [('', 'Nouveau poste')] + \
218 sorted([('dae-%s' % p
.id, label_poste_display(p
)) for p
in dae_ | copies
] +
219 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
224 Validation conditionnelles de certains champs.
226 cleaned_data
= self
.cleaned_data
228 # Gestion de la mise à disposition
229 mise_a_disposition
= cleaned_data
.get("mise_a_disposition")
230 valeur_point_min
= cleaned_data
.get("valeur_point_min")
231 valeur_point_max
= cleaned_data
.get("valeur_point_max")
232 if mise_a_disposition
is False and (valeur_point_min
is None or valeur_point_max
is None):
233 msg
= u
"Ce champ est obligatoire."
234 self
._errors
["valeur_point_min"] = self
.error_class([msg
])
235 self
._errors
["valeur_point_max"] = self
.error_class([msg
])
236 raise forms
.ValidationError("Les valeurs de point sont vides")
238 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
239 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
240 self
._errors
["local"] = self
.error_class([msg
])
241 self
._errors
["expatrie"] = ''
242 raise forms
.ValidationError(msg
)
249 def save(self
, *args
, **kwargs
):
250 kwargs2
= kwargs
.copy()
251 kwargs2
['commit'] = False
252 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
254 if 'commit' not in kwargs
or kwargs
['commit']:
259 class ChoosePosteForm(forms
.ModelForm
):
264 # La liste des choix est laissée vide. Voir PosteForm.__init__.
265 poste
= forms
.ChoiceField(choices
=(), required
=False)
267 def __init__(self
, request
=None, *args
, **kwargs
):
268 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
269 self
.fields
['poste'].choices
= self
._poste_choices(request
)
271 def _poste_choices(self
, request
):
272 """ Menu déroulant pour les postes. """
273 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
274 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
275 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
277 return [('', '----------')] + \
278 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
282 class EmployeForm(forms
.ModelForm
):
283 """ Formulaire des employés. """
286 fields
= ('employe', 'nom', 'prenom', 'genre')
288 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
289 employe
= forms
.ChoiceField(choices
=(), required
=False)
291 def __init__(self
, *args
, **kwargs
):
292 """ Mise à jour dynamique du contenu du menu des employés. """
293 request
= kwargs
.pop('request', None)
294 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
295 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
298 class DossierForm(forms
.ModelForm
):
299 """ Formulaire des dossiers. """
303 widgets
= dict(statut_residence
=forms
.RadioSelect(),
304 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
305 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
310 class PosteWorkflowForm(WorkflowFormMixin
):
311 bouton_libelles
= POSTE_ETATS_BOUTONS
316 def __init__(self
, *args
, **kwargs
):
317 super(PosteWorkflowForm
, self
).__init__(*args
, **kwargs
)
318 self
.fields
['etat'].help_text
= WF_HELP_TEXT
321 class DossierWorkflowForm(WorkflowFormMixin
):
322 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
324 fields
= ('etat', 'contrat')
327 def __init__(self
, *args
, **kwargs
):
328 super(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
329 self
.fields
['etat'].help_text
= WF_HELP_TEXT
330 self
._etat_initial
= self
.instance
.etat
333 super(DossierWorkflowForm
, self
).save()
334 poste
= self
.instance
.poste
335 if poste
.etat
== self
._etat_initial
:
336 poste
.etat
= self
.instance
.etat
339 class ContratForm(forms
.ModelForm
):
342 fields
= ('type', 'fichier')