1 # -*- encoding: utf-8 -*-
6 from ajax_select
import make_ajax_form
7 from auf
.django
.references
import models
as ref
8 from django
.core
.mail
import send_mail
, EmailMessage
9 from django
.contrib
.auth
.admin
import UserAdmin
10 from django
.contrib
.auth
.models
import User
11 from django
import forms
12 from django
.core
.urlresolvers
import reverse
13 from django
.contrib
import admin
14 from django
.contrib
.contenttypes
.models
import ContentType
15 from django
.conf
import settings
16 from django
.db
.models
import Q
, Count
17 from django
.template
.defaultfilters
import date
18 from django
.template
import Context
, loader
19 from django
.utils
.formats
import date_format
21 from project
import groups
22 from project
.decorators
import in_drh_or_admin
23 from project
.rh
import models
as rh
24 from project
.permissions
import user_gere_obj_de_sa_region
, \
27 user_can_change_obj
, \
30 from project
.rh
.forms
import ContratForm
, AyantDroitForm
, EmployeAdminForm
, \
31 AjaxSelect
, DossierForm
, ResponsableInlineForm
, \
32 ClassementHistoriqueForm
34 from project
.rh
.change_list
import ChangeList
37 def listing_par_defaut(model
, request
):
39 Teste si la requete provient de la même page.
41 if not 'HTTP_REFERER' in request
.META
.keys():
43 referer
= request
.META
['HTTP_REFERER']
44 referer
= "/".join(referer
.split('/')[3:])
45 referer
= "/%s" % referer
.split('?')[0]
46 change_list_view
= 'admin:%s_%s_changelist' % (
47 model
._meta
.app_label
,
48 model
.__name__
.lower(),)
49 return referer
!= reverse(change_list_view
)
52 class BaseAdmin(admin
.ModelAdmin
):
56 'css/admin_custom.css',
57 'jquery-autocomplete/jquery.autocomplete.css',
60 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
61 'jquery-autocomplete/jquery.autocomplete.min.js',
65 # Admin pour reversion
67 class ArchivableAdmin(admin
.ModelAdmin
):
69 Admin pour les modèles archivables
71 list_filter
= ('archive', )
73 def queryset(self
, request
):
74 return self
.model
.avec_archives
.all()
76 def _archive(self
, obj
):
81 _archive
.short_description
= u
'Archivé'
82 _archive
.admin_order_field
= 'archive'
85 class RegionProxy(ref
.Region
):
86 """ Proxy utilisé pour les organigrammes par région """
90 verbose_name
= u
"Organigramme par région"
91 verbose_name_plural
= u
"Organigramme par région"
94 class ImplantationProxy(ref
.Implantation
):
95 """ Proxy utilisé pour les organigrammes par implantation """
99 verbose_name
= u
"Organigramme par implantations"
100 verbose_name_plural
= u
"Organigramme par implantations"
103 class ServiceProxy(rh
.Service
):
104 """ Proxy utilisé pour les organigrammes par service """
109 verbose_name
= u
"Organigramme par services"
110 verbose_name_plural
= u
"Organigramme par services"
113 class EmployeProxy(rh
.Employe
):
114 """ Proxy utilisé pour les organigrammes des employés """
118 verbose_name
= u
"Organigramme des employés"
119 verbose_name_plural
= u
"Organigramme des employés"
122 class DateRangeMixin(object):
123 prefixe_recherche_temporelle
= ""
125 def get_changelist(self
, request
, **kwargs
):
127 On filtre par défaut sur les items 'actifs'.
128 Le changelist plug le filtrage temporel.
130 if listing_par_defaut(self
.model
, request
):
131 params
= request
.GET
.copy()
132 params
.update({'statut': 'Actif'})
137 # Override of the InlineModelAdmin to support the link in the tabular inline
138 class LinkedInline(admin
.options
.InlineModelAdmin
):
139 template
= "admin/linked.html"
140 admin_model_path
= None
142 def __init__(self
, *args
):
143 super(LinkedInline
, self
).__init__(*args
)
144 if self
.admin_model_path
is None:
145 self
.admin_model_path
= self
.model
.__name__
.lower()
148 class ProtectRegionMixin(object):
150 def changelist_view(self
, request
, extra_context
=None):
152 On filtre par défaut sur la ZA du user connecté
154 if listing_par_defaut(self
.model
, request
):
155 if user_gere_obj_de_sa_region(request
.user
):
156 params
= request
.GET
.copy()
157 zones
= groups
.get_zones_from_user(request
.user
)
158 prefix_za
= "%s__in" % self
.model
.prefix_implantation
159 params
.update({prefix_za
: zones
})
161 return super(ProtectRegionMixin
, self
) \
162 .changelist_view(request
, extra_context
)
164 def queryset(self
, request
):
165 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
167 if in_drh_or_admin(request
.user
):
170 if user_gere_obj_de_sa_region(request
.user
):
171 zones
= groups
.get_zones_from_user(request
.user
)
172 qkey
= '%s__in' % self
.model
.prefix_implantation
173 q
= Q(**{qkey
: zones
})
174 qs
= qs
.filter(q
).distinct()
178 def has_add_permission(self
, request
):
179 return user_can_add_obj(request
.user
)
181 def has_change_permission(self
, request
, obj
=None):
183 return user_can_list_obj(request
.user
)
185 return user_can_change_obj(request
.user
, obj
)
187 def has_delete_permission(self
, request
, obj
=None):
188 return user_can_delete_obj(request
.user
, obj
) if obj
else True
191 class DerniereModificationAdmin(admin
.ModelAdmin
):
193 def queryset(self
, request
):
194 qs
= super(DerniereModificationAdmin
, self
).queryset(request
)
195 ct
= ContentType
.objects
.get_for_model(self
.model
)
196 db_table
= self
.model
._meta
.db_table
197 pk
= self
.model
._meta
.pk
.column
198 return qs
.extra(select
={
200 "SELECT action_time FROM django_admin_log "
201 "WHERE content_type_id = %d AND object_id = %s.%s "
202 "ORDER BY action_time DESC "
203 "LIMIT 1" % (ct
.id, db_table
, pk
),
207 "INNER JOIN django_admin_log l ON l.user_id = u.id "
208 "WHERE l.content_type_id = %d AND object_id = %s.%s "
209 "ORDER BY action_time DESC "
210 "LIMIT 1" % (ct
.id, db_table
, pk
),
213 def derniere_modification(self
, obj
):
215 if obj
.date_modification
:
216 text
+= obj
.date_modification
.strftime('%d-%m-%Y %H:%M')
217 if obj
.user_modification
:
218 text
+= ' par ' + obj
.user_modification
220 derniere_modification
.short_description
= u
'dernière modification'
221 derniere_modification
.admin_order_field
= 'date_modification'
226 class CommentaireInlineForm(forms
.ModelForm
):
228 def save(self
, commit
=True):
230 # Hack: reversion.VersionAdmin ne sauvegarde pas les champs qui ne
231 # sont pas explicitement dans le formulaire. Il plante cependant
232 # leur valeur dans `self.initial`. Ceci est un peu fragile. Si
233 # c'est possible, il serait plus approprié que Reversion se rende
234 # compte qu'il manque des champs.
235 instance
= super(CommentaireInlineForm
, self
).save(commit
=False)
236 if instance
.owner_id
is None and 'owner' in self
.initial
:
237 instance
.owner_id
= self
.initial
['owner']
238 if instance
.date_creation
is None and 'date_creation' in self
.initial
:
239 instance
.date_creation
= self
.initial
['date_creation']
246 class ReadOnlyInlineMixin(object):
248 def get_readonly_fields(self
, request
, obj
=None):
249 return [f
.name
for f
in self
.model
._meta
.fields
]
252 class AyantDroitInline(admin
.StackedInline
):
253 model
= rh
.AyantDroit
254 form
= AyantDroitForm
261 ('nom_affichage', 'genre'),
269 class AyantDroitCommentaireInline(admin
.TabularInline
):
270 readonly_fields
= ('owner',)
271 model
= rh
.AyantDroitCommentaire
273 form
= CommentaireInlineForm
276 class ContratInline(admin
.TabularInline
):
282 class DossierROInline(ReadOnlyInlineMixin
, LinkedInline
):
283 template
= "admin/rh/dossier/linked.html"
287 fields
= ('poste', 'date_debut', 'date_fin', )
289 def has_add_permission(self
, request
=None):
292 def has_change_permission(self
, request
, obj
=None):
295 def has_delete_permission(self
, request
, obj
=None):
299 class DossierClassementRecordInline(admin
.TabularInline
):
300 def queryset(self
, request
):
301 return (super(DossierClassementRecordInline
, self
)
302 .queryset(request
).order_by('-date_debut', '-id'))
303 model
= rh
.RHDossierClassementRecord
304 form
= ClassementHistoriqueForm
308 class DossierCommentaireInline(admin
.TabularInline
):
309 readonly_fields
= ('owner',)
310 model
= rh
.DossierCommentaire
312 form
= CommentaireInlineForm
315 class DossierPieceInline(admin
.TabularInline
):
316 model
= rh
.DossierPiece
320 class EmployeInline(admin
.TabularInline
):
324 class EmployeCommentaireInline(admin
.TabularInline
):
325 readonly_fields
= ('owner',)
326 model
= rh
.EmployeCommentaire
328 form
= CommentaireInlineForm
331 class EmployePieceInline(admin
.TabularInline
):
332 model
= rh
.EmployePiece
336 class PosteCommentaireInline(admin
.TabularInline
):
337 readonly_fields
= ('owner',)
338 model
= rh
.PosteCommentaire
340 form
= CommentaireInlineForm
343 class PosteFinancementInline(admin
.TabularInline
):
344 model
= rh
.PosteFinancement
347 class PostePieceInline(admin
.TabularInline
):
348 model
= rh
.PostePiece
351 class RemunerationInline(admin
.TabularInline
):
352 model
= rh
.Remuneration
356 class RemunerationROInline(ReadOnlyInlineMixin
, RemunerationInline
):
360 class TypePosteInline(admin
.TabularInline
):
364 class PosteComparaisonInline(admin
.TabularInline
):
365 model
= rh
.PosteComparaison
368 class ClassementAdmin(reversion
.VersionAdmin
,
370 DerniereModificationAdmin
,
372 ignore_duplicate_revisions
= True
373 list_display
= ('_classement', 'derniere_modification', '_archive')
374 list_filter
= ('archive', )
378 'degre', 'coefficient', 'archive')}),
381 def _classement(self
, obj
):
383 _classement
.short_description
= u
"Classement"
386 class DeviseAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
387 DerniereModificationAdmin
, BaseAdmin
):
388 ignore_duplicate_revisions
= True
390 'code', 'nom', 'derniere_modification', '_archive',
392 list_filter
= ('archive', )
394 (None, {'fields': ('code', 'nom', 'archive', )}),
398 class DossierAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
399 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
400 change_list_template
= "admin/rh/dossier/change_list.html"
401 ignore_duplicate_revisions
= True
402 alphabet_filter
= 'employe__nom'
410 'poste__nom_feminin',
411 'poste__implantation__nom',
419 '_zone_administrative',
423 'derniere_modification',
426 list_display_links
= ('_nom',)
428 'poste__implantation__zone_administrative',
429 'poste__implantation',
430 'poste__type_poste__categorie_emploi',
432 'rh_contrats__type_contrat',
435 inlines
= (DossierPieceInline
,
436 DossierClassementRecordInline
,
439 DossierCommentaireInline
,
449 'organisme_bstg',)}),
454 'remplacement_de', )}),
459 'regime_travail_nb_heure_semaine'),)}),
462 'compte_compta', 'compte_courriel',
465 ('Occupation du Poste par cet Employe', {
466 'fields': (('date_debut', 'date_fin'), )}
469 form
= make_ajax_form(rh
.Dossier
, {
470 'employe': 'employes',
472 'remplacement_de': 'dossiers',
473 }, superclass
=DossierForm
)
475 def lookup_allowed(self
, key
, value
):
477 'employe__nom__istartswith',
478 'poste__implantation__zone_administrative__code__exact',
479 'poste__implantation__zone_administrative__in',
480 'poste__implantation__id__exact',
481 'poste__type_poste__id__exact',
482 'poste__type_poste__categorie_emploi__id__exact',
483 'rh_contrats__type_contrat__id__exact',
491 _id
.short_description
= u
"#"
492 _id
.admin_order_field
= "id"
494 def _apercu(self
, d
):
495 apercu_link
= u
"""<a title="Aperçu du dossier"
496 onclick="return showAddAnotherPopup(this);"
498 <img src="%simg/dossier-apercu.png" />
500 (reverse('dossier_apercu', args
=(d
.id,)),
504 _apercu
.allow_tags
= True
505 _apercu
.short_description
= u
""
509 _nom
.allow_tags
= True
510 _nom
.short_description
= u
"Dossier"
512 def _employe(self
, obj
):
513 employe
= obj
.employe
514 view_link
= reverse('employe_apercu', args
=(employe
.id,))
515 edit_link
= reverse('admin:rh_employe_change', args
=(employe
.id,))
517 view
= u
"""<a href="%s"
518 title="Aperçu l'employé"
519 onclick="return showAddAnotherPopup(this);">
520 <img src="%simg/employe-apercu.png" />
521 </a>""" % (view_link
, settings
.STATIC_URL
,)
522 return u
"""%s<a href='%s' style="%s;">%s</a>""" % \
523 (view
, edit_link
, style
, employe
)
524 _employe
.allow_tags
= True
525 _employe
.short_description
= u
"Employé"
526 _employe
.admin_order_field
= "employe__nom"
528 def _poste(self
, dossier
):
529 link
= u
"""<a title="Aperçu du poste"
530 onclick="return showAddAnotherPopup(this);"
531 href='%s'><img src="%simg/poste-apercu.png" />
533 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
534 (reverse('poste_apercu', args
=(dossier
.poste
.id,)),
536 reverse('admin:rh_poste_change', args
=(dossier
.poste
.id,)),
541 _poste
.allow_tags
= True
542 _poste
.short_description
= u
'Poste'
543 _poste
.admin_order_field
= 'poste__nom'
545 def _zone_administrative(self
, obj
):
546 return obj
.poste
.implantation
.zone_administrative
.code
547 _zone_administrative
.short_description
= u
"Zone administrative"
548 _zone_administrative
.admin_order_field
= \
549 'poste__implantation__zone_administrative__code'
551 def _implantation(self
, obj
):
552 return obj
.poste
.implantation
.nom
553 _implantation
.short_description
= u
"Implantation"
554 _implantation
.admin_order_field
= 'poste__implantation__nom'
556 def _date_debut(self
, obj
):
557 return date(obj
.date_debut
)
559 _date_debut
.short_description
= u
'Début'
560 _date_debut
.admin_order_field
= 'date_debut'
562 def _date_fin(self
, obj
):
563 return date(obj
.date_fin
)
564 _date_fin
.short_description
= u
'Fin'
565 _date_fin
.admin_order_field
= 'date_fin'
567 def _date_modification(self
, obj
):
568 return date(obj
.date_modification
) \
569 if obj
.date_modification
is not None else "(aucune)"
570 _date_modification
.short_description
= u
'date modification'
571 _date_modification
.admin_order_field
= 'date_modification'
575 dossiers_dae
= d
.dossiers_dae
.all()
576 if len(dossiers_dae
) > 0:
577 dossier_dae
= dossiers_dae
[0]
578 apercu_link
= u
"""<a title="Aperçu du dossier"
579 onclick="return showAddAnotherPopup(this);"
581 <img src="%simg/loupe.png" />
583 (reverse('embauche_consulter', args
=(dossier_dae
.id,)),
587 _dae
.allow_tags
= True
588 _dae
.short_description
= u
"DAE"
590 def save_formset(self
, request
, form
, formset
, change
):
591 instances
= formset
.save(commit
=False)
592 for instance
in instances
:
593 if instance
.__class__
== rh
.DossierCommentaire
:
594 instance
.owner
= request
.user
595 instance
.date_creation
= datetime
.datetime
.now()
599 class EmployeAdminBase(DateRangeMixin
, ProtectRegionMixin
,
600 DerniereModificationAdmin
, BaseAdmin
):
601 prefixe_recherche_temporelle
= "rh_dossiers__"
602 alphabet_filter
= 'nom'
603 DEFAULT_ALPHABET
= u
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
605 'id', 'nom', 'prenom', 'nom_affichage',
606 'rh_dossiers__poste__nom',
607 'rh_dossiers__poste__nom_feminin'
610 form
= EmployeAdminForm
612 '_id', '_apercu', '_nom', '_dossiers_postes',
613 #'_zone_administrative',
616 'derniere_modification'
618 list_display_links
= ('_nom',)
620 'rh_dossiers__poste__implantation__zone_administrative',
621 'rh_dossiers__poste__implantation', 'nb_postes'
624 AyantDroitInline
, DossierROInline
, EmployePieceInline
,
625 EmployeCommentaireInline
631 ('nom_affichage', 'genre'),
636 ('Informations personnelles', {
637 'fields': ('situation_famille', 'date_entree', )
639 ('Coordonnées personnelles', {
641 ('tel_domicile', 'tel_cellulaire'),
642 ('adresse', 'ville'),
643 ('code_postal', 'province'),
652 _id
.short_description
= u
"#"
653 _id
.admin_order_field
= "id"
655 def _apercu(self
, obj
):
656 return u
"""<a title="Aperçu de l'employé"
657 onclick="return showAddAnotherPopup(this);"
659 <img src="%simg/employe-apercu.png" />
661 (reverse('employe_apercu', args
=(obj
.id,)), settings
.STATIC_URL
)
662 _apercu
.allow_tags
= True
663 _apercu
.short_description
= u
""
666 edit_link
= reverse('admin:rh_employe_change', args
=(obj
.id,))
667 return u
"""<a href='%s'><strong>%s</strong></a>""" % \
668 (edit_link
, "%s %s" % (obj
.nom
.upper(), obj
.prenom
))
669 _nom
.allow_tags
= True
670 _nom
.short_description
= u
"Employé"
671 _nom
.admin_order_field
= "nom"
673 def _zone_administrative(self
, obj
):
675 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
676 zone
= d
.poste
.implantation
.zone_administrative
.code
680 _zone_administrative
.short_description
= u
"Zone administrative"
682 def _implantation(self
, obj
):
684 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
685 implantation
= d
.poste
.implantation
.nom
689 _implantation
.short_description
= u
"Implantation"
691 def _dossiers_postes(self
, obj
):
693 for d
in obj
.rh_dossiers
.all().order_by('-date_debut'):
696 if d
.date_fin
is not None and d
.date_fin
< datetime
.date
.today():
697 link_style
= u
' style="color:#666;"'
698 list_style
= u
' style="color:grey;"'
700 dossier
= u
"""<a title="Aperçu du dossier"
702 onclick="return showAddAnotherPopup(this);"
703 title="Aperçu du dossier">
704 <img src="%simg/dossier-apercu.png" />
706 <a href="%s"%s>Dossier</a>
708 (reverse('dossier_apercu', args
=(d
.id,)),
710 reverse('admin:rh_dossier_change', args
=(d
.id,)),
713 poste
= u
"""<a title="Aperçu du poste"
715 onclick="return showAddAnotherPopup(this);"
716 title="Aperçu du poste">
717 <img src="%simg/poste-apercu.png" />
719 <a href="%s"%s>%s [%d]</a>
721 (reverse('poste_apercu', args
=(d
.poste
.id,)),
723 reverse('admin:rh_poste_change', args
=(d
.poste
.id,)),
728 link
= u
"""<li%s>%s %s</li>""" % \
729 (list_style
, dossier
, poste
)
732 return "<ul>%s</ul>" % "\n".join(l
)
733 _dossiers_postes
.allow_tags
= True
734 _dossiers_postes
.short_description
= u
"Dossiers et postes"
736 def _date_modification(self
, obj
):
737 return date(obj
.date_modification
) \
738 if obj
.date_modification
is not None else "(aucune)"
739 _date_modification
.short_description
= u
'date modification'
740 _date_modification
.admin_order_field
= 'date_modification'
742 def queryset(self
, request
):
743 qs
= super(EmployeAdminBase
, self
).queryset(request
)
744 return qs
.select_related(depth
=1).order_by('nom')
746 def save_formset(self
, request
, form
, formset
, change
):
747 instances
= formset
.save(commit
=False)
748 for instance
in instances
:
749 if instance
.__class__
== rh
.EmployeCommentaire
:
750 instance
.owner
= request
.user
751 instance
.date_creation
= datetime
.datetime
.now()
755 class EmployeAdmin(reversion
.VersionAdmin
, EmployeAdminBase
):
756 change_list_template
= "admin/rh/employe/change_list.html"
757 ignore_duplicate_revisions
= True
760 class EmployeProxyAdmin(EmployeAdminBase
):
761 list_display
= ('_id', '_apercu', '_nom', '_organigramme')
765 def __init__(self
, *args
, **kwargs
):
766 super(EmployeProxyAdmin
, self
).__init__(*args
, **kwargs
)
767 self
.list_display_links
= (None, )
769 def queryset(self
, request
):
770 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
772 if in_drh_or_admin(request
.user
) or \
773 user_gere_obj_de_sa_region(request
.user
):
778 def has_add_permission(self
, obj
):
781 def has_change_permission(self
, request
, obj
=None):
782 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
783 if groups
.CORRESPONDANT_RH
in user_groups
or \
784 groups
.ADMINISTRATEURS
in user_groups
or \
785 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
786 in_drh_or_admin(request
.user
):
790 def _organigramme(self
, obj
):
792 for d
in rh
.Dossier
.objects
.filter(
793 Q(date_fin__gt
=datetime
.date
.today()) |
Q(date_fin
=None),
794 Q(date_debut__lt
=datetime
.date
.today()) |
Q(date_debut
=None),
798 u
'Organigramme, niveau: ' \
799 u
'<input type="text" id="level_%s" ' \
800 u
'style="width:30px;height:15px;" /> ' \
801 u
'<input type="button" value="Générer" ' \
802 u
"""onclick="window.location='%s' + """ \
803 u
"""document.getElementById('level_%s').value" />""" % (
805 reverse('rho_employe_sans_niveau', args
=(d
.poste
.id,)),
808 link
= u
"""<li>%s [%s]:<br />%s</li>""" % (
814 return "<ul>%s</ul>" % "\n".join(l
)
816 _organigramme
.allow_tags
= True
817 _organigramme
.short_description
= "Organigramme"
820 class CategorieEmploiAdmin(reversion
.VersionAdmin
,
821 DerniereModificationAdmin
, BaseAdmin
):
822 ignore_duplicate_revisions
= True
823 list_display
= ('nom', 'derniere_modification')
824 inlines
= (TypePosteInline
,)
826 (None, {'fields': ('nom', )}),
830 class OrganismeBstgAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
832 ignore_duplicate_revisions
= True
833 search_fields
= ('nom',)
834 list_display
= ('nom', 'type', 'pays', 'derniere_modification')
835 list_filter
= ('type', )
836 inlines
= (DossierROInline
,)
838 (None, {'fields': ('nom', 'type', 'pays',)}),
842 class PosteAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
843 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
844 change_list_template
= "admin/rh/poste/change_list.html"
845 ignore_duplicate_revisions
= True
846 form
= make_ajax_form(rh
.Poste
, {
847 'implantation': 'implantations',
848 'type_poste': 'typepostes',
849 'responsable': 'postes',
850 'valeur_point_min': 'valeurpoints',
851 'valeur_point_max': 'valeurpoints',
853 alphabet_filter
= 'nom'
858 'implantation__zone_administrative__code',
859 'implantation__zone_administrative__nom',
860 'rh_dossiers__employe__id',
861 'rh_dossiers__employe__nom',
862 'rh_dossiers__employe__prenom',
865 '_id', '_apercu', '_nom', '_occupe_par', 'implantation', '_service',
866 '_responsable', 'date_debut', 'date_fin', 'derniere_modification',
870 'implantation__zone_administrative',
874 'type_poste__categorie_emploi',
875 'type_poste__famille_professionnelle',
878 list_display_links
= ('_nom',)
881 ('nom', 'nom_feminin'),
891 'regime_travail_nb_heure_semaine'),
895 'fields': (('local', 'expatrie', 'mise_a_disposition', 'appel'),)}
898 'fields': (('classement_min',
911 ('Comparatifs de rémunération', {
912 'fields': ('devise_comparaison',
913 ('comp_locale_min', 'comp_locale_max'),
914 ('comp_universite_min', 'comp_universite_max'),
915 ('comp_fonctionpub_min', 'comp_fonctionpub_max'),
916 ('comp_ong_min', 'comp_ong_max'),
917 ('comp_autre_min', 'comp_autre_max'))}
920 'fields': ('justification',)}
922 ('Autres Méta-données', {
923 'fields': ('date_debut', 'date_fin')}
927 inlines
= (PosteFinancementInline
,
930 PosteComparaisonInline
,
931 PosteCommentaireInline
, )
933 def lookup_allowed(self
, key
, value
):
935 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
937 'implantation__zone_administrative__code__exact',
938 'implantation__id__exact', 'type_poste__id__exact',
939 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
940 'service__isnull', 'vacant__exact', 'vacant__isnull'
941 ) or super(PosteAdmin
, self
).lookup_allowed(key
, value
)
945 _id
.short_description
= '#'
946 _id
.admin_order_field
= 'id'
948 def _apercu(self
, poste
):
949 view_link
= u
"""<a onclick="return showAddAnotherPopup(this);"
950 title="Aperçu du poste"
952 <img src="%simg/poste-apercu.png" />
954 (reverse('poste_apercu', args
=(poste
.id,)),
955 settings
.STATIC_URL
,)
957 _apercu
.allow_tags
= True
958 _apercu
.short_description
= ''
960 def _nom(self
, poste
):
961 return """<a href="%s">%s</a>""" % \
962 (reverse('admin:rh_poste_change', args
=(poste
.id,)),
964 _nom
.allow_tags
= True
965 _nom
.short_description
= u
'Poste'
966 _nom
.admin_order_field
= 'nom'
968 def _occupe_par(self
, obj
):
969 """Formatte la méthode Poste.occupe_par() pour l'admin"""
971 if obj
.date_fin
is not None and obj
.date_fin
< datetime
.date
.today():
973 employes
= obj
.occupe_par()
977 link
= u
"""<a href='%s'
978 title='Aperçu de l\'employé'
979 onclick='return showAddAnotherPopup(this)'>
980 <img src='%simg/employe-apercu.png' />
982 <a href='%s'>%s</a>""" % \
983 (reverse('employe_apercu', args
=(e
.id,)),
985 reverse('admin:rh_employe_change', args
=(e
.id,)),
988 output
= "\n<br />".join(l
)
990 _occupe_par
.allow_tags
= True
991 _occupe_par
.short_description
= "Occupé par"
993 def _zone_administrative(self
, poste
):
994 return poste
.implantation
.zone_administrative
.code
995 _zone_administrative
.short_description
= 'Zone administrative'
996 _zone_administrative
.admin_order_field
= \
997 'implantation__zone_administrative__code'
999 def _implantation(self
, poste
):
1000 return poste
.implantation
.nom
1001 _implantation
.short_description
= 'Implantation'
1002 _implantation
.admin_order_field
= 'implantation'
1004 def _service(self
, obj
):
1006 _service
.short_description
= 'Service'
1007 _service
.allow_tags
= True
1009 def _responsable(self
, obj
):
1011 responsable
= u
"""<a href="%s"
1012 onclick="return showAddAnotherPopup(this)">
1013 <img src="%simg/poste-apercu.png"
1014 title="Aperçu du poste" />
1016 <a href="%s">%s [%d]</a>
1018 (reverse('poste_apercu', args
=(obj
.responsable
.id,)),
1019 settings
.STATIC_URL
,
1020 reverse('admin:rh_poste_change', args
=(obj
.responsable
.id,)),
1021 obj
.responsable
.nom
,
1027 dossier
= obj
.responsable
.rh_dossiers
.all() \
1028 .order_by('-date_debut')[0]
1029 employe_id
= dossier
.employe
.id
1030 employe_html
= u
"""<br />
1032 onclick="return showAddAnotherPopup(this)">
1033 <img src="%simg/employe-apercu.png"
1034 title="Aperçu de l'employé">
1036 <a href="%s">%s</a>""" % \
1037 (reverse('employe_apercu', args
=(employe_id
,)),
1038 settings
.STATIC_URL
,
1039 reverse('admin:rh_employe_change', args
=(employe_id
,)),
1044 return "%s %s" % (responsable
, employe_html
)
1045 _responsable
.short_description
= 'Responsable'
1046 _responsable
.allow_tags
= True
1048 def _date_debut(self
, obj
):
1049 return date_format(obj
.date_debut
)
1050 _date_debut
.short_description
= u
'Début'
1051 _date_debut
.admin_order_field
= 'date_debut'
1053 def _date_fin(self
, obj
):
1054 return date_format(obj
.date_fin
)
1055 _date_fin
.short_description
= u
'Fin'
1056 _date_fin
.admin_order_field
= 'date_fin'
1058 def _dae(self
, poste
):
1060 postes_dae
= poste
.postes_dae
.all()
1061 if len(postes_dae
) > 0:
1062 poste_dae
= postes_dae
[0]
1064 u
'<a title="Aperçu du dossier" href="%s" ' \
1065 u
'onclick="return showAddAnotherPopup(this);">' \
1066 u
'<img src="%simg/loupe.png" /></a>' % (reverse(
1067 'poste_consulter', args
=("dae-%s" % poste_dae
.id,)
1068 ), settings
.STATIC_URL
)
1070 _dae
.allow_tags
= True
1071 _dae
.short_description
= u
"DAE"
1073 def save_formset(self
, request
, form
, formset
, change
):
1074 instances
= formset
.save(commit
=False)
1075 for instance
in instances
:
1076 if instance
.__class__
== rh
.PosteCommentaire
:
1077 instance
.owner
= request
.user
1078 instance
.date_creation
= datetime
.datetime
.now()
1083 class ResponsableInline(admin
.TabularInline
):
1084 model
= rh
.ResponsableImplantation
1086 fk_name
= "implantation"
1087 form
= ResponsableInlineForm
1090 class ResponsableImplantationAdmin(BaseAdmin
):
1093 inlines
= (ResponsableInline
, )
1094 list_filter
= ('zone_administrative', 'statut', )
1095 list_display
= ('_zone_administrative', '_nom', 'statut', '_responsable', )
1096 list_display_links
= ('_nom',)
1098 readonly_fields
= ('nom', )
1101 'responsable__employe__id',
1102 'responsable__employe__nom',
1103 'responsable__employe__prenom',
1106 inlines
= (ResponsableInline
, )
1108 def _zone_administrative(self
, obj
):
1109 return obj
.zone_administrative
.code
1110 _zone_administrative
.short_description
= u
"Zone administrative"
1111 _zone_administrative
.admin_order_field
= 'zone_administrative__code'
1113 def _nom(self
, obj
):
1115 _nom
.short_description
= u
"Implantation"
1116 _nom
.admin_order_field
= 'nom'
1118 def _responsable(self
, obj
):
1120 employe
= employe
= obj
.responsable
.employe
1121 except Exception, e
:
1123 u
"<span style='color: red;'>"
1124 u
"Pas de responsable</span><!-- %s -->" % e
1127 dossiers
= employe
.dossiers_encours()
1128 if len(dossiers
) == 0:
1129 return u
"<span style='color: red;'>%s %s </span>" % (
1130 employe
, u
"sans dossier actif")
1133 except Exception, e
:
1134 return u
"<!-- %s -->" % e
1135 _responsable
.allow_tags
= True
1136 _responsable
.short_description
= u
"Responsable"
1137 _responsable
.admin_order_field
= 'responsable__employe__nom'
1139 def has_add_permission(self
, request
=None):
1142 def has_change_permission(self
, request
, obj
=None):
1143 return in_drh_or_admin(request
.user
)
1145 def has_delete_permission(self
, request
, obj
=None):
1149 class ServiceAdminBase(ArchivableAdmin
, DerniereModificationAdmin
, BaseAdmin
):
1150 list_display
= ('nom', 'derniere_modification', '_archive')
1151 list_filter
= ('archive', )
1153 (None, {'fields': ('nom', 'archive')}),
1157 class ServiceAdmin(reversion
.VersionAdmin
, ServiceAdminBase
):
1158 ignore_duplicate_revisions
= True
1161 class ServiceProxyAdmin(ServiceAdminBase
):
1162 list_display
= ('nom', '_organigramme', '_archive', )
1165 def __init__(self
, *args
, **kwargs
):
1166 super(ServiceProxyAdmin
, self
).__init__(*args
, **kwargs
)
1167 self
.list_display_links
= (None, )
1169 def queryset(self
, request
):
1170 return super(ServiceProxyAdmin
, self
).queryset(request
) \
1171 .annotate(num_postes
=Count('rh_postes')) \
1172 .filter(num_postes__gt
=0)
1174 def has_add_permission(self
, obj
):
1177 def has_change_permission(self
, request
, obj
=None):
1178 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1179 if groups
.CORRESPONDANT_RH
in user_groups
or \
1180 groups
.ADMINISTRATEURS
in user_groups
or \
1181 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1182 in_drh_or_admin(request
.user
):
1186 def _organigramme(self
, obj
):
1187 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1188 (reverse('rho_service', args
=(obj
.id,)))
1189 _organigramme
.allow_tags
= True
1190 _organigramme
.short_description
= "Organigramme"
1193 class StatutAdmin(reversion
.VersionAdmin
,
1195 DerniereModificationAdmin
,
1197 ignore_duplicate_revisions
= True
1198 list_display
= ('code', 'nom', 'derniere_modification', '_archive')
1199 list_filter
= ('archive', )
1202 'fields': ('code', 'nom', 'archive'),
1207 class TauxChangeAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
1209 ignore_duplicate_revisions
= True
1210 list_display
= ('taux', 'devise', 'annee', 'derniere_modification')
1211 list_filter
= ('devise',)
1214 'fields': ('taux', 'devise', 'annee', ),
1219 class TypeContratAdmin(reversion
.VersionAdmin
,
1221 DerniereModificationAdmin
,
1223 ignore_duplicate_revisions
= True
1224 list_display
= ('nom', 'nom_long', 'derniere_modification', '_archive')
1225 list_filter
= ('archive', )
1228 'fields': ('nom', 'nom_long', 'archive'),
1233 class TypePosteAdmin(reversion
.VersionAdmin
,
1235 DerniereModificationAdmin
,
1237 ignore_duplicate_revisions
= True
1238 search_fields
= ('nom', 'nom_feminin', )
1239 list_display
= ('nom', 'categorie_emploi',
1240 'derniere_modification', '_archive',)
1241 list_filter
= ('categorie_emploi', 'famille_professionnelle', 'archive')
1245 'nom', 'nom_feminin', 'is_responsable', 'categorie_emploi',
1246 'famille_professionnelle',
1253 class TypeRemunerationAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
1254 DerniereModificationAdmin
, BaseAdmin
):
1255 ignore_duplicate_revisions
= True
1257 'nom', 'type_paiement', 'nature_remuneration',
1258 'derniere_modification', '_archive'
1260 list_filter
= ('archive', )
1264 'nom', 'type_paiement', 'nature_remuneration', 'archive'
1270 class TypeRevalorisationAdmin(reversion
.VersionAdmin
,
1272 DerniereModificationAdmin
,
1274 ignore_duplicate_revisions
= True
1275 list_display
= ('nom', 'derniere_modification', '_archive')
1276 list_filter
= ('archive', )
1278 (None, {'fields': ('nom', 'archive')}),
1282 class ValeurPointAdmin(reversion
.VersionAdmin
,
1283 DerniereModificationAdmin
,
1285 ignore_duplicate_revisions
= True
1287 '_devise_code', '_devise_nom', 'annee', 'implantation',
1288 'valeur', 'derniere_modification'
1290 list_filter
= ('annee', 'devise', 'implantation__zone_administrative', )
1292 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
1295 def queryset(self
, request
):
1296 return super(ValeurPointAdmin
, self
).queryset(request
) \
1297 .select_related('devise', 'implantation')
1299 def _devise_code(self
, obj
):
1300 return obj
.devise
.code
1301 _devise_code
.short_description
= "Code de la devise"
1303 def _devise_nom(self
, obj
):
1304 return obj
.devise
.nom
1305 _devise_nom
.short_description
= "Nom de la devise"
1308 class ImplantationProxyAdmin(BaseAdmin
):
1309 list_display
= ('nom', '_organigramme')
1312 def __init__(self
, *args
, **kwargs
):
1313 super(ImplantationProxyAdmin
, self
).__init__(*args
, **kwargs
)
1314 self
.list_display_links
= (None, )
1316 def has_add_permission(self
, obj
):
1319 def has_change_permission(self
, request
, obj
=None):
1320 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1321 if groups
.CORRESPONDANT_RH
in user_groups
or \
1322 groups
.ADMINISTRATEURS
in user_groups
or \
1323 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1324 in_drh_or_admin(request
.user
):
1328 def _organigramme(self
, obj
):
1329 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1330 reverse('rho_implantation', args
=(obj
.id,))
1332 _organigramme
.allow_tags
= True
1333 _organigramme
.short_description
= "Organigramme"
1336 class RegionProxyAdmin(BaseAdmin
):
1337 list_display
= ('nom', '_organigramme')
1340 def __init__(self
, *args
, **kwargs
):
1341 super(RegionProxyAdmin
, self
).__init__(*args
, **kwargs
)
1342 self
.list_display_links
= (None, )
1344 def has_add_permission(self
, obj
):
1347 def has_change_permission(self
, request
, obj
=None):
1348 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1349 if groups
.CORRESPONDANT_RH
in user_groups
or \
1350 groups
.ADMINISTRATEURS
in user_groups
or \
1351 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1352 in_drh_or_admin(request
.user
):
1356 def _organigramme(self
, obj
):
1357 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1358 reverse('rho_region', args
=(obj
.id,))
1360 _organigramme
.allow_tags
= True
1361 _organigramme
.short_description
= "Organigramme"
1364 class ProfileInline(admin
.StackedInline
):
1365 model
= rh
.UserProfile
1368 class RHUserAdmin(UserAdmin
):
1369 inlines
= list(UserAdmin
.inlines
) + [ProfileInline
]
1372 def _invalidate(modaladmin
, req
, qs
):
1373 qs
.update(valide
=False)
1374 _invalidate
.short_description
= 'Invalider'
1377 def _communique(modaladmin
, req
, qs
):
1378 url_prefix
= ('https://' if req
.is_secure() else 'http://') + (
1379 req
.META
['SERVER_NAME'] if 'SERVER_NAME' in req
.META
else
1380 'localhost') + (':%s' % (req
.META
['SERVER_PORT']) if 'SERVER_PORT'
1381 in req
.META
else '')
1383 types
= [x
[0] for x
in rh
.TYPES_CHANGEMENT
]
1385 # make a list of all possible email types. It is reversed so that
1386 # we start with the largest combinations.
1387 combs
= list(itertools
.chain(*map(
1388 lambda x
: itertools
.combinations(types
, x
),
1389 range(1, len(types
)+1))))
1391 combs
= reversed(combs
)
1393 recipient_list
= ref
.Employe
.objects
.filter(
1394 changement_notifications__in
=
1395 rh
.ChangementPersonnelNotifications
.objects
.all()).distinct()
1406 recipients
= ref
.Employe
.objects
.none()
1408 recipients
= recipient_list
.filter(changement_notifications__type
=t
)
1410 recipient_list
= recipient_list
.exclude(
1411 id__in
=recipients
.values_list('id', flat
=True))
1413 types_dict
= dict(rh
.TYPES_CHANGEMENT
)
1424 ctx
['types'].append({
1425 'bgcolor': thead_colors
[t
],
1426 'name': types_dict
[t
],
1427 'changements': changements
,
1430 for ch
in qs
.filter(type__in
=[t
]):
1431 changements
.append(ch
)
1433 template
= loader
.get_template('email/mouvement_employe.html')
1434 content
= template
.render(Context(ctx
))
1436 sujet
= '[SGRH] ARRIVÉES - DÉPARTS - MOBILITÉS'
1441 settings
.SERVER_EMAIL
,
1442 [x
.courriel
for x
in recipients
],
1444 msg
.content_subtype
= "html"
1449 date_communication
=datetime
.datetime
.now()
1452 _communique
.short_description
= u
'Envoyer aux desinataires prévus.'
1455 class ChangementPersonnelAdmin(admin
.ModelAdmin
):
1463 return obj
.dossier
.__unicode__()
1464 _dossier
.short_description
= u
'Dossier'
1467 'type', _dossier
, 'valide', 'communique', 'date_communication'
1477 admin
.site
.unregister(User
)
1478 admin
.site
.register(User
, RHUserAdmin
)
1480 admin
.site
.register(rh
.Classement
, ClassementAdmin
)
1481 admin
.site
.register(rh
.Devise
, DeviseAdmin
)
1482 admin
.site
.register(rh
.Dossier
, DossierAdmin
)
1483 admin
.site
.register(EmployeProxy
, EmployeProxyAdmin
)
1484 admin
.site
.register(ServiceProxy
, ServiceProxyAdmin
)
1485 admin
.site
.register(rh
.Employe
, EmployeAdmin
)
1486 admin
.site
.register(rh
.CategorieEmploi
, CategorieEmploiAdmin
)
1487 admin
.site
.register(rh
.FamilleProfessionnelle
)
1488 admin
.site
.register(rh
.OrganismeBstg
, OrganismeBstgAdmin
)
1489 admin
.site
.register(rh
.Poste
, PosteAdmin
)
1490 admin
.site
.register(
1491 rh
.ResponsableImplantationProxy
, ResponsableImplantationAdmin
1493 admin
.site
.register(rh
.Service
, ServiceAdmin
)
1494 admin
.site
.register(rh
.Statut
, StatutAdmin
)
1495 admin
.site
.register(rh
.TauxChange
, TauxChangeAdmin
)
1496 admin
.site
.register(rh
.TypeContrat
, TypeContratAdmin
)
1497 admin
.site
.register(rh
.TypePoste
, TypePosteAdmin
)
1498 admin
.site
.register(rh
.TypeRemuneration
, TypeRemunerationAdmin
)
1499 admin
.site
.register(rh
.TypeRevalorisation
, TypeRevalorisationAdmin
)
1500 admin
.site
.register(rh
.ValeurPoint
, ValeurPointAdmin
)
1501 admin
.site
.register(ImplantationProxy
, ImplantationProxyAdmin
)
1502 admin
.site
.register(RegionProxy
, RegionProxyAdmin
)
1503 admin
.site
.register(rh
.ChangementPersonnelNotifications
)
1504 admin
.site
.register(rh
.ChangementPersonnel
, ChangementPersonnelAdmin
)