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