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
, \
28 user_can_delete_obj
, \
31 from project
.rh
.forms
import ContratForm
, AyantDroitForm
, EmployeAdminForm
, \
32 AjaxSelect
, DossierForm
, ResponsableInlineForm
, \
33 ClassementHistoriqueForm
35 from project
.rh
.change_list
import ChangeList
38 def listing_par_defaut(model
, request
):
40 Teste si la requete provient de la même page.
42 if not 'HTTP_REFERER' in request
.META
.keys():
44 referer
= request
.META
['HTTP_REFERER']
45 referer
= "/".join(referer
.split('/')[3:])
46 referer
= "/%s" % referer
.split('?')[0]
47 change_list_view
= 'admin:%s_%s_changelist' % (
48 model
._meta
.app_label
,
49 model
.__name__
.lower(),)
50 return referer
!= reverse(change_list_view
)
53 class BaseAdmin(admin
.ModelAdmin
):
57 'css/admin_custom.css',
58 'jquery-autocomplete/jquery.autocomplete.css',
61 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
62 'jquery-autocomplete/jquery.autocomplete.min.js',
66 # Admin pour reversion
68 class ArchivableAdmin(admin
.ModelAdmin
):
70 Admin pour les modèles archivables
72 list_filter
= ('archive', )
74 def queryset(self
, request
):
75 return self
.model
.avec_archives
.all()
77 def _archive(self
, obj
):
82 _archive
.short_description
= u
'Archivé'
83 _archive
.admin_order_field
= 'archive'
86 class RegionProxy(ref
.Region
):
87 """ Proxy utilisé pour les organigrammes par région """
91 verbose_name
= u
"Organigramme par région"
92 verbose_name_plural
= u
"Organigramme par région"
95 class ImplantationProxy(ref
.Implantation
):
96 """ Proxy utilisé pour les organigrammes par implantation """
100 verbose_name
= u
"Organigramme par implantations"
101 verbose_name_plural
= u
"Organigramme par implantations"
104 class ServiceProxy(rh
.Service
):
105 """ Proxy utilisé pour les organigrammes par service """
110 verbose_name
= u
"Organigramme par services"
111 verbose_name_plural
= u
"Organigramme par services"
114 class EmployeProxy(rh
.Employe
):
115 """ Proxy utilisé pour les organigrammes des employés """
119 verbose_name
= u
"Organigramme des employés"
120 verbose_name_plural
= u
"Organigramme des employés"
123 class DateRangeMixin(object):
124 prefixe_recherche_temporelle
= ""
126 def get_changelist(self
, request
, **kwargs
):
128 On filtre par défaut sur les items 'actifs'.
129 Le changelist plug le filtrage temporel.
131 if listing_par_defaut(self
.model
, request
):
132 params
= request
.GET
.copy()
133 params
.update({'statut': 'Actif'})
138 # Override of the InlineModelAdmin to support the link in the tabular inline
139 class LinkedInline(admin
.options
.InlineModelAdmin
):
140 template
= "admin/linked.html"
141 admin_model_path
= None
143 def __init__(self
, *args
):
144 super(LinkedInline
, self
).__init__(*args
)
145 if self
.admin_model_path
is None:
146 self
.admin_model_path
= self
.model
.__name__
.lower()
149 class ProtectRegionMixin(object):
151 def changelist_view(self
, request
, extra_context
=None):
153 On filtre par défaut sur la ZA du user connecté
155 if listing_par_defaut(self
.model
, request
):
156 if user_gere_obj_de_sa_region(request
.user
):
157 params
= request
.GET
.copy()
158 zones
= groups
.get_zones_from_user(request
.user
)
159 prefix_za
= "%s__in" % self
.model
.prefix_implantation
160 params
.update({prefix_za
: zones
})
162 return super(ProtectRegionMixin
, self
) \
163 .changelist_view(request
, extra_context
)
165 def queryset(self
, request
):
166 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
168 if in_drh_or_admin(request
.user
):
171 if user_gere_obj_de_sa_region(request
.user
):
172 zones
= groups
.get_zones_from_user(request
.user
)
173 qkey
= '%s__in' % self
.model
.prefix_implantation
174 q
= Q(**{qkey
: zones
})
175 qs
= qs
.filter(q
).distinct()
179 def has_add_permission(self
, request
):
180 return user_can_add_obj(request
.user
)
182 def has_change_permission(self
, request
, obj
=None):
184 return user_can_list_obj(request
.user
)
186 return user_can_change_obj(request
.user
, obj
)
188 def has_delete_permission(self
, request
, obj
=None):
189 return user_can_delete_obj(request
.user
, obj
) if obj
else True
192 class DerniereModificationAdmin(admin
.ModelAdmin
):
194 def queryset(self
, request
):
195 qs
= super(DerniereModificationAdmin
, self
).queryset(request
)
196 ct
= ContentType
.objects
.get_for_model(self
.model
)
197 db_table
= self
.model
._meta
.db_table
198 pk
= self
.model
._meta
.pk
.column
199 return qs
.extra(select
={
201 "SELECT action_time FROM django_admin_log "
202 "WHERE content_type_id = %d AND object_id = %s.%s "
203 "ORDER BY action_time DESC "
204 "LIMIT 1" % (ct
.id, db_table
, pk
),
208 "INNER JOIN django_admin_log l ON l.user_id = u.id "
209 "WHERE l.content_type_id = %d AND object_id = %s.%s "
210 "ORDER BY action_time DESC "
211 "LIMIT 1" % (ct
.id, db_table
, pk
),
214 def derniere_modification(self
, obj
):
216 if obj
.date_modification
:
217 text
+= obj
.date_modification
.strftime('%d-%m-%Y %H:%M')
218 if obj
.user_modification
:
219 text
+= ' par ' + obj
.user_modification
221 derniere_modification
.short_description
= u
'dernière modification'
222 derniere_modification
.admin_order_field
= 'date_modification'
227 class CommentaireInlineForm(forms
.ModelForm
):
229 def save(self
, commit
=True):
231 # Hack: reversion.VersionAdmin ne sauvegarde pas les champs qui ne
232 # sont pas explicitement dans le formulaire. Il plante cependant
233 # leur valeur dans `self.initial`. Ceci est un peu fragile. Si
234 # c'est possible, il serait plus approprié que Reversion se rende
235 # compte qu'il manque des champs.
236 instance
= super(CommentaireInlineForm
, self
).save(commit
=False)
237 if instance
.owner_id
is None and 'owner' in self
.initial
:
238 instance
.owner_id
= self
.initial
['owner']
239 if instance
.date_creation
is None and 'date_creation' in self
.initial
:
240 instance
.date_creation
= self
.initial
['date_creation']
247 class ReadOnlyInlineMixin(object):
249 def get_readonly_fields(self
, request
, obj
=None):
250 return [f
.name
for f
in self
.model
._meta
.fields
]
253 class AyantDroitInline(admin
.StackedInline
):
254 model
= rh
.AyantDroit
255 form
= AyantDroitForm
262 ('nom_affichage', 'genre'),
270 class AyantDroitCommentaireInline(admin
.TabularInline
):
271 readonly_fields
= ('owner',)
272 model
= rh
.AyantDroitCommentaire
274 form
= CommentaireInlineForm
277 class ContratInline(admin
.TabularInline
):
283 class DossierROInline(ReadOnlyInlineMixin
, LinkedInline
):
284 template
= "admin/rh/dossier/linked.html"
288 fields
= ('poste', 'date_debut', 'date_fin', )
290 def has_add_permission(self
, request
=None):
293 def has_change_permission(self
, request
, obj
=None):
296 def has_delete_permission(self
, request
, obj
=None):
300 class DossierClassementRecordInline(admin
.TabularInline
):
301 def queryset(self
, request
):
302 return (super(DossierClassementRecordInline
, self
)
303 .queryset(request
).order_by('-date_debut', '-id'))
304 model
= rh
.RHDossierClassementRecord
305 form
= ClassementHistoriqueForm
309 class DossierCommentaireInline(admin
.TabularInline
):
310 readonly_fields
= ('owner',)
311 model
= rh
.DossierCommentaire
313 form
= CommentaireInlineForm
316 class DossierPieceInline(admin
.TabularInline
):
317 model
= rh
.DossierPiece
321 class EmployeInline(admin
.TabularInline
):
325 class EmployeCommentaireInline(admin
.TabularInline
):
326 readonly_fields
= ('owner',)
327 model
= rh
.EmployeCommentaire
329 form
= CommentaireInlineForm
332 class EmployePieceInline(admin
.TabularInline
):
333 model
= rh
.EmployePiece
337 class PosteCommentaireInline(admin
.TabularInline
):
338 readonly_fields
= ('owner',)
339 model
= rh
.PosteCommentaire
341 form
= CommentaireInlineForm
344 class PosteFinancementInline(admin
.TabularInline
):
345 model
= rh
.PosteFinancement
348 class PostePieceInline(admin
.TabularInline
):
349 model
= rh
.PostePiece
352 class RemunerationInline(admin
.TabularInline
):
353 model
= rh
.Remuneration
357 class RemunerationROInline(ReadOnlyInlineMixin
, RemunerationInline
):
361 class TypePosteInline(admin
.TabularInline
):
365 class PosteComparaisonInline(admin
.TabularInline
):
366 model
= rh
.PosteComparaison
369 class ClassementAdmin(reversion
.VersionAdmin
,
371 DerniereModificationAdmin
,
373 ignore_duplicate_revisions
= True
374 list_display
= ('_classement', 'derniere_modification', '_archive')
375 list_filter
= ('archive', )
379 'degre', 'coefficient', 'archive')}),
382 def _classement(self
, obj
):
384 _classement
.short_description
= u
"Classement"
387 class DeviseAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
388 DerniereModificationAdmin
, BaseAdmin
):
389 ignore_duplicate_revisions
= True
391 'code', 'nom', 'derniere_modification', '_archive',
393 list_filter
= ('archive', )
395 (None, {'fields': ('code', 'nom', 'archive', )}),
399 class DossierAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
400 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
401 change_list_template
= "admin/rh/dossier/change_list.html"
402 ignore_duplicate_revisions
= True
403 alphabet_filter
= 'employe__nom'
411 'poste__nom_feminin',
412 'poste__implantation__nom',
420 '_zone_administrative',
424 'derniere_modification',
427 list_display_links
= ('_nom',)
429 'poste__implantation__zone_administrative',
430 'poste__implantation',
431 'poste__type_poste__categorie_emploi',
433 'rh_contrats__type_contrat',
436 inlines
= (DossierPieceInline
,
437 DossierClassementRecordInline
,
440 DossierCommentaireInline
,
450 'organisme_bstg',)}),
455 'remplacement_de', )}),
460 'regime_travail_nb_heure_semaine'),)}),
463 'compte_compta', 'compte_courriel',
466 ('Occupation du Poste par cet Employe', {
467 'fields': (('date_debut', 'date_fin'), )}
470 form
= make_ajax_form(rh
.Dossier
, {
471 'employe': 'employes',
473 'remplacement_de': 'dossiers',
474 }, superclass
=DossierForm
)
476 def lookup_allowed(self
, key
, value
):
478 'employe__nom__istartswith',
479 'poste__implantation__zone_administrative__code__exact',
480 'poste__implantation__zone_administrative__in',
481 'poste__implantation__id__exact',
482 'poste__type_poste__id__exact',
483 'poste__type_poste__categorie_emploi__id__exact',
484 'rh_contrats__type_contrat__id__exact',
492 _id
.short_description
= u
"#"
493 _id
.admin_order_field
= "id"
495 def _apercu(self
, d
):
496 apercu_link
= u
"""<a title="Aperçu du dossier"
497 onclick="return showAddAnotherPopup(this);"
499 <img src="%simg/dossier-apercu.png" />
501 (reverse('dossier_apercu', args
=(d
.id,)),
505 _apercu
.allow_tags
= True
506 _apercu
.short_description
= u
""
510 _nom
.allow_tags
= True
511 _nom
.short_description
= u
"Dossier"
513 def _employe(self
, obj
):
514 employe
= obj
.employe
515 view_link
= reverse('employe_apercu', args
=(employe
.id,))
516 edit_link
= reverse('admin:rh_employe_change', args
=(employe
.id,))
518 view
= u
"""<a href="%s"
519 title="Aperçu l'employé"
520 onclick="return showAddAnotherPopup(this);">
521 <img src="%simg/employe-apercu.png" />
522 </a>""" % (view_link
, settings
.STATIC_URL
,)
523 return u
"""%s<a href='%s' style="%s;">%s</a>""" % \
524 (view
, edit_link
, style
, employe
)
525 _employe
.allow_tags
= True
526 _employe
.short_description
= u
"Employé"
527 _employe
.admin_order_field
= "employe__nom"
529 def _poste(self
, dossier
):
530 link
= u
"""<a title="Aperçu du poste"
531 onclick="return showAddAnotherPopup(this);"
532 href='%s'><img src="%simg/poste-apercu.png" />
534 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
535 (reverse('poste_apercu', args
=(dossier
.poste
.id,)),
537 reverse('admin:rh_poste_change', args
=(dossier
.poste
.id,)),
542 _poste
.allow_tags
= True
543 _poste
.short_description
= u
'Poste'
544 _poste
.admin_order_field
= 'poste__nom'
546 def _zone_administrative(self
, obj
):
547 return obj
.poste
.implantation
.zone_administrative
.code
548 _zone_administrative
.short_description
= u
"Zone administrative"
549 _zone_administrative
.admin_order_field
= \
550 'poste__implantation__zone_administrative__code'
552 def _implantation(self
, obj
):
553 return obj
.poste
.implantation
.nom
554 _implantation
.short_description
= u
"Implantation"
555 _implantation
.admin_order_field
= 'poste__implantation__nom'
557 def _date_debut(self
, obj
):
558 return date(obj
.date_debut
)
560 _date_debut
.short_description
= u
'Début'
561 _date_debut
.admin_order_field
= 'date_debut'
563 def _date_fin(self
, obj
):
564 return date(obj
.date_fin
)
565 _date_fin
.short_description
= u
'Fin'
566 _date_fin
.admin_order_field
= 'date_fin'
568 def _date_modification(self
, obj
):
569 return date(obj
.date_modification
) \
570 if obj
.date_modification
is not None else "(aucune)"
571 _date_modification
.short_description
= u
'date modification'
572 _date_modification
.admin_order_field
= 'date_modification'
576 dossiers_dae
= d
.dossiers_dae
.all()
577 if len(dossiers_dae
) > 0:
578 dossier_dae
= dossiers_dae
[0]
579 apercu_link
= u
"""<a title="Aperçu du dossier"
580 onclick="return showAddAnotherPopup(this);"
582 <img src="%simg/loupe.png" />
584 (reverse('embauche_consulter', args
=(dossier_dae
.id,)),
588 _dae
.allow_tags
= True
589 _dae
.short_description
= u
"DAE"
591 def save_formset(self
, request
, form
, formset
, change
):
592 instances
= formset
.save(commit
=False)
593 for instance
in instances
:
594 if instance
.__class__
== rh
.DossierCommentaire
:
595 instance
.owner
= request
.user
596 instance
.date_creation
= datetime
.datetime
.now()
600 class EmployeAdminBase(DateRangeMixin
, ProtectRegionMixin
,
601 DerniereModificationAdmin
, BaseAdmin
):
602 prefixe_recherche_temporelle
= "rh_dossiers__"
603 alphabet_filter
= 'nom'
604 DEFAULT_ALPHABET
= u
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
606 'id', 'nom', 'prenom', 'nom_affichage',
607 'rh_dossiers__poste__nom',
608 'rh_dossiers__poste__nom_feminin'
611 form
= EmployeAdminForm
613 '_id', '_apercu', '_nom', '_dossiers_postes',
614 #'_zone_administrative',
617 'derniere_modification'
619 list_display_links
= ('_nom',)
621 'rh_dossiers__poste__implantation__zone_administrative',
622 'rh_dossiers__poste__implantation', 'nb_postes'
625 AyantDroitInline
, DossierROInline
, EmployePieceInline
,
626 EmployeCommentaireInline
632 ('nom_affichage', 'genre'),
637 ('Informations personnelles', {
638 'fields': ('situation_famille', 'date_entree', )
640 ('Coordonnées personnelles', {
642 ('tel_domicile', 'tel_cellulaire'),
643 ('adresse', 'ville'),
644 ('code_postal', 'province'),
653 _id
.short_description
= u
"#"
654 _id
.admin_order_field
= "id"
656 def _apercu(self
, obj
):
657 return u
"""<a title="Aperçu de l'employé"
658 onclick="return showAddAnotherPopup(this);"
660 <img src="%simg/employe-apercu.png" />
662 (reverse('employe_apercu', args
=(obj
.id,)), settings
.STATIC_URL
)
663 _apercu
.allow_tags
= True
664 _apercu
.short_description
= u
""
667 edit_link
= reverse('admin:rh_employe_change', args
=(obj
.id,))
668 return u
"""<a href='%s'><strong>%s</strong></a>""" % \
669 (edit_link
, "%s %s" % (obj
.nom
.upper(), obj
.prenom
))
670 _nom
.allow_tags
= True
671 _nom
.short_description
= u
"Employé"
672 _nom
.admin_order_field
= "nom"
674 def _zone_administrative(self
, obj
):
676 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
677 zone
= d
.poste
.implantation
.zone_administrative
.code
681 _zone_administrative
.short_description
= u
"Zone administrative"
683 def _implantation(self
, obj
):
685 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
686 implantation
= d
.poste
.implantation
.nom
690 _implantation
.short_description
= u
"Implantation"
692 def _dossiers_postes(self
, obj
):
694 for d
in obj
.rh_dossiers
.all().order_by('-date_debut'):
697 if d
.date_fin
is not None and d
.date_fin
< datetime
.date
.today():
698 link_style
= u
' style="color:#666;"'
699 list_style
= u
' style="color:grey;"'
701 dossier
= u
"""<a title="Aperçu du dossier"
703 onclick="return showAddAnotherPopup(this);"
704 title="Aperçu du dossier">
705 <img src="%simg/dossier-apercu.png" />
707 <a href="%s"%s>Dossier</a>
709 (reverse('dossier_apercu', args
=(d
.id,)),
711 reverse('admin:rh_dossier_change', args
=(d
.id,)),
714 poste
= u
"""<a title="Aperçu du poste"
716 onclick="return showAddAnotherPopup(this);"
717 title="Aperçu du poste">
718 <img src="%simg/poste-apercu.png" />
720 <a href="%s"%s>%s [%d]</a>
722 (reverse('poste_apercu', args
=(d
.poste
.id,)),
724 reverse('admin:rh_poste_change', args
=(d
.poste
.id,)),
729 link
= u
"""<li%s>%s %s</li>""" % \
730 (list_style
, dossier
, poste
)
733 return "<ul>%s</ul>" % "\n".join(l
)
734 _dossiers_postes
.allow_tags
= True
735 _dossiers_postes
.short_description
= u
"Dossiers et postes"
737 def _date_modification(self
, obj
):
738 return date(obj
.date_modification
) \
739 if obj
.date_modification
is not None else "(aucune)"
740 _date_modification
.short_description
= u
'date modification'
741 _date_modification
.admin_order_field
= 'date_modification'
743 def queryset(self
, request
):
744 qs
= super(EmployeAdminBase
, self
).queryset(request
)
745 return qs
.select_related(depth
=1).order_by('nom')
747 def save_formset(self
, request
, form
, formset
, change
):
748 instances
= formset
.save(commit
=False)
749 for instance
in instances
:
750 if instance
.__class__
== rh
.EmployeCommentaire
:
751 instance
.owner
= request
.user
752 instance
.date_creation
= datetime
.datetime
.now()
756 class EmployeAdmin(reversion
.VersionAdmin
, EmployeAdminBase
):
757 change_list_template
= "admin/rh/employe/change_list.html"
758 ignore_duplicate_revisions
= True
761 class EmployeProxyAdmin(EmployeAdminBase
):
762 list_display
= ('_id', '_apercu', '_nom', '_organigramme')
766 def __init__(self
, *args
, **kwargs
):
767 super(EmployeProxyAdmin
, self
).__init__(*args
, **kwargs
)
768 self
.list_display_links
= (None, )
770 def queryset(self
, request
):
771 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
773 if in_drh_or_admin(request
.user
) or \
774 user_gere_obj_de_sa_region(request
.user
):
779 def has_add_permission(self
, obj
):
782 def has_change_permission(self
, request
, obj
=None):
783 user_groups
= get_user_groupnames(request
.user
)
784 if groups
.CORRESPONDANT_RH
in user_groups
or \
785 groups
.ADMINISTRATEURS
in user_groups
or \
786 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
787 in_drh_or_admin(request
.user
):
791 def _organigramme(self
, obj
):
793 for d
in rh
.Dossier
.objects
.filter(
794 Q(date_fin__gt
=datetime
.date
.today()) |
Q(date_fin
=None),
795 Q(date_debut__lt
=datetime
.date
.today()) |
Q(date_debut
=None),
799 u
'Organigramme, niveau: ' \
800 u
'<input type="text" id="level_%s" ' \
801 u
'style="width:30px;height:15px;" /> ' \
802 u
'<input type="button" value="Générer" ' \
803 u
"""onclick="window.location='%s' + """ \
804 u
"""document.getElementById('level_%s').value" />""" % (
806 reverse('rho_employe_sans_niveau', args
=(d
.poste
.id,)),
809 link
= u
"""<li>%s [%s]:<br />%s</li>""" % (
815 return "<ul>%s</ul>" % "\n".join(l
)
817 _organigramme
.allow_tags
= True
818 _organigramme
.short_description
= "Organigramme"
821 class CategorieEmploiAdmin(reversion
.VersionAdmin
,
822 DerniereModificationAdmin
, BaseAdmin
):
823 ignore_duplicate_revisions
= True
824 list_display
= ('nom', 'derniere_modification')
825 inlines
= (TypePosteInline
,)
827 (None, {'fields': ('nom', )}),
831 class OrganismeBstgAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
833 ignore_duplicate_revisions
= True
834 search_fields
= ('nom',)
835 list_display
= ('nom', 'type', 'pays', 'derniere_modification')
836 list_filter
= ('type', )
837 inlines
= (DossierROInline
,)
839 (None, {'fields': ('nom', 'type', 'pays',)}),
843 class PosteAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
844 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
845 change_list_template
= "admin/rh/poste/change_list.html"
846 ignore_duplicate_revisions
= True
847 form
= make_ajax_form(rh
.Poste
, {
848 'implantation': 'implantations',
849 'type_poste': 'typepostes',
850 'responsable': 'postes',
851 'valeur_point_min': 'valeurpoints',
852 'valeur_point_max': 'valeurpoints',
854 alphabet_filter
= 'nom'
859 'implantation__zone_administrative__code',
860 'implantation__zone_administrative__nom',
861 'rh_dossiers__employe__id',
862 'rh_dossiers__employe__nom',
863 'rh_dossiers__employe__prenom',
866 '_id', '_apercu', '_nom', '_occupe_par', 'implantation', '_service',
867 '_responsable', 'date_debut', 'date_fin', 'derniere_modification',
871 'implantation__zone_administrative',
875 'type_poste__categorie_emploi',
876 'type_poste__famille_professionnelle',
879 list_display_links
= ('_nom',)
882 ('nom', 'nom_feminin'),
892 'regime_travail_nb_heure_semaine'),
896 'fields': (('local', 'expatrie', 'mise_a_disposition', 'appel'),)}
899 'fields': (('classement_min',
912 ('Comparatifs de rémunération', {
913 'fields': ('devise_comparaison',
914 ('comp_locale_min', 'comp_locale_max'),
915 ('comp_universite_min', 'comp_universite_max'),
916 ('comp_fonctionpub_min', 'comp_fonctionpub_max'),
917 ('comp_ong_min', 'comp_ong_max'),
918 ('comp_autre_min', 'comp_autre_max'))}
921 'fields': ('justification',)}
923 ('Autres Méta-données', {
924 'fields': ('date_debut', 'date_fin')}
928 inlines
= (PosteFinancementInline
,
931 PosteComparaisonInline
,
932 PosteCommentaireInline
, )
934 def lookup_allowed(self
, key
, value
):
936 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
938 'implantation__zone_administrative__code__exact',
939 'implantation__id__exact', 'type_poste__id__exact',
940 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
941 'service__isnull', 'vacant__exact', 'vacant__isnull'
942 ) or super(PosteAdmin
, self
).lookup_allowed(key
, value
)
946 _id
.short_description
= '#'
947 _id
.admin_order_field
= 'id'
949 def _apercu(self
, poste
):
950 view_link
= u
"""<a onclick="return showAddAnotherPopup(this);"
951 title="Aperçu du poste"
953 <img src="%simg/poste-apercu.png" />
955 (reverse('poste_apercu', args
=(poste
.id,)),
956 settings
.STATIC_URL
,)
958 _apercu
.allow_tags
= True
959 _apercu
.short_description
= ''
961 def _nom(self
, poste
):
962 return """<a href="%s">%s</a>""" % \
963 (reverse('admin:rh_poste_change', args
=(poste
.id,)),
965 _nom
.allow_tags
= True
966 _nom
.short_description
= u
'Poste'
967 _nom
.admin_order_field
= 'nom'
969 def _occupe_par(self
, obj
):
970 """Formatte la méthode Poste.occupe_par() pour l'admin"""
972 if obj
.date_fin
is not None and obj
.date_fin
< datetime
.date
.today():
974 employes
= obj
.occupe_par()
978 link
= u
"""<a href='%s'
979 title='Aperçu de l\'employé'
980 onclick='return showAddAnotherPopup(this)'>
981 <img src='%simg/employe-apercu.png' />
983 <a href='%s'>%s</a>""" % \
984 (reverse('employe_apercu', args
=(e
.id,)),
986 reverse('admin:rh_employe_change', args
=(e
.id,)),
989 output
= "\n<br />".join(l
)
991 _occupe_par
.allow_tags
= True
992 _occupe_par
.short_description
= "Occupé par"
994 def _zone_administrative(self
, poste
):
995 return poste
.implantation
.zone_administrative
.code
996 _zone_administrative
.short_description
= 'Zone administrative'
997 _zone_administrative
.admin_order_field
= \
998 'implantation__zone_administrative__code'
1000 def _implantation(self
, poste
):
1001 return poste
.implantation
.nom
1002 _implantation
.short_description
= 'Implantation'
1003 _implantation
.admin_order_field
= 'implantation'
1005 def _service(self
, obj
):
1007 _service
.short_description
= 'Service'
1008 _service
.allow_tags
= True
1010 def _responsable(self
, obj
):
1012 responsable
= u
"""<a href="%s"
1013 onclick="return showAddAnotherPopup(this)">
1014 <img src="%simg/poste-apercu.png"
1015 title="Aperçu du poste" />
1017 <a href="%s">%s [%d]</a>
1019 (reverse('poste_apercu', args
=(obj
.responsable
.id,)),
1020 settings
.STATIC_URL
,
1021 reverse('admin:rh_poste_change', args
=(obj
.responsable
.id,)),
1022 obj
.responsable
.nom
,
1028 dossier
= obj
.responsable
.rh_dossiers
.all() \
1029 .order_by('-date_debut')[0]
1030 employe_id
= dossier
.employe
.id
1031 employe_html
= u
"""<br />
1033 onclick="return showAddAnotherPopup(this)">
1034 <img src="%simg/employe-apercu.png"
1035 title="Aperçu de l'employé">
1037 <a href="%s">%s</a>""" % \
1038 (reverse('employe_apercu', args
=(employe_id
,)),
1039 settings
.STATIC_URL
,
1040 reverse('admin:rh_employe_change', args
=(employe_id
,)),
1045 return "%s %s" % (responsable
, employe_html
)
1046 _responsable
.short_description
= 'Responsable'
1047 _responsable
.allow_tags
= True
1049 def _date_debut(self
, obj
):
1050 return date_format(obj
.date_debut
)
1051 _date_debut
.short_description
= u
'Début'
1052 _date_debut
.admin_order_field
= 'date_debut'
1054 def _date_fin(self
, obj
):
1055 return date_format(obj
.date_fin
)
1056 _date_fin
.short_description
= u
'Fin'
1057 _date_fin
.admin_order_field
= 'date_fin'
1059 def _dae(self
, poste
):
1061 postes_dae
= poste
.postes_dae
.all()
1062 if len(postes_dae
) > 0:
1063 poste_dae
= postes_dae
[0]
1065 u
'<a title="Aperçu du dossier" href="%s" ' \
1066 u
'onclick="return showAddAnotherPopup(this);">' \
1067 u
'<img src="%simg/loupe.png" /></a>' % (reverse(
1068 'poste_consulter', args
=("dae-%s" % poste_dae
.id,)
1069 ), settings
.STATIC_URL
)
1071 _dae
.allow_tags
= True
1072 _dae
.short_description
= u
"DAE"
1074 def save_formset(self
, request
, form
, formset
, change
):
1075 instances
= formset
.save(commit
=False)
1076 for instance
in instances
:
1077 if instance
.__class__
== rh
.PosteCommentaire
:
1078 instance
.owner
= request
.user
1079 instance
.date_creation
= datetime
.datetime
.now()
1084 class ResponsableInline(admin
.TabularInline
):
1085 model
= rh
.ResponsableImplantation
1087 fk_name
= "implantation"
1088 form
= ResponsableInlineForm
1091 class ResponsableImplantationAdmin(BaseAdmin
):
1094 inlines
= (ResponsableInline
, )
1095 list_filter
= ('zone_administrative', 'statut', )
1096 list_display
= ('_zone_administrative', '_nom', 'statut', '_responsable', )
1097 list_display_links
= ('_nom',)
1099 readonly_fields
= ('nom', )
1102 'responsable__employe__id',
1103 'responsable__employe__nom',
1104 'responsable__employe__prenom',
1107 inlines
= (ResponsableInline
, )
1109 def _zone_administrative(self
, obj
):
1110 return obj
.zone_administrative
.code
1111 _zone_administrative
.short_description
= u
"Zone administrative"
1112 _zone_administrative
.admin_order_field
= 'zone_administrative__code'
1114 def _nom(self
, obj
):
1116 _nom
.short_description
= u
"Implantation"
1117 _nom
.admin_order_field
= 'nom'
1119 def _responsable(self
, obj
):
1121 employe
= employe
= obj
.responsable
.employe
1122 except Exception, e
:
1124 u
"<span style='color: red;'>"
1125 u
"Pas de responsable</span><!-- %s -->" % e
1128 dossiers
= employe
.dossiers_encours()
1129 if len(dossiers
) == 0:
1130 return u
"<span style='color: red;'>%s %s </span>" % (
1131 employe
, u
"sans dossier actif")
1134 except Exception, e
:
1135 return u
"<!-- %s -->" % e
1136 _responsable
.allow_tags
= True
1137 _responsable
.short_description
= u
"Responsable"
1138 _responsable
.admin_order_field
= 'responsable__employe__nom'
1140 def has_add_permission(self
, request
=None):
1143 def has_change_permission(self
, request
, obj
=None):
1144 return in_drh_or_admin(request
.user
)
1146 def has_delete_permission(self
, request
, obj
=None):
1150 class ServiceAdminBase(ArchivableAdmin
, DerniereModificationAdmin
, BaseAdmin
):
1151 list_display
= ('nom', 'derniere_modification', '_archive')
1152 list_filter
= ('archive', )
1154 (None, {'fields': ('nom', 'archive')}),
1158 class ServiceAdmin(reversion
.VersionAdmin
, ServiceAdminBase
):
1159 ignore_duplicate_revisions
= True
1162 class ServiceProxyAdmin(ServiceAdminBase
):
1163 list_display
= ('nom', '_organigramme', '_archive', )
1166 def __init__(self
, *args
, **kwargs
):
1167 super(ServiceProxyAdmin
, self
).__init__(*args
, **kwargs
)
1168 self
.list_display_links
= (None, )
1170 def queryset(self
, request
):
1171 return super(ServiceProxyAdmin
, self
).queryset(request
) \
1172 .annotate(num_postes
=Count('rh_postes')) \
1173 .filter(num_postes__gt
=0)
1175 def has_add_permission(self
, obj
):
1178 def has_change_permission(self
, request
, obj
=None):
1179 user_groups
= get_user_groupnames(request
.user
)
1180 if groups
.CORRESPONDANT_RH
in user_groups
or \
1181 groups
.ADMINISTRATEURS
in user_groups
or \
1182 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1183 in_drh_or_admin(request
.user
):
1187 def _organigramme(self
, obj
):
1188 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1189 (reverse('rho_service', args
=(obj
.id,)))
1190 _organigramme
.allow_tags
= True
1191 _organigramme
.short_description
= "Organigramme"
1194 class StatutAdmin(reversion
.VersionAdmin
,
1196 DerniereModificationAdmin
,
1198 ignore_duplicate_revisions
= True
1199 list_display
= ('code', 'nom', 'derniere_modification', '_archive')
1200 list_filter
= ('archive', )
1203 'fields': ('code', 'nom', 'archive'),
1208 class TauxChangeAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
1210 ignore_duplicate_revisions
= True
1211 list_display
= ('taux', 'devise', 'annee', 'derniere_modification')
1212 list_filter
= ('devise',)
1215 'fields': ('taux', 'devise', 'annee', ),
1220 class TypeContratAdmin(reversion
.VersionAdmin
,
1222 DerniereModificationAdmin
,
1224 ignore_duplicate_revisions
= True
1225 list_display
= ('nom', 'nom_long', 'derniere_modification', '_archive')
1226 list_filter
= ('archive', )
1229 'fields': ('nom', 'nom_long', 'archive'),
1234 class TypePosteAdmin(reversion
.VersionAdmin
,
1236 DerniereModificationAdmin
,
1238 ignore_duplicate_revisions
= True
1239 search_fields
= ('nom', 'nom_feminin', )
1240 list_display
= ('nom', 'categorie_emploi',
1241 'derniere_modification', '_archive',)
1242 list_filter
= ('categorie_emploi', 'famille_professionnelle', 'archive')
1246 'nom', 'nom_feminin', 'is_responsable', 'categorie_emploi',
1247 'famille_professionnelle',
1254 class TypeRemunerationAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
1255 DerniereModificationAdmin
, BaseAdmin
):
1256 ignore_duplicate_revisions
= True
1258 'nom', 'type_paiement', 'nature_remuneration',
1259 'derniere_modification', '_archive'
1261 list_filter
= ('archive', )
1265 'nom', 'type_paiement', 'nature_remuneration', 'archive'
1271 class TypeRevalorisationAdmin(reversion
.VersionAdmin
,
1273 DerniereModificationAdmin
,
1275 ignore_duplicate_revisions
= True
1276 list_display
= ('nom', 'derniere_modification', '_archive')
1277 list_filter
= ('archive', )
1279 (None, {'fields': ('nom', 'archive')}),
1283 class ValeurPointAdmin(reversion
.VersionAdmin
,
1284 DerniereModificationAdmin
,
1286 ignore_duplicate_revisions
= True
1288 '_devise_code', '_devise_nom', 'annee', 'implantation',
1289 'valeur', 'derniere_modification'
1291 list_filter
= ('annee', 'devise', 'implantation__zone_administrative', )
1293 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
1296 def queryset(self
, request
):
1297 return super(ValeurPointAdmin
, self
).queryset(request
) \
1298 .select_related('devise', 'implantation')
1300 def _devise_code(self
, obj
):
1301 return obj
.devise
.code
1302 _devise_code
.short_description
= "Code de la devise"
1304 def _devise_nom(self
, obj
):
1305 return obj
.devise
.nom
1306 _devise_nom
.short_description
= "Nom de la devise"
1309 class ImplantationProxyAdmin(BaseAdmin
):
1310 list_display
= ('nom', '_organigramme')
1313 def __init__(self
, *args
, **kwargs
):
1314 super(ImplantationProxyAdmin
, self
).__init__(*args
, **kwargs
)
1315 self
.list_display_links
= (None, )
1317 def has_add_permission(self
, obj
):
1320 def has_change_permission(self
, request
, obj
=None):
1321 user_groups
= get_user_groupnames(request
.user
)
1322 if groups
.CORRESPONDANT_RH
in user_groups
or \
1323 groups
.ADMINISTRATEURS
in user_groups
or \
1324 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1325 in_drh_or_admin(request
.user
):
1329 def _organigramme(self
, obj
):
1330 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1331 reverse('rho_implantation', args
=(obj
.id,))
1333 _organigramme
.allow_tags
= True
1334 _organigramme
.short_description
= "Organigramme"
1337 class RegionProxyAdmin(BaseAdmin
):
1338 list_display
= ('nom', '_organigramme')
1341 def __init__(self
, *args
, **kwargs
):
1342 super(RegionProxyAdmin
, self
).__init__(*args
, **kwargs
)
1343 self
.list_display_links
= (None, )
1345 def has_add_permission(self
, obj
):
1348 def has_change_permission(self
, request
, obj
=None):
1349 user_groups
= get_user_groupnames(request
.user
)
1350 if groups
.CORRESPONDANT_RH
in user_groups
or \
1351 groups
.ADMINISTRATEURS
in user_groups
or \
1352 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1353 in_drh_or_admin(request
.user
):
1357 def _organigramme(self
, obj
):
1358 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1359 reverse('rho_region', args
=(obj
.id,))
1361 _organigramme
.allow_tags
= True
1362 _organigramme
.short_description
= "Organigramme"
1365 class ProfileInline(admin
.StackedInline
):
1366 model
= rh
.UserProfile
1369 class RHUserAdmin(UserAdmin
):
1370 inlines
= list(UserAdmin
.inlines
) + [ProfileInline
]
1373 def _invalidate(modaladmin
, req
, qs
):
1374 qs
.update(valide
=False)
1375 _invalidate
.short_description
= 'Invalider'
1378 def _communique(modaladmin
, req
, qs
):
1379 url_prefix
= ('https://' if req
.is_secure() else 'http://') + (
1380 req
.META
['SERVER_NAME'] if 'SERVER_NAME' in req
.META
else
1381 'localhost') + (':%s' % (req
.META
['SERVER_PORT']) if 'SERVER_PORT'
1382 in req
.META
else '')
1384 types
= [x
[0] for x
in rh
.TYPES_CHANGEMENT
]
1386 # make a list of all possible email types. It is reversed so that
1387 # we start with the largest combinations.
1388 combs
= list(itertools
.chain(*map(
1389 lambda x
: itertools
.combinations(types
, x
),
1390 range(1, len(types
)+1))))
1392 combs
= reversed(combs
)
1394 recipient_list
= ref
.Employe
.objects
.filter(
1395 changement_notifications__in
=
1396 rh
.ChangementPersonnelNotifications
.objects
.all()).distinct()
1407 recipients
= ref
.Employe
.objects
.none()
1409 recipients
= recipient_list
.filter(changement_notifications__type
=t
)
1411 recipient_list
= recipient_list
.exclude(
1412 id__in
=recipients
.values_list('id', flat
=True))
1414 types_dict
= dict(rh
.TYPES_CHANGEMENT
)
1425 ctx
['types'].append({
1426 'bgcolor': thead_colors
[t
],
1427 'name': types_dict
[t
],
1429 'changements': changements
,
1432 for ch
in qs
.filter(type__in
=[t
]):
1433 changements
.append(ch
)
1435 template
= loader
.get_template('email/mouvement_employe.html')
1436 content
= template
.render(Context(ctx
))
1438 sujet
= '[SGRH] ARRIVÉES - DÉPARTS - MOBILITÉS'
1443 settings
.SERVER_EMAIL
,
1444 [x
.courriel
for x
in recipients
],
1446 msg
.content_subtype
= "html"
1451 date_communication
=datetime
.datetime
.now()
1454 _communique
.short_description
= u
'Envoyer aux desinataires prévus.'
1457 class ChangementPersonnelAdmin(admin
.ModelAdmin
):
1465 return obj
.dossier
.__unicode__()
1466 _dossier
.short_description
= u
'Dossier'
1469 'type', _dossier
, 'valide', 'communique', 'date_communication'
1479 admin
.site
.unregister(User
)
1480 admin
.site
.register(User
, RHUserAdmin
)
1482 admin
.site
.register(rh
.Classement
, ClassementAdmin
)
1483 admin
.site
.register(rh
.Devise
, DeviseAdmin
)
1484 admin
.site
.register(rh
.Dossier
, DossierAdmin
)
1485 admin
.site
.register(EmployeProxy
, EmployeProxyAdmin
)
1486 admin
.site
.register(ServiceProxy
, ServiceProxyAdmin
)
1487 admin
.site
.register(rh
.Employe
, EmployeAdmin
)
1488 admin
.site
.register(rh
.CategorieEmploi
, CategorieEmploiAdmin
)
1489 admin
.site
.register(rh
.FamilleProfessionnelle
)
1490 admin
.site
.register(rh
.OrganismeBstg
, OrganismeBstgAdmin
)
1491 admin
.site
.register(rh
.Poste
, PosteAdmin
)
1492 admin
.site
.register(
1493 rh
.ResponsableImplantationProxy
, ResponsableImplantationAdmin
1495 admin
.site
.register(rh
.Service
, ServiceAdmin
)
1496 admin
.site
.register(rh
.Statut
, StatutAdmin
)
1497 admin
.site
.register(rh
.TauxChange
, TauxChangeAdmin
)
1498 admin
.site
.register(rh
.TypeContrat
, TypeContratAdmin
)
1499 admin
.site
.register(rh
.TypePoste
, TypePosteAdmin
)
1500 admin
.site
.register(rh
.TypeRemuneration
, TypeRemunerationAdmin
)
1501 admin
.site
.register(rh
.TypeRevalorisation
, TypeRevalorisationAdmin
)
1502 admin
.site
.register(rh
.ValeurPoint
, ValeurPointAdmin
)
1503 admin
.site
.register(ImplantationProxy
, ImplantationProxyAdmin
)
1504 admin
.site
.register(RegionProxy
, RegionProxyAdmin
)
1505 admin
.site
.register(rh
.ChangementPersonnelNotifications
)
1506 admin
.site
.register(rh
.ChangementPersonnel
, ChangementPersonnelAdmin
)