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