1 # -*- encoding: utf-8 -*-
8 from django
.db
.models
import Q
10 from datamaster_modeles
import models
as ref
12 import rh
.models
as rh
15 KEY_DATE_DEBUT
= "debut"
18 TYPE_REMUN_BSTG
= (3,)
20 TYPE_REMUN_BASE
= (1,)
21 TYPE_REMUN_FONC_RESP
= (7, 8)
22 TYPE_REMUN_EXPAT
= (4,)
23 TYPE_REMUN_LOGEMENT
= (6,)
24 TYPE_REMUN_TRANSP
= (9,)
25 TYPE_REMUN_13E
= (18,)
26 TYPE_PRIME_INTERIM
= (19,)
27 TYPE_REMUN_ALL_INDEMNITES
= list(itertools
.chain(*(TYPE_REMUN_BSTG
,
28 TYPE_REMUN_MAD
, TYPE_REMUN_BASE
, TYPE_REMUN_FONC_RESP
,
29 TYPE_REMUN_EXPAT
, TYPE_REMUN_LOGEMENT
, TYPE_REMUN_TRANSP
,
30 TYPE_REMUN_13E
, TYPE_PRIME_INTERIM
)))
31 TYPE_PRIME_INSTALLATION
= (13,)
32 TYPE_PRIME_DEMENAG
= (15,)
33 TYPE_PRIME_AVION
= (14,)
34 TYPE_PRIME_ALL
= list(itertools
.chain(
35 *(TYPE_PRIME_INSTALLATION
, TYPE_PRIME_DEMENAG
, TYPE_PRIME_AVION
)
37 TYPE_CHARGE_PATRONALE
= (17,)
38 TYPE_CHARGE_ALL
= list(itertools
.chain(*(TYPE_CHARGE_PATRONALE
,)))
39 TYPE_NATURE_INDEMN
= u
"Indemnité"
40 TYPE_NATURE_PAIEMENT
= u
"Accessoire"
41 TYPE_NATURE_CHARGES
= u
"Charges"
42 TYPE_NATURE_TRAITEMENT
= u
"Traitement"
43 HEADER_SEPARATOR
= ('sep', ods
.Separator
, {'columnwidth': '0.4cm'})
46 class MasseSalariale():
47 """ Rapport de la masse salariale. """
49 def __init__(self
, date_debut
, date_fin
, custom_filter
=None,
50 ne_pas_grouper
=False):
51 """ date_debut: date de début pour les données temporelles
53 custom_filter: dictionnaire des paramètres à passer au queryset.
55 if not date_debut
and not date_fin
:
58 date_debut
= datetime
.date(
59 *time
.strptime(date_debut
, "%d-%m-%Y")[0:3]
61 date_fin
= datetime
.date(*time
.strptime(date_fin
, "%d-%m-%Y")[0:3])
63 rapport_date_delta
= date_fin
- date_debut
64 rapport_date_delta
+= datetime
.timedelta(days
=1)
66 self
.annee
= date_fin
.year
68 self
.devise_base
= rh
.Devise
.objects
.filter(code
='EUR')[0]
71 q_range
= self
.build_qs("date_", date_debut
, date_fin
)
72 q_range_d
= self
.build_qs("dossier__date_", date_debut
, date_fin
)
73 remunerations
= rh
.Remuneration
.objects
.filter(q_range
) \
77 remunerations
= remunerations
.filter(**custom_filter
)
79 remunerations
= remunerations
.exclude(supprime
=True) \
81 "dossier", "dossier_employe", "dossier_poste", "type"
86 lineariser_dossiers
= not ne_pas_grouper
88 for r
in remunerations
:
89 if lineariser_dossiers
:
90 key
= r
.dossier
.employe_id
94 if key
not in contenu
:
99 if lineariser_dossiers
:
100 contenu
[key
]['remunerations'].append(r
)
102 if r
.dossier_id
== key
:
103 contenu
[key
]['remunerations'].append(r
)
104 contenu
[key
]['dossiers'].add(r
.dossier
)
109 for pays
in ref
.Pays
.objects
.all():
110 pays_list
[pays
.id] = pays
112 valeurs_point_par_imp
= \
114 (v
.implantation
.id, v
) for v
in \
115 rh
.ValeurPoint
.objects
.filter(annee
=self
.annee
).all()
119 ('bureau', u
"Bureau", {'columnwidth': '2cm'}),
120 ('pays', u
"Pays", {'columnwidth': '3.5cm'}),
121 ('implantation', u
"Implantation", {'columnwidth': '3cm'}),
122 ('valeur_point', u
"Valeur du point",
123 {'columnwidth': '2.3cm'}),
124 ('numero_employe', u
"Numéro d'employé",
125 {'columnwidth': '2.4cm'}),
126 ('nom', u
"Nom", {'columnwidth': '5.4cm'}),
127 ('prenom', u
"Prénom", {'columnwidth': '4.8cm'}),
128 ('type_de_poste', u
"Type de poste", {'columnwidth': '2.7cm'}),
129 ('intitule_de_poste', u
"Intitulé du poste",
130 {'columnwidth': '7.25cm'}),
131 ('niveau', u
"Niveau", {'columnwidth': '1.75cm'}),
132 ('point', u
"Point", {'columnwidth': '1.75cm'}),
133 ('regime_de_travail', u
"Régime de travail",
134 {'columnwidth': '2cm'}),
135 ('local_expatrie', u
"Local / Expatrié",
136 {'columnwidth': '2.25cm'}),
137 ('statut', u
"Statut", {'columnwidth': '1.25cm'}),
138 ('date_fin_contrat', u
"Date de fin de contrat",
139 {'columnwidth': '2cm'}),
141 ('date_debut', u
"Date de début", {'columnwidth': '1.92cm'}),
142 ('date_fin', u
"Date de fin", {'columnwidth': '1.92cm'}),
143 ('nb_jours', u
"Nombre de jours", {'columnwidth': '2.82cm'}),
145 ('devise', u
"Devise", {'columnwidth': '1.46cm'}),
146 ('salaire_bstg_annuel', u
"Salaire BSTG ANNUEL",
147 {'columnwidth': '2.5cm'}),
148 ('salaire_bstg_total', u
"Salaire BSTG total ",
149 {'columnwidth': '2.5cm'}),
150 ('organisme_bstg', u
"Organisme BSTG",
151 {'columnwidth': '2.9cm'}),
153 ('salaire_theorique', u
"Salaire théorique",
154 {'columnwidth': '2.5cm'}),
155 ('salaire_base_brut', u
"Salaire de base brut",
156 {'columnwidth': '2.5cm'}),
157 ('salaire_complementaire', u
"Salaire complémentaire",
158 {'columnwidth': '2.5cm'}),
160 ('indemnite_fonctions', u
"Indemnités de fonctions",
161 {'columnwidth': '2.5cm'}),
162 ('indemnite_expat', u
"Indemnités d'expatriation",
163 {'columnwidth': '2.5cm'}),
164 ('indemnite_logement', u
"Indemnités de logement",
165 {'columnwidth': '2.5cm'}),
166 ('indemnite_transp', u
"Indemnités de transport",
167 {'columnwidth': '2.5cm'}),
168 ('indemnite_13e', u
"Indemnités 13e mois",
169 {'columnwidth': '2.5cm'}),
170 ('prime_interim', u
"Prime d'intérim",
171 {'columnwidth': '2.5cm'}),
172 ('indemnite_autre', u
"Autre indemnités",
173 {'columnwidth': '2.5cm'}),
174 ('indemnite_sous_total', u
"Sous-total d'indemnités",
175 {'columnwidth': '2.5cm'}),
177 ('prime_installation', u
"Prime d'installation",
178 {'columnwidth': '2.5cm'}),
179 ('prime_demenagement', u
"Prime de déménagement",
180 {'columnwidth': '2.5cm'}),
181 ('prime_avion', u
"Prime d'avion",
182 {'columnwidth': '2.5cm'}),
183 ('prime_autre', u
"Autre prime",
184 {'columnwidth': '2.5cm'}),
185 ('prime_sous_total', u
"Total des primes",
186 {'columnwidth': '2.5cm'}),
188 ('charges_patronales', u
"Charges patronales",
189 {'columnwidth': '2.5cm'}),
190 ('charges_autre', u
"Autres charges patronales",
191 {'columnwidth': '2.5cm'}),
192 ('charges_sous_total', u
"Sous-total des charges patronales",
193 {'columnwidth': '2.5cm'}),
195 ('sous_total_traitement_annee', u
"Total traitement annuel"),
196 ('sous_total_indemnite_annee', u
"Total indemnités annuel"),
197 ('sous_total_accessoire_annee', u
"Total accessoires annuel"),
198 ('sous_total_charges_annee', u
"Total charges annuel"),
200 ('masse_salariale', u
"Masse salariale annuelle",
201 {'columnwidth': '2.5cm'}),
202 ('masse_salariale_annee', u
"Masse salariale %s" % self
.annee
,
203 {'columnwidth': '2.5cm'}),
204 ('masse_salariale_annee_euro', u
"Masse salariale %s EUR" % \
205 self
.annee
, {'columnwidth': '2.5cm'}),
209 grand_total_euro
= 0.0
211 for item
in contenu
.values():
212 dossiers
= item
['dossiers']
213 remuns
= item
['remunerations']
217 dossier
= list(dossiers
)[0]
219 regime
= float(dossier
.poste
.regime_travail
) / 100
221 if dossier
.statut_residence
== "expat":
226 #on détermine la date du début et fin du dossier si année en cours
228 d_date_fin
= dossier
.date_fin \
229 if dossier
.date_fin
.year
== date_fin
.year
else None
230 except AttributeError:
233 d_date_debut
= dossier
.date_debut \
234 if dossier
.date_debut
.year
== date_fin
.year
else None
235 except AttributeError:
239 pays_list
[dossier
.poste
.implantation
.adresse_physique_pays
.id]
241 #on détermine si les rémunérations sont tous dans la même devise
242 devise
= remuns
[0].devise
245 if devise
!= r
.devise
:
259 for r
in bstg_dossier
.rh_remunerations
.all():
260 if r
.type.id in TYPE_REMUN_BSTG
:
264 bstg_remun_euro
= rh
.Remuneration(
265 montant
=bstg_remun
.montant
, devise
=bstg_remun
.devise
267 self
.convertir(bstg_remun_euro
)
269 salaire_complement
= 0.0
292 total_remun_annee
= {
300 montant
= float(r
.montant
)
302 if r
.type_id
in TYPE_REMUN_MAD
:
303 salaire_complement
+= montant
305 if r
.type_id
in TYPE_REMUN_BASE
:
306 salaire_base
+= montant
308 if r
.type_id
in TYPE_REMUN_FONC_RESP
:
309 indemnites
['fonc_resp'] += montant
311 if r
.type_id
in TYPE_REMUN_EXPAT
:
312 indemnites
['expat'] += montant
314 if r
.type_id
in TYPE_REMUN_LOGEMENT
:
315 indemnites
['logement'] += montant
317 if r
.type_id
in TYPE_REMUN_TRANSP
:
318 indemnites
['transp'] += montant
320 if r
.type_id
in TYPE_REMUN_13E
:
321 indemnites
['13e'] += montant
323 if r
.type_id
in TYPE_PRIME_INTERIM
:
324 indemnites
['interim'] += montant
326 if r
.type_id
not in TYPE_REMUN_ALL_INDEMNITES \
327 and r
.type.nature_remuneration
== TYPE_NATURE_INDEMN
:
328 indemnites
['autre_recurr'] += montant
330 if r
.type_id
in TYPE_PRIME_INSTALLATION
:
331 primes
['installation'] += montant
333 if r
.type_id
in TYPE_PRIME_DEMENAG
:
334 primes
['demenagement'] += montant
336 if r
.type_id
in TYPE_PRIME_AVION
:
337 primes
['avion'] += montant
339 if r
.type_id
not in TYPE_PRIME_ALL
and \
340 r
.type.nature_remuneration
== TYPE_NATURE_PAIEMENT
:
341 primes
['autre'] += montant
343 if r
.type_id
in TYPE_CHARGE_PATRONALE
:
344 charges
['patronale'] += montant
346 if r
.type_id
not in TYPE_CHARGE_ALL
and \
347 r
.type.nature_remuneration
== TYPE_NATURE_CHARGES
:
348 charges
['autre'] += montant
350 if r
.type.nature_remuneration
== TYPE_NATURE_INDEMN
:
351 total_remun_annee
['indemnite'] += montant
353 if r
.type.nature_remuneration
== TYPE_NATURE_PAIEMENT
:
354 total_remun_annee
['accessoire'] += montant
356 if r
.type.nature_remuneration
== TYPE_NATURE_CHARGES
:
357 total_remun_annee
['charges'] += montant
359 if r
.type.nature_remuneration
== TYPE_NATURE_TRAITEMENT
:
360 total_remun_annee
['traitement'] += montant
362 total_indemnites
= sum(indemnites
.values())
364 #Calcul du nombre de jours pour ce dossier.
365 if dossier
.date_debut
and dossier
.date_debut
> date_debut
and \
366 not dossier
.date_fin
:
367 date_delta
= date_fin
- dossier
.date_fin
368 elif dossier
.date_fin
and dossier
.date_fin
< date_fin
and \
369 not dossier
.date_debut
:
370 date_delta
= dossier
.date_fin
- date_debut
371 elif dossier
.date_fin
and dossier
.date_debut
:
372 date_delta
= dossier
.date_fin
- date_debut
374 date_delta
= rapport_date_delta
376 masse_salariale
= (salaire_base
+ total_indemnites
+ \
377 sum(primes
.values()) + sum(charges
.values()))
378 masse_salariale_euro
= rh
.Remuneration(montant
=masse_salariale
,
379 devise
=remuns
[0].devise
)
380 self
.convertir(masse_salariale_euro
)
382 if dossier
.classement
and dossier
.classement
.coefficient
:
383 coefficient
= dossier
.classement
.coefficient
387 #todo valeur du point si pas présent
388 valeur_point
= valeurs_point_par_imp
.get(
389 dossier
.poste
.implantation_id
392 salaire_theorique
= (
393 round(valeur_point
.valeur
* int(coefficient
) * regime
, 2) \
394 if valeur_point
and coefficient
and regime
else None)
396 rapport_nombre_jours
= (float(date_delta
.days
)
397 / rapport_date_delta
.days
)
399 'bureau': dossier
.poste
.implantation
.region
.code
,
400 'pays': unicode(pays
),
401 'implantation': dossier
.poste
.implantation
.nom_court
,
402 'type_implantation': dossier
.poste
.implantation
.type,
404 'valeur_point': valeur_point
,
405 'numero_employe': dossier
.employe_id
,
406 'nom': dossier
.employe
.nom
.upper(),
407 'prenom': dossier
.employe
.prenom
,
408 'type_de_poste': dossier
.poste
.type_poste
.nom
,
409 'intitule_de_poste': dossier
.poste
.nom_feminin
410 if dossier
.employe
.genre
== "F" else
412 'niveau': unicode(dossier
.classement
),
413 'point': coefficient
,
414 'regime_de_travail': "%s %%" % int(regime
* 100),
415 'local_expatrie': statut
,
416 'statut': dossier
.statut
.code
,
417 'date_fin_contrat': dossier
.date_fin
or "",
418 'date_debut': d_date_debut
or "",
419 'date_fin': d_date_fin
or "",
420 'nb_jours': date_delta
.days
,
421 'devise': remuns
[0].devise
.code
,
422 'salaire_bstg_annuel': bstg_remun
.montant \
423 if bstg_remun
else "",
424 'salaire_bstg_total': bstg_remun_euro
.montant \
425 if bstg_remun
else "",
426 'organisme_bstg': dossier
.organisme_bstg
or "",
427 'salaire_theorique': salaire_theorique
,
428 'salaire_base_brut': \
429 salaire_base
* regime
* rapport_nombre_jours
,
430 'salaire_complementaire': \
431 salaire_complement
* regime
*
432 rapport_nombre_jours
,
433 #'salaire_total': None
434 'indemnite_fonctions': indemnites
['fonc_resp'] * \
435 regime
* rapport_nombre_jours
,
436 'indemnite_expat': indemnites
['expat'] * regime
* \
437 rapport_nombre_jours
,
438 'indemnite_logement': indemnites
['logement'] * \
439 regime
* rapport_nombre_jours
,
440 'indemnite_transp': indemnites
['transp'] * regime
* \
441 rapport_nombre_jours
,
442 'indemnite_13e': indemnites
['13e'] * regime
* \
443 rapport_nombre_jours
,
444 'prime_interim': indemnites
['interim'] * regime
* \
445 rapport_nombre_jours
,
446 'indemnite_autre': indemnites
['autre_recurr'] * \
447 regime
* rapport_nombre_jours
,
448 'indemnite_sous_total': total_indemnites
* regime
* \
449 rapport_nombre_jours
,
451 total_indemnites
+ salaire_base
+
453 ) * regime
* rapport_nombre_jours
,
454 'prime_installation': primes
['installation'] * regime
* \
455 rapport_nombre_jours
,
456 'prime_demenagement': primes
['demenagement'] * regime
* \
457 rapport_nombre_jours
,
458 'prime_avion': primes
['avion'] * regime
* \
459 rapport_nombre_jours
,
460 'prime_autre': primes
['autre'] * regime
* \
461 rapport_nombre_jours
,
462 'prime_sous_total': sum(primes
.values()) * regime
* \
463 rapport_nombre_jours
,
464 'charges_patronales': charges
['patronale'],
465 'charges_autre': charges
['autre'],
466 'charges_sous_total': sum(charges
.values()),
467 'sous_total_traitement_annee': \
468 total_remun_annee
['traitement'],
469 'sous_total_indemnite_annee': \
470 total_remun_annee
['indemnite'],
471 'sous_total_accessoire_annee': \
472 total_remun_annee
['accessoire'],
473 'sous_total_charges_annee': \
474 total_remun_annee
['charges'],
475 'masse_salariale': masse_salariale
,
476 'masse_salariale_annee': masse_salariale
* \
477 regime
* rapport_nombre_jours
,
478 'masse_salariale_annee_euro': \
479 masse_salariale_euro
.montant
* regime
*
480 rapport_nombre_jours
,
481 'sep': ods
.Separator(),
484 grand_total
+= round(masse_salariale
, 2)
485 grand_total_euro
+= round(masse_salariale_euro
.montant
* regime
487 date_delta
.days
/ rapport_date_delta
.days
490 self
.rapport
.append(item_rapport
)
492 self
.rapport
= sorted(self
.rapport
, key
=lambda r
: r
['nom'])
494 self
.grand_totaux
= (grand_total
, grand_total_euro
)
496 def build_qs(self
, prefix
, date_debut
, date_fin
):
498 Q(**{"%s%s__isnull" % (prefix
, KEY_DATE_DEBUT
): True})
500 Q(**{"%s%s__isnull" % (prefix
, KEY_DATE_FIN
): True})
501 date_debut_superieure_ou_egale_a_borne_gauche
= \
502 Q(**{"%s%s__gte" % (prefix
, KEY_DATE_DEBUT
): date_debut
})
503 date_debut_inferieure_ou_egale_a_borne_gauche
= \
504 Q(**{"%s%s__lte" % (prefix
, KEY_DATE_DEBUT
): date_debut
})
505 date_fin_superieure_ou_egale_a_borne_gauche
= \
506 Q(**{"%s%s__gte" % (prefix
, KEY_DATE_FIN
): date_debut
})
507 date_fin_inferieure_ou_egale_a_borne_droite
= \
508 Q(**{"%s%s__lte" % (prefix
, KEY_DATE_FIN
): date_fin
})
509 date_debut_inferieure_ou_egale_a_borne_droite
= \
510 Q(**{"%s%s__lte" % (prefix
, KEY_DATE_DEBUT
): date_fin
})
511 date_fin_superieure_ou_egale_a_borne_droite
= \
512 Q(**{"%s%s__gte" % (prefix
, KEY_DATE_FIN
): date_fin
})
516 date_debut_null
& date_fin_null
518 date_debut_inferieure_ou_egale_a_borne_gauche
&
519 date_fin_superieure_ou_egale_a_borne_gauche
&
520 date_fin_inferieure_ou_egale_a_borne_droite
522 date_debut_superieure_ou_egale_a_borne_gauche
&
523 date_debut_inferieure_ou_egale_a_borne_droite
&
524 date_fin_superieure_ou_egale_a_borne_droite
526 date_debut_inferieure_ou_egale_a_borne_gauche
&
527 date_fin_superieure_ou_egale_a_borne_droite
530 date_fin_superieure_ou_egale_a_borne_droite
532 date_debut_inferieure_ou_egale_a_borne_gauche
&
538 def convertir(self
, remuneration
):
539 if remuneration
.devise
!= self
.devise_base
:
541 remuneration
.montant
= float(remuneration
.montant
) * \
542 self
.trouver_taux(remuneration
.devise
).taux
543 remuneration
.devise
= self
.devise_base
544 except AttributeError:
547 def trouver_taux(self
, devise
):
548 if devise
.code
not in self
.taux_change
:
550 t
= rh
.TauxChange
.objects
.filter(
551 devise
=devise
, annee
=self
.annee
555 self
.taux_change
[devise
.code
] = t
556 return self
.taux_change
[devise
.code
]
559 self
.csv_handle
= StringIO
.StringIO()
560 csv_writer
= csv
.writer(self
.csv_handle
, delimiter
=",",
561 doublequote
=False, escapechar
="\\", quoting
=csv
.QUOTE_ALL
,
563 header
= [v
[0] for v
in self
.rapport
[0]]
564 csv_writer
.writerow(header
)
565 for row
in self
.rapport
:
566 values
= [v
[1] for v
in row
]
568 [unicode(r
).encode('utf-8') for r
in values
]
572 self
.doc
= ods
.OpenDocumentSpreadsheet()
573 table
= self
.doc
.add_table(name
=u
'Masse salariale %s' % self
.annee
)
575 for h
in self
.headers
:
577 table
.add_column(**h
[2])
579 table
.add_row([h
[1] for h
in self
.headers
], rowheight
='2cm')
581 for r
in self
.rapport
:
582 table
.add_row([r
[h
[0]] for h
in self
.headers
])
584 #a.doc.write('hello_world.ods')