1 # -*- encoding: utf-8 -*-
4 from django
.db
.models
import Q
, Max
5 from django
import forms
6 from django
.forms
.models
import inlineformset_factory
, modelformset_factory
7 from django
.contrib
.admin
import widgets
as admin_widgets
8 from ajax_select
.fields
import AutoCompleteSelectField
9 from auf
.django
.workflow
.forms
import WorkflowFormMixin
10 from auf
.django
.references
import models
as ref
11 from dae
import models
as dae
12 from utils
import get_employe_from_user
, is_user_dans_services_centraux
13 from rh
import models
as rh
14 from workflow
import grp_drh
, POSTE_ETATS_BOUTONS
, DOSSIER_ETAT_FINALISE
, POSTE_ETAT_FINALISE
16 def _implantation_choices(obj
, request
):
18 employe
= get_employe_from_user(request
.user
)
20 if is_user_dans_services_centraux(request
.user
):
21 q
= Q(**{ 'id' : employe
.implantation_id
})
24 q
= Q(**{ 'region' : employe
.implantation
.region
})
27 if grp_drh
in request
.user
.groups
.all():
29 return [('', '----------')] + [(i
.id, unicode(i
), )for i
in ref
.Implantation
.objects
.filter(q
)]
31 def _employe_choices(obj
, request
):
33 employe
= get_employe_from_user(request
.user
)
35 if is_user_dans_services_centraux(request
.user
):
36 q_dae_region_service
= Q(poste__implantation
=employe
.implantation
)
37 q_rh_region_service
= Q(poste__implantation
=employe
.implantation
)
40 q_dae_region_service
= Q(poste__implantation__region
=employe
.implantation
.region
)
41 q_rh_region_service
= Q(poste__implantation__region
=employe
.implantation
.region
)
43 if grp_drh
in request
.user
.groups
.all():
44 q_dae_region_service
= Q()
45 q_rh_region_service
= Q()
47 # 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
48 # On retient un employé qui travaille présentement dans la même région que le user connecté.
49 dossiers_regionaux_ids
= [d
.id for d
in dae
.Dossier
.objects
.filter(q_dae_region_service
)]
50 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
]
51 dae_employe
= dae
.Employe
.objects
.filter(id__in
=employes_ids
)
52 dae_
= dae_employe
.filter(id_rh__isnull
=True)
53 copies
= dae_employe
.filter(Q(id_rh__isnull
=False))
54 id_copies
= [p
.id_rh_id
for p
in copies
.all()]
56 dossiers_regionaux_ids
= [d
.id for d
in rh
.Dossier
.objects
.filter(q_rh_region_service
)]
57 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
]
58 rhv1
= rh
.Employe
.objects
.filter(id__in
=employes_ids
).exclude(id__in
=id_copies
)
60 # On ajoute les nouveaux Employés DAE qui ont été crées, mais qui n'ont pas de Dossier associés
61 employes_avec_dae
= [d
.employe_id
for d
in dae
.Dossier
.objects
.all()]
62 employes_orphelins
= dae
.Employe
.objects
.exclude(id__in
=employes_avec_dae
)
65 def option_label(employe
):
66 return "%s %s" % (employe
.nom
.upper(), employe
.prenom
.title())
68 return [('', 'Nouvel employé')] + \
69 sorted([('dae-%s' % p
.id, option_label(p
)) for p
in dae_ | copies | employes_orphelins
] +
70 [('rh-%s' % p
.id, option_label(p
)) for p
in rhv1
],
73 def label_poste_display(poste
):
74 """Formate un visuel pour un poste dans une liste déroulante"""
77 annee
= poste
.date_debut
.year
78 label
= u
"%s %s - %s [%s]" %(annee
, poste
.type_poste
, poste
.type_poste
.famille_emploi
.nom
, poste
.id)
81 PostePieceForm
= inlineformset_factory(dae
.Poste
, dae
.PostePiece
)
82 DossierPieceForm
= inlineformset_factory(dae
.Dossier
, dae
.DossierPiece
)
83 FinancementForm
= inlineformset_factory(dae
.Poste
, dae
.PosteFinancement
, extra
=2)
85 class DossierComparaisonForm(forms
.ModelForm
):
87 recherche
= AutoCompleteSelectField('dossiers', required
=False)
88 poste
= forms
.CharField(max_length
=255, widget
=forms
.TextInput(attrs
={'size':'60'}))
91 model
= dae
.DossierComparaison
92 exclude
= ('dossier',)
94 DossierComparaisonFormSet
= modelformset_factory(
95 dae
.DossierComparaison
, extra
=3, max_num
=3, form
=DossierComparaisonForm
98 class PosteComparaisonForm(forms
.ModelForm
):
100 recherche
= AutoCompleteSelectField('dae_postes', required
=False)
103 model
= dae
.PosteComparaison
106 PosteComparaisonFormSet
= modelformset_factory(
107 dae
.PosteComparaison
, extra
=3, max_num
=3, form
=PosteComparaisonForm
110 class FlexibleRemunForm(forms
.ModelForm
):
112 montant_mensuel
= forms
.DecimalField(required
=False)
113 montant
= forms
.DecimalField(required
=True, label
='Montant annuel')
116 model
= dae
.Remuneration
118 def clean_devise(self
):
119 devise
= self
.cleaned_data
['devise']
120 if devise
.code
== 'EUR':
122 implantation
= ref
.Implantation
.objects
.get(id=self
.data
['implantation'])
123 liste_taux
= devise
.tauxchange_set
.order_by('-annee')
124 if len(liste_taux
) == 0:
125 raise forms
.ValidationError(u
"La devise %s n'a pas de taux pour l'implantation %s" % (devise
, implantation
))
129 RemunForm
= inlineformset_factory(
130 dae
.Dossier
, dae
.Remuneration
, extra
=5, form
=FlexibleRemunForm
133 class PosteForm(forms
.ModelForm
):
134 """ Formulaire des postes. """
136 # On ne propose que les services actifs
137 service
= forms
.ModelChoiceField(queryset
=rh
.Service
.objects
.filter(archive
=False), required
=True)
139 responsable
=AutoCompleteSelectField('responsables', required
=True)
140 #responsable = forms.ModelChoiceField(
141 # queryset=rh.Poste.objects.select_related(depth=1))
143 # La liste des choix est laissée vide. Voir __init__ pour la raison.
144 poste
= forms
.ChoiceField(label
="Nouveau poste ou évolution du poste",
145 choices
=(), required
=False)
147 valeur_point_min
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
148 valeur_point_max
= forms
.ModelChoiceField(queryset
=rh
.ValeurPoint
.actuelles
.all(), required
=False)
153 fields
= ('type_intervention',
154 'poste', 'implantation', 'type_poste', 'service', 'nom',
155 'responsable', 'local', 'expatrie', 'mise_a_disposition',
156 'appel', 'date_debut', 'date_fin',
157 'regime_travail', 'regime_travail_nb_heure_semaine',
158 'classement_min', 'classement_max',
159 'valeur_point_min', 'valeur_point_max',
160 'devise_min', 'devise_max',
161 'salaire_min', 'salaire_max',
162 'indemn_expat_min', 'indemn_expat_max',
163 'indemn_fct_min', 'indemn_fct_max',
164 'charges_patronales_min', 'charges_patronales_max',
165 'autre_min', 'autre_max', 'devise_comparaison',
166 'comp_locale_min', 'comp_locale_max',
167 'comp_universite_min', 'comp_universite_max',
168 'comp_fonctionpub_min', 'comp_fonctionpub_max',
169 'comp_ong_min', 'comp_ong_max',
170 'comp_autre_min', 'comp_autre_max',
173 widgets
= dict(type_intervention
=forms
.RadioSelect(),
174 appel
=forms
.RadioSelect(),
175 nom
=forms
.TextInput(attrs
={'size': 60},),
176 date_debut
=admin_widgets
.AdminDateWidget(),
177 date_fin
=admin_widgets
.AdminDateWidget(),
178 justification
=forms
.Textarea(attrs
={'cols': 80},),
179 #devise_min=forms.Select(attrs={'disabled':'disabled'}),
180 #devise_max=forms.Select(attrs={'disabled':'disabled'}),
183 def __init__(self
, *args
, **kwargs
):
184 """ Mise à jour dynamique du contenu du menu des postes.
186 Si on ne met le menu à jour de cette façon, à chaque instantiation du
187 formulaire, son contenu est mis en cache par le système et il ne
188 reflète pas les changements apportés par les ajouts, modifications,
191 Aussi, dans ce cas-ci, on ne peut pas utiliser un ModelChoiceField
192 car le "id" de chaque choix est spécial (voir _poste_choices).
195 request
= kwargs
.pop('request')
196 super(PosteForm
, self
).__init__(*args
, **kwargs
)
197 self
.fields
['poste'].choices
= self
._poste_choices(request
)
198 self
.fields
['implantation'].choices
= _implantation_choices(self
, request
)
200 # Quand le dae.Poste n'existe pas, on recherche dans les dossiers rhv1
201 if self
.instance
and self
.instance
.id is None:
202 dossiers
= self
.instance
.get_dossiers()
203 if len(dossiers
) > 0:
204 self
.initial
['service'] = dossiers
[0].poste
.service
207 def _poste_choices(self
, request
):
208 """ Menu déroulant pour les postes.
210 Constitué des postes de dae et des postes de rh_v1 qui n'ont pas
211 d'équivalent dans dae.
214 copies
= dae
.Poste
.objects
.ma_region_ou_service(request
.user
).exclude(id_rh__isnull
=True).filter(etat
=POSTE_ETAT_FINALISE
)
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([('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 if cleaned_data
.get("local") is False and cleaned_data
.get("expatrie") is False:
231 msg
= "Le poste doit au moins être ouvert localement ou aux expatriés"
232 self
._errors
["local"] = self
.error_class([msg
])
233 self
._errors
["expatrie"] = ''
234 raise forms
.ValidationError(msg
)
239 def save(self
, *args
, **kwargs
):
240 kwargs2
= kwargs
.copy()
241 kwargs2
['commit'] = False
242 poste
= super(PosteForm
, self
).save(*args
, **kwargs2
)
244 if 'commit' not in kwargs
or kwargs
['commit']:
246 poste
.date_creation
= datetime
.datetime
.now()
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)
268 return [('', '----------')] + \
269 sorted([('dae-%s' % p
.id, unicode(p
)) for p
in dae_ | copies
],
273 class EmployeForm(forms
.ModelForm
):
274 """ Formulaire des employés. """
277 fields
= ('employe', 'nom', 'prenom', 'genre')
279 # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison.
280 employe
= forms
.ChoiceField(choices
=(), required
=False)
282 def __init__(self
, *args
, **kwargs
):
283 """ Mise à jour dynamique du contenu du menu des employés. """
284 request
= kwargs
.pop('request', None)
285 super(EmployeForm
, self
).__init__(*args
, **kwargs
)
286 self
.fields
['employe'].choices
= _employe_choices(self
, request
)
289 class DossierForm(forms
.ModelForm
):
290 """ Formulaire des dossiers. """
292 exclude
= ('etat', 'employe', 'poste', 'date_debut', )
294 widgets
= dict(statut_residence
=forms
.RadioSelect(),
295 contrat_date_debut
=admin_widgets
.AdminDateWidget(),
296 contrat_date_fin
=admin_widgets
.AdminDateWidget(),
299 def save(self
, *args
, **kwargs
):
300 dossier
= super(PosteForm
, self
).save(*args
, **kwargs
)
301 if dossier
.id is None:
302 dossier
.date_creation
= datetime
.datetime
.now()
308 class PosteWorkflowForm(WorkflowFormMixin
):
309 bouton_libelles
= POSTE_ETATS_BOUTONS
314 def __init__(self
, *args
, **kwargs
):
315 super(PosteWorkflowForm
, 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(DossierWorkflowForm
, self
).__init__(*args
, **kwargs
)
327 self
.fields
['etat'].help_text
= WF_HELP_TEXT
328 self
._etat_initial
= self
.instance
.etat
331 super(DossierWorkflowForm
, self
).save()
332 poste
= self
.instance
.poste
333 if poste
.etat
== self
._etat_initial
:
334 poste
.etat
= self
.instance
.etat
337 class ContratForm(forms
.ModelForm
):
340 fields
= ('type_contrat', )
343 class DAENumeriseeForm(forms
.ModelForm
):
347 fields
= ('dae_numerisee',)
349 class DAEImportableForm(forms
.Form
):
350 qs_poste
= dae
.Poste
.objects
.filter(etat
=POSTE_ETAT_FINALISE
)
351 qs_dossier
= dae
.Dossier
.objects
.filter(etat
=DOSSIER_ETAT_FINALISE
)
352 poste
= forms
.ModelChoiceField(queryset
=qs_poste
, label
="Poste finalisé", required
=False)
353 dossier
= forms
.ModelChoiceField(queryset
=qs_dossier
, label
="DAE finalisée", required
=False)
355 def clean_poste(self
):
356 poste
= self
.cleaned_data
['poste']
357 if poste
is not None and poste
.est_importe():
358 raise forms
.ValidationError("Ce poste a déjà été importé")
361 def clean_dossier(self
):
362 dossier
= self
.cleaned_data
['dossier']
363 if dossier
is not None and not dossier
.poste
.est_importe():
364 raise forms
.ValidationError("Le poste de ce dossier doit être importé avant de pouvoir importer le dossier.")
367 def importer_poste(self
):
368 poste
= self
.cleaned_data
['poste']
369 if poste
is not None and not poste
.est_importe():