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