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', )}),
458 ('regime_travail', 'regime_travail_nb_heure_semaine'),)}),
459 ('Occupation du Poste par cet Employe', {
460 'fields': (('date_debut', 'date_fin'), )}
463 form
= make_ajax_form(rh
.Dossier
, {
464 'employe': 'employes',
466 'remplacement_de': 'dossiers',
467 }, superclass
=DossierForm
)
469 def lookup_allowed(self
, key
, value
):
471 'employe__nom__istartswith',
472 'poste__implantation__zone_administrative__code__exact',
473 'poste__implantation__zone_administrative__in',
474 'poste__implantation__id__exact',
475 'poste__type_poste__id__exact',
476 'poste__type_poste__categorie_emploi__id__exact',
477 'rh_contrats__type_contrat__id__exact',
485 _id
.short_description
= u
"#"
486 _id
.admin_order_field
= "id"
488 def _apercu(self
, d
):
489 apercu_link
= u
"""<a title="Aperçu du dossier"
490 onclick="return showAddAnotherPopup(this);"
492 <img src="%simg/dossier-apercu.png" />
494 (reverse('dossier_apercu', args
=(d
.id,)),
498 _apercu
.allow_tags
= True
499 _apercu
.short_description
= u
""
503 _nom
.allow_tags
= True
504 _nom
.short_description
= u
"Dossier"
506 def _employe(self
, obj
):
507 employe
= obj
.employe
508 view_link
= reverse('employe_apercu', args
=(employe
.id,))
509 edit_link
= reverse('admin:rh_employe_change', args
=(employe
.id,))
511 view
= u
"""<a href="%s"
512 title="Aperçu l'employé"
513 onclick="return showAddAnotherPopup(this);">
514 <img src="%simg/employe-apercu.png" />
515 </a>""" % (view_link
, settings
.STATIC_URL
,)
516 return u
"""%s<a href='%s' style="%s;">%s</a>""" % \
517 (view
, edit_link
, style
, employe
)
518 _employe
.allow_tags
= True
519 _employe
.short_description
= u
"Employé"
520 _employe
.admin_order_field
= "employe__nom"
522 def _poste(self
, dossier
):
523 link
= u
"""<a title="Aperçu du poste"
524 onclick="return showAddAnotherPopup(this);"
525 href='%s'><img src="%simg/poste-apercu.png" />
527 <a href="%s" title="Modifier le poste">%s [%d]</a>""" % \
528 (reverse('poste_apercu', args
=(dossier
.poste
.id,)),
530 reverse('admin:rh_poste_change', args
=(dossier
.poste
.id,)),
535 _poste
.allow_tags
= True
536 _poste
.short_description
= u
'Poste'
537 _poste
.admin_order_field
= 'poste__nom'
539 def _zone_administrative(self
, obj
):
540 return obj
.poste
.implantation
.zone_administrative
.code
541 _zone_administrative
.short_description
= u
"Zone administrative"
542 _zone_administrative
.admin_order_field
= \
543 'poste__implantation__zone_administrative__code'
545 def _implantation(self
, obj
):
546 return obj
.poste
.implantation
.nom
547 _implantation
.short_description
= u
"Implantation"
548 _implantation
.admin_order_field
= 'poste__implantation__nom'
550 def _date_debut(self
, obj
):
551 return date(obj
.date_debut
)
553 _date_debut
.short_description
= u
'Début'
554 _date_debut
.admin_order_field
= 'date_debut'
556 def _date_fin(self
, obj
):
557 return date(obj
.date_fin
)
558 _date_fin
.short_description
= u
'Fin'
559 _date_fin
.admin_order_field
= 'date_fin'
561 def _date_modification(self
, obj
):
562 return date(obj
.date_modification
) \
563 if obj
.date_modification
is not None else "(aucune)"
564 _date_modification
.short_description
= u
'date modification'
565 _date_modification
.admin_order_field
= 'date_modification'
569 dossiers_dae
= d
.dossiers_dae
.all()
570 if len(dossiers_dae
) > 0:
571 dossier_dae
= dossiers_dae
[0]
572 apercu_link
= u
"""<a title="Aperçu du dossier"
573 onclick="return showAddAnotherPopup(this);"
575 <img src="%simg/loupe.png" />
577 (reverse('embauche_consulter', args
=(dossier_dae
.id,)),
581 _dae
.allow_tags
= True
582 _dae
.short_description
= u
"DAE"
584 def save_formset(self
, request
, form
, formset
, change
):
585 instances
= formset
.save(commit
=False)
586 for instance
in instances
:
587 if instance
.__class__
== rh
.DossierCommentaire
:
588 instance
.owner
= request
.user
589 instance
.date_creation
= datetime
.datetime
.now()
593 class EmployeAdminBase(DateRangeMixin
, ProtectRegionMixin
,
594 DerniereModificationAdmin
, BaseAdmin
):
595 prefixe_recherche_temporelle
= "rh_dossiers__"
596 alphabet_filter
= 'nom'
597 DEFAULT_ALPHABET
= u
'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
599 'id', 'nom', 'prenom', 'nom_affichage',
600 'rh_dossiers__poste__nom',
601 'rh_dossiers__poste__nom_feminin'
604 form
= EmployeAdminForm
606 '_id', '_apercu', '_nom', '_dossiers_postes',
607 #'_zone_administrative',
610 'derniere_modification'
612 list_display_links
= ('_nom',)
614 'rh_dossiers__poste__implantation__zone_administrative',
615 'rh_dossiers__poste__implantation', 'nb_postes'
618 AyantDroitInline
, DossierROInline
, EmployePieceInline
,
619 EmployeCommentaireInline
625 ('nom_affichage', 'genre'),
630 ('Informations personnelles', {
631 'fields': ('situation_famille', 'date_entree', )
633 ('Coordonnées personnelles', {
635 ('tel_domicile', 'tel_cellulaire'),
636 ('adresse', 'ville'),
637 ('code_postal', 'province'),
646 _id
.short_description
= u
"#"
647 _id
.admin_order_field
= "id"
649 def _apercu(self
, obj
):
650 return u
"""<a title="Aperçu de l'employé"
651 onclick="return showAddAnotherPopup(this);"
653 <img src="%simg/employe-apercu.png" />
655 (reverse('employe_apercu', args
=(obj
.id,)), settings
.STATIC_URL
)
656 _apercu
.allow_tags
= True
657 _apercu
.short_description
= u
""
660 edit_link
= reverse('admin:rh_employe_change', args
=(obj
.id,))
661 return u
"""<a href='%s'><strong>%s</strong></a>""" % \
662 (edit_link
, "%s %s" % (obj
.nom
.upper(), obj
.prenom
))
663 _nom
.allow_tags
= True
664 _nom
.short_description
= u
"Employé"
665 _nom
.admin_order_field
= "nom"
667 def _zone_administrative(self
, obj
):
669 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
670 zone
= d
.poste
.implantation
.zone_administrative
.code
674 _zone_administrative
.short_description
= u
"Zone administrative"
676 def _implantation(self
, obj
):
678 d
= rh
.Dossier
.objects
.filter(employe
=obj
.id, principal
=True)[0]
679 implantation
= d
.poste
.implantation
.nom
683 _implantation
.short_description
= u
"Implantation"
685 def _dossiers_postes(self
, obj
):
687 for d
in obj
.rh_dossiers
.all().order_by('-date_debut'):
690 if d
.date_fin
is not None and d
.date_fin
< datetime
.date
.today():
691 link_style
= u
' style="color:#666;"'
692 list_style
= u
' style="color:grey;"'
694 dossier
= u
"""<a title="Aperçu du dossier"
696 onclick="return showAddAnotherPopup(this);"
697 title="Aperçu du dossier">
698 <img src="%simg/dossier-apercu.png" />
700 <a href="%s"%s>Dossier</a>
702 (reverse('dossier_apercu', args
=(d
.id,)),
704 reverse('admin:rh_dossier_change', args
=(d
.id,)),
707 poste
= u
"""<a title="Aperçu du poste"
709 onclick="return showAddAnotherPopup(this);"
710 title="Aperçu du poste">
711 <img src="%simg/poste-apercu.png" />
713 <a href="%s"%s>%s [%d]</a>
715 (reverse('poste_apercu', args
=(d
.poste
.id,)),
717 reverse('admin:rh_poste_change', args
=(d
.poste
.id,)),
722 link
= u
"""<li%s>%s %s</li>""" % \
723 (list_style
, dossier
, poste
)
726 return "<ul>%s</ul>" % "\n".join(l
)
727 _dossiers_postes
.allow_tags
= True
728 _dossiers_postes
.short_description
= u
"Dossiers et postes"
730 def _date_modification(self
, obj
):
731 return date(obj
.date_modification
) \
732 if obj
.date_modification
is not None else "(aucune)"
733 _date_modification
.short_description
= u
'date modification'
734 _date_modification
.admin_order_field
= 'date_modification'
736 def queryset(self
, request
):
737 qs
= super(EmployeAdminBase
, self
).queryset(request
)
738 return qs
.select_related(depth
=1).order_by('nom')
740 def save_formset(self
, request
, form
, formset
, change
):
741 instances
= formset
.save(commit
=False)
742 for instance
in instances
:
743 if instance
.__class__
== rh
.EmployeCommentaire
:
744 instance
.owner
= request
.user
745 instance
.date_creation
= datetime
.datetime
.now()
749 class EmployeAdmin(reversion
.VersionAdmin
, EmployeAdminBase
):
750 change_list_template
= "admin/rh/employe/change_list.html"
751 ignore_duplicate_revisions
= True
754 class EmployeProxyAdmin(EmployeAdminBase
):
755 list_display
= ('_id', '_apercu', '_nom', '_organigramme')
759 def __init__(self
, *args
, **kwargs
):
760 super(EmployeProxyAdmin
, self
).__init__(*args
, **kwargs
)
761 self
.list_display_links
= (None, )
763 def queryset(self
, request
):
764 qs
= super(ProtectRegionMixin
, self
).queryset(request
)
766 if in_drh_or_admin(request
.user
) or \
767 user_gere_obj_de_sa_region(request
.user
):
772 def has_add_permission(self
, obj
):
775 def has_change_permission(self
, request
, obj
=None):
776 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
777 if groups
.CORRESPONDANT_RH
in user_groups
or \
778 groups
.ADMINISTRATEURS
in user_groups
or \
779 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
780 in_drh_or_admin(request
.user
):
784 def _organigramme(self
, obj
):
786 for d
in rh
.Dossier
.objects
.filter(
787 Q(date_fin__gt
=datetime
.date
.today()) |
Q(date_fin
=None),
788 Q(date_debut__lt
=datetime
.date
.today()) |
Q(date_debut
=None),
792 u
'Organigramme, niveau: ' \
793 u
'<input type="text" id="level_%s" ' \
794 u
'style="width:30px;height:15px;" /> ' \
795 u
'<input type="button" value="Générer" ' \
796 u
"""onclick="window.location='%s' + """ \
797 u
"""document.getElementById('level_%s').value" />""" % (
799 reverse('rho_employe_sans_niveau', args
=(d
.poste
.id,)),
802 link
= u
"""<li>%s [%s]:<br />%s</li>""" % (
808 return "<ul>%s</ul>" % "\n".join(l
)
810 _organigramme
.allow_tags
= True
811 _organigramme
.short_description
= "Organigramme"
814 class CategorieEmploiAdmin(reversion
.VersionAdmin
,
815 DerniereModificationAdmin
, BaseAdmin
):
816 ignore_duplicate_revisions
= True
817 list_display
= ('nom', 'derniere_modification')
818 inlines
= (TypePosteInline
,)
820 (None, {'fields': ('nom', )}),
824 class OrganismeBstgAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
826 ignore_duplicate_revisions
= True
827 search_fields
= ('nom',)
828 list_display
= ('nom', 'type', 'pays', 'derniere_modification')
829 list_filter
= ('type', )
830 inlines
= (DossierROInline
,)
832 (None, {'fields': ('nom', 'type', 'pays',)}),
836 class PosteAdmin(DateRangeMixin
, ProtectRegionMixin
, reversion
.VersionAdmin
,
837 AjaxSelect
, DerniereModificationAdmin
, BaseAdmin
):
838 change_list_template
= "admin/rh/poste/change_list.html"
839 ignore_duplicate_revisions
= True
840 form
= make_ajax_form(rh
.Poste
, {
841 'implantation': 'implantations',
842 'type_poste': 'typepostes',
843 'responsable': 'postes',
844 'valeur_point_min': 'valeurpoints',
845 'valeur_point_max': 'valeurpoints',
847 alphabet_filter
= 'nom'
852 'implantation__zone_administrative__code',
853 'implantation__zone_administrative__nom',
854 'rh_dossiers__employe__id',
855 'rh_dossiers__employe__nom',
856 'rh_dossiers__employe__prenom',
859 '_id', '_apercu', '_nom', '_occupe_par', 'implantation', '_service',
860 '_responsable', 'date_debut', 'date_fin', 'derniere_modification',
864 'implantation__zone_administrative',
868 'type_poste__categorie_emploi',
869 'type_poste__famille_professionnelle',
872 list_display_links
= ('_nom',)
875 ('nom', 'nom_feminin'),
885 'regime_travail_nb_heure_semaine'),
889 'fields': (('local', 'expatrie', 'mise_a_disposition', 'appel'),)}
892 'fields': (('classement_min',
905 ('Comparatifs de rémunération', {
906 'fields': ('devise_comparaison',
907 ('comp_locale_min', 'comp_locale_max'),
908 ('comp_universite_min', 'comp_universite_max'),
909 ('comp_fonctionpub_min', 'comp_fonctionpub_max'),
910 ('comp_ong_min', 'comp_ong_max'),
911 ('comp_autre_min', 'comp_autre_max'))}
914 'fields': ('justification',)}
916 ('Autres Méta-données', {
917 'fields': ('date_debut', 'date_fin')}
921 inlines
= (PosteFinancementInline
,
924 PosteComparaisonInline
,
925 PosteCommentaireInline
, )
927 def lookup_allowed(self
, key
, value
):
929 'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
931 'implantation__zone_administrative__code__exact',
932 'implantation__id__exact', 'type_poste__id__exact',
933 'type_poste__categorie_emploi__id__exact', 'service__id__exact',
934 'service__isnull', 'vacant__exact', 'vacant__isnull'
935 ) or super(PosteAdmin
, self
).lookup_allowed(key
, value
)
939 _id
.short_description
= '#'
940 _id
.admin_order_field
= 'id'
942 def _apercu(self
, poste
):
943 view_link
= u
"""<a onclick="return showAddAnotherPopup(this);"
944 title="Aperçu du poste"
946 <img src="%simg/poste-apercu.png" />
948 (reverse('poste_apercu', args
=(poste
.id,)),
949 settings
.STATIC_URL
,)
951 _apercu
.allow_tags
= True
952 _apercu
.short_description
= ''
954 def _nom(self
, poste
):
955 return """<a href="%s">%s</a>""" % \
956 (reverse('admin:rh_poste_change', args
=(poste
.id,)),
958 _nom
.allow_tags
= True
959 _nom
.short_description
= u
'Poste'
960 _nom
.admin_order_field
= 'nom'
962 def _occupe_par(self
, obj
):
963 """Formatte la méthode Poste.occupe_par() pour l'admin"""
965 if obj
.date_fin
is not None and obj
.date_fin
< datetime
.date
.today():
967 employes
= obj
.occupe_par()
971 link
= u
"""<a href='%s'
972 title='Aperçu de l\'employé'
973 onclick='return showAddAnotherPopup(this)'>
974 <img src='%simg/employe-apercu.png' />
976 <a href='%s'>%s</a>""" % \
977 (reverse('employe_apercu', args
=(e
.id,)),
979 reverse('admin:rh_employe_change', args
=(e
.id,)),
982 output
= "\n<br />".join(l
)
984 _occupe_par
.allow_tags
= True
985 _occupe_par
.short_description
= "Occupé par"
987 def _zone_administrative(self
, poste
):
988 return poste
.implantation
.zone_administrative
.code
989 _zone_administrative
.short_description
= 'Zone administrative'
990 _zone_administrative
.admin_order_field
= \
991 'implantation__zone_administrative__code'
993 def _implantation(self
, poste
):
994 return poste
.implantation
.nom
995 _implantation
.short_description
= 'Implantation'
996 _implantation
.admin_order_field
= 'implantation'
998 def _service(self
, obj
):
1000 _service
.short_description
= 'Service'
1001 _service
.allow_tags
= True
1003 def _responsable(self
, obj
):
1005 responsable
= u
"""<a href="%s"
1006 onclick="return showAddAnotherPopup(this)">
1007 <img src="%simg/poste-apercu.png"
1008 title="Aperçu du poste" />
1010 <a href="%s">%s [%d]</a>
1012 (reverse('poste_apercu', args
=(obj
.responsable
.id,)),
1013 settings
.STATIC_URL
,
1014 reverse('admin:rh_poste_change', args
=(obj
.responsable
.id,)),
1015 obj
.responsable
.nom
,
1021 dossier
= obj
.responsable
.rh_dossiers
.all() \
1022 .order_by('-date_debut')[0]
1023 employe_id
= dossier
.employe
.id
1024 employe_html
= u
"""<br />
1026 onclick="return showAddAnotherPopup(this)">
1027 <img src="%simg/employe-apercu.png"
1028 title="Aperçu de l'employé">
1030 <a href="%s">%s</a>""" % \
1031 (reverse('employe_apercu', args
=(employe_id
,)),
1032 settings
.STATIC_URL
,
1033 reverse('admin:rh_employe_change', args
=(employe_id
,)),
1038 return "%s %s" % (responsable
, employe_html
)
1039 _responsable
.short_description
= 'Responsable'
1040 _responsable
.allow_tags
= True
1042 def _date_debut(self
, obj
):
1043 return date_format(obj
.date_debut
)
1044 _date_debut
.short_description
= u
'Début'
1045 _date_debut
.admin_order_field
= 'date_debut'
1047 def _date_fin(self
, obj
):
1048 return date_format(obj
.date_fin
)
1049 _date_fin
.short_description
= u
'Fin'
1050 _date_fin
.admin_order_field
= 'date_fin'
1052 def _dae(self
, poste
):
1054 postes_dae
= poste
.postes_dae
.all()
1055 if len(postes_dae
) > 0:
1056 poste_dae
= postes_dae
[0]
1058 u
'<a title="Aperçu du dossier" href="%s" ' \
1059 u
'onclick="return showAddAnotherPopup(this);">' \
1060 u
'<img src="%simg/loupe.png" /></a>' % (reverse(
1061 'poste_consulter', args
=("dae-%s" % poste_dae
.id,)
1062 ), settings
.STATIC_URL
)
1064 _dae
.allow_tags
= True
1065 _dae
.short_description
= u
"DAE"
1067 def save_formset(self
, request
, form
, formset
, change
):
1068 instances
= formset
.save(commit
=False)
1069 for instance
in instances
:
1070 if instance
.__class__
== rh
.PosteCommentaire
:
1071 instance
.owner
= request
.user
1072 instance
.date_creation
= datetime
.datetime
.now()
1077 class ResponsableInline(admin
.TabularInline
):
1078 model
= rh
.ResponsableImplantation
1080 fk_name
= "implantation"
1081 form
= ResponsableInlineForm
1084 class ResponsableImplantationAdmin(BaseAdmin
):
1087 inlines
= (ResponsableInline
, )
1088 list_filter
= ('zone_administrative', 'statut', )
1089 list_display
= ('_zone_administrative', '_nom', 'statut', '_responsable', )
1090 list_display_links
= ('_nom',)
1092 readonly_fields
= ('nom', )
1095 'responsable__employe__id',
1096 'responsable__employe__nom',
1097 'responsable__employe__prenom',
1100 inlines
= (ResponsableInline
, )
1102 def _zone_administrative(self
, obj
):
1103 return obj
.zone_administrative
.code
1104 _zone_administrative
.short_description
= u
"Zone administrative"
1105 _zone_administrative
.admin_order_field
= 'zone_administrative__code'
1107 def _nom(self
, obj
):
1109 _nom
.short_description
= u
"Implantation"
1110 _nom
.admin_order_field
= 'nom'
1112 def _responsable(self
, obj
):
1114 employe
= employe
= obj
.responsable
.employe
1115 except Exception, e
:
1117 u
"<span style='color: red;'>"
1118 u
"Pas de responsable</span><!-- %s -->" % e
1121 dossiers
= employe
.dossiers_encours()
1122 if len(dossiers
) == 0:
1123 return u
"<span style='color: red;'>%s %s </span>" % (
1124 employe
, u
"sans dossier actif")
1127 except Exception, e
:
1128 return u
"<!-- %s -->" % e
1129 _responsable
.allow_tags
= True
1130 _responsable
.short_description
= u
"Responsable"
1131 _responsable
.admin_order_field
= 'responsable__employe__nom'
1133 def has_add_permission(self
, request
=None):
1136 def has_change_permission(self
, request
, obj
=None):
1137 return in_drh_or_admin(request
.user
)
1139 def has_delete_permission(self
, request
, obj
=None):
1143 class ServiceAdminBase(ArchivableAdmin
, DerniereModificationAdmin
, BaseAdmin
):
1144 list_display
= ('nom', 'derniere_modification', '_archive')
1145 list_filter
= ('archive', )
1147 (None, {'fields': ('nom', 'archive')}),
1151 class ServiceAdmin(reversion
.VersionAdmin
, ServiceAdminBase
):
1152 ignore_duplicate_revisions
= True
1155 class ServiceProxyAdmin(ServiceAdminBase
):
1156 list_display
= ('nom', '_organigramme', '_archive', )
1159 def __init__(self
, *args
, **kwargs
):
1160 super(ServiceProxyAdmin
, self
).__init__(*args
, **kwargs
)
1161 self
.list_display_links
= (None, )
1163 def queryset(self
, request
):
1164 return super(ServiceProxyAdmin
, self
).queryset(request
) \
1165 .annotate(num_postes
=Count('rh_postes')) \
1166 .filter(num_postes__gt
=0)
1168 def has_add_permission(self
, obj
):
1171 def has_change_permission(self
, request
, obj
=None):
1172 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1173 if groups
.CORRESPONDANT_RH
in user_groups
or \
1174 groups
.ADMINISTRATEURS
in user_groups
or \
1175 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1176 in_drh_or_admin(request
.user
):
1180 def _organigramme(self
, obj
):
1181 return """<a href="%s"><strong>Organigramme</strong></a>""" % \
1182 (reverse('rho_service', args
=(obj
.id,)))
1183 _organigramme
.allow_tags
= True
1184 _organigramme
.short_description
= "Organigramme"
1187 class StatutAdmin(reversion
.VersionAdmin
,
1189 DerniereModificationAdmin
,
1191 ignore_duplicate_revisions
= True
1192 list_display
= ('code', 'nom', 'derniere_modification', '_archive')
1193 list_filter
= ('archive', )
1196 'fields': ('code', 'nom', 'archive'),
1201 class TauxChangeAdmin(reversion
.VersionAdmin
, DerniereModificationAdmin
,
1203 ignore_duplicate_revisions
= True
1204 list_display
= ('taux', 'devise', 'annee', 'derniere_modification')
1205 list_filter
= ('devise',)
1208 'fields': ('taux', 'devise', 'annee', ),
1213 class TypeContratAdmin(reversion
.VersionAdmin
,
1215 DerniereModificationAdmin
,
1217 ignore_duplicate_revisions
= True
1218 list_display
= ('nom', 'nom_long', 'derniere_modification', '_archive')
1219 list_filter
= ('archive', )
1222 'fields': ('nom', 'nom_long', 'archive'),
1227 class TypePosteAdmin(reversion
.VersionAdmin
,
1229 DerniereModificationAdmin
,
1231 ignore_duplicate_revisions
= True
1232 search_fields
= ('nom', 'nom_feminin', )
1233 list_display
= ('nom', 'categorie_emploi',
1234 'derniere_modification', '_archive',)
1235 list_filter
= ('categorie_emploi', 'famille_professionnelle', 'archive')
1239 'nom', 'nom_feminin', 'is_responsable', 'categorie_emploi',
1240 'famille_professionnelle',
1247 class TypeRemunerationAdmin(reversion
.VersionAdmin
, ArchivableAdmin
,
1248 DerniereModificationAdmin
, BaseAdmin
):
1249 ignore_duplicate_revisions
= True
1251 'nom', 'type_paiement', 'nature_remuneration',
1252 'derniere_modification', '_archive'
1254 list_filter
= ('archive', )
1258 'nom', 'type_paiement', 'nature_remuneration', 'archive'
1264 class TypeRevalorisationAdmin(reversion
.VersionAdmin
,
1266 DerniereModificationAdmin
,
1268 ignore_duplicate_revisions
= True
1269 list_display
= ('nom', 'derniere_modification', '_archive')
1270 list_filter
= ('archive', )
1272 (None, {'fields': ('nom', 'archive')}),
1276 class ValeurPointAdmin(reversion
.VersionAdmin
,
1277 DerniereModificationAdmin
,
1279 ignore_duplicate_revisions
= True
1281 '_devise_code', '_devise_nom', 'annee', 'implantation',
1282 'valeur', 'derniere_modification'
1284 list_filter
= ('annee', 'devise', 'implantation__zone_administrative', )
1286 (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
1289 def queryset(self
, request
):
1290 return super(ValeurPointAdmin
, self
).queryset(request
) \
1291 .select_related('devise', 'implantation')
1293 def _devise_code(self
, obj
):
1294 return obj
.devise
.code
1295 _devise_code
.short_description
= "Code de la devise"
1297 def _devise_nom(self
, obj
):
1298 return obj
.devise
.nom
1299 _devise_nom
.short_description
= "Nom de la devise"
1302 class ImplantationProxyAdmin(BaseAdmin
):
1303 list_display
= ('nom', '_organigramme')
1306 def __init__(self
, *args
, **kwargs
):
1307 super(ImplantationProxyAdmin
, self
).__init__(*args
, **kwargs
)
1308 self
.list_display_links
= (None, )
1310 def has_add_permission(self
, obj
):
1313 def has_change_permission(self
, request
, obj
=None):
1314 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1315 if groups
.CORRESPONDANT_RH
in user_groups
or \
1316 groups
.ADMINISTRATEURS
in user_groups
or \
1317 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1318 in_drh_or_admin(request
.user
):
1322 def _organigramme(self
, obj
):
1323 return '<a href="%s"><strong>Organigramme</strong></a>' % (
1324 reverse('rho_implantation', args
=(obj
.id,))
1326 _organigramme
.allow_tags
= True
1327 _organigramme
.short_description
= "Organigramme"
1330 class RegionProxyAdmin(BaseAdmin
):
1331 list_display
= ('nom', '_organigramme')
1334 def __init__(self
, *args
, **kwargs
):
1335 super(RegionProxyAdmin
, self
).__init__(*args
, **kwargs
)
1336 self
.list_display_links
= (None, )
1338 def has_add_permission(self
, obj
):
1341 def has_change_permission(self
, request
, obj
=None):
1342 user_groups
= [g
.name
for g
in request
.user
.groups
.all()]
1343 if groups
.CORRESPONDANT_RH
in user_groups
or \
1344 groups
.ADMINISTRATEURS
in user_groups
or \
1345 groups
.DIRECTEUR_DE_BUREAU
in user_groups
or \
1346 in_drh_or_admin(request
.user
):
1350 def _organigramme(self
, obj
):
1351 return """<a href="%s"><strong>Organigramme</strong></a>""" % (
1352 reverse('rho_region', args
=(obj
.id,))
1354 _organigramme
.allow_tags
= True
1355 _organigramme
.short_description
= "Organigramme"
1358 class ProfileInline(admin
.StackedInline
):
1359 model
= rh
.UserProfile
1362 class RHUserAdmin(UserAdmin
):
1363 inlines
= list(UserAdmin
.inlines
) + [ProfileInline
]
1366 def _invalidate(modaladmin
, req
, qs
):
1367 qs
.update(valide
=False)
1368 _invalidate
.short_description
= 'Invalider'
1371 def _communique(modaladmin
, req
, qs
):
1372 url_prefix
= ('https://' if req
.is_secure() else 'http://') + (
1373 req
.META
['SERVER_NAME'] if 'SERVER_NAME' in req
.META
else
1374 'localhost') + (':%s' % (req
.META
['SERVER_PORT']) if 'SERVER_PORT'
1375 in req
.META
else '')
1377 types
= [x
[0] for x
in rh
.TYPES_CHANGEMENT
]
1379 # make a list of all possible email types. It is reversed so that
1380 # we start with the largest combinations.
1381 combs
= list(itertools
.chain(*map(
1382 lambda x
: itertools
.combinations(types
, x
),
1383 range(1, len(types
)+1))))
1385 combs
= reversed(combs
)
1387 recipient_list
= ref
.Employe
.objects
.filter(
1388 changement_notifications__in
=
1389 rh
.ChangementPersonnelNotifications
.objects
.all()).distinct()
1400 recipients
= ref
.Employe
.objects
.none()
1402 recipients
= recipient_list
.filter(changement_notifications__type
=t
)
1404 recipient_list
= recipient_list
.exclude(
1405 id__in
=recipients
.values_list('id', flat
=True))
1407 types_dict
= dict(rh
.TYPES_CHANGEMENT
)
1418 ctx
['types'].append({
1419 'bgcolor': thead_colors
[t
],
1420 'name': types_dict
[t
],
1421 'changements': changements
,
1424 for ch
in qs
.filter(type__in
=[t
]):
1425 changements
.append(ch
)
1427 template
= loader
.get_template('email/mouvement_employe.html')
1428 content
= template
.render(Context(ctx
))
1430 sujet
= '[SGRH] ARRIVÉES - DÉPARTS - MOBILITÉS'
1435 settings
.SERVER_EMAIL
,
1436 [x
.courriel
for x
in recipients
],
1438 msg
.content_subtype
= "html"
1443 date_communication
=datetime
.datetime
.now()
1446 _communique
.short_description
= u
'Envoyer aux desinataires prévus.'
1449 class ChangementPersonnelAdmin(admin
.ModelAdmin
):
1457 return obj
.dossier
.__unicode__()
1458 _dossier
.short_description
= u
'Dossier'
1461 'type', _dossier
, 'valide', 'communique', 'date_communication'
1471 admin
.site
.unregister(User
)
1472 admin
.site
.register(User
, RHUserAdmin
)
1474 admin
.site
.register(rh
.Classement
, ClassementAdmin
)
1475 admin
.site
.register(rh
.Devise
, DeviseAdmin
)
1476 admin
.site
.register(rh
.Dossier
, DossierAdmin
)
1477 admin
.site
.register(EmployeProxy
, EmployeProxyAdmin
)
1478 admin
.site
.register(ServiceProxy
, ServiceProxyAdmin
)
1479 admin
.site
.register(rh
.Employe
, EmployeAdmin
)
1480 admin
.site
.register(rh
.CategorieEmploi
, CategorieEmploiAdmin
)
1481 admin
.site
.register(rh
.FamilleProfessionnelle
)
1482 admin
.site
.register(rh
.OrganismeBstg
, OrganismeBstgAdmin
)
1483 admin
.site
.register(rh
.Poste
, PosteAdmin
)
1484 admin
.site
.register(
1485 rh
.ResponsableImplantationProxy
, ResponsableImplantationAdmin
1487 admin
.site
.register(rh
.Service
, ServiceAdmin
)
1488 admin
.site
.register(rh
.Statut
, StatutAdmin
)
1489 admin
.site
.register(rh
.TauxChange
, TauxChangeAdmin
)
1490 admin
.site
.register(rh
.TypeContrat
, TypeContratAdmin
)
1491 admin
.site
.register(rh
.TypePoste
, TypePosteAdmin
)
1492 admin
.site
.register(rh
.TypeRemuneration
, TypeRemunerationAdmin
)
1493 admin
.site
.register(rh
.TypeRevalorisation
, TypeRevalorisationAdmin
)
1494 admin
.site
.register(rh
.ValeurPoint
, ValeurPointAdmin
)
1495 admin
.site
.register(ImplantationProxy
, ImplantationProxyAdmin
)
1496 admin
.site
.register(RegionProxy
, RegionProxyAdmin
)
1497 admin
.site
.register(rh
.ChangementPersonnelNotifications
)
1498 admin
.site
.register(rh
.ChangementPersonnel
, ChangementPersonnelAdmin
)