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