Commit | Line | Data |
---|---|---|
5d680e84 | 1 | # -*- encoding: utf-8 -*- |
ce110fb9 | 2 | |
aa512122 | 3 | import datetime |
072820fc | 4 | from django.db.models import Q, Max |
5d680e84 | 5 | from django import forms |
320d7584 | 6 | from django.forms.models import inlineformset_factory, modelformset_factory |
9536ea21 | 7 | from django.contrib.admin import widgets as admin_widgets |
afc204bf | 8 | from ajax_select.fields import AutoCompleteSelectField |
8fa94e8b | 9 | from auf.django.workflow.forms import WorkflowFormMixin |
bf6f2712 | 10 | from auf.django.references import models as ref |
5d680e84 | 11 | from dae import models as dae |
d8cfc3d5 | 12 | from utils import get_employe_from_user, is_user_dans_services_centraux |
09aa8374 | 13 | from rh import models as rh |
317ce433 | 14 | from workflow import grp_drh, POSTE_ETATS_BOUTONS, DOSSIER_ETAT_FINALISE, POSTE_ETAT_FINALISE |
f258e4e7 OL |
15 | |
16 | def _implantation_choices(obj, request): | |
17 | # TRAITEMENT NORMAL | |
18 | employe = get_employe_from_user(request.user) | |
19 | # SERVICE | |
d8cfc3d5 | 20 | if is_user_dans_services_centraux(request.user): |
f258e4e7 OL |
21 | q = Q(**{ 'id' : employe.implantation_id }) |
22 | # REGION | |
23 | else: | |
24 | q = Q(**{ 'region' : employe.implantation.region }) | |
25 | ||
26 | # TRAITEMENT DRH | |
27 | if grp_drh in request.user.groups.all(): | |
28 | q = Q() | |
29 | return [('', '----------')] + [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)] | |
30 | ||
31 | def _employe_choices(obj, request): | |
f258e4e7 OL |
32 | # TRAITEMENT NORMAL |
33 | employe = get_employe_from_user(request.user) | |
34 | # SERVICE | |
d8cfc3d5 | 35 | if is_user_dans_services_centraux(request.user): |
072820fc | 36 | q_dae_region_service = Q(poste__implantation=employe.implantation) |
09aa8374 | 37 | q_rh_region_service = Q(poste__implantation=employe.implantation) |
f258e4e7 OL |
38 | # REGION |
39 | else: | |
072820fc | 40 | q_dae_region_service = Q(poste__implantation__region=employe.implantation.region) |
09aa8374 | 41 | q_rh_region_service = Q(poste__implantation__region=employe.implantation.region) |
f258e4e7 OL |
42 | # TRAITEMENT DRH |
43 | if grp_drh in request.user.groups.all(): | |
072820fc OL |
44 | q_dae_region_service = Q() |
45 | q_rh_region_service = Q() | |
f258e4e7 | 46 | |
072820fc OL |
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)) | |
f258e4e7 | 54 | id_copies = [p.id_rh_id for p in copies.all()] |
072820fc OL |
55 | |
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] | |
f258e4e7 OL |
58 | rhv1 = rh.Employe.objects.filter(id__in=employes_ids).exclude(id__in=id_copies) |
59 | ||
67c15007 OL |
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) | |
63 | ||
64 | ||
f258e4e7 OL |
65 | def option_label(employe): |
66 | return "%s %s" % (employe.nom.upper(), employe.prenom.title()) | |
67 | ||
68 | return [('', 'Nouvel employé')] + \ | |
67c15007 | 69 | sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins] + |
f258e4e7 OL |
70 | [('rh-%s' % p.id, option_label(p)) for p in rhv1], |
71 | key=lambda t: t[1]) | |
72 | ||
4bce4d24 OL |
73 | def label_poste_display(poste): |
74 | """Formate un visuel pour un poste dans une liste déroulante""" | |
23294f7d OL |
75 | annee = "" |
76 | if poste.date_debut: | |
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) | |
4bce4d24 | 79 | return label |
9cb4de55 | 80 | |
25086dcf EMS |
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) | |
03b395db OL |
84 | |
85 | class DossierComparaisonForm(forms.ModelForm): | |
11f22317 | 86 | |
03b395db OL |
87 | recherche = AutoCompleteSelectField('dossiers', required=False) |
88 | poste = forms.CharField(max_length=255, widget=forms.TextInput(attrs={'size':'60'})) | |
89 | ||
320d7584 | 90 | class Meta: |
03b395db | 91 | model = dae.DossierComparaison |
320d7584 | 92 | exclude = ('dossier',) |
03b395db | 93 | |
320d7584 EMS |
94 | DossierComparaisonFormSet = modelformset_factory( |
95 | dae.DossierComparaison, extra=3, max_num=3, form=DossierComparaisonForm | |
25086dcf | 96 | ) |
03b395db | 97 | |
068d1462 | 98 | class PosteComparaisonForm(forms.ModelForm): |
11f22317 | 99 | |
e503e64d | 100 | recherche = AutoCompleteSelectField('dae_postes', required=False) |
068d1462 | 101 | |
320d7584 | 102 | class Meta: |
068d1462 | 103 | model = dae.PosteComparaison |
320d7584 | 104 | exclude = ('poste',) |
068d1462 | 105 | |
320d7584 EMS |
106 | PosteComparaisonFormSet = modelformset_factory( |
107 | dae.PosteComparaison, extra=3, max_num=3, form=PosteComparaisonForm | |
25086dcf | 108 | ) |
068d1462 | 109 | |
0a085c42 OL |
110 | class FlexibleRemunForm(forms.ModelForm): |
111 | ||
112 | montant_mensuel = forms.DecimalField(required=False) | |
113 | montant = forms.DecimalField(required=True, label='Montant annuel') | |
114 | ||
115 | class Meta: | |
116 | model = dae.Remuneration | |
117 | ||
dc4b78a7 OL |
118 | def clean_devise(self): |
119 | devise = self.cleaned_data['devise'] | |
67173010 OL |
120 | if devise.code == 'EUR': |
121 | return devise | |
a7186cbb | 122 | implantation = ref.Implantation.objects.get(id=self.data['implantation']) |
2455f48d | 123 | liste_taux = devise.tauxchange_set.order_by('-annee') |
dc4b78a7 | 124 | if len(liste_taux) == 0: |
a7186cbb | 125 | raise forms.ValidationError(u"La devise %s n'a pas de taux pour l'implantation %s" % (devise, implantation)) |
dc4b78a7 OL |
126 | else: |
127 | return devise | |
128 | ||
25086dcf EMS |
129 | RemunForm = inlineformset_factory( |
130 | dae.Dossier, dae.Remuneration, extra=5, form=FlexibleRemunForm | |
131 | ) | |
0a085c42 | 132 | |
1b217058 | 133 | class PosteForm(forms.ModelForm): |
5d680e84 | 134 | """ Formulaire des postes. """ |
12c7f8a7 | 135 | |
ea7adc69 | 136 | # On ne propose que les services actifs |
7ba822a6 | 137 | service = forms.ModelChoiceField(queryset=rh.Service.objects.all(), required=True) |
ea7adc69 | 138 | |
12c7f8a7 OL |
139 | responsable=AutoCompleteSelectField('responsables', required=True) |
140 | #responsable = forms.ModelChoiceField( | |
141 | # queryset=rh.Poste.objects.select_related(depth=1)) | |
142 | ||
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) | |
11f22317 | 146 | |
12c7f8a7 OL |
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) | |
11f22317 EMS |
149 | |
150 | ||
5d680e84 NC |
151 | class Meta: |
152 | model = dae.Poste | |
c3be904d OL |
153 | fields = ('type_intervention', |
154 | 'poste', 'implantation', 'type_poste', 'service', 'nom', | |
154677c3 | 155 | 'responsable', 'local', 'expatrie', 'mise_a_disposition', |
b15bf543 | 156 | 'appel', 'date_debut', 'date_fin', |
5d680e84 NC |
157 | 'regime_travail', 'regime_travail_nb_heure_semaine', |
158 | 'classement_min', 'classement_max', | |
159 | 'valeur_point_min', 'valeur_point_max', | |
3d627bfd | 160 | 'devise_min', 'devise_max', |
5f61bccb OL |
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', | |
5d680e84 NC |
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', | |
8fa94e8b | 170 | 'comp_autre_min', 'comp_autre_max', |
2e092e0c | 171 | 'justification', |
8fa94e8b | 172 | ) |
c3be904d OL |
173 | widgets = dict(type_intervention=forms.RadioSelect(), |
174 | appel=forms.RadioSelect(), | |
3d627bfd | 175 | nom=forms.TextInput(attrs={'size': 60},), |
e88caaf0 OL |
176 | date_debut=admin_widgets.AdminDateWidget(), |
177 | date_fin=admin_widgets.AdminDateWidget(), | |
2e092e0c | 178 | justification=forms.Textarea(attrs={'cols': 80},), |
3d627bfd | 179 | #devise_min=forms.Select(attrs={'disabled':'disabled'}), |
180 | #devise_max=forms.Select(attrs={'disabled':'disabled'}), | |
181 | ) | |
5d680e84 | 182 | |
c2458db6 | 183 | def __init__(self, *args, **kwargs): |
5d680e84 NC |
184 | """ Mise à jour dynamique du contenu du menu des postes. |
185 | ||
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, | |
189 | etc... | |
190 | ||
139686f2 NC |
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). | |
193 | ||
5d680e84 | 194 | """ |
c2458db6 | 195 | request = kwargs.pop('request') |
5d680e84 | 196 | super(PosteForm, self).__init__(*args, **kwargs) |
f258e4e7 OL |
197 | self.fields['poste'].choices = self._poste_choices(request) |
198 | self.fields['implantation'].choices = _implantation_choices(self, request) | |
5d680e84 | 199 | |
cc3098d0 OL |
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: | |
09aa8374 | 204 | self.initial['service'] = dossiers[0].poste.service |
9508a5b8 | 205 | |
cc3098d0 | 206 | |
f258e4e7 | 207 | def _poste_choices(self, request): |
5d680e84 NC |
208 | """ Menu déroulant pour les postes. |
209 | ||
210 | Constitué des postes de dae et des postes de rh_v1 qui n'ont pas | |
211 | d'équivalent dans dae. | |
212 | ||
213 | """ | |
aa512122 | 214 | copies = dae.Poste.objects.ma_region_ou_service(request.user).exclude(id_rh__isnull=True).filter(etat=POSTE_ETAT_FINALISE) |
5d680e84 | 215 | id_copies = [p.id_rh_id for p in copies.all()] |
f614ca5c | 216 | rhv1 = rh.Poste.objects.ma_region_ou_service(request.user).exclude(id__in=id_copies) |
139686f2 NC |
217 | # Optimisation de la requête |
218 | rhv1 = rhv1.select_related(depth=1) | |
5d680e84 | 219 | |
98d51b59 | 220 | return [('', 'Nouveau poste')] + \ |
aa512122 | 221 | sorted([('rh-%s' % p.id, label_poste_display(p)) for p in rhv1], |
5d680e84 | 222 | key=lambda t: t[1]) |
3ed49093 | 223 | |
4dd75e7b OL |
224 | def clean(self): |
225 | """ | |
226 | Validation conditionnelles de certains champs. | |
227 | """ | |
228 | cleaned_data = self.cleaned_data | |
229 | ||
f42c6e20 OL |
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) | |
f42c6e20 | 235 | |
4dd75e7b OL |
236 | return cleaned_data |
237 | ||
238 | ||
494ff2be NC |
239 | def save(self, *args, **kwargs): |
240 | kwargs2 = kwargs.copy() | |
241 | kwargs2['commit'] = False | |
242 | poste = super(PosteForm, self).save(*args, **kwargs2) | |
243 | # id_rh | |
244 | if 'commit' not in kwargs or kwargs['commit']: | |
aa512122 OL |
245 | if poste.id is None: |
246 | poste.date_creation = datetime.datetime.now() | |
494ff2be NC |
247 | poste.save() |
248 | return poste | |
249 | ||
3ed49093 | 250 | |
139686f2 NC |
251 | class ChoosePosteForm(forms.ModelForm): |
252 | class Meta: | |
253 | model = dae.Poste | |
254 | fields = ('poste',) | |
255 | ||
256 | # La liste des choix est laissée vide. Voir PosteForm.__init__. | |
257 | poste = forms.ChoiceField(choices=(), required=False) | |
258 | ||
4ee6d70a | 259 | def __init__(self, request=None, *args, **kwargs): |
139686f2 | 260 | super(ChoosePosteForm, self).__init__(*args, **kwargs) |
4ee6d70a | 261 | self.fields['poste'].choices = self._poste_choices(request) |
139686f2 | 262 | |
4ee6d70a | 263 | def _poste_choices(self, request): |
139686f2 | 264 | """ Menu déroulant pour les postes. """ |
4ee6d70a OL |
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) | |
139686f2 | 267 | |
98d51b59 | 268 | return [('', '----------')] + \ |
139686f2 NC |
269 | sorted([('dae-%s' % p.id, unicode(p)) for p in dae_ | copies], |
270 | key=lambda t: t[1]) | |
271 | ||
272 | ||
139686f2 NC |
273 | class EmployeForm(forms.ModelForm): |
274 | """ Formulaire des employés. """ | |
275 | class Meta: | |
276 | model = dae.Employe | |
277 | fields = ('employe', 'nom', 'prenom', 'genre') | |
278 | ||
279 | # La liste des choix est laissée vide. Voir Poste.__init__ pour la raison. | |
280 | employe = forms.ChoiceField(choices=(), required=False) | |
281 | ||
ac6235f6 | 282 | def __init__(self, *args, **kwargs): |
139686f2 | 283 | """ Mise à jour dynamique du contenu du menu des employés. """ |
ac6235f6 | 284 | request = kwargs.pop('request', None) |
139686f2 | 285 | super(EmployeForm, self).__init__(*args, **kwargs) |
f258e4e7 | 286 | self.fields['employe'].choices = _employe_choices(self, request) |
139686f2 | 287 | |
139686f2 | 288 | |
139686f2 NC |
289 | class DossierForm(forms.ModelForm): |
290 | """ Formulaire des dossiers. """ | |
291 | class Meta: | |
16b1454e | 292 | exclude= ('etat', 'employe', 'poste', 'date_debut', ) |
139686f2 | 293 | model = dae.Dossier |
4d25e2ba | 294 | widgets = dict(statut_residence=forms.RadioSelect(), |
0e0aeb7e OL |
295 | contrat_date_debut=admin_widgets.AdminDateWidget(), |
296 | contrat_date_fin=admin_widgets.AdminDateWidget(), | |
4d25e2ba | 297 | ) |
e6f52402 | 298 | |
aa512122 | 299 | def save(self, *args, **kwargs): |
38112342 | 300 | dossier = super(DossierForm, self).save(*args, **kwargs) |
aa512122 OL |
301 | if dossier.id is None: |
302 | dossier.date_creation = datetime.datetime.now() | |
303 | dossier.save() | |
304 | return dossier | |
305 | ||
3799cafc | 306 | WF_HELP_TEXT = "" |
e0b93e3a | 307 | |
e6f52402 | 308 | class PosteWorkflowForm(WorkflowFormMixin): |
56589624 | 309 | bouton_libelles = POSTE_ETATS_BOUTONS |
e6f52402 OL |
310 | class Meta: |
311 | fields = ('etat', ) | |
312 | model = dae.Poste | |
9536ea21 | 313 | |
e0b93e3a | 314 | def __init__(self, *args, **kwargs): |
e54b7d5d | 315 | super(PosteWorkflowForm, self).__init__(*args, **kwargs) |
e0b93e3a OL |
316 | self.fields['etat'].help_text = WF_HELP_TEXT |
317 | ||
318 | ||
e6f52402 | 319 | class DossierWorkflowForm(WorkflowFormMixin): |
56589624 | 320 | bouton_libelles = POSTE_ETATS_BOUTONS # meme workflow que poste... |
e6f52402 | 321 | class Meta: |
9e40cfbe | 322 | fields = ('etat', ) |
e6f52402 | 323 | model = dae.Dossier |
e0b93e3a OL |
324 | |
325 | def __init__(self, *args, **kwargs): | |
e54b7d5d | 326 | super(DossierWorkflowForm, self).__init__(*args, **kwargs) |
e0b93e3a | 327 | self.fields['etat'].help_text = WF_HELP_TEXT |
e54b7d5d | 328 | self._etat_initial = self.instance.etat |
e0b93e3a | 329 | |
e54b7d5d EMS |
330 | def save(self): |
331 | super(DossierWorkflowForm, self).save() | |
332 | poste = self.instance.poste | |
333 | if poste.etat == self._etat_initial: | |
334 | poste.etat = self.instance.etat | |
335 | poste.save() | |
9536ea21 EMS |
336 | |
337 | class ContratForm(forms.ModelForm): | |
338 | ||
339 | class Meta: | |
9dfa4296 | 340 | fields = ('type_contrat', 'fichier', ) |
9536ea21 EMS |
341 | model = dae.Contrat |
342 | ||
c3f0b49f EMS |
343 | class DAENumeriseeForm(forms.ModelForm): |
344 | ||
345 | class Meta: | |
346 | model = dae.Dossier | |
347 | fields = ('dae_numerisee',) | |
317ce433 OL |
348 | |
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) | |
354 | ||
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é") | |
359 | return poste | |
360 | ||
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.") | |
365 | return dossier | |
366 | ||
367 | def importer_poste(self): | |
368 | poste = self.cleaned_data['poste'] | |
369 | if poste is not None and not poste.est_importe(): | |
370 | poste.importer() | |
371 |