Merge branch 'master' into dae_refactoring_process
authorOlivier Larchevêque <olivier.larcheveque@auf.org>
Thu, 15 Nov 2012 15:57:51 +0000 (10:57 -0500)
committerOlivier Larchevêque <olivier.larcheveque@auf.org>
Thu, 15 Nov 2012 15:57:51 +0000 (10:57 -0500)
67 files changed:
buildout.cfg
project/assets/js/dae-embauche.js
project/conf.py.edit
project/dae/catalogues.py
project/dae/forms.py
project/dae/groups.py
project/dae/mail.py
project/dae/managers.py
project/dae/models.py
project/dae/templates/dae/embauche-row.html
project/dae/templates/dae/embauches_finalisees.html
project/dae/templates/dae/index.html
project/dae/templates/dae/poste-row.html
project/dae/templatetags/dae.py
project/dae/test/poste.py
project/dae/views.py
project/dae/workflow.py
project/dashboard.py
project/decorators.py
project/groups.py
project/menu.py
project/permissions.py
project/recrutement/admin.py
project/recrutement/forms.py
project/recrutement/locale/fr/LC_MESSAGES/django.mo
project/recrutement/locale/fr/LC_MESSAGES/django.po
project/recrutement/migrations/0005_auto__add_offreemploievaluateur.py [new file with mode: 0644]
project/recrutement/migrations/0006_convert_candidatevaluation.py [new file with mode: 0644]
project/recrutement/models.py
project/recrutement/templates/admin/recrutement/candidat/change_list.html [new file with mode: 0644]
project/recrutement/templates/recrutement/selectionner_statut.html [new file with mode: 0644]
project/recrutement/views.py
project/rh/admin.py
project/rh/catalogues.py
project/rh/forms.py
project/rh/managers.py
project/rh/models.py
project/rh/ods.py
project/rh/templates/rh/include/dossier.html
project/rh/templates/rh/rapports/contrats.html
project/rh/templates/rh/rapports/employes_sans_contrat.html
project/rh/templates/rh/rapports/masse_salariale.html
project/rh/templatetags/rapports.py
project/rh/test/common.py
project/rh/test/dossier.py
project/rh/test/employe.py
project/rh/test/poste.py
project/rh/test/rapport.py
project/rh/views.py
project/settings.py
project/templates/actions.html
project/templates/index.html
project/templates/menu.html
project/urls.py
project/views.py
src/qbe/django_qbe/exports.py
src/qbe/django_qbe/forms.py
src/qbe/django_qbe/locale/fr/LC_MESSAGES/django.mo [new file with mode: 0644]
src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.mo [new file with mode: 0644]
src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.po [new file with mode: 0644]
src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.mo [deleted file]
src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.po [deleted file]
src/qbe/django_qbe/static/django_qbe/css/qbe.css
src/qbe/django_qbe/static/django_qbe/js/qbe.core.js
src/qbe/django_qbe/templates/qbe.html
src/qbe/django_qbe/utils.py
versions.cfg

index 076ff10..03068a0 100644 (file)
@@ -9,18 +9,24 @@ parts = django
 find-links = http://pypi.auf.org/simple/auf.recipe.django/
     http://pypi.auf.org/simple/auf.django.skin/
     http://pypi.auf.org/simple/auf.django.auth/
+    http://pypi.auf.org/simple/auf.django.saml/
     http://pypi.auf.org/simple/auf.django.workflow/
     http://pypi.auf.org/simple/auf.django.admingroup/
     http://pypi.auf.org/simple/auf.django.permissions/
     http://pypi.auf.org/simple/auf.django.emploi/
+    http://pypi.auf.org/simple/auf.django.export/
     http://pypi.auf.org/simple/auf.django.references/
+    http://pypi.auf.org/simple/auf.django.piwik/
     http://pypi.auf.org/simple/django-alphafilter/
     http://pypi.auf.org/simple/odsgen/
+
 develop = src/qbe
     src/auf.django.metadata
+
 eggs =
     django
     south
+    raven
     django-admin-tools
     django-ajax-selects
     django-alphafilter
@@ -34,12 +40,15 @@ eggs =
     django-urldecorators
     auf.django.admingroup
     auf.django.auth
+    auf.django.saml
     auf.django.emploi
+    auf.django.export
     auf.django.metadata
     auf.django.permissions
     auf.django.references
     auf.django.skin
     auf.django.workflow
+    auf.django.piwik
     auf.recipe.django
     odsgen
     django-picklefield
index dc890ac..557488a 100644 (file)
@@ -24,8 +24,10 @@ function loadSalaire() {
     if (implantation && devise && classement) {
         $.getJSON('/dae/salaire/' + implantation + '/' + devise + '/' + classement,
           function(data) {
-            $('#id_salaire').val(clean_float(data.salaire_devise));
-            $('#id_salaire').trigger('change');
+            if (data.status == 'OK') {
+                $('#id_salaire').val(clean_float(data.salaire_devise));
+                $('#id_salaire').trigger('change');
+            }
           });
     }
 }
index 0f33865..114cec0 100644 (file)
@@ -15,3 +15,5 @@ EMAIL_FROM = ''
 ROA_API_KEY = ''
 
 AUTH_PASSWORD_REQUIRED = True
+
+PIWIK_TOKEN = ''
index 3ca3d7e..9c1dc52 100644 (file)
@@ -72,12 +72,10 @@ class Dossier(object):
             Q(employe__nom__icontains=q) | \
             Q(employe__prenom__icontains=q)
 
-        if groups.is_user_dans_services_centraux(request.user):
-            q_place = Q(**{prefixe_implantation: employe.implantation})
-        else:
-            q_place = Q(**{
-                prefixe_implantation + '__region': employe.implantation.region
-            })
+        q_place = Q(**{
+            prefixe_implantation + '__zone_administrative':
+            employe.implantation.zone_administrative
+        })
 
         user_groupes = [g.name for g in request.user.groups.all()]
         if groups.DRH_NIVEAU_1 in user_groupes:
@@ -119,11 +117,13 @@ class Poste(object):
             q_place = Q(**{prefixe_implantation: employe.implantation})
         else:
             q_place = Q(**{
-                prefixe_implantation + '__region': employe.implantation.region
+                prefixe_implantation + '__zone_administrative':
+                employe.implantation.zone_administrative
             })
 
         user_groupes = [g.name for g in request.user.groups.all()]
-        if groups.DRH_NIVEAU_1 in user_groupes:
+        if groups.DRH_NIVEAU_1 in user_groupes or \
+           groups.DRH_NIVEAU_2 in user_groupes:
             q_filtre = q_recherche
         else:
             q_filtre = q_place & q_recherche
index e2ac873..929b73c 100644 (file)
@@ -87,16 +87,12 @@ class BaseInlineFormSetWithInitial(BaseInlineFormSet):
 def _implantation_choices(obj, request):
     # TRAITEMENT NORMAL
     employe = groups.get_employe_from_user(request.user)
-    # SERVICE
-    if groups.is_user_dans_services_centraux(request.user):
-        q = Q(**{'id': employe.implantation_id})
-    # REGION
-    else:
-        q = Q(**{'region': employe.implantation.region})
+    q = Q(**{'zone_administrative': employe.implantation.zone_administrative})
 
     # TRAITEMENT DRH
     user_groupes = [g.name for g in request.user.groups.all()]
-    if groups.DRH_NIVEAU_1 in user_groupes:
+    if groups.DRH_NIVEAU_1 in user_groupes or \
+       groups.DRH_NIVEAU_2 in user_groupes:
         q = Q()
     return [('', '----------')] + \
             [(i.id, unicode(i), )for i in ref.Implantation.objects.filter(q)]
@@ -105,21 +101,20 @@ def _implantation_choices(obj, request):
 def _employe_choices(obj, request):
     # TRAITEMENT NORMAL
     employe = groups.get_employe_from_user(request.user)
