Merge branch 'dev' of git.auf.org: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 employe = obj.responsable.rh_dossiers.all()[0]
868 employe_id = obj.responsable.rh_dossiers.all()[0].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 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 = ('nom', 'statut', '_responsable', )
950 readonly_fields = ('nom', )
951 fields = ('nom', )
952 inlines = (ResponsableInline, )
953
954 def _responsable(self, obj):
955 try:
956 employe = obj.responsable.employe
957 dossiers = employe.dossiers_encours()
958 if len(dossiers) == 0:
959 return u"<span style='color: red;'>%s %s </span>" % (
960 employe, u"sans dossier actif")
961 else:
962 return employe
963 except Exception:
964 if obj.statut in (1, 2): # ouverte, ouverture imminente
965 css = "style='color: red;'"
966 else:
967 css = ""
968 return u"<span %s>Pas de responsable</span>" % css
969 _responsable.allow_tags = True
970 _responsable.short_description = u"Responsable"
971
972 def has_add_permission(self, request=None):
973 return False
974
975 def has_change_permission(self, request, obj=None):
976 return in_drh_or_admin(request.user)
977
978 def has_delete_permission(self, request, obj=None):
979 return False
980
981
982 class ServiceAdmin(AUFMetadataAdminMixin, BaseAdmin, ArchiveMixin):
983 list_display = (
984 'nom',
985 '_archive',
986 '_date_modification',
987 'user_modification',
988 )
989 list_filter = ('archive', )
990 fieldsets = AUFMetadataAdminMixin.fieldsets + (
991 (None, {
992 'fields': ('nom', 'archive', ),
993 }),
994 )
995
996 def _date_modification(self, obj):
997 return date(obj.date_modification) \
998 if obj.date_modification is not None else "(aucune)"
999 _date_modification.short_description = u'date modification'
1000 _date_modification.admin_order_field = 'date_modification'
1001
1002
1003 class ServiceProxyAdmin(ServiceAdmin):
1004 list_display = ('nom', '_organigramme', '_archive', )
1005 actions = None
1006
1007 def __init__(self, *args, **kwargs):
1008 super(ServiceProxyAdmin, self).__init__(*args, **kwargs)
1009 self.list_display_links = (None, )
1010
1011 def queryset(self, request):
1012 return super(ServiceProxyAdmin, self).queryset(request) \
1013 .annotate(num_postes=Count('rh_postes')) \
1014 .filter(num_postes__gt=0)
1015
1016 def has_add_permission(self, obj):
1017 return False
1018
1019 def has_change_permission(self, request, obj=None):
1020 return in_drh_or_admin(request.user)
1021
1022 def _organigramme(self, obj):
1023 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1024 (reverse('rho_service', args=(obj.id,)))
1025 _organigramme.allow_tags = True
1026 _organigramme.short_description = "Organigramme"
1027
1028
1029 class StatutAdmin(AUFMetadataAdminMixin, BaseAdmin):
1030 list_display = ('code', 'nom', '_date_modification', 'user_modification', )
1031 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1032 (None, {
1033 'fields': ('code', 'nom', ),
1034 }),
1035 )
1036
1037 def _date_modification(self, obj):
1038 return date(obj.date_modification) \
1039 if obj.date_modification is not None else "(aucune)"
1040 _date_modification.short_description = u'date modification'
1041 _date_modification.admin_order_field = 'date_modification'
1042
1043
1044 class TauxChangeAdmin(BaseAdmin):
1045 list_display = (
1046 'taux',
1047 'devise',
1048 'annee',
1049 '_date_modification',
1050 'user_modification',
1051 )
1052 list_filter = ('devise', )
1053 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1054 (None, {
1055 'fields': ('taux', 'devise', 'annee', ),
1056 }),
1057 )
1058
1059 def _date_modification(self, obj):
1060 return date(obj.date_modification) \
1061 if obj.date_modification is not None else "(aucune)"
1062 _date_modification.short_description = u'date modification'
1063 _date_modification.admin_order_field = 'date_modification'
1064
1065
1066 class TypeContratAdmin(BaseAdmin):
1067 list_display = (
1068 'nom',
1069 'nom_long',
1070 '_date_modification',
1071 'user_modification',
1072 )
1073 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1074 (None, {
1075 'fields': ('nom', 'nom_long', ),
1076 }),
1077 )
1078
1079 def _date_modification(self, obj):
1080 return date(obj.date_modification) \
1081 if obj.date_modification is not None else "(aucune)"
1082 _date_modification.short_description = u'date modification'
1083 _date_modification.admin_order_field = 'date_modification'
1084
1085
1086 class TypePosteAdmin(AUFMetadataAdminMixin, BaseAdmin):
1087 search_fields = ('nom', 'nom_feminin', )
1088 list_display = (
1089 'nom',
1090 'categorie_emploi',
1091 '_date_modification',
1092 'user_modification',
1093 )
1094 list_filter = ('categorie_emploi', 'famille_professionnelle')
1095 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1096 (None, {
1097 'fields': (
1098 'nom',
1099 'nom_feminin',
1100 'is_responsable',
1101 'categorie_emploi',
1102 'famille_professionnelle',
1103 )}
1104 ),
1105 )
1106
1107 def _date_modification(self, obj):
1108 return date(obj.date_modification) \
1109 if obj.date_modification is not None else "(aucune)"
1110 _date_modification.short_description = u'date modification'
1111 _date_modification.admin_order_field = 'date_modification'
1112
1113
1114 class TypeRemunerationAdmin(AUFMetadataAdminMixin, BaseAdmin,
1115 ArchiveMixin):
1116 list_display = (
1117 'nom',
1118 'type_paiement',
1119 'nature_remuneration',
1120 '_archive',
1121 '_date_modification',
1122 'user_modification',)
1123 list_filter = ('archive', )
1124 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1125 (None, {'fields': ('nom', 'type_paiement', 'nature_remuneration',
1126 'archive')}),
1127 )
1128
1129 def _date_modification(self, obj):
1130 return date(obj.date_modification) \
1131 if obj.date_modification is not None else "(aucune)"
1132 _date_modification.short_description = u'date modification'
1133 _date_modification.admin_order_field = 'date_modification'
1134
1135
1136 class TypeRevalorisationAdmin(AUFMetadataAdminMixin, BaseAdmin):
1137 list_display = ('nom', '_date_modification', 'user_modification', )
1138 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1139 (None, {'fields': ('nom', )}),
1140 )
1141
1142 def _date_modification(self, obj):
1143 return date(obj.date_modification) \
1144 if obj.date_modification is not None else "(aucune)"
1145 _date_modification.short_description = u'date modification'
1146 _date_modification.admin_order_field = 'date_modification'
1147
1148
1149 class ValeurPointAdmin(AUFMetadataAdminMixin, BaseAdmin):
1150 list_display = (
1151 '_devise_code',
1152 '_devise_nom',
1153 'annee',
1154 'implantation',
1155 'valeur',
1156 '_date_modification',
1157 'user_modification',
1158 )
1159 list_filter = ('annee', 'devise', 'implantation__region', )
1160 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1161 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee', )}),
1162 )
1163
1164 def _date_modification(self, obj):
1165 return date(obj.date_modification) \
1166 if obj.date_modification is not None else "(aucune)"
1167 _date_modification.short_description = u'date modification'
1168 _date_modification.admin_order_field = 'date_modification'
1169
1170 def _devise_code(self, obj):
1171 return obj.devise.code
1172 _devise_code.short_description = "Code de la devise"
1173
1174 def _devise_nom(self, obj):
1175 return obj.devise.nom
1176 _devise_nom.short_description = "Nom de la devise"
1177
1178
1179 class ImplantationProxyAdmin(BaseAdmin):
1180 list_display = ('nom', '_organigramme')
1181 actions = None
1182
1183 def __init__(self, *args, **kwargs):
1184 super(ImplantationProxyAdmin, self).__init__(*args, **kwargs)
1185 self.list_display_links = (None, )
1186
1187 def has_add_permission(self, obj):
1188 return False
1189
1190 def has_change_permission(self, request, obj=None):
1191 return in_drh_or_admin(request.user)
1192
1193 def _organigramme(self, obj):
1194 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1195 reverse('rho_implantation', args=(obj.id,))
1196 )
1197 _organigramme.allow_tags = True
1198 _organigramme.short_description = "Organigramme"
1199
1200
1201 class RegionProxyAdmin(BaseAdmin):
1202 list_display = ('nom', '_organigramme')
1203 actions = None
1204
1205 def __init__(self, *args, **kwargs):
1206 super(RegionProxyAdmin, self).__init__(*args, **kwargs)
1207 self.list_display_links = (None, )
1208
1209 def has_add_permission(self, obj):
1210 return False
1211
1212 def has_change_permission(self, request, obj=None):
1213 return in_drh_or_admin(request.user)
1214
1215 def _organigramme(self, obj):
1216 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1217 reverse('rho_region', args=(obj.id,))
1218 )
1219 _organigramme.allow_tags = True
1220 _organigramme.short_description = "Organigramme"
1221
1222
1223 admin.site.register(rh.Classement, ClassementAdmin)
1224 admin.site.register(rh.Devise, DeviseAdmin)
1225 admin.site.register(rh.Dossier, DossierAdmin)
1226 admin.site.register(EmployeProxy, EmployeProxyAdmin)
1227 admin.site.register(ServiceProxy, ServiceProxyAdmin)
1228 admin.site.register(rh.Employe, EmployeAdmin)
1229 admin.site.register(rh.CategorieEmploi, CategorieEmploiAdmin)
1230 admin.site.register(rh.FamilleProfessionnelle)
1231 admin.site.register(rh.OrganismeBstg, OrganismeBstgAdmin)
1232 admin.site.register(rh.Poste, PosteAdmin)
1233 admin.site.register(
1234 rh.ResponsableImplantationProxy, ResponsableImplantationAdmin
1235 )
1236 admin.site.register(rh.Service, ServiceAdmin)
1237 admin.site.register(rh.Statut, StatutAdmin)
1238 admin.site.register(rh.TauxChange, TauxChangeAdmin)
1239 admin.site.register(rh.TypeContrat, TypeContratAdmin)
1240 admin.site.register(rh.TypePoste, TypePosteAdmin)
1241 admin.site.register(rh.TypeRemuneration, TypeRemunerationAdmin)
1242 admin.site.register(rh.TypeRevalorisation, TypeRevalorisationAdmin)
1243 admin.site.register(rh.ValeurPoint, ValeurPointAdmin)
1244 admin.site.register(ImplantationProxy, ImplantationProxyAdmin)
1245 admin.site.register(RegionProxy, RegionProxyAdmin)