3cf87607fff95c8fee4b6202231eff0a57be45dc
[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 'est_cadre',
433 'statut',
434 'organisme_bstg',)}),
435 ('Recrutement', {
436 'fields': (
437 'statut_residence',
438 'remplacement',
439 'remplacement_de', )}),
440 ('Rémunération', {
441 'fields': (
442 'classement',
443 ('regime_travail', 'regime_travail_nb_heure_semaine'),)}),
444 ('Occupation du Poste par cet Employe', {
445 'fields': (('date_debut', 'date_fin'), )}
446 ),
447 )
448 form = make_ajax_form(rh.Dossier, {
449 'employe': 'employes',
450 'poste': 'postes',
451 'remplacement_de': 'dossiers',
452 }, superclass=DossierForm)
453
454 def lookup_allowed(self, key, value):
455 if key in (
456 'employe__nom__istartswith',
457 'poste__implantation__zone_administrative__code__exact',
458 'poste__implantation__zone_administrative__in',
459 'poste__implantation__id__exact',
460 'poste__type_poste__id__exact',
461 'poste__type_poste__categorie_emploi__id__exact',
462 'rh_contrats__type_contrat__id__exact',
463 'principal__exact',
464 'principal__isnull',
465 ):
466 return True
467
468 def _id(self, obj):
469 return obj.id
470 _id.short_description = u"#"
471 _id.admin_order_field = "id"
472
473 def _apercu(self, d):
474 apercu_link = u"""<a title="Aperçu du dossier"
475 onclick="return showAddAnotherPopup(this);"
476 href='%s'>
477 <img src="%simg/dossier-apercu.png" />
478 </a>""" % \
479 (reverse('dossier_apercu', args=(d.id,)),
480 settings.STATIC_URL,
481 )
482 return apercu_link
483 _apercu.allow_tags = True
484 _apercu.short_description = u""
485
486 def _nom(self, obj):
487 return "Dossier"
488 _nom.allow_tags = True
489 _nom.short_description = u"Dossier"
490
491 def _employe(self, obj):
492 employe = obj.employe
493 view_link = reverse('employe_apercu', args=(employe.id,))
494 edit_link = reverse('admin:rh_employe_change', args=(employe.id,))
495 style = ""
496 view = u"""<a href="%s"
497 title="Aperçu l'employé"
498 onclick="return showAddAnotherPopup(this);">
499 <img src="%simg/employe-apercu.png" />
500 </a>""" % (view_link, settings.STATIC_URL,)
501 return u"""%s<a href='%s' style="%s;">%s</a>""" % \
502 (view, edit_link, style, employe)
503 _employe.allow_tags = True
504 _employe.short_description = u"Employé"
505 _employe.admin_order_field = "employe__nom"
506
507 def _poste(self, dossier):
508 link = u"""<a title="Aperçu du poste"
509 onclick="return showAddAnotherPopup(this);"
510 href='%s'><img src="%simg/poste-apercu.png" />
511 </a>
512 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
513 (reverse('poste_apercu', args=(dossier.poste.id,)),
514 settings.STATIC_URL,
515 reverse('admin:rh_poste_change', args=(dossier.poste.id,)),
516 dossier.poste.nom,
517 dossier.poste.id,
518 )
519 return link
520 _poste.allow_tags = True
521 _poste.short_description = u'Poste'
522 _poste.admin_order_field = 'poste__nom'
523
524 def _zone_administrative(self, obj):
525 return obj.poste.implantation.zone_administrative.code
526 _zone_administrative.short_description = u"Zone administrative"
527 _zone_administrative.admin_order_field = \
528 'poste__implantation__zone_administrative__code'
529
530 def _implantation(self, obj):
531 return obj.poste.implantation.nom
532 _implantation.short_description = u"Implantation"
533 _implantation.admin_order_field = 'poste__implantation__nom'
534
535 def _date_debut(self, obj):
536 return date(obj.date_debut)
537
538 _date_debut.short_description = u'Début'
539 _date_debut.admin_order_field = 'date_debut'
540
541 def _date_fin(self, obj):
542 return date(obj.date_fin)
543 _date_fin.short_description = u'Fin'
544 _date_fin.admin_order_field = 'date_fin'
545
546 def _date_modification(self, obj):
547 return date(obj.date_modification) \
548 if obj.date_modification is not None else "(aucune)"
549 _date_modification.short_description = u'date modification'
550 _date_modification.admin_order_field = 'date_modification'
551
552 def _dae(self, d):
553 apercu_link = ""
554 dossiers_dae = d.dossiers_dae.all()
555 if len(dossiers_dae) > 0:
556 dossier_dae = dossiers_dae[0]
557 apercu_link = u"""<a title="Aperçu du dossier"
558 onclick="return showAddAnotherPopup(this);"
559 href='%s'>
560 <img src="%simg/loupe.png" />
561 </a>""" % \
562 (reverse('embauche_consulter', args=(dossier_dae.id,)),
563 settings.STATIC_URL,
564 )
565 return apercu_link
566 _dae.allow_tags = True
567 _dae.short_description = u"DAE"
568
569 def save_formset(self, request, form, formset, change):
570 instances = formset.save(commit=False)
571 for instance in instances:
572 if instance.__class__ == rh.DossierCommentaire:
573 instance.owner = request.user
574 instance.date_creation = datetime.datetime.now()
575 instance.save()
576
577
578 class EmployeAdminBase(DateRangeMixin, ProtectRegionMixin,
579 DerniereModificationAdmin, BaseAdmin):
580 prefixe_recherche_temporelle = "rh_dossiers__"
581 alphabet_filter = 'nom'
582 DEFAULT_ALPHABET = u'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
583 search_fields = (
584 'id', 'nom', 'prenom', 'nom_affichage',
585 'rh_dossiers__poste__nom',
586 'rh_dossiers__poste__nom_feminin'
587 )
588 ordering = ('nom', )
589 form = EmployeAdminForm
590 list_display = (
591 '_id', '_apercu', '_nom', '_dossiers_postes',
592 #'_zone_administrative',
593 #'_implantation',
594 'date_entree',
595 'derniere_modification'
596 )
597 list_display_links = ('_nom',)
598 list_filter = (
599 'rh_dossiers__poste__implantation__zone_administrative',
600 'rh_dossiers__poste__implantation', 'nb_postes'
601 )
602 inlines = (
603 AyantDroitInline, DossierROInline, EmployePieceInline,
604 EmployeCommentaireInline
605 )
606 fieldsets = (
607 ('Identification', {
608 'fields': (
609 ('nom', 'prenom'),
610 ('nom_affichage', 'genre'),
611 'nationalite',
612 'date_naissance',
613 )
614 }),
615 ('Informations personnelles', {
616 'fields': ('situation_famille', 'date_entree', )
617 }),
618 ('Coordonnées personnelles', {
619 'fields': (
620 ('tel_domicile', 'tel_cellulaire'),
621 ('adresse', 'ville'),
622 ('code_postal', 'province'),
623 'pays',
624 'courriel_perso'
625 )
626 }),
627 )
628
629 def _id(self, obj):
630 return obj.id
631 _id.short_description = u"#"
632 _id.admin_order_field = "id"
633
634 def _apercu(self, obj):
635 return u"""<a title="Aperçu de l'employé"
636 onclick="return showAddAnotherPopup(this);"
637 href='%s'>
638 <img src="%simg/employe-apercu.png" />
639 </a>""" % \
640 (reverse('employe_apercu', args=(obj.id,)), settings.STATIC_URL)
641 _apercu.allow_tags = True
642 _apercu.short_description = u""
643
644 def _nom(self, obj):
645 edit_link = reverse('admin:rh_employe_change', args=(obj.id,))
646 return u"""<a href='%s'><strong>%s</strong></a>""" % \
647 (edit_link, "%s %s" % (obj.nom.upper(), obj.prenom))
648 _nom.allow_tags = True
649 _nom.short_description = u"Employé"
650 _nom.admin_order_field = "nom"
651
652 def _zone_administrative(self, obj):
653 try:
654 d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
655 zone = d.poste.implantation.zone_administrative.code
656 except:
657 zone = None
658 return zone
659 _zone_administrative.short_description = u"Zone administrative"
660
661 def _implantation(self, obj):
662 try:
663 d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
664 implantation = d.poste.implantation.nom
665 except:
666 implantation = None
667 return implantation
668 _implantation.short_description = u"Implantation"
669
670 def _dossiers_postes(self, obj):
671 l = []
672 for d in obj.rh_dossiers.all().order_by('-date_debut'):
673 link_style = u''
674 list_style = u''
675 if d.date_fin is not None and d.date_fin < datetime.date.today():
676 link_style = u' style="color:#666;"'
677 list_style = u' style="color:grey;"'
678
679 dossier = u"""<a title="Aperçu du dossier"
680 href="%s"
681 onclick="return showAddAnotherPopup(this);"
682 title="Aperçu du dossier">
683 <img src="%simg/dossier-apercu.png" />
684 </a>
685 <a href="%s"%s>Dossier</a>
686 &nbsp;""" % \
687 (reverse('dossier_apercu', args=(d.id,)),
688 settings.STATIC_URL,
689 reverse('admin:rh_dossier_change', args=(d.id,)),
690 link_style,)
691
692 poste = u"""<a title="Aperçu du poste"
693 href="%s"
694 onclick="return showAddAnotherPopup(this);"
695 title="Aperçu du poste">
696 <img src="%simg/poste-apercu.png" />
697 </a>
698 <a href="%s"%s>%s [%d]</a>
699 &nbsp;""" % \
700 (reverse('poste_apercu', args=(d.poste.id,)),
701 settings.STATIC_URL,
702 reverse('admin:rh_poste_change', args=(d.poste.id,)),
703 link_style,
704 d.poste.nom,
705 d.poste.id)
706
707 link = u"""<li%s>%s %s</li>""" % \
708 (list_style, dossier, poste)
709
710 l.append(link)
711 return "<ul>%s</ul>" % "\n".join(l)
712 _dossiers_postes.allow_tags = True
713 _dossiers_postes.short_description = u"Dossiers et postes"
714
715 def _date_modification(self, obj):
716 return date(obj.date_modification) \
717 if obj.date_modification is not None else "(aucune)"
718 _date_modification.short_description = u'date modification'
719 _date_modification.admin_order_field = 'date_modification'
720
721 def queryset(self, request):
722 qs = super(EmployeAdminBase, self).queryset(request)
723 return qs.select_related(depth=1).order_by('nom')
724
725 def save_formset(self, request, form, formset, change):
726 instances = formset.save(commit=False)
727 for instance in instances:
728 if instance.__class__ == rh.EmployeCommentaire:
729 instance.owner = request.user
730 instance.date_creation = datetime.datetime.now()
731 instance.save()
732
733
734 class EmployeAdmin(reversion.VersionAdmin, EmployeAdminBase):
735 change_list_template = "admin/rh/employe/change_list.html"
736 ignore_duplicate_revisions = True
737
738
739 class EmployeProxyAdmin(EmployeAdminBase):
740 list_display = ('_id', '_apercu', '_nom', '_organigramme')
741 list_per_page = 500
742 actions = None
743
744 def __init__(self, *args, **kwargs):
745 super(EmployeProxyAdmin, self).__init__(*args, **kwargs)
746 self.list_display_links = (None, )
747
748 def queryset(self, request):
749 qs = super(ProtectRegionMixin, self).queryset(request)
750
751 if in_drh_or_admin(request.user) or \
752 user_gere_obj_de_sa_region(request.user):
753 return qs
754
755 return qs.none()
756
757 def has_add_permission(self, obj):
758 return False
759
760 def has_change_permission(self, request, obj=None):
761 user_groups = [g.name for g in request.user.groups.all()]
762 if groups.CORRESPONDANT_RH in user_groups or \
763 groups.ADMINISTRATEURS in user_groups or \
764 groups.DIRECTEUR_DE_BUREAU in user_groups or \
765 in_drh_or_admin(request.user):
766 return True
767 return False
768
769 def _organigramme(self, obj):
770 l = []
771 for d in rh.Dossier.objects.filter(
772 Q(date_fin__gt=datetime.date.today()) | Q(date_fin=None),
773 Q(date_debut__lt=datetime.date.today()) | Q(date_debut=None),
774 employe=obj.id
775 ):
776 organigramme = \
777 u'Organigramme, niveau: ' \
778 u'<input type="text" id="level_%s" ' \
779 u'style="width:30px;height:15px;" /> ' \
780 u'<input type="button" value="Générer" ' \
781 u"""onclick="window.location='%s' + """ \
782 u"""document.getElementById('level_%s').value" />""" % (
783 d.poste.id,
784 reverse('rho_employe_sans_niveau', args=(d.poste.id,)),
785 d.poste.id
786 )
787 link = u"""<li>%s [%s]:<br />%s</li>""" % (
788 d.poste.nom,
789 d.poste.id,
790 organigramme
791 )
792 l.append(link)
793 return "<ul>%s</ul>" % "\n".join(l)
794
795 _organigramme.allow_tags = True
796 _organigramme.short_description = "Organigramme"
797
798
799 class CategorieEmploiAdmin(reversion.VersionAdmin,
800 DerniereModificationAdmin, BaseAdmin):
801 ignore_duplicate_revisions = True
802 list_display = ('nom', 'derniere_modification')
803 inlines = (TypePosteInline,)
804 fieldsets = (
805 (None, {'fields': ('nom', )}),
806 )
807
808
809 class OrganismeBstgAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
810 BaseAdmin):
811 ignore_duplicate_revisions = True
812 search_fields = ('nom',)
813 list_display = ('nom', 'type', 'pays', 'derniere_modification')
814 list_filter = ('type', )
815 inlines = (DossierROInline,)
816 fieldsets = (
817 (None, {'fields': ('nom', 'type', 'pays',)}),
818 )
819
820
821 class PosteAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
822 AjaxSelect, DerniereModificationAdmin, BaseAdmin):
823 change_list_template = "admin/rh/poste/change_list.html"
824 ignore_duplicate_revisions = True
825 form = make_ajax_form(rh.Poste, {
826 'implantation': 'implantations',
827 'type_poste': 'typepostes',
828 'responsable': 'postes',
829 'valeur_point_min': 'valeurpoints',
830 'valeur_point_max': 'valeurpoints',
831 })
832 alphabet_filter = 'nom'
833 search_fields = (
834 'id',
835 'nom',
836 'implantation__nom',
837 'implantation__zone_administrative__code',
838 'implantation__zone_administrative__nom',
839 'rh_dossiers__employe__id',
840 'rh_dossiers__employe__nom',
841 'rh_dossiers__employe__prenom',
842 )
843 list_display = (
844 '_id', '_apercu', '_nom', '_occupe_par', 'implantation', '_service',
845 '_responsable', 'date_debut', 'date_fin', 'derniere_modification',
846 '_dae'
847 )
848 list_filter = (
849 'implantation__zone_administrative',
850 'implantation',
851 'service',
852 'type_poste',
853 'type_poste__categorie_emploi',
854 'type_poste__famille_professionnelle',
855 'vacant',
856 )
857 list_display_links = ('_nom',)
858 fieldsets = (
859 (None, {'fields': (
860 ('nom', 'nom_feminin'),
861 'implantation',
862 'type_poste',
863 'service',
864 'responsable',
865 )}
866 ),
867 ('Contrat', {
868 'fields': ((
869 'regime_travail',
870 'regime_travail_nb_heure_semaine'),
871 )}
872 ),
873 ('Recrutement', {
874 'fields': (('local', 'expatrie', 'mise_a_disposition', 'appel'),)}
875 ),
876 ('Rémunération', {
877 'fields': (('classement_min',
878 'valeur_point_min',
879 'devise_min',
880 'salaire_min',
881 'indemn_min',
882 'autre_min',),
883 ('classement_max',
884 'valeur_point_max',
885 'devise_max',
886 'salaire_max',
887 'indemn_max',
888 'autre_max',),
889 )}),
890 ('Comparatifs de rémunération', {
891 'fields': ('devise_comparaison',
892 ('comp_locale_min', 'comp_locale_max'),
893 ('comp_universite_min', 'comp_universite_max'),
894 ('comp_fonctionpub_min', 'comp_fonctionpub_max'),
895 ('comp_ong_min', 'comp_ong_max'),
896 ('comp_autre_min', 'comp_autre_max'))}
897 ),
898 ('Justification', {
899 'fields': ('justification',)}
900 ),
901 ('Autres Méta-données', {
902 'fields': ('date_debut', 'date_fin')}
903 ),
904 )
905
906 inlines = (PosteFinancementInline,
907 PostePieceInline,
908 DossierROInline,
909 PosteComparaisonInline,
910 PosteCommentaireInline, )
911
912 def lookup_allowed(self, key, value):
913 return key in (
914 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
915 'date_fin__isnull',
916 'implantation__zone_administrative__code__exact',
917 'implantation__id__exact', 'type_poste__id__exact',
918 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
919 'service__isnull', 'vacant__exact', 'vacant__isnull'
920 ) or super(PosteAdmin, self).lookup_allowed(key, value)
921
922 def _id(self, obj):
923 return "%s" % obj.id
924 _id.short_description = '#'
925 _id.admin_order_field = 'id'
926
927 def _apercu(self, poste):
928 view_link = u"""<a onclick="return showAddAnotherPopup(this);"
929 title="Aperçu du poste"
930 href='%s'>
931 <img src="%simg/poste-apercu.png" />
932 </a>""" % \
933 (reverse('poste_apercu', args=(poste.id,)),
934 settings.STATIC_URL,)
935 return view_link
936 _apercu.allow_tags = True
937 _apercu.short_description = ''
938
939 def _nom(self, poste):
940 return """<a href="%s">%s</a>""" % \
941 (reverse('admin:rh_poste_change', args=(poste.id,)),
942 poste.nom)
943 _nom.allow_tags = True
944 _nom.short_description = u'Poste'
945 _nom.admin_order_field = 'nom'
946
947 def _occupe_par(self, obj):
948 """Formatte la méthode Poste.occupe_par() pour l'admin"""
949 output = u"Vacant"
950 if obj.date_fin is not None and obj.date_fin < datetime.date.today():
951 return u"s/o"
952 employes = obj.occupe_par()
953 if employes:
954 l = []
955 for e in employes:
956 link = u"""<a href='%s'
957 title='Aperçu de l\'employé'
958 onclick='return showAddAnotherPopup(this)'>
959 <img src='%simg/employe-apercu.png' />
960 </a>
961 <a href='%s'>%s</a>""" % \
962 (reverse('employe_apercu', args=(e.id,)),
963 settings.STATIC_URL,
964 reverse('admin:rh_employe_change', args=(e.id,)),
965 e)
966 l.append(link)
967 output = "\n<br />".join(l)
968 return output
969 _occupe_par.allow_tags = True
970 _occupe_par.short_description = "Occupé par"
971
972 def _zone_administrative(self, poste):
973 return poste.implantation.zone_administrative.code
974 _zone_administrative.short_description = 'Zone administrative'
975 _zone_administrative.admin_order_field = \
976 'implantation__zone_administrative__code'
977
978 def _implantation(self, poste):
979 return poste.implantation.nom
980 _implantation.short_description = 'Implantation'
981 _implantation.admin_order_field = 'implantation'
982
983 def _service(self, obj):
984 return obj.service
985 _service.short_description = 'Service'
986 _service.allow_tags = True
987
988 def _responsable(self, obj):
989 try:
990 responsable = u"""<a href="%s"
991 onclick="return showAddAnotherPopup(this)">
992 <img src="%simg/poste-apercu.png"
993 title="Aperçu du poste" />
994 </a>
995 <a href="%s">%s [%d]</a>
996 <br />""" % \
997 (reverse('poste_apercu', args=(obj.responsable.id,)),
998 settings.STATIC_URL,
999 reverse('admin:rh_poste_change', args=(obj.responsable.id,)),
1000 obj.responsable.nom,
1001 obj.responsable.id)
1002 except:
1003 responsable = ''
1004
1005 try:
1006 dossier = obj.responsable.rh_dossiers.all() \
1007 .order_by('-date_debut')[0]
1008 employe_id = dossier.employe.id
1009 employe_html = u"""<br />
1010 <a href="%s"
1011 onclick="return showAddAnotherPopup(this)">
1012 <img src="%simg/employe-apercu.png"
1013 title="Aperçu de l'employé">
1014 </a>
1015 <a href="%s">%s</a>""" % \
1016 (reverse('employe_apercu', args=(employe_id,)),
1017 settings.STATIC_URL,
1018 reverse('admin:rh_employe_change', args=(employe_id,)),
1019 dossier.employe)
1020 except:
1021 employe_html = ""
1022
1023 return "%s %s" % (responsable, employe_html)
1024 _responsable.short_description = 'Responsable'
1025 _responsable.allow_tags = True
1026
1027 def _date_debut(self, obj):
1028 return date_format(obj.date_debut)
1029 _date_debut.short_description = u'Début'
1030 _date_debut.admin_order_field = 'date_debut'
1031
1032 def _date_fin(self, obj):
1033 return date_format(obj.date_fin)
1034 _date_fin.short_description = u'Fin'
1035 _date_fin.admin_order_field = 'date_fin'
1036
1037 def _dae(self, poste):
1038 apercu_link = ""
1039 postes_dae = poste.postes_dae.all()
1040 if len(postes_dae) > 0:
1041 poste_dae = postes_dae[0]
1042 apercu_link = \
1043 u'<a title="Aperçu du dossier" href="%s" ' \
1044 u'onclick="return showAddAnotherPopup(this);">' \
1045 u'<img src="%simg/loupe.png" /></a>' % (reverse(
1046 'poste_consulter', args=("dae-%s" % poste_dae.id,)
1047 ), settings.STATIC_URL)
1048 return apercu_link
1049 _dae.allow_tags = True
1050 _dae.short_description = u"DAE"
1051
1052 def save_formset(self, request, form, formset, change):
1053 instances = formset.save(commit=False)
1054 for instance in instances:
1055 if instance.__class__ == rh.PosteCommentaire:
1056 instance.owner = request.user
1057 instance.date_creation = datetime.datetime.now()
1058 instance.save()
1059 formset.save_m2m()
1060
1061
1062 class ResponsableInline(admin.TabularInline):
1063 model = rh.ResponsableImplantation
1064 extra = 0
1065 fk_name = "implantation"
1066 form = ResponsableInlineForm
1067
1068
1069 class ResponsableImplantationAdmin(BaseAdmin):
1070 actions = None
1071 fields = ('nom', )
1072 inlines = (ResponsableInline, )
1073 list_filter = ('zone_administrative', 'statut', )
1074 list_display = ('_zone_administrative', '_nom', 'statut', '_responsable', )
1075 list_display_links = ('_nom',)
1076 list_per_page = 500
1077 readonly_fields = ('nom', )
1078 search_fields = (
1079 'nom',
1080 'responsable__employe__id',
1081 'responsable__employe__nom',
1082 'responsable__employe__prenom',
1083 )
1084 ordering = ('nom',)
1085 inlines = (ResponsableInline, )
1086
1087 def _zone_administrative(self, obj):
1088 return obj.zone_administrative.code
1089 _zone_administrative.short_description = u"Zone administrative"
1090 _zone_administrative.admin_order_field = 'zone_administrative__code'
1091
1092 def _nom(self, obj):
1093 return obj.nom
1094 _nom.short_description = u"Implantation"
1095 _nom.admin_order_field = 'nom'
1096
1097 def _responsable(self, obj):
1098 try:
1099 employe = employe = obj.responsable.employe
1100 except Exception, e:
1101 return (
1102 u"<span style='color: red;'>"
1103 u"Pas de responsable</span><!-- %s -->" % e
1104 )
1105 try:
1106 dossiers = employe.dossiers_encours()
1107 if len(dossiers) == 0:
1108 return u"<span style='color: red;'>%s %s </span>" % (
1109 employe, u"sans dossier actif")
1110 else:
1111 return employe
1112 except Exception, e:
1113 return u"<!-- %s -->" % e
1114 _responsable.allow_tags = True
1115 _responsable.short_description = u"Responsable"
1116 _responsable.admin_order_field = 'responsable__employe__nom'
1117
1118 def has_add_permission(self, request=None):
1119 return False
1120
1121 def has_change_permission(self, request, obj=None):
1122 return in_drh_or_admin(request.user)
1123
1124 def has_delete_permission(self, request, obj=None):
1125 return False
1126
1127
1128 class ServiceAdminBase(ArchivableAdmin, DerniereModificationAdmin, BaseAdmin):
1129 list_display = ('nom', '_archive', 'derniere_modification')
1130 list_filter = ('archive', )
1131 fieldsets = (
1132 (None, {'fields': ('nom', 'archive')}),
1133 )
1134
1135
1136 class ServiceAdmin(reversion.VersionAdmin, ServiceAdminBase):
1137 ignore_duplicate_revisions = True
1138
1139
1140 class ServiceProxyAdmin(ServiceAdminBase):
1141 list_display = ('nom', '_organigramme', '_archive', )
1142 actions = None
1143
1144 def __init__(self, *args, **kwargs):
1145 super(ServiceProxyAdmin, self).__init__(*args, **kwargs)
1146 self.list_display_links = (None, )
1147
1148 def queryset(self, request):
1149 return super(ServiceProxyAdmin, self).queryset(request) \
1150 .annotate(num_postes=Count('rh_postes')) \
1151 .filter(num_postes__gt=0)
1152
1153 def has_add_permission(self, obj):
1154 return False
1155
1156 def has_change_permission(self, request, obj=None):
1157 user_groups = [g.name for g in request.user.groups.all()]
1158 if groups.CORRESPONDANT_RH in user_groups or \
1159 groups.ADMINISTRATEURS in user_groups or \
1160 groups.DIRECTEUR_DE_BUREAU in user_groups or \
1161 in_drh_or_admin(request.user):
1162 return True
1163 return False
1164
1165 def _organigramme(self, obj):
1166 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1167 (reverse('rho_service', args=(obj.id,)))
1168 _organigramme.allow_tags = True
1169 _organigramme.short_description = "Organigramme"
1170
1171
1172 class StatutAdmin(reversion.VersionAdmin,
1173 ArchivableAdmin,
1174 DerniereModificationAdmin,
1175 BaseAdmin):
1176 ignore_duplicate_revisions = True
1177 list_display = ('code', 'nom', 'derniere_modification', '_archive')
1178 list_filter = ('archive', )
1179 fieldsets = (
1180 (None, {
1181 'fields': ('code', 'nom', 'archive'),
1182 }),
1183 )
1184
1185
1186 class TauxChangeAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
1187 BaseAdmin):
1188 ignore_duplicate_revisions = True
1189 list_display = ('taux', 'devise', 'annee', 'derniere_modification')
1190 list_filter = ('devise',)
1191 fieldsets = (
1192 (None, {
1193 'fields': ('taux', 'devise', 'annee', ),
1194 }),
1195 )
1196
1197
1198 class TypeContratAdmin(reversion.VersionAdmin,
1199 ArchivableAdmin,
1200 DerniereModificationAdmin,
1201 BaseAdmin):
1202 ignore_duplicate_revisions = True
1203 list_display = ('nom', 'nom_long', 'derniere_modification', '_archive')
1204 list_filter = ('archive', )
1205 fieldsets = (
1206 (None, {
1207 'fields': ('nom', 'nom_long', 'archive'),
1208 }),
1209 )
1210
1211
1212 class TypePosteAdmin(reversion.VersionAdmin,
1213 ArchivableAdmin,
1214 DerniereModificationAdmin,
1215 BaseAdmin):
1216 ignore_duplicate_revisions = True
1217 search_fields = ('nom', 'nom_feminin', )
1218 list_display = ('nom', 'categorie_emploi',
1219 'derniere_modification', '_archive',)
1220 list_filter = ('categorie_emploi', 'famille_professionnelle', 'archive')
1221 fieldsets = (
1222 (None, {
1223 'fields': (
1224 'nom', 'nom_feminin', 'is_responsable', 'categorie_emploi',
1225 'famille_professionnelle',
1226 'archive',
1227 )
1228 }),
1229 )
1230
1231
1232 class TypeRemunerationAdmin(reversion.VersionAdmin, ArchivableAdmin,
1233 DerniereModificationAdmin, BaseAdmin):
1234 ignore_duplicate_revisions = True
1235 list_display = (
1236 'nom', 'type_paiement', 'nature_remuneration', '_archive',
1237 'derniere_modification'
1238 )
1239 list_filter = ('archive', )
1240 fieldsets = (
1241 (None, {
1242 'fields': (
1243 'nom', 'type_paiement', 'nature_remuneration', 'archive'
1244 )
1245 }),
1246 )
1247
1248
1249 class TypeRevalorisationAdmin(reversion.VersionAdmin,
1250 ArchivableAdmin,
1251 DerniereModificationAdmin,
1252 BaseAdmin):
1253 ignore_duplicate_revisions = True
1254 list_display = ('nom', 'derniere_modification', '_archive')
1255 list_filter = ('archive', )
1256 fieldsets = (
1257 (None, {'fields': ('nom', 'archive')}),
1258 )
1259
1260
1261 class ValeurPointAdmin(reversion.VersionAdmin,
1262 DerniereModificationAdmin,
1263 BaseAdmin):
1264 ignore_duplicate_revisions = True
1265 list_display = (
1266 '_devise_code', '_devise_nom', 'annee', 'implantation',
1267 'valeur', 'derniere_modification'
1268 )
1269 list_filter = ('annee', 'devise', 'implantation__zone_administrative', )
1270 fieldsets = (
1271 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
1272 )
1273
1274 def queryset(self, request):
1275 return super(ValeurPointAdmin, self).queryset(request) \
1276 .select_related('devise', 'implantation')
1277
1278 def _devise_code(self, obj):
1279 return obj.devise.code
1280 _devise_code.short_description = "Code de la devise"
1281
1282 def _devise_nom(self, obj):
1283 return obj.devise.nom
1284 _devise_nom.short_description = "Nom de la devise"
1285
1286
1287 class ImplantationProxyAdmin(BaseAdmin):
1288 list_display = ('nom', '_organigramme')
1289 actions = None
1290
1291 def __init__(self, *args, **kwargs):
1292 super(ImplantationProxyAdmin, self).__init__(*args, **kwargs)
1293 self.list_display_links = (None, )
1294
1295 def has_add_permission(self, obj):
1296 return False
1297
1298 def has_change_permission(self, request, obj=None):
1299 user_groups = [g.name for g in request.user.groups.all()]
1300 if groups.CORRESPONDANT_RH in user_groups or \
1301 groups.ADMINISTRATEURS in user_groups or \
1302 groups.DIRECTEUR_DE_BUREAU in user_groups or \
1303 in_drh_or_admin(request.user):
1304 return True
1305 return False
1306
1307 def _organigramme(self, obj):
1308 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1309 reverse('rho_implantation', args=(obj.id,))
1310 )
1311 _organigramme.allow_tags = True
1312 _organigramme.short_description = "Organigramme"
1313
1314
1315 class RegionProxyAdmin(BaseAdmin):
1316 list_display = ('nom', '_organigramme')
1317 actions = None
1318
1319 def __init__(self, *args, **kwargs):
1320 super(RegionProxyAdmin, self).__init__(*args, **kwargs)
1321 self.list_display_links = (None, )
1322
1323 def has_add_permission(self, obj):
1324 return False
1325
1326 def has_change_permission(self, request, obj=None):
1327 user_groups = [g.name for g in request.user.groups.all()]
1328 if groups.CORRESPONDANT_RH in user_groups or \
1329 groups.ADMINISTRATEURS in user_groups or \
1330 groups.DIRECTEUR_DE_BUREAU in user_groups or \
1331 in_drh_or_admin(request.user):
1332 return True
1333 return False
1334
1335 def _organigramme(self, obj):
1336 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1337 reverse('rho_region', args=(obj.id,))
1338 )
1339 _organigramme.allow_tags = True
1340 _organigramme.short_description = "Organigramme"
1341
1342
1343 class ProfileInline(admin.StackedInline):
1344 model = rh.UserProfile
1345
1346
1347 class RHUserAdmin(UserAdmin):
1348 inlines = list(UserAdmin.inlines) + [ProfileInline]
1349
1350
1351 admin.site.unregister(User)
1352 admin.site.register(User, RHUserAdmin)
1353
1354 admin.site.register(rh.Classement, ClassementAdmin)
1355 admin.site.register(rh.Devise, DeviseAdmin)
1356 admin.site.register(rh.Dossier, DossierAdmin)
1357 admin.site.register(EmployeProxy, EmployeProxyAdmin)
1358 admin.site.register(ServiceProxy, ServiceProxyAdmin)
1359 admin.site.register(rh.Employe, EmployeAdmin)
1360 admin.site.register(rh.CategorieEmploi, CategorieEmploiAdmin)
1361 admin.site.register(rh.FamilleProfessionnelle)
1362 admin.site.register(rh.OrganismeBstg, OrganismeBstgAdmin)
1363 admin.site.register(rh.Poste, PosteAdmin)
1364 admin.site.register(
1365 rh.ResponsableImplantationProxy, ResponsableImplantationAdmin
1366 )
1367 admin.site.register(rh.Service, ServiceAdmin)
1368 admin.site.register(rh.Statut, StatutAdmin)
1369 admin.site.register(rh.TauxChange, TauxChangeAdmin)
1370 admin.site.register(rh.TypeContrat, TypeContratAdmin)
1371 admin.site.register(rh.TypePoste, TypePosteAdmin)
1372 admin.site.register(rh.TypeRemuneration, TypeRemunerationAdmin)
1373 admin.site.register(rh.TypeRevalorisation, TypeRevalorisationAdmin)
1374 admin.site.register(rh.ValeurPoint, ValeurPointAdmin)
1375 admin.site.register(ImplantationProxy, ImplantationProxyAdmin)
1376 admin.site.register(RegionProxy, RegionProxyAdmin)