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