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