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