-    # SERVICE
-    if groups.is_user_dans_services_centraux(request.user):
-        q_dae_region_service = Q(poste__implantation=employe.implantation)
-        q_rh_region_service = Q(poste__implantation=employe.implantation)
-    # REGION
-    else:
-        q_dae_region_service = Q(
-            poste__implantation__region=employe.implantation.region
+    q_dae_region_service = Q(
+        poste__implantation__zone_administrative=(
+            employe.implantation.zone_administrative
         )
-        q_rh_region_service = Q(
-            poste__implantation__region=employe.implantation.region
+    )
+    q_rh_region_service = Q(
+        poste__implantation__zone_administrative=(
+            employe.implantation.zone_administrative
         )
+    )
     # TRAITEMENT DRH
     user_groupes = [g.name for g in request.user.groups.all()]
-    if groups.DRH_NIVEAU_1 in user_groupes:
+    if groups.DRH_NIVEAU_1 in user_groupes or \
+       groups.DRH_NIVEAU_2 in user_groupes:
         q_dae_region_service = Q()
         q_rh_region_service = Q()
 
@@ -161,16 +156,16 @@ def _employe_choices(obj, request):
     employes_avec_dae = [d.employe_id for d in dae.Dossier.objects.all()]
     employes_orphelins = dae.Employe.objects.exclude(id__in=employes_avec_dae)
 
-    def option_label(employe):
-        return "%s %s" % (employe.nom.upper(), employe.prenom.title())
+    def option_label(employe, extra=""):
+        if extra:
+            extra = " [%s]" % extra
+        return "%s %s %s" % (employe.nom.upper(), employe.prenom.title(), extra)
 
-    return [('', 'Nouvel employé')] + \
-           sorted(
-               [('dae-%s' % p.id, option_label(p))
-                for p in dae_ | copies | employes_orphelins] +
-               [('rh-%s' % p.id, option_label(p)) for p in rhv1],
-               key=lambda t: t[1]
-           )
+    lbl_rh = sorted([('rh-%s' % p.id, option_label(p, "existant dans rh")) for p in rhv1],
+            key=lambda t: t[1])
+    lbl_dae = sorted([('dae-%s' % p.id, option_label(p)) for p in dae_ | copies | employes_orphelins],
+            key=lambda t: t[1])
+    return [('', 'Nouvel employé')] + lbl_rh + lbl_dae
 
 
 def label_poste_display(poste):
index 7d92ace..4de4fd1 100644 (file)
@@ -11,5 +11,6 @@ dae_groupes = (
     groups.HAUTE_DIRECTION,
     groups.ACCIOR,
     groups.ABF,
+    groups.FINANCE,
     groups.SERVICE_UTILISATEURS,
 )
index e50d3fc..b83565b 100644 (file)
@@ -8,6 +8,8 @@ from project import groups
 
 
 def send_drh_finalisation_mail(request, dossier):
+    if getattr(settings, 'MAIL_NOTIFICATION_DAE', False) is False:
+        return
     subject = 'DAE: contrat pour %s' % dossier
     dossier_url = request.build_absolute_uri(
         reverse('embauche_consulter', kwargs={'dossier_id': dossier.id})
index ca64649..f712815 100644 (file)
@@ -27,12 +27,14 @@ class TodoManagerMixin(object):
                 elif g not in (
                     groups.ACCIOR,
                     groups.ABF,
+                    groups.FINANCE,
                     groups.HAUTE_DIRECTION,
                     groups.DRH_NIVEAU_1,
                     groups.DRH_NIVEAU_2,
                 ):
                     q2 &= Q(**{
-                        self.prefixe_implantation: employe.implantation.region
+                        self.prefixe_implantation:
+                        employe.implantation.zone_administrative
                     })
                 q |= q2
 
index 3d49ff4..135e7e8 100644 (file)
@@ -657,7 +657,6 @@ class Dossier(DossierWorkflow, rh.Dossier_):
         dossier_rh.regime_travail_nb_heure_semaine = \
                 self.regime_travail_nb_heure_semaine
         dossier_rh.date_debut = self.contrat_date_debut
-        dossier_rh.date_fin = self.contrat_date_fin
         dossier_rh.save()
 
         rh.DossierComparaison.objects.filter(dossier=dossier_rh).delete()
index dc4584b..6027968 100644 (file)
@@ -1,6 +1,6 @@
 {% load dae %}
 <tr>
-    <td>{{ dossier.poste.implantation|region_ou_service }}</td>
+    <td>{{ dossier.poste.implantation.zone_administrative }}</td>
     <td>{{ dossier.poste.implantation }}</td>
     <td><a href="{% url embauche_consulter dossier.id %}">{{ dossier.poste.nom }}</a></td>
     <td>{{ dossier.employe }}</td>
index 90bf4c7..af4ec45 100644 (file)
@@ -31,7 +31,7 @@
     <thead>
       <tr>
         <th></th>
-        {% sort_header "region" "Région" %}
+        {% sort_header "zone_administrative" "Zone administrative" %}
         {% sort_header "implantation" "Implantation" %}
         {% sort_header "poste" "Poste" %}
         {% sort_header "personne" "Personne" %}
@@ -46,7 +46,7 @@
       {% for dossier in embauches.object_list %}
       <tr>
         <td><input type="checkbox" name="ids" value="{{ dossier.id }}"></td>
-        <td>{{ dossier.poste.implantation|region_ou_service }}</td>
+        <td>{{ dossier.poste.implantation.zone_administrative }}</td>
         <td>{{ dossier.poste.implantation }}</td>
         <td><a href="{% url poste_consulter dossier.poste.key %}">{{ dossier.poste.nom }}</a></td>
         <td><a href="{% url embauche_consulter dossier.id %}">{{ dossier.employe }}</a></td>
index c2ac8d2..7d145f0 100644 (file)
@@ -6,7 +6,7 @@
 {% block sous_titre %}Demande d'autorisation d'engagement{% endblock %}
 
 {% block main %}
-<h1>Demande d'autorisation d'embauche (DAE)</h1>
+<h1>DAE</h1>
 
 <p>
 Ce module permet la gestion de deux types de demandes :
index 8118def..40a82aa 100644 (file)
@@ -1,6 +1,6 @@
 {% load dae %}
 <tr>
-    <td>{{ poste.implantation|region_ou_service }}</td>
+    <td>{{ poste.implantation.zone_administrative }}</td>
     <td>{{ poste.implantation }}</td>
     <td><a href="{% url poste_consulter poste.key %}">{{ poste.nom }}</a></td>
     <td>{{ poste.date_creation|date:"d-m-Y" }}</td>
index 2b69560..7a9622c 100644 (file)
@@ -3,14 +3,16 @@
 import os
 
 from django import template
+# -*- coding: utf-8 -*-
 
-from project import groups 
+from project import groups
 
 from project.dae.workflow import ETATS_EDITABLE
 
 
 register = template.Library()
 
+
 @register.filter
 def test_membre_drh(user):
     grps = [g.name for g in user.groups.all()]
@@ -19,19 +21,18 @@ def test_membre_drh(user):
     else:
         return False
 
+
 @register.filter
 def peut_ajouter(user):
+    grp_ok = (groups.ADMINISTRATEURS,
+            groups.CORRESPONDANT_RH,
+            groups.DRH_NIVEAU_1,
+            groups.DRH_NIVEAU_2)
     for g in [g.name for g in user.groups.all()]:
-        if g in (groups.ADMINISTRATEURS,
-                groups.CORRESPONDANT_RH,
-                groups.DRH_NIVEAU_1,
-                groups.DRH_NIVEAU_2):
+        if g in grp_ok:
             return True
     return False
 
-@register.filter
-def peut_importer(user):
-    return groups.DRH_NIVEAU_1 in [g.name for g in user.groups.all()]
 
 @register.filter
 def est_editable(obj, user):
@@ -45,21 +46,18 @@ def est_editable(obj, user):
     else:
         return False
 
-@register.filter
-def region_ou_service(implantation):
-    if implantation.id == 15:
-        return u"Services centraux de Montréal (SCM)"
-    if implantation.id == 19:
-        return u"Services centraux de Paris (SCP)"
-    return implantation.region
 
 @register.filter
 def basename(path):
     return os.path.basename(path)
 
+
 @register.inclusion_tag('dae/sort_header.html', takes_context=True)
 def sort_header(context, field, title):
-    """Génère une entête qu'on peut cliquer pour trier la colonne correspondante dans une table."""
+    """
+    Génère une entête qu'on peut cliquer pour trier
+    la colonne correspondante dans une table.
+    """
     qs = context['request'].GET.copy()
     current = qs.get('tri', None)
     if current == field:
@@ -73,6 +71,7 @@ def sort_header(context, field, title):
         qs['tri'] = field
     return {'title': title, 'qs': qs.urlencode(), 'cls': cls}
 
+
 @register.inclusion_tag('dae/pagination.html', takes_context=True)
 def pagination(context, page):
     """Génère la navigation permettant de se promener de page en page."""
@@ -81,4 +80,7 @@ def pagination(context, page):
     previous_qs['page'] = page.previous_page_number()
     next_qs = qs.copy()
     next_qs['page'] = page.next_page_number()
-    return {'page': page, 'previous_qs': previous_qs.urlencode(), 'next_qs': next_qs.urlencode()}
+    return {'page': page,
+            'previous_qs': previous_qs.urlencode(),
+            'next_qs': next_qs.urlencode()
+            }
index 0a38b13..15a342b 100644 (file)
@@ -6,15 +6,18 @@ from django.contrib.auth.models import User
 from project.dae import models as dae
 from project.dae.test.common import DaeTest
 from project.dae.forms import PosteForm
+from project.dae.workflow import POSTE_ACTION_ENVOYER_ADMINISTRATEUR, \
+    POSTE_ETAT_BROUILLON, \
+    POSTE_ETAT_ADMINISTRATEUR
 
-class PosteAddTest(DaeTest):
+class PosteTest(DaeTest):
     """
     Test l'ajout d'un poste
     """
     url = reverse('admin:rh_poste_add')
 
     def setUp(self):
-        super(PosteAddTest, self).setUp()
+        super(PosteTest, self).setUp()
         key = "rh-%s" % self.poste_cnf_ngaoundere.id
         self.url = reverse('poste', kwargs={'key': key})
         self.post = {
@@ -116,6 +119,30 @@ class PosteAddTest(DaeTest):
         for k, expect_value in self.expect.items():
             self.assertEqual(getattr(poste, k), expect_value)
 
+    def test_validation_poste(self):
+        self._test_drh()
+        self.client.post(self.url, self.post)
+
+        p = dae.Poste.objects.get(nom='nom')
+        self.assertEqual(p.etat, POSTE_ETAT_BROUILLON)
+
+        key = "dae-%s" % p.id
+        url = reverse('poste_consulter', args=(key, ))
+        post = {
+            POSTE_ACTION_ENVOYER_ADMINISTRATEUR:  "Envoyer à l'adminstrateur",
+            'commentaire': 'xxx',
+            }
+        resp = self.client.post(url, post)
+        self.assertEqual(resp.status_code, 302)
+
+        p = dae.Poste.objects.get(nom='nom')
+        self.assertEqual(p.etat, POSTE_ETAT_ADMINISTRATEUR)
+        
+        commentaires = p.commentaires.all()
+        self.assertEqual(len(commentaires), 1)
+        self.assertEqual(commentaires[0].texte, post['commentaire'])
+        self.assertEqual(commentaires[0].etat_initial, POSTE_ETAT_BROUILLON)
+        self.assertEqual(commentaires[0].etat_final, POSTE_ETAT_ADMINISTRATEUR)
 
     def test_anonyme(self):
         """
@@ -159,28 +186,35 @@ class PosteAddTest(DaeTest):
         self._test_drh2()
         self._test_acces_ok(self.url)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas ajouter un poste
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas ajouter un poste
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas ajouter un poste
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas ajouter un poste
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas ajouter un poste
         """
index d68a2f6..c4dbf2c 100644 (file)
@@ -35,9 +35,12 @@ from project.dae.mail import send_drh_finalisation_mail
 from project.dae.workflow import \
         DOSSIER_ETAT_FINALISE, DOSSIER_ETAT_REGION_FINALISATION, \
         DOSSIER_ETAT_DRH_FINALISATION, POSTE_ETAT_FINALISE
-from project.decorators import \
-        redirect_interdiction, drh_or_admin_required, in_drh_or_admin
+from project.decorators import redirect_interdiction,\
+        drh_or_admin_required,\
+        in_drh_or_admin,\
+        in_one_of_group
 from project.rh import models as rh
+from project import groups
 
 
 # Helpers
@@ -75,6 +78,7 @@ def poste_consulter(request, key):
             request.POST, instance=poste, request=request
         )
         if validationForm.is_valid():
+            validationForm.save()
             messages.add_message(
                 request, messages.SUCCESS, "La validation a été enregistrée."
             )
@@ -88,11 +92,12 @@ def poste_consulter(request, key):
         'poste': poste,
         'validationForm': validationForm,
         'comparaisons_internes': comparaisons_internes,
-        'importer': in_drh_or_admin(request.user)
+        'importer': request.user.is_superuser,
     })
 
 
 
+@user_passes_test(lambda u: u.is_superuser)
 @drh_or_admin_required
 def poste_importer(request, id):
     poste_dae = get_object_or_404(dae.Poste, id=id)
@@ -112,6 +117,11 @@ def poste_importer(request, id):
 @dae_groupe_requis
 @poste_dans_ma_region_ou_service
 @poste_est_modifiable
+@in_one_of_group((groups.CORRESPONDANT_RH,
+    groups.ADMINISTRATEURS,
+    groups.DIRECTEUR_DE_BUREAU,
+    groups.DRH_NIVEAU_1,
+    groups.DRH_NIVEAU_2))
 def poste(request, key=None):
     """ Formulaire pour un poste.
 
@@ -334,7 +344,7 @@ def embauche_consulter(request, dossier_id):
         'validationForm': validationForm,
         'comparaisons_internes': comparaisons_internes,
         'comparaisons': comparaisons,
-        'importer': in_drh_or_admin(request.user)
+        'importer': request.user.is_superuser,
     })
 
 @user_passes_test(lambda u: u.is_superuser)
@@ -556,8 +566,8 @@ def embauches_finalisees(request):
         importees = search_form.cleaned_data.get('importees')
         if q:
             criteria = [
-                Q(poste__implantation__region__nom__icontains=word) |
-                Q(poste__implantation__region__code=word) |
+                Q(poste__implantation__zone_administrative__nom__icontains=word) |
+                Q(poste__implantation__zone_administrative__code=word) |
                 Q(poste__implantation__nom__icontains=word) |
                 Q(poste__nom__icontains=word) |
                 Q(employe__nom__icontains=word) |
@@ -579,7 +589,7 @@ def embauches_finalisees(request):
         dir = ''
     if tri == 'region':
         embauches = embauches.order_by(
-            dir + 'poste__implantation__region__nom'
+            dir + 'poste__implantation__zone_administrative__nom'
         )
     elif tri == 'implantation':
         embauches = embauches.order_by(dir + 'poste__implantation__nom')
@@ -1028,11 +1038,13 @@ def salaire(request, implantation, devise, classement):
             .order_by('-annee')
 
     if vp.count() == 0:
-        raise Exception(u"pas de valeur de point pour le couple\
-                implantation/devise (%s/%s)" % (implantation, devise))
+        status = u"pas de valeur de point pour le couple \
+implantation/devise (%s/%s)" % (implantation, devise)
+        return HttpResponse(dumps(dict(status=status)))
 
     if taux.count() == 0:
-        raise Exception(u"Pas de taux pour la devise %s" % devise)
+        status = u"Pas de taux pour la devise %s" % devise
+        return HttpResponse(dumps(dict(status=status)))
 
     classement = get_object_or_404(rh.Classement, pk=classement)
     if classement.coefficient is None:
@@ -1040,7 +1052,8 @@ def salaire(request, implantation, devise, classement):
     taux, vp = taux[0].taux, vp[0].valeur
 
     salaire_euro = round(vp * classement.coefficient * taux, 2)
-    data = dict(salaire_euro=salaire_euro, taux=taux,
+    data = dict(status='OK',
+                salaire_euro=salaire_euro, taux=taux,
                 salaire_devise=round(salaire_euro / taux, 2))
 
     return HttpResponse(dumps(data))
index 9b0ba68..d616d86 100644 (file)
@@ -12,6 +12,7 @@ dae_groupes = (
     groups.DRH_NIVEAU_2,
     groups.ACCIOR,
     groups.ABF,
+    groups.FINANCE,
     groups.HAUTE_DIRECTION,
     groups.SERVICE_UTILISATEURS,
 )
@@ -26,6 +27,7 @@ POSTE_ETAT_DRH_2 = 'DRH_2'
 POSTE_ETAT_DEMANDE_MODIF = 'DEMANDE_MODIF'
 POSTE_ETAT_ACCIOR = 'ACCIOR'
 POSTE_ETAT_ABF = 'ABF'
+POSTE_ETAT_FINANCE = 'FINANCE'
 POSTE_ETAT_HAUTE_DIRECTION = 'HAUTE_DIRECTION'
 POSTE_ETAT_DEMANDE_JUSTIF = 'DEMANDE_JUSTIF'
 POSTE_ETAT_REGION_FINALISATION = 'REGION_FINALISATION'
@@ -43,6 +45,7 @@ POSTE_ACTION_DEMANDE_MODIF = POSTE_ETAT_DEMANDE_MODIF
 POSTE_ACTION_RETOUR_DEMANDE_MODIF = 'RETOUR_DEMANDE_MODIF'
 POSTE_ACTION_ENVOYER_ACCIOR = POSTE_ETAT_ACCIOR
 POSTE_ACTION_ENVOYER_ABF = POSTE_ETAT_ABF
+POSTE_ACTION_ENVOYER_FINANCE = POSTE_ETAT_FINANCE
 POSTE_ACTION_ENVOYER_HAUTE_DIRECTION = POSTE_ETAT_HAUTE_DIRECTION
 POSTE_ACTION_DEMANDE_JUSTIF = POSTE_ETAT_DEMANDE_JUSTIF
 POSTE_ACTION_RETOUR_DEMANDE_JUSTIF = 'RETOUR_DEMANDE_JUSTIF'
@@ -61,6 +64,7 @@ POSTE_ETATS = {
     POSTE_ETAT_DEMANDE_MODIF: u"Demande de modification",
     POSTE_ETAT_ACCIOR: u"ACCIOR",
     POSTE_ETAT_ABF: u"ABF",
+    POSTE_ETAT_FINANCE: u"Finance",
     POSTE_ETAT_HAUTE_DIRECTION: u"Validation : Secrétaire général / Recteur",
     POSTE_ETAT_DEMANDE_JUSTIF: u"Demande de justification",
     POSTE_ETAT_REGION_FINALISATION: u'Retour à la région',
@@ -78,6 +82,7 @@ POSTE_ETATS_BOUTONS = {
     POSTE_ETAT_DEMANDE_MODIF: u"Envoyer une demande de modification",
     POSTE_ETAT_ACCIOR: u"Envoyer à l&#39;ACCIOR",
     POSTE_ETAT_ABF: u"Envoyer à l&#39;ABF",
+    POSTE_ETAT_FINANCE: u"Envoyer aux finances",
     POSTE_ETAT_HAUTE_DIRECTION: u"Envoyer à la haute direction",
     POSTE_ETAT_DEMANDE_JUSTIF: u"Envoyer une demande de justification",
     POSTE_ETAT_REGION_FINALISATION: u"Retourner à la région pour finalisation",
@@ -140,9 +145,14 @@ POSTE_ACTIONS = {
         'etat_initial': (POSTE_ETAT_ACCIOR, POSTE_ETAT_DRH_2),
         'etat_final': POSTE_ETAT_ABF,
     },
+    POSTE_ACTION_ENVOYER_FINANCE: {
+        'nom': u'Envoyer',
+        'etat_initial': (POSTE_ETAT_ABF, ),
+        'etat_final': POSTE_ETAT_FINANCE,
+    },
     POSTE_ACTION_ENVOYER_HAUTE_DIRECTION: {
         'nom': u'Envoyer',
-        'etat_initial': (POSTE_ETAT_ABF,),
+        'etat_initial': (POSTE_ETAT_FINANCE,),
         'etat_final': POSTE_ETAT_HAUTE_DIRECTION,
     },
     POSTE_ACTION_DEMANDE_JUSTIF: {
@@ -225,11 +235,16 @@ class PosteWorkflow(WorkflowMixin):
         return groups.ACCIOR in user_groups or groups.DRH_NIVEAU_1 in user_groups \
                 or groups.DRH_NIVEAU_2 in user_groups
 
-    def acces_haute_direction(self, action, request):
+    def acces_finance(self, action, request):
         user_groups = [g.name for g in request.user.groups.all()]
         return groups.ABF in user_groups or groups.DRH_NIVEAU_1 in user_groups \
                 or groups.DRH_NIVEAU_2 in user_groups
 
+    def acces_haute_direction(self, action, request):
+        user_groups = [g.name for g in request.user.groups.all()]
+        return groups.FINANCE in user_groups or groups.DRH_NIVEAU_1 in user_groups \
+                or groups.DRH_NIVEAU_2 in user_groups
+
     def acces_region_finalisation(self, action, request):
         user_groups = [g.name for g in request.user.groups.all()]
         return groups.HAUTE_DIRECTION in user_groups or groups.DRH_NIVEAU_1 in user_groups \
@@ -271,6 +286,7 @@ DOSSIER_ETAT_DRH_2 = POSTE_ETAT_DRH_2
 DOSSIER_ETAT_DEMANDE_MODIF = POSTE_ETAT_DEMANDE_MODIF
 DOSSIER_ETAT_ACCIOR = POSTE_ETAT_ACCIOR
 DOSSIER_ETAT_ABF = POSTE_ETAT_ABF
+DOSSIER_ETAT_FINANCE = POSTE_ETAT_FINANCE
 DOSSIER_ETAT_HAUTE_DIRECTION = POSTE_ETAT_HAUTE_DIRECTION
 DOSSIER_ETAT_DEMANDE_JUSTIF = POSTE_ETAT_DEMANDE_JUSTIF
 DOSSIER_ETAT_REGION_FINALISATION = POSTE_ETAT_REGION_FINALISATION
@@ -309,6 +325,7 @@ MAP_GROUPE_ETATS_A_FAIRE = {
     groups.DRH_NIVEAU_2: (POSTE_ETAT_DRH_2, DOSSIER_ETAT_DRH_2),
     groups.ACCIOR: (POSTE_ETAT_ACCIOR, DOSSIER_ETAT_ACCIOR),
     groups.ABF: (POSTE_ETAT_ABF, DOSSIER_ETAT_ABF),
+    groups.FINANCE: (POSTE_ETAT_FINANCE, DOSSIER_ETAT_FINANCE),
     groups.HAUTE_DIRECTION: (
         POSTE_ETAT_HAUTE_DIRECTION, DOSSIER_ETAT_HAUTE_DIRECTION
     ),
index 97a9b70..10e0a35 100644 (file)
@@ -20,7 +20,7 @@ from project.rh.historique import get_active_revisions
 
 class CustomIndexDashboard(Dashboard):
     """
-    Custom index dashboard for SIGMA.
+    Custom index dashboard for SGRH.
     """
     def init_with_context(self, context):
         request = context['request']
@@ -37,7 +37,7 @@ class CustomIndexDashboard(Dashboard):
                     )
                 ),
                 modules.ModelList(
-                    title='Gestion des candidatures',
+                    title='Recrutement',
                     models=(
                         'auf.django.emploi.models.OffreEmploi',
                         'project.recrutement.models.ProxyOffreEmploi',
@@ -46,6 +46,7 @@ class CustomIndexDashboard(Dashboard):
                         'project.recrutement.models.CandidatEvaluation',
                         'project.recrutement.models.MesCandidatEvaluation',
                         'project.recrutement.models.Evaluateur',
+                        'project.recrutement.models.OffreEmploiEvaluateur',
                         'project.recrutement.models.CourrielTemplate',
                     )
                 ),
index d9d8ce9..c386666 100644 (file)
@@ -69,7 +69,8 @@ def region_protected(model):
                groups.DIRECTEUR_DE_BUREAU in user_groups:
                 employe = groups.get_employe_from_user(request.user)
                 q = Q(**{
-                    model.prefix_implantation: employe.implantation.region
+                    model.prefix_implantation:
+                    employe.implantation.zone_administrative
                 })
                 qs = model.objects.filter(q)
                 if int(id) in [o.id for o in qs]:
@@ -77,7 +78,8 @@ def region_protected(model):
             return redirect_interdiction(request)
         return wrapped
     return wrapper
-    
+
+
 def in_one_of_group(groups):
     """
     Test si le user appartient au moins 1 des ces groupes
index 4940faf..815ff8c 100644 (file)
@@ -1,38 +1,31 @@
 # -*- encoding: utf-8 -*-
 
-import auf.django.references.models as ref
+from django.core.exceptions import ObjectDoesNotExist
 
 CORRESPONDANT_RH = 'Correspondants RH'
-ADMINISTRATEURS ='Administrateurs' 
+ADMINISTRATEURS = 'Administrateurs'
 DIRECTEUR_DE_BUREAU = 'Directeurs de bureau'
 DRH_NIVEAU_1 = 'DRH'
 DRH_NIVEAU_2 = 'DRH-2'
 ACCIOR = 'ACCIOR'
 ABF = 'ABF'
+FINANCE = 'Finance'
 HAUTE_DIRECTION = 'Haute direction'
 SERVICE_UTILISATEURS = 'Service utilisateurs'
 
-# Groupes impliqués dans le Worflow
-#grp_correspondants_rh = safe_create_groupe(name=CORRESPONDANT_RH)
-#grp_administrateurs = safe_create_groupe(name=ADMINISTRATEURS)
-#grp_directeurs_bureau = safe_create_groupe(name=DIRECTEUR_DE_BUREAU)
-#grp_drh = safe_create_groupe(name=DRH_NIVEAU_1)
-#grp_drh2 = safe_create_groupe(name=DRH_NIVEAU_2)
-#grp_accior = safe_create_groupe(name=ACCIOR)
-#grp_abf = safe_create_groupe(name=ABF)
-#grp_haute_direction = safe_create_groupe(name=HAUTE_DIRECTION)
-#grp_service_utilisateurs = safe_create_groupe(name=SERVICE_UTILISATEURS)
 
 def get_employe_from_user(user):
-  """
-  Retourne un employé AUF à partir de son user Django. 
-  """
-  try:
-      employe = ref.Authentification.objects.get(courriel=user.email).id
-      # ajouter coordonnées via ref.Employe? (courriel, tel, etc.)
-  except:
-      raise Exception(u"L'employé avec le courriel %s n'a pas été trouvé dans le référentiel." % user.email)
-  return employe
+    """
+    Retourne un employé AUF à partir de son user Django.
+    """
+    import auf.django.references.models as ref
+    try:
+        employe = ref.Employe.objects.get(courriel=user.email)
+    except ObjectDoesNotExist:
+        raise Exception(u"L'employé avec le courriel %s n'a pas \
+                été trouvé dans le référentiel." % user.email)
+    return employe
+
 
 def is_user_dans_services_centraux(user):
     employe = get_employe_from_user(user)
@@ -40,5 +33,6 @@ def is_user_dans_services_centraux(user):
         return False
     return employe.implantation_id in (15, 19)
 
+
 def is_user_dans_region(user):
     return not is_user_dans_services_centraux(user)
index 03e4c1a..ff84e67 100644 (file)
@@ -66,11 +66,8 @@ class CustomMenu(Menu):
                         items.MenuItem('Rapport des modifications',
                             reverse('rhr_historique_des_modifications')),
                         )
-            self.children += [
-                items.MenuItem('Rapports',
-                    children=rapports),
 
-                items.MenuItem('Organigrammes',
+            items_organigrammes = items.MenuItem('Organigrammes',
                     children=[
                         items.MenuItem('Organigramme par employé',
                             reverse('admin:rh_employeproxy_changelist')),
@@ -80,13 +77,22 @@ class CustomMenu(Menu):
                             reverse('admin:rh_implantationproxy_changelist')),
                         items.MenuItem('Organigramme par bureau',
                             reverse('admin:rh_regionproxy_changelist')),
-                        ]),
-                items.MenuItem('Requêtes',
+                        ])
+
+            items_rapports = items.MenuItem('Rapports', children=rapports)
+
+            items_requetes = items.MenuItem('Requêtes',
                     children=[
                         items.MenuItem('Requêtes sauvegardées',
                             reverse('admin:django_qbe_savedquery_changelist')),
                         items.MenuItem('Constructeur de requêtes',
                             reverse('qbe_form')),
-                        ]),
-            ]
+                        ])
+
+            children = [items_rapports, items_organigrammes, ]
+            if in_drh_or_admin(request.user):
+                children.append(items_requetes)
+
+            self.children += children
+
         super(CustomMenu, self).init_with_context(context)
index 213b55c..c9318b3 100644 (file)
@@ -20,6 +20,12 @@ def user_can_add_obj(user):
         return True
     return False
 
