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