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