recrutement merge
[auf_rh_dae.git] / project / rh / admin.py
1 # -*- encoding: utf-8 -*-
2
3 import datetime
4
5 import reversion
6 from ajax_select import make_ajax_form
7 from auf.django.references import models as ref
8 from django import forms
9 from django.core.urlresolvers import reverse
10 from django.contrib import admin
11 from django.contrib.contenttypes.models import ContentType
12 from django.conf import settings
13 from django.db.models import Q, Count
14 from django.template.defaultfilters import date
15 from django.utils.formats import date_format
16
17 from project import groups
18 from project.decorators import in_drh_or_admin
19 from project.rh import models as rh
20 from project.permissions import user_gere_obj_de_sa_region, \
21 user_can_add_obj, \
22 user_can_change_obj, \
23 user_can_delete_obj, \
24 get_region_user
25
26 from project.rh.forms import ContratForm, AyantDroitForm, EmployeAdminForm, \
27 AjaxSelect, DossierForm, ResponsableInlineForm
28 from project.rh.change_list import ChangeList
29
30
31 class BaseAdmin(admin.ModelAdmin):
32
33 class Media:
34 css = {'screen': (
35 'css/admin_custom.css',
36 'jquery-autocomplete/jquery.autocomplete.css',
37 )}
38 js = (
39 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
40 'jquery-autocomplete/jquery.autocomplete.min.js',
41 )
42
43 # Admin pour reversion
44
45 class ArchivableAdmin(admin.ModelAdmin):
46 """
47 Admin pour les modèles archivables
48 """
49 list_filter = ('archive', )
50
51 def queryset(self, request):
52 return self.model.avec_archives.all()
53
54 def _archive(self, obj):
55 if obj.archive:
56 return "oui"
57 else:
58 return "non"
59 _archive.short_description = u'Archivé'
60 _archive.admin_order_field = 'archive'
61
62
63 class RegionProxy(ref.Region):
64 """ Proxy utilisé pour les organigrammes par région """
65 class Meta:
66 managed = False
67 proxy = True
68 verbose_name = u"Organigramme par région"
69 verbose_name_plural = u"Organigramme par région"
70
71
72 class ImplantationProxy(ref.Implantation):
73 """ Proxy utilisé pour les organigrammes par implantation """
74 class Meta:
75 managed = False
76 proxy = True
77 verbose_name = u"Organigramme par implantations"
78 verbose_name_plural = u"Organigramme par implantations"
79
80
81 class ServiceProxy(rh.Service):
82 """ Proxy utilisé pour les organigrammes par service """
83
84 class Meta:
85 managed = False
86 proxy = True
87 verbose_name = u"Organigramme par services"
88 verbose_name_plural = u"Organigramme par services"
89
90
91 class EmployeProxy(rh.Employe):
92 """ Proxy utilisé pour les organigrammes des employés """
93 class Meta:
94 managed = False
95 proxy = True
96 verbose_name = u"Organigramme des employés"
97 verbose_name_plural = u"Organigramme des employés"
98
99
100 class DateRangeMixin(object):
101 prefixe_recherche_temporelle = ""
102
103 def get_changelist(self, request, **kwargs):
104 if 'HTTP_REFERER' in request.META.keys():
105 referer = request.META['HTTP_REFERER']
106 referer = "/".join(referer.split('/')[3:])
107 referer = "/%s" % referer.split('?')[0]
108 change_list_view = 'admin:%s_%s_changelist' % (
109 self.model._meta.app_label,
110 self.model.__name__.lower(),)
111 if referer != reverse(change_list_view):
112 params = request.GET.copy()
113 params.update({'statut': 'Actif'})
114 request.GET = params
115 return ChangeList
116
117
118 # Override of the InlineModelAdmin to support the link in the tabular inline
119 class LinkedInline(admin.options.InlineModelAdmin):
120 template = "admin/linked.html"
121 admin_model_path = None
122
123 def __init__(self, *args):
124 super(LinkedInline, self).__init__(*args)
125 if self.admin_model_path is None:
126 self.admin_model_path = self.model.__name__.lower()
127
128
129 class ProtectRegionMixin(object):
130
131 def queryset(self, request):
132 qs = super(ProtectRegionMixin, self).queryset(request)
133
134 if in_drh_or_admin(request.user):
135 return qs
136
137 if user_gere_obj_de_sa_region(request.user):
138 region_user = get_region_user(request.user)
139 q = Q(**{self.model.prefix_implantation: \
140 region_user})
141 qs = qs.filter(q).distinct()
142 return qs
143 return qs.none()
144
145 def has_add_permission(self, request):
146 return user_can_add_obj(request.user)
147
148 def has_change_permission(self, request, obj=None):
149 return user_can_change_obj(request.user, obj) if obj else True
150
151 def has_delete_permission(self, request, obj=None):
152 return user_can_delete_obj(request.user, obj) if obj else True
153
154
155 class DerniereModificationAdmin(admin.ModelAdmin):
156
157 def queryset(self, request):
158 qs = super(DerniereModificationAdmin, self).queryset(request)
159 ct = ContentType.objects.get_for_model(self.model)
160 db_table = self.model._meta.db_table
161 pk = self.model._meta.pk.column
162 return qs.extra(select={
163 'date_modification':
164 "SELECT action_time FROM django_admin_log "
165 "WHERE content_type_id = %d AND object_id = %s.%s "
166 "ORDER BY action_time DESC "
167 "LIMIT 1" % (ct.id, db_table, pk),
168 'user_modification':
169 "SELECT u.username "
170 "FROM auth_user u "
171 "INNER JOIN django_admin_log l ON l.user_id = u.id "
172 "WHERE l.content_type_id = %d AND object_id = %s.%s "
173 "ORDER BY action_time DESC "
174 "LIMIT 1" % (ct.id, db_table, pk),
175 })
176
177 def derniere_modification(self, obj):
178 text = ''
179 if obj.date_modification:
180 text += obj.date_modification.strftime('%d-%m-%Y %H:%M')
181 if obj.user_modification:
182 text += ' par ' + obj.user_modification
183 return text
184 derniere_modification.short_description = u'dernière modification'
185 derniere_modification.admin_order_field = 'date_modification'
186
187
188 # Inlines
189
190 class CommentaireInlineForm(forms.ModelForm):
191
192 def save(self, commit=True):
193
194 # Hack: reversion.VersionAdmin ne sauvegarde pas les champs qui ne
195 # sont pas explicitement dans le formulaire. Il plante cependant
196 # leur valeur dans `self.initial`. Ceci est un peu fragile. Si
197 # c'est possible, il serait plus approprié que Reversion se rende
198 # compte qu'il manque des champs.
199 instance = super(CommentaireInlineForm, self).save(commit=False)
200 if instance.owner_id is None and 'owner' in self.initial:
201 instance.owner_id = self.initial['owner']
202 if instance.date_creation is None and 'date_creation' in self.initial:
203 instance.date_creation = self.initial['date_creation']
204 if commit:
205 instance.save()
206 self.save_m2m()
207 return instance
208
209
210 class ReadOnlyInlineMixin(object):
211
212 def get_readonly_fields(self, request, obj=None):
213 return [f.name for f in self.model._meta.fields]
214
215
216 class AyantDroitInline(admin.StackedInline):
217 model = rh.AyantDroit
218 form = AyantDroitForm
219 extra = 0
220
221 fieldsets = (
222 (None, {
223 'fields': (
224 ('nom', 'prenom'),
225 ('nom_affichage', 'genre'),
226 'nationalite',
227 'date_naissance',
228 'lien_parente',
229 )}),
230 )
231
232
233 class AyantDroitCommentaireInline(admin.TabularInline):
234 readonly_fields = ('owner',)
235 model = rh.AyantDroitCommentaire
236 extra = 1
237 form = CommentaireInlineForm
238
239
240 class ContratInline(admin.TabularInline):
241 form = ContratForm
242 model = rh.Contrat
243 extra = 1
244
245
246 class DossierROInline(ReadOnlyInlineMixin, LinkedInline):
247 template = "admin/rh/dossier/linked.html"
248 model = rh.Dossier
249 extra = 0
250 can_delete = False
251
252 def has_add_permission(self, request=None):
253 return False
254
255 def has_change_permission(self, request, obj=None):
256 return False
257
258 def has_delete_permission(self, request, obj=None):
259 return False
260
261
262 class DossierCommentaireInline(admin.TabularInline):
263 readonly_fields = ('owner',)
264 model = rh.DossierCommentaire
265 extra = 1
266 form = CommentaireInlineForm
267
268
269 class DossierPieceInline(admin.TabularInline):
270 model = rh.DossierPiece
271 extra = 4
272
273
274 class EmployeInline(admin.TabularInline):
275 model = rh.Employe
276
277
278 class EmployeCommentaireInline(admin.TabularInline):
279 readonly_fields = ('owner',)
280 model = rh.EmployeCommentaire
281 extra = 1
282 form = CommentaireInlineForm
283
284
285 class EmployePieceInline(admin.TabularInline):
286 model = rh.EmployePiece
287 extra = 4
288
289
290 class PosteCommentaireInline(admin.TabularInline):
291 readonly_fields = ('owner',)
292 model = rh.PosteCommentaire
293 extra = 1
294 form = CommentaireInlineForm
295
296
297 class PosteFinancementInline(admin.TabularInline):
298 model = rh.PosteFinancement
299
300
301 class PostePieceInline(admin.TabularInline):
302 model = rh.PostePiece
303
304
305 class RemunerationInline(admin.TabularInline):
306 model = rh.Remuneration
307 extra = 1
308
309
310 class RemunerationROInline(ReadOnlyInlineMixin, RemunerationInline):
311 pass
312
313
314 class TypePosteInline(admin.TabularInline):
315 model = rh.TypePoste
316
317
318 class PosteComparaisonInline(admin.TabularInline):
319 model = rh.PosteComparaison
320
321
322 class ClassementAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
323 BaseAdmin):
324 ignore_duplicate_revisions = True
325 list_display = ('_classement', 'derniere_modification')
326 fieldsets = (
327 (None, {'fields': ('type', 'echelon', 'degre', 'coefficient',)}),
328 )
329
330 def _classement(self, obj):
331 return unicode(obj)
332 _classement.short_description = u"Classement"
333
334
335 class DeviseAdmin(reversion.VersionAdmin, ArchivableAdmin,
336 DerniereModificationAdmin, BaseAdmin):
337 ignore_duplicate_revisions = True
338 list_display = (
339 'code', 'nom', '_archive', 'derniere_modification',
340 )
341 list_filter = ('archive', )
342 fieldsets = (
343 (None, {'fields': ('code', 'nom', 'archive', )}),
344 )
345
346
347 class DossierAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
348 AjaxSelect, DerniereModificationAdmin, BaseAdmin):
349 change_list_template = "admin/rh/dossier/change_list.html"
350 ignore_duplicate_revisions = True
351 alphabet_filter = 'employe__nom'
352 search_fields = (
353 'id',
354 'employe__id',
355 'poste__id',
356 'employe__nom',
357 'employe__prenom',
358 'poste__nom',
359 'poste__nom_feminin',
360 'poste__implantation__nom',
361 )
362 list_display = (
363 '_id',
364 '_apercu',
365 '_nom',
366 '_employe',
367 '_poste',
368 '_region',
369 '_implantation',
370 '_date_debut',
371 '_date_fin',
372 'derniere_modification',
373 '_dae',
374 )
375 list_display_links = ('_nom',)
376 list_filter = (
377 'poste__implantation__region',
378 'poste__implantation',
379 'poste__type_poste__categorie_emploi',
380 'poste__type_poste',
381 'rh_contrats__type_contrat',
382 'principal',
383 )
384 inlines = (DossierPieceInline, ContratInline,
385 RemunerationInline,
386 DossierCommentaireInline,
387 )
388 fieldsets = (
389 (None, {
390 'fields': (
391 'employe',
392 'poste',
393 'principal',
394 'statut',
395 'organisme_bstg',)}),
396 ('Recrutement', {
397 'fields': (
398 'statut_residence',
399 'remplacement',
400 'remplacement_de', )}),
401 ('Rémunération', {
402 'fields': (
403 'classement',
404 ('regime_travail', 'regime_travail_nb_heure_semaine'),)}),
405 ('Occupation du Poste par cet Employe', {
406 'fields': (('date_debut', 'date_fin'), )}
407 ),
408 )
409 form = make_ajax_form(rh.Dossier, {
410 'employe': 'employes',
411 'poste': 'postes',
412 'remplacement_de': 'dossiers',
413 }, superclass=DossierForm)
414
415 def lookup_allowed(self, key, value):
416 if key in (
417 'employe__nom__istartswith',
418 'poste__implantation__region__id__exact',
419 'poste__implantation__id__exact',
420 'poste__type_poste__id__exact',
421 'poste__type_poste__categorie_emploi__id__exact',
422 'rh_contrats__type_contrat__id__exact',
423 'principal__exact',
424 'principal__isnull',
425 ):
426 return True
427
428 def _id(self, obj):
429 return obj.id
430 _id.short_description = u"#"
431 _id.admin_order_field = "id"
432
433 def _apercu(self, d):
434 apercu_link = u"""<a title="Aperçu du dossier"
435 onclick="return showAddAnotherPopup(this);"
436 href='%s'>
437 <img src="%simg/dossier-apercu.png" />
438 </a>""" % \
439 (reverse('dossier_apercu', args=(d.id,)),
440 settings.STATIC_URL,
441 )
442 return apercu_link
443 _apercu.allow_tags = True
444 _apercu.short_description = u""
445
446 def _nom(self, obj):
447 return "Dossier"
448 _nom.allow_tags = True
449 _nom.short_description = u"Dossier"
450
451 def _employe(self, obj):
452 employe = obj.employe
453 view_link = reverse('employe_apercu', args=(employe.id,))
454 edit_link = reverse('admin:rh_employe_change', args=(employe.id,))
455 style = ""
456 view = u"""<a href="%s"
457 title="Aperçu l'employé"
458 onclick="return showAddAnotherPopup(this);">
459 <img src="%simg/employe-apercu.png" />
460 </a>""" % (view_link, settings.STATIC_URL,)
461 return u"""%s<a href='%s' style="%s;">%s</a>""" % \
462 (view, edit_link, style, employe)
463 _employe.allow_tags = True
464 _employe.short_description = u"Employé"
465 _employe.admin_order_field = "employe__nom"
466
467 def _poste(self, dossier):
468 link = u"""<a title="Aperçu du poste"
469 onclick="return showAddAnotherPopup(this);"
470 href='%s'><img src="%simg/poste-apercu.png" />
471 </a>
472 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
473 (reverse('poste_apercu', args=(dossier.poste.id,)),
474 settings.STATIC_URL,
475 reverse('admin:rh_poste_change', args=(dossier.poste.id,)),
476 dossier.poste.nom,
477 dossier.poste.id,
478 )
479 return link
480 _poste.allow_tags = True
481 _poste.short_description = u'Poste'
482 _poste.admin_order_field = 'poste__nom'
483
484 def _region(self, obj):
485 return obj.poste.implantation.region.code
486 _region.short_description = u"Région"
487 _region.admin_order_field = 'poste__implantation__region__code'
488
489 def _implantation(self, obj):
490 return obj.poste.implantation.nom
491 _implantation.short_description = u"Implantation"
492 _implantation.admin_order_field = 'poste__implantation__nom'
493
494 def _date_debut(self, obj):
495 return date(obj.date_debut)
496
497 _date_debut.short_description = u'Début'
498 _date_debut.admin_order_field = 'date_debut'
499
500 def _date_fin(self, obj):
501 return date(obj.date_fin)
502 _date_fin.short_description = u'Fin'
503 _date_fin.admin_order_field = 'date_fin'
504
505 def _date_modification(self, obj):
506 return date(obj.date_modification) \
507 if obj.date_modification is not None else "(aucune)"
508 _date_modification.short_description = u'date modification'
509 _date_modification.admin_order_field = 'date_modification'
510
511 def _dae(self, d):
512 apercu_link = ""
513 dossiers_dae = d.dossiers_dae.all()
514 if len(dossiers_dae) > 0:
515 dossier_dae = dossiers_dae[0]
516 apercu_link = u"""<a title="Aperçu du dossier"
517 onclick="return showAddAnotherPopup(this);"
518 href='%s'>
519 <img src="%simg/loupe.png" />
520 </a>""" % \
521 (reverse('embauche_consulter', args=(dossier_dae.id,)),
522 settings.STATIC_URL,
523 )
524 return apercu_link
525 _dae.allow_tags = True
526 _dae.short_description = u"DAE"
527
528 def save_formset(self, request, form, formset, change):
529 instances = formset.save(commit=False)
530 for instance in instances:
531 if instance.__class__ == rh.DossierCommentaire:
532 instance.owner = request.user
533 instance.date_creation = datetime.datetime.now()
534 instance.save()
535
536
537 class EmployeAdminBase(DateRangeMixin, ProtectRegionMixin,
538 DerniereModificationAdmin, BaseAdmin):
539 prefixe_recherche_temporelle = "rh_dossiers__"
540 alphabet_filter = 'nom'
541 DEFAULT_ALPHABET = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
542 search_fields = (
543 'id', 'nom', 'prenom', 'nom_affichage',
544 'rh_dossiers__poste__nom',
545 'rh_dossiers__poste__nom_feminin'
546 )
547 ordering = ('nom', )
548 form = EmployeAdminForm
549 list_display = (
550 '_id', '_apercu', '_nom', '_dossiers_postes',
551 #'_region',
552 #'_implantation',
553 'date_entree',
554 'derniere_modification'
555 )
556 list_display_links = ('_nom',)
557 list_filter = (
558 'rh_dossiers__poste__implantation__region',
559 'rh_dossiers__poste__implantation', 'nb_postes'
560 )
561 inlines = (
562 AyantDroitInline, DossierROInline, EmployePieceInline,
563 EmployeCommentaireInline
564 )
565 fieldsets = (
566 ('Identification', {
567 'fields': (
568 ('nom', 'prenom'),
569 ('nom_affichage', 'genre'),
570 'nationalite',
571 'date_naissance',
572 )
573 }),
574 ('Informations personnelles', {
575 'fields': ('situation_famille', 'date_entree', )
576 }),
577 ('Coordonnées personnelles', {
578 'fields': (
579 ('tel_domicile', 'tel_cellulaire'),
580 ('adresse', 'ville'),
581 ('code_postal', 'province'),
582 'pays',
583 'courriel_perso'
584 )
585 }),
586 )
587
588 def _id(self, obj):
589 return obj.id
590 _id.short_description = u"#"
591 _id.admin_order_field = "id"
592
593 def _apercu(self, obj):
594 return u"""<a title="Aperçu de l'employé"
595 onclick="return showAddAnotherPopup(this);"
596 href='%s'>
597 <img src="%simg/employe-apercu.png" />
598 </a>""" % \
599 (reverse('employe_apercu', args=(obj.id,)), settings.STATIC_URL)
600 _apercu.allow_tags = True
601 _apercu.short_description = u""
602
603 def _nom(self, obj):
604 edit_link = reverse('admin:rh_employe_change', args=(obj.id,))
605 return u"""<a href='%s'><strong>%s</strong></a>""" % \
606 (edit_link, "%s %s" % (obj.nom.upper(), obj.prenom))
607 _nom.allow_tags = True
608 _nom.short_description = u"Employé"
609 _nom.admin_order_field = "nom"
610
611 def _region(self, obj):
612 try:
613 d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
614 region = d.poste.implantation.region.code
615 except:
616 region = None
617 return region
618 _region.short_description = u"Région"
619
620 def _implantation(self, obj):
621 try:
622 d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
623 implantation = d.poste.implantation.nom
624 except:
625 implantation = None
626 return implantation
627 _implantation.short_description = u"Implantation"
628
629 def _dossiers_postes(self, obj):
630 l = []
631 for d in obj.rh_dossiers.all().order_by('-date_debut'):
632 link_style = u''
633 list_style = u''
634 if d.date_fin is not None and d.date_fin < datetime.date.today():
635 link_style = u' style="color:#666;"'
636 list_style = u' style="color:grey;"'
637
638 dossier = u"""<a title="Aperçu du dossier"
639 href="%s"
640 onclick="return showAddAnotherPopup(this);"
641 title="Aperçu du dossier">
642 <img src="%simg/dossier-apercu.png" />
643 </a>
644 <a href="%s"%s>Dossier</a>
645 &nbsp;""" % \
646 (reverse('dossier_apercu', args=(d.id,)),
647 settings.STATIC_URL,
648 reverse('admin:rh_dossier_change', args=(d.id,)),
649 link_style,)
650
651 poste = u"""<a title="Aperçu du poste"
652 href="%s"
653 onclick="return showAddAnotherPopup(this);"
654 title="Aperçu du poste">
655 <img src="%simg/poste-apercu.png" />
656 </a>
657 <a href="%s"%s>%s [%d]</a>
658 &nbsp;""" % \
659 (reverse('poste_apercu', args=(d.poste.id,)),
660 settings.STATIC_URL,
661 reverse('admin:rh_poste_change', args=(d.poste.id,)),
662 link_style,
663 d.poste.nom,
664 d.poste.id)
665
666 link = u"""<li%s>%s %s</li>""" % \
667 (list_style, dossier, poste)
668
669 l.append(link)
670 return "<ul>%s</ul>" % "\n".join(l)
671 _dossiers_postes.allow_tags = True
672 _dossiers_postes.short_description = u"Dossiers et postes"
673
674 def _date_modification(self, obj):
675 return date(obj.date_modification) \
676 if obj.date_modification is not None else "(aucune)"
677 _date_modification.short_description = u'date modification'
678 _date_modification.admin_order_field = 'date_modification'
679
680 def queryset(self, request):
681 qs = super(EmployeAdminBase, self).queryset(request)
682 return qs.select_related(depth=1).order_by('nom')
683
684 def save_formset(self, request, form, formset, change):
685 instances = formset.save(commit=False)
686 for instance in instances:
687 if instance.__class__ == rh.EmployeCommentaire:
688 instance.owner = request.user
689 instance.date_creation = datetime.datetime.now()
690 instance.save()
691
692
693 class EmployeAdmin(reversion.VersionAdmin, EmployeAdminBase):
694 change_list_template = "admin/rh/employe/change_list.html"
695 ignore_duplicate_revisions = True
696
697
698 class EmployeProxyAdmin(EmployeAdminBase):
699 list_display = ('_id', '_apercu', '_nom', '_organigramme')
700 list_per_page = 500
701 actions = None
702
703 def __init__(self, *args, **kwargs):
704 super(EmployeProxyAdmin, self).__init__(*args, **kwargs)
705 self.list_display_links = (None, )
706
707 def queryset(self, request):
708 qs = super(ProtectRegionMixin, self).queryset(request)
709
710 if in_drh_or_admin(request.user) or \
711 user_gere_obj_de_sa_region(request.user):
712 return qs
713
714 return qs.none()
715
716 def has_add_permission(self, obj):
717 return False
718
719 def has_change_permission(self, request, obj=None):
720 user_groups = [g.name for g in request.user.groups.all()]
721 if groups.CORRESPONDANT_RH in user_groups or \
722 groups.ADMINISTRATEURS in user_groups or \
723 groups.DIRECTEUR_DE_BUREAU in user_groups or \
724 in_drh_or_admin(request.user):
725 return True
726 return False
727
728 def _organigramme(self, obj):
729 l = []
730 for d in rh.Dossier.objects.filter(
731 Q(date_fin__gt=datetime.date.today()) | Q(date_fin=None),
732 Q(date_debut__lt=datetime.date.today()) | Q(date_debut=None),
733 employe=obj.id
734 ):
735 organigramme = \
736 u'Organigramme, niveau: ' \
737 u'<input type="text" id="level_%s" ' \
738 u'style="width:30px;height:15px;" /> ' \
739 u'<input type="button" value="Générer" ' \
740 u"""onclick="window.location='%s' + """ \
741 u"""document.getElementById('level_%s').value" />""" % (
742 d.poste.id,
743 reverse('rho_employe_sans_niveau', args=(d.poste.id,)),
744 d.poste.id
745 )
746 link = u"""<li>%s [%s]:<br />%s</li>""" % (
747 d.poste.nom,
748 d.poste.id,
749 organigramme
750 )
751 l.append(link)
752 return "<ul>%s</ul>" % "\n".join(l)
753
754 _organigramme.allow_tags = True
755 _organigramme.short_description = "Organigramme"
756
757
758 class CategorieEmploiAdmin(reversion.VersionAdmin,
759 DerniereModificationAdmin, BaseAdmin):
760 ignore_duplicate_revisions = True
761 list_display = ('nom', 'derniere_modification')
762 inlines = (TypePosteInline,)
763 fieldsets = (
764 (None, {'fields': ('nom', )}),
765 )
766
767
768 class OrganismeBstgAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
769 BaseAdmin):
770 ignore_duplicate_revisions = True
771 search_fields = ('nom',)
772 list_display = ('nom', 'type', 'pays', 'derniere_modification')
773 list_filter = ('type', )
774 inlines = (DossierROInline,)
775 fieldsets = (
776 (None, {'fields': ('nom', 'type', 'pays',)}),
777 )
778
779
780 class PosteAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
781 AjaxSelect, DerniereModificationAdmin, BaseAdmin):
782 change_list_template = "admin/rh/poste/change_list.html"
783 ignore_duplicate_revisions = True
784 form = make_ajax_form(rh.Poste, {
785 'implantation': 'implantations',
786 'type_poste': 'typepostes',
787 'responsable': 'postes',
788 'valeur_point_min': 'valeurpoints',
789 'valeur_point_max': 'valeurpoints',
790 })
791 alphabet_filter = 'nom'
792 search_fields = (
793 'id',
794 'nom',
795 'implantation__nom',
796 'implantation__region__code',
797 'implantation__region__nom',
798 'rh_dossiers__employe__id',
799 'rh_dossiers__employe__nom',
800 'rh_dossiers__employe__prenom',
801 )
802 list_display = (
803 '_id', '_apercu', '_nom', '_occupe_par', 'implantation', '_service',
804 '_responsable', 'date_debut', 'date_fin', 'derniere_modification',
805 '_dae'
806 )
807 list_filter = (
808 'implantation__region',
809 'implantation',
810 'service',
811 'type_poste',
812 'type_poste__categorie_emploi',
813 'type_poste__famille_professionnelle',
814 'vacant',
815 )
816 list_display_links = ('_nom',)
817 fieldsets = (
818 (None, {'fields': (
819 ('nom', 'nom_feminin'),
820 'implantation',
821 'type_poste',
822 'service',
823 'responsable',
824 )}
825 ),
826 ('Contrat', {
827 'fields': ((
828 'regime_travail',
829 'regime_travail_nb_heure_semaine'),
830 )}
831 ),
832 ('Recrutement', {
833 'fields': (('local', 'expatrie', 'mise_a_disposition', 'appel'),)}
834 ),
835 ('Rémunération', {
836 'fields': (('classement_min',
837 'valeur_point_min',
838 'devise_min',
839 'salaire_min',
840 'indemn_min',
841 'autre_min',),
842 ('classement_max',
843 'valeur_point_max',
844 'devise_max',
845 'salaire_max',
846 'indemn_max',
847 'autre_max',),
848 )}),
849 ('Comparatifs de rémunération', {
850 'fields': ('devise_comparaison',
851 ('comp_locale_min', 'comp_locale_max'),
852 ('comp_universite_min', 'comp_universite_max'),
853 ('comp_fonctionpub_min', 'comp_fonctionpub_max'),
854 ('comp_ong_min', 'comp_ong_max'),
855 ('comp_autre_min', 'comp_autre_max'))}
856 ),
857 ('Justification', {
858 'fields': ('justification',)}
859 ),
860 ('Autres Méta-données', {
861 'fields': ('date_debut', 'date_fin')}
862 ),
863 )
864
865 inlines = (PosteFinancementInline,
866 PostePieceInline,
867 DossierROInline,
868 PosteComparaisonInline,
869 PosteCommentaireInline, )
870
871 def lookup_allowed(self, key, value):
872 return key in (
873 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
874 'date_fin__isnull', 'implantation__region__id__exact',
875 'implantation__id__exact', 'type_poste__id__exact',
876 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
877 'service__isnull', 'vacant__exact', 'vacant__isnull',
878 ) or super(PosteAdmin, self).lookup_allowed(key, value)
879
880 def _id(self, obj):
881 return "%s" % obj.id
882 _id.short_description = '#'
883 _id.admin_order_field = 'id'
884
885 def _apercu(self, poste):
886 view_link = u"""<a onclick="return showAddAnotherPopup(this);"
887 title="Aperçu du poste"
888 href='%s'>
889 <img src="%simg/poste-apercu.png" />
890 </a>""" % \
891 (reverse('poste_apercu', args=(poste.id,)),
892 settings.STATIC_URL,)
893 return view_link
894 _apercu.allow_tags = True
895 _apercu.short_description = ''
896
897 def _nom(self, poste):
898 return """<a href="%s">%s</a>""" % \
899 (reverse('admin:rh_poste_change', args=(poste.id,)),
900 poste.nom)
901 _nom.allow_tags = True
902 _nom.short_description = u'Poste'
903 _nom.admin_order_field = 'nom'
904
905 def _occupe_par(self, obj):
906 """Formatte la méthode Poste.occupe_par() pour l'admin"""
907 output = u"Vacant"
908 if obj.date_fin is not None and obj.date_fin < datetime.date.today():
909 return u"s/o"
910 employes = obj.occupe_par()
911 if employes:
912 l = []
913 for e in employes:
914 link = u"""<a href='%s'
915 title='Aperçu de l\'employé'
916 onclick='return showAddAnotherPopup(this)'>
917 <img src='%simg/employe-apercu.png' />
918 </a>
919 <a href='%s'>%s</a>""" % \
920 (reverse('employe_apercu', args=(e.id,)),
921 settings.STATIC_URL,
922 reverse('admin:rh_employe_change', args=(e.id,)),
923 e)
924 l.append(link)
925 output = "\n<br />".join(l)
926 return output
927 _occupe_par.allow_tags = True
928 _occupe_par.short_description = "Occupé par"
929
930 def _region(self, poste):
931 return poste.implantation.region.code
932 _region.short_description = 'Région'
933 _region.admin_order_field = 'implantation__region__code'
934
935 def _implantation(self, poste):
936 return poste.implantation.nom
937 _implantation.short_description = 'Implantation'
938 _implantation.admin_order_field = 'implantation'
939
940 def _service(self, obj):
941 return obj.service
942 _service.short_description = 'Service'
943 _service.allow_tags = True
944
945 def _responsable(self, obj):
946 try:
947 responsable = u"""<a href="%s"
948 onclick="return showAddAnotherPopup(this)">
949 <img src="%simg/poste-apercu.png"
950 title="Aperçu du poste" />
951 </a>
952 <a href="%s">%s [%d]</a>
953 <br />""" % \
954 (reverse('poste_apercu', args=(obj.responsable.id,)),
955 settings.STATIC_URL,
956 reverse('admin:rh_poste_change', args=(obj.responsable.id,)),
957 obj.responsable.nom,
958 obj.responsable.id)
959 except:
960 responsable = ''
961
962 try:
963 dossier = obj.responsable.rh_dossiers.all() \
964 .order_by('-date_debut')[0]
965 employe_id = dossier.employe.id
966 employe_html = u"""<br />
967 <a href="%s"
968 onclick="return showAddAnotherPopup(this)">
969 <img src="%simg/employe-apercu.png"
970 title="Aperçu de l'employé">
971 </a>
972 <a href="%s">%s</a>""" % \
973 (reverse('employe_apercu', args=(employe_id,)),
974 settings.STATIC_URL,
975 reverse('admin:rh_employe_change', args=(employe_id,)),
976 dossier.employe)
977 except:
978 employe_html = ""
979
980 return "%s %s" % (responsable, employe_html)
981 _responsable.short_description = 'Responsable'
982 _responsable.allow_tags = True
983
984 def _date_debut(self, obj):
985 return date_format(obj.date_debut)
986 _date_debut.short_description = u'Début'
987 _date_debut.admin_order_field = 'date_debut'
988
989 def _date_fin(self, obj):
990 return date_format(obj.date_fin)
991 _date_fin.short_description = u'Fin'
992 _date_fin.admin_order_field = 'date_fin'
993
994 def _dae(self, poste):
995 apercu_link = ""
996 postes_dae = poste.postes_dae.all()
997 if len(postes_dae) > 0:
998 poste_dae = postes_dae[0]
999 apercu_link = \
1000 u'<a title="Aperçu du dossier" href="%s" ' \
1001 u'onclick="return showAddAnotherPopup(this);">' \
1002 u'<img src="%simg/loupe.png" /></a>' % (reverse(
1003 'poste_consulter', args=("dae-%s" % poste_dae.id,)
1004 ), settings.STATIC_URL)
1005 return apercu_link
1006 _dae.allow_tags = True
1007 _dae.short_description = u"DAE"
1008
1009 def save_formset(self, request, form, formset, change):
1010 instances = formset.save(commit=False)
1011 for instance in instances:
1012 if instance.__class__ == rh.PosteCommentaire:
1013 instance.owner = request.user
1014 instance.date_creation = datetime.datetime.now()
1015 instance.save()
1016 formset.save_m2m()
1017
1018
1019 class ResponsableInline(admin.TabularInline):
1020 model = rh.ResponsableImplantation
1021 extra = 0
1022 fk_name = "implantation"
1023 form = ResponsableInlineForm
1024
1025
1026 class ResponsableImplantationAdmin(BaseAdmin):
1027 actions = None
1028 fields = ('nom', )
1029 inlines = (ResponsableInline, )
1030 list_filter = ('region', 'statut', )
1031 list_display = ('_region', '_nom', 'statut', '_responsable', )
1032 list_display_links = ('_nom',)
1033 list_per_page = 500
1034 readonly_fields = ('nom', )
1035 search_fields = (
1036 'nom',
1037 'responsable__employe__id',
1038 'responsable__employe__nom',
1039 'responsable__employe__prenom',
1040 )
1041 ordering = ('nom',)
1042 inlines = (ResponsableInline, )
1043
1044 def _region(self, obj):
1045 return obj.region.code
1046 _region.short_description = u"Région"
1047 _region.admin_order_field = 'region__code'
1048
1049 def _nom(self, obj):
1050 return obj.nom
1051 _nom.short_description = u"Implantation"
1052 _nom.admin_order_field = 'nom'
1053
1054 def _responsable(self, obj):
1055 try:
1056 employe = obj.responsable.employe
1057 dossiers = employe.dossiers_encours()
1058 if len(dossiers) == 0:
1059 return u"<span style='color: red;'>%s %s </span>" % (
1060 employe, u"sans dossier actif")
1061 else:
1062 return employe
1063 except Exception:
1064 if obj.statut in (1, 2): # ouverte, ouverture imminente
1065 css = "style='color: red;'"
1066 else:
1067 css = ""
1068 return u"<span %s>Pas de responsable</span>" % css
1069 _responsable.allow_tags = True
1070 _responsable.short_description = u"Responsable"
1071 _responsable.admin_order_field = 'responsable__employe__nom'
1072
1073 def has_add_permission(self, request=None):
1074 return False
1075
1076 def has_change_permission(self, request, obj=None):
1077 return in_drh_or_admin(request.user)
1078
1079 def has_delete_permission(self, request, obj=None):
1080 return False
1081
1082
1083 class ServiceAdminBase(ArchivableAdmin, DerniereModificationAdmin, BaseAdmin):
1084 list_display = ('nom', '_archive', 'derniere_modification')
1085 list_filter = ('archive', )
1086 fieldsets = (
1087 (None, {'fields': ('nom', 'archive')}),
1088 )
1089
1090
1091 class ServiceAdmin(reversion.VersionAdmin, ServiceAdminBase):
1092 ignore_duplicate_revisions = True
1093
1094
1095 class ServiceProxyAdmin(ServiceAdminBase):
1096 list_display = ('nom', '_organigramme', '_archive', )
1097 actions = None
1098
1099 def __init__(self, *args, **kwargs):
1100 super(ServiceProxyAdmin, self).__init__(*args, **kwargs)
1101 self.list_display_links = (None, )
1102
1103 def queryset(self, request):
1104 return super(ServiceProxyAdmin, self).queryset(request) \
1105 .annotate(num_postes=Count('rh_postes')) \
1106 .filter(num_postes__gt=0)
1107
1108 def has_add_permission(self, obj):
1109 return False
1110
1111 def has_change_permission(self, request, obj=None):
1112 user_groups = [g.name for g in request.user.groups.all()]
1113 if groups.CORRESPONDANT_RH in user_groups or \
1114 groups.ADMINISTRATEURS in user_groups or \
1115 groups.DIRECTEUR_DE_BUREAU in user_groups or \
1116 in_drh_or_admin(request.user):
1117 return True
1118 return False
1119
1120 def _organigramme(self, obj):
1121 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1122 (reverse('rho_service', args=(obj.id,)))
1123 _organigramme.allow_tags = True
1124 _organigramme.short_description = "Organigramme"
1125
1126
1127 class StatutAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
1128 BaseAdmin):
1129 ignore_duplicate_revisions = True
1130 list_display = ('code', 'nom', 'derniere_modification')
1131 fieldsets = (
1132 (None, {
1133 'fields': ('code', 'nom', ),
1134 }),
1135 )
1136
1137
1138 class TauxChangeAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
1139 BaseAdmin):
1140 ignore_duplicate_revisions = True
1141 list_display = ('taux', 'devise', 'annee', 'derniere_modification')
1142 list_filter = ('devise',)
1143 fieldsets = (
1144 (None, {
1145 'fields': ('taux', 'devise', 'annee', ),
1146 }),
1147 )
1148
1149
1150 class TypeContratAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
1151 BaseAdmin):
1152 ignore_duplicate_revisions = True
1153 list_display = ('nom', 'nom_long', 'derniere_modification')
1154 fieldsets = (
1155 (None, {
1156 'fields': ('nom', 'nom_long', ),
1157 }),
1158 )
1159
1160
1161 class TypePosteAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
1162 BaseAdmin):
1163 ignore_duplicate_revisions = True
1164 search_fields = ('nom', 'nom_feminin', )
1165 list_display = ('nom', 'categorie_emploi', 'derniere_modification')
1166 list_filter = ('categorie_emploi', 'famille_professionnelle')
1167 fieldsets = (
1168 (None, {
1169 'fields': (
1170 'nom', 'nom_feminin', 'is_responsable', 'categorie_emploi',
1171 'famille_professionnelle',
1172 )
1173 }),
1174 )
1175
1176
1177 class TypeRemunerationAdmin(reversion.VersionAdmin, ArchivableAdmin,
1178 DerniereModificationAdmin, BaseAdmin):
1179 ignore_duplicate_revisions = True
1180 list_display = (
1181 'nom', 'type_paiement', 'nature_remuneration', '_archive',
1182 'derniere_modification'
1183 )
1184 list_filter = ('archive', )
1185 fieldsets = (
1186 (None, {
1187 'fields': (
1188 'nom', 'type_paiement', 'nature_remuneration', 'archive'
1189 )
1190 }),
1191 )
1192
1193
1194 class TypeRevalorisationAdmin(reversion.VersionAdmin,
1195 DerniereModificationAdmin, BaseAdmin):
1196 ignore_duplicate_revisions = True
1197 list_display = ('nom', 'derniere_modification')
1198 fieldsets = (
1199 (None, {'fields': ('nom',)}),
1200 )
1201
1202
1203 class ValeurPointAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
1204 BaseAdmin):
1205 ignore_duplicate_revisions = True
1206 list_display = (
1207 '_devise_code', '_devise_nom', 'annee', 'implantation',
1208 'valeur', 'derniere_modification'
1209 )
1210 list_filter = ('annee', 'devise', 'implantation__region', )
1211 fieldsets = (
1212 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
1213 )
1214
1215 def _devise_code(self, obj):
1216 return obj.devise.code
1217 _devise_code.short_description = "Code de la devise"
1218
1219 def _devise_nom(self, obj):
1220 return obj.devise.nom
1221 _devise_nom.short_description = "Nom de la devise"
1222
1223
1224 class ImplantationProxyAdmin(BaseAdmin):
1225 list_display = ('nom', '_organigramme')
1226 actions = None
1227
1228 def __init__(self, *args, **kwargs):
1229 super(ImplantationProxyAdmin, self).__init__(*args, **kwargs)
1230 self.list_display_links = (None, )
1231
1232 def has_add_permission(self, obj):
1233 return False
1234
1235 def has_change_permission(self, request, obj=None):
1236 user_groups = [g.name for g in request.user.groups.all()]
1237 if groups.CORRESPONDANT_RH in user_groups or \
1238 groups.ADMINISTRATEURS in user_groups or \
1239 groups.DIRECTEUR_DE_BUREAU in user_groups or \
1240 in_drh_or_admin(request.user):
1241 return True
1242 return False
1243
1244 def _organigramme(self, obj):
1245 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1246 reverse('rho_implantation', args=(obj.id,))
1247 )
1248 _organigramme.allow_tags = True
1249 _organigramme.short_description = "Organigramme"
1250
1251
1252 class RegionProxyAdmin(BaseAdmin):
1253 list_display = ('nom', '_organigramme')
1254 actions = None
1255
1256 def __init__(self, *args, **kwargs):
1257 super(RegionProxyAdmin, self).__init__(*args, **kwargs)
1258 self.list_display_links = (None, )
1259
1260 def has_add_permission(self, obj):
1261 return False
1262
1263 def has_change_permission(self, request, obj=None):
1264 user_groups = [g.name for g in request.user.groups.all()]
1265 if groups.CORRESPONDANT_RH in user_groups or \
1266 groups.ADMINISTRATEURS in user_groups or \
1267 groups.DIRECTEUR_DE_BUREAU in user_groups or \
1268 in_drh_or_admin(request.user):
1269 return True
1270 return False
1271
1272 def _organigramme(self, obj):
1273 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1274 reverse('rho_region', args=(obj.id,))
1275 )
1276 _organigramme.allow_tags = True
1277 _organigramme.short_description = "Organigramme"
1278
1279
1280 admin.site.register(rh.Classement, ClassementAdmin)
1281 admin.site.register(rh.Devise, DeviseAdmin)
1282 admin.site.register(rh.Dossier, DossierAdmin)
1283 admin.site.register(EmployeProxy, EmployeProxyAdmin)
1284 admin.site.register(ServiceProxy, ServiceProxyAdmin)
1285 admin.site.register(rh.Employe, EmployeAdmin)
1286 admin.site.register(rh.CategorieEmploi, CategorieEmploiAdmin)
1287 admin.site.register(rh.FamilleProfessionnelle)
1288 admin.site.register(rh.OrganismeBstg, OrganismeBstgAdmin)
1289 admin.site.register(rh.Poste, PosteAdmin)
1290 admin.site.register(
1291 rh.ResponsableImplantationProxy, ResponsableImplantationAdmin
1292 )
1293 admin.site.register(rh.Service, ServiceAdmin)
1294 admin.site.register(rh.Statut, StatutAdmin)
1295 admin.site.register(rh.TauxChange, TauxChangeAdmin)
1296 admin.site.register(rh.TypeContrat, TypeContratAdmin)
1297 admin.site.register(rh.TypePoste, TypePosteAdmin)
1298 admin.site.register(rh.TypeRemuneration, TypeRemunerationAdmin)
1299 admin.site.register(rh.TypeRevalorisation, TypeRevalorisationAdmin)
1300 admin.site.register(rh.ValeurPoint, ValeurPointAdmin)
1301 admin.site.register(ImplantationProxy, ImplantationProxyAdmin)
1302 admin.site.register(RegionProxy, RegionProxyAdmin)