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
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
.utils
.formats
import date_format
20 from project
import groups
21 from project
.decorators
import in_drh_or_admin
22 from project
.rh
import models
as rh
23 from project
.permissions
import user_gere_obj_de_sa_region
, \
26 user_can_change_obj
, \
29 from project
.rh
.forms
import ContratForm
, AyantDroitForm
, EmployeAdminForm
, \
30 AjaxSelect
, DossierForm
, ResponsableInlineForm
31 from project
.rh
.change_list
import ChangeList
34 def listing_par_defaut(model
, request
):
36 Teste si la requete provient de la même page.
38 if not 'HTTP_REFERER' in request
.META
.keys():
40 referer
= request
.META
['HTTP_REFERER']
41 referer
= "/".join(referer
.split('/')[3:])
42 referer
= "/%s" % referer
.split('?')[0]
43 change_list_view
= 'admin:%s_%s_changelist' % (
44 model
._meta
.app_label
,
45 model
.__name__
.lower(),)
46 return referer
!= reverse(change_list_view
)
49 class BaseAdmin(admin
.ModelAdmin
):
53 'css/admin_custom.css',
54 'jquery-autocomplete/jquery.autocomplete.css',
57 'https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js',
58 'jquery-autocomplete/jquery.autocomplete.min.js',
62 # Admin pour reversion
64 class ArchivableAdmin(admin
.ModelAdmin
):
66 Admin pour les modèles archivables
68 list_filter
= ('archive', )
70 def queryset(self
, request
):
71 return self
.model
.avec_archives
.all()
73 def _archive(self
, obj
):
78 _archive
.short_description
= u
'Archivé'
79 _archive
.admin_order_field
= 'archive'
82 class RegionProxy(ref
.Region
):
83 """ Proxy utilisé pour les organigrammes par région """
87 verbose_name
= u
"Organigramme par région"
88 verbose_name_plural
= u
"Organigramme par région"
91 class ImplantationProxy(ref
.Implantation
):
92 """ Proxy utilisé pour les organigrammes par implantation """
96 verbose_name
= u
"Organigramme par implantations"
97 verbose_name_plural
= u
"Organigramme par implantations"
100 class ServiceProxy(rh
.Service
):
101 """ Proxy utilisé pour les organigrammes par service """
106 verbose_name
= u
"Organigramme par services"
107 verbose_name_plural
= u
"Organigramme par services"
110 class EmployeProxy(rh
.Employe
):
111 """ Proxy utilisé pour les organigrammes des employés """
115 verbose_name
= u
"Organigramme des employés"
116 verbose_name_plural
= u
"Organigramme des employés"
119 class DateRangeMixin(object):
120 prefixe_recherche_temporelle
= ""
122 def get_changelist(self
, request
, **kwargs
):
124 On filtre par défaut sur les items 'actifs'.
125 Le changelist plug le filtrage temporel.
127 if listing_par_defaut(self
.model
, request
):
128 params
= request
.GET
.copy()
129 params
.update({'statut': 'Actif'})
134 # Override of the InlineModelAdmin to support the link in the tabular inline
135 class LinkedInline(admin
.options
.InlineModelAdmin
):
136 template
= "admin/linked.html"
137 admin_model_path
= None
139 def __init__(self
, *args
):
140 super(LinkedInline
, self
).__init__(*args
)
141 if self
.admin_model_path
is None:
142 self
.admin_model_path
= self
.model
.__name__
.lower()
145 class ProtectRegionMixin(object):
147 def changelist_view(self
, request
, extra_context
=None):
149 On filtre par défaut sur la ZA du user connecté
151 if listing_par_defaut(self
.model
, request
):
152 if user_gere_obj_de_sa_region(request
.user
):
153 params
= request
.GET
.copy()
154 zones
= groups
.get_zones_from_user(request
.user
)
155 prefix_za
= "%s__in" % self
.model
.prefix_implantation
156 params
.update({prefix_za
: zones
})
158 return super(ProtectRegionMixin
, self
) \
159 .changelist_view(request
, extra_context
)
161 def queryset(self
, request
):
162 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
164 if in_drh_or_admin(request
.user
):
167 if user_gere_obj_de_sa_region(request
.user
):
168 zones
= groups
.get_zones_from_user(request
.user
)
169 qkey
= '%s__in' % self
.model
.prefix_implantation
170 q
= Q(**{qkey
: zones
})
171 qs
= qs
.filter(q
).distinct()
175 def has_add_permission(self
, request
):
176 return user_can_add_obj(request
.user
)
178 def has_change_permission(self
, request
, obj
=None):
180 return user_can_list_obj(request
.user
)
182 return user_can_change_obj(request
.user
, obj
)
184 def has_delete_permission(self
, request
, obj
=None):
185 return user_can_delete_obj(request
.user
, obj
) if obj
else True
188 class DerniereModificationAdmin(admin
.ModelAdmin
):
190 def queryset(self
, request
):
191 qs
= super(DerniereModificationAdmin
, self
).queryset(request
)
192 ct
= ContentType
.objects
.get_for_model(self
.model
)
193 db_table
= self
.model
._meta
.db_table
194 pk
= self
.model
._meta
.pk
.column
195 return qs
.extra(select
={
197 "SELECT action_time FROM django_admin_log "
198 "WHERE content_type_id = %d AND object_id = %s.%s "
199 "ORDER BY action_time DESC "
200 "LIMIT 1" % (ct
.id, db_table
, pk
),
204 "INNER JOIN django_admin_log l ON l.user_id = u.id "
205 "WHERE l.content_type_id = %d AND object_id = %s.%s "
206 "ORDER BY action_time DESC "
207 "LIMIT 1" % (ct
.id, db_table
, pk
),
210 def derniere_modification(self
, obj
):
212 if obj
.date_modification
:
213 text
+= obj
.date_modification
.strftime('%d-%m-%Y %H:%M')
214 if obj
.user_modification
:
215 text
+= ' par ' + obj
.user_modification
217 derniere_modification
.short_description
= u
'dernière modification'
218 derniere_modification
.admin_order_field
= 'date_modification'
223 class CommentaireInlineForm(forms
.ModelForm
):
225 def save(self
, commit
=True):
227 # Hack: reversion.VersionAdmin ne sauvegarde pas les champs qui ne
228 # sont pas explicitement dans le formulaire. Il plante cependant
229 # leur valeur dans `self.initial`. Ceci est un peu fragile. Si
230 # c'est possible, il serait plus approprié que Reversion se rende
231 # compte qu'il manque des champs.
232 instance
= super(CommentaireInlineForm
, self
).save(commit
=False)
233 if instance
.owner_id
is None and 'owner' in self
.initial
:
234 instance
.owner_id
= self
.initial
['owner']
235 if instance
.date_creation
is None and 'date_creation' in self
.initial
:
236 instance
.date_creation
= self
.initial
['date_creation']
243 class ReadOnlyInlineMixin(object):
245 def get_readonly_fields(self
, request
, obj
=None):
246 return [f
.name
for f
in self
.model
._meta
.fields
]
249 class AyantDroitInline(admin
.StackedInline
):
250 model
= rh
.AyantDroit
251 form
= AyantDroitForm
258 ('nom_affichage', 'genre'),
266 class AyantDroitCommentaireInline(admin
.TabularInline
):
267 readonly_fields
= ('owner',)
268 model
= rh
.AyantDroitCommentaire
270 form
= CommentaireInlineForm
273 class ContratInline(admin
.TabularInline
):
279 class DossierROInline(ReadOnlyInlineMixin
, LinkedInline
):
280 template
= "admin/rh/dossier/linked.html"
284 fields
= ('poste', 'date_debut', 'date_fin', )
286 def has_add_permission(self
, request
=None):
289 def has_change_permission(self
, request
, obj
=None):
292 def has_delete_permission(self
, request
, obj
=None):
296 class DossierCommentaireInline(admin
.TabularInline
):
297 readonly_fields
= ('owner',)
298 model
= rh
.DossierCommentaire
300 form
= CommentaireInlineForm
303 class DossierPieceInline(admin
.TabularInline
):
304 model
= rh
.DossierPiece
308 class EmployeInline(admin
.TabularInline
):
312 class EmployeCommentaireInline(admin
.TabularInline
):
313 readonly_fields
= ('owner',)
314 model
= rh
.EmployeCommentaire
316 form
= CommentaireInlineForm
319 class EmployePieceInline(admin
.TabularInline
):
320 model
= rh
.EmployePiece
324 class PosteCommentaireInline(admin
.TabularInline
):
325 readonly_fields
= ('owner',)
326 model
= rh
.PosteCommentaire
328 form
= CommentaireInlineForm
331 class PosteFinancementInline(admin
.TabularInline
):
332 model
= rh
.PosteFinancement
335 class PostePieceInline(admin
.TabularInline
):
336 model
= rh
.PostePiece
339 class RemunerationInline(admin
.TabularInline
):
340 model
= rh
.Remuneration
344 class RemunerationROInline(ReadOnlyInlineMixin
, RemunerationInline
):
348 class TypePosteInline(admin
.TabularInline
):
352 class PosteComparaisonInline(admin
.TabularInline
):
353 model
= rh
.PosteComparaison
356 class ClassementAdmin(reversion
.VersionAdmin
,
358 DerniereModificationAdmin
,
360 ignore_duplicate_revisions
= True
361 list_display
= ('_classement', 'derniere_modification', '_archive')
362 list_filter
= ('archive', )
366 'degre', 'coefficient', 'archive')}),
369 def _classement(self
, obj
):
371 _classement
.short_description
= u
"Classement"
374 class DeviseAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
375 DerniereModificationAdmin
, BaseAdmin
):
376 ignore_duplicate_revisions
= True
378 'code', 'nom', '_archive', 'derniere_modification',
380 list_filter
= ('archive', )
382 (None, {'fields': ('code', 'nom', 'archive', )}),
386 class DossierAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
387 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
388 change_list_template
= "admin/rh/dossier/change_list.html"
389 ignore_duplicate_revisions
= True
390 alphabet_filter
= 'employe__nom'
398 'poste__nom_feminin',
399 'poste__implantation__nom',
407 '_zone_administrative',
411 'derniere_modification',
414 list_display_links
= ('_nom',)
416 'poste__implantation__zone_administrative',
417 'poste__implantation',
418 'poste__type_poste__categorie_emploi',
420 'rh_contrats__type_contrat',
423 inlines
= (DossierPieceInline
, ContratInline
,
425 DossierCommentaireInline
,
435 'organisme_bstg',)}),
440 'remplacement_de', )}),
444 ('regime_travail', 'regime_travail_nb_heure_semaine'),)}),
445 ('Occupation du Poste par cet Employe', {
446 'fields': (('date_debut', 'date_fin'), )}
449 form
= make_ajax_form(rh
.Dossier
, {
450 'employe': 'employes',
452 'remplacement_de': 'dossiers',
453 }, superclass
=DossierForm
)
455 def lookup_allowed(self
, key
, value
):
457 'employe__nom__istartswith',
458 'poste__implantation__zone_administrative__code__exact',
459 'poste__implantation__zone_administrative__in',
460 'poste__implantation__id__exact',
461 'poste__type_poste__id__exact',
462 'poste__type_poste__categorie_emploi__id__exact',
463 'rh_contrats__type_contrat__id__exact',
471 _id
.short_description
= u
"#"
472 _id
.admin_order_field
= "id"
474 def _apercu(self
, d
):
475 apercu_link
= u
"""<a title="Aperçu du dossier"
476 onclick="return showAddAnotherPopup(this);"
478 <img src="%simg/dossier-apercu.png" />
480 (reverse('dossier_apercu', args
=(d
.id,)),
484 _apercu
.allow_tags
= True
485 _apercu
.short_description
= u
""
489 _nom
.allow_tags
= True
490 _nom
.short_description
= u
"Dossier"
492 def _employe(self
, obj
):
493 employe
= obj
.employe
494 view_link
= reverse('employe_apercu', args
=(employe
.id,))
495 edit_link
= reverse('admin:rh_employe_change', args
=(employe
.id,))
497 view
= u
"""<a href="%s"
498 title="Aperçu l'employé"
499 onclick="return showAddAnotherPopup(this);">
500 <img src="%simg/employe-apercu.png" />
501 </a>""" % (view_link
, settings
.STATIC_URL
,)
502 return u
"""%s<a href='%s' style="%s;">%s</a>""" % \
503 (view
, edit_link
, style
, employe
)
504 _employe
.allow_tags
= True
505 _employe
.short_description
= u
"Employé"
506 _employe
.admin_order_field
= "employe__nom"
508 def _poste(self
, dossier
):
509 link
= u
"""<a title="Aperçu du poste"
510 onclick="return showAddAnotherPopup(this);"
511 href='%s'><img src="%simg/poste-apercu.png" />
513 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
514 (reverse('poste_apercu', args
=(dossier
.poste
.id,)),
516 reverse('admin:rh_poste_change', args
=(dossier
.poste
.id,)),
521 _poste
.allow_tags
= True
522 _poste
.short_description
= u
'Poste'
523 _poste
.admin_order_field
= 'poste__nom'
525 def _zone_administrative(self
, obj
):
526 return obj
.poste
.implantation
.zone_administrative
.code
527 _zone_administrative
.short_description
= u
"Zone administrative"
528 _zone_administrative
.admin_order_field
= \
529 'poste__implantation__zone_administrative__code'
531 def _implantation(self
, obj
):
532 return obj
.poste
.implantation
.nom
533 _implantation
.short_description
= u
"Implantation"
534 _implantation
.admin_order_field
= 'poste__implantation__nom'
536 def _date_debut(self
, obj
):
537 return date(obj
.date_debut
)
539 _date_debut
.short_description
= u
'Début'
540 _date_debut
.admin_order_field
= 'date_debut'
542 def _date_fin(self
, obj
):
543 return date(obj
.date_fin
)
544 _date_fin
.short_description
= u
'Fin'
545 _date_fin
.admin_order_field
= 'date_fin'
547 def _date_modification(self
, obj
):
548 return date(obj
.date_modification
) \
549 if obj
.date_modification
is not None else "(aucune)"
550 _date_modification
.short_description
= u
'date modification'
551 _date_modification
.admin_order_field
= 'date_modification'
555 dossiers_dae
= d
.dossiers_dae
.all()
556 if len(dossiers_dae
) > 0:
557 dossier_dae
= dossiers_dae
[0]
558 apercu_link
= u
"""<a title="Aperçu du dossier"
559 onclick="return showAddAnotherPopup(this);"
561 <img src="%simg/loupe.png" />
563 (reverse('embauche_consulter', args
=(dossier_dae
.id,)),
567 _dae
.allow_tags
= True
568 _dae
.short_description
= u
"DAE"
570 def save_formset(self
, request
, form
, formset
, change
):
571 instances
= formset
.save(commit
=False)
572 for instance
in instances
:
573 if instance
.__class__
== rh
.DossierCommentaire
:
574 instance
.owner
= request
.user
575 instance
.date_creation
= datetime
.datetime
.now()
579 class EmployeAdminBase(DateRangeMixin
, ProtectRegionMixin
,
580 DerniereModificationAdmin
, BaseAdmin
):
581 prefixe_recherche_temporelle
= "rh_dossiers__"
582 alphabet_filter
= 'nom'
583 DEFAULT_ALPHABET
= u
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
585 'id', 'nom', 'prenom', 'nom_affichage',
586 'rh_dossiers__poste__nom',
587 'rh_dossiers__poste__nom_feminin'
590 form
= EmployeAdminForm
592 '_id', '_apercu', '_nom', '_dossiers_postes',
593 #'_zone_administrative',
596 'derniere_modification'
598 list_display_links
= ('_nom',)
600 'rh_dossiers__poste__implantation__zone_administrative',
601 'rh_dossiers__poste__implantation', 'nb_postes'
604 AyantDroitInline
, DossierROInline
, EmployePieceInline
,
605 EmployeCommentaireInline
611 ('nom_affichage', 'genre'),
616 ('Informations personnelles', {
617 'fields': ('situation_famille', 'date_entree', )
619 ('Coordonnées personnelles', {
621 ('tel_domicile', 'tel_cellulaire'),
622 ('adresse', 'ville'),
623 ('code_postal', 'province'),
632 _id
.short_description
= u
"#"
633 _id
.admin_order_field
= "id"
635 def _apercu(self
, obj
):
636 return u
"""<a title="Aperçu de l'employé"
637 onclick="return showAddAnotherPopup(this);"
639 <img src="%simg/employe-apercu.png" />
641 (reverse('employe_apercu', args
=(obj
.id,)), settings
.STATIC_URL
)
642 _apercu
.allow_tags
= True
643 _apercu
.short_description
= u
""
646 edit_link
= reverse('admin:rh_employe_change', args
=(obj
.id,))
647 return u
"""<a href='%s'><strong>%s</strong></a>""" % \
648 (edit_link
, "%s %s" % (obj
.nom
.upper(), obj
.prenom
))
649 _nom
.allow_tags
= True
650 _nom
.short_description
= u
"Employé"
651 _nom
.admin_order_field
= "nom"
653 def _zone_administrative(self
, obj
):
655 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
656 zone
= d
.poste
.implantation
.zone_administrative
.code
660 _zone_administrative
.short_description
= u
"Zone administrative"
662 def _implantation(self
, obj
):
664 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
665 implantation
= d
.poste
.implantation
.nom
669 _implantation
.short_description
= u
"Implantation"
671 def _dossiers_postes(self
, obj
):
673 for d
in obj
.rh_dossiers
.all().order_by('-date_debut'):
676 if d
.date_fin
is not None and d
.date_fin
< datetime
.date
.today():
677 link_style
= u
' style="color:#666;"'
678 list_style
= u
' style="color:grey;"'
680 dossier
= u
"""<a title="Aperçu du dossier"
682 onclick="return showAddAnotherPopup(this);"
683 title="Aperçu du dossier">
684 <img src="%simg/dossier-apercu.png" />
686 <a href="%s"%s>Dossier</a>
688 (reverse('dossier_apercu', args
=(d
.id,)),
690 reverse('admin:rh_dossier_change', args
=(d
.id,)),
693 poste
= u
"""<a title="Aperçu du poste"
695 onclick="return showAddAnotherPopup(this);"
696 title="Aperçu du poste">
697 <img src="%simg/poste-apercu.png" />
699 <a href="%s"%s>%s [%d]</a>
701 (reverse('poste_apercu', args
=(d
.poste
.id,)),
703 reverse('admin:rh_poste_change', args
=(d
.poste
.id,)),
708 link
= u
"""<li%s>%s %s</li>""" % \
709 (list_style
, dossier
, poste
)
712 return "<ul>%s</ul>" % "\n".join(l
)
713 _dossiers_postes
.allow_tags
= True
714 _dossiers_postes
.short_description
= u
"Dossiers et postes"
716 def _date_modification(self
, obj
):
717 return date(obj
.date_modification
) \
718 if obj
.date_modification
is not None else "(aucune)"
719 _date_modification
.short_description
= u
'date modification'
720 _date_modification
.admin_order_field
= 'date_modification'
722 def queryset(self
, request
):
723 qs
= super(EmployeAdminBase
, self
).queryset(request
)
724 return qs
.select_related(depth
=1).order_by('nom')
726 def save_formset(self
, request
, form
, formset
, change
):
727 instances
= formset
.save(commit
=False)
728 for instance
in instances
:
729 if instance
.__class__
== rh
.EmployeCommentaire
:
730 instance
.owner
= request
.user
731 instance
.date_creation
= datetime
.datetime
.now()
735 class EmployeAdmin(reversion
.VersionAdmin
, EmployeAdminBase
):
736 change_list_template
= "admin/rh/employe/change_list.html"
737 ignore_duplicate_revisions
= True
740 class EmployeProxyAdmin(EmployeAdminBase
):
741 list_display
= ('_id', '_apercu', '_nom', '_organigramme')
745 def __init__(self
, *args
, **kwargs
):
746 super(EmployeProxyAdmin
, self
).__init__(*args
, **kwargs
)
747 self
.list_display_links
= (None, )
749 def queryset(self
, request
):
750 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
752 if in_drh_or_admin(request
.user
) or \
753 user_gere_obj_de_sa_region(request
.user
):
758 def has_add_permission(self
, obj
):
761 def has_change_permission(self
, request
, obj
=None):
762 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
763 if groups
.CORRESPONDANT_RH
in user_groups
or \
764 groups
.ADMINISTRATEURS
in user_groups
or \
765 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
766 in_drh_or_admin(request
.user
):
770 def _organigramme(self
, obj
):
772 for d
in rh
.Dossier
.objects
.filter(
773 Q(date_fin__gt
=datetime
.date
.today()) |
Q(date_fin
=None),
774 Q(date_debut__lt
=datetime
.date
.today()) |
Q(date_debut
=None),
778 u
'Organigramme, niveau: ' \
779 u
'<input type="text" id="level_%s" ' \
780 u
'style="width:30px;height:15px;" /> ' \
781 u
'<input type="button" value="Générer" ' \
782 u
"""onclick="window.location='%s' + """ \
783 u
"""document.getElementById('level_%s').value" />""" % (
785 reverse('rho_employe_sans_niveau', args
=(d
.poste
.id,)),
788 link
= u
"""<li>%s [%s]:<br />%s</li>""" % (
794 return "<ul>%s</ul>" % "\n".join(l
)
796 _organigramme
.allow_tags
= True
797 _organigramme
.short_description
= "Organigramme"
800 class CategorieEmploiAdmin(reversion
.VersionAdmin
,
801 DerniereModificationAdmin
, BaseAdmin
):
802 ignore_duplicate_revisions
= True
803 list_display
= ('nom', 'derniere_modification')
804 inlines
= (TypePosteInline
,)
806 (None, {'fields': ('nom', )}),
810 class OrganismeBstgAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
812 ignore_duplicate_revisions
= True
813 search_fields
= ('nom',)
814 list_display
= ('nom', 'type', 'pays', 'derniere_modification')
815 list_filter
= ('type', )
816 inlines
= (DossierROInline
,)
818 (None, {'fields': ('nom', 'type', 'pays',)}),
822 class PosteAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
823 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
824 change_list_template
= "admin/rh/poste/change_list.html"
825 ignore_duplicate_revisions
= True
826 form
= make_ajax_form(rh
.Poste
, {
827 'implantation': 'implantations',
828 'type_poste': 'typepostes',
829 'responsable': 'postes',
830 'valeur_point_min': 'valeurpoints',
831 'valeur_point_max': 'valeurpoints',
833 alphabet_filter
= 'nom'
838 'implantation__zone_administrative__code',
839 'implantation__zone_administrative__nom',
840 'rh_dossiers__employe__id',
841 'rh_dossiers__employe__nom',
842 'rh_dossiers__employe__prenom',
845 '_id', '_apercu', '_nom', '_occupe_par', 'implantation', '_service',
846 '_responsable', 'date_debut', 'date_fin', 'derniere_modification',
850 'implantation__zone_administrative',
854 'type_poste__categorie_emploi',
855 'type_poste__famille_professionnelle',
858 list_display_links
= ('_nom',)
861 ('nom', 'nom_feminin'),
871 'regime_travail_nb_heure_semaine'),
875 'fields': (('local', 'expatrie', 'mise_a_disposition', 'appel'),)}
878 'fields': (('classement_min',
891 ('Comparatifs de rémunération', {
892 'fields': ('devise_comparaison',
893 ('comp_locale_min', 'comp_locale_max'),
894 ('comp_universite_min', 'comp_universite_max'),
895 ('comp_fonctionpub_min', 'comp_fonctionpub_max'),
896 ('comp_ong_min', 'comp_ong_max'),
897 ('comp_autre_min', 'comp_autre_max'))}
900 'fields': ('justification',)}
902 ('Autres Méta-données', {
903 'fields': ('date_debut', 'date_fin')}
907 inlines
= (PosteFinancementInline
,
910 PosteComparaisonInline
,
911 PosteCommentaireInline
, )
913 def lookup_allowed(self
, key
, value
):
915 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
917 'implantation__zone_administrative__code__exact',
918 'implantation__id__exact', 'type_poste__id__exact',
919 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
920 'service__isnull', 'vacant__exact', 'vacant__isnull'
921 ) or super(PosteAdmin
, self
).lookup_allowed(key
, value
)
925 _id
.short_description
= '#'
926 _id
.admin_order_field
= 'id'
928 def _apercu(self
, poste
):
929 view_link
= u
"""<a onclick="return showAddAnotherPopup(this);"
930 title="Aperçu du poste"
932 <img src="%simg/poste-apercu.png" />
934 (reverse('poste_apercu', args
=(poste
.id,)),
935 settings
.STATIC_URL
,)
937 _apercu
.allow_tags
= True
938 _apercu
.short_description
= ''
940 def _nom(self
, poste
):
941 return """<a href="%s">%s</a>""" % \
942 (reverse('admin:rh_poste_change', args
=(poste
.id,)),
944 _nom
.allow_tags
= True
945 _nom
.short_description
= u
'Poste'
946 _nom
.admin_order_field
= 'nom'
948 def _occupe_par(self
, obj
):
949 """Formatte la méthode Poste.occupe_par() pour l'admin"""
951 if obj
.date_fin
is not None and obj
.date_fin
< datetime
.date
.today():
953 employes
= obj
.occupe_par()
957 link
= u
"""<a href='%s'
958 title='Aperçu de l\'employé'
959 onclick='return showAddAnotherPopup(this)'>
960 <img src='%simg/employe-apercu.png' />
962 <a href='%s'>%s</a>""" % \
963 (reverse('employe_apercu', args
=(e
.id,)),
965 reverse('admin:rh_employe_change', args
=(e
.id,)),
968 output
= "\n<br />".join(l
)
970 _occupe_par
.allow_tags
= True
971 _occupe_par
.short_description
= "Occupé par"
973 def _zone_administrative(self
, poste
):
974 return poste
.implantation
.zone_administrative
.code
975 _zone_administrative
.short_description
= 'Zone administrative'
976 _zone_administrative
.admin_order_field
= \
977 'implantation__zone_administrative__code'
979 def _implantation(self
, poste
):
980 return poste
.implantation
.nom
981 _implantation
.short_description
= 'Implantation'
982 _implantation
.admin_order_field
= 'implantation'
984 def _service(self
, obj
):
986 _service
.short_description
= 'Service'
987 _service
.allow_tags
= True
989 def _responsable(self
, obj
):
991 responsable
= u
"""<a href="%s"
992 onclick="return showAddAnotherPopup(this)">
993 <img src="%simg/poste-apercu.png"
994 title="Aperçu du poste" />
996 <a href="%s">%s [%d]</a>
998 (reverse('poste_apercu', args
=(obj
.responsable
.id,)),
1000 reverse('admin:rh_poste_change', args
=(obj
.responsable
.id,)),
1001 obj
.responsable
.nom
,
1007 dossier
= obj
.responsable
.rh_dossiers
.all() \
1008 .order_by('-date_debut')[0]
1009 employe_id
= dossier
.employe
.id
1010 employe_html
= u
"""<br />
1012 onclick="return showAddAnotherPopup(this)">
1013 <img src="%simg/employe-apercu.png"
1014 title="Aperçu de l'employé">
1016 <a href="%s">%s</a>""" % \
1017 (reverse('employe_apercu', args
=(employe_id
,)),
1018 settings
.STATIC_URL
,
1019 reverse('admin:rh_employe_change', args
=(employe_id
,)),
1024 return "%s %s" % (responsable
, employe_html
)
1025 _responsable
.short_description
= 'Responsable'
1026 _responsable
.allow_tags
= True
1028 def _date_debut(self
, obj
):
1029 return date_format(obj
.date_debut
)
1030 _date_debut
.short_description
= u
'Début'
1031 _date_debut
.admin_order_field
= 'date_debut'
1033 def _date_fin(self
, obj
):
1034 return date_format(obj
.date_fin
)
1035 _date_fin
.short_description
= u
'Fin'
1036 _date_fin
.admin_order_field
= 'date_fin'
1038 def _dae(self
, poste
):
1040 postes_dae
= poste
.postes_dae
.all()
1041 if len(postes_dae
) > 0:
1042 poste_dae
= postes_dae
[0]
1044 u
'<a title="Aperçu du dossier" href="%s" ' \
1045 u
'onclick="return showAddAnotherPopup(this);">' \
1046 u
'<img src="%simg/loupe.png" /></a>' % (reverse(
1047 'poste_consulter', args
=("dae-%s" % poste_dae
.id,)
1048 ), settings
.STATIC_URL
)
1050 _dae
.allow_tags
= True
1051 _dae
.short_description
= u
"DAE"
1053 def save_formset(self
, request
, form
, formset
, change
):
1054 instances
= formset
.save(commit
=False)
1055 for instance
in instances
:
1056 if instance
.__class__
== rh
.PosteCommentaire
:
1057 instance
.owner
= request
.user
1058 instance
.date_creation
= datetime
.datetime
.now()
1063 class ResponsableInline(admin
.TabularInline
):
1064 model
= rh
.ResponsableImplantation
1066 fk_name
= "implantation"
1067 form
= ResponsableInlineForm
1070 class ResponsableImplantationAdmin(BaseAdmin
):
1073 inlines
= (ResponsableInline
, )
1074 list_filter
= ('zone_administrative', 'statut', )
1075 list_display
= ('_zone_administrative', '_nom', 'statut', '_responsable', )
1076 list_display_links
= ('_nom',)
1078 readonly_fields
= ('nom', )
1081 'responsable__employe__id',
1082 'responsable__employe__nom',
1083 'responsable__employe__prenom',
1086 inlines
= (ResponsableInline
, )
1088 def _zone_administrative(self
, obj
):
1089 return obj
.zone_administrative
.code
1090 _zone_administrative
.short_description
= u
"Zone administrative"
1091 _zone_administrative
.admin_order_field
= 'zone_administrative__code'
1093 def _nom(self
, obj
):
1095 _nom
.short_description
= u
"Implantation"
1096 _nom
.admin_order_field
= 'nom'
1098 def _responsable(self
, obj
):
1100 employe
= employe
= obj
.responsable
.employe
1101 except Exception, e
:
1103 u
"<span style='color: red;'>"
1104 u
"Pas de responsable</span><!-- %s -->" % e
1107 dossiers
= employe
.dossiers_encours()
1108 if len(dossiers
) == 0:
1109 return u
"<span style='color: red;'>%s %s </span>" % (
1110 employe
, u
"sans dossier actif")
1113 except Exception, e
:
1114 return u
"<!-- %s -->" % e
1115 _responsable
.allow_tags
= True
1116 _responsable
.short_description
= u
"Responsable"
1117 _responsable
.admin_order_field
= 'responsable__employe__nom'
1119 def has_add_permission(self
, request
=None):
1122 def has_change_permission(self
, request
, obj
=None):
1123 return in_drh_or_admin(request
.user
)
1125 def has_delete_permission(self
, request
, obj
=None):
1129 class ServiceAdminBase(ArchivableAdmin
, DerniereModificationAdmin
, BaseAdmin
):
1130 list_display
= ('nom', '_archive', 'derniere_modification')
1131 list_filter
= ('archive', )
1133 (None, {'fields': ('nom', 'archive')}),
1137 class ServiceAdmin(reversion
.VersionAdmin
, ServiceAdminBase
):
1138 ignore_duplicate_revisions
= True
1141 class ServiceProxyAdmin(ServiceAdminBase
):
1142 list_display
= ('nom', '_organigramme', '_archive', )
1145 def __init__(self
, *args
, **kwargs
):
1146 super(ServiceProxyAdmin
, self
).__init__(*args
, **kwargs
)
1147 self
.list_display_links
= (None, )
1149 def queryset(self
, request
):
1150 return super(ServiceProxyAdmin
, self
).queryset(request
) \
1151 .annotate(num_postes
=Count('rh_postes')) \
1152 .filter(num_postes__gt
=0)
1154 def has_add_permission(self
, obj
):
1157 def has_change_permission(self
, request
, obj
=None):
1158 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1159 if groups
.CORRESPONDANT_RH
in user_groups
or \
1160 groups
.ADMINISTRATEURS
in user_groups
or \
1161 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1162 in_drh_or_admin(request
.user
):
1166 def _organigramme(self
, obj
):
1167 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1168 (reverse('rho_service', args
=(obj
.id,)))
1169 _organigramme
.allow_tags
= True
1170 _organigramme
.short_description
= "Organigramme"
1173 class StatutAdmin(reversion
.VersionAdmin
,
1175 DerniereModificationAdmin
,
1177 ignore_duplicate_revisions
= True
1178 list_display
= ('code', 'nom', 'derniere_modification', '_archive')
1179 list_filter
= ('archive', )
1182 'fields': ('code', 'nom', 'archive'),
1187 class TauxChangeAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
1189 ignore_duplicate_revisions
= True
1190 list_display
= ('taux', 'devise', 'annee', 'derniere_modification')
1191 list_filter
= ('devise',)
1194 'fields': ('taux', 'devise', 'annee', ),
1199 class TypeContratAdmin(reversion
.VersionAdmin
,
1201 DerniereModificationAdmin
,
1203 ignore_duplicate_revisions
= True
1204 list_display
= ('nom', 'nom_long', 'derniere_modification', '_archive')
1205 list_filter
= ('archive', )
1208 'fields': ('nom', 'nom_long', 'archive'),
1213 class TypePosteAdmin(reversion
.VersionAdmin
,
1215 DerniereModificationAdmin
,
1217 ignore_duplicate_revisions
= True
1218 search_fields
= ('nom', 'nom_feminin', )
1219 list_display
= ('nom', 'categorie_emploi',
1220 'derniere_modification', '_archive',)
1221 list_filter
= ('categorie_emploi', 'famille_professionnelle', 'archive')
1225 'nom', 'nom_feminin', 'is_responsable', 'categorie_emploi',
1226 'famille_professionnelle',
1233 class TypeRemunerationAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
1234 DerniereModificationAdmin
, BaseAdmin
):
1235 ignore_duplicate_revisions
= True
1237 'nom', 'type_paiement', 'nature_remuneration', '_archive',
1238 'derniere_modification'
1240 list_filter
= ('archive', )
1244 'nom', 'type_paiement', 'nature_remuneration', 'archive'
1250 class TypeRevalorisationAdmin(reversion
.VersionAdmin
,
1252 DerniereModificationAdmin
,
1254 ignore_duplicate_revisions
= True
1255 list_display
= ('nom', 'derniere_modification', '_archive')
1256 list_filter
= ('archive', )
1258 (None, {'fields': ('nom', 'archive')}),
1262 class ValeurPointAdmin(reversion
.VersionAdmin
,
1263 DerniereModificationAdmin
,
1265 ignore_duplicate_revisions
= True
1267 '_devise_code', '_devise_nom', 'annee', 'implantation',
1268 'valeur', 'derniere_modification'
1270 list_filter
= ('annee', 'devise', 'implantation__zone_administrative', )
1272 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
1275 def queryset(self
, request
):
1276 return super(ValeurPointAdmin
, self
).queryset(request
) \
1277 .select_related('devise', 'implantation')
1279 def _devise_code(self
, obj
):
1280 return obj
.devise
.code
1281 _devise_code
.short_description
= "Code de la devise"
1283 def _devise_nom(self
, obj
):
1284 return obj
.devise
.nom
1285 _devise_nom
.short_description
= "Nom de la devise"
1288 class ImplantationProxyAdmin(BaseAdmin
):
1289 list_display
= ('nom', '_organigramme')
1292 def __init__(self
, *args
, **kwargs
):
1293 super(ImplantationProxyAdmin
, self
).__init__(*args
, **kwargs
)
1294 self
.list_display_links
= (None, )
1296 def has_add_permission(self
, obj
):
1299 def has_change_permission(self
, request
, obj
=None):
1300 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1301 if groups
.CORRESPONDANT_RH
in user_groups
or \
1302 groups
.ADMINISTRATEURS
in user_groups
or \
1303 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1304 in_drh_or_admin(request
.user
):
1308 def _organigramme(self
, obj
):
1309 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1310 reverse('rho_implantation', args
=(obj
.id,))
1312 _organigramme
.allow_tags
= True
1313 _organigramme
.short_description
= "Organigramme"
1316 class RegionProxyAdmin(BaseAdmin
):
1317 list_display
= ('nom', '_organigramme')
1320 def __init__(self
, *args
, **kwargs
):
1321 super(RegionProxyAdmin
, self
).__init__(*args
, **kwargs
)
1322 self
.list_display_links
= (None, )
1324 def has_add_permission(self
, obj
):
1327 def has_change_permission(self
, request
, obj
=None):
1328 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1329 if groups
.CORRESPONDANT_RH
in user_groups
or \
1330 groups
.ADMINISTRATEURS
in user_groups
or \
1331 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1332 in_drh_or_admin(request
.user
):
1336 def _organigramme(self
, obj
):
1337 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1338 reverse('rho_region', args
=(obj
.id,))
1340 _organigramme
.allow_tags
= True
1341 _organigramme
.short_description
= "Organigramme"
1344 class ProfileInline(admin
.StackedInline
):
1345 model
= rh
.UserProfile
1348 class RHUserAdmin(UserAdmin
):
1349 inlines
= list(UserAdmin
.inlines
) + [ProfileInline
]
1352 def _invalidate(modaladmin
, req
, qs
):
1353 qs
.update(valide
=False)
1354 _invalidate
.short_description
= 'Invalider'
1357 def _communique(modaladmin
, req
, qs
):
1358 url_prefix
= ('https://' if req
.is_secure() else 'http://') + (
1359 req
.META
['SERVER_NAME'] if 'SERVER_NAME' in req
.META
else
1360 'localhost') + (':%s' % (req
.META
['SERVER_PORT']) if 'SERVER_PORT'
1361 in req
.META
else '')
1363 types
= [x
[0] for x
in rh
.TYPES_CHANGEMENT
]
1365 # make a list of all possible email types. It is reversed so that
1366 # we start with the largest combinations.
1367 combs
= list(itertools
.chain(*map(
1368 lambda x
: itertools
.combinations(types
, x
),
1369 range(1, len(types
)+1))))
1371 combs
= reversed(combs
)
1373 recipient_list
= ref
.Employe
.objects
.filter(
1374 changement_notifications__in
=
1375 rh
.ChangementPersonnelNotifications
.objects
.all()).distinct()
1379 # qf c'est pour trouver les recipients.
1382 [Q(**{'changement_notifications__type': t
}) for t
in comb
]
1384 recipients
= recipient_list
.filter(qf
)
1386 recipient_list
= recipient_list
.exclude(
1387 id__in
=recipients
.values_list('id', flat
=True))
1389 types_dict
= dict(rh
.TYPES_CHANGEMENT
)
1392 sujet
= '[SGRH] Rapport des employés de type: %s' % (
1394 [types_dict
[c
].lower() for c
in
1398 message
+= u
'%s: \n' % (types_dict
[t
].decode('utf8'))
1399 message
+= '\n'.join([u
'\t%s %s, %s' % (
1400 x
.dossier
.employe
.prenom
,
1401 x
.dossier
.employe
.nom
,
1402 url_prefix
+ reverse(
1403 'admin:rh_dossier_change', args
=[x
.dossier
.id]))
1404 for x
in qs
.filter(type__in
=[t
])])
1408 settings
.SERVER_EMAIL
,
1409 [x
.courriel
for x
in recipients
],
1410 fail_silently
=False)
1412 qs
.update(communique
=True)
1414 _communique
.short_description
= u
'Envoyer aux desinataires prévus.'
1417 class ChangementPersonnelAdmin(admin
.ModelAdmin
):
1425 'type', 'dossier', 'valide', 'communique', 'date_communication'
1435 admin
.site
.unregister(User
)
1436 admin
.site
.register(User
, RHUserAdmin
)
1438 admin
.site
.register(rh
.Classement
, ClassementAdmin
)
1439 admin
.site
.register(rh
.Devise
, DeviseAdmin
)
1440 admin
.site
.register(rh
.Dossier
, DossierAdmin
)
1441 admin
.site
.register(EmployeProxy
, EmployeProxyAdmin
)
1442 admin
.site
.register(ServiceProxy
, ServiceProxyAdmin
)
1443 admin
.site
.register(rh
.Employe
, EmployeAdmin
)
1444 admin
.site
.register(rh
.CategorieEmploi
, CategorieEmploiAdmin
)
1445 admin
.site
.register(rh
.FamilleProfessionnelle
)
1446 admin
.site
.register(rh
.OrganismeBstg
, OrganismeBstgAdmin
)
1447 admin
.site
.register(rh
.Poste
, PosteAdmin
)
1448 admin
.site
.register(
1449 rh
.ResponsableImplantationProxy
, ResponsableImplantationAdmin
1451 admin
.site
.register(rh
.Service
, ServiceAdmin
)
1452 admin
.site
.register(rh
.Statut
, StatutAdmin
)
1453 admin
.site
.register(rh
.TauxChange
, TauxChangeAdmin
)
1454 admin
.site
.register(rh
.TypeContrat
, TypeContratAdmin
)
1455 admin
.site
.register(rh
.TypePoste
, TypePosteAdmin
)
1456 admin
.site
.register(rh
.TypeRemuneration
, TypeRemunerationAdmin
)
1457 admin
.site
.register(rh
.TypeRevalorisation
, TypeRevalorisationAdmin
)
1458 admin
.site
.register(rh
.ValeurPoint
, ValeurPointAdmin
)
1459 admin
.site
.register(ImplantationProxy
, ImplantationProxyAdmin
)
1460 admin
.site
.register(RegionProxy
, RegionProxyAdmin
)
1461 admin
.site
.register(rh
.ChangementPersonnelNotifications
)
1462 admin
.site
.register(rh
.ChangementPersonnel
, ChangementPersonnelAdmin
)