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