Merge remote-tracking branch 'origin/dev' into masse_salariale_jp
[auf_rh_dae.git] / project / rh / masse_salariale.py
CommitLineData
98d6eb6c
JPC
1# -*- encoding: utf-8 -*-
2import time
3import datetime
899aea3b
JPC
4import csv
5import StringIO
07f120f3 6import itertools
98d6eb6c
JPC
7
8from django.db.models import Q
9
10from datamaster_modeles import models as ref
c99116c3 11import rh.ods as ods
98d6eb6c
JPC
12import rh.models as rh
13
14
15KEY_DATE_DEBUT = "debut"
16KEY_DATE_FIN = "fin"
4d17560e
JPC
17
18TYPE_REMUN_BSTG = (3,)
19TYPE_REMUN_MAD = (2,)
20TYPE_REMUN_BASE = (1,)
95ad7aab 21TYPE_REMUN_FONC_RESP = (7, 8)
4d17560e
JPC
22TYPE_REMUN_EXPAT = (4,)
23TYPE_REMUN_LOGEMENT = (6,)
24TYPE_REMUN_TRANSP = (9,)
25TYPE_REMUN_13E = (18,)
95ad7aab 26TYPE_PRIME_INTERIM = (19,)
07f120f3
JPC
27TYPE_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)))
95ad7aab
JPC
31TYPE_PRIME_INSTALLATION = (13,)
32TYPE_PRIME_DEMENAG = (15,)
33TYPE_PRIME_AVION = (14,)
07f120f3
JPC
34TYPE_PRIME_ALL = list(itertools.chain(
35 *(TYPE_PRIME_INSTALLATION, TYPE_PRIME_DEMENAG, TYPE_PRIME_AVION)
36 ))
37TYPE_CHARGE_PATRONALE = (17,)
38TYPE_CHARGE_ALL = list(itertools.chain(*(TYPE_CHARGE_PATRONALE,)))
39TYPE_NATURE_INDEMN = u"Indemnité"
899aea3b
JPC
40TYPE_NATURE_PAIEMENT = u"Accessoire"
41TYPE_NATURE_CHARGES = u"Charges"
42
9163be6f 43
98d6eb6c
JPC
44class MasseSalariale():
45 """ Rapport de la masse salariale. """
46
c99116c3
JPC
47 def __init__(self, date_debut, date_fin, custom_filter=None,
48 ne_pas_grouper=False):
98d6eb6c
JPC
49 """ date_debut: date de début pour les données temporelles
50 date_fin: idem
df37184c 51 custom_filter: dictionnaire des paramètres à passer au queryset.
98d6eb6c 52 """
df37184c
JPC
53 if not date_debut and not date_fin:
54 return
55
98d6eb6c
JPC
56 date_debut = datetime.date(
57 *time.strptime(date_debut, "%d-%m-%Y")[0:3]
58 )
59 date_fin = datetime.date(*time.strptime(date_fin, "%d-%m-%Y")[0:3])
9163be6f 60 rapport_date_delta = date_fin - date_debut
98d6eb6c
JPC
61
62 self.annee = date_fin.year
63
64 self.devise_base = rh.Devise.objects.filter(code='EUR')[0]
65 self.taux_change = {}
66
67 q_range = self.build_qs("date_", date_debut, date_fin)
68 q_range_d = self.build_qs("dossier__date_", date_debut, date_fin)
69 remunerations = rh.Remuneration.objects.filter(q_range) \
70 .filter(q_range_d) \
023bdc65
JPC
71
72 if custom_filter:
73 remunerations = remunerations.filter(**custom_filter)
74
75 remunerations = remunerations.exclude(supprime=True) \
899aea3b
JPC
76 .select_related(
77 "dossier", "dossier_employe", "dossier_poste", "type"
78 )
98d6eb6c 79
c99116c3
JPC
80 contenu = {}
81
82 lineariser_dossiers = not ne_pas_grouper
83
98d6eb6c 84 for r in remunerations:
c99116c3
JPC
85 if lineariser_dossiers:
86 key = r.dossier.employe_id
87 else:
88 key = r.dossier_id
89
90 if key not in contenu:
91 contenu[key] = {
9163be6f
JPC
92 'dossiers': set(),
93 'remunerations': []
94 }
c99116c3
JPC
95 if lineariser_dossiers:
96 contenu[key]['remunerations'].append(r)
97 else:
98 if r.dossier_id == key:
99 contenu[key]['remunerations'].append(r)
100 contenu[key]['dossiers'].add(r.dossier)
9163be6f 101
98d6eb6c
JPC
102 self.rapport = []
103
57e2b793
JPC
104 pays_list = {}
105 for pays in ref.Pays.objects.all():
106 pays_list[pays.id] = pays
107
98d6eb6c
JPC
108 valeurs_point_par_imp = \
109 dict(
9163be6f
JPC
110 (v.implantation.id, v) for v in \
111 rh.ValeurPoint.objects.filter(annee=self.annee).all()
98d6eb6c 112 )
57e2b793
JPC
113
114 self.headers = (
115 ('bureau', u"Bureau"),
116 ('pays', u"Pays"),
117 ('implantation', u"Implantation"),
118 ('valeur_point', u"Valeur du point"),
119 ('numero_employe', u"Numéro d'employé"),
120 ('nom', u"Nom"),
121 ('prenom', u"Prénom"),
122 ('type_de_poste', u"Type de poste"),
123 ('intitule_de_poste', u"Intitulé du poste"),
124 ('niveau', u"Niveau"),
125 ('point', u"Point"),
126 ('regime_de_travail', u"Régime de travail"),
127 ('local_expatrie', u"Local / Expatrié"),
128 ('statut', u"Statut"),
129 ('date_fin_contrat', u"Date de fin de contrat"),
130 ('date_debut', u"Date de début"),
131 ('date_fin', u"Date de fin"),
132 ('nb_jours', u"Nombre de jours"),
133 ('devise', u"Devise"),
134 ('salaire_bstg_annuel', u"Salaire annuel BSTG"),
135 ('salaire_bstg_total', u"Salaire total BSTG"),
136 ('organisme_bstg', u"Organisme BSTG"),
137 ('salaire_theorique', u"Salaire théorique"),
138 ('salaire_base_brut', u"Salaire de base brut"),
139 ('salaire_complementaire', u"Salaire complémentaire"),
140 ('indemnite_fonctions', u"Indemnités de fonctions"),
141 ('indemnite_expat', u"Indemnités d'expatriation"),
142 ('indemnite_logement', u"Indemnités de logement"),
143 ('indemnite_transp', u"Indemnités de transport"),
144 ('indemnite_13e', u"Indemnités 13e mois"),
57e2b793 145 ('prime_interim', u"Prime d'intérim"),
07f120f3
JPC
146 ('indemnite_autre', u"Autre indemnités"),
147 ('indemnite_sous_total', u"Sous-total d'indemnités"),
57e2b793
JPC
148 ('prime_installation', u"Prime d'installation"),
149 ('prime_demenagement', u"Prime de déménagement"),
150 ('prime_avion', u"Prime d'avion"),
151 ('prime_autre', u"Autre prime"),
07f120f3 152 ('prime_sous_total', u"Total des primes"),
57e2b793 153 ('charges_patronales', u"Charges patronales"),
07f120f3
JPC
154 ('charges_autre', u"Autres charges patronales"),
155 ('charges_sous_total', u"Sous-total des charges patronales"),
57e2b793
JPC
156 ('masse_salariale', u"Masse salariale"),
157 ('masse_salariale_annee', u"Masse salariale %s" % self.annee),
158 ('masse_salariale_annee_euro', u"Masse salariale euro %s" % \
159 self.annee),
160 )
161
778fb9d6
JPC
162 grand_total = 0.0
163 grand_total_euro = 0.0
164
c99116c3 165 for item in contenu.values():
98d6eb6c
JPC
166 dossiers = item['dossiers']
167 remuns = item['remunerations']
07f120f3 168
c99116c3
JPC
169 if len(dossiers) > 1:
170 print dossiers[0].employe_id
171
98d6eb6c 172 dossier = list(dossiers)[0]
07f120f3
JPC
173 for d in dossiers:
174 if d.principal:
175 dossier = list(dossiers)[0]
176
95ad7aab 177 regime = float(dossier.poste.regime_travail) / 100
9163be6f 178
98d6eb6c
JPC
179 if dossier.poste.expatrie:
180 statut = "E"
181 else:
182 statut = "L"
183
9163be6f
JPC
184 #on détermine la date du début et fin du dossier si année en cours
185 try:
186 d_date_fin = dossier.date_fin \
187 if dossier.date_fin.year == date_fin.year else None
188 except AttributeError:
189 d_date_fin = None
190 try:
191 d_date_debut = dossier.date_debut \
192 if dossier.date_debut.year == date_fin.year else None
193 except AttributeError:
194 d_date_debut = None
98d6eb6c
JPC
195
196 pays = \
197 pays_list[dossier.poste.implantation.adresse_physique_pays.id]
198
199 #on détermine si les rémunérations sont tous dans la même devise
200 devise = remuns[0].devise
201 meme_devise = True
202 for r in remuns[1:]:
203 if devise != r.devise:
204 meme_devise = False
205
206 if not meme_devise:
207 for r in remuns:
208 self.convertir(r)
209
210 bstg_dossier = None
211 for d in dossiers:
212 if d.organisme_bstg:
213 bstg_dossier = d
214
9163be6f 215 bstg_remun = None
98d6eb6c 216 if bstg_dossier:
9163be6f 217 for r in bstg_dossier.rh_remunerations.all():
958e67e8 218 if r.type.id in TYPE_REMUN_BSTG:
9163be6f 219 bstg_remun = r
98d6eb6c 220
958e67e8 221 if bstg_remun:
df37184c
JPC
222 bstg_remun_euro = rh.Remuneration(
223 montant=bstg_remun.montant, devise=bstg_remun.devise
224 )
958e67e8 225 self.convertir(bstg_remun_euro)
958e67e8 226
4d17560e
JPC
227 salaire_complement = 0.0
228 salaire_base = 0.0
229 indemnites = {
230 'fonc_resp': 0.0,
231 'expat': 0.0,
232 'logement': 0.0,
233 'transp': 0.0,
234 '13e': 0.0,
235 'autre_recurr': 0.0,
07f120f3 236 'interim': 0.0,
4d17560e 237 }
95ad7aab
JPC
238
239 primes = {
95ad7aab
JPC
240 'installation': 0.0,
241 'demenagement': 0.0,
242 'avion': 0.0,
243 'autre': 0.0,
244 }
07f120f3
JPC
245 charges = {
246 'patronale': 0.0,
247 'autre': 0.0,
248 }
4d17560e
JPC
249 for r in remuns:
250 montant = float(r.montant)
95ad7aab
JPC
251
252 if r.type_id in TYPE_REMUN_MAD:
4d17560e
JPC
253 salaire_complement += montant
254
95ad7aab 255 if r.type_id in TYPE_REMUN_BASE:
4d17560e
JPC
256 salaire_base += montant
257
95ad7aab 258 if r.type_id in TYPE_REMUN_FONC_RESP:
4d17560e
JPC
259 indemnites['fonc_resp'] += montant
260
95ad7aab 261 if r.type_id in TYPE_REMUN_EXPAT:
4d17560e
JPC
262 indemnites['expat'] += montant
263
95ad7aab 264 if r.type_id in TYPE_REMUN_LOGEMENT:
4d17560e
JPC
265 indemnites['logement'] += montant
266
95ad7aab 267 if r.type_id in TYPE_REMUN_TRANSP:
4d17560e
JPC
268 indemnites['transp'] += montant
269
95ad7aab 270 if r.type_id in TYPE_REMUN_13E:
4d17560e
JPC
271 indemnites['13e'] += montant
272
95ad7aab 273 if r.type_id in TYPE_PRIME_INTERIM:
07f120f3
JPC
274 indemnites['interim'] += montant
275
276 if r.type_id not in TYPE_REMUN_ALL_INDEMNITES \
277 and r.type.nature_remuneration == TYPE_NATURE_INDEMN:
278 indemnites['autre_recurr'] += montant
95ad7aab
JPC
279
280 if r.type_id in TYPE_PRIME_INSTALLATION:
281 primes['installation'] += montant
282
283 if r.type_id in TYPE_PRIME_DEMENAG:
284 primes['demenagement'] += montant
285
286 if r.type_id in TYPE_PRIME_AVION:
287 primes['avion'] += montant
288
07f120f3 289 if r.type_id not in TYPE_PRIME_ALL and \
95ad7aab
JPC
290 r.type.nature_remuneration == TYPE_NATURE_PAIEMENT:
291 primes['autre'] += montant
292
07f120f3
JPC
293 if r.type_id in TYPE_CHARGE_PATRONALE:
294 charges['patronale'] += montant
295
296 if r.type_id not in TYPE_CHARGE_ALL and \
297 r.type.nature_remuneration == TYPE_NATURE_CHARGES:
298 charges['autre'] += montant
899aea3b 299
4d17560e
JPC
300 total_indemnites = sum(indemnites.values())
301
9163be6f 302 #Calcul du nombre de jours pour ce dossier.
4d17560e
JPC
303 if dossier.date_debut and dossier.date_debut > date_debut and \
304 not dossier.date_fin:
305 date_delta = date_fin - dossier.date_fin
306 elif dossier.date_fin and dossier.date_fin < date_fin and \
307 not dossier.date_debut:
308 date_delta = dossier.date_fin - date_debut
309 elif dossier.date_fin and dossier.date_debut:
310 date_delta = dossier.date_fin - date_debut
311 else:
9163be6f 312 date_delta = rapport_date_delta
98d6eb6c 313
899aea3b 314 masse_salariale = (salaire_base + total_indemnites + \
07f120f3 315 sum(primes.values()) + sum(charges.values()))
df37184c 316 masse_salariale_euro = rh.Remuneration(montant=masse_salariale,
023bdc65
JPC
317 devise=remuns[0].devise)
318 self.convertir(masse_salariale_euro)
319
22b53270
JPC
320 if dossier.classement and dossier.classement.coefficient:
321 coefficient = dossier.classement.coefficient
322 else:
323 coefficient = ""
324
325 #todo valeur du point si pas présent
326 valeur_point = valeurs_point_par_imp.get(
327 dossier.poste.implantation_id
328 ) or ""
329
330 salaire_theorique = "%s" % (
331 round(valeur_point.valeur * int(coefficient) * regime, 2) \
332 if valeur_point and coefficient and regime else "")
333
3d2b92bc
JPC
334 rapport_nombre_jours = (float(date_delta.days)
335 / rapport_date_delta.days)
57e2b793
JPC
336 item_rapport = {
337 'bureau': dossier.poste.implantation.region.code,
338 'pays': unicode(pays),
339 'implantation': dossier.poste.implantation.nom_court,
340 'type_implantation': dossier.poste.implantation.type,
341 #'imputation': None,
342 'valeur_point': valeur_point,
343 'numero_employe': dossier.employe_id,
344 'nom': dossier.employe.nom.upper(),
345 'prenom': dossier.employe.prenom,
346 'type_de_poste': dossier.poste.type_poste.nom,
347 'intitule_de_poste': dossier.poste.nom,
348 'niveau': unicode(dossier.classement),
349 'point': coefficient,
350 'regime_de_travail': "%s %%" % int(regime * 100),
351 'local_expatrie': statut,
352 'statut': dossier.statut.code,
353 'date_fin_contrat': dossier.date_fin or "",
354 'date_debut': d_date_debut or "",
355 'date_fin': d_date_fin or "",
356 'nb_jours': date_delta.days,
357 'devise': remuns[0].devise,
358 'salaire_bstg_annuel': bstg_remun.montant \
359 if bstg_remun else "",
360 'salaire_bstg_total': bstg_remun_euro.montant \
361 if bstg_remun else "",
362 'organisme_bstg': dossier.organisme_bstg or "",
363 'salaire_theorique': salaire_theorique,
364 'salaire_base_brut': \
3d2b92bc 365 salaire_base * regime * rapport_nombre_jours,
57e2b793 366 'salaire_complementaire': \
3d2b92bc
JPC
367 salaire_complement * regime *
368 rapport_nombre_jours,
57e2b793
JPC
369 #'salaire_total': None
370 'indemnite_fonctions': indemnites['fonc_resp'] * \
3d2b92bc 371 regime * rapport_nombre_jours,
57e2b793 372 'indemnite_expat': indemnites['expat'] * regime * \
3d2b92bc 373 rapport_nombre_jours,
57e2b793 374 'indemnite_logement': indemnites['logement'] * \
3d2b92bc 375 regime * rapport_nombre_jours,
57e2b793 376 'indemnite_transp': indemnites['transp'] * regime * \
3d2b92bc 377 rapport_nombre_jours,
57e2b793 378 'indemnite_13e': indemnites['13e'] * regime * \
3d2b92bc 379 rapport_nombre_jours,
07f120f3 380 'prime_interim': indemnites['interim'] * regime * \
3d2b92bc 381 rapport_nombre_jours,
57e2b793 382 'indemnite_autre': indemnites['autre_recurr'] * \
3d2b92bc 383 regime * rapport_nombre_jours,
07f120f3 384 'indemnite_sous_total': total_indemnites * regime * \
3d2b92bc 385 rapport_nombre_jours,
57e2b793 386 'total_brut': (
95ad7aab
JPC
387 total_indemnites + salaire_base +
388 salaire_complement
3d2b92bc 389 ) * regime * rapport_nombre_jours,
57e2b793 390 'prime_installation': primes['installation'] * regime * \
3d2b92bc 391 rapport_nombre_jours,
57e2b793 392 'prime_demenagement': primes['demenagement'] * regime * \
3d2b92bc 393 rapport_nombre_jours,
57e2b793 394 'prime_avion': primes['avion'] * regime * \
3d2b92bc 395 rapport_nombre_jours,
57e2b793 396 'prime_autre': primes['autre'] * regime * \
3d2b92bc 397 rapport_nombre_jours,
07f120f3 398 'prime_sous_total': sum(primes.values()) * regime * \
3d2b92bc 399 rapport_nombre_jours,
07f120f3
JPC
400 'charges_patronales': charges['patronale'],
401 'charges_autre': charges['autre'],
402 'charges_sous_total': sum(charges.values()),
57e2b793
JPC
403 'masse_salariale': masse_salariale,
404 'masse_salariale_annee': masse_salariale * \
3d2b92bc 405 regime * rapport_nombre_jours,
57e2b793 406 'masse_salariale_annee_euro': \
3d2b92bc
JPC
407 masse_salariale_euro.montant * regime *
408 rapport_nombre_jours,
57e2b793 409 }
98d6eb6c 410
778fb9d6 411 for key in item_rapport.keys():
3d2b92bc 412 if type(item_rapport[key]) == type(float()):
778fb9d6 413 item_rapport[key] = round(item_rapport[key], 2)
3d2b92bc
JPC
414
415 grand_total += round(masse_salariale, 2)
c99116c3 416 grand_total_euro += round(masse_salariale_euro.montant * regime
3d2b92bc 417 * (
778fb9d6 418 date_delta.days / rapport_date_delta.days
3d2b92bc 419 ), 2)
778fb9d6 420
98d6eb6c
JPC
421 self.rapport.append(item_rapport)
422
778fb9d6
JPC
423 self.grand_totaux = (grand_total, grand_total_euro)
424
98d6eb6c
JPC
425 def build_qs(self, prefix, date_debut, date_fin):
426 date_debut_null = \
427 Q(**{"%s%s__isnull" % (prefix, KEY_DATE_DEBUT): True})
428 date_fin_null = \
429 Q(**{"%s%s__isnull" % (prefix, KEY_DATE_FIN): True})
430 date_debut_superieure_ou_egale_a_borne_gauche = \
431 Q(**{"%s%s__gte" % (prefix, KEY_DATE_DEBUT): date_debut})
432 date_debut_inferieure_ou_egale_a_borne_gauche = \
433 Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT): date_debut})
434 date_fin_superieure_ou_egale_a_borne_gauche = \
435 Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN): date_debut})
436 date_fin_inferieure_ou_egale_a_borne_droite = \
437 Q(**{"%s%s__lte" % (prefix, KEY_DATE_FIN): date_fin})
438 date_debut_inferieure_ou_egale_a_borne_droite = \
439 Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT): date_fin})
440 date_fin_superieure_ou_egale_a_borne_droite = \
441 Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN): date_fin})
442
443 q_range = \
444 (
445 date_debut_null & date_fin_null
446 ) | (
447 date_debut_inferieure_ou_egale_a_borne_gauche &
448 date_fin_superieure_ou_egale_a_borne_gauche &
449 date_fin_inferieure_ou_egale_a_borne_droite
450 ) | (
451 date_debut_superieure_ou_egale_a_borne_gauche &
452 date_debut_inferieure_ou_egale_a_borne_droite &
453 date_fin_superieure_ou_egale_a_borne_droite
454 ) | (
455 date_debut_inferieure_ou_egale_a_borne_gauche &
456 date_fin_superieure_ou_egale_a_borne_droite
457 ) | (
458 date_debut_null &
459 date_fin_superieure_ou_egale_a_borne_droite
460 ) | (
461 date_debut_inferieure_ou_egale_a_borne_gauche &
462 date_fin_null
463 )
464
465 return q_range
466
467 def convertir(self, remuneration):
9163be6f
JPC
468 if remuneration.devise != self.devise_base:
469 remuneration.montant = float(remuneration.montant) * \
470 self.trouver_taux(remuneration.devise).taux
471 remuneration.devise = self.devise_base
98d6eb6c
JPC
472
473 def trouver_taux(self, devise):
474 if devise.code not in self.taux_change:
475 t = rh.TauxChange.objects.filter(
476 devise=devise, annee=self.annee
477 )[0]
478 self.taux_change[devise.code] = t
479 return self.taux_change[devise.code]
899aea3b
JPC
480
481 def csv(self):
482 self.csv_handle = StringIO.StringIO()
023bdc65
JPC
483 csv_writer = csv.writer(self.csv_handle, delimiter=",",
484 doublequote=False, escapechar="\\", quoting=csv.QUOTE_ALL,
485 )
486 header = [v[0] for v in self.rapport[0]]
487 csv_writer.writerow(header)
488 for row in self.rapport:
489 values = [v[1] for v in row]
490 csv_writer.writerow(
491 [unicode(r).encode('utf-8') for r in values]
492 )
c99116c3
JPC
493
494 def ods(self):
495 self.ods = ods.OpenDocumentSpreadsheet()
496 self.ods