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