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