ergo UI : uniformiastion colonnes des changelists
[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 inlines = (DossierPieceInline, ContratInline,
353 RemunerationInline,
354 DossierCommentaireInline,
355 )
356 fieldsets = AUFMetadataAdminMixin.fieldsets + (
357 (None, {
358 'fields': (
359 'employe',
360 'poste',
361 'principal',
362 'statut',
363 'organisme_bstg',)}),
364 ('Recrutement', {
365 'fields': (
366 'statut_residence',
367 'remplacement',
368 'remplacement_de', )}),
369 ('Rémunération', {
370 'fields': (
371 'classement',
372 ('regime_travail', 'regime_travail_nb_heure_semaine'),)}),
373 ('Occupation du Poste par cet Employe', {
374 'fields': (('date_debut', 'date_fin'), )}
375 ),
376 )
377 form = make_ajax_form(rh.Dossier, {
378 'employe': 'employes',
379 'poste': 'postes',
380 'remplacement_de': 'dossiers',
381 }, superclass=DossierForm)
382
383 def lookup_allowed(self, key, value):
384 if key in (
385 'employe__nom__istartswith',
386 'poste__implantation__region__id__exact',
387 'poste__implantation__id__exact',
388 'poste__type_poste__id__exact',
389 'poste__type_poste__categorie_emploi__id__exact',
390 'rh_contrats__type_contrat__id__exact',
391 'principal__exact',
392 'principal__isnull',
393 ):
394 return True
395
396 def _id(self, obj):
397 return obj.id
398 _id.short_description = u"#"
399 _id.admin_order_field = "id"
400
401 def _apercu(self, d):
402 apercu_link = u"""<a title="Aperçu du dossier"
403 onclick="return showAddAnotherPopup(this);"
404 href='%s'>
405 <img src="%simg/dossier-apercu.png" />
406 </a>""" % \
407 (reverse('dossier_apercu', args=(d.id,)),
408 settings.STATIC_URL,
409 )
410 return apercu_link
411 _apercu.allow_tags = True
412 _apercu.short_description = u""
413
414 def _nom(self, obj):
415 return "Dossier"
416 _nom.allow_tags = True
417 _nom.short_description = u"Dossier"
418
419 def _employe(self, obj):
420 employe = obj.employe
421 view_link = reverse('employe_apercu', args=(employe.id,))
422 edit_link = reverse('admin:rh_employe_change', args=(employe.id,))
423
424 style = ""
425 view = u"""<a href="%s"
426 title="Aperçu l'employé"
427 onclick="return showAddAnotherPopup(this);">
428 <img src="%simg/employe-apercu.png" />
429 </a>""" % (view_link, settings.STATIC_URL,)
430 return u"""%s<a href='%s' style="%s;">%s</a>""" % \
431 (view, edit_link, style, employe)
432 _employe.allow_tags = True
433 _employe.short_description = u"Employé"
434 _employe.admin_order_field = "employe__nom"
435
436 def _poste(self, dossier):
437 link = u"""<a title="Aperçu du poste"
438 onclick="return showAddAnotherPopup(this);"
439 href='%s'><img src="%simg/poste-apercu.png" />
440 </a>
441 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
442 (reverse('poste_apercu', args=(dossier.poste.id,)),
443 settings.STATIC_URL,
444 reverse('admin:rh_poste_change', args=(dossier.poste.id,)),
445 dossier.poste.nom,
446 dossier.poste.id,
447 )
448 return link
449 _poste.allow_tags = True
450 _poste.short_description = u'Poste'
451 _poste.admin_order_field = 'poste__nom'
452
453 def _region(self, obj):
454 return obj.poste.implantation.region.code
455 _region.short_description = u"Région"
456 _region.admin_order_field = 'poste__implantation__region__code'
457
458 def _implantation(self, obj):
459 return obj.poste.implantation.nom
460 _implantation.short_description = u"Implantation"
461 _implantation.admin_order_field = 'poste__implantation__nom'
462
463 def _date_debut(self, obj):
464 return date(obj.date_debut)
465
466 _date_debut.short_description = u'Début'
467 _date_debut.admin_order_field = 'date_debut'
468
469 def _date_fin(self, obj):
470 return date(obj.date_fin)
471 _date_fin.short_description = u'Fin'
472 _date_fin.admin_order_field = 'date_fin'
473
474 def _date_modification(self, obj):
475 return date(obj.date_modification) \
476 if obj.date_modification is not None else "(aucune)"
477 _date_modification.short_description = u'date modification'
478 _date_modification.admin_order_field = 'date_modification'
479
480 def _dae(self, d):
481 apercu_link = ""
482 dossiers_dae = d.dossiers_dae.all()
483 if len(dossiers_dae) > 0:
484 dossier_dae = dossiers_dae[0]
485 apercu_link = u"""<a title="Aperçu du dossier"
486 onclick="return showAddAnotherPopup(this);"
487 href='%s'>
488 <img src="%simg/loupe.png" />
489 </a>""" % \
490 (reverse('embauche_consulter', args=(dossier_dae.id,)),
491 settings.STATIC_URL,
492 )
493 return apercu_link
494 _dae.allow_tags = True
495 _dae.short_description = u"DAE"
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 '_region',
524 '_implantation',
525 'date_entree',
526 '_date_modification',
527 )
528 list_display_links = ('_nom',)
529 list_filter = (
530 'rh_dossiers__poste__implantation__region',
531 'rh_dossiers__poste__implantation',
532 'nb_postes',
533 )
534 inlines = (AyantDroitInline,
535 DossierROInline,
536 EmployePieceInline,
537 EmployeCommentaireInline)
538 fieldsets = AUFMetadataAdminMixin.fieldsets + (
539 ('Identification', {
540 'fields': (
541 ('nom', 'prenom'),
542 ('nom_affichage', 'genre'),
543 'nationalite',
544 'date_naissance',
545 )}
546 ),
547 ('Informations personnelles', {
548 'fields': ('situation_famille', 'date_entree', )}
549 ),
550 ('Coordonnées personnelles', {
551 'fields': (
552 ('tel_domicile', 'tel_cellulaire'),
553 ('adresse', 'ville'),
554 ('code_postal', 'province'),
555 'pays',
556 'courriel_perso'
557 )}
558 ),
559 )
560
561 def _id(self, obj):
562 return obj.id
563 _id.short_description = u"#"
564 _id.admin_order_field = "id"
565
566 def _apercu(self, obj):
567 return u"""<a title="Aperçu de l'employé"
568 onclick="return showAddAnotherPopup(this);"
569 href='%s'>
570 <img src="%simg/employe-apercu.png" />
571 </a>""" % \
572 (reverse('employe_apercu', args=(obj.id,)), settings.STATIC_URL)
573 _apercu.allow_tags = True
574 _apercu.short_description = u""
575
576 def _nom(self, obj):
577 edit_link = reverse('admin:rh_employe_change', args=(obj.id,))
578 return u"""<a href='%s'><strong>%s</strong></a>""" % \
579 (edit_link, "%s %s" % (obj.nom.upper(), obj.prenom))
580 _nom.allow_tags = True
581 _nom.short_description = u"Employé"
582 _nom.admin_order_field = "nom"
583
584 def _region(self, obj):
585 try:
586 d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
587 region = d.poste.implantation.region.code
588 except:
589 region = None
590 return region
591 _region.short_description = u"Région"
592
593 def _implantation(self, obj):
594 try:
595 d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
596 implantation = d.poste.implantation.nom
597 except:
598 implantation = None
599 return implantation
600 _implantation.short_description = u"Implantation"
601
602 def _dossiers_postes(self, obj):
603 l = []
604 for d in obj.rh_dossiers.all().order_by('-date_debut'):
605 actif = True
606 link_style = u''
607 list_style = u''
608 if d.date_fin is not None and d.date_fin < datetime.date.today():
609 actif = False
610 link_style = u' style="color:#666;"'
611 list_style = u' style="color:grey;"'
612
613 dossier = u"""<a title="Aperçu du dossier"
614 href="%s"
615 onclick="return showAddAnotherPopup(this);"
616 title="Aperçu du dossier">
617 <img src="%simg/dossier-apercu.png" />
618 </a>
619 <a href="%s"%s>Dossier</a>
620 &nbsp;""" % \
621 (reverse('dossier_apercu', args=(d.id,)),
622 settings.STATIC_URL,
623 reverse('admin:rh_dossier_change', args=(d.id,)),
624 link_style,)
625
626 poste = u"""<a title="Aperçu du poste"
627 href="%s"
628 onclick="return showAddAnotherPopup(this);"
629 title="Aperçu du poste">
630 <img src="%simg/poste-apercu.png" />
631 </a>
632 <a href="%s"%s>%s [%d]</a>
633 &nbsp;""" % \
634 (reverse('poste_apercu', args=(d.poste.id,)),
635 settings.STATIC_URL,
636 reverse('admin:rh_poste_change', args=(d.poste.id,)),
637 link_style,
638 d.poste.nom,
639 d.poste.id)
640
641 link = u"""<li%s>%s %s</li>""" % \
642 (list_style, dossier, poste)
643
644 l.append(link)
645 return "<ul>%s</ul>" % "\n".join(l)
646 _dossiers_postes.allow_tags = True
647 _dossiers_postes.short_description = u"Dossiers et postes"
648
649 def _date_modification(self, obj):
650 return date(obj.date_modification) \
651 if obj.date_modification is not None else "(aucune)"
652 _date_modification.short_description = u'date modification'
653 _date_modification.admin_order_field = 'date_modification'
654
655 def queryset(self, request):
656 qs = super(EmployeAdmin, self).queryset(request)
657 return qs.select_related(depth=1).order_by('nom')
658
659 def save_formset(self, request, form, formset, change):
660 instances = formset.save(commit=False)
661 for instance in instances:
662 if instance.__class__ == rh.EmployeCommentaire:
663 instance.owner = request.user
664 instance.date_creation = datetime.datetime.now()
665 instance.save()
666
667
668 class EmployeProxyAdmin(EmployeAdmin):
669 list_display = ('_id', '_apercu', '_nom', '_organigramme')
670 actions = None
671
672 def __init__(self, *args, **kwargs):
673 super(EmployeProxyAdmin, self).__init__(*args, **kwargs)
674 self.list_display_links = (None, )
675
676 def has_add_permission(self, obj):
677 return False
678
679 def _organigramme(self, obj):
680 l = []
681 for d in rh.Dossier.objects.filter(
682 Q(date_fin__gt=datetime.date.today()) | Q(date_fin=None),
683 Q(date_debut__lt=datetime.date.today()) | Q(date_debut=None),
684 employe=obj.id
685 ):
686 organigramme = \
687 u'Organigramme, niveau: ' \
688 u'<input type="text" id="level_%s" ' \
689 u'style="width:30px;height:15px;" /> ' \
690 u'<input type="button" value="Générer" ' \
691 u"""onclick="window.location='%s' + """ \
692 u"""document.getElementById('level_%s').value" />""" % (
693 d.poste.id,
694 reverse('rho_employe_sans_niveau', args=(d.poste.id,)),
695 d.poste.id
696 )
697 link = u"""<li>%s - [%s] %s : %s</li>""" % (
698 d.date_debut.year,
699 d.poste.id,
700 d.poste.nom,
701 organigramme
702 )
703 l.append(link)
704 return "<ul>%s</ul>" % "\n".join(l)
705
706 _organigramme.allow_tags = True
707 _organigramme.short_description = "Organigramme"
708
709
710 class CategorieEmploiAdmin(AUFMetadataAdminMixin, BaseAdmin):
711 list_display = ('nom', '_date_modification', 'user_modification', )
712 inlines = (TypePosteInline,)
713 fieldsets = AUFMetadataAdminMixin.fieldsets + (
714 (None, {'fields': ('nom', )}),)
715
716 def _date_modification(self, obj):
717 return date(obj.date_modification) \
718 if obj.date_modification is not None else "(aucune)"
719 _date_modification.short_description = u'date modification'
720 _date_modification.admin_order_field = 'date_modification'
721
722
723 class OrganismeBstgAdmin(AUFMetadataAdminMixin, BaseAdmin):
724 search_fields = ('nom',)
725 list_display = (
726 'nom',
727 'type',
728 'pays',
729 '_date_modification',
730 'user_modification',
731 )
732 list_filter = ('type', )
733 inlines = (DossierROInline,)
734 fieldsets = AUFMetadataAdminMixin.fieldsets + (
735 (None, {'fields': ('nom', 'type', 'pays',)}),
736 )
737
738 def _date_modification(self, obj):
739 return date(obj.date_modification) \
740 if obj.date_modification is not None else "(aucune)"
741 _date_modification.short_description = u'date modification'
742 _date_modification.admin_order_field = 'date_modification'
743
744
745 class PosteAdmin(DateRangeMixin, AUFMetadataAdminMixin,
746 ProtectRegionMixin, BaseAdmin, AjaxSelect):
747 form = make_ajax_form(rh.Poste, {
748 'implantation': 'implantations',
749 'type_poste': 'typepostes',
750 'responsable': 'postes',
751 'valeur_point_min': 'valeurpoints',
752 'valeur_point_max': 'valeurpoints',
753 })
754 alphabet_filter = 'nom'
755 search_fields = (
756 'id',
757 'nom',
758 'implantation__nom',
759 'implantation__region__code',
760 'implantation__region__nom',
761 'rh_dossiers__employe__id',
762 'rh_dossiers__employe__nom',
763 'rh_dossiers__employe__prenom',
764 )
765 list_display = (
766 '_id',
767 '_apercu',
768 '_nom',
769 '_occupe_par',
770 '_region',
771 '_implantation',
772 '_service',
773 '_responsable',
774 '_date_debut',
775 '_date_fin',
776 '_date_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 lookup_allowed(self, key, value):
844 return key in (
845 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
846 'date_fin__isnull', 'implantation__region__id__exact',
847 'implantation__id__exact', 'type_poste__id__exact',
848 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
849 'service__isnull', 'vacant__exact', 'vacant__isnull',
850 ) or super(PosteAdmin, self).lookup_allowed(key, value)
851
852 def _id(self, obj):
853 return "%s" % obj.id
854 _id.short_description = '#'
855 _id.admin_order_field = 'id'
856
857 def _apercu(self, poste):
858 view_link = u"""<a onclick="return showAddAnotherPopup(this);"
859 title="Aperçu du poste"
860 href='%s'>
861 <img src="%simg/poste-apercu.png" />
862 </a>""" % \
863 (reverse('poste_apercu', args=(poste.id,)),
864 settings.STATIC_URL,)
865 return view_link
866 _apercu.allow_tags = True
867 _apercu.short_description = ''
868
869 def _nom(self, poste):
870 return """<a href="%s">%s</a>""" % \
871 (reverse('admin:rh_poste_change', args=(poste.id,)),
872 poste.nom)
873 _nom.allow_tags = True
874 _nom.short_description = u'Poste'
875 _nom.admin_order_field = 'nom'
876
877 def _occupe_par(self, obj):
878 """Formatte la méthode Poste.occupe_par() pour l'admin"""
879 output = u"Vacant"
880 if obj.date_fin is not None and obj.date_fin < datetime.date.today():
881 return u"s/o"
882 employes = obj.occupe_par()
883 if employes:
884 l = []
885 for e in employes:
886 link = u"""<a href='%s'
887 title='Aperçu de l\'employé'
888 onclick='return showAddAnotherPopup(this)'>
889 <img src='%simg/employe-apercu.png' />
890 </a>
891 <a href='%s'>%s</a>""" % \
892 (reverse('employe_apercu', args=(e.id,)),
893 settings.STATIC_URL,
894 reverse('admin:rh_employe_change', args=(e.id,)),
895 e)
896 l.append(link)
897 output = "\n<br />".join(l)
898 return output
899 _occupe_par.allow_tags = True
900 _occupe_par.short_description = "Occupé par"
901
902 def _region(self, poste):
903 return poste.implantation.region.code
904 _region.short_description = 'Région'
905 _region.admin_order_field = 'implantation__region__code'
906
907 def _implantation(self, poste):
908 return poste.implantation.nom
909 _implantation.short_description = 'Implantation'
910 _implantation.admin_order_field = 'implantation'
911
912 def _service(self, obj):
913 if obj.service.supprime:
914 return """<span style="color:red">%s</span>""" % obj.service
915 else:
916 return obj.service
917 _service.short_description = 'Service'
918 _service.allow_tags = True
919
920 def _responsable(self, obj):
921 try:
922 responsable = u"""<a href="%s"
923 onclick="return showAddAnotherPopup(this)">
924 <img src="%simg/poste-apercu.png"
925 title="Aperçu du poste" />
926 </a>
927 <a href="%s">%s [%d]</a>
928 <br />""" % \
929 (reverse('poste_apercu', args=(obj.responsable.id,)),
930 settings.STATIC_URL,
931 reverse('admin:rh_poste_change', args=(obj.responsable.id,)),
932 obj.responsable.nom,
933 obj.responsable.id)
934 except:
935 responsable = ''
936
937 try:
938 dossier = obj.responsable.rh_dossiers.all().order_by('-date_debut')[0]
939 employe_id = dossier.employe.id
940 employe_html = u"""<br />
941 <a href="%s"
942 onclick="return showAddAnotherPopup(this)">
943 <img src="%simg/employe-apercu.png"
944 title="Aperçu de l'employé">
945 </a>
946 <a href="%s">%s</a>""" % \
947 (reverse('employe_apercu', args=(employe_id,)),
948 settings.STATIC_URL,
949 reverse('admin:rh_employe_change', args=(employe_id,)),
950 dossier.employe)
951 except:
952 employe_html = ""
953
954 return "%s %s" % (responsable, employe_html)
955 _responsable.short_description = 'Responsable'
956 _responsable.allow_tags = True
957
958 def _date_debut(self, obj):
959 return date_format(obj.date_debut)
960 _date_debut.short_description = u'Début'
961 _date_debut.admin_order_field = 'date_debut'
962
963 def _date_fin(self, obj):
964 return date_format(obj.date_fin)
965 _date_fin.short_description = u'Fin'
966 _date_fin.admin_order_field = 'date_fin'
967
968 def _date_modification(self, obj):
969 return date(obj.date_modification)
970 _date_modification.short_description = u'date modification'
971 _date_modification.admin_order_field = 'date_modification'
972
973 def _dae(self, poste):
974 apercu_link = ""
975 postes_dae = poste.postes_dae.all()
976 if len(postes_dae) > 0:
977 poste_dae = postes_dae[0]
978 apercu_link = \
979 u'<a title="Aperçu du dossier" href="%s" ' \
980 u'onclick="return showAddAnotherPopup(this);">' \
981 u'<img src="%simg/loupe.png" /></a>' % (reverse(
982 'poste_consulter', args=("dae-%s" % poste_dae.id,)
983 ), settings.STATIC_URL)
984 return apercu_link
985 _dae.allow_tags = True
986 _dae.short_description = u"DAE"
987
988 def save_formset(self, request, form, formset, change):
989 instances = formset.save(commit=False)
990 for instance in instances:
991 if instance.__class__ == rh.PosteCommentaire:
992 instance.owner = request.user
993 instance.date_creation = datetime.datetime.now()
994 instance.save()
995 formset.save_m2m()
996
997
998 class ResponsableInline(admin.TabularInline):
999 model = rh.ResponsableImplantation
1000 extra = 0
1001 fk_name = "implantation"
1002 form = ResponsableInlineForm
1003
1004
1005 class ResponsableImplantationAdmin(BaseAdmin):
1006 actions = None
1007 fields = ('nom', )
1008 inlines = (ResponsableInline, )
1009 list_filter = ('region', 'statut', )
1010 list_display = ('_region', '_nom', 'statut', '_responsable', )
1011 list_display_links = ('_nom',)
1012 readonly_fields = ('nom', )
1013 search_fields = (
1014 'nom',
1015 'responsable__employe__id',
1016 'responsable__employe__nom',
1017 'responsable__employe__prenom',
1018 )
1019 ordering = ('nom',)
1020
1021 def _region(self, obj):
1022 return obj.region.code
1023 _region.short_description = u"Région"
1024 _region.admin_order_field = 'region__code'
1025
1026 def _nom(self, obj):
1027 return obj.nom
1028 _nom.short_description = u"Implantation"
1029 _nom.admin_order_field = 'nom'
1030
1031 def _responsable(self, obj):
1032 try:
1033 employe = obj.responsable.employe
1034 dossiers = employe.dossiers_encours()
1035 if len(dossiers) == 0:
1036 return u"<span style='color: red;'>%s %s </span>" % (
1037 employe, u"sans dossier actif")
1038 else:
1039 return employe
1040 except Exception:
1041 if obj.statut in (1, 2): # ouverte, ouverture imminente
1042 css = "style='color: red;'"
1043 else:
1044 css = ""
1045 return u"<span %s>Pas de responsable</span>" % css
1046 _responsable.allow_tags = True
1047 _responsable.short_description = u"Responsable"
1048 _responsable.admin_order_field = 'responsable__employe__nom'
1049
1050 def has_add_permission(self, request=None):
1051 return False
1052
1053 def has_change_permission(self, request, obj=None):
1054 return in_drh_or_admin(request.user)
1055
1056 def has_delete_permission(self, request, obj=None):
1057 return False
1058
1059
1060 class ServiceAdmin(AUFMetadataAdminMixin, BaseAdmin, ArchiveMixin):
1061 list_display = (
1062 'nom',
1063 '_archive',
1064 '_date_modification',
1065 'user_modification',
1066 )
1067 list_filter = ('archive', )
1068 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1069 (None, {
1070 'fields': ('nom', 'archive', ),
1071 }),
1072 )
1073
1074 def _date_modification(self, obj):
1075 return date(obj.date_modification) \
1076 if obj.date_modification is not None else "(aucune)"
1077 _date_modification.short_description = u'date modification'
1078 _date_modification.admin_order_field = 'date_modification'
1079
1080
1081 class ServiceProxyAdmin(ServiceAdmin):
1082 list_display = ('nom', '_organigramme', '_archive', )
1083 actions = None
1084
1085 def __init__(self, *args, **kwargs):
1086 super(ServiceProxyAdmin, self).__init__(*args, **kwargs)
1087 self.list_display_links = (None, )
1088
1089 def queryset(self, request):
1090 return super(ServiceProxyAdmin, self).queryset(request) \
1091 .annotate(num_postes=Count('rh_postes')) \
1092 .filter(num_postes__gt=0)
1093
1094 def has_add_permission(self, obj):
1095 return False
1096
1097 def has_change_permission(self, request, obj=None):
1098 return in_drh_or_admin(request.user)
1099
1100 def _organigramme(self, obj):
1101 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1102 (reverse('rho_service', args=(obj.id,)))
1103 _organigramme.allow_tags = True
1104 _organigramme.short_description = "Organigramme"
1105
1106
1107 class StatutAdmin(AUFMetadataAdminMixin, BaseAdmin):
1108 list_display = ('code', 'nom', '_date_modification', 'user_modification', )
1109 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1110 (None, {
1111 'fields': ('code', 'nom', ),
1112 }),
1113 )
1114
1115 def _date_modification(self, obj):
1116 return date(obj.date_modification) \
1117 if obj.date_modification is not None else "(aucune)"
1118 _date_modification.short_description = u'date modification'
1119 _date_modification.admin_order_field = 'date_modification'
1120
1121
1122 class TauxChangeAdmin(BaseAdmin):
1123 list_display = (
1124 'taux',
1125 'devise',
1126 'annee',
1127 '_date_modification',
1128 'user_modification',
1129 )
1130 list_filter = ('devise', )
1131 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1132 (None, {
1133 'fields': ('taux', 'devise', 'annee', ),
1134 }),
1135 )
1136
1137 def _date_modification(self, obj):
1138 return date(obj.date_modification) \
1139 if obj.date_modification is not None else "(aucune)"
1140 _date_modification.short_description = u'date modification'
1141 _date_modification.admin_order_field = 'date_modification'
1142
1143
1144 class TypeContratAdmin(BaseAdmin):
1145 list_display = (
1146 'nom',
1147 'nom_long',
1148 '_date_modification',
1149 'user_modification',
1150 )
1151 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1152 (None, {
1153 'fields': ('nom', 'nom_long', ),
1154 }),
1155 )
1156
1157 def _date_modification(self, obj):
1158 return date(obj.date_modification) \
1159 if obj.date_modification is not None else "(aucune)"
1160 _date_modification.short_description = u'date modification'
1161 _date_modification.admin_order_field = 'date_modification'
1162
1163
1164 class TypePosteAdmin(AUFMetadataAdminMixin, BaseAdmin):
1165 search_fields = ('nom', 'nom_feminin', )
1166 list_display = (
1167 'nom',
1168 'categorie_emploi',
1169 '_date_modification',
1170 'user_modification',
1171 )
1172 list_filter = ('categorie_emploi', 'famille_professionnelle')
1173 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1174 (None, {
1175 'fields': (
1176 'nom',
1177 'nom_feminin',
1178 'is_responsable',
1179 'categorie_emploi',
1180 'famille_professionnelle',
1181 )}
1182 ),
1183 )
1184
1185 def _date_modification(self, obj):
1186 return date(obj.date_modification) \
1187 if obj.date_modification is not None else "(aucune)"
1188 _date_modification.short_description = u'date modification'
1189 _date_modification.admin_order_field = 'date_modification'
1190
1191
1192 class TypeRemunerationAdmin(AUFMetadataAdminMixin, BaseAdmin,
1193 ArchiveMixin):
1194 list_display = (
1195 'nom',
1196 'type_paiement',
1197 'nature_remuneration',
1198 '_archive',
1199 '_date_modification',
1200 'user_modification',)
1201 list_filter = ('archive', )
1202 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1203 (None, {'fields': ('nom', 'type_paiement', 'nature_remuneration',
1204 'archive')}),
1205 )
1206
1207 def _date_modification(self, obj):
1208 return date(obj.date_modification) \
1209 if obj.date_modification is not None else "(aucune)"
1210 _date_modification.short_description = u'date modification'
1211 _date_modification.admin_order_field = 'date_modification'
1212
1213
1214 class TypeRevalorisationAdmin(AUFMetadataAdminMixin, BaseAdmin):
1215 list_display = ('nom', '_date_modification', 'user_modification', )
1216 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1217 (None, {'fields': ('nom', )}),
1218 )
1219
1220 def _date_modification(self, obj):
1221 return date(obj.date_modification) \
1222 if obj.date_modification is not None else "(aucune)"
1223 _date_modification.short_description = u'date modification'
1224 _date_modification.admin_order_field = 'date_modification'
1225
1226
1227 class ValeurPointAdmin(AUFMetadataAdminMixin, BaseAdmin):
1228 list_display = (
1229 '_devise_code',
1230 '_devise_nom',
1231 'annee',
1232 'implantation',
1233 'valeur',
1234 '_date_modification',
1235 'user_modification',
1236 )
1237 list_filter = ('annee', 'devise', 'implantation__region', )
1238 fieldsets = AUFMetadataAdminMixin.fieldsets + (
1239 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee', )}),
1240 )
1241
1242 def _date_modification(self, obj):
1243 return date(obj.date_modification) \
1244 if obj.date_modification is not None else "(aucune)"
1245 _date_modification.short_description = u'date modification'
1246 _date_modification.admin_order_field = 'date_modification'
1247
1248 def _devise_code(self, obj):
1249 return obj.devise.code
1250 _devise_code.short_description = "Code de la devise"
1251
1252 def _devise_nom(self, obj):
1253 return obj.devise.nom
1254 _devise_nom.short_description = "Nom de la devise"
1255
1256
1257 class ImplantationProxyAdmin(BaseAdmin):
1258 list_display = ('nom', '_organigramme')
1259 actions = None
1260
1261 def __init__(self, *args, **kwargs):
1262 super(ImplantationProxyAdmin, self).__init__(*args, **kwargs)
1263 self.list_display_links = (None, )
1264
1265 def has_add_permission(self, obj):
1266 return False
1267
1268 def has_change_permission(self, request, obj=None):
1269 return in_drh_or_admin(request.user)
1270
1271 def _organigramme(self, obj):
1272 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1273 reverse('rho_implantation', args=(obj.id,))
1274 )
1275 _organigramme.allow_tags = True
1276 _organigramme.short_description = "Organigramme"
1277
1278
1279 class RegionProxyAdmin(BaseAdmin):
1280 list_display = ('nom', '_organigramme')
1281 actions = None
1282
1283 def __init__(self, *args, **kwargs):
1284 super(RegionProxyAdmin, self).__init__(*args, **kwargs)
1285 self.list_display_links = (None, )
1286
1287 def has_add_permission(self, obj):
1288 return False
1289
1290 def has_change_permission(self, request, obj=None):
1291 return in_drh_or_admin(request.user)
1292
1293 def _organigramme(self, obj):
1294 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1295 reverse('rho_region', args=(obj.id,))
1296 )
1297 _organigramme.allow_tags = True
1298 _organigramme.short_description = "Organigramme"
1299
1300
1301 admin.site.register(rh.Classement, ClassementAdmin)
1302 admin.site.register(rh.Devise, DeviseAdmin)
1303 admin.site.register(rh.Dossier, DossierAdmin)
1304 admin.site.register(EmployeProxy, EmployeProxyAdmin)
1305 admin.site.register(ServiceProxy, ServiceProxyAdmin)
1306 admin.site.register(rh.Employe, EmployeAdmin)
1307 admin.site.register(rh.CategorieEmploi, CategorieEmploiAdmin)
1308 admin.site.register(rh.FamilleProfessionnelle)
1309 admin.site.register(rh.OrganismeBstg, OrganismeBstgAdmin)
1310 admin.site.register(rh.Poste, PosteAdmin)
1311 admin.site.register(
1312 rh.ResponsableImplantationProxy, ResponsableImplantationAdmin
1313 )
1314 admin.site.register(rh.Service, ServiceAdmin)
1315 admin.site.register(rh.Statut, StatutAdmin)
1316 admin.site.register(rh.TauxChange, TauxChangeAdmin)
1317 admin.site.register(rh.TypeContrat, TypeContratAdmin)
1318 admin.site.register(rh.TypePoste, TypePosteAdmin)
1319 admin.site.register(rh.TypeRemuneration, TypeRemunerationAdmin)
1320 admin.site.register(rh.TypeRevalorisation, TypeRevalorisationAdmin)
1321 admin.site.register(rh.ValeurPoint, ValeurPointAdmin)
1322 admin.site.register(ImplantationProxy, ImplantationProxyAdmin)
1323 admin.site.register(RegionProxy, RegionProxyAdmin)