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