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
, modelformset_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
88 exclude
= ('dossier',)
90 DossierComparaisonFormSet
= modelformset_factory(
91 dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
94 class PosteComparaisonForm(forms
.ModelForm
):
96 recherche
= AutoCompleteSelectField('postes', required
=False)
99 model
= dae
.PosteComparaison
102 PosteComparaisonFormSet
= modelformset_factory(
103 dae
.PosteComparaison
, extra
=3, max_num
=3, form
=PosteComparaisonForm
106 class FlexibleRemunForm(forms
.ModelForm
):
108 montant_mensuel
= forms
.DecimalField(required
=False)
109 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
112 model
= dae
.Remuneration
114 def clean_devise(self
):
115 devise
= self
.cleaned_data
['devise']
116 if devise
.code
== 'EUR':
118 implantation
= ref
.Implantation
.objects
.get(id=self
.data
['implantation'])
119 liste_taux
= devise
.tauxchange_set
.order_by('-annee')
120 if len(liste_taux
) == 0:
121 raise forms
.ValidationError(u
"La devise %s n'a pas de taux pour l'implantation %s" % (devise
, implantation
))
125 RemunForm
= inlineformset_factory(
126 dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
129 class PosteForm(forms
.ModelForm
):
130 """ Formulaire des postes. """
132 # On ne propose que les services actifs
133 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(actif
=True), required
=True)
135 responsable
=AutoCompleteSelectField('responsables', required
=True)
136 #responsable = forms.ModelChoiceField(
137 # queryset=rh.Poste.objects.select_related(depth=1))
139 # La liste des choix est laissée vide. Voir __init__ pour la raison.
140 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
141 choices
=(), required
=False)
143 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
144 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
149 exclude
= ('actif', )
150 fields
= ('type_intervention',
151 'poste', 'implantation', 'type_poste', 'service', 'nom',
152 'responsable', 'local', 'expatrie', 'mise_a_disposition',
153 'appel', 'date_debut', 'date_fin',
154 'regime_travail', 'regime_travail_nb_heure_semaine',
155 'classement_min', 'classement_max',
156 'valeur_point_min', 'valeur_point_max',
157 'devise_min', 'devise_max',
158 'salaire_min', 'salaire_max',
159 'indemn_expat_min', 'indemn_expat_max',
160 'indemn_fct_min', 'indemn_fct_max',
161 'charges_patronales_min', 'charges_patronales_max',
162 'autre_min', 'autre_max', 'devise_comparaison',
163 'comp_locale_min', 'comp_locale_max',
164 'comp_universite_min', 'comp_universite_max',
165 'comp_fonctionpub_min', 'comp_fonctionpub_max',
166 'comp_ong_min', 'comp_ong_max',
167 'comp_autre_min', 'comp_autre_max',
170 widgets
= dict(type_intervention
=forms
.RadioSelect(),
171 appel
=forms
.RadioSelect(),
172 nom
=forms
.TextInput(attrs
={'size': 60},),
173 date_debut
=admin_widgets
.AdminDateWidget(),
174 date_fin
=admin_widgets
.AdminDateWidget(),
175 justification
=forms
.Textarea(attrs
={'cols': 80},),
176 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
177 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
180 def __init__(self
, *args
, **kwargs
):
181 """ Mise à jour dynamique du contenu du menu des postes.
183 Si on ne met le menu à jour de cette façon, à chaque instantiation du
184 formulaire, son contenu est mis en cache par le système et il ne
185 reflète pas les changements apportés par les ajouts, modifications,
188 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
189 car le "id" de chaque choix est spécial (voir _poste_choices).
192 request
= kwargs
.pop('request')
193 super(PosteForm
, self
).__init__(*args
, **kwargs
)
194 self
.fields
['poste'].choices
= self
._poste_choices(request
)
195 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
197 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
198 if self
.instance
and self
.instance
.id is None:
199 dossiers
= self
.instance
.get_dossiers()
200 if len(dossiers
) > 0:
201 self
.initial
['service'] = dossiers
[0].service_id
202 self
.initial
['nom'] = "%s %s" % (self
.initial
['nom'], self
.instance
.get_complement_nom())
205 def _poste_choices(self
, request
):
206 """ Menu déroulant pour les postes.
208 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
209 d'équivalent dans dae.
212 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True, id_rh__isnull
=True)
213 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
214 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
215 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(actif
=True).exclude(id__in
=id_copies
)
216 # Optimisation de la requête
217 rhv1
= rhv1
.select_related(depth
=1)
219 return [('', 'Nouveau poste')] + \
220 sorted([('dae-%s' % p
.id, label_poste_display(p
)) for p
in dae_ | copies
] +
221 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
226 Validation conditionnelles de certains champs.
228 cleaned_data
= self
.cleaned_data
230 # Gestion de la mise à disposition
231 mise_a_disposition
= cleaned_data
.get("mise_a_disposition")
232 valeur_point_min
= cleaned_data
.get("valeur_point_min")
233 valeur_point_max
= cleaned_data
.get("valeur_point_max")
234 if mise_a_disposition
is False and (valeur_point_min
is None or valeur_point_max
is None):
235 msg
= u
"Ce champ est obligatoire."
236 self
._errors
["valeur_point_min"] = self
.error_class([msg
])
237 self
._errors
["valeur_point_max"] = self
.error_class([msg
])
238 raise forms
.ValidationError("Les valeurs de point sont vides")
240 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
241 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
242 self
._errors
["local"] = self
.error_class([msg
])
243 self
._errors
["expatrie"] = ''
244 raise forms
.ValidationError(msg
)
251 def save(self
, *args
, **kwargs
):
252 kwargs2
= kwargs
.copy()
253 kwargs2
['commit'] = False
254 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
256 if 'commit' not in kwargs
or kwargs
['commit']:
261 class ChoosePosteForm(forms
.ModelForm
):
266 # La liste des choix est laissée vide. Voir PosteForm.__init__.
267 poste
= forms
.ChoiceField(choices
=(), required
=False)
269 def __init__(self
, request
=None, *args
, **kwargs
):
270 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
271 self
.fields
['poste'].choices
= self
._poste_choices(request
)
273 def _poste_choices(self
, request
):
274 """ Menu déroulant pour les postes. """
275 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
276 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
277 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
279 return [('', '----------')] + \
280 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
284 class EmployeForm(forms
.ModelForm
):
285 """ Formulaire des employés. """
288 fields
= ('employe', 'nom', 'prenom', 'genre')
290 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
291 employe
= forms
.ChoiceField(choices
=(), required
=False)
293 def __init__(self
, *args
, **kwargs
):
294 """ Mise à jour dynamique du contenu du menu des employés. """
295 request
= kwargs
.pop('request', None)
296 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
297 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
300 class DossierForm(forms
.ModelForm
):
301 """ Formulaire des dossiers. """
305 widgets
= dict(statut_residence
=forms
.RadioSelect(),
306 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
307 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
312 class PosteWorkflowForm(WorkflowFormMixin
):
313 bouton_libelles
= POSTE_ETATS_BOUTONS
318 def __init__(self
, *args
, **kwargs
):
319 super(PosteWorkflowForm
, self
).__init__(*args
, **kwargs
)
320 self
.fields
['etat'].help_text
= WF_HELP_TEXT
323 class DossierWorkflowForm(WorkflowFormMixin
):
324 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
329 def __init__(self
, *args
, **kwargs
):
330 super(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
331 self
.fields
['etat'].help_text
= WF_HELP_TEXT
332 self
._etat_initial
= self
.instance
.etat
335 super(DossierWorkflowForm
, self
).save()
336 poste
= self
.instance
.poste
337 if poste
.etat
== self
._etat_initial
:
338 poste
.etat
= self
.instance
.etat
341 class ContratForm(forms
.ModelForm
):
344 fields
= ('type', 'fichier')
347 class DAENumeriseeForm(forms
.ModelForm
):
351 fields
= ('dae_numerisee',)