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