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