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"""
76 annee
= poste
.date_debut
.year
77 label
= u
"%s %s - %s [%s]" %(annee
, poste
.type_poste
, poste
.type_poste
.famille_emploi
.nom
, poste
.id)
80 PostePieceForm
= inlineformset_factory(dae
.Poste
, dae
.PostePiece
)
81 DossierPieceForm
= inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)
82 FinancementForm
= inlineformset_factory(dae
.Poste
, dae
.PosteFinancement
, extra
=2)
84 class DossierComparaisonForm(forms
.ModelForm
):
86 recherche
= AutoCompleteSelectField('dossiers', required
=False)
87 poste
= forms
.CharField(max_length
=255, widget
=forms
.TextInput(attrs
={'size':'60'}))
90 model
= dae
.DossierComparaison
91 exclude
= ('dossier',)
93 DossierComparaisonFormSet
= modelformset_factory(
94 dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
97 class PosteComparaisonForm(forms
.ModelForm
):
99 recherche
= AutoCompleteSelectField('dae_postes', required
=False)
102 model
= dae
.PosteComparaison
105 PosteComparaisonFormSet
= modelformset_factory(
106 dae
.PosteComparaison
, extra
=3, max_num
=3, form
=PosteComparaisonForm
109 class FlexibleRemunForm(forms
.ModelForm
):
111 montant_mensuel
= forms
.DecimalField(required
=False)
112 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
115 model
= dae
.Remuneration
117 def clean_devise(self
):
118 devise
= self
.cleaned_data
['devise']
119 if devise
.code
== 'EUR':
121 implantation
= ref
.Implantation
.objects
.get(id=self
.data
['implantation'])
122 liste_taux
= devise
.tauxchange_set
.order_by('-annee')
123 if len(liste_taux
) == 0:
124 raise forms
.ValidationError(u
"La devise %s n'a pas de taux pour l'implantation %s" % (devise
, implantation
))
128 RemunForm
= inlineformset_factory(
129 dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
132 class PosteForm(forms
.ModelForm
):
133 """ Formulaire des postes. """
135 # On ne propose que les services actifs
136 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(archive
=False), required
=True)
138 responsable
=AutoCompleteSelectField('responsables', required
=True)
139 #responsable = forms.ModelChoiceField(
140 # queryset=rh.Poste.objects.select_related(depth=1))
142 # La liste des choix est laissée vide. Voir __init__ pour la raison.
143 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
144 choices
=(), required
=False)
146 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
147 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
152 fields
= ('type_intervention',
153 'poste', 'implantation', 'type_poste', 'service', 'nom',
154 'responsable', 'local', 'expatrie', 'mise_a_disposition',
155 'appel', 'date_debut', 'date_fin',
156 'regime_travail', 'regime_travail_nb_heure_semaine',
157 'classement_min', 'classement_max',
158 'valeur_point_min', 'valeur_point_max',
159 'devise_min', 'devise_max',
160 'salaire_min', 'salaire_max',
161 'indemn_expat_min', 'indemn_expat_max',
162 'indemn_fct_min', 'indemn_fct_max',
163 'charges_patronales_min', 'charges_patronales_max',
164 'autre_min', 'autre_max', 'devise_comparaison',
165 'comp_locale_min', 'comp_locale_max',
166 'comp_universite_min', 'comp_universite_max',
167 'comp_fonctionpub_min', 'comp_fonctionpub_max',
168 'comp_ong_min', 'comp_ong_max',
169 'comp_autre_min', 'comp_autre_max',
172 widgets
= dict(type_intervention
=forms
.RadioSelect(),
173 appel
=forms
.RadioSelect(),
174 nom
=forms
.TextInput(attrs
={'size': 60},),
175 date_debut
=admin_widgets
.AdminDateWidget(),
176 date_fin
=admin_widgets
.AdminDateWidget(),
177 justification
=forms
.Textarea(attrs
={'cols': 80},),
178 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
179 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
182 def __init__(self
, *args
, **kwargs
):
183 """ Mise à jour dynamique du contenu du menu des postes.
185 Si on ne met le menu à jour de cette façon, à chaque instantiation du
186 formulaire, son contenu est mis en cache par le système et il ne
187 reflète pas les changements apportés par les ajouts, modifications,
190 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
191 car le "id" de chaque choix est spécial (voir _poste_choices).
194 request
= kwargs
.pop('request')
195 super(PosteForm
, self
).__init__(*args
, **kwargs
)
196 self
.fields
['poste'].choices
= self
._poste_choices(request
)
197 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
199 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
200 if self
.instance
and self
.instance
.id is None:
201 dossiers
= self
.instance
.get_dossiers()
202 if len(dossiers
) > 0:
203 self
.initial
['service'] = dossiers
[0].poste
.service
206 def _poste_choices(self
, request
):
207 """ Menu déroulant pour les postes.
209 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
210 d'équivalent dans dae.
213 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
214 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
215 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
216 rhv1
= rh
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id__in
=id_copies
)
217 # Optimisation de la requête
218 rhv1
= rhv1
.select_related(depth
=1)
220 return [('', 'Nouveau poste')] + \
221 sorted([('dae-%s' % p
.id, label_poste_display(p
)) for p
in dae_ | copies
] +
222 [('rh-%s' % p
.id, label_poste_display(p
)) for p
in rhv1
],
227 Validation conditionnelles de certains champs.
229 cleaned_data
= self
.cleaned_data
231 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
232 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
233 self
._errors
["local"] = self
.error_class([msg
])
234 self
._errors
["expatrie"] = ''
235 raise forms
.ValidationError(msg
)
241 def save(self
, *args
, **kwargs
):
242 kwargs2
= kwargs
.copy()
243 kwargs2
['commit'] = False
244 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
246 if 'commit' not in kwargs
or kwargs
['commit']:
251 class ChoosePosteForm(forms
.ModelForm
):
256 # La liste des choix est laissée vide. Voir PosteForm.__init__.
257 poste
= forms
.ChoiceField(choices
=(), required
=False)
259 def __init__(self
, request
=None, *args
, **kwargs
):
260 super(ChoosePosteForm
, self
).__init__(*args
, **kwargs
)
261 self
.fields
['poste'].choices
= self
._poste_choices(request
)
263 def _poste_choices(self
, request
):
264 """ Menu déroulant pour les postes. """
265 dae_
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).filter(id_rh__isnull
=True)
266 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True)
267 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
269 return [('', '----------')] + \
270 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
274 class EmployeForm(forms
.ModelForm
):
275 """ Formulaire des employés. """
278 fields
= ('employe', 'nom', 'prenom', 'genre')
280 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
281 employe
= forms
.ChoiceField(choices
=(), required
=False)
283 def __init__(self
, *args
, **kwargs
):
284 """ Mise à jour dynamique du contenu du menu des employés. """
285 request
= kwargs
.pop('request', None)
286 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
287 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
290 class DossierForm(forms
.ModelForm
):
291 """ Formulaire des dossiers. """
293 exclude
= ('etat', 'employe', 'poste', 'date_debut', )
295 widgets
= dict(statut_residence
=forms
.RadioSelect(),
296 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
297 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
302 class PosteWorkflowForm(WorkflowFormMixin
):
303 bouton_libelles
= POSTE_ETATS_BOUTONS
308 def __init__(self
, *args
, **kwargs
):
309 super(PosteWorkflowForm
, self
).__init__(*args
, **kwargs
)
310 self
.fields
['etat'].help_text
= WF_HELP_TEXT
313 class DossierWorkflowForm(WorkflowFormMixin
):
314 bouton_libelles
= POSTE_ETATS_BOUTONS
# meme workflow que poste...
319 def __init__(self
, *args
, **kwargs
):
320 super(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
321 self
.fields
['etat'].help_text
= WF_HELP_TEXT
322 self
._etat_initial
= self
.instance
.etat
325 super(DossierWorkflowForm
, self
).save()
326 poste
= self
.instance
.poste
327 if poste
.etat
== self
._etat_initial
:
328 poste
.etat
= self
.instance
.etat
331 class ContratForm(forms
.ModelForm
):
334 fields
= ('type_contrat', )
337 class DAENumeriseeForm(forms
.ModelForm
):
341 fields
= ('dae_numerisee',)
343 class DAEImportableForm(forms
.Form
):
344 qs_poste
= dae
.Poste
.objects
.filter(etat
=POSTE_ETAT_FINALISE
)
345 qs_dossier
= dae
.Dossier
.objects
.filter(etat
=DOSSIER_ETAT_FINALISE
)
346 poste
= forms
.ModelChoiceField(queryset
=qs_poste
, label
="Poste finalisé", required
=False)
347 dossier
= forms
.ModelChoiceField(queryset
=qs_dossier
, label
="DAE finalisée", required
=False)
349 def clean_poste(self
):
350 poste
= self
.cleaned_data
['poste']
351 if poste
is not None and poste
.est_importe():
352 raise forms
.ValidationError("Ce poste a déjà été importé")
355 def clean_dossier(self
):
356 dossier
= self
.cleaned_data
['dossier']
357 if dossier
is not None and not dossier
.poste
.est_importe():
358 raise forms
.ValidationError("Le poste de ce dossier doit être importé avant de pouvoir importer le dossier.")
361 def importer_poste(self
):
362 poste
= self
.cleaned_data
['poste']
363 if poste
is not None and not poste
.est_importe():