+def user_can_list_obj(user):
+    if user_gere_obj_de_sa_region(user) or \
+            in_drh_or_admin(user):
+        return True
+    return False
+
 def user_can_change_obj(user, obj):
     if in_drh_or_admin(user) or (
             user_gere_obj_de_sa_region(user) and \
@@ -38,11 +44,11 @@ def obj_in_region_user(user, obj):
     if isinstance(obj, rh.Dossier):
         return True
     if isinstance(obj, rh.Poste):
-        return obj.implantation.region == region_user
+        return obj.implantation.zone_administrative == region_user
     return False
     
 def get_region_user(user):
     # gère actuellement qu'une seule région par user
     employe = get_employe_from_user(user)
-    region_user = employe.implantation.region
+    region_user = employe.implantation.zone_administrative
     return region_user
index 4bc35f3..81f954b 100644 (file)
@@ -2,24 +2,31 @@
 
 import textwrap
 
-from auf.django.emploi.models import OffreEmploi, Candidat, CandidatPiece
-from auf.django.references.models import Region, Bureau
+from auf.django.emploi.models import CandidatPiece, Candidat, OffreEmploi
+from auf.django.references.models import Region, Bureau, Implantation
 from django.conf import settings
 from django.contrib import admin
 from django.core.urlresolvers import reverse
 from django.db.models import Avg
+from django.shortcuts import render_to_response
+from django.template import RequestContext
+
+from auf.django.export.admin import ExportAdmin
+from auf.django.emploi.models import STATUT_CHOICES
 from django.forms.models import BaseInlineFormSet
 from django.http import HttpResponseRedirect
 from django.shortcuts import redirect
 from reversion.admin import VersionAdmin
 
 from project import groups
+
 from project.rh import models as rh
 from project.recrutement.forms import OffreEmploiForm
 from project.recrutement.models import \
         Evaluateur, CandidatEvaluation, \
         ProxyOffreEmploi, ProxyCandidat, MesCandidatEvaluation, \
-        CourrielTemplate
+        CourrielTemplate, OffreEmploiEvaluateur
+
 
 ### CONSTANTES
 IMPLANTATIONS_CENTRALES = [15, 19]
@@ -58,6 +65,24 @@ class OffreEmploiAdminMixin(BaseAdmin):
     list_filter = ('statut',)
     actions = ['affecter_evaluateurs_offre_emploi', ]
     form = OffreEmploiForm
+    fieldsets = (
+        (None, {
+            'fields': (
+                'est_affiche',
+                'statut',
+                'date_limite',
+                'nom',
+                'description',
+                'poste',
+                'region',
+                'lieu_affectation',
+                'bureau',
+                'debut_affectation',
+                'duree_affectation',
+                'renumeration',
+            )
+        }),
+    )
 
     ### Actions à afficher
     def get_actions(self, request):
@@ -91,17 +116,12 @@ class OffreEmploiAdminMixin(BaseAdmin):
         user_groupes = [g.name for g in request.user.groups.all()]
 
         # Region
-        
-        if 'region' in form.declared_fields:
+        region_field = None
+        if 'region' in form.declared_fields.keys():
             region_field = form.declared_fields['region']
-            read_only = False
-        elif 'region' in form.base_fields:
+        if 'region' in form.base_fields.keys():
             region_field = form.base_fields['region']
-            read_only = False
-        else:
-            read_only = True
-
-        if not read_only:
+        if region_field:
             if groups.DRH_NIVEAU_1 in user_groupes or \
                groups.DRH_NIVEAU_2 in user_groupes or \
                groups.HAUTE_DIRECTION in user_groupes:
@@ -111,16 +131,12 @@ class OffreEmploiAdminMixin(BaseAdmin):
                                     filter(id=employe.implantation.region.id)
 
         # Poste
-        if 'poste' in form.declared_fields:
+        poste_field = None
+        if 'poste' in form.declared_fields.keys():
             poste_field = form.declared_fields['poste']
-            read_only = False
-        elif 'poste' in form.base_fields:
+        if 'poste' in form.base_fields.keys():
             poste_field = form.base_fields['poste']
-            read_only = False
-        else:
-            read_only = True
-
-        if not read_only:
+        if poste_field:
             if groups.DRH_NIVEAU_1 in user_groupes or \
                groups.DRH_NIVEAU_2 in user_groupes or \
                groups.HAUTE_DIRECTION in user_groupes:
@@ -131,15 +147,12 @@ class OffreEmploiAdminMixin(BaseAdmin):
                         exclude(implantation__in=IMPLANTATIONS_CENTRALES)
 
         # Bureau
-        if 'bureau' in form.declared_fields:
+        bureau_field = None
+        if 'bureau' in form.declared_fields.keys():
             bureau_field = form.declared_fields['bureau']
-            read_only = False
-        elif 'bureau' in form.base_fields:
+        if 'bureau' in form.base_fields.keys():
             bureau_field = form.base_fields['bureau']
-            read_only = False
-        else:
-            read_only = True
-        if not read_only:
+        if bureau_field:
             if groups.DRH_NIVEAU_1 in user_groupes or \
                 groups.DRH_NIVEAU_2 in user_groupes or \
                 groups.HAUTE_DIRECTION in user_groupes:
@@ -230,6 +243,17 @@ class OffreEmploiAdminMixin(BaseAdmin):
 
         return False
 
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'lieu_affectation':
+            user_groupes = [g.name for g in request.user.groups.all()]
+            if not (request.user.is_superuser is True or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes):
+                employe = groups.get_employe_from_user(request.user)
+                kwargs["queryset"] = Implantation.objects.filter(region=employe.implantation.region)
+            return db_field.formfield(**kwargs)
+        return super(OffreEmploiAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
 
 class OffreEmploiAdmin(VersionAdmin, OffreEmploiAdminMixin):
     pass
@@ -266,11 +290,6 @@ class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
     def response_change(self, request, obj):
         return redirect('admin:recrutement_proxyoffreemploi_changelist')
 
-    ### Formulaire
-    def get_form(self, request, obj=None, **kwargs):
-        form = super(ProxyOffreEmploiAdmin, self).get_form(request, obj, **kwargs)
-        return form
-
     ### Permissions add, delete, change
     def has_add_permission(self, request):
         return False
@@ -279,20 +298,10 @@ class ProxyOffreEmploiAdmin(OffreEmploiAdminMixin):
         return False
 
     def has_change_permission(self, request, obj=None):
-        user_groupes = [g.name for g in request.user.groups.all()]
-        if request.user.is_superuser is True or \
-            groups.CORRESPONDANT_RH in user_groupes or \
-            groups.DRH_NIVEAU_1 in user_groupes or \
-            groups.DRH_NIVEAU_2 in user_groupes or \
-            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
-            groups.ADMINISTRATEURS in user_groupes or \
-            groups.HAUTE_DIRECTION in user_groupes:
-            return True
-
         if obj is not None:
             return True
 
-        return False
+        return not super(ProxyOffreEmploiAdmin, self).has_change_permission(request, obj)
 
 
 class CandidatPieceInline(admin.TabularInline):
@@ -333,14 +342,14 @@ class CandidatEvaluationInline(admin.TabularInline):
         return self.readonly_fields
 
 
-class CandidatAdminMixin(BaseAdmin):
+class CandidatAdminMixin(BaseAdmin, ExportAdmin):
     search_fields = ('nom', 'prenom')
     exclude = ('actif', )
     list_editable = ('statut', )
     list_display = ('_candidat', 'offre_emploi',
                     'voir_offre_emploi', 'calculer_moyenne',
                     'afficher_candidat', '_date_creation', 'statut', )
-    list_filter = ('offre_emploi', 'offre_emploi__region', 'statut', )
+    list_filter = ('offre_emploi__nom', 'offre_emploi__region', 'statut', )
 
     fieldsets = (
         ("Offre d'emploi", {
@@ -348,7 +357,7 @@ class CandidatAdminMixin(BaseAdmin):
         }),
         ('Informations personnelles', {
             'fields': (
-                'prenom', 'nom', 'genre', 'nationalite',
+                'nom', 'prenom', 'genre', 'nationalite',
                 'situation_famille', 'nombre_dependant'
             )
         }),
@@ -372,7 +381,13 @@ class CandidatAdminMixin(BaseAdmin):
         CandidatPieceInline,
         CandidatEvaluationInline,
     ]
-    actions = ['envoyer_courriel_candidats']
+    actions = ['envoyer_courriel_candidats', 'changer_statut']
+
+    export_fields = ['statut', 'offre_emploi', 'prenom', 'nom', 'genre',
+                     'nationalite', 'situation_famille', 'nombre_dependant',
+                     'niveau_diplome', 'employeur_actuel', 'poste_actuel',
+                     'domaine_professionnel', 'telephone', 'email', 'adresse',
+                     'ville', 'etat_province', 'code_postal', 'pays']
 
     def _candidat(self, obj):
         txt = u"%s %s (%s)" % (obj.nom.upper(), obj.prenom, obj.genre)
@@ -402,6 +417,23 @@ class CandidatAdminMixin(BaseAdmin):
         )
     envoyer_courriel_candidats.short_description = u'Envoyer courriel'
 
+    ### Changer le statut à des candidats
+    def changer_statut(modeladmin, request, queryset):
+        if request.POST.get('post'):
+            queryset.update(statut=request.POST.get('statut'))
+            return None
+
+        context = {
+            'action_checkbox_name': admin.helpers.ACTION_CHECKBOX_NAME,
+            'queryset': queryset,
+            'status': STATUT_CHOICES,
+        }
+
+        return render_to_response("recrutement/selectionner_statut.html",
+                context, context_instance = RequestContext(request))
+
+    changer_statut.short_description = u'Changer statut'
+
     ### Évaluer un candidat
     def evaluer_candidat(self, obj):
         return "<a href='%s?candidat__id__exact=%s'>" \
@@ -417,7 +449,7 @@ class CandidatAdminMixin(BaseAdmin):
         items = [u"<li><a href='%s%s'>%s</li>" % \
                 (settings.OE_PRIVE_MEDIA_URL, pj.path, pj.get_nom_display()) \
                 for pj in obj.pieces_jointes()]
-        html = "<a href='%s'>Voir le candidat</a>" % (
+        html = "<a href='%s'>Candidature</a>" % (
             reverse('admin:recrutement_proxycandidat_change', args=(obj.id,))
         )
         return "%s<ul>%s</ul>" % (html, "\n".join(items))
@@ -502,6 +534,21 @@ class CandidatAdminMixin(BaseAdmin):
             return True
         return False
 
+    def formfield_for_foreignkey(self, db_field, request, **kwargs):
+        if db_field.name == 'offre_emploi':
+            employe = groups.get_employe_from_user(request.user)
+            user_groupes = [g.name for g in request.user.groups.all()]
+            if request.user.is_superuser is True or \
+                groups.CORRESPONDANT_RH in user_groupes or \
+                groups.DRH_NIVEAU_1 in user_groupes or \
+                groups.DRH_NIVEAU_2 in user_groupes:
+                qs_offres = OffreEmploi.objects.all()
+            else:
+                qs_offres =OffreEmploi.objects.filter(region=employe.implantation.region)
+            kwargs["queryset"] = qs_offres
+            return db_field.formfield(**kwargs)
+        return super(CandidatAdminMixin, self).formfield_for_foreignkey(db_field, request, **kwargs)
+
     def get_changelist(self, request, **kwargs):
         return OrderedChangeList
 
@@ -538,10 +585,12 @@ class CandidatAdminMixin(BaseAdmin):
 
 
 class CandidatAdmin(VersionAdmin, CandidatAdminMixin):
+    change_list_template = 'admin/recrutement/candidat/change_list.html'
     pass
 
 
 class ProxyCandidatAdmin(CandidatAdminMixin):
+    change_list_template = 'admin/recrutement/candidat/change_list.html'
     list_editable = ()
     readonly_fields = (
         'statut', 'offre_emploi', 'prenom', 'nom', 'genre', 'nationalite',
@@ -582,23 +631,17 @@ class ProxyCandidatAdmin(CandidatAdminMixin):
         return False
 
     def has_change_permission(self, request, obj=None):
-        user_groupes = [g.name for g in request.user.groups.all()]
-        if request.user.is_superuser is True or \
-            groups.CORRESPONDANT_RH in user_groupes or \
-            groups.DRH_NIVEAU_1 in user_groupes or \
-            groups.DRH_NIVEAU_2 in user_groupes or \
-            groups.DIRECTEUR_DE_BUREAU in user_groupes or \
-            groups.ADMINISTRATEURS in user_groupes or \
-            groups.HAUTE_DIRECTION in user_groupes:
-            return True
-
         if obj is not None:
-            evaluateur = Evaluateur.objects.get(user=request.user)
-            for e in obj.evaluations.all():
-                if e.evaluateur == evaluateur:
-                    return True
-
-        return False
+            return obj in self.queryset(request)
+            #try:
+            #    evaluateur = Evaluateur.objects.get(user=request.user)
+            #    for e in obj.evaluations.all():
+            #        if e.evaluateur == evaluateur:
+            #            return True
+            #    return False
+            #except:
+            #    pass
+        return super(ProxyCandidatAdmin, self).has_change_permission(request, obj)
 
     def get_actions(self, request):
         return None
@@ -791,7 +834,7 @@ class CandidatEvaluationAdmin(BaseAdmin):
             groups.DIRECTEUR_DE_BUREAU in user_groupes or \
             groups.ADMINISTRATEURS in user_groupes or \
             groups.HAUTE_DIRECTION in user_groupes:
-            return qs
+            return qs.filter(candidat__statut__in=('REC', 'SEL'))
 
         evaluateur = Evaluateur.objects.get(user=request.user)
         candidats_evaluations = \
@@ -802,6 +845,7 @@ class CandidatEvaluationAdmin(BaseAdmin):
 
 
 class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
+    list_filter = []
 
     def has_change_permission(self, request, obj=None):
         try:
@@ -829,6 +873,10 @@ class MesCandidatEvaluationAdmin(CandidatEvaluationAdmin):
         return qs.filter(id__in=candidats_evaluations_ids)
 
 
+class OffreEmploiEvaluateurAdmin(BaseAdmin):
+    pass
+
+
 class CourrielTemplateAdmin(BaseAdmin, VersionAdmin):
     ### Actions à afficher
     def get_actions(self, request):
@@ -844,3 +892,4 @@ admin.site.register(CandidatEvaluation, CandidatEvaluationAdmin)
 admin.site.register(MesCandidatEvaluation, MesCandidatEvaluationAdmin)
 admin.site.register(Evaluateur, EvaluateurAdmin)
 admin.site.register(CourrielTemplate, CourrielTemplateAdmin)
+admin.site.register(OffreEmploiEvaluateur, OffreEmploiEvaluateurAdmin)
index e3ff7d7..94e0577 100644 (file)
@@ -4,6 +4,7 @@ from django import forms
 from django.forms import ModelForm
 
 from auf.django.emploi import forms as emploi
+from auf.django.emploi import models as emploi_models
 
 from project.recrutement import models as recr
 
@@ -74,6 +75,11 @@ class PostulerOffreEmploiForm(emploi.PostulerOffreEmploiForm):
     pass
 
 class OffreEmploiForm(ModelForm):
+    nom = forms.CharField(label='Intitulé du poste')
+
+    class Meta:
+        model = emploi_models.OffreEmploi
+
     def clean(self):
         cleaned_data = self.cleaned_data
         date_limite = cleaned_data.get("date_limite")
index bc38133..3472b34 100644 (file)
Binary files a/project/recrutement/locale/fr/LC_MESSAGES/django.mo and b/project/recrutement/locale/fr/LC_MESSAGES/django.mo differ
index 8fe0685..4d93b61 100644 (file)
@@ -20,7 +20,7 @@ msgstr ""
 #: __init__.py:2 templates/recrutement/affecter_evaluateurs.html:17
 #: templates/recrutement/selectionner_template.html:12
 msgid "Recrutement"
-msgstr "Gestion des candidatures"
+msgstr "Recrutement"
 
 #: templates/admin/recrutement/candidat/change_form.html:19
 #: templates/admin/recrutement/proxycandidat/change_form.html:19
diff --git a/project/recrutement/migrations/0005_auto__add_offreemploievaluateur.py b/project/recrutement/migrations/0005_auto__add_offreemploievaluateur.py
new file mode 100644 (file)
index 0000000..502b960
--- /dev/null
@@ -0,0 +1,241 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import SchemaMigration
+from django.db import models
+
+class Migration(SchemaMigration):
+    
+    def forwards(self, orm):
+        
+        # Adding model 'OffreEmploiEvaluateur'
+        db.create_table('recrutement_offreemploievaluateur', (
+            ('offre_emploi', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['emploi.OffreEmploi'], db_column='offre_emploi')),
+            ('evaluateur', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['recrutement.Evaluateur'], db_column='evaluateur')),
+            ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
+        ))
+        db.send_create_signal('recrutement', ['OffreEmploiEvaluateur'])
+    
+    
+    def backwards(self, orm):
+        
+        # Deleting model 'OffreEmploiEvaluateur'
+        db.delete_table('recrutement_offreemploievaluateur')
+    
+    
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'emploi.candidat': {
+            'Meta': {'object_name': 'Candidat'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'adresse': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'code_postal': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'date_creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'domaine_professionnel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'employeur_actuel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'etat_province': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'genre': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nationalite': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'nationalite'", 'to': "orm['references.Pays']"}),
+            'niveau_diplome': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nombre_dependant': ('django.db.models.fields.IntegerField', [], {}),
+            'offre_emploi': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'offre_emploi'", 'to': "orm['emploi.OffreEmploi']"}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'pays'", 'to': "orm['references.Pays']"}),
+            'poste_actuel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'prenom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'situation_famille': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'statut': ('django.db.models.fields.CharField', [], {'default': "'NOUV'", 'max_length': '4'}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'ville': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'emploi.offreemploi': {
+            'Meta': {'object_name': 'OffreEmploi'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'bureau': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Bureau']", 'db_column': "'bureau'"}),
+            'date_creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'date_limite': ('django.db.models.fields.DateField', [], {}),
+            'debut_affectation': ('django.db.models.fields.DateField', [], {}),
+            'description': ('django.db.models.fields.TextField', [], {}),
+            'duree_affectation': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'est_affiche': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lieu_affectation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Implantation']", 'db_column': "'implantation'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'poste': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'poste_nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"}),
+            'renumeration': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'resume': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'statut': ('django.db.models.fields.CharField', [], {'default': "'NOUV'", 'max_length': '4'})
+        },
+        'recrutement.candidat': {
+            'Meta': {'object_name': 'Candidat', 'db_table': "'emploi_candidat'", '_ormbases': ['emploi.Candidat']}
+        },
+        'recrutement.candidatcourriel': {
+            'Meta': {'object_name': 'CandidatCourriel'},
+            'candidats': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['emploi.Candidat']", 'symmetrical': 'False'}),
+            'html': ('tinymce.models.HTMLField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'plain_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'sujet': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'template'", 'to': "orm['recrutement.CourrielTemplate']"})
+        },
+        'recrutement.candidatevaluation': {
+            'Meta': {'object_name': 'CandidatEvaluation'},
+            'candidat': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'evaluations'", 'db_column': "'candidat'", 'to': "orm['emploi.Candidat']"}),
+            'commentaire': ('django.db.models.fields.TextField', [], {'default': "'Aucun'", 'null': 'True', 'blank': 'True'}),
+            'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'evaluateur': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'evaluateur'", 'to': "orm['recrutement.Evaluateur']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'note': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'recrutement.courrieltemplate': {
+            'Meta': {'object_name': 'CourrielTemplate'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom_modele': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'plain_text': ('django.db.models.fields.TextField', [], {}),
+            'sujet': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'recrutement.evaluateur': {
+            'Meta': {'object_name': 'Evaluateur'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'recrutement.offreemploievaluateur': {
+            'Meta': {'unique_together': "(('offre_emploi', 'evaluateur'),)", 'object_name': 'OffreEmploiEvaluateur'},
+            'evaluateur': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['recrutement.Evaluateur']", 'db_column': "'evaluateur'"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'offre_emploi': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['emploi.OffreEmploi']", 'db_column': "'offre_emploi'"})
+        },
+        'references.bureau': {
+            'Meta': {'object_name': 'Bureau', 'db_table': "u'ref_bureau'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'implantation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Implantation']", 'db_column': "'implantation'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"})
+        },
+        'references.implantation': {
+            'Meta': {'object_name': 'Implantation', 'db_table': "u'ref_implantation'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'adresse_physique_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_physique_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_physique'", 'to_field': "'code'", 'db_column': "'adresse_physique_pays'", 'to': "orm['references.Pays']"}),
+            'adresse_physique_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'adresse_postale_boite_postale': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_postale_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_postale'", 'to_field': "'code'", 'db_column': "'adresse_postale_pays'", 'to': "orm['references.Pays']"}),
+            'adresse_postale_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'bureau_rattachement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Implantation']", 'db_column': "'bureau_rattachement'"}),
+            'code_meteo': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'commentaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'courriel_interne': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'date_extension': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_fermeture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_inauguration': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_ouverture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fax_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fuseau_horaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'hebergement_convention': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_convention_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_etablissement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'modif_date': ('django.db.models.fields.DateField', [], {}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"}),
+            'remarque': ('django.db.models.fields.TextField', [], {}),
+            'responsable_implantation': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'statut': ('django.db.models.fields.IntegerField', [], {}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'telephone_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'zone_administrative': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.ZoneAdministrative']"})
+        },
+        'references.pays': {
+            'Meta': {'object_name': 'Pays', 'db_table': "u'ref_pays'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2'}),
+            'code_bureau': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Bureau']", 'to_field': "'code'", 'null': 'True', 'db_column': "'code_bureau'", 'blank': 'True'}),
+            'code_iso3': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}),
+            'developpement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'monnaie': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nord_sud': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"})
+        },
+        'references.region': {
+            'Meta': {'object_name': 'Region', 'db_table': "u'ref_region'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'implantation_bureau': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'gere_region'", 'null': 'True', 'db_column': "'implantation_bureau'", 'to': "orm['references.Implantation']"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'references.zoneadministrative': {
+            'Meta': {'object_name': 'ZoneAdministrative', 'db_table': "'ref_zoneadministrative'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '4', 'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        }
+    }
+    
+    complete_apps = ['recrutement']
diff --git a/project/recrutement/migrations/0006_convert_candidatevaluation.py b/project/recrutement/migrations/0006_convert_candidatevaluation.py
new file mode 100644 (file)
index 0000000..6d2fbfa
--- /dev/null
@@ -0,0 +1,235 @@
+# encoding: utf-8
+import datetime
+from south.db import db
+from south.v2 import DataMigration
+from django.db import models
+
+class Migration(DataMigration):
+
+    def forwards(self, orm):
+        "Write your forwards methods here."
+        for ce in orm.CandidatEvaluation.objects.all():
+            orm.OffreEmploiEvaluateur.objects.get_or_create(
+                offre_emploi=ce.candidat.offre_emploi,
+                evaluateur=ce.evaluateur
+            )
+
+    def backwards(self, orm):
+        "Write your backwards methods here."
+
+    models = {
+        'auth.group': {
+            'Meta': {'object_name': 'Group'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
+            'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
+        },
+        'auth.permission': {
+            'Meta': {'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
+            'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
+        },
+        'auth.user': {
+            'Meta': {'object_name': 'User'},
+            'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
+            'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
+            'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
+            'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
+        },
+        'contenttypes.contenttype': {
+            'Meta': {'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
+            'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'emploi.candidat': {
+            'Meta': {'object_name': 'Candidat'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'adresse': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'code_postal': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'date_creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'domaine_professionnel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'email': ('django.db.models.fields.EmailField', [], {'max_length': '255'}),
+            'employeur_actuel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'etat_province': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'genre': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nationalite': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'nationalite'", 'to': "orm['references.Pays']"}),
+            'niveau_diplome': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nombre_dependant': ('django.db.models.fields.IntegerField', [], {}),
+            'offre_emploi': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'offre_emploi'", 'to': "orm['emploi.OffreEmploi']"}),
+            'pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'pays'", 'to': "orm['references.Pays']"}),
+            'poste_actuel': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'prenom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'situation_famille': ('django.db.models.fields.CharField', [], {'max_length': '1'}),
+            'statut': ('django.db.models.fields.CharField', [], {'default': "'NOUV'", 'max_length': '4'}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'ville': ('django.db.models.fields.CharField', [], {'max_length': '255'})
+        },
+        'emploi.offreemploi': {
+            'Meta': {'object_name': 'OffreEmploi'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'bureau': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Bureau']", 'db_column': "'bureau'"}),
+            'date_creation': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'date_limite': ('django.db.models.fields.DateField', [], {}),
+            'debut_affectation': ('django.db.models.fields.DateField', [], {}),
+            'description': ('django.db.models.fields.TextField', [], {}),
+            'duree_affectation': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'est_affiche': ('django.db.models.fields.BooleanField', [], {'default': 'False', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'lieu_affectation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Implantation']", 'db_column': "'implantation'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'poste': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'poste_nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"}),
+            'renumeration': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'resume': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'statut': ('django.db.models.fields.CharField', [], {'default': "'NOUV'", 'max_length': '4'})
+        },
+        'recrutement.candidat': {
+            'Meta': {'object_name': 'Candidat', 'db_table': "'emploi_candidat'", '_ormbases': ['emploi.Candidat']}
+        },
+        'recrutement.candidatcourriel': {
+            'Meta': {'object_name': 'CandidatCourriel'},
+            'candidats': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['emploi.Candidat']", 'symmetrical': 'False'}),
+            'html': ('tinymce.models.HTMLField', [], {'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'plain_text': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
+            'sujet': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'template'", 'to': "orm['recrutement.CourrielTemplate']"})
+        },
+        'recrutement.candidatevaluation': {
+            'Meta': {'object_name': 'CandidatEvaluation'},
+            'candidat': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'evaluations'", 'db_column': "'candidat'", 'to': "orm['emploi.Candidat']"}),
+            'commentaire': ('django.db.models.fields.TextField', [], {'default': "'Aucun'", 'null': 'True', 'blank': 'True'}),
+            'date': ('django.db.models.fields.DateField', [], {'auto_now_add': 'True', 'blank': 'True'}),
+            'evaluateur': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'db_column': "'evaluateur'", 'to': "orm['recrutement.Evaluateur']"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'note': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'})
+        },
+        'recrutement.courrieltemplate': {
+            'Meta': {'object_name': 'CourrielTemplate'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'nom_modele': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
+            'plain_text': ('django.db.models.fields.TextField', [], {}),
+            'sujet': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        },
+        'recrutement.evaluateur': {
+            'Meta': {'object_name': 'Evaluateur'},
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
+        },
+        'recrutement.offreemploievaluateur': {
+            'Meta': {'unique_together': "(('offre_emploi', 'evaluateur'),)", 'object_name': 'OffreEmploiEvaluateur'},
+            'evaluateur': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['recrutement.Evaluateur']", 'db_column': "'evaluateur'"}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'offre_emploi': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['emploi.OffreEmploi']", 'db_column': "'offre_emploi'"})
+        },
+        'references.bureau': {
+            'Meta': {'object_name': 'Bureau', 'db_table': "u'ref_bureau'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'implantation': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Implantation']", 'db_column': "'implantation'"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"})
+        },
+        'references.implantation': {
+            'Meta': {'object_name': 'Implantation', 'db_table': "u'ref_implantation'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'adresse_physique_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_physique_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
+            'adresse_physique_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_physique'", 'to_field': "'code'", 'db_column': "'adresse_physique_pays'", 'to': "orm['references.Pays']"}),
+            'adresse_physique_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'adresse_physique_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'adresse_postale_boite_postale': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_bureau': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_code_postal': ('django.db.models.fields.CharField', [], {'max_length': '20', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_code_postal_avant_ville': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'adresse_postale_no': ('django.db.models.fields.CharField', [], {'max_length': '30', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_pays': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'impl_adresse_postale'", 'to_field': "'code'", 'db_column': "'adresse_postale_pays'", 'to': "orm['references.Pays']"}),
+            'adresse_postale_precision': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_precision_avant': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_region': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_rue': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'adresse_postale_ville': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'bureau_rattachement': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Implantation']", 'db_column': "'bureau_rattachement'"}),
+            'code_meteo': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'commentaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'courriel': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'courriel_interne': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
+            'date_extension': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_fermeture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_inauguration': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'date_ouverture': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'fax': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fax_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'fuseau_horaire': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'hebergement_convention': ('django.db.models.fields.NullBooleanField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_convention_date': ('django.db.models.fields.DateField', [], {'null': 'True', 'blank': 'True'}),
+            'hebergement_etablissement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'modif_date': ('django.db.models.fields.DateField', [], {}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nom_court': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'nom_long': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"}),
+            'remarque': ('django.db.models.fields.TextField', [], {}),
+            'responsable_implantation': ('django.db.models.fields.IntegerField', [], {'null': 'True', 'blank': 'True'}),
+            'statut': ('django.db.models.fields.IntegerField', [], {}),
+            'telephone': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'telephone_interne': ('django.db.models.fields.CharField', [], {'max_length': '255', 'blank': 'True'}),
+            'type': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'url': ('django.db.models.fields.URLField', [], {'max_length': '255', 'blank': 'True'}),
+            'zone_administrative': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.ZoneAdministrative']"})
+        },
+        'references.pays': {
+            'Meta': {'object_name': 'Pays', 'db_table': "u'ref_pays'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '2'}),
+            'code_bureau': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Bureau']", 'to_field': "'code'", 'null': 'True', 'db_column': "'code_bureau'", 'blank': 'True'}),
+            'code_iso3': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '3'}),
+            'developpement': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'monnaie': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
+            'nord_sud': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True', 'blank': 'True'}),
+            'region': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['references.Region']", 'db_column': "'region'"})
+        },
+        'references.region': {
+            'Meta': {'object_name': 'Region', 'db_table': "u'ref_region'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
+            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
+            'implantation_bureau': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'gere_region'", 'null': 'True', 'db_column': "'implantation_bureau'", 'to': "orm['references.Implantation']"}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'})
+        },
+        'references.zoneadministrative': {
+            'Meta': {'object_name': 'ZoneAdministrative', 'db_table': "'ref_zoneadministrative'", 'managed': 'False'},
+            'actif': ('django.db.models.fields.BooleanField', [], {'default': 'True', 'blank': 'True'}),
+            'code': ('django.db.models.fields.CharField', [], {'max_length': '4', 'primary_key': 'True'}),
+            'nom': ('django.db.models.fields.CharField', [], {'max_length': '100'})
+        }
+    }
+
+    complete_apps = ['recrutement']
index 06b01e0..10d8175 100644 (file)
@@ -108,6 +108,37 @@ def sync_delete_groupe_evaluateur(sender, **kwargs):
     instance.user.save()
 
 
+class OffreEmploiEvaluateur(models.Model):
+    offre_emploi = models.ForeignKey(emploi.OffreEmploi, db_column='offre_emploi',
+                                     verbose_name=u'offre d\'emploi')
+    evaluateur = models.ForeignKey(Evaluateur, db_column='evaluateur',
+                                   verbose_name=u'évaluateur')
+
+    class Meta:
+        unique_together = ('offre_emploi', 'evaluateur')
+        verbose_name = 'Évaluateur - offre emploi'
+        verbose_name_plural = 'Évaluateurs - offre emploi'
+
+    def __unicode__(self):
+        return u"%s - %s" % (self.offre_emploi, self.evaluateur)
+
+    def save(self):
+
+        if self.pk is None:
+            candidats = Candidat.objects.filter(offre_emploi=self.offre_emploi)
+            for candidat in candidats:
+                candidat_evaluation, created = CandidatEvaluation.objects.get_or_create(candidat=candidat, evaluateur=self.evaluateur)
+
+        super(OffreEmploiEvaluateur, self).save()
+
+    def delete(self):
+        candidats = Candidat.objects.filter(offre_emploi=self.offre_emploi)
+        for candidat in candidats:
+            CandidatEvaluation.objects.filter(candidat=candidat, evaluateur=self.evaluateur).delete()
+
+        super(OffreEmploiEvaluateur, self).delete()
+
+
 class CandidatEvaluation(models.Model):
     candidat = models.ForeignKey(emploi.Candidat, db_column='candidat',
                 related_name='evaluations',)
diff --git a/project/recrutement/templates/admin/recrutement/candidat/change_list.html b/project/recrutement/templates/admin/recrutement/candidat/change_list.html
new file mode 100644 (file)
index 0000000..253a058
--- /dev/null
@@ -0,0 +1,17 @@
+{% extends "admin/change_list.html" %}
+{% load i18n %}
+
+
+{% block object-tools %}
+    <ul class="object-tools">
+        {% if not is_popup %}
+                       <li><a href="{{ export_csv_url }}">{% trans 'Export CSV' %}</a></li>
+                       <li><a href="{{ export_ods_url }}">{% trans 'Export ODS' %}</a></li>
+            <li><a href="{{recoverlist_url}}" class="recoverlink">{% blocktrans with cl.opts.verbose_name_plural|escape as name %}Recover deleted {{name}}{% endblocktrans %}</a></li>
+        {% endif %}
+               {% if has_add_permission %}
+            <li><a href="{{add_url}}{% if is_popup %}?_popup=1{% endif %}" class="addlink">{% blocktrans with cl.opts.verbose_name|escape as name %}Add {{name}}{% endblocktrans %}</a></li>
+        {% endif %}
+    </ul>
+{% endblock %}
+
diff --git a/project/recrutement/templates/recrutement/selectionner_statut.html b/project/recrutement/templates/recrutement/selectionner_statut.html
new file mode 100644 (file)
index 0000000..b60e3e8
--- /dev/null
@@ -0,0 +1,60 @@
+{% extends 'admin/base_site.html' %}
+{% load i18n adminmedia form_utils_tags %}
+
+{% block title %}RH - Recrutement{% endblock %}
+{% block sous_titre %}Changer statut aux candidats{% endblock %}
+{% block extrahead %}
+{{ form.media }}
+{% endblock %}
+{% block breadcrumbs %}{% if not is_popup %}
+<div class="breadcrumbs">
+     <a href="../../">{% trans "Home" %}</a> &rsaquo;
+     <a href="../">{% trans "Recrutement" %}</a> &rsaquo;
+     {% trans "Changer statut - Sélectionner le statut" %}
+</div>
+{% endif %}{% endblock %}
+
+
+{% block content %}
+<div id="content-main">
+    {% block object-tools %}{% endblock %}
+
+    <div class="module">
+        <h2>Sélectionner le statut</h2>
+
+        <form action="" method="post">{% csrf_token %}
+            <table>
+                <tr>
+                    <td>Changer le statut des candidats suivant:</td>
+                    <td>
+                        <ul>
+                            {{ queryset|unordered_list }}
+                        </ul>
+                    </td>
+                </tr>
+                <tr>
+                    <td>Nouveau statut</td>
+                    <td>
+                        <select name="statut">
+                        {% for statut in status %}
+                        <option value="{{ statut.0 }}">{{ statut.1 }}</option>
+                        {% endfor %}
+                        </select>
+                    </td>
+                </tr>
+            </table>
+            {% for obj in queryset %}
+            <input type="hidden" name="{{ action_checkbox_name }}" value="{{ obj.pk }}" />
+            {% endfor %}
+            <input type="hidden" name="action" value="changer_statut" />
+            <input type="hidden" name="post" value="yes" />
+            <div class="submit-row">
+                <input type="submit" value="Changer">
+            </div>
+        </form>
+    </div>
+
+
+</div>
+
+{% endblock %}
index d9755cb..02ee759 100644 (file)
@@ -23,7 +23,7 @@ def mediaserve(request, path, document_root=None, show_indexes=False):
     grant_ok = False
     user = request.user
 
-    for grp in user.groups.all():
+    for grp in [g.name for g in user.groups.all()]:
         if grp in recrutement_groupes:
             grant_ok = True
             break
@@ -83,7 +83,7 @@ def envoyer_courriel_candidats(request):
                 request, messages.SUCCESS,
                 "Le email a été envoyé aux candidats."
             )
-            return redirect("admin:recrutement_candidat_changelist")
+            return redirect("admin:emploi_candidat_changelist")
     else:
         form = forms.CandidatCourrielForm(
             candidats=candidats, template=template,
@@ -108,7 +108,7 @@ def affecter_evaluateurs_offre_emploi(request):
                 request, messages.SUCCESS,
                 "Les évaluateurs ont été affectés aux offres d'emploi."
             )
-            return redirect("admin:recrutement_proxyoffreemploi_changelist")
+            return redirect("admin:emploi_offreemploi_changelist")
     else:
         form = forms.EvaluateurForm(offres_emploi=offres_emploi)
     return render(request, "recrutement/affecter_evaluateurs.html", {
index 6a90877..c257513 100644 (file)
@@ -18,14 +18,29 @@ from project import groups
 from project.decorators import in_drh_or_admin
 from project.rh import models as rh
 from project.permissions import user_gere_obj_de_sa_region, \
+        user_can_list_obj, \
         user_can_add_obj, \
         user_can_change_obj, \
-        user_can_delete_obj
+        user_can_delete_obj, \
+        get_region_user
 
 from project.rh.forms import ContratForm, AyantDroitForm, EmployeAdminForm, \
         AjaxSelect, DossierForm, ResponsableInlineForm
 from project.rh.change_list import ChangeList
 
+def listing_par_defaut(model, request):
+    """
+    Teste si la requete provient de la même page.
+    """
+    if not 'HTTP_REFERER' in request.META.keys():
+        return False
+    referer = request.META['HTTP_REFERER']
+    referer = "/".join(referer.split('/')[3:])
+    referer = "/%s" % referer.split('?')[0]
+    change_list_view = 'admin:%s_%s_changelist' % (
+            model._meta.app_label,
+            model.__name__.lower(),)
+    return referer != reverse(change_list_view)
 
 class BaseAdmin(admin.ModelAdmin):
 
@@ -39,6 +54,7 @@ class BaseAdmin(admin.ModelAdmin):
             'jquery-autocomplete/jquery.autocomplete.min.js',
         )
 
+
 # Admin pour reversion
 
 class ArchivableAdmin(admin.ModelAdmin):
@@ -100,17 +116,14 @@ class DateRangeMixin(object):
     prefixe_recherche_temporelle = ""
 
     def get_changelist(self, request, **kwargs):
-        if 'HTTP_REFERER' in request.META.keys():
-                    referer = request.META['HTTP_REFERER']
-                    referer = "/".join(referer.split('/')[3:])
-                    referer = "/%s" % referer.split('?')[0]
-                    change_list_view = 'admin:%s_%s_changelist' % (
-                            self.model._meta.app_label,
-                            self.model.__name__.lower(),)
-                    if referer != reverse(change_list_view):
-                        params = request.GET.copy()
-                        params.update({'statut': 'Actif'})
-                        request.GET = params
+        """
+        On filtre par défaut sur les items 'actifs'.
+        Le changelist plug le filtrage temporel.
+        """
+        if listing_par_defaut(self.model, request):
+            params = request.GET.copy()
+            params.update({'statut': 'Actif'})
+            request.GET = params
         return ChangeList
 
 
@@ -127,31 +140,45 @@ class LinkedInline(admin.options.InlineModelAdmin):
 
 class ProtectRegionMixin(object):
 
+    def changelist_view(self, request, extra_context=None):
+        """
+        On filtre par défaut sur la ZA du user connecté
+        """
+        if listing_par_defaut(self.model, request):
+            if user_gere_obj_de_sa_region(request.user):
+                params = request.GET.copy()
+                employe = groups.get_employe_from_user(request.user)
+                za = employe.implantation.zone_administrative.code
+                prefix_za = "%s__code__exact" % self.model.prefix_implantation
+                params.update({prefix_za: za})
+                request.GET = params
+        return super(ProtectRegionMixin, self).changelist_view(request, extra_context)
+
     def queryset(self, request):
         qs = super(ProtectRegionMixin, self).queryset(request)
 
         if in_drh_or_admin(request.user):
             return qs
 
-        #if user_gere_obj_de_sa_region(request.user):
-        #    region_user = get_region_user(request.user)
-        #    q = Q(**{self.model.prefix_implantation: \
-        #            region_user})
-        #    qs = qs.filter(q).distinct()
-        #    return qs
+        if user_gere_obj_de_sa_region(request.user):
+            region_user = get_region_user(request.user)
+            q = Q(**{self.model.prefix_implantation: \
+                    region_user})
+            qs = qs.filter(q).distinct()
+            return qs
         return qs.none()
 
     def has_add_permission(self, request):
-        return in_drh_or_admin(request.user)
-        #return user_can_add_obj(request.user)
+        return user_can_add_obj(request.user)
 
     def has_change_permission(self, request, obj=None):
-        return in_drh_or_admin(request.user)
-        #return user_can_change_obj(request.user, obj) if obj else True
+        if obj is None:
+            return user_can_list_obj(request.user)
+        else:
+            return user_can_change_obj(request.user, obj)
 
     def has_delete_permission(self, request, obj=None):
-        return in_drh_or_admin(request.user)
-        #return user_can_delete_obj(request.user, obj) if obj else True
+        return user_can_delete_obj(request.user, obj) if obj else True
 
 
 class DerniereModificationAdmin(admin.ModelAdmin):
@@ -250,6 +277,7 @@ class DossierROInline(ReadOnlyInlineMixin, LinkedInline):
     model = rh.Dossier
     extra = 0
     can_delete = False
+    fields = ('poste', 'date_debut', 'date_fin', )
 
     def has_add_permission(self, request=None):
         return False
@@ -367,7 +395,7 @@ class DossierAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
         '_nom',
         '_employe',
         '_poste',
-        '_region',
+        '_zone_administrative',
         '_implantation',
         '_date_debut',
         '_date_fin',
@@ -376,7 +404,7 @@ class DossierAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
     )
     list_display_links = ('_nom',)
     list_filter = (
-        'poste__implantation__region',
+        'poste__implantation__zone_administrative',
         'poste__implantation',
         'poste__type_poste__categorie_emploi',
         'poste__type_poste',
@@ -417,7 +445,7 @@ class DossierAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
     def lookup_allowed(self, key, value):
         if key in (
             'employe__nom__istartswith',
-            'poste__implantation__region__id__exact',
+            'poste__implantation__zone_administrative__code__exact',
             'poste__implantation__id__exact',
             'poste__type_poste__id__exact',
             'poste__type_poste__categorie_emploi__id__exact',
@@ -483,10 +511,10 @@ class DossierAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
     _poste.short_description = u'Poste'
     _poste.admin_order_field = 'poste__nom'
 
-    def _region(self, obj):
-        return obj.poste.implantation.region.code
-    _region.short_description = u"Région"
-    _region.admin_order_field = 'poste__implantation__region__code'
+    def _zone_administrative(self, obj):
+        return obj.poste.implantation.zone_administrative.code
+    _zone_administrative.short_description = u"Zone administrative"
+    _zone_administrative.admin_order_field = 'poste__implantation__zone_administrative__code'
 
     def _implantation(self, obj):
         return obj.poste.implantation.nom
@@ -550,14 +578,14 @@ class EmployeAdminBase(DateRangeMixin, ProtectRegionMixin,
     form = EmployeAdminForm
     list_display = (
         '_id', '_apercu', '_nom', '_dossiers_postes',
-        #'_region',
+        #'_zone_administrative',
         #'_implantation',
         'date_entree',
         'derniere_modification'
     )
     list_display_links = ('_nom',)
     list_filter = (
-        'rh_dossiers__poste__implantation__region',
+        'rh_dossiers__poste__implantation__zone_administrative',
         'rh_dossiers__poste__implantation', 'nb_postes'
     )
     inlines = (
@@ -610,14 +638,14 @@ class EmployeAdminBase(DateRangeMixin, ProtectRegionMixin,
     _nom.short_description = u"Employé"
     _nom.admin_order_field = "nom"
 
-    def _region(self, obj):
+    def _zone_administrative(self, obj):
         try:
             d = rh.Dossier.objects.filter(employe=obj.id, principal=True)[0]
-            region = d.poste.implantation.region.code
+            zone = d.poste.implantation.zone_administrative.code
         except:
-            region = None
-        return region
-    _region.short_description = u"Région"
+            zone = None
+        return zone
+    _zone_administrative.short_description = u"Zone administrative"
 
     def _implantation(self, obj):
         try:
@@ -795,8 +823,8 @@ class PosteAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
         'id',
         'nom',
         'implantation__nom',
-        'implantation__region__code',
-        'implantation__region__nom',
+        'implantation__zone_administrative__code',
+        'implantation__zone_administrative__nom',
         'rh_dossiers__employe__id',
         'rh_dossiers__employe__nom',
         'rh_dossiers__employe__prenom',
@@ -807,7 +835,7 @@ class PosteAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
         '_dae'
     )
     list_filter = (
-        'implantation__region',
+        'implantation__zone_administrative',
         'implantation',
         'service',
         'type_poste',
@@ -873,7 +901,7 @@ class PosteAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
     def lookup_allowed(self, key, value):
         return key in (
             'date_debut__gte', 'date_debut__isnull', 'date_fin__lte',
-            'date_fin__isnull', 'implantation__region__id__exact',
+            'date_fin__isnull', 'implantation__zone_administrative__code__exact',
             'implantation__id__exact', 'type_poste__id__exact',
             'type_poste__categorie_emploi__id__exact', 'service__id__exact',
             'service__isnull', 'vacant__exact', 'vacant__isnull',
@@ -929,10 +957,10 @@ class PosteAdmin(DateRangeMixin, ProtectRegionMixin, reversion.VersionAdmin,
     _occupe_par.allow_tags = True
     _occupe_par.short_description = "Occupé par"
 
-    def _region(self, poste):
-        return poste.implantation.region.code
-    _region.short_description = 'Région'
-    _region.admin_order_field = 'implantation__region__code'
+    def _zone_administrative(self, poste):
+        return poste.implantation.zone_administrative.code
+    _zone_administrative.short_description = 'Zone administrative'
+    _zone_administrative.admin_order_field = 'implantation__zone_administrative__code'
 
     def _implantation(self, poste):
         return poste.implantation.nom
@@ -1029,8 +1057,8 @@ class ResponsableImplantationAdmin(BaseAdmin):
     actions = None
     fields = ('nom', )
     inlines = (ResponsableInline, )
-    list_filter = ('region', 'statut', )
-    list_display = ('_region', '_nom', 'statut', '_responsable', )
+    list_filter = ('zone_administrative', 'statut', )
+    list_display = ('_zone_administrative', '_nom', 'statut', '_responsable', )
     list_display_links = ('_nom',)
     list_per_page = 500
     readonly_fields = ('nom', )
@@ -1043,10 +1071,10 @@ class ResponsableImplantationAdmin(BaseAdmin):
     ordering = ('nom',)
     inlines = (ResponsableInline, )
 
-    def _region(self, obj):
-        return obj.region.code
-    _region.short_description = u"Région"
-    _region.admin_order_field = 'region__code'
+    def _zone_administrative(self, obj):
+        return obj.zone_administrative.code
+    _zone_administrative.short_description = u"Zone administrative"
+    _zone_administrative.admin_order_field = 'zone_administrative__code'
 
     def _nom(self, obj):
         return obj.nom
@@ -1055,19 +1083,18 @@ class ResponsableImplantationAdmin(BaseAdmin):
 
     def _responsable(self, obj):
         try:
-            employe = obj.responsable.employe
+            employe = employe = obj.responsable.employe
+        except Exception, e:
+            return u"<span style='color: red;'>Pas de responsable</span><!-- %s -->" % e
+        try:
             dossiers = employe.dossiers_encours()
             if len(dossiers) == 0:
                 return u"<span style='color: red;'>%s %s </span>" % (
                     employe, u"sans dossier actif")
             else:
                 return employe
-        except Exception:
-            if obj.statut in (1, 2):  # ouverte, ouverture imminente
-                css = "style='color: red;'"
-            else:
-                css = ""
-            return u"<span %s>Pas de responsable</span>" % css
+        except Exception, e:
+            return u"<!-- %s -->" % e
     _responsable.allow_tags = True
     _responsable.short_description = u"Responsable"
     _responsable.admin_order_field = 'responsable__employe__nom'
@@ -1209,7 +1236,7 @@ class ValeurPointAdmin(reversion.VersionAdmin, DerniereModificationAdmin,
         '_devise_code', '_devise_nom', 'annee', 'implantation',
         'valeur', 'derniere_modification'
     )
-    list_filter = ('annee', 'devise', 'implantation__region', )
+    list_filter = ('annee', 'devise', 'implantation__zone_administrative', )
     fieldsets = (
         (None, {'fields': ('valeur', 'devise', 'implantation', 'annee')}),
     )
index c039911..96d745e 100644 (file)
@@ -33,12 +33,12 @@ class Implantation(object):
     def get_query(self, q, request):
         implantations = ref.Implantation.objects.filter(
             Q(nom__icontains=q) | Q(nom_court__icontains=q) |
-            Q(nom_long__icontains=q) | Q(region__nom__icontains=q)
+            Q(nom_long__icontains=q) | Q(zone_administrative__nom__icontains=q)
         )
         if user_gere_obj_de_sa_region(request.user):
             employe = get_employe_from_user(request.user)
             implantations = implantations.filter(
-                region=employe.implantation.region
+                zone_administrative=employe.implantation.zone_administrative
             )
         return implantations
 
index 272fecb..afab3db 100644 (file)
@@ -81,7 +81,7 @@ class EmployeAdminForm(forms.ModelForm, AjaxSelect):
 
 
 class ResponsableInlineForm(forms.ModelForm):
-    employes_actifs = rh.Employe.objects.actifs()
+    employes_actifs = rh.Employe.objects.actifs().distinct()
     employe = forms.ModelChoiceField(queryset=employes_actifs)
 
     class Meta:
@@ -89,8 +89,9 @@ class ResponsableInlineForm(forms.ModelForm):
 
 
 class MasseSalarialeForm(forms.Form):
-    region = forms.ModelChoiceField(
-        label=u'Région', queryset=ref.Region.objects.all(), required=False
+    zone_administrative = forms.ModelChoiceField(
+        label=u'Région', queryset=ref.ZoneAdministrative.objects.all(),
+        required=False
     )
     implantation = forms.ModelChoiceField(
         label=u'Implantation', queryset=ref.Implantation.objects.all(),
@@ -112,10 +113,13 @@ class MasseSalarialeForm(forms.Form):
         )
         if is_user_dans_region(user):
             employe = get_employe_from_user(user)
-            self.fields['region'].queryset = ref.Region.objects.filter(
-                id=employe.implantation.region.id
-            )
+            self.fields['zone_administrative'].queryset = \
+                    ref.ZoneAdministrative.objects.filter(
+                        code=employe.implantation.zone_administrative.code
+                    )
             self.fields['implantation'].queryset = \
                     ref.Implantation.objects.filter(
-                        region=employe.implantation.region
+                        zone_administrative=(
+                            employe.implantation.zone_administrative
+                        )
                     )
index f432041..ff8f303 100644 (file)
@@ -27,7 +27,10 @@ class SecurityManager(models.Manager):
         # TRAITEMENT NORMAL
         ############################################
         # REGION
-        q = Q(**{self.prefixe_implantation: employe.implantation.region})
+        q = Q(**{
+            self.prefixe_implantation:
+            employe.implantation.zone_administrative
+        })
 
         # SERVICE
         if self.prefixe_service \
@@ -49,6 +52,12 @@ class SecurityManager(models.Manager):
             liste = self.get_query_set().all()
 
         ############################################
+        # TRAITEMENT FINANCE
+        ############################################
+        if groups.FINANCE in groupes:
+            liste = self.get_query_set().all()
+
+        ############################################
         # TRAITEMENT HAUTE DIRECTION
         ############################################
         if groups.HAUTE_DIRECTION in groupes:
@@ -70,7 +79,8 @@ class ActifsQuerySet(QuerySet):
     debut_field = 'date_debut'
     fin_field = 'date_fin'
 
-    def get_q_actifs(self, date_min=None, date_max=None, annee=None):
+    def _actifs(self, debut_field, fin_field, date_min=None, date_max=None,
+            annee=None):
         q = Q()
         if annee:
             janvier = date(annee, 1, 1)
@@ -81,27 +91,34 @@ class ActifsQuerySet(QuerySet):
             date_min = date_max = date.today()
         if date_min:
             q = q & (
-                Q(**{self.fin_field + '__gte': date_min}) |
-                Q(**{self.fin_field: None}))
+                Q(**{fin_field + '__gte': date_min}) |
+                Q(**{fin_field: None}))
         if date_max:
             q = q & (
-                Q(**{self.debut_field + '__lte': date_max}) |
-                Q(**{self.debut_field: None}))
+                Q(**{debut_field + '__lte': date_max}) |
+                Q(**{debut_field: None}))
 
-        q_inconnus = self.get_q_inconnus()
+        q_inconnus = self._inconnus(debut_field, fin_field)
         return q & ~q_inconnus
 
+    def _inconnus(self, debut_field, fin_field):
+        q = Q(**{
+            debut_field + '__isnull': True,
+            fin_field + '__isnull': True,
+            })
+        return q
+
+    def get_q_actifs(self, date_min=None, date_max=None, annee=None):
+        return self._actifs(self.debut_field, self.fin_field,
+                date_min, date_max, annee)
+
     def actifs(self, date_min=None, date_max=None, annee=None):
         qs = self
         q = self.get_q_actifs(date_min, date_max, annee)
         return qs.filter(q)
 
     def get_q_inconnus(self, date_min=None, date_max=None, annee=None):
-        q = Q(**{
-            self.debut_field + '__isnull': True,
-            self.fin_field + '__isnull': True,
-            })
-        return q
+        return self._inconnus(self.debut_field, self.fin_field)
 
     def inconnus(self):
         qs = self
@@ -144,7 +161,7 @@ class PosteQuerySet(ActifsQuerySet):
 
 class PosteManager(SecurityManager):
     prefixe_service = "service"
-    prefixe_implantation = "implantation__region"
+    prefixe_implantation = "implantation__zone_administrative"
 
     def get_query_set(self):
         return PosteQuerySet(self.model).select_related('type_poste')
@@ -152,6 +169,8 @@ class PosteManager(SecurityManager):
     def ma_region_ou_service(self, user):
         return super(PosteManager, self).ma_region_ou_service(user)
 
+    def actifs(self, *args, **kwargs):
+        return self.get_query_set().actifs(*args, **kwargs)
 
 class DossierQuerySet(ActifsQuerySet):
     pass
@@ -159,12 +178,15 @@ class DossierQuerySet(ActifsQuerySet):
 
 class DossierManager(SecurityManager):
     prefixe_service = "poste__service"
-    prefixe_implantation = "poste__implantation__region"
+    prefixe_implantation = "poste__implantation__zone_administrative"
 
     def get_query_set(self):
         return DossierQuerySet(self.model) \
                 .select_related('poste', 'employe')
 
+    def actifs(self, *args, **kwargs):
+        return self.get_query_set().actifs(*args, **kwargs)
+
     def sans_contrats_ou_echus(self, *args, **kwargs):
         app = self.model._meta.app_label
         sql = """
@@ -181,11 +203,10 @@ class DossierManager(SecurityManager):
 class RemunerationQuerySet(ActifsQuerySet):
 
     def actifs(self, *args, **kwargs):
-        return self \
-                ._actifs('date_debut', 'date_fin', *args, **kwargs) \
-                ._actifs(
-                    'dossier__date_debut', 'dossier__date_fin',
-                    *args, **kwargs)
+        remun_q = self._actifs('date_debut', 'date_fin', *args, **kwargs)
+        dossier_q = self._actifs('dossier__date_debut', 'dossier__date_fin',
+                *args, **kwargs)
+        return self.filter(remun_q & dossier_q)
 
 
 class RemunerationManager(models.Manager):
@@ -223,12 +244,12 @@ class ContratManager(models.Manager):
 
 class PosteComparaisonManager(SecurityManager):
     use_for_related_fields = True
-    prefixe_implantation = "implantation__region"
+    prefixe_implantation = "implantation__zone_administrative"
 
 
 class DossierComparaisonManager(SecurityManager):
     use_for_related_fields = True
-    prefixe_implantation = "implantation__region"
+    prefixe_implantation = "implantation__zone_administrative"
 
 
 class DeviseManager(models.Manager):
index a5bfad7..43ae7bf 100644 (file)
@@ -41,6 +41,9 @@ storage_prive = FileSystemStorage(settings.PRIVE_MEDIA_ROOT,
                             base_url=settings.PRIVE_MEDIA_URL)
 
 
+class RemunIntegrityException(Exception):
+    pass
+
 def poste_piece_dispatch(instance, filename):
     path = "%s/poste/%s/%s" % (
         instance._meta.app_label, instance.poste_id, filename
@@ -338,10 +341,10 @@ class Poste_( DateActiviteMixin, models.Model,):
         )
         return representation
 
-    prefix_implantation = "implantation__region"
+    prefix_implantation = "implantation__zone_administrative"
 
-    def get_regions(self):
-        return [self.implantation.region]
+    def get_zones_administratives(self):
+        return [self.implantation.zone_administrative]
 
     def get_devise(self):
         vp = ValeurPoint.objects.filter(
@@ -575,21 +578,21 @@ class Employe(models.Model):
 
     def dossiers_passes(self):
         params = {KEY_STATUT: STATUT_INACTIF, }
-        search = RechercheTemporelle(params, self.__class__)
+        search = RechercheTemporelle(params, Dossier)
         search.purge_params(params)
         q = search.get_q_temporel(self.rh_dossiers)
         return self.rh_dossiers.filter(q)
 
     def dossiers_futurs(self):
         params = {KEY_STATUT: STATUT_FUTUR, }
-        search = RechercheTemporelle(params, self.__class__)
+        search = RechercheTemporelle(params, Dossier)
         search.purge_params(params)
         q = search.get_q_temporel(self.rh_dossiers)
         return self.rh_dossiers.filter(q)
 
     def dossiers_encours(self):
         params = {KEY_STATUT: STATUT_ACTIF, }
-        search = RechercheTemporelle(params, self.__class__)
+        search = RechercheTemporelle(params, Dossier)
         search.purge_params(params)
         q = search.get_q_temporel(self.rh_dossiers)
         return self.rh_dossiers.filter(q)
@@ -626,13 +629,14 @@ class Employe(models.Model):
             pass
         return poste
 
-    prefix_implantation = "rh_dossiers__poste__implantation__region"
+    prefix_implantation = \
+            "rh_dossiers__poste__implantation__zone_administrative"
 
-    def get_regions(self):
-        regions = []
-        for d in self.dossiers.all():
-            regions.append(d.poste.implantation.region)
-        return regions
+    def get_zones_administratives(self):
+        return [
+            d.poste.implantation.zone_administrative
+            for d in self.dossiers.all()
+        ]
 
 reversion.register(Employe, format='xml', follow=[
     'pieces', 'commentaires', 'ayantdroits'
@@ -723,13 +727,14 @@ class AyantDroit(models.Model):
     def __unicode__(self):
         return u'%s %s' % (self.nom.upper(), self.prenom, )
 
-    prefix_implantation = "employe__dossiers__poste__implantation__region"
+    prefix_implantation = \
+            "employe__dossiers__poste__implantation__zone_administrative"
 
-    def get_regions(self):
-        regions = []
-        for d in self.employe.dossiers.all():
-            regions.append(d.poste.implantation.region)
-        return regions
+    def get_zones_administratives(self):
+        return [
+            d.poste.implantation.zone_administrative
+            for d in self.employe.dossiers.all()
+        ]
 
 reversion.register(AyantDroit, format='xml', follow=['commentaires'])
 
@@ -841,10 +846,10 @@ class Dossier_(DateActiviteMixin, models.Model, DevisableMixin,):
             poste = self.poste.nom_feminin
         return u'%s - %s' % (self.employe, poste)
 
-    prefix_implantation = "poste__implantation__region"
+    prefix_implantation = "poste__implantation__zone_administrative"
 
-    def get_regions(self):
-        return [self.poste.implantation.region]
+    def get_zones_administratives(self):
+        return [self.poste.implantation.zone_administrative]
 
     def remunerations(self):
         key = "%s_remunerations" % self._meta.app_label
@@ -1425,11 +1430,6 @@ class OrganismeBstg(models.Model):
     def __unicode__(self):
         return u'%s (%s)' % (self.nom, self.get_type_display())
 
-    prefix_implantation = "pays__region"
-
-    def get_regions(self):
-        return [self.pays.region]
-
 reversion.register(OrganismeBstg, format='xml')
 
 
index ca731a9..af83eed 100644 (file)
@@ -90,7 +90,7 @@ def masse_salariale(lignes, annee, titres_traitements, titres_indemnites,
         # GESTION de l'affichage des VACANTS
         if 'dossier' not in ligne.keys():
             row.add_cells([
-                ligne['poste'].implantation.region.code,
+                ligne['poste'].implantation.zone_administrative.code,
                 ligne['poste'].implantation.adresse_physique_pays.nom,
                 ligne['poste'].implantation.nom_court
             ])
@@ -158,7 +158,7 @@ def masse_salariale(lignes, annee, titres_traitements, titres_indemnites,
             continue
 
         row.add_cells([
-            ligne['poste'].implantation.region.code,
+            ligne['poste'].implantation.zone_administrative.code,
             ligne['poste'].implantation.adresse_physique_pays.nom,
             ligne['poste'].implantation.nom_court
         ])
index de978f5..206e1d6 100644 (file)
@@ -1,6 +1,6 @@
 <fieldset class="module aligned">
     <h2>Localisation</h2>
-    {% include "rh/form-row.html" with label="Région" value=dossier.poste.implantation.region %}
+    {% include "rh/form-row.html" with label="Zone administrative" value=dossier.poste.implantation.zone_administrative %}
     {% include "rh/form-row.html" with label="Implantation" value=dossier.poste.implantation %}
 </fieldset>
 
index 27afccf..19d5f5b 100644 (file)
@@ -25,7 +25,7 @@
 <div id="changelist-filter">
   <h2>{% trans 'Filter' %}</h2>
   
-  {% filter_region_contrat %}
+  {% filter_zone_administrative_contrat %}
   {% filter_implantation_contrat %}
   {% filter_type_contrat %}
   {% filter_echeance_contrat %}
@@ -57,7 +57,7 @@
     <td>{{ contrat.date_debut }}</td>
     <td>{{ contrat.date_fin|default:'' }}</td>
     <td>{{ contrat.dossier.get_statut_residence_display }}</td>
-    <td>{{ contrat.dossier.poste.implantation.region.nom }}</td>
+    <td>{{ contrat.dossier.poste.implantation.zone_administrative.nom }}</td>
     <td>{{ contrat.dossier.poste.implantation.nom }}</td>
   </tr>
   {% endfor %}
index 56af714..ae74f3c 100644 (file)
@@ -17,7 +17,7 @@
 {% block contentrapport %}
 <div id="changelist-filter">
   <h2>{% trans 'Filter' %}</h2>
-  {% filter_region_dossier %}
+  {% filter_zone_administrative_dossier %}
   {% filter_implantation_dossier %}
 </div>
 
@@ -40,7 +40,7 @@
       <td>{{ d.dernier_contrat.date_debut|default_if_none:"" }}</td>
       <td>{{ d.dernier_contrat.date_fin|default_if_none:"" }}</td>
       <td>{{ d.get_statut_residence_display }}</td>
-      <td>{{ d.poste.implantation.region.code }}</td>
+      <td>{{ d.poste.implantation.zone_administrative.code }}</td>
       <td>{{ d.poste.implantation.nom }}</td>
     </tr>
     {% endfor %}
index 73c90fa..60a3f33 100644 (file)
@@ -109,7 +109,7 @@ $(document).ready(function() {
   <tbody>
     {% for ligne in lignes %}
     <tr class="{% cycle 'row1' 'row2' %}">
-      <td>{{ ligne.poste.implantation.region.code }}</td>
+      <td>{{ ligne.poste.implantation.zone_administrative.code }}</td>
       <td>{{ ligne.poste.implantation.adresse_physique_pays.nom }}</td>
       <td>{{ ligne.poste.implantation.nom_court }}</td>
       <td class="numeric">
index 7faf777..484326c 100644 (file)
@@ -6,7 +6,8 @@ from django.utils.encoding import smart_unicode
 from django.template import Library
 from django.utils.http import urlencode
 
-from auf.django.references.models import Implantation, Region
+from auf.django.references.models import \
+        Implantation, Region, ZoneAdministrative
 
 from project import groups
 
@@ -39,6 +40,18 @@ def filter_region(context, prefix=None):
 
 
 @register.inclusion_tag('admin/filter.html', takes_context=True)
+def filter_zone_administrative(context, prefix=None):
+    label = "".join([prefix or "", "implantation__zone_administrative"])
+    return {
+        'title': u"zone administrative",
+        'choices': prepare_choices(
+            ZoneAdministrative.objects.values_list('code', 'nom'), label, context,
+            remove=['pays', 'nord_sud']
+        )
+    }
+
+
+@register.inclusion_tag('admin/filter.html', takes_context=True)
 def filter_implantation(context, prefix=None):
     label = "".join([prefix or "", "implantation"])
     return {
@@ -50,27 +63,35 @@ def filter_implantation(context, prefix=None):
 
 
 @register.inclusion_tag('admin/filter.html', takes_context=True)
-def filter_region_contrat(context):
+def filter_zone_administrative_contrat(context):
     request = context['request']
     user_groups = [g.name for g in request.user.groups.all()]
     if groups.CORRESPONDANT_RH in user_groups or\
          groups.ADMINISTRATEURS in user_groups or\
          groups.DIRECTEUR_DE_BUREAU in user_groups:
         employe = groups.get_employe_from_user(request.user)
-        regions = Region.objects.filter(id=employe.implantation.region.id)
+        zones = ZoneAdministrative.objects.filter(
+            code=employe.implantation.zone_administrative.code
+        )
     else:
-       regions = Region.objects.all()
-    return {'title': u"région",
-            'choices': prepare_choices(regions.values_list('id', 'nom'), 'dossier__poste__implantation__region', context, remove=['pays', 'nord_sud'])}
+       zones = ZoneAdministrative.objects.all()
+    return {
+        'title': u"région",
+        'choices': prepare_choices(
+            zones.values_list('code', 'nom'),
+            'dossier__poste__implantation__zone_administrative',
+            context, remove=['pays', 'nord_sud']
+        )
+    }
 
 
 @register.inclusion_tag('admin/filter.html', takes_context=True)
-def filter_region_dossier(context):
+def filter_zone_administrative_dossier(context):
     return {
         'title': u"région",
         'choices': prepare_choices(
-            Region.objects.values_list('id', 'nom'),
-            'poste__implantation__region', context,
+            ZoneAdministrative.objects.values_list('code', 'nom'),
+            'poste__implantation__zone_administrative', context,
             remove=['pays', 'nord_sud']
         )
     }
@@ -95,7 +116,9 @@ def filter_implantation_contrat(context):
          groups.ADMINISTRATEURS in user_groups or\
          groups.DIRECTEUR_DE_BUREAU in user_groups:
         employe = groups.get_employe_from_user(request.user)
-        implantations = Implantation.objects.filter(region=employe.implantation.region)
+        implantations = Implantation.objects.filter(
+            zone_administrative=employe.implantation.zone_administrative
+        )
     else:
        implantations = Implantation.objects.all()
     return {'title': u"implantation",
@@ -193,12 +216,12 @@ def filter_a_venir(context):
 
 
 @register.inclusion_tag('admin/filter_select.html', takes_context=True)
-def filter_region_remun(context):
+def filter_zone_administrative_remun(context):
     return {
         'title': u"région",
         'choices': prepare_choices(
-            Region.objects.values_list('id', 'nom'),
-            'dossiers__poste__implantation__region', context,
+            ZoneAdministrative.objects.values_list('code', 'nom'),
+            'dossiers__poste__implantation__zone_administrative', context,
             remove=['pays', 'nord_sud']
         )
     }
index fb238c3..9b3bc1a 100644 (file)
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 
 import datetime
+from auf.django.saml.settings import SAML_REDIRECT_FIELD_NAME
 from django.contrib.auth.models import User, Group
 from project import groups
 from auf.django.references import models as ref
@@ -49,6 +50,7 @@ class RhTest(TestCase):
         self.grp_drh2
         self.grp_accior
         self.grp_abf
+        self.grp_finance
         self.grp_haute_direction
         self.grp_service_utilisateurs
         
@@ -144,6 +146,9 @@ class RhTest(TestCase):
         self.grp_abf = Group(name=groups.ABF)
         self.grp_abf.save()
 
+        self.grp_finance = Group(name=groups.FINANCE)
+        self.grp_finance.save()
+
         self.grp_haute_direction = Group(name=groups.HAUTE_DIRECTION)
         self.grp_haute_direction.save()
 
@@ -158,10 +163,12 @@ class RhTest(TestCase):
         # Régions / Implantations
         #########################
         self.REGION_ACGL = ref.Region.objects.get(id=1)
+        self.ZA_ACGL = ref.ZoneAdministrative.objects.get(code=u'ACGL')
         self.IMPLANTATION_ACGL_CNF_NGAOUNDERE = ref.Implantation.objects.get(id=90)
         self.IMPLANTATION_ACGL_CNF_BANGUI = ref.Implantation.objects.get(id=85)
 
         self.REGION_BAP = ref.Region.objects.get(id=4)
+        self.ZA_BAP = ref.ZoneAdministrative.objects.get(code=u'AP')
         self.IMPLANTATION_BAP_BUREAU = ref.Implantation.objects.get(id=51)
         self.IMPLANTATION_BAP_IFI = ref.Implantation.objects.get(id=55)
 
@@ -259,6 +266,7 @@ class RhTest(TestCase):
             
             # on le porte dans le référentiel employé
             ref_e = ref.Employe(id=u.id,
+                    courriel=email,
                     nom=e.nom, prenom=e.prenom,
                     implantation=e.dossier_principal().poste.implantation,
                     implantation_physique=e.dossier_principal().poste.implantation,
@@ -284,7 +292,11 @@ class RhTest(TestCase):
 
     def _test_acces_ko(self, url):
         response = self.client.get(url, follow=True)
-        is_ko = response.status_code in (403, 404) or 'next' in response.context
+        is_ko = response.status_code in (403, 404) or \
+                'next' in response.context or \
+                'next' in response.context['request'].GET.keys() or \
+                SAML_REDIRECT_FIELD_NAME in response.context['request'].GET.keys()
+        
         self.assertEqual(is_ko, True)
 
     def _test_anonyme(self):
@@ -354,6 +366,16 @@ class RhTest(TestCase):
         credentials = {'username': email, 'password': self.password}
         self.assertTrue(self.client.login(**credentials), "login failed")
 
+    def _test_grp_finance(self):
+        email = "0@test.auf"
+        
+        u = User.objects.get(email=email)
+        self.grp_finance.user_set.add(u)
+        self.grp_finance.save()
+
+        credentials = {'username': email, 'password': self.password}
+        self.assertTrue(self.client.login(**credentials), "login failed")
+
     def _test_grp_haute_direction(self):
         email = "0@test.auf"
         
index cada6d2..19c7f8a 100644 (file)
@@ -62,7 +62,14 @@ class DossierAddTest(RhTest):
         """
         Un membre de l'ABF ne peut pas ajouter un dossier
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def _test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas ajouter un dossier
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
     def _test_grp_haute_direction(self):
@@ -143,7 +150,14 @@ class DossierDeleteTest(RhTest):
         """
         Un membre de l'ABF  ne peut pas supprimer un dossier
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def _test_grp_finance(self):
+        """
+        Un membre de l'ABF  ne peut pas supprimer un dossier
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
     def _test_grp_haute_direction(self):
@@ -231,7 +245,14 @@ class DossierListTest(RhTest):
         """
         Un membre de l'ABF ne peut pas voir les dossiers
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def _test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas voir les dossiers
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
     def _test_grp_haute_direction(self):
index 2ca5076..0662b4c 100644 (file)
@@ -52,28 +52,35 @@ class EmployeAddTest(RhTest):
         self._test_drh2()
         self._test_acces_ok(self.url)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas ajouter un employé
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas ajouter un employé
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas ajouter un employé
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas ajouter un employé
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas ajouter un employé
         """
@@ -140,21 +147,28 @@ class EmployeDeleteTest(RhTest):
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
-        Un membre de l'ABF  ne peut pas supprimer un employé
+        Un membre de l'ABF ne peut pas supprimer un employé
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_finance(self):
         """
-        Un membre de la haute direction  ne peut pas supprimer un employé
+        Un membre de Finance ne peut pas supprimer un employé
+        """
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_haute_direction(self):
+        """
+        Un membre de la haute direction ne peut pas supprimer un employé
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur
         ne peut pas supprimer un employé
@@ -222,21 +236,28 @@ class EmployeListTest(RhTest):
         self._test_drh2()
         self._test_acces_ok(self.url)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas voir les employés
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas voir les employés
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas voir les employés
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction  ne peut pas voir les employés
         """
@@ -333,21 +354,28 @@ class EmployeApercuTest(RhTest):
         dossiers = self.client.get(self.url2).context['dossiers']
         self.assertEqual(len(dossiers), 1)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas voir les aperçus d'employés
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas voir les aperçus d'employés
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas voir les aperçus d'employés
+        """
+        self._test_grp_finance()
+        self._test_acces_ko(self.url)
+
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas voir
         les aperçus d'employés
@@ -355,7 +383,7 @@ class EmployeApercuTest(RhTest):
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne pas pas voir
         les aperçus d'employés
index 06b3896..cc8f875 100644 (file)
@@ -58,21 +58,28 @@ class PosteAddTest(RhTest):
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas ajouter un poste
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas ajouter un poste
+        """
+        self._test_grp_finance()
+        self._test_acces_ko(self.url)
+
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas ajouter un poste
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas ajouter un poste
         """
@@ -132,28 +139,35 @@ class PosteDeleteTest(RhTest):
         self._test_drh2()
         self._test_acces_ok(self.url)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
-        Un membre de l'ACCIOR  ne peut pas supprimer un poste
+        Un membre de l'ACCIOR ne peut pas supprimer un poste
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
+        """
+        Un membre de l'ABF ne peut pas supprimer un poste
+        """
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_finance(self):
         """
-        Un membre de l'ABF  ne peut pas supprimer un poste
+        Un membre de Finance ne peut pas supprimer un poste
         """
-        self._test_grp_abf(self)
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction  ne peut pas supprimer un poste
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur  ne peut pas supprimer un poste
         """
@@ -220,28 +234,35 @@ class PosteListTest(RhTest):
         self._test_drh2()
         self._test_acces_ok(self.url)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas voir les postes
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas voir les postes
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas voir les postes
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction  ne peut pas voir les postes
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas voir les postes
         """
index ab14bcb..c4c2779 100644 (file)
@@ -1,6 +1,8 @@
 # -*- coding: utf-8 -*-
 
+import datetime
 from django.core.urlresolvers import reverse
+from django.contrib.auth.models import User
 from project.rh.test.common import RhTest
 from project.rh import models as rh
 
@@ -28,31 +30,6 @@ class RapportContratTest(RhTest):
                 dossier=self.dossier_bap_ifi)
         contrat.save()
 
-        salaire = rh.TypeRemuneration(nom="salaire",
-                type_paiement="Régulier",
-                nature_remuneration="Indemnité",
-                )
-        salaire.save()
-
-        remun_cnf_ngaoundere = rh.Remuneration(
-                type=salaire,
-                date_debut=self.today,
-                dossier=self.dossier_cnf_ngaoundere,
-                montant="100",
-                devise=self.devise_cad,
-                )
-        remun_cnf_ngaoundere.save()
-
-        remun_bap_ifi = rh.Remuneration(
-                type=salaire,
-                date_debut=self.today,
-                dossier=self.dossier_bap_ifi,
-                montant="200",
-                devise=self.devise_cad,
-                )
-        remun_bap_ifi.save()
-
-
     def test_anonyme(self):
         """
         Un anonyme ne peut pas accéder à ce rapport
@@ -108,28 +85,35 @@ class RapportContratTest(RhTest):
         contrats = self.client.get(self.url).context['contrats']
         self.assertEqual(len(contrats), 2)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas accéder à ce rapport
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas accéder à ce rapport
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas accéder à ce rapport
+        """
+        self._test_grp_finance()
+        self._test_acces_ko(self.url)
+
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas accéder à ce rapport
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas accéder à ce rapport
         """
@@ -183,7 +167,6 @@ class RapportMasseSalarialeTest(RhTest):
         """
         self._test_drh()
         self._test_acces_ok(self.url)
-        # todo TEST rapport
 
     def test_drh2(self):
         """
@@ -191,30 +174,36 @@ class RapportMasseSalarialeTest(RhTest):
         """
         self._test_drh2()
         self._test_acces_ok(self.url)
-        # todo TEST rapport
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas accéder à ce rapport
         """
         self._test_grp_accior()
         self._test_acces_ko(self.url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas accéder à ce rapport
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        self._test_acces_ko(self.url)
+
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas accéder à ce rapport
+        """
+        self._test_grp_finance()
         self._test_acces_ko(self.url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas accéder à ce rapport
         """
         self._test_grp_haute_direction()
         self._test_acces_ko(self.url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas accéder à ce rapport
         """
@@ -299,7 +288,7 @@ class RapportOrganigrammeTest(RhTest):
         for url in self.urls:
             self._test_acces_ok(url)
 
-    def _test_grp_accior(self):
+    def test_grp_accior(self):
         """
         Un membre de l'ACCIOR ne peut pas accéder à ce rapport
         """
@@ -307,15 +296,23 @@ class RapportOrganigrammeTest(RhTest):
         for url in self.urls:
             self._test_acces_ko(url)
 
-    def _test_grp_abf(self):
+    def test_grp_abf(self):
         """
         Un membre de l'ABF ne peut pas accéder à ce rapport
         """
-        self._test_grp_abf(self)
+        self._test_grp_abf()
+        for url in self.urls:
+            self._test_acces_ko(url)
+
+    def test_grp_finance(self):
+        """
+        Un membre de Finance ne peut pas accéder à ce rapport
+        """
+        self._test_grp_abf()
         for url in self.urls:
             self._test_acces_ko(url)
 
-    def _test_grp_haute_direction(self):
+    def test_grp_haute_direction(self):
         """
         Un membre de la haute direction ne peut pas accéder à ce rapport
         """
@@ -323,10 +320,149 @@ class RapportOrganigrammeTest(RhTest):
         for url in self.urls:
             self._test_acces_ko(url)
 
-    def _test_grp_service_utilisateurs(self):
+    def test_grp_service_utilisateurs(self):
         """
         Un membre du groupe service utilisateur ne peut pas accéder à ce rapport
         """
         self._test_grp_service_utilisateurs()
         for url in self.urls:
             self._test_acces_ko(url)
+
+class RapportMasseSalarialeCalculTest(RhTest):
+    """
+    Test l'accès au rapport de masse salariale
+    """
+    url = reverse('rhr_masse_salariale')
+
+    today = datetime.date.today()
+    annee_moins_2 = datetime.date(today.year - 2, 1, 1)
+    annee_moins_1 = datetime.date(today.year - 1, 1, 1)
+    annee_plus_2 = datetime.date(today.year + 2, 1, 1)
+    annee_plus_1 = datetime.date(today.year + 1, 1, 1)
+
+
+    def setUp(self):
+        super(RapportMasseSalarialeCalculTest, self).setUp()
+
+        self.type_salaire = rh.TypeRemuneration(nom="salaire",
+                type_paiement="Régulier",
+                nature_remuneration="Indemnité",
+                )
+        self.type_salaire.save()
+
+        # 4 dossiers actifs
+        self.dossier_cnf_ngaoundere.date_debut = self.annee_moins_1
+        self.dossier_cnf_ngaoundere.date_fin = self.annee_plus_1
+        self.dossier_cnf_ngaoundere.save()
+
+        self.dossier_cnf_bangui.date_debut = self.annee_moins_1
+        self.dossier_cnf_bangui.date_fin = self.annee_plus_1
+        self.dossier_cnf_bangui.save()
+
+        self.dossier_bap_bureau.date_debut = self.annee_moins_1
+        self.dossier_bap_bureau.date_fin = self.annee_plus_1
+        self.dossier_bap_bureau.save()
+
+        self.dossier_bap_ifi.date_debut = self.annee_moins_1
+        self.dossier_bap_ifi.date_fin = self.annee_plus_1
+        self.dossier_bap_ifi.save()
+
+
+        self.remun_cnf_ngaoundere = rh.Remuneration(
+                type=self.type_salaire,
+                date_debut=self.annee_moins_1,
+                dossier=self.dossier_cnf_ngaoundere,
+                montant="111",
+                devise=self.devise_cad,
+                )
+        self.remun_cnf_ngaoundere.save()
+
+        self.remun_cnf_bangui = rh.Remuneration(
+                type=self.type_salaire,
+                date_debut=self.annee_moins_1,
+                dossier=self.dossier_cnf_bangui,
+                montant="222",
+                devise=self.devise_cad,
+                )
+        self.remun_cnf_bangui.save()
+
+        self.remun_bap_bureau = rh.Remuneration(
+                type=self.type_salaire,
+                date_debut=self.annee_moins_1,
+                dossier=self.dossier_bap_bureau,
+                montant="333",
+                devise=self.devise_cad,
+                )
+        self.remun_bap_bureau.save()
+
+        self.remun_bap_ifi = rh.Remuneration(
+                type=self.type_salaire,
+                date_debut=self.annee_moins_1,
+                dossier=self.dossier_bap_ifi,
+                montant="444",
+                devise=self.devise_cad,
+                )
+        self.remun_bap_ifi.save()
+
+        self.taux_change_cad = \
+            rh.TauxChange(devise=self.devise_cad,
+                taux=0.5,
+                annee=self.today.year)
+        self.taux_change_cad.save()
+
+    def test_taux_change_presence(self):
+        """
+        Intégrité des rémunérations.
+        """
+        from project.rh.views import rapports_masse_salariale
+
+        self._test_drh()
+        user_drh = User.objects.get(email="0@test.auf")
+        params = {'annee': self.today.year,  }
+
+        class Request:
+            GET = params
+            user = user_drh
+            
+            def get_full_path(self):
+                return
+
+        request = Request()
+        
+        self.taux_change_cad.delete()
+
+        self.assertRaises(rh.RemunIntegrityException,
+                rapports_masse_salariale, request)
+
+
+    def test_filtrage(self):
+        """
+        Test la page avec des paramètres de filtrage.
+        """
+        self._test_drh()
+        self._test_acces_ok(self.url)
+
+        params = {'implantation': self.IMPLANTATION_ACGL_CNF_NGAOUNDERE.id,
+                'annee': self.today.year,
+                }
+        resp = self.client.get(self.url, params)
+        self.assertEqual(resp.status_code, 200)
+        self.assertEqual(resp.context['form'].is_valid(), True, resp.context['form'].errors)
+        self.assertEqual(len(resp.context['lignes']), 1)
+
+        params = {
+                'zone_administrative': self.ZA_ACGL.code,
+                'annee': self.today.year,
+                }
+        resp = self.client.get(self.url, params)
+        self.assertEqual(resp.status_code, 200)
+        self.assertEqual(resp.context['form'].is_valid(), True)
+        self.assertEqual(len(resp.context['lignes']), 2)
+
+        params = {
+                'annee': self.today.year,
+                }
+        resp = self.client.get(self.url, params)
+        self.assertEqual(resp.status_code, 200)
+        self.assertEqual(resp.context['form'].is_valid(), True)
+        self.assertEqual(len(resp.context['lignes']), 4)
index eb8310d..0dca607 100644 (file)
@@ -110,7 +110,11 @@ def rapports_contrat(request):
        groups.ADMINISTRATEURS in user_groups or\
        groups.DIRECTEUR_DE_BUREAU in user_groups:
         employe = get_employe_from_user(request.user)
-        q = q & Q(dossier__poste__implantation__region=employe.implantation.region)
+        q = q & Q(
+            dossier__poste__implantation__zone_administrative=(
+                employe.implantation.zone_administrative
+            )
+        )
 
     contrats = contrats.filter(q)
 
@@ -130,7 +134,7 @@ def rapports_contrat(request):
         ("date_debut", u"Début contrat"),
         ("date_fin", u"Fin contrat"),
         ("dossier__statut_residence", u"Statut"),
-        ("dossier__poste__implantation__region", u"Région"),
+        ("dossier__poste__implantation__zone_administrative", u"Zone administrative"),
         ("dossier__poste__implantation", u"Implantation"),
     ]
     h = SortHeaders(
@@ -168,7 +172,8 @@ def rapports_employes_sans_contrat(request):
        groups.ADMINISTRATEURS in user_groups or\
        groups.DIRECTEUR_DE_BUREAU in user_groups:
         employe = get_employe_from_user(request.user)
-        lookup_params['poste__implantation__region'] = employe.implantation.region
+        lookup_params['poste__implantation__zone_administrative'] = \
+                employe.implantation.zone_administrative
 
     
     dossiers = rh.Dossier.objects.sans_contrats_ou_echus(**lookup_params)
@@ -190,7 +195,7 @@ def rapports_employes_sans_contrat(request):
         ("rh_contrats__date_debut", u"Début contrat"),
         ("rh_contrats__date_fin", u"Fin contrat"),
         ("statut_residence", u"Statut"),
-        ("poste__implantation__region__code", u"Région"),
+        ("poste__implantation__zone_administrative__code", u"Zone administrative"),
         ("poste__implantation__nom", u"Implantation"),
     ]
     h = SortHeaders(
@@ -217,7 +222,7 @@ def rapports_employes_sans_contrat(request):
 def rapports_masse_salariale(request):
     form = MasseSalarialeForm(request.user, request.GET)
     if 'annee' in request.GET and form.is_valid():
-        region = form.cleaned_data['region']
+        zone_administrative = form.cleaned_data['zone_administrative']
         implantation = form.cleaned_data['implantation']
         annee = form.cleaned_data['annee']
         debut_annee = date(annee, 1, 1)
@@ -230,7 +235,7 @@ def rapports_masse_salariale(request):
                 .actifs(annee=annee) \
                 .select_related(
                     'poste', 'poste__implantation',
-                    'poste__implantation__region',
+                    'poste__implantation__zone_administrative',
                     'poste__implantation__adresse_physique_pays',
                     'employe', 'poste__type_poste', 'classement',
                     'statut', 'organisme_bstg'
@@ -251,16 +256,17 @@ def rapports_masse_salariale(request):
                         )
                     }
                 )
-        if region:
-            dossiers = dossiers.filter(poste__implantation__region=region)
+        if zone_administrative:
+            dossiers = dossiers.filter(poste__implantation__zone_administrative=zone_administrative)
         if implantation:
             dossiers = dossiers.filter(poste__implantation=implantation)
 
         # Récupérer les rémunérations actives
-        remuns = rh.Remuneration.objects \
+        remun_actives = rh.Remuneration.objects \
                 .actifs(annee=annee) \
-                .select_related('devise', 'type') \
-                .extra(
+                .select_related('devise', 'type')
+
+        remuns = remun_actives.extra(
                     tables=['rh_tauxchange'],
                     where=[
                         'rh_tauxchange.annee = %s',
@@ -271,8 +277,13 @@ def rapports_masse_salariale(request):
                         'taux_change': 'rh_tauxchange.taux'
                     }
                 )
-        if region:
-            remuns = remuns.filter(dossier__poste__implantation__region=region)
+
+        if len(remun_actives) != len(remuns):
+            raise rh.RemunIntegrityException("Toutes les remunerations ne disposent pas d'un "
+                "taux de change pour l'année %d" % annee)
+
+        if zone_administrative:
+            remuns = remuns.filter(dossier__poste__implantation__zone_administrative=zone_administrative)
         if implantation:
             remuns = remuns.filter(dossier__poste__implantation=implantation)
         remuns_par_dossier = defaultdict(list)
@@ -419,8 +430,8 @@ def rapports_masse_salariale(request):
                         'taux_change': 'rh_tauxchange.taux'
                     }
                 )
-        if region:
-            postes = postes.filter(implantation__region=region)
+        if zone_administrative:
+            postes = postes.filter(implantation__zone_administrative=zone_administrative)
         if implantation:
             postes = postes.filter(implantation=implantation)
         postes = list(postes)
@@ -666,8 +677,12 @@ def employe_apercu(request, employe_id):
        groups.ADMINISTRATEURS in user_groups or\
        groups.DIRECTEUR_DE_BUREAU in user_groups:
         employe_connecte = get_employe_from_user(request.user)
-        q = Q(employe=employe) & Q(poste__implantation__region=employe_connecte.implantation.region)
-    
+        q = Q(employe=employe) & Q(
+            poste__implantation__zone_administrative=(
+                employe_connecte.implantation.zone_administrative
+            )
+        )
+
     dossiers = rh.Dossier.objects.filter(q).order_by('-date_debut')
 
     c = {
index e7be987..bd4769b 100644 (file)
@@ -3,15 +3,14 @@
 import os
 import socket
 from project.conf import *  # NOQA
+from project.groups import DRH_NIVEAU_1, DRH_NIVEAU_2
 
 PROJET_TITRE = "Ressources humaines"
 
 # Rapports d'erreurs
 SERVER_EMAIL = 'ne-pas-repondre@auf.org'
 EMAIL_SUBJECT_PREFIX = '[auf_rh_dae - %s] ' % socket.gethostname()
-ADMINS = (
-    ('Équipe ARI-SI', 'developpeurs@ca.auf.org'),
-)
+ADMINS = ()
 
 MANAGERS = ADMINS
 
@@ -26,15 +25,18 @@ DATE_INPUT_FORMATS = ('%d-%m-%Y', )
 SESSION_SAVE_EVERY_REQUEST = True
 SESSION_EXPIRE_AT_BROWSER_CLOSE = True
 
+PROJECT_ROOT = os.path.dirname(__file__)
+SITE_ROOT = os.path.dirname(PROJECT_ROOT)
+
 # Absolute path to the directory that holds media.
 # Example: "/home/media/media.lawrence.com/"
-MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'media')
-PRIVE_MEDIA_ROOT = os.path.join(os.path.dirname(__file__), 'media_prive')
+MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media')
+PRIVE_MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'media_prive')
 
 STATICFILES_DIRS = (
-    os.path.join(os.path.dirname(__file__), 'assets'),
+    os.path.join(PROJECT_ROOT, 'assets'),
 )
-STATIC_ROOT = os.path.join(os.path.dirname(__file__), 'static')
+STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')
 STATIC_URL = '/static/'
 
 # URL that handles the media served from MEDIA_ROOT. Make sure to use a
@@ -57,6 +59,8 @@ MIDDLEWARE_CLASSES = (
     'django.contrib.sessions.middleware.SessionMiddleware',
     'django.contrib.messages.middleware.MessageMiddleware',
     'django.contrib.auth.middleware.AuthenticationMiddleware',
+    'auf.django.saml.middleware.SPMiddleware',
+    'auf.django.piwik.middleware.TrackMiddleware',
     'django.middleware.doc.XViewMiddleware',
     'reversion.middleware.RevisionMiddleware',
 )
@@ -71,6 +75,8 @@ INSTALLED_APPS = (
     'auf.django.workflow',
     'auf.django.permissions',
     'auf.django.emploi',
+    'auf.django.saml',
+    'auf.django.export',
     'admin_tools',
     'admin_tools.theming',
     'admin_tools.menu',
@@ -85,6 +91,7 @@ INSTALLED_APPS = (
     'django_qbe',
     'ajax_select',
     'south',
+    'raven.contrib.django',
     'reversion',
     'alphafilter',
     'form_utils',
@@ -108,11 +115,9 @@ TEMPLATE_CONTEXT_PROCESSORS = (
 )
 
 AUTHENTICATION_BACKENDS = (
-    'auf.django.auth.backends.CascadeBackend',
+    'auf.django.saml.backends.SPBackend',
     'auf.django.permissions.backends.AuthenticationBackend',
 )
-LOGIN_URL = "/connexion"
-LOGIN_REDIRECT_URL = "/"
 
 TEMPLATE_DIRS = (
     os.path.join(os.path.dirname(__file__), "templates"),
@@ -194,3 +199,53 @@ QBE_ALLOWED_FIELDS = {'Rh': {
 }}
 
 SOUTH_TESTS_MIGRATE = False
+
+LOGGING = {
+    'version': 1,
+    'disable_existing_loggers': True,
+    'root': {
+        'level': 'WARNING',
+        'handlers': ['sentry'],
+    },
+    'formatters': {
+        'verbose': {
+            'format': '%(levelname)s %(asctime)s %(module)s %(process)d %(thread)d %(message)s'
+        },
+    },
+    'handlers': {
+        'sentry': {
+            'level': 'WARNING',
+            'class': 'raven.contrib.django.handlers.SentryHandler',
+        },
+        'console': {
+            'level': 'DEBUG',
+            'class': 'logging.StreamHandler',
+            'formatter': 'verbose'
+        }
+    },
+    'loggers': {
+        'django.db.backends': {
+            'level': 'ERROR',
+            'handlers': ['console'],
+            'propagate': False,
+        },
+        'raven': {
+            'level': 'DEBUG',
+            'handlers': ['console'],
+            'propagate': False,
+        },
+        'sentry.errors': {
+            'level': 'DEBUG',
+            'handlers': ['console'],
+            'propagate': False,
+        },
+    },
+}
+
+# Pour accéder aux requêtes savegardées, il faut définir les permissions dans
+# l'admin
+def qbe_access(user):
+    grps_user = [g.name for g in user.groups.all()]
+    inter = [g for g in grps_user if g in (DRH_NIVEAU_1, DRH_NIVEAU_2, )]
+    return len(inter) > 0
+QBE_ACCESS_FOR = qbe_access
index cdc8f6d..4f6a8c5 100644 (file)
@@ -1,11 +1 @@
-<ul>
-    {% if user.is_authenticated %}
-      <li class="username">{{ user }}</li>
-      {% if perms.recrutement or user_in_dae_groupes or perms.rh %}
-        <li><a href="{% url admin:index %}">Administration</a></li>
-      {% endif %}
-      <li><a href="{% url django.contrib.auth.views.logout %}?next=/">Déconnexion</a></li>
-    {% else %}
-      <li><a href="{% url django.contrib.auth.views.login %}?next={{request.path}}">Connexion</a></li>
-    {% endif %}
-</ul>
+{% include "saml/auth.html" %}
index 856a8b0..519d264 100644 (file)
@@ -6,31 +6,23 @@
 
 {% block main %}
 
-{% if not request.user.is_authenticated %}
-<h1>Connexion</h1>
+{% for k,v in meta.items %}
+<p>{{ k }} : {{ v }}</p>
+{% endfor %}
 
-    <form method="post" action="{% url django.contrib.auth.views.login %}">{% csrf_token %}
-        <table>
-        {{ form.as_table }}
-        </table>
-        <input type="submit" value="Se connecter" />
-        <input type="hidden" name="next" value="{{ next }}" />
-    </form>
+<h1>Vos droits d'accès</h1>
+{% if request.user.groups.all %}
+  <p>Vous faîtes partie des groupes suivants : </p>
+  <ul>
+      {% for g in request.user.groups.all %}
+      <li>{{ g.name }}</li>
+      {% endfor %}
+  </ul>
 {% else %}
-    <h1>Vos droits d'accès</h1>
-    {% if request.user.groups.all %}
-      <p>Vous faîtes partie des groupes suivants : </p>
-      <ul>
-          {% for g in request.user.groups.all %}
-          <li>{{ g.name }}</li>
-          {% endfor %}
-      </ul>
-    {% else %}
-      <p>
-      Vous n'avez actuellement aucun droit dans ce système.<br />
-      Contactez la DRH si vous croyez qu'il s'agit d'une erreur.
-      </p>
-    {% endif %}
+  <p>
+  Vous n'avez actuellement aucun droit dans ce système.<br />
+  Contactez la DRH si vous croyez qu'il s'agit d'une erreur.
+  </p>
 {% endif %}
 
 {% endblock %}
index cfb73df..6b63834 100644 (file)
@@ -7,14 +7,13 @@
 
   {% if perms.rh %}
   <li>
-    <a href="{% url admin:app_list app_label="recrutement" %}">Gestion des
-        candidatures</a>
+    <a href="{% url admin:index %}">Recrutement</a>
   </li>
   {% endif %}
 
   {% if perms.dae %}
   <li class="{% menu_actif request '^dae$' %}">
-    <a href="{% url dae_index %}">Demande d'autorisation d'engagement</a>
+    <a href="{% url dae_index %}">DAE</a>
     <ul>
         {% if request.user|peut_ajouter %}
         <li class="{% menu_actif request '^poste$' %}">
@@ -30,7 +29,7 @@
         </li>
         {% endif %}
         <li class="{% menu_actif request '^embauches$' %}">
-          <a href="{% url dae_embauches_liste %}">Personnel : voir et valider</a>
+          <a href="{% url dae_embauches_liste %}">DAE : voir et valider</a>
         </li>
         <li class="{% menu_actif request '^embauches_finalisees$' %}">
           <a href="{% url embauches_finalisees %}">DAE finalisées</a>
     
   {% if perms.rh %}
   <li>
-    <a href="{% url admin:app_list app_label="rh" %}">Gestion des personnels</a>
+    <a href="{% url admin:index %}">Gestion des personnels</a>
   </li>
   {% endif %}
+
+  {% if user.is_staff %}
+  <li>
+    <a href="{% url admin:index %}">Administration</a>
+  </li>
+  {% endif %}
+
 </ul>
index 5334386..dd06a52 100644 (file)
@@ -4,6 +4,7 @@ from django.contrib import admin
 from urldecorators.defaults import patterns, include, url
 from urldecorators.defaults import handler500  # NOQA
 from auf.django import permissions
+from auf.django.saml import settings as saml_settings
 from project.monkey import patch_ajax_selects
 
 patch_ajax_selects()
@@ -13,28 +14,43 @@ permissions.autodiscover()
 
 urlpatterns = patterns(
     '',
-    url(r'^$', 'project.views.index', name='index'),
-    url(r'^admin_tools/', include('admin_tools.urls')),
+    url(r'^$', 'project.views.index', name='index',
+        decorators=['auf.django.saml.decorators.login_required']),
+
+    url(r'^', include('auf.django.saml.urls')),
+
+    url(r'^admin_tools/', include('admin_tools.urls'),
+        decorators=['auf.django.saml.decorators.login_required']),
     (r'^admin/', include(admin.site.urls)),
+
     url(r'^api/(?P<method>[a-z_-]+)/(?P<offre_id>\d+)/$',
         'project.recrutement.api.api', name='recrutement_api'),
     url(r'^api/(?P<method>[a-z_-]+)/$', 'project.recrutement.api.api',
         name='recrutement_api'),
-    (r'^connexion/$', 'django.contrib.auth.views.login'),
-    (r'^deconnexion/$', 'django.contrib.auth.views.logout'),
-    #url(r'^private_files/', include('private_files.urls')),
-    url(r'^captcha/', include('captcha.urls')),
-    (r'^ajax_select/', include('ajax_select.urls')),
-    (r'^tinymce/', include('tinymce.urls')),
-    url(r'^prive/(?P<filename>.*)$', 'project.views.piece'),
+
+    url(r'^captcha/', include('captcha.urls'),
+        decorators=['auf.django.saml.decorators.login_required']),
+    url(r'^ajax_select/', include('ajax_select.urls'),
+        decorators=['auf.django.saml.decorators.login_required']),
+    url(r'^tinymce/', include('tinymce.urls'),
+        decorators=['auf.django.saml.decorators.login_required']),
+    url(r'^prive/(?P<filename>.*)$', 'project.views.piece',
+        decorators=['auf.django.saml.decorators.login_required']),
 
     # apps
     url(r'^dae/', include('project.dae.urls'),
-        decorators=['django.contrib.auth.decorators.login_required']),
+        decorators=['auf.django.saml.decorators.login_required']),
     url(r'^recrutement/', include('project.recrutement.urls'),
-        decorators=['django.contrib.auth.decorators.login_required']),
+        decorators=['auf.django.saml.decorators.login_required']),
     url(r'^rh/', include('project.rh.urls'),
-        decorators=['django.contrib.auth.decorators.login_required']),
+        decorators=['auf.django.saml.decorators.login_required']),
 
-    url(r'^qbe/', include('django_qbe.urls')),
+    url(r'^qbe/', include('django_qbe.urls'),
+        decorators=['auf.django.saml.decorators.login_required']),
 )
+
+if not saml_settings.SAML_AUTH:
+    urlpatterns += patterns(
+        '',
+        (r'^', include('auf.django.saml.mellon_urls')),
+    )
index 7466549..fd83264 100644 (file)
@@ -4,8 +4,8 @@ import os
 from sendfile import sendfile
 
 from django.conf import settings
+from django.shortcuts import render
 from django.contrib.auth.decorators import login_required
-from django.contrib.auth.views import login
 from django.http import Http404
 
 from project.decorators import redirect_interdiction
@@ -13,8 +13,10 @@ from project.rh import models as rh_models
 from project.dae import models as dae_models
 
 
+@login_required
 def index(request):
-    return login(request, template_name='index.html')
+    c = {}
+    return render(request, 'index.html', c)
 
 
 @login_required
index 5f5e8e5..7b32c0b 100644 (file)
@@ -67,7 +67,7 @@ class UnicodeWriter(object):
 
 def txt(msg):
     text = msg
-    if msg is not None and not isinstance(msg, unicode):
+    if msg is not None and isinstance(msg, str):
         text = unicode (msg, "utf-8")
     return text
 
index 620696b..168f917 100644 (file)
@@ -29,7 +29,15 @@ except AttributeError:
 SORT_CHOICES = (
     ("", ""),
     ("asc", _("Ascending")),
-    ("des", _("Descending")),
+    ("desc", _("Descending")),
+)
+
+STATUS_CHOICES = (
+    ("", "Tous"),
+    ("inactive", "Inactifs"),
+    ("active", "Actifs"),
+    ("futur", "Futurs"),
+    ("unknown", "Inconnus"),
 )
 
 
@@ -38,6 +46,8 @@ class QueryByExampleForm(forms.Form):
     model = forms.CharField(label=_("Model"))
     field = forms.CharField(label=_("Field"))
     criteria = forms.CharField(label=_("Criteria"), required=False)
+    status = forms.ChoiceField(label=_("Statut"), choices=STATUS_CHOICES,
+                             required=False)
     sort = forms.ChoiceField(label=_("Sort"), choices=SORT_CHOICES,
                              required=False)
 
@@ -59,6 +69,10 @@ class QueryByExampleForm(forms.Form):
             field_attr_class = "qbeFillFields enable:sort,%s" % criteria_names
         else:
             field_attr_class = "qbeFillFields enable:sort,criteria"
+        status_widget = forms.Select(attrs={'disabled': "disabled",
+                                          'class': 'hidden'},
+                                   choices=STATUS_CHOICES)
+        self.fields['status'].widget = status_widget
         field_widget = forms.Select(attrs={'class': field_attr_class})
         self.fields['field'].widget = field_widget
 
@@ -79,6 +93,7 @@ class BaseQueryByExampleFormSet(BaseFormSet):
     _selects = []
     _froms = []
     _wheres = []
+    _statuses = []
     _sorts = []
     _params = []
     _models = {}
@@ -120,7 +135,7 @@ class BaseQueryByExampleFormSet(BaseFormSet):
             # Don't bother validating the formset unless each form is valid on
             # its own
             return
-        selects, froms, wheres, sorts, params = self.get_query_parts()
+        selects, froms, wheres, sorts, params, statuses = self.get_query_parts()
         if not selects:
             validation_message = _(u"At least you must check a row to get.")
             raise forms.ValidationError, validation_message
@@ -129,6 +144,7 @@ class BaseQueryByExampleFormSet(BaseFormSet):
         self._wheres = wheres
         self._sorts = sorts
         self._params = params
+        self._statuses = statuses
 
     def translate_model_to_db_table(self, model_name):
         """
@@ -152,6 +168,7 @@ class BaseQueryByExampleFormSet(BaseFormSet):
         wheres = []
         sorts = []
         params = []
+        statuses = []
         app_model_labels = None
         lookup_cast = self._db_operations.lookup_cast
         qn = self._db_operations.quote_name
@@ -176,13 +193,26 @@ class BaseQueryByExampleFormSet(BaseFormSet):
             show = data["show"]
             criteria = data["criteria"]
             sort = data["sort"]
+            status = data["status"]
             db_field = u"%s.%s" % (qn(model), qn(field))
             operator, over = criteria
-            is_join = operator.lower() == 'join'
+            try:
+                is_join = operator.lower() == 'join'
+            except:
+                is_join = False
+                
             if show and not is_join:
-                selects.append(db_field)
+                try:
+                    self._models[model]._meta.get_field(field)
+                    selects.append(db_field)
+                except:
+                    # dynamic field can't be viewable
+                    pass
             if sort:
-                sorts.append(db_field)
+                sorts.append((db_field, sort))
+            if status:
+                statuses.append((model, status))
+            
             if all(criteria):
                 if is_join:
                     over_split = over.lower().rsplit(".", 1)
@@ -191,9 +221,14 @@ class BaseQueryByExampleFormSet(BaseFormSet):
                     
                     if model in self._models:
                         _field = self._models[model]._meta.get_field(field)
+                        try:
+                            _db_column = qn(_field.db_column)
+                        except:
+                            _db_column = qn(_field.attname)
+
                         join = u"%s.%s = %s.%s" \
                                % (join_model, join_field, qn(model),
-                                  qn(_field.db_column))
+                                  qn(_db_column))
                     else:
                         join = u"%s.%s = %s" \
                                % (join_model, join_field,
@@ -216,18 +251,42 @@ class BaseQueryByExampleFormSet(BaseFormSet):
                                      db_operator))
             if qn(model) not in froms and model in self._db_table_names:
                 froms.append(qn(model))
-        return selects, froms, wheres, sorts, params
+        return selects, froms, wheres, sorts, params, statuses
 
     def get_raw_query(self, limit=None, offset=None, count=False,
                       add_extra_ids=False, add_params=False):
+        qn = self._db_operations.quote_name
         if self._raw_query:
             return self._raw_query
         if self._sorts:
-            order_by = u"ORDER BY %s" % (", ".join(self._sorts))
+            order_by = u"ORDER BY %s" % (", ".join([" ".join(x) for x in self._sorts]))
         else:
             order_by = u""
-        if self._wheres:
-            wheres = u"WHERE %s" % (" AND ".join(self._wheres))
+        _my_wheres = self._wheres
+        if self._statuses:
+            for m, s in self._statuses:
+                if m not in ('rh_employe', 'rh_dossier', 'rh_poste'):
+                    continue
+                # Test cas spécial: Pour état employé, vérifier via les dossiers.
+                if m == 'rh_employe':
+                    m = qn('rh_dossier')
+                    if m not in self._froms:
+                        self._froms.append(m)
+                        _my_wheres.append("`rh_employe`.`id` = `rh_dossier`.`employe`")
+                if s == "inactive":
+                    _my_wheres.append("%s.date_fin < DATE(NOW())" % m)
+                if s == "active":
+                    _my_wheres.append(
+                        "(((%s.`date_debut` <= DATE(NOW()) OR %s.`date_debut` IS NULL) AND %s.`date_fin` >= DATE(NOW())) OR "
+                        "((%s.`date_fin` >= DATE(NOW()) OR %s.`date_fin` IS NULL) AND %s.`date_debut` <= DATE(NOW())) OR "
+                        "(%s.`date_debut` <= DATE(NOW()) AND %s.`date_fin` >= DATE(NOW())))"
+                        % (m, m, m, m, m, m, m, m))
+                if s == "futur":
+                    _my_wheres.append("%s.date_debut > DATE(NOW())" % m)
+                if s == "unknown":
+                    _my_wheres.append("(%s.date_debut IS NULL AND %s.date_fin IS NULL)" % (m, m))
+        if _my_wheres:
+            wheres = u"WHERE %s" % (" AND ".join(_my_wheres))
         else:
             wheres = u""
         if count:
diff --git a/src/qbe/django_qbe/locale/fr/LC_MESSAGES/django.mo b/src/qbe/django_qbe/locale/fr/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..991632a
Binary files /dev/null and b/src/qbe/django_qbe/locale/fr/LC_MESSAGES/django.mo differ
diff --git a/src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.mo b/src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.mo
new file mode 100644 (file)
index 0000000..991632a
Binary files /dev/null and b/src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.mo differ
diff --git a/src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.po b/src/qbe/django_qbe/locale/fr/fr_CA/LC_MESSAGES/django.po
new file mode 100644 (file)
index 0000000..f5e03eb
--- /dev/null
@@ -0,0 +1,219 @@
+# django-qbe FR translations
+# Copyright (C) 2011
+# This file is distributed under the same license as the django-qbe package.
+# Olivier Larchevêque <olivier.larcheveque@auf.org>, 2011
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2011-08-02 13:25+0000\n"
+"PO-Revision-Date: 2011-08-02 13:25+0000\n"
+"Last-Translator: Olivier Larchevêque <olivier.larcheveque@auf.org>\n"
+"Language-Team: Olivier Larchevêque <olivier.larcheveque@auf.org>\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: forms.py:29
+msgid "Ascending"
+msgstr "Croissant"
+
+#: forms.py:30
+msgid "Descending"
+msgstr "Décroissant"
+
+#: forms.py:35 templates/qbe.html:48 templates/qbe.html.py:72
+msgid "Show"
+msgstr "Voir"
+
+#: forms.py:36 templates/qbe.html:49
+msgid "Model"
+msgstr "Modèle"
+
+#: forms.py:37 templates/qbe.html:50
+msgid "Field"
+msgstr "Champs"
+
+#: forms.py:38 templates/qbe.html:52
+msgid "Criteria"
+msgstr "Critère"
+
+#: forms.py:39 templates/qbe.html:51
+msgid "Sort"
+msgstr "Trier"
+
+#: forms.py:123
+msgid "At least you must check a row to get."
+msgstr "Sélectionner au moins une ligne à afficher."
+
+#: forms.py:320
+msgid "#"
+msgstr ""
+
+#: views.py:57 views.py:118 views.py:170
+msgid "Query by Example"
+msgstr "Constructeur de requêtes (QBE)"
+
+#: views.py:169
+msgid "Reports"
+msgstr "Rapports"
+
+#: widgets.py:10
+msgid "is equal to"
+msgstr "égal à"
+
+#: widgets.py:11
+msgid "contains"
+msgstr "contient"
+
+#: widgets.py:12
+msgid "matchs regex"
+msgstr "expression régulière"
+
+#: widgets.py:13
+msgid "starts with"
+msgstr "commence par"
+
+#: widgets.py:14
+msgid "ends with"
+msgstr "fini par"
+
+#: widgets.py:15
+msgid "is greater than"
+msgstr "est supérieur à"
+
+#: widgets.py:16
+msgid "is greater than or equal to"
+msgstr "est supérieur ou égal à"
+
+#: widgets.py:17
+msgid "is less than"
+msgstr "est inférieur à"
+
+#: widgets.py:18
+msgid "is less than or equal to"
+msgstr "est inférieur ou égal à"
+
+#: widgets.py:19
+msgid "(i) is equal to"
+msgstr "(i) est égal à"
+
+#: widgets.py:20
+msgid "(i) contains"
+msgstr "(i) contient"
+
+#: widgets.py:21
+msgid "(i) matchs regex"
+msgstr "(i) expression régulière"
+
+#: widgets.py:22
+msgid "(i) starts with"
+msgstr "(i) commence par"
+
+#: widgets.py:23
+msgid "(i) ends with"
+msgstr "(i) fini par"
+
+#: widgets.py:24
+msgid "joins to"
+msgstr "jointure sur"
+
+#: widgets.py:33
+msgid "Check this"
+msgstr ""
+
+#: templates/qbe.html:18 templates/qbe_results.html:9
+msgid "Home"
+msgstr "Accueil"
+
+#: templates/qbe.html:27
+msgid "Diagram"
+msgstr "Diagramme"
+
+#: templates/qbe.html:30
+msgid "Tabular"
+msgstr "Tables"
+
+#: templates/qbe.html:33 templates/qbe.html.py:96
+msgid "Models"
+msgstr "Modèles"
+
+#: templates/qbe.html:53
+msgid "Delete"
+msgstr "Supprimer"
+
+#: templates/qbe.html:72
+msgid "rows"
+msgstr "lignes"
+
+#: templates/qbe.html:74
+msgid "Auto complete"
+msgstr "Auto-complétion"
+
+#: templates/qbe.html:76
+msgid "Run"
+msgstr "Exécuter"
+
+#: templates/qbe.html:80
+msgid "Database"
+msgstr "Base de données"
+
+#: templates/qbe_results.html:9
+msgid "Results"
+msgstr "Résultats"
+
+#: templates/qbe_results.html:21
+#, fuzzy
+msgid "Showing all"
+msgstr "Tout afficher"
+
+#: templates/qbe_results.html:21 templates/qbe_results.html.py:23
+#: templates/qbe_results.html:82
+msgid "result"
+msgstr "résultat"
+
+#: templates/qbe_results.html:23
+msgid "Showing from"
+msgstr "Afficher de"
+
+#: templates/qbe_results.html:23
+msgid "to"
+msgstr "à"
+
+#: templates/qbe_results.html:23
+msgid "of"
+msgstr "de"
+
+#: templates/qbe_results.html:27
+msgid "show first ones"
+msgstr "Montrer les premiers résultats"
+
+#: templates/qbe_results.html:29
+msgid "show all"
+msgstr "Tout afficher"
+
+#: templates/qbe_results.html:31
+msgid "edit query"
+msgstr "Modifier la requête"
+
+#: templates/qbe_results.html:32
+msgid "view query"
+msgstr "Voir la requête"
+
+#: templates/qbe_results.html:36
+msgid "Save query as"
+msgstr "Sauvegarder la requête comme"
+
+#: templates/qbe_results.html:39
+msgid "Drag this yo your bookmarks bar to save this query"
+msgstr "Glisser le lien sur vos favoris pour sauvegarder la requête"
+
+#: templates/qbe_results.html:39
+msgid "bookmark"
+msgstr "favori"
+
+#: templates/qbe_results.html:43
+msgid "Export to"
+msgstr "Exporter en"
diff --git a/src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.mo b/src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.mo
deleted file mode 100644 (file)
index 991632a..0000000
Binary files a/src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.po b/src/qbe/django_qbe/locale/fr_CA/LC_MESSAGES/django.po
deleted file mode 100644 (file)
index f5e03eb..0000000
+++ /dev/null
@@ -1,219 +0,0 @@
-# django-qbe FR translations
-# Copyright (C) 2011
-# This file is distributed under the same license as the django-qbe package.
-# Olivier Larchevêque <olivier.larcheveque@auf.org>, 2011
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2011-08-02 13:25+0000\n"
-"PO-Revision-Date: 2011-08-02 13:25+0000\n"
-"Last-Translator: Olivier Larchevêque <olivier.larcheveque@auf.org>\n"
-"Language-Team: Olivier Larchevêque <olivier.larcheveque@auf.org>\n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-
-#: forms.py:29
-msgid "Ascending"
-msgstr "Croissant"
-
-#: forms.py:30
-msgid "Descending"
-msgstr "Décroissant"
-
-#: forms.py:35 templates/qbe.html:48 templates/qbe.html.py:72
-msgid "Show"
-msgstr "Voir"
-
-#: forms.py:36 templates/qbe.html:49
-msgid "Model"
-msgstr "Modèle"
-
-#: forms.py:37 templates/qbe.html:50
-msgid "Field"
-msgstr "Champs"
-
-#: forms.py:38 templates/qbe.html:52
-msgid "Criteria"
-msgstr "Critère"
-
-#: forms.py:39 templates/qbe.html:51
-msgid "Sort"
-msgstr "Trier"
-
-#: forms.py:123
-msgid "At least you must check a row to get."
-msgstr "Sélectionner au moins une ligne à afficher."
-
-#: forms.py:320
-msgid "#"
-msgstr ""
-
-#: views.py:57 views.py:118 views.py:170
-msgid "Query by Example"
-msgstr "Constructeur de requêtes (QBE)"
-
-#: views.py:169
-msgid "Reports"
-msgstr "Rapports"
-
-#: widgets.py:10
-msgid "is equal to"
-msgstr "égal à"
-
-#: widgets.py:11
-msgid "contains"
-msgstr "contient"
-
-#: widgets.py:12
-msgid "matchs regex"
-msgstr "expression régulière"
-
-#: widgets.py:13
-msgid "starts with"
-msgstr "commence par"
-
-#: widgets.py:14
-msgid "ends with"
-msgstr "fini par"
-
-#: widgets.py:15
-msgid "is greater than"
-msgstr "est supérieur à"
-
-#: widgets.py:16
-msgid "is greater than or equal to"
-msgstr "est supérieur ou égal à"
-
-#: widgets.py:17
-msgid "is less than"
-msgstr "est inférieur à"
-
-#: widgets.py:18
-msgid "is less than or equal to"
-msgstr "est inférieur ou égal à"
-
-#: widgets.py:19
-msgid "(i) is equal to"
-msgstr "(i) est égal à"
-
-#: widgets.py:20
-msgid "(i) contains"
-msgstr "(i) contient"
-
-#: widgets.py:21
-msgid "(i) matchs regex"
-msgstr "(i) expression régulière"
-
-#: widgets.py:22
-msgid "(i) starts with"
-msgstr "(i) commence par"
-
-#: widgets.py:23
-msgid "(i) ends with"
-msgstr "(i) fini par"
-
-#: widgets.py:24
-msgid "joins to"
-msgstr "jointure sur"
-
-#: widgets.py:33
-msgid "Check this"
-msgstr ""
-
-#: templates/qbe.html:18 templates/qbe_results.html:9
-msgid "Home"
-msgstr "Accueil"
-
-#: templates/qbe.html:27
-msgid "Diagram"
-msgstr "Diagramme"
-
-#: templates/qbe.html:30
-msgid "Tabular"
-msgstr "Tables"
-
-#: templates/qbe.html:33 templates/qbe.html.py:96
-msgid "Models"
-msgstr "Modèles"
-
-#: templates/qbe.html:53
-msgid "Delete"
-msgstr "Supprimer"
-
-#: templates/qbe.html:72
-msgid "rows"
-msgstr "lignes"
-
-#: templates/qbe.html:74
-msgid "Auto complete"
-msgstr "Auto-complétion"
-
-#: templates/qbe.html:76
-msgid "Run"
-msgstr "Exécuter"
-
-#: templates/qbe.html:80
-msgid "Database"
-msgstr "Base de données"
-
-#: templates/qbe_results.html:9
-msgid "Results"
-msgstr "Résultats"
-
-#: templates/qbe_results.html:21
-#, fuzzy
-msgid "Showing all"
-msgstr "Tout afficher"
-
-#: templates/qbe_results.html:21 templates/qbe_results.html.py:23
-#: templates/qbe_results.html:82
-msgid "result"
-msgstr "résultat"
-
-#: templates/qbe_results.html:23
-msgid "Showing from"
-msgstr "Afficher de"
-
-#: templates/qbe_results.html:23
-msgid "to"
-msgstr "à"
-
-#: templates/qbe_results.html:23
-msgid "of"
-msgstr "de"
-
-#: templates/qbe_results.html:27
-msgid "show first ones"
-msgstr "Montrer les premiers résultats"
-
-#: templates/qbe_results.html:29
-msgid "show all"
-msgstr "Tout afficher"
-
-#: templates/qbe_results.html:31
-msgid "edit query"
-msgstr "Modifier la requête"
-
-#: templates/qbe_results.html:32
-msgid "view query"
-msgstr "Voir la requête"
-
-#: templates/qbe_results.html:36
-msgid "Save query as"
-msgstr "Sauvegarder la requête comme"
-
-#: templates/qbe_results.html:39
-msgid "Drag this yo your bookmarks bar to save this query"
-msgstr "Glisser le lien sur vos favoris pour sauvegarder la requête"
-
-#: templates/qbe_results.html:39
-msgid "bookmark"
-msgstr "favori"
-
-#: templates/qbe_results.html:43
-msgid "Export to"
-msgstr "Exporter en"
index 7564825..ad8fb80 100644 (file)
@@ -166,3 +166,7 @@ input.qbeCheckModels {
     overflow: auto;
     margin-top: 10px;
 }
+
+.qbeTabular .hidden {
+       display: none;
+}
index 5f230bb..7ef17f5 100644 (file)
@@ -199,7 +199,8 @@ qbe.Core = function() {};
                         optFields.push(option);
                     }
                 }
-                $("#"+ domTo).html('<option value="">*</option>' + optPrimaries.join("") + optForeigns.join("") + optManies.join("") + optFields.join(""));
+                optEtat = '<option value="status">État</option>'
+                $("#"+ domTo).html('<option value="">*</option>' + optPrimaries.join("") + optForeigns.join("") + optManies.join("") + optFields.join("") + optEtat);
                 // We need to raise change event
                 $("#"+ domTo).change();
             }
@@ -216,11 +217,32 @@ qbe.Core = function() {};
             css = $(this).attr("class");
             cssSplit = css.split("enable:")
             inputs = cssSplit[cssSplit.length-1].split(",");
+            if (field == 'status') {
+                $("#"+ prefix + "-sort").attr("disabled", "disabled");
+                $("#"+ prefix + "-sort").val("");
+                $("#"+ prefix + "-criteria").attr("disabled", "disabled");
+                $("#"+ prefix + "-criteria").val("");
+                $("#"+ prefix + "-criteria").addClass("hidden");
+                $("#"+ prefix + "-criteria_0").attr("disabled", "disabled");
+                $("#"+ prefix + "-criteria_0").val("");
+                $("#"+ prefix + "-criteria_0").addClass("hidden");
+                $("#"+ prefix + "-criteria_1").attr("disabled", "disabled");
+                $("#"+ prefix + "-criteria_1").val("");
+                $("#"+ prefix + "-criteria_1").addClass("hidden");
+                $("#"+ prefix + "-status").removeClass("hidden");
+                $("#"+ prefix + "-status").removeAttr("disabled");
+
+            }
+            else {
+                $("#"+ prefix + "-status").val("");
+                $("#"+ prefix + "-status").addClass("hidden");
+
             for(var i=0; i<inputs.length; i++) {
                 input = inputs[i];
                 domTo = prefix +"-"+ input;
                 if (field) {
                     $("#"+ domTo).removeAttr("disabled");
+                    $("#"+ domTo).removeClass("hidden");
                 } else {
                     $("#"+ domTo).attr("disabled", "disabled");
                     $("#"+ domTo).val("");
@@ -262,6 +284,7 @@ qbe.Core = function() {};
                     }
                 }
             }
+            }
         };
 
         /**
index 99a54be..bb4763c 100644 (file)
@@ -60,7 +60,7 @@
                 <td>{{ form.model.errors }}{{ form.model }}</td>
                 <td>{{ form.field.errors }}{{ form.field }}</td>
                 <td>{{ form.sort.errors }}{{ form.sort }}</td>
-                <td>{{ form.criteria.errors }}{{ form.criteria }}</td>
+                <td>{{ form.criteria.errors }}{{ form.criteria }}{{ form.status }}</td>
                 <td class="delete"></td>
             </tr>
         {% endfor %}
index 1fbe9da..7830cb0 100644 (file)
@@ -88,10 +88,14 @@ def qbe_models(admin_site=None, only_admin_models=False, json=False):
 
     def get_target(field):
         name = field.rel.to.__module__.split(".")[-2].lower().capitalize()
+        try:
+            t_field = field.rel.field_name
+        except:
+            t_field = field.rel.to._meta.pk.name
         target = {
             'name': name,
             'model': field.rel.to.__name__,
-            'field': field.rel.to._meta.pk.name,
+            'field': t_field,
         }
         if hasattr(field.rel, 'through') and field.rel.through is not None:
             name = field.rel.through.__module__.split(".")[-2]
index b56d9b8..9e5c96d 100644 (file)
@@ -1,10 +1,9 @@
 [versions]
-django = 1.3.1
-south = 0.7
+django = 1.3.4
 django-admin-tools = 0.4.1
 django-ajax-selects = 1.1.4
 django-alphafilter = 0.5.3auf4
-django-reversion = 1.5.2
+django-reversion = 1.5.5
 django-sendfile = 0.2.1
 django-urldecorators = 0.3
 auf.django.auth = 0.5.5dev
@@ -17,7 +16,6 @@ pygraphviz = 1.0
 
 # Added by Buildout Versions at 2012-05-09 13:24:49.321297
 auf.django.admingroup = 0.3dev
-auf.recipe.django = 1.7
 buildout-versions = 1.7
 distribute = 0.6.15
 django-debug-toolbar = 0.9.4
@@ -64,5 +62,17 @@ auf.django.emploi = 1.2dev
 # Added by Buildout Versions at 2012-06-15 14:24:42.911205
 odsgen = 0.1
 
-# Added by Buildout Versions at 2012-07-11 15:30:58.885863
-auf.django.references = 0.21
+# Added by Buildout Versions at 2012-07-10 15:40:30.011741
+auf.django.export = 0.4
+
+# Added by Buildout Versions at 2012-08-23 09:19:43.234537
+auf.recipe.django = 2.1
+
+# Added by Buildout Versions at 2012-11-06 12:21:22.409839
+auf.django.piwik = 1.8
+auf.django.references = 0.24
+auf.django.saml = 1.17
+raven = 2.0.9
+
+# Added by Buildout Versions at 2012-11-06 12:23:14.684419
+South = 0.7.6