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