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