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