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