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