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/django-alphafilter/
http://pypi.auf.org/simple/auf.django.references/
+ http://pypi.auf.org/simple/django-alphafilter/
+ http://pypi.auf.org/simple/odsgen/
develop = src/qbe
src/auf.django.metadata
eggs =
auf.django.skin
auf.django.workflow
auf.recipe.django
- odfpy
+ odsgen
django-picklefield
pygraphviz
simplejson
# -*- encoding: utf-8 -*-
-from django import forms
+from datetime import date
+
from ajax_select.fields import AutoCompleteSelectField
-from project.rh.models import Dossier, Contrat, AyantDroit, Employe, \
- ResponsableImplantation
+from auf.django.references import models as ref
+from django import forms
+from django.db.models import Min
+
+from project.groups import get_employe_from_user
+from project.permissions import user_gere_obj_de_sa_region
+from project.rh import models as rh
class AjaxSelect(object):
class DossierForm(forms.ModelForm, FormDate):
class Model:
- model = Dossier
+ model = rh.Dossier
class ContratForm(forms.ModelForm, FormDate):
class Model:
- model = Contrat
+ model = rh.Contrat
class AyantDroitForm(forms.ModelForm, AjaxSelect):
self.fields['date_naissance'].widget = forms.widgets.DateInput()
class Meta:
- model = AyantDroit
+ model = rh.AyantDroit
class EmployeAdminForm(forms.ModelForm, AjaxSelect):
-
nationalite = AutoCompleteSelectField(
'pays', help_text="Taper le nom ou le code du pays", required=False
)
)
class Meta:
- model = Employe
+ model = rh.Employe
def __init__(self, *args, **kwargs):
super(EmployeAdminForm, self).__init__(*args, **kwargs)
class ResponsableInlineForm(forms.ModelForm):
- employes_actifs = Employe.objects.actifs()
+ employes_actifs = rh.Employe.objects.actifs()
employe = forms.ModelChoiceField(queryset=employes_actifs)
class Meta:
- model = ResponsableImplantation
+ model = rh.ResponsableImplantation
+
+
+class MasseSalarialeForm(forms.Form):
+ region = forms.ModelChoiceField(
+ label=u'Région', queryset=ref.Region.objects.all(), required=False
+ )
+ implantation = forms.ModelChoiceField(
+ label=u'Implantation', queryset=ref.Implantation.objects.all(),
+ required=False
+ )
+
+ def __init__(self, user, *args, **kwargs):
+ super(MasseSalarialeForm, self).__init__(*args, **kwargs)
+ min_date = rh.Remuneration.objects \
+ .aggregate(Min('date_debut'))['date_debut__min']
+ years = range(
+ date.today().year,
+ min_date.year if min_date else 1997,
+ -1
+ )
+ self.fields['annee'] = forms.TypedChoiceField(
+ label='Année', choices=zip(years, years), coerce=int,
+ required=False
+ )
+ if user_gere_obj_de_sa_region(user):
+ employe = get_employe_from_user(user)
+ self.fields['region'].queryset = ref.Region.objects.filter(
+ id=employe.implantation.region.id
+ )
+ self.fields['implantation'].queryset = \
+ ref.Implantation.objects.filter(
+ region=employe.implantation.region
+ )
# -*- encoding: utf-8 -*-
-import datetime
from datetime import date
from django.db import models
from django.db.models import Q
+from django.db.models.query import QuerySet
from auf.django.metadata.managers import NoDeleteManager, NoDeleteQuerySet
from project.groups import get_employe_from_user
-from project.groups import grp_administrateurs, \
- grp_directeurs_bureau, \
- grp_drh, \
- grp_drh2, \
- grp_accior, \
- grp_abf, \
- grp_haute_direction, \
- grp_service_utilisateurs, \
- grp_correspondants_rh
+from project.groups import \
+ grp_drh, grp_drh2, grp_accior, grp_abf, grp_haute_direction, \
+ grp_service_utilisateurs
class SecurityManager(models.Manager):
def ma_region_ou_service(self, user):
"""
Filtrage des postes en fonction du user connecté (region / service)
- On s'intéresse aussi au groupe auquel appartient le user car certains groupes
- peuvent tout voir.
+ On s'intéresse aussi au groupe auquel appartient le user car
+ certains groupes peuvent tout voir.
"""
employe = get_employe_from_user(user)
# TRAITEMENT NORMAL
############################################
# REGION
- q = Q(**{ self.prefixe_implantation : employe.implantation.region })
+ q = Q(**{self.prefixe_implantation: employe.implantation.region})
# SERVICE
- if self.prefixe_service and grp_service_utilisateurs in user.groups.all():
- q = q | Q(**{ self.prefixe_service : employe.service})
+ if self.prefixe_service \
+ and grp_service_utilisateurs in user.groups.all():
+ q = q | Q(**{self.prefixe_service: employe.service})
liste = self.get_query_set().filter(q)
return liste
+class ActifsQuerySet(QuerySet):
+
+ def _actifs(self, debut_field, fin_field, date_min=None, date_max=None,
+ annee=None):
+ qs = self
+ if annee:
+ janvier = date(annee, 1, 1)
+ decembre = date(annee, 12, 31)
+ date_min = max(janvier, date_min) if date_min else janvier
+ date_max = min(decembre, date_max) if date_max else decembre
+ if not date_min and not date_max:
+ date_min = date_max = date.today()
+ if date_min:
+ qs = qs.filter(
+ Q(**{fin_field + '__gte': date_min}) |
+ Q(**{fin_field: None})
+ )
+ if date_max:
+ qs = qs.filter(
+ Q(**{debut_field + '__lte': date_max}) |
+ Q(**{debut_field: None})
+ )
+ return qs
+
+ def actifs(self, *args, **kwargs):
+ return self._actifs('date_debut', 'date_fin', *args, **kwargs)
+
+
+class PosteQuerySet(NoDeleteQuerySet, ActifsQuerySet):
+ pass
+
+
class PosteManager(SecurityManager, NoDeleteManager):
- """
- Chargement de tous les objets FK existants sur chaque QuerySet.
- """
prefixe_service = "service"
prefixe_implantation = "implantation__region"
- def actifs(self):
- q_actif = Q(date_fin__gt=datetime.datetime.now()) | Q(date_fin__isnull=True)
- return super(PosteManager, self).get_query_set().filter(q_actif)
+ def get_query_set(self):
+ return PosteQuerySet(self.model).filter(supprime=False) \
+ .select_related('type_poste')
+
+ def actifs(self, *args, **kwargs):
+ return self.get_query_set().actifs(*args, **kwargs)
def ma_region_ou_service(self, user):
return super(PosteManager, self).ma_region_ou_service(user)
- def get_query_set(self):
- fkeys = (
- #'id_rh',
- #'responsable',
- #'implantation',
- #'implantation.bureau_rattachement',
- 'type_poste',
- #'service',
- #'classement_min',
- #'classement_max',
- #'valeur_point_min',
- #'valeur_point_max',
- )
- return super(PosteManager, self).get_query_set() \
- .select_related(*fkeys).all()
+
+class DossierQuerySet(NoDeleteQuerySet, ActifsQuerySet):
+ pass
class DossierManager(SecurityManager, NoDeleteManager):
prefixe_implantation = "poste__implantation__region"
def get_query_set(self):
- fkeys = (
- 'poste',
- 'employe',
- )
- return super(DossierManager, self).get_query_set() \
- .select_related(*fkeys).all()
+ return DossierQuerySet(self.model) \
+ .filter(supprime=False) \
+ .select_related('poste', 'employe')
- def ma_region_ou_service(self, user):
- return super(DossierManager, self).ma_region_ou_service(user)
+ def actifs(self, *args, **kwargs):
+ return self.get_query_set().actifs(*args, **kwargs)
+
+
+class RemunerationQuerySet(NoDeleteQuerySet, ActifsQuerySet):
+
+ def actifs(self, *args, **kwargs):
+ return self \
+ ._actifs('date_debut', 'date_fin', *args, **kwargs) \
+ ._actifs(
+ 'dossier__date_debut', 'dossier__date_fin', *args, **kwargs
+ )
+
+
+class RemunerationManager(NoDeleteManager):
+
+ def get_query_set(self):
+ return RemunerationQuerySet(self.model).filter(supprime=False)
+
+ def actifs(self, *args, **kwargs):
+ return self.get_query_set().actifs(*args, **kwargs)
-class EmployeQuerySet(NoDeleteQuerySet):
+class EmployeQuerySet(NoDeleteQuerySet, ActifsQuerySet):
+
def actifs(self, date_min=None, date_max=None, annee=None):
- qs = self
- if annee:
- janvier = date(annee, 1, 1)
- decembre = date(annee, 12, 31)
- date_min = max(janvier, date_min) if date_min else janvier
- date_max = min(decembre, date_max) if date_max else decembre
- if not date_min and not date_max:
- date_min = date_max = date.today()
- if date_min:
- qs = qs.filter(
- Q(rh_dossiers__date_fin__gte=date_min) | Q(rh_dossiers__date_fin=None)
- )
- if date_max:
- qs = qs.filter(
- Q(rh_dossiers__date_debut__lte=date_max) | Q(rh_dossiers__date_debut=None)
- )
- return qs.distinct()
+ return self._actifs('dossiers__date_debut', 'dossiers__date_fin') \
+ .distinct()
+
class EmployeManager(NoDeleteManager):
def get_query_set(self):
def actifs(self, *args, **kwargs):
return self.get_query_set().actifs(*args, **kwargs)
+
class PosteComparaisonManager(SecurityManager):
use_for_related_fields = True
prefixe_implantation = "implantation__region"
class DeviseManager(NoDeleteManager):
pass
+
class ServiceManager(NoDeleteManager):
pass
+
class TypeRemunerationManager(NoDeleteManager):
pass
PosteManager, DossierManager, EmployeManager, \
DossierComparaisonManager, \
PosteComparaisonManager, DeviseManager, ServiceManager, \
- TypeRemunerationManager
+ TypeRemunerationManager, RemunerationManager
from project.rh.validators import validate_date_passee
)
-
### EMPLOYÉ/PERSONNE
class Employe(AUFMetadata):
)
-
class DossierCommentaire(Commentaire):
dossier = models.ForeignKey(
Dossier, db_column='dossier', related_name='commentaires'
date_debut = models.DateField(u"date de début", null=True, blank=True)
date_fin = models.DateField(u"date de fin", null=True, blank=True)
+ objects = RemunerationManager()
+
class Meta:
abstract = True
ordering = ['type__nom', '-date_fin']
# encoding: utf-8
-from decimal import Decimal
-
-import odf.opendocument
-import odf.style
-import odf.table
-from odf.style import Style, MasterPage, PageLayout, PageLayoutProperties, \
- TextProperties, GraphicProperties, ParagraphProperties, \
- DrawingPageProperties
-
-
-class Separator():
-
- def __unicode__(self):
- return u""
-
- def __str__(self):
- return ""
-
-
-def valuetype(val):
- valuetype = "string"
- if isinstance(val, str):
- valuetype = "string"
- if isinstance(val, (int, float, Decimal)):
- valuetype = "float"
- if isinstance(val, bool):
- valuetype = "boolean"
-
- return valuetype
-
-
-class Wrapper(object):
-
- def __init__(self, *args, **kwargs):
- self.__wrapped = self._wrapper_constructor(*args, **kwargs)
-
- def __getattr__(self, attr):
- return getattr(self.__wrapped, attr)
-
-
-class OpenDocumentSpreadsheet(Wrapper):
- _wrapper_constructor = staticmethod(
- odf.opendocument.OpenDocumentSpreadsheet
+import odsgen
+
+
+def masse_salariale(lignes, annee, titres_traitements, titres_indemnites,
+ titres_primes, titres_charges, masse_salariale_totale):
+ doc = odsgen.Document()
+ doc.add_iso_date_style('iso-date')
+
+ table = doc.add_table(name=u'Masse salariale')
+ for width in (
+ ['1.75cm', '5cm', '4cm', '3cm', '3.5cm', '5cm', '4cm', '6cm',
+ '14cm', '2.5cm', '1.5cm', '4.5cm', '3cm', '1.5cm', '3.75cm',
+ '0.5cm', '2.5cm', '2cm', '3cm', '0.5cm', '1.5cm', '4cm',
+ '3.25cm', '5cm', '0.5cm', '4.25cm'] +
+ ['5cm'] * len(titres_traitements) + ['4cm', '0.5cm'] +
+ ['5cm'] * len(titres_indemnites) + ['4cm', '0.5cm'] +
+ ['5cm'] * len(titres_primes) + ['4cm', '0.5cm'] +
+ ['5cm'] * len(titres_charges) + ['4cm', '0.5cm'] +
+ ['3.75cm'] * 4 + ['0.5cm', '2.75cm', '4cm']
+ ):
+ table.add_column(columnwidth=width)
+
+ row = table.add_row()
+ row.add_cells([
+ u'Bureau', u'Pays', u'Implantation', u'Valeur du point',
+ u"Numéro d'employé", u'Nom', u'Prénom', u'Type de poste',
+ u'Intitulé du poste', u'Niveau actuel', u'Points',
+ u'Régime de travail annuel', u'Local / Expatrié', u'Statut',
+ u'Date de fin de contrat'
+ ], fontweight='bold')
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells([
+ u'Date de début', u'Date de fin', u'Nombre de jours'
+ ], fontweight='bold')
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells([
+ u'Devise', u'Salaire BSTG ANNUEL', u'Salaire BSTG EUR',
+ u'Organisme BSTG'
+ ], fontweight='bold')
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ [u'Salaire théorique annuel'] + titres_traitements +
+ [u'Total des traitements'],
+ backgroundcolor='#ecab44', fontweight='bold'
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ titres_indemnites + [u'Total des indemnités'],
+ backgroundcolor='#fff840', fontweight='bold'
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ titres_primes + [u'Total des primes'],
+ backgroundcolor='#d7fb0f', fontweight='bold'
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ titres_charges + [u'Total des charges'],
+ backgroundcolor='#fb680f', fontweight='bold'
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cell(
+ u'Total des traitements', backgroundcolor='#ecab44',
+ fontweight='bold'
+ )
+ row.add_cell(
+ u'Total des indemnités', backgroundcolor='#fff840',
+ fontweight='bold'
+ )
+ row.add_cell(
+ u'Total des primes', backgroundcolor='#d7fb0f',
+ fontweight='bold'
+ )
+ row.add_cell(
+ u'Total des charges', backgroundcolor='#fb680f',
+ fontweight='bold'
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ [u'Masse salariale', u'Masse salariale EUR'],
+ backgroundcolor='#e6c6ed', fontweight='bold'
)
- def __init__(self, *args, **kwargs):
- super(OpenDocumentSpreadsheet, self).__init__(*args, **kwargs)
- self._automatic_style_idx = 0
-
- def add_table(self, **kwargs):
- table = Table(**kwargs)
- table._doc = self
- self.spreadsheet.addElement(table)
- return table
-
- def add_automatic_style(self, **kwargs):
- name = 'auto_style_%d' % self._automatic_style_idx
- style = odf.style.Style(name=name, **kwargs)
- self.automaticstyles.addElement(style)
- self._automatic_style_idx += 1
- return style
-
-
-class Table(Wrapper):
- _wrapper_constructor = staticmethod(odf.table.Table)
-
- def add_row(self, values=[], **kwargs):
- # attributs appartenant à table-column-poperties
-# props = {}
-# for attr in ['rowheight']:
-# if attr in kwargs:
-# props[attr] = kwargs.pop(attr)
-
- style = self._doc.add_automatic_style(family='table-row')
- if 'rowheight' in kwargs:
- style.addElement(odf.style.TableRowProperties(
- rowheight=kwargs['rowheight']))
- kwargs['stylename'] = style.getAttribute('name')
- del kwargs['rowheight']
-
- style = {}
-
- row = TableRow(**kwargs)
- row._doc = self._doc
- for value in values:
- row.add_cell(value, verticalalign='middle', **style)
- self.addElement(row)
- return row
-
- def add_column(self, **kwargs):
-
- # attributs appartenant à table-column-poperties
- props = {}
- for attr in ['columnwidth']:
- if attr in kwargs:
- props[attr] = kwargs.pop(attr)
-
- if props:
- style = self._doc.add_automatic_style(family='table-column')
- style.addElement(odf.style.TableColumnProperties(**props))
- kwargs['stylename'] = style.getAttribute('name')
- col = odf.table.TableColumn(**kwargs)
- self.addElement(col)
- return col
-
-
-class TableRow(Wrapper):
- _wrapper_constructor = staticmethod(odf.table.TableRow)
-
- def add_cell(self, value=None, **kwargs):
- if value:
- if isinstance(value, (basestring, unicode)):
- kwargs['stringvalue'] = unicode(value)
- elif isinstance(value, (int, float, Decimal)):
- kwargs['valuetype'] = "float"
- kwargs['value'] = float(value)
- elif type(value) == type(None) or isinstance(value, Separator):
- kwargs['stringvalue'] = u""
- else:
- kwargs['stringvalue'] = unicode(value)
-
- style = self._doc.add_automatic_style(family='table-cell')
- if 'verticalalign' in kwargs:
- style.addElement(odf.style.TableCellProperties(
- verticalalign=kwargs['verticalalign'], wrapoption='wrap'))
- del kwargs['verticalalign']
-
- if isinstance(value, Separator) or type(value) == type(Separator()):
- style.addElement(odf.style.TableCellProperties(
- backgroundcolor='#D3D3D3'))
-
- kwargs['stylename'] = style.getAttribute('name')
-# props = {}
-# if 'fontweight' in kwargs:
-# props['fontweight'] = kwargs.pop('fontweight')
-# if 'stringvalue' in kwargs:
-# props['stringvalue'] = kwargs.pop('stringvalue')
-
- cell = odf.table.TableCell(**kwargs)
-# if 'fontweight' in props:
-# tablecontents = Style(name="Bold", family="paragraph")
-# tablecontents.addElement(TextProperties(fontweight="bold"))
-# self._doc.styles.addElement(tablecontents)
-
-# if 'stringvalue' in props:
-# p = P(stylename='Bold',text=props['stringvalue'])
-# cell.addElement(p)
-
- self.addElement(cell)
- return cell
+ for ligne in lignes:
+ row = table.add_row()
+ row.add_cells([
+ ligne['poste'].implantation.region.code,
+ ligne['poste'].implantation.adresse_physique_pays.nom,
+ ligne['poste'].implantation.nom_court
+ ])
+ row.add_cell(ligne['valeur_point'], decimalplaces=2)
+ row.add_cells([
+ ligne['dossier'].employe.id or 'VACANT',
+ ligne['dossier'].employe.nom,
+ ligne['dossier'].employe.prenom,
+ ligne['poste'].type_poste.nom,
+ ligne['poste'].nom,
+ unicode(ligne['dossier'].classement),
+ ligne['dossier'].classement
+ and ligne['dossier'].classement.coefficient,
+ ligne['regime_travail'],
+ ligne['local_expatrie'],
+ ligne['dossier'].statut.code,
+ ])
+ row.add_cell(ligne['dossier'].date_fin, datastylename='iso-date')
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells([
+ ligne['date_debut'], ligne['date_fin']
+ ], datastylename='iso-date')
+ row.add_cell(ligne['jours'])
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cell(ligne['devise'])
+ row.add_cells([
+ ligne['salaire_bstg'], ligne['salaire_bstg_eur']
+ ], decimalplaces=2)
+ row.add_cell(
+ ligne['dossier'].organisme_bstg
+ and ligne['dossier'].organisme_bstg.nom,
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ [ligne['salaire_theorique']] + ligne['traitements'] +
+ [ligne['total_traitements']],
+ decimalplaces=2
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ ligne['indemnites'] + [ligne['total_indemnites']],
+ decimalplaces=2
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ ligne['primes'] + [ligne['total_primes']],
+ decimalplaces=2
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells(
+ ligne['charges'] + [ligne['total_charges']],
+ decimalplaces=2
+ )
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells([
+ ligne['total_traitements'], ligne['total_indemnites'],
+ ligne['total_primes'], ligne['total_charges']
+ ], decimalplaces=2)
+ row.add_cell(backgroundcolor='#d3d3d3')
+ row.add_cells([
+ ligne['masse_salariale'], ligne['masse_salariale_eur']
+ ], decimalplaces=2)
+ return doc
--- /dev/null
+/*
+ * File: FixedHeader.min.js
+ * Version: 2.0.6
+ * Author: Allan Jardine (www.sprymedia.co.uk)
+ *
+ * Copyright 2009-2011 Allan Jardine, all rights reserved.
+ *
+ * This source file is free software, under either the GPL v2 license or a
+ * BSD (3 point) style license, as supplied with this software.
+ *
+ * This source file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
+ */
+var FixedHeader=function(a,c){if("function"!=typeof this.fnInit)alert("FixedHeader warning: FixedHeader must be initialised with the 'new' keyword.");else{var b={aoCache:[],oSides:{top:!0,bottom:!1,left:!1,right:!1},oZIndexes:{top:104,bottom:103,left:102,right:101},oMes:{iTableWidth:0,iTableHeight:0,iTableLeft:0,iTableRight:0,iTableTop:0,iTableBottom:0},oOffset:{top:0},nTable:null,bUseAbsPos:!1,bFooter:!1};this.fnGetSettings=function(){return b};this.fnUpdate=function(){this._fnUpdateClones();this._fnUpdatePositions()};
+this.fnPosition=function(){this._fnUpdatePositions()};this.fnInit(a,c);if("function"==typeof a.fnSettings)a._oPluginFixedHeader=this}};
+FixedHeader.prototype={fnInit:function(a,c){var b=this.fnGetSettings(),d=this;this.fnInitSettings(b,c);if("function"==typeof a.fnSettings){if("functon"==typeof a.fnVersionCheck&&!0!==a.fnVersionCheck("1.6.0")){alert("FixedHeader 2 required DataTables 1.6.0 or later. Please upgrade your DataTables installation");return}var e=a.fnSettings();if(""!=e.oScroll.sX||""!=e.oScroll.sY){alert("FixedHeader 2 is not supported with DataTables' scrolling mode at this time");return}b.nTable=e.nTable;e.aoDrawCallback.push({fn:function(){FixedHeader.fnMeasure();
+d._fnUpdateClones.call(d);d._fnUpdatePositions.call(d)},sName:"FixedHeader"})}else b.nTable=a;b.bFooter=0<$(">tfoot",b.nTable).length?!0:!1;b.bUseAbsPos=jQuery.browser.msie&&("6.0"==jQuery.browser.version||"7.0"==jQuery.browser.version);b.oSides.top&&b.aoCache.push(d._fnCloneTable("fixedHeader","FixedHeader_Header",d._fnCloneThead));b.oSides.bottom&&b.aoCache.push(d._fnCloneTable("fixedFooter","FixedHeader_Footer",d._fnCloneTfoot));b.oSides.left&&b.aoCache.push(d._fnCloneTable("fixedLeft","FixedHeader_Left",
+d._fnCloneTLeft));b.oSides.right&&b.aoCache.push(d._fnCloneTable("fixedRight","FixedHeader_Right",d._fnCloneTRight));FixedHeader.afnScroll.push(function(){d._fnUpdatePositions.call(d)});jQuery(window).resize(function(){FixedHeader.fnMeasure();d._fnUpdateClones.call(d);d._fnUpdatePositions.call(d)});FixedHeader.fnMeasure();d._fnUpdateClones();d._fnUpdatePositions()},fnInitSettings:function(a,c){if("undefined"!=typeof c){if("undefined"!=typeof c.top)a.oSides.top=c.top;if("undefined"!=typeof c.bottom)a.oSides.bottom=
+c.bottom;if("undefined"!=typeof c.left)a.oSides.left=c.left;if("undefined"!=typeof c.right)a.oSides.right=c.right;if("undefined"!=typeof c.zTop)a.oZIndexes.top=c.zTop;if("undefined"!=typeof c.zBottom)a.oZIndexes.bottom=c.zBottom;if("undefined"!=typeof c.zLeft)a.oZIndexes.left=c.zLeft;if("undefined"!=typeof c.zRight)a.oZIndexes.right=c.zRight;if("undefined"!=typeof c.offsetTop)a.oOffset.top=c.offsetTop}a.bUseAbsPos=jQuery.browser.msie&&("6.0"==jQuery.browser.version||"7.0"==jQuery.browser.version)},
+_fnCloneTable:function(a,c,b){var d=this.fnGetSettings(),e;if("absolute"!=jQuery(d.nTable.parentNode).css("position"))d.nTable.parentNode.style.position="relative";e=d.nTable.cloneNode(!1);e.removeAttribute("id");var f=document.createElement("div");f.style.position="absolute";f.style.top="0px";f.style.left="0px";f.className+=" FixedHeader_Cloned "+a+" "+c;if("fixedHeader"==a)f.style.zIndex=d.oZIndexes.top;if("fixedFooter"==a)f.style.zIndex=d.oZIndexes.bottom;if("fixedLeft"==a)f.style.zIndex=d.oZIndexes.left;
+else if("fixedRight"==a)f.style.zIndex=d.oZIndexes.right;e.style.margin="0";f.appendChild(e);document.body.appendChild(f);return{nNode:e,nWrapper:f,sType:a,sPosition:"",sTop:"",sLeft:"",fnClone:b}},_fnMeasure:function(){var a=this.fnGetSettings(),c=a.oMes,b=jQuery(a.nTable),d=b.offset(),e=this._fnSumScroll(a.nTable.parentNode,"scrollTop");this._fnSumScroll(a.nTable.parentNode,"scrollLeft");c.iTableWidth=b.outerWidth();c.iTableHeight=b.outerHeight();c.iTableLeft=d.left+a.nTable.parentNode.scrollLeft;
+c.iTableTop=d.top+e;c.iTableRight=c.iTableLeft+c.iTableWidth;c.iTableRight=FixedHeader.oDoc.iWidth-c.iTableLeft-c.iTableWidth;c.iTableBottom=FixedHeader.oDoc.iHeight-c.iTableTop-c.iTableHeight},_fnSumScroll:function(a,c){for(var b=a[c];(a=a.parentNode)&&!("HTML"==a.nodeName||"BODY"==a.nodeName);)b=a[c];return b},_fnUpdatePositions:function(){var a=this.fnGetSettings();this._fnMeasure();for(var c=0,b=a.aoCache.length;c<b;c++)"fixedHeader"==a.aoCache[c].sType?this._fnScrollFixedHeader(a.aoCache[c]):
+"fixedFooter"==a.aoCache[c].sType?this._fnScrollFixedFooter(a.aoCache[c]):"fixedLeft"==a.aoCache[c].sType?this._fnScrollHorizontalLeft(a.aoCache[c]):this._fnScrollHorizontalRight(a.aoCache[c])},_fnUpdateClones:function(){for(var a=this.fnGetSettings(),c=0,b=a.aoCache.length;c<b;c++)a.aoCache[c].fnClone.call(this,a.aoCache[c])},_fnScrollHorizontalRight:function(a){var c=this.fnGetSettings(),b=c.oMes,d=FixedHeader.oWin,e=FixedHeader.oDoc,f=a.nWrapper,g=jQuery(f).outerWidth();d.iScrollRight<b.iTableRight?
+(this._fnUpdateCache(a,"sPosition","absolute","position",f.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",f.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+b.iTableWidth-g+"px","left",f.style)):b.iTableLeft<e.iWidth-d.iScrollRight-g?c.bUseAbsPos?(this._fnUpdateCache(a,"sPosition","absolute","position",f.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",f.style),this._fnUpdateCache(a,"sLeft",e.iWidth-d.iScrollRight-g+"px","left",f.style)):(this._fnUpdateCache(a,"sPosition","fixed",
+"position",f.style),this._fnUpdateCache(a,"sTop",b.iTableTop-d.iScrollTop+"px","top",f.style),this._fnUpdateCache(a,"sLeft",d.iWidth-g+"px","left",f.style)):(this._fnUpdateCache(a,"sPosition","absolute","position",f.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",f.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",f.style))},_fnScrollHorizontalLeft:function(a){var c=this.fnGetSettings(),b=c.oMes,d=FixedHeader.oWin,e=a.nWrapper,f=jQuery(e).outerWidth();d.iScrollLeft<b.iTableLeft?
+(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",e.style)):d.iScrollLeft<b.iTableLeft+b.iTableWidth-f?c.bUseAbsPos?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",d.iScrollLeft+"px","left",e.style)):(this._fnUpdateCache(a,"sPosition","fixed","position",e.style),
+this._fnUpdateCache(a,"sTop",b.iTableTop-d.iScrollTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft","0px","left",e.style)):(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+b.iTableWidth-f+"px","left",e.style))},_fnScrollFixedFooter:function(a){var c=this.fnGetSettings(),b=c.oMes,d=FixedHeader.oWin,e=FixedHeader.oDoc,f=a.nWrapper,g=jQuery("thead",c.nTable).outerHeight(),h=jQuery(f).outerHeight();
+d.iScrollBottom<b.iTableBottom?(this._fnUpdateCache(a,"sPosition","absolute","position",f.style),this._fnUpdateCache(a,"sTop",b.iTableTop+b.iTableHeight-h+"px","top",f.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",f.style)):d.iScrollBottom<b.iTableBottom+b.iTableHeight-h-g?c.bUseAbsPos?(this._fnUpdateCache(a,"sPosition","absolute","position",f.style),this._fnUpdateCache(a,"sTop",e.iHeight-d.iScrollBottom-h+"px","top",f.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",
+f.style)):(this._fnUpdateCache(a,"sPosition","fixed","position",f.style),this._fnUpdateCache(a,"sTop",d.iHeight-h+"px","top",f.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft-d.iScrollLeft+"px","left",f.style)):(this._fnUpdateCache(a,"sPosition","absolute","position",f.style),this._fnUpdateCache(a,"sTop",b.iTableTop+h+"px","top",f.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",f.style))},_fnScrollFixedHeader:function(a){for(var c=this.fnGetSettings(),b=c.oMes,d=FixedHeader.oWin,e=
+a.nWrapper,f=0,g=c.nTable.getElementsByTagName("tbody"),h=0;h<g.length;++h)f+=g[h].offsetHeight;b.iTableTop>d.iScrollTop+c.oOffset.top?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",e.style)):d.iScrollTop+c.oOffset.top>b.iTableTop+f?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",b.iTableTop+f+"px","top",e.style),this._fnUpdateCache(a,
+"sLeft",b.iTableLeft+"px","left",e.style)):c.bUseAbsPos?(this._fnUpdateCache(a,"sPosition","absolute","position",e.style),this._fnUpdateCache(a,"sTop",d.iScrollTop+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft+"px","left",e.style)):(this._fnUpdateCache(a,"sPosition","fixed","position",e.style),this._fnUpdateCache(a,"sTop",c.oOffset.top+"px","top",e.style),this._fnUpdateCache(a,"sLeft",b.iTableLeft-d.iScrollLeft+"px","left",e.style))},_fnUpdateCache:function(a,c,b,d,e){a[c]!=b&&(e[d]=
+b,a[c]=b)},_fnCloneThead:function(a){var c=this.fnGetSettings(),b=a.nNode;for(a.nWrapper.style.width=jQuery(c.nTable).outerWidth()+"px";0<b.childNodes.length;)jQuery("thead th",b).unbind("click"),b.removeChild(b.childNodes[0]);a=jQuery("thead",c.nTable).clone(!0)[0];b.appendChild(a);jQuery("thead>tr th",c.nTable).each(function(a){jQuery("thead>tr th:eq("+a+")",b).width(jQuery(this).width())});jQuery("thead>tr td",c.nTable).each(function(a){jQuery("thead>tr td:eq("+a+")",b).width(jQuery(this).width())})},
+_fnCloneTfoot:function(a){var c=this.fnGetSettings(),b=a.nNode;for(a.nWrapper.style.width=jQuery(c.nTable).outerWidth()+"px";0<b.childNodes.length;)b.removeChild(b.childNodes[0]);a=jQuery("tfoot",c.nTable).clone(!0)[0];b.appendChild(a);jQuery("tfoot:eq(0)>tr th",c.nTable).each(function(a){jQuery("tfoot:eq(0)>tr th:eq("+a+")",b).width(jQuery(this).width())});jQuery("tfoot:eq(0)>tr td",c.nTable).each(function(a){jQuery("tfoot:eq(0)>tr th:eq("+a+")",b)[0].style.width(jQuery(this).width())})},_fnCloneTLeft:function(a){var c=
+this.fnGetSettings(),b=a.nNode,d=$("tbody",c.nTable)[0];for($("tbody tr:eq(0) td",c.nTable);0<b.childNodes.length;)b.removeChild(b.childNodes[0]);b.appendChild(jQuery("thead",c.nTable).clone(!0)[0]);b.appendChild(jQuery("tbody",c.nTable).clone(!0)[0]);c.bFooter&&b.appendChild(jQuery("tfoot",c.nTable).clone(!0)[0]);$("thead tr",b).each(function(){$("th:gt(0)",this).remove()});$("tfoot tr",b).each(function(){$("th:gt(0)",this).remove()});$("tbody tr",b).each(function(){$("td:gt(0)",this).remove()});
+this.fnEqualiseHeights("tbody",d.parentNode,b);c=jQuery("thead tr th:eq(0)",c.nTable).outerWidth();b.style.width=c+"px";a.nWrapper.style.width=c+"px"},_fnCloneTRight:function(a){for(var c=this.fnGetSettings(),b=$("tbody",c.nTable)[0],d=a.nNode,e=jQuery("tbody tr:eq(0) td",c.nTable).length;0<d.childNodes.length;)d.removeChild(d.childNodes[0]);d.appendChild(jQuery("thead",c.nTable).clone(!0)[0]);d.appendChild(jQuery("tbody",c.nTable).clone(!0)[0]);c.bFooter&&d.appendChild(jQuery("tfoot",c.nTable).clone(!0)[0]);
+jQuery("thead tr th:not(:nth-child("+e+"n))",d).remove();jQuery("tfoot tr th:not(:nth-child("+e+"n))",d).remove();$("tbody tr",d).each(function(){$("td:lt("+(e-1)+")",this).remove()});this.fnEqualiseHeights("tbody",b.parentNode,d);c=jQuery("thead tr th:eq("+(e-1)+")",c.nTable).outerWidth();d.style.width=c+"px";a.nWrapper.style.width=c+"px"},fnEqualiseHeights:function(a,c,b){var d=$(a+" tr:eq(0)",c).children(":eq(0)"),e=d.outerHeight()-d.height(),f=$.browser.msie&&("6.0"==$.browser.version||"7.0"==
+$.browser.version);$(a+" tr",b).each(function(b){$.browser.mozilla||$.browser.opera?$(this).children().height($(a+" tr:eq("+b+")",c).outerHeight()):$(this).children().height($(a+" tr:eq("+b+")",c).outerHeight()-e);f||$(a+" tr:eq("+b+")",c).height($(a+" tr:eq("+b+")",c).outerHeight())})}};FixedHeader.oWin={iScrollTop:0,iScrollRight:0,iScrollBottom:0,iScrollLeft:0,iHeight:0,iWidth:0};FixedHeader.oDoc={iHeight:0,iWidth:0};FixedHeader.afnScroll=[];
+FixedHeader.fnMeasure=function(){var a=jQuery(window),c=jQuery(document),b=FixedHeader.oWin,d=FixedHeader.oDoc;d.iHeight=c.height();d.iWidth=c.width();b.iHeight=a.height();b.iWidth=a.width();b.iScrollTop=a.scrollTop();b.iScrollLeft=a.scrollLeft();b.iScrollRight=d.iWidth-b.iScrollLeft-b.iWidth;b.iScrollBottom=d.iHeight-b.iScrollTop-b.iHeight};FixedHeader.VERSION="2.0.6";FixedHeader.prototype.VERSION=FixedHeader.VERSION;
+jQuery(window).scroll(function(){FixedHeader.fnMeasure();for(var a=0,c=FixedHeader.afnScroll.length;a<c;a++)FixedHeader.afnScroll[a]()});
-{% extends 'rh/rapports/base.html' %}
+{% extends 'admin/base_site.html' %}
{% load adminmedia rapports i18n l10n %}
-{% block nomrapport %}Rapport de masse salariale{% endblock %}
-{% block count_elements %}<h2>Rapport année {{ request.GET.annee }}</h2>{% endblock %}
+{% block content_title %}<h1>Rapport de masse salariale</h1>{% endblock %}
{% block extrastyle %}
{{ block.super }}
<style type="text/css">
- #changelist .actions .filter {width: auto; float: left;}
- #changelist .actions .filter h3 {font-size: 11px; margin-left: 0.5em;}
- #result_list .highlighted {background: #ffff88;}
+table.rapport { border: 1px solid #dddddd; }
+table.rapport th { background: white; }
+table.rapport th.traitements { background: #ecab44; }
+table.rapport th.indemnites { background: #fff840; }
+table.rapport th.primes { background: #d7fb0f; }
+table.rapport th.charges { background: #fb680f; }
+table.rapport .highlighted { background: #ffff88; }
+th.section-end, td.section-end { border-right: 10px solid #dddddd; }
+td.nowrap { white-space: nowrap; }
</style>
{% endblock %}
{% block extrahead %}
{{ block.super }}
-<script type="text/javascript" src="{{ STATIC_URL }}js/jquery-1.5.1.min.js"></script>
-<script type="text/javascript" src="{% admin_media_prefix %}js/jquery-stickytableheaders.js"></script>
+<script type="text/javascript"
+ src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
+<script type="text/javascript" src="{{ STATIC_URL }}rh/FixedHeader.min.js"></script>
<script type="text/javascript">
-$(function() {
- $('#result_list tr').click(function() {
+$(document).ready(function() {
+ new FixedHeader($('table.rapport').get(0));
+ $('table.rapport tr').click(function() {
$(this).toggleClass('highlighted');
});
});
</script>
{% endblock %}
-{% block contentrapport %}
-{% if url_ods %}
-<ul class="object-tools">
- <li>
- <a href="{{ url_ods }}">Exporter en ods</a>
- </li>
-</ul>
-{% endif %}
-<form method="GET">
- <input type="hidden" name="ne_pas_grouper" value="True" />
- <div class="actions">
- <div class="filter">
- <h3>Région</h3>
- {{ form.region }}
- </div>
- <div class="filter">
- <h3>Implantation</h3>
- {{ form.implantation }}
- </div>
- <div class="filter">
- <h3>Année</h3>
- {{ form.annee }}
- </div>
- <div class="filter" style="margin-left:20px">
- <h3> </h3>
- <button type="submit" class="button" title="Exécuter l'action sélectionnée">Rechercher</button>
- </div>
- <div class="clear"></div>
- {% comment %}
- <label>Plage de dates:
- {{ form.date_debut }} au {{ form.date_fin }}
- </label>
- {% endcomment %}
- </div>
+{% block content %}
+<form class="module">
+ <table style="width: 100%">
+ {{ form }}
+ <tr>
+ <td></td>
+ <td>
+ <input type="submit" value="Afficher">
+ <input type="submit" name="ods" value="Format Calc">
+ </td>
+ </tr>
+ </table>
</form>
-<div class="clear"></div>
+{% if lignes %}
+<p>
+ <strong>Masse salariale totale: {{ masse_salariale_totale }} EUR</strong>
+</p>
-<script type="text/javascript">
- jQuery(document).ready(function(){
- $("#result_list").stickyTableHeaders();
- });
-</script>
-<table id="result_list" class="results">
-<thead>
-<tr>
- {% table_header headers %}
-</tr>
-</thead>
-{% localize on %}
-{% spaceless %}
-{% for row in rapport %}
- <tr class="{% cycle 'row1' 'row2' %}">
- {% for column in header_keys %}
- {% if column == 'sep' %}
- <td style="background:gray;"> </td>
- {% else %}
- {% if row|hash:column|is_float %}
- <td>
- {% comment %}
- {% if options.backgroundcolor %}
- style="background-color:{{ options.backgroundcolor }}"
- {% endif %}
- {% endcomment %}
- {{ row|hash:column|floatformat:2|localize }}
- {% if column != "point" %}
- {% if forloop.last or column|contains:"euro" %}
- EUR
- {% else %}
- {{ row.devise }}
- {% endif %}
- {% endif %}
- </td>
- {% else %}
- <td>{{ row|hash:column|default:"" }}</td>
- {% endif %}
- {% endif %}
- {% endfor %}
- </tr>
-{% endfor %}{% endspaceless %}
+<table class="rapport">
+ <thead>
<tr>
- <td colspan="{{ colspan }}" style="text-align:right;font-weight:bold;">
- TOTAL :
- </td>
- <td>{{ total_euro }}</td>
+ <th>Bureau</th>
+ <th>Pays</th>
+ <th>Implantation</th>
+ <th>Valeur du point</th>
+ <th>Numéro d'employé</th>
+ <th>Nom</th>
+ <th>Prénom</th>
+ <th>Type de poste</th>
+ <th>Intitulé du poste</th>
+ <th>Niveau actuel</th>
+ <th>Points</th>
+ <th>Régime de travail annuel</th>
+ <th>Local / Expatrié</th>
+ <th>Statut</th>
+ <th class="section-end">Date de fin de contrat</th>
+ <th>Date de début</th>
+ <th>Date de fin</th>
+ <th class="section-end">Nombre de jours</th>
+ <th>Devise</th>
+ <th>Salaire BSTG ANNUEL</th>
+ <th>Salaire BSTG EUR</th>
+ <th class="section-end">Organisme BSTG</th>
+ <th class="traitements">
+ Salaire théorique annuel
+ </th>
+ {% for titre in titres_traitements %}
+ <th class="traitements">{{ titre }}</th>
+ {% endfor %}
+ <th class="traitements section-end">
+ Total des traitements
+ </th>
+ {% for titre in titres_indemnites %}
+ <th class="indemnites">{{ titre }}</th>
+ {% endfor %}
+ <th class="indemnites section-end">Total des indemnités</th>
+ {% for titre in titres_primes %}
+ <th class="primes">{{ titre }}</th>
+ {% endfor %}
+ <th class="primes section-end">Total des primes</th>
+ {% for titre in titres_charges %}
+ <th class="charges">{{ titre }}</th>
+ {% endfor %}
+ <th class="charges section-end">Total des charges</th>
+ <th class="traitements">Total des traitements</th>
+ <th class="indemnites">Total des indemnités</th>
+ <th class="primes">Total des primes</th>
+ <th class="charges section-end">Total des charges</th>
+ <th>Masse salariale</th>
+ <th>Masse salariale EUR</th>
+ </tr>
+ </thead>
+ <tbody>
+ {% for ligne in lignes %}
+ <tr class="{% cycle 'row1' 'row2' %}">
+ <td>{{ ligne.poste.implantation.region.code }}</td>
+ <td>{{ ligne.poste.implantation.adresse_physique_pays.nom }}</td>
+ <td>{{ ligne.poste.implantation.nom_court }}</td>
+ <td class="nowrap">
+ {% if ligne.valeur_point %}
+ {{ ligne.valeur_point }} {{ ligne.valeur_point_devise }}
+ {% endif %}
+ </td>
+ <td>
+ {% if ligne.dossier %}
+ {{ ligne.dossier.employe.id|stringformat:"d" }}
+ {% else %}
+ VACANT
+ {% endif %}
+ </td>
+ <td>{{ ligne.dossier.employe.nom }}</td>
+ <td>{{ ligne.dossier.employe.prenom }}</td>
+ <td>{{ ligne.poste.type_poste.nom }}</td>
+ <td>{{ ligne.poste.nom }}</td>
+ <td>{{ ligne.dossier.classement }}</td>
+ <td class="nowrap">
+ {{ ligne.dossier.classement.coefficient|floatformat:2 }}
+ </td>
+ <td class="nowrap">
+ {{ ligne.regime_travail|floatformat }} %
+ </td>
+ <td>{{ ligne.local_expatrie }}</td>
+ <td>{{ ligne.dossier.statut.code }}</td>
+ <td class="section-end">{{ ligne.dossier.date_fin|date }}</td>
+ <td>
+ {{ ligne.date_debut|date }}
+ </td>
+ <td>
+ {{ ligne.date_fin|date }}
+ </td>
+ <td class="section-end">
+ {{ ligne.jours }}
+ </td>
+ <td>{{ ligne.devise }}</td>
+ <td class="nowrap">
+ {% if ligne.salaire_bstg %}
+ {{ ligne.salaire_bstg }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ <td class="nowrap">
+ {% if ligne.salaire_bstg_eur %}
+ {{ ligne.salaire_bstg_eur }} EUR
+ {% endif %}
+ </td>
+ <td class="section-end">{{ ligne.dossier.organisme_bstg.nom }}</td>
+ <td class="nowrap">
+ {% if ligne.salaire_theorique %}
+ {{ ligne.salaire_theorique }} {{ ligne.valeur_point_devise }}
+ {% endif %}
+ </td>
+ {% for traitement in ligne.traitements %}
+ <td class="nowrap">
+ {% if traitement %}
+ {{ traitement }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% endfor %}
+ <td class="section-end nowrap">
+ {% if ligne.total_traitements %}
+ {{ ligne.total_traitements }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% for indemnite in ligne.indemnites %}
+ <td class="nowrap">
+ {% if indemnite %}
+ {{ indemnite }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% endfor %}
+ <td class="section-end nowrap">
+ {% if ligne.total_indemnites %}
+ {{ ligne.total_indemnites }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% for prime in ligne.primes %}
+ <td class="nowrap">
+ {% if prime %}
+ {{ prime }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% endfor %}
+ <td class="section-end nowrap">
+ {% if ligne.total_primes %}
+ {{ ligne.total_primes }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% for charge in ligne.charges %}
+ <td class="nowrap">
+ {% if charge %}
+ {{ charge }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ {% endfor %}
+ <td class="section-end nowrap">
+ {% if ligne.total_charges %}
+ {{ ligne.total_charges }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ <td class="nowrap">
+ {% if ligne.total_traitements %}
+ {{ ligne.total_traitements }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ <td class="nowrap">
+ {% if ligne.total_indemnites %}
+ {{ ligne.total_indemnites }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ <td class="nowrap">
+ {% if ligne.total_primes %}
+ {{ ligne.total_primes }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ <td class="section-end nowrap">
+ {% if ligne.total_charges %}
+ {{ ligne.total_charges }} {{ ligne.devise }}
+ {% endif %}
+ </td>
+ <td class="nowrap">
+ {{ ligne.masse_salariale }} {{ ligne.devise }}
+ </td>
+ <td class="nowrap">
+ {{ ligne.masse_salariale_eur }} EUR
+ </td>
</tr>
+ {% endfor %}
+ </tbody>
</table>
-{% endlocalize %}
+{% endif %}
+
{% endblock %}
# -*- encoding: utf-8 -*-
-import urllib
+from collections import defaultdict
from datetime import date
-import StringIO
+from decimal import Decimal
import pygraphviz as pgv
-
-from django import forms
+from auf.django.references import models as ref
from django.conf import settings
from django.contrib.auth.decorators import login_required
-from django.core.servers.basehttp import FileWrapper
from django.core.urlresolvers import reverse
from django.db.models import Q
from django.http import HttpResponse
from django.shortcuts import render, get_object_or_404
-from auf.django.references import models as ref
-
from project.decorators import drh_or_admin_required
from project.decorators import region_protected
-from project.groups import get_employe_from_user
-from project.groups import grp_drh, grp_correspondants_rh
-
-from project.rh import models as rh
+from project.groups import \
+ get_employe_from_user, grp_drh, grp_correspondants_rh
+from project.rh import ods
from project.rh import graph as rh_graph
+from project.rh import models as rh
from project.rh.change_list import RechercheTemporelle
+from project.rh.forms import MasseSalarialeForm
from project.rh.lib import get_lookup_params
-from project.rh.masse_salariale import MasseSalariale
from project.rh.templatetags.rapports import SortHeaders
+TWOPLACES = Decimal('0.01')
+
@login_required
def profil(request):
# affichage
employes = set([c.dossier.employe_id for c in contrats])
-
headers = [
("dossier__employe__id", u"#"),
("dossier__employe__nom", u"Employé"),
@login_required
@drh_or_admin_required
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']
+ implantation = form.cleaned_data['implantation']
+ annee = form.cleaned_data['annee']
+ debut_annee = date(annee, 1, 1)
+ fin_annee = date(annee, 12, 31)
+ jours_annee = (fin_annee - debut_annee).days + 1
+ today = date.today()
+
+ # Récupérer les dossiers actifs
+ dossiers = rh.Dossier.objects \
+ .actifs(annee=annee) \
+ .select_related(
+ 'poste', 'poste__implantation',
+ 'poste__implantation__region',
+ 'poste__implantation__adresse_physique_pays',
+ 'employe', 'poste__type_poste', 'classement',
+ 'statut', 'organisme_bstg'
+ ) \
+ .extra(
+ tables=['rh_valeurpoint', 'rh_devise'],
+ where=[
+ 'rh_valeurpoint.annee = %s',
+ 'rh_valeurpoint.implantation = ref_implantation.id',
+ 'rh_devise.id = rh_valeurpoint.devise'
+ ],
+ params=[annee],
+ select={
+ 'valeur_point': 'rh_valeurpoint.valeur',
+ 'valeur_point_devise': 'rh_devise.code'
+ }
+ )
+ if region:
+ dossiers = dossiers.filter(poste__implantation__region=region)
+ if implantation:
+ dossiers = dossiers.filter(poste__implantation=implantation)
+
+ # Récupérer les rémunérations actives
+ remuns = rh.Remuneration.objects \
+ .actifs(annee=annee) \
+ .select_related('devise', 'type') \
+ .extra(
+ tables=['rh_tauxchange'],
+ where=[
+ 'rh_tauxchange.annee = %s',
+ 'rh_tauxchange.devise = rh_devise.id'
+ ],
+ params=[annee],
+ select={
+ 'taux_change': 'rh_tauxchange.taux'
+ }
+ )
+ remuns_par_dossier = defaultdict(list)
+ for remun in remuns:
+ remuns_par_dossier[remun.dossier_id].append(remun)
+
+ # Récupérer les types de rémunération par nature
+ types_remun_par_nature = defaultdict(list)
+ for type in rh.TypeRemuneration.objects.all():
+ types_remun_par_nature[type.nature_remuneration].append(type)
+ titres_traitements = [
+ t.nom for t in types_remun_par_nature[u'Traitement']
+ ]
+ titres_indemnites = [
+ t.nom for t in types_remun_par_nature[u'Indemnité']
+ ]
+ titres_primes = [
+ t.nom for t in types_remun_par_nature[u'Accessoire']
+ ]
+ titres_charges = [
+ t.nom for t in types_remun_par_nature[u'Charges']
+ ]
- class RechercheTemporelle(forms.Form):
- CHOICE_ANNEES = range(
- rh.Remuneration.objects.exclude(date_debut=None)
- .order_by('date_debut')[0].date_debut.year,
- date.today().year + 1
- )
+ # Boucler sur les dossiers et préprarer les lignes du rapport
+ lignes = []
+ masse_salariale_totale = 0
+ for dossier in dossiers:
+ debut_cette_annee = \
+ max(dossier.date_debut or debut_annee, debut_annee)
+ fin_cette_annee = \
+ min(dossier.date_fin or fin_annee, fin_annee)
+ jours = (fin_cette_annee - debut_cette_annee).days + 1
+
+ remuns = remuns_par_dossier[dossier.id]
+ devises = set(remun.devise.code for remun in remuns)
+ if len(devises) == 1:
+ devise = remuns[0].devise.code
+ montant_remun = lambda r: r.montant
+ taux_change = Decimal(str(remuns[0].taux_change))
+ else:
+ devise = 'EUR'
+ montant_remun = lambda r: (
+ r.montant * Decimal(str(r.taux_change))
+ )
+ taux_change = Decimal(1)
+
+ remuns_par_type = defaultdict(lambda: 0)
+ for remun in remuns:
+ remuns_par_type[remun.type_id] += (
+ montant_remun(remun) * ((
+ min(remun.date_fin or fin_annee, fin_annee) -
+ max(remun.date_debut or debut_annee, debut_annee)
+ ).days + 1) / jours_annee
+ ).quantize(TWOPLACES)
+ traitements = [
+ remuns_par_type[type.id]
+ for type in types_remun_par_nature[u'Traitement']
+ ]
+ indemnites = [
+ remuns_par_type[type.id]
+ for type in types_remun_par_nature[u'Indemnité']
+ ]
+ primes = [
+ remuns_par_type[type.id]
+ for type in types_remun_par_nature[u'Accessoire']
+ ]
+ charges = [
+ remuns_par_type[type.id]
+ for type in types_remun_par_nature[u'Charges']
+ ]
+ masse_salariale = sum(remuns_par_type.values())
+ masse_salariale_eur = (
+ masse_salariale * taux_change
+ ).quantize(TWOPLACES)
+ masse_salariale_totale += masse_salariale_eur
+
+ if dossier.valeur_point and dossier.classement \
+ and dossier.classement.coefficient and dossier.regime_travail:
+ salaire_theorique = (Decimal(str(
+ dossier.valeur_point * dossier.classement.coefficient
+ )) * dossier.regime_travail / 100).quantize(TWOPLACES)
+ else:
+ salaire_theorique = None
+
+ lignes.append({
+ 'dossier': dossier,
+ 'poste': dossier.poste,
+ 'date_debut': (
+ dossier.date_debut
+ if dossier.date_debut and dossier.date_debut.year == annee
+ else None
+ ),
+ 'date_fin': (
+ dossier.date_fin
+ if dossier.date_fin and dossier.date_fin.year == annee
+ else None
+ ),
+ 'jours': jours,
+ 'devise': devise,
+ 'valeur_point': dossier.valeur_point,
+ 'valeur_point_devise': dossier.valeur_point_devise,
+ 'regime_travail': dossier.regime_travail,
+ 'local_expatrie': {
+ 'local': 'L', 'expat': 'E'
+ }.get(dossier.statut_residence),
+ 'salaire_bstg': remuns_par_type[2],
+ 'salaire_bstg_eur': (
+ (remuns_par_type[2] * taux_change).quantize(TWOPLACES)
+ ),
+ 'salaire_theorique': salaire_theorique,
+ 'traitements': traitements,
+ 'total_traitements': sum(traitements),
+ 'indemnites': indemnites,
+ 'total_indemnites': sum(indemnites),
+ 'primes': primes,
+ 'total_primes': sum(primes),
+ 'charges': charges,
+ 'total_charges': sum(charges),
+ 'masse_salariale': masse_salariale,
+ 'masse_salariale_eur': masse_salariale_eur,
+ })
- annee = forms.CharField(
- initial=date.today().year,
- widget=forms.Select(
- choices=((a, a) for a in reversed(CHOICE_ANNEES))
+ # Récupérer les postes actifs pour déterminer le nombre de jours
+ # vacants.
+ postes = list(
+ rh.Poste.objects.actifs(annee=annee)
+ .select_related(
+ 'devise_max', 'valeur_point_max', 'classement_max'
)
- )
-
- region = forms.CharField(
- widget=forms.Select(choices=[('', '')] +
- [(i.id, i) for i in ref.Region.objects.all()]
- )
+ .extra(
+ tables=['rh_tauxchange'],
+ where=[
+ 'rh_tauxchange.annee = %s',
+ 'rh_tauxchange.devise = rh_devise.id'
+ ],
+ params=[annee],
+ select={
+ 'taux_change': 'rh_tauxchange.taux'
+ }
)
-
- implantation = forms.CharField(
- widget=forms.Select(choices=[('', '')] +
- [(i.id, i) for i in ref.Implantation.objects.all()]
- )
+ )
+ postes_par_id = dict((poste.id, poste) for poste in postes)
+ jours_vacants_date = dict(
+ (poste.id, max(today, poste.date_debut or today))
+ for poste in postes
+ )
+ jours_vacants = defaultdict(lambda: 0)
+ for dossier in rh.Dossier.objects.actifs(annee=annee) \
+ .order_by('date_debut'):
+ if dossier.poste_id not in jours_vacants_date:
+ continue
+ derniere_date = jours_vacants_date[dossier.poste_id]
+ if dossier.date_debut is not None:
+ jours_vacants[dossier.poste_id] += max((
+ dossier.date_debut - derniere_date
+ ).days - 1, 0)
+ jours_vacants_date[dossier.poste_id] = max(
+ min(dossier.date_fin or fin_annee, fin_annee),
+ derniere_date
)
- #date_debut = forms.DateField(widget=adminwidgets.AdminDateWidget)
- #date_fin = forms.DateField(widget=adminwidgets.AdminDateWidget)
-
- form = RechercheTemporelle(request.GET)
- get_filtre = [
- (k, v) for k, v in request.GET.items()
- if k not in ('date_debut', 'date_fin', 'implantation')
- ]
- query_string = urllib.urlencode(get_filtre)
-
- date_debut = None
- date_fin = None
- if request.GET.get('annee', None):
- date_debut = "01-01-%s" % request.GET.get('annee', None)
- date_fin = "31-12-%s" % request.GET.get('annee', None)
-
- implantation = request.GET.get('implantation')
- region = request.GET.get('region')
+ # Ajouter les lignes des postes vacants au rapport
+ for poste_id, jours in jours_vacants.iteritems():
+ if jours == 0:
+ continue
+ poste = postes_par_id[poste_id]
+ if poste.valeur_point_max and poste.classement_max \
+ and poste.classement_max.coefficient and poste.regime_travail:
+ salaire_theorique = (Decimal(str(
+ poste.valeur_point_max.valeur *
+ poste.classement_max.coefficient
+ )) * poste.regime_travail / 100).quantize(TWOPLACES)
+ else:
+ salaire_theorique = None
- custom_filter = {}
- if implantation:
- custom_filter['dossier__poste__implantation'] = implantation
- if region:
- custom_filter['dossier__poste__implantation__region'] = region
+ local_expatrie = '/'.join(
+ (['L'] if poste.local else []) +
+ (['E'] if poste.expatrie else [])
+ )
- c = {
- 'title': 'Rapport de masse salariale',
- 'form': form,
- 'headers': [],
- 'query_string': query_string,
- }
- if date_debut or date_fin:
- masse = MasseSalariale(date_debut, date_fin, custom_filter,
- request.GET.get('ne_pas_grouper', False))
- if masse.rapport:
- if request.GET.get('ods'):
- for h in (
- h for h in masse.headers if 'background-color' in h[2]
- ):
- del h[2]['background-color']
- masse.ods()
- output = StringIO.StringIO()
- masse.doc.save(output)
- output.seek(0)
-
- response = HttpResponse(
- FileWrapper(output),
- content_type=(
- 'application/vnd.oasis.opendocument.spreadsheet'
- )
- )
- response['Content-Disposition'] = \
- 'attachment; filename=Masse Salariale %s.ods' % \
- masse.annee
- return response
- else:
- c['rapport'] = masse.rapport
- c['header_keys'] = [h[0] for h in masse.headers]
- #on enleve le background pour le header
- for h in (
- h for h in masse.headers if 'background-color' in h[2]
- ):
- h[2]['background'] = 'none'
- h = SortHeaders(request, masse.headers, order_field_type="ot",
- not_sortable=c['header_keys'], order_field="o")
- c['headers'] = list(h.headers())
- for key, nom, opts in masse.headers:
- c['headers']
- c['total'] = masse.grand_totaux[0]
- c['total_euro'] = masse.grand_totaux[1]
- c['colspan'] = len(c['header_keys']) - 1
- get_filtre.append(('ods', True))
- query_string = urllib.urlencode(get_filtre)
- c['url_ods'] = "%s?%s" % (
- reverse('rhr_masse_salariale'), query_string)
-
- return render(request, 'rh/rapports/masse_salariale.html', c)
+ salaire = (
+ poste.salaire_max * jours / jours_annee *
+ poste.regime_travail / 100
+ ).quantize(TWOPLACES)
+ indemnites = (
+ poste.indemn_max * jours / jours_annee *
+ poste.regime_travail / 100
+ ).quantize(TWOPLACES)
+ charges = (
+ poste.autre_max * jours / jours_annee *
+ poste.regime_travail / 100
+ ).quantize(TWOPLACES)
+ masse_salariale = salaire + indemnites + charges
+ masse_salariale_eur = (
+ masse_salariale * Decimal(str(poste.taux_change))
+ ).quantize(TWOPLACES)
+ masse_salariale_totale += masse_salariale_eur
+
+ lignes.append({
+ 'poste': poste,
+ 'regime_travail': poste.regime_travail,
+ 'local_expatrie': local_expatrie,
+ 'jours': jours,
+ 'devise': poste.devise_max and poste.devise_max.code,
+ 'valeur_point': (
+ poste.valeur_point_max and poste.valeur_point_max.valeur
+ ),
+ 'valeur_point_devise': (
+ poste.valeur_point_max and \
+ poste.valeur_point_max.devise.code
+ ),
+ 'salaire_theorique': salaire_theorique,
+ 'salaire_de_base': salaire,
+ 'total_indemnites': indemnites,
+ 'total_charges': charges,
+ 'masse_salariale': masse_salariale,
+ 'masse_salariale_eur': masse_salariale_eur
+ })
+ if 'ods' in request.GET:
+ doc = ods.masse_salariale(
+ lignes=lignes,
+ annee=annee,
+ titres_traitements=titres_traitements,
+ titres_indemnites=titres_indemnites,
+ titres_primes=titres_primes,
+ titres_charges=titres_charges,
+ masse_salariale_totale=masse_salariale_totale
+ )
+ response = HttpResponse(
+ mimetype='vnd.oasis.opendocument.spreadsheet'
+ )
+ response['Content-Disposition'] = 'filename=masse-salariale.ods'
+ doc.write(response)
+ return response
+ else:
+ return render(request, 'rh/rapports/masse_salariale.html', {
+ 'form': form,
+ 'titres_traitements': titres_traitements,
+ 'titres_indemnites': titres_indemnites,
+ 'titres_primes': titres_primes,
+ 'titres_charges': titres_charges,
+ 'masse_salariale_totale': masse_salariale_totale,
+ 'lignes': lignes,
+ 'annee': annee
+ })
+ return render(request, 'rh/rapports/masse_salariale.html', {
+ 'form': form
+ })
@login_required
SHORT_DATE_FORMAT = 'd-m-Y'
LANGUAGE_CODE = 'fr-ca'
+USE_L10N = True
+USE_THOUSAND_SEPARATOR = True
# Absolute path to the directory that holds media.
# Example: "/home/media/media.lawrence.com/"
'admin_tools.theming',
'admin_tools.menu',
'admin_tools.dashboard',
+ 'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
+ 'django.contrib.humanize',
'django.contrib.messages',
'django.contrib.sessions',
- 'django.contrib.admin',
'django.contrib.staticfiles',
'django_qbe',
'ajax_select',
# Added by Buildout Versions at 2012-06-08 11:15:52.545562
auf.django.emploi = 1.2dev
+
+# Added by Buildout Versions at 2012-06-15 14:24:42.911205
+odsgen = 0.1