Writing to CSV file, and keeping order
[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
40 """
41 date_debut = datetime.date(
42 *time.strptime(date_debut, "%d-%m-%Y")[0:3]
43 )
44 date_fin = datetime.date(*time.strptime(date_fin, "%d-%m-%Y")[0:3])
9163be6f 45 rapport_date_delta = date_fin - date_debut
98d6eb6c
JPC
46
47 self.annee = date_fin.year
48
49 self.devise_base = rh.Devise.objects.filter(code='EUR')[0]
50 self.taux_change = {}
51
52 q_range = self.build_qs("date_", date_debut, date_fin)
53 q_range_d = self.build_qs("dossier__date_", date_debut, date_fin)
54 remunerations = rh.Remuneration.objects.filter(q_range) \
55 .filter(q_range_d) \
023bdc65
JPC
56
57 if custom_filter:
58 remunerations = remunerations.filter(**custom_filter)
59
60 remunerations = remunerations.exclude(supprime=True) \
899aea3b
JPC
61 .select_related(
62 "dossier", "dossier_employe", "dossier_poste", "type"
63 )
98d6eb6c
JPC
64
65 employes = {}
66 for r in remunerations:
67 if r.dossier.employe_id not in employes:
9163be6f
JPC
68 employes[r.dossier.employe_id] = {
69 'dossiers': set(),
70 'remunerations': []
71 }
98d6eb6c
JPC
72 employes[r.dossier.employe_id]['remunerations'].append(r)
73 employes[r.dossier.employe_id]['dossiers'].add(r.dossier)
74
75 self.employes = employes
9163be6f 76
98d6eb6c
JPC
77 self.rapport = []
78
79 pays_list = ref.Pays.objects.all()
80 valeurs_point_par_imp = \
81 dict(
9163be6f
JPC
82 (v.implantation.id, v) for v in \
83 rh.ValeurPoint.objects.filter(annee=self.annee).all()
98d6eb6c
JPC
84 )
85 for item in self.employes.values():
86 dossiers = item['dossiers']
87 remuns = item['remunerations']
88 #TODO, choisir le dossier primaire
89 if len(dossiers) > 1:
90 #TODO
91 pass
92 dossier = list(dossiers)[0]
95ad7aab 93 regime = float(dossier.poste.regime_travail) / 100
9163be6f 94
98d6eb6c
JPC
95 if dossier.poste.expatrie:
96 statut = "E"
97 else:
98 statut = "L"
99
9163be6f
JPC
100 #on détermine la date du début et fin du dossier si année en cours
101 try:
102 d_date_fin = dossier.date_fin \
103 if dossier.date_fin.year == date_fin.year else None
104 except AttributeError:
105 d_date_fin = None
106 try:
107 d_date_debut = dossier.date_debut \
108 if dossier.date_debut.year == date_fin.year else None
109 except AttributeError:
110 d_date_debut = None
98d6eb6c
JPC
111
112 pays = \
113 pays_list[dossier.poste.implantation.adresse_physique_pays.id]
114
115 #on détermine si les rémunérations sont tous dans la même devise
116 devise = remuns[0].devise
117 meme_devise = True
118 for r in remuns[1:]:
119 if devise != r.devise:
120 meme_devise = False
121
122 if not meme_devise:
123 for r in remuns:
124 self.convertir(r)
125
126 bstg_dossier = None
127 for d in dossiers:
128 if d.organisme_bstg:
129 bstg_dossier = d
130
9163be6f 131 bstg_remun = None
98d6eb6c 132 if bstg_dossier:
9163be6f 133 for r in bstg_dossier.rh_remunerations.all():
4d17560e 134 if r.type in TYPE_REMUN_BSTG:
9163be6f 135 bstg_remun = r
98d6eb6c 136
4d17560e
JPC
137 salaire_complement = 0.0
138 salaire_base = 0.0
139 indemnites = {
140 'fonc_resp': 0.0,
141 'expat': 0.0,
142 'logement': 0.0,
143 'transp': 0.0,
144 '13e': 0.0,
145 'autre_recurr': 0.0,
146 }
95ad7aab
JPC
147
148 primes = {
149 'interim': 0.0,
150 'installation': 0.0,
151 'demenagement': 0.0,
152 'avion': 0.0,
153 'autre': 0.0,
154 }
899aea3b 155 charges = 0.0
4d17560e
JPC
156 for r in remuns:
157 montant = float(r.montant)
95ad7aab
JPC
158
159 if r.type_id in TYPE_REMUN_MAD:
4d17560e
JPC
160 salaire_complement += montant
161
95ad7aab 162 if r.type_id in TYPE_REMUN_BASE:
4d17560e
JPC
163 salaire_base += montant
164
95ad7aab 165 if r.type_id in TYPE_REMUN_FONC_RESP:
4d17560e
JPC
166 indemnites['fonc_resp'] += montant
167
95ad7aab 168 if r.type_id in TYPE_REMUN_EXPAT:
4d17560e
JPC
169 indemnites['expat'] += montant
170
95ad7aab 171 if r.type_id in TYPE_REMUN_LOGEMENT:
4d17560e
JPC
172 indemnites['logement'] += montant
173
95ad7aab 174 if r.type_id in TYPE_REMUN_TRANSP:
4d17560e
JPC
175 indemnites['transp'] += montant
176
95ad7aab 177 if r.type_id in TYPE_REMUN_13E:
4d17560e
JPC
178 indemnites['13e'] += montant
179
95ad7aab 180 if r.type_id not in TYPE_REMUN_AUTRE_RECURR_NOT \
4d17560e
JPC
181 and r.type.type_paiement != TYPE_PAIEMENT_PONCTUEL:
182 indemnites['autre_recurr'] += montant
183
95ad7aab
JPC
184 if r.type_id in TYPE_PRIME_INTERIM:
185 primes['interim'] += montant
186
187 if r.type_id in TYPE_PRIME_INSTALLATION:
188 primes['installation'] += montant
189
190 if r.type_id in TYPE_PRIME_DEMENAG:
191 primes['demenagement'] += montant
192
193 if r.type_id in TYPE_PRIME_AVION:
194 primes['avion'] += montant
195
196 if r.type_id not in TYPE_REMUN_AUTRE_RECURR_NOT and \
197 r.type.nature_remuneration == TYPE_NATURE_PAIEMENT:
198 primes['autre'] += montant
199
899aea3b
JPC
200 if r.type.nature_remuneration == TYPE_NATURE_CHARGES:
201 charges += montant
202
4d17560e
JPC
203 total_indemnites = sum(indemnites.values())
204
9163be6f 205 #Calcul du nombre de jours pour ce dossier.
4d17560e
JPC
206 if dossier.date_debut and dossier.date_debut > date_debut and \
207 not dossier.date_fin:
208 date_delta = date_fin - dossier.date_fin
209 elif dossier.date_fin and dossier.date_fin < date_fin and \
210 not dossier.date_debut:
211 date_delta = dossier.date_fin - date_debut
212 elif dossier.date_fin and dossier.date_debut:
213 date_delta = dossier.date_fin - date_debut
214 else:
9163be6f 215 date_delta = rapport_date_delta
98d6eb6c 216
899aea3b
JPC
217 masse_salariale = (salaire_base + total_indemnites + \
218 sum(primes.values()) + charges)
023bdc65
JPC
219 masse_salariale_euro = rh.Remuneration(montant=masse_salariale,
220 devise=remuns[0].devise)
221 self.convertir(masse_salariale_euro)
222
223 item_rapport = (
224 ('bureau', dossier.poste.implantation.region.code),
225 ('pays', unicode(pays)),
226 ('implantation', dossier.poste.implantation.nom_court),
227 ('type_implantation', dossier.poste.implantation.type),
228 #'imputation', None),
229 ('valeur_point',
9163be6f 230 #todo valeur du point si pas présent
98d6eb6c
JPC
231 valeurs_point_par_imp.get(
232 dossier.poste.implantation_id
023bdc65
JPC
233 ) or ""),
234 ('numero_employe', dossier.employe_id),
235 ('nom', dossier.employe.nom.upper()),
236 ('prenom', dossier.employe.prenom),
237 ('type_de_poste', dossier.poste.type_poste.nom),
238 ('intitule_de_poste', dossier.poste.nom),
239 ('niveau', unicode(dossier.classement)),
240 ('point', "%s" %
98d6eb6c 241 dossier.classement.coefficient \
9163be6f
JPC
242 if dossier.classement and
243 dossier.classement.coefficient
023bdc65
JPC
244 else ""),
245 ('regime_de_travail', "%s %%" % int(regime * 100)),
246 ('local_expatrie', statut),
247 ('statut', dossier.statut.code),
248 ('date_fin_contrat', dossier.date_fin or ""),
249 ('date_debut', d_date_debut or ""),
250 ('date_fin', d_date_fin or ""),
251 ('nb_jours', date_delta.days),
252 ('devise', remuns[0].devise),
253 ('salaire_bstg_annuel', bstg_remun.montant \
254 if bstg_remun else ""),
255 ('salaire_bstg_total',
9163be6f
JPC
256 self.convertir(bstg_remun) \
257 * regime * date_delta.days \
023bdc65
JPC
258 if bstg_remun else ""),
259 ('organisme_bstg', dossier.organisme_bstg or ""),
260 ('salaire_base_brut', \
95ad7aab
JPC
261 salaire_base * regime * (
262 date_delta.days / rapport_date_delta.days
023bdc65
JPC
263 )),
264 ('salaire_complementaire', \
95ad7aab
JPC
265 salaire_complement * regime * (
266 date_delta.days / rapport_date_delta.days
023bdc65
JPC
267 )),
268 #'salaire_total', None
269 ('indemnite_fonctions', indemnites['fonc_resp'] * \
270 regime * \
271 (date_delta.days / rapport_date_delta.days)),
272 ('indemnite_expat', indemnites['expat'] * regime * \
273 (date_delta.days / rapport_date_delta.days)),
274 ('indemnite_logement', indemnites['logement'] * \
275 regime * \
276 (date_delta.days / rapport_date_delta.days)),
277 ('indemnites_transp', indemnites['transp'] * regime * \
278 (date_delta.days / rapport_date_delta.days)),
279 ('indemnites_13e', indemnites['13e'] * regime * \
280 (date_delta.days / rapport_date_delta.days)),
281 ('indemnites_autre', indemnites['autre_recurr'] * \
282 regime * \
283 (date_delta.days / rapport_date_delta.days)),
284 ('indemnites_total', total_indemnites * regime * \
285 (date_delta.days / rapport_date_delta.days)),
286 ('total_brut', (
95ad7aab
JPC
287 total_indemnites + salaire_base +
288 salaire_complement
289 ) * regime * (
290 date_delta.days / rapport_date_delta.days
023bdc65
JPC
291 )),
292 ('prime_interim', primes['interim'] * regime * \
293 (date_delta.days / rapport_date_delta.days)),
294 ('prime_installation', primes['installation'] * regime * \
295 (date_delta.days / rapport_date_delta.days)),
296 ('prime_demenagement', primes['demenagement'] * regime * \
297 (date_delta.days / rapport_date_delta.days)),
298 ('prime_avion', primes['avion'] * regime * \
299 (date_delta.days / rapport_date_delta.days)),
300 ('prime_autre', primes['autre'] * regime * \
301 (date_delta.days / rapport_date_delta.days)),
302 ('prime_total', sum(primes.values()) * regime * \
303 (date_delta.days / rapport_date_delta.days)),
304 ('charges_patronales', charges),
305 ('charges_patronales_%s' % self.annee, charges * regime * \
306 (date_delta.days / rapport_date_delta.days)),
307 ('masse_salariale', masse_salariale),
308 ('masse_salariale_%s' % self.annee, masse_salariale * \
899aea3b
JPC
309 regime * (
310 date_delta.days / rapport_date_delta.days
023bdc65
JPC
311 )),
312 ('masse_salariale__euro_%s' % self.annee, \
313 masse_salariale_euro.montant * regime * (
314 date_delta.days / rapport_date_delta.days
315 )),
316 )
98d6eb6c
JPC
317
318 self.rapport.append(item_rapport)
319
98d6eb6c
JPC
320 def build_qs(self, prefix, date_debut, date_fin):
321 date_debut_null = \
322 Q(**{"%s%s__isnull" % (prefix, KEY_DATE_DEBUT): True})
323 date_fin_null = \
324 Q(**{"%s%s__isnull" % (prefix, KEY_DATE_FIN): True})
325 date_debut_superieure_ou_egale_a_borne_gauche = \
326 Q(**{"%s%s__gte" % (prefix, KEY_DATE_DEBUT): date_debut})
327 date_debut_inferieure_ou_egale_a_borne_gauche = \
328 Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT): date_debut})
329 date_fin_superieure_ou_egale_a_borne_gauche = \
330 Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN): date_debut})
331 date_fin_inferieure_ou_egale_a_borne_droite = \
332 Q(**{"%s%s__lte" % (prefix, KEY_DATE_FIN): date_fin})
333 date_debut_inferieure_ou_egale_a_borne_droite = \
334 Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT): date_fin})
335 date_fin_superieure_ou_egale_a_borne_droite = \
336 Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN): date_fin})
337
338 q_range = \
339 (
340 date_debut_null & date_fin_null
341 ) | (
342 date_debut_inferieure_ou_egale_a_borne_gauche &
343 date_fin_superieure_ou_egale_a_borne_gauche &
344 date_fin_inferieure_ou_egale_a_borne_droite
345 ) | (
346 date_debut_superieure_ou_egale_a_borne_gauche &
347 date_debut_inferieure_ou_egale_a_borne_droite &
348 date_fin_superieure_ou_egale_a_borne_droite
349 ) | (
350 date_debut_inferieure_ou_egale_a_borne_gauche &
351 date_fin_superieure_ou_egale_a_borne_droite
352 ) | (
353 date_debut_null &
354 date_fin_superieure_ou_egale_a_borne_droite
355 ) | (
356 date_debut_inferieure_ou_egale_a_borne_gauche &
357 date_fin_null
358 )
359
360 return q_range
361
362 def convertir(self, remuneration):
9163be6f
JPC
363 if remuneration.devise != self.devise_base:
364 remuneration.montant = float(remuneration.montant) * \
365 self.trouver_taux(remuneration.devise).taux
366 remuneration.devise = self.devise_base
98d6eb6c
JPC
367
368 def trouver_taux(self, devise):
369 if devise.code not in self.taux_change:
370 t = rh.TauxChange.objects.filter(
371 devise=devise, annee=self.annee
372 )[0]
373 self.taux_change[devise.code] = t
374 return self.taux_change[devise.code]
899aea3b
JPC
375
376 def csv(self):
377 self.csv_handle = StringIO.StringIO()
023bdc65
JPC
378 csv_writer = csv.writer(self.csv_handle, delimiter=",",
379 doublequote=False, escapechar="\\", quoting=csv.QUOTE_ALL,
380 )
381 header = [v[0] for v in self.rapport[0]]
382 csv_writer.writerow(header)
383 for row in self.rapport:
384 values = [v[1] for v in row]
385 csv_writer.writerow(
386 [unicode(r).encode('utf-8') for r in values]
387 )