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 auf
.django
.references
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
import models
as rh
13 from workflow
import grp_drh
, POSTE_ETATS_BOUTONS
, DOSSIER_ETAT_FINALISE
, POSTE_ETAT_FINALISE
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(poste__implantation
=employe
.implantation
)
39 q_dae_region_service
= Q(poste__implantation__region
=employe
.implantation
.region
)
40 q_rh_region_service
= Q(poste__implantation__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('dae_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(archive
=False), 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 fields
= ('type_intervention',
150 'poste', 'implantation', 'type_poste', 'service', 'nom',
151 'responsable', 'local', 'expatrie', 'mise_a_disposition',
152 'appel', 'date_debut', 'date_fin',
153 'regime_travail', 'regime_travail_nb_heure_semaine',
154 'classement_min', 'classement_max',
155 'valeur_point_min', 'valeur_point_max',
156 'devise_min', 'devise_max',
157 'salaire_min', 'salaire_max',
158 'indemn_expat_min', 'indemn_expat_max',
159 'indemn_fct_min', 'indemn_fct_max',
160 'charges_patronales_min', 'charges_patronales_max',
161 'autre_min', 'autre_max', 'devise_comparaison',
162 'comp_locale_min', 'comp_locale_max',
163 'comp_universite_min', 'comp_universite_max',
164 'comp_fonctionpub_min', 'comp_fonctionpub_max',
165 'comp_ong_min', 'comp_ong_max',
166 'comp_autre_min', 'comp_autre_max',
169 widgets
= dict(type_intervention
=forms
.RadioSelect(),
170 appel
=forms
.RadioSelect(),
171 nom
=forms
.TextInput(attrs
={'size': 60},),
172 date_debut
=admin_widgets
.AdminDateWidget(),
173 date_fin
=admin_widgets
.AdminDateWidget(),
174 justification
=forms
.Textarea(attrs
={'cols': 80},),
175 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
176 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
179 def __init__(self
, *args
, **kwargs
):
180 """ Mise à jour dynamique du contenu du menu des postes.
182 Si on ne met le menu à jour de cette façon, à chaque instantiation du
183 formulaire, son contenu est mis en cache par le système et il ne
184 reflète pas les changements apportés par les ajouts, modifications,
187 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
188 car le "id" de chaque choix est spécial (voir _poste_choices).
191 request
= kwargs
.pop('request')
192 super(PosteForm
, self
).__init__(*args
, **kwargs
)
193 self
.fields
['poste'].choices
= self
._poste_choices(request
)
194 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
196 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
197 if self
.instance
and self
.instance
.id is None:
198 dossiers
= self
.instance
.get_dossiers()
199 if len(dossiers
) > 0:
200 self
.initial
['service'] = dossiers
[0].poste
.service
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(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
).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 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
229 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
230 self
._errors
["local"] = self
.error_class([msg
])
231 self
._errors
["expatrie"] = ''
232 raise forms
.ValidationError(msg
)
238 def save(self
, *args
, **kwargs
):
239 kwargs2
= kwargs
.copy()
240 kwargs2
['commit'] = False
241 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
243 if 'commit' not in kwargs
or kwargs
['commit']:
248 class ChoosePosteForm(forms
.ModelForm
):
253 # La liste des choix est laissée vide. Voir PosteForm.__init__.
254 poste
= forms
.ChoiceField(choices
=(), required
=False)
256 def __init__(self
, request
=None, *args
, **kwargs
):
257 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
258 self
.fields
['poste'].choices
= self
._poste_choices(request
)
260 def _poste_choices(self
, request
):
261 """ Menu déroulant pour les postes. """
262 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
263 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
264 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
266 return [('', '----------')] + \
267 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
271 class EmployeForm(forms
.ModelForm
):
272 """ Formulaire des employés. """
275 fields
= ('employe', 'nom', 'prenom', 'genre')
277 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
278 employe
= forms
.ChoiceField(choices
=(), required
=False)
280 def __init__(self
, *args
, **kwargs
):
281 """ Mise à jour dynamique du contenu du menu des employés. """
282 request
= kwargs
.pop('request', None)
283 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
284 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
287 class DossierForm(forms
.ModelForm
):
288 """ Formulaire des dossiers. """
290 exclude
= ('etat', 'employe', 'poste', 'date_debut', )
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(PosteWorkflowForm
, 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(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
318 self
.fields
['etat'].help_text
= WF_HELP_TEXT
319 self
._etat_initial
= self
.instance
.etat
322 super(DossierWorkflowForm
, self
).save()
323 poste
= self
.instance
.poste
324 if poste
.etat
== self
._etat_initial
:
325 poste
.etat
= self
.instance
.etat
328 class ContratForm(forms
.ModelForm
):
331 fields
= ('type_contrat', )
334 class DAENumeriseeForm(forms
.ModelForm
):
338 fields
= ('dae_numerisee',)
340 class DAEImportableForm(forms
.Form
):
341 qs_poste
= dae
.Poste
.objects
.filter(etat
=POSTE_ETAT_FINALISE
)
342 qs_dossier
= dae
.Dossier
.objects
.filter(etat
=DOSSIER_ETAT_FINALISE
)
343 poste
= forms
.ModelChoiceField(queryset
=qs_poste
, label
="Poste finalisé", required
=False)
344 dossier
= forms
.ModelChoiceField(queryset
=qs_dossier
, label
="DAE finalisée", required
=False)
346 def clean_poste(self
):
347 poste
= self
.cleaned_data
['poste']
348 if poste
is not None and poste
.est_importe():
349 raise forms
.ValidationError("Ce poste a déjà été importé")
352 def clean_dossier(self
):
353 dossier
= self
.cleaned_data
['dossier']
354 if dossier
is not None and not dossier
.poste
.est_importe():
355 raise forms
.ValidationError("Le poste de ce dossier doit être importé avant de pouvoir importer le dossier.")
358 def importer_poste(self
):
359 poste
= self
.cleaned_data
['poste']
360 if poste
is not None and not poste
.est_importe():