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