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