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