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