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