7d47243b77878244da0a8d92a5e37c193a3713ee
[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 import itertools
7
8 from django.db.models import Q
9 from django.db.models import Count
10
11 from datamaster_modeles import models as ref
12
13 import project.rh.ods as ods
14 import project.rh.models as rh
15
16
17 KEY_DATE_DEBUT = "debut"
18 KEY_DATE_FIN = "fin"
19
20 TYPE_REMUN_BSTG = (3,)
21 TYPE_REMUN_MAD = (2,)
22 TYPE_REMUN_BASE = (1,)
23 TYPE_REMUN_FONC_RESP = (7, 8)
24 TYPE_REMUN_EXPAT = (4,)
25 TYPE_REMUN_LOGEMENT = (6,)
26 TYPE_REMUN_SCOLARITE = (5,)
27 TYPE_REMUN_TRANSP = (9,)
28 TYPE_REMUN_13E = (18,)
29 TYPE_PRIME_INTERIM = (19,)
30 TYPE_REMUN_ALL_INDEMNITES = list(itertools.chain(*(
31 TYPE_REMUN_BSTG, TYPE_REMUN_BASE, TYPE_REMUN_FONC_RESP,
32 TYPE_REMUN_EXPAT, TYPE_REMUN_LOGEMENT, TYPE_REMUN_TRANSP,
33 TYPE_REMUN_13E, TYPE_PRIME_INTERIM, TYPE_REMUN_SCOLARITE)))
34 TYPE_PRIME_INSTALLATION = (13,)
35 TYPE_PRIME_DEMENAG = (15,)
36 TYPE_PRIME_AVION = (14,)
37 TYPE_PRIME_ALL = list(itertools.chain(
38 *(TYPE_PRIME_INSTALLATION, TYPE_PRIME_DEMENAG, TYPE_PRIME_AVION)
39 ))
40 TYPE_CHARGE_PATRONALE = (17,)
41 TYPE_CHARGE_ALL = list(itertools.chain(*(TYPE_CHARGE_PATRONALE,)))
42 TYPE_NATURE_INDEMN = u"Indemnité"
43 TYPE_NATURE_PAIEMENT = u"Accessoire"
44 TYPE_NATURE_CHARGES = u"Charges"
45 TYPE_NATURE_TRAITEMENT = u"Traitement"
46 HEADER_SEPARATOR = ('sep', ods.Separator(), {'columnwidth': '0.4cm'})
47
48
49 class MasseSalariale():
50 """ Rapport de la masse salariale. """
51
52 def __init__(self, date_debut, date_fin, custom_filter=None,
53 ne_pas_grouper=False):
54 """ date_debut: date de début pour les données temporelles
55 date_fin: idem
56 custom_filter: dictionnaire des paramètres à passer au queryset.
57 """
58 if not date_debut and not date_fin:
59 return
60
61 date_debut = datetime.date(
62 *time.strptime(date_debut, "%d-%m-%Y")[0:3]
63 )
64 date_fin = datetime.date(*time.strptime(date_fin, "%d-%m-%Y")[0:3])
65
66 rapport_date_delta = date_fin - date_debut
67 rapport_date_delta += datetime.timedelta(days=1)
68
69 self.annee = date_fin.year
70
71 self.devise_base = rh.Devise.objects.filter(code='EUR')[0]
72 self.taux_change = {}
73
74 q_range = self.build_qs("date_", date_debut, date_fin)
75 q_range_d = self.build_qs("dossier__date_", date_debut, date_fin)
76 remunerations = rh.Remuneration.objects.filter(q_range) \
77 .filter(q_range_d) \
78
79 if custom_filter:
80 remunerations = remunerations.filter(**custom_filter)
81 self.custom_filter = custom_filter
82
83 self.region = None
84 self.implantation = None
85 if 'dossier__poste__implantation__region' in custom_filter:
86 self.region = ref.Region.objects.get(
87 id=custom_filter['dossier__poste__implantation__region']
88 )
89 if 'dossier__poste__implantation' in custom_filter:
90 self.implantation = ref.Implantation.objects.get(
91 id=custom_filter['dossier__poste__implantation']
92 )
93
94 remunerations = remunerations.exclude(supprime=True) \
95 .select_related(
96 "dossier", "dossier_employe", "dossier_poste", "type"
97 )
98
99 custom_filter = {}
100 for k, v in self.custom_filter.items():
101 custom_filter[k.replace('dossier__', '')] = v
102 count_dossiers_by_employe = dict((d['id'], d['count']) for d in
103 rh.Dossier.objects.filter(q_range).values('id') \
104 .filter(**custom_filter) \
105 .annotate(count=Count('employe')))
106
107 contenu = {}
108
109 lineariser_dossiers = not ne_pas_grouper
110
111 for r in remunerations:
112 if lineariser_dossiers:
113 key = r.dossier.employe_id
114 else:
115 key = r.dossier_id
116
117 if key not in contenu:
118 contenu[key] = {
119 'dossiers': set(),
120 'remunerations': []
121 }
122 if lineariser_dossiers:
123 contenu[key]['remunerations'].append(r)
124 else:
125 if r.dossier_id == key:
126 contenu[key]['remunerations'].append(r)
127 contenu[key]['dossiers'].add(r.dossier)
128
129 self.rapport = []
130
131 pays_list = {}
132 for pays in ref.Pays.objects.all():
133 pays_list[pays.id] = pays
134
135 valeurs_point_par_imp = \
136 dict(
137 (v.implantation.id, v) for v in \
138 rh.ValeurPoint.objects.filter(annee=self.annee).all()
139 )
140
141 self.headers = (
142 ('bureau', u"Bureau", {'columnwidth': '2cm'}),
143 ('pays', u"Pays", {'columnwidth': '3.5cm'}),
144 ('implantation', u"Implantation", {'columnwidth': '3cm'}),
145 ('valeur_point', u"Valeur du point",
146 {'columnwidth': '2.3cm'}),
147 ('numero_employe', u"Numéro d'employé",
148 {'columnwidth': '2.4cm'}),
149 ('nom', u"Nom", {'columnwidth': '5.4cm'}),
150 ('prenom', u"Prénom", {'columnwidth': '4.8cm'}),
151 ('type_de_poste', u"Type de poste", {'columnwidth': '2.7cm'}),
152 ('intitule_de_poste', u"Intitulé du poste",
153 {'columnwidth': '7.25cm'}),
154 ('niveau', u"Niveau actel", {'columnwidth': '1.75cm'}),
155 ('point', u"Point", {'columnwidth': '1.75cm'}),
156 ('regime_de_travail', u"Régime de travail annuel",
157 {'columnwidth': '2cm'}),
158 ('local_expatrie', u"Local / Expatrié",
159 {'columnwidth': '2.25cm'}),
160 ('statut', u"Statut", {'columnwidth': '1.25cm'}),
161 ('date_fin_contrat', u"Date de fin de contrat",
162 {'columnwidth': '2cm'}),
163 HEADER_SEPARATOR,
164 ('date_debut', u"Date de début", {'columnwidth': '1.92cm'}),
165 ('date_fin', u"Date de fin", {'columnwidth': '1.92cm'}),
166 ('nb_jours', u"Nombre de jours", {'columnwidth': '2.82cm'}),
167 HEADER_SEPARATOR,
168 ('devise', u"Devise", {'columnwidth': '1.46cm'}),
169 ('salaire_bstg_annuel', u"Salaire BSTG ANNUEL",
170 {'columnwidth': '2.5cm'}),
171 ('salaire_bstg_euro', u"Salaire BSTG EUR",
172 {'columnwidth': '2.5cm'}),
173 ('organisme_bstg', u"Organisme BSTG",
174 {'columnwidth': '2.9cm'}),
175 HEADER_SEPARATOR,
176 ('salaire_theorique', u"Salaire théorique ANNUEL",
177 {'columnwidth': '2.5cm', 'background-color': '#ecab44'}),
178 ('salaire_base_brut', u"Salaire de base brut",
179 {'columnwidth': '2.5cm', 'background-color': '#ecab44'}),
180 ('salaire_complementaire', u"Salaire complémentaire",
181 {'columnwidth': '2.5cm', 'background-color': '#ecab44'}),
182 HEADER_SEPARATOR,
183 ('indemnite_fonctions', u"Indemnités de fonctions",
184 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
185 ('indemnite_expat', u"Indemnités d'expatriation",
186 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
187 ('indemnite_scolarite', u"Indemnités de frais de scolarité",
188 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
189 ('indemnite_logement', u"Indemnités de logement",
190 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
191 ('indemnite_transp', u"Indemnités de transport",
192 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
193 ('indemnite_13e', u"Indemnités 13e mois",
194 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
195 ('prime_interim', u"Prime d'intérim",
196 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
197 ('indemnite_autre', u"Autre indemnités",
198 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
199 ('indemnite_sous_total', u"Sous-total d'indemnités",
200 {'columnwidth': '2.5cm', 'background-color': '#fff840'}),
201 HEADER_SEPARATOR,
202 ('prime_installation', u"Prime d'installation",
203 {'columnwidth': '2.5cm', 'background-color': '#d7fb0f'}),
204 ('prime_demenagement', u"Prime de déménagement",
205 {'columnwidth': '2.5cm', 'background-color': '#d7fb0f'}),
206 ('prime_avion', u"Prime d'avion",
207 {'columnwidth': '2.5cm', 'background-color': '#d7fb0f'}),
208 ('prime_autre', u"Autre prime",
209 {'columnwidth': '2.5cm', 'background-color': '#d7fb0f'}),
210 ('prime_sous_total', u"Total des primes",
211 {'columnwidth': '2.5cm', 'background-color': '#d7fb0f'}),
212 HEADER_SEPARATOR,
213 ('charges_patronales', u"Charges patronales",
214 {'columnwidth': '2.5cm', 'background-color': '#fb680f'}),
215 ('charges_autre', u"Autres charges patronales",
216 {'columnwidth': '2.5cm', 'background-color': '#fb680f'}),
217 ('charges_sous_total', u"Sous-total des charges patronales",
218 {'columnwidth': '2.5cm', 'background-color': '#fb680f'}),
219 HEADER_SEPARATOR,
220 ('sous_total_traitement_annee', u"Total traitements",
221 {'background-color': '#ecab44'}),
222 ('sous_total_indemnite_annee', u"Total indemnités",
223 {'background-color': '#fff840'}),
224 ('sous_total_accessoire_annee', u"Total accessoires",
225 {'background-color': '#d7fb0f'}),
226 ('sous_total_charges_annee', u"Total charges",
227 {'background-color': '#fb680f'}),
228 HEADER_SEPARATOR,
229 ('masse_salariale', u"Masse salariale ANNUELLE",
230 {'columnwidth': '2.5cm', 'background-color': '#e6c6ed'}),
231 ('masse_salariale_annee', u"Masse salariale",
232 {'columnwidth': '2.5cm', 'background-color': '#e6c6ed'}),
233 ('masse_salariale_annee_euro', u"Masse salariale %s EUR" % \
234 self.annee, {
235 'columnwidth': '2.5cm',
236 'background-color': '#e6c6ed'
237 }
238 ),
239 )
240
241 grand_total = 0.0
242 grand_total_euro = 0.0
243
244 if not lineariser_dossiers:
245 for dossier_id, count in count_dossiers_by_employe.items():
246 if dossier_id not in contenu or \
247 len(contenu[dossier_id]['dossiers']) != count:
248 not_in = []
249 if dossier_id in contenu:
250 for d in contenu[dossier_id]['dossiers']:
251 not_in.append(d.id)
252 custom_filter = {}
253 for k, v in self.custom_filter.items():
254 custom_filter[k.replace('dossier__', '')] = v
255
256 employe_id = rh.Dossier.objects.values('employe').filter(
257 id=dossier_id).all()[0]['employe']
258 dossiers = rh.Dossier.objects.filter(q_range) \
259 .filter(employe=employe_id) \
260 .filter(**custom_filter) \
261 .all()
262 for d in dossiers:
263 if d.id not in contenu:
264 contenu[d.id] = {
265 'dossiers': set((d,)),
266 'remunerations': []
267 }
268 else:
269 contenu[d.id]['dossiers'].add(d)
270
271 postes = rh.Poste.objects
272
273 custom_filter = {}
274 for k, v in self.custom_filter.items():
275 custom_filter[k.replace('dossier__poste__', '')] = v
276
277 if custom_filter:
278 postes = postes.filter(**custom_filter)
279
280 postes_vacants = [p for p in postes.filter(q_range).all()
281 if p.is_vacant()]
282 remuneration_base = rh.TypeRemuneration.objects.get(
283 id=TYPE_REMUN_BASE[0])
284 remuneration_indem = rh.TypeRemuneration(
285 nature_remuneration=TYPE_NATURE_INDEMN)
286 remuneration_charge = rh.TypeRemuneration(
287 nature_remuneration=TYPE_NATURE_CHARGES)
288 for p in postes_vacants:
289 d = rh.Dossier()
290 d.employe = rh.Employe()
291 d.statut = rh.Statut()
292 d.poste = p
293 d.classement = p.classement_max
294 d.point = p.valeur_point_max
295 if p.devise_max:
296 remunerations = [
297 rh.Remuneration(
298 montant=p.salaire_max, devise=p.devise_max,
299 type=remuneration_base
300 ),
301 rh.Remuneration(
302 montant=p.indemn_max, devise=p.devise_max,
303 type=remuneration_indem
304 ),
305 rh.Remuneration(
306 montant=p.autre_max, devise=p.devise_max,
307 type=remuneration_charge
308 )
309 ]
310 else:
311 remunerations = [
312 rh.Remuneration(
313 montant=0, devise=self.devise_base, type=type
314 )
315 for type in (
316 remuneration_base, remuneration_indem,
317 remuneration_charge
318 )
319 ]
320 contenu['p_%s' % p.id] = {
321 'dossiers': set([d]),
322 'remunerations': remunerations
323 }
324
325 for item in contenu.values():
326 dossiers = item['dossiers']
327 remuns = item['remunerations']
328
329 if not dossiers:
330 continue
331 dossier = list(dossiers)[0]
332 for d in dossiers:
333 if d.principal:
334 dossier = d
335
336 regime = (float(dossier.regime_travail) / 100)
337
338 if dossier.statut_residence == "expat":
339 statut = "E"
340 else:
341 statut = "L"
342
343 #on détermine la date du début et fin du dossier si année en cours
344 try:
345 d_date_fin = dossier.date_fin \
346 if dossier.date_fin.year == date_fin.year else None
347 except AttributeError:
348 d_date_fin = None
349 try:
350 d_date_debut = dossier.date_debut \
351 if dossier.date_debut.year == date_fin.year else None
352 except AttributeError:
353 d_date_debut = None
354
355 pays = \
356 pays_list[dossier.poste.implantation.adresse_physique_pays.id]
357
358 #on détermine si les rémunérations sont tous dans la même devise
359 try:
360 devise = remuns[0].devise
361 except IndexError:
362 devise = self.devise_base
363 meme_devise = True
364 for r in remuns[1:]:
365 if devise != r.devise:
366 meme_devise = False
367
368 if not meme_devise:
369 for r in remuns:
370 self.convertir(r)
371 devise = remuns[0].devise
372
373 bstg_dossier = None
374 for d in dossiers:
375 if d.organisme_bstg:
376 bstg_dossier = d
377
378 bstg_remun = None
379 if bstg_dossier:
380 for r in bstg_dossier.rh_remunerations.all():
381 if r.type.id in TYPE_REMUN_MAD:
382 bstg_remun = rh.Remuneration(
383 montant=float(r.montant),
384 devise=r.devise
385 )
386
387 if bstg_remun:
388 bstg_remun_euro = rh.Remuneration(
389 montant=float(bstg_remun.montant),
390 devise=bstg_remun.devise
391 )
392 self.convertir(bstg_remun_euro)
393
394 salaire_complement = 0.0
395 salaire_base = 0.0
396 indemnites = {
397 'fonc_resp': 0.0,
398 'expat': 0.0,
399 'scolarite': 0.0,
400 'logement': 0.0,
401 'transp': 0.0,
402 '13e': 0.0,
403 'autre_recurr': 0.0,
404 'interim': 0.0,
405 }
406
407 primes = {
408 'installation': 0.0,
409 'demenagement': 0.0,
410 'avion': 0.0,
411 'autre': 0.0,
412 }
413 charges = {
414 'patronale': 0.0,
415 'autre': 0.0,
416 }
417
418 total_remun_annee = {
419 'traitement': 0.0,
420 'indemnite': 0.0,
421 'accessoire': 0.0,
422 'charges': 0.0,
423 }
424
425 #Calcul du nombre de jours pour ce dossier.
426 dossier_date_delta = self.calculer_nombre_jours(
427 dossier.date_debut, dossier.date_fin,
428 date_debut, date_fin)
429
430 masse_salariale = 0.0
431 masse_salariale_annee = 0.0
432 for r in remuns:
433 montant = float(r.montant)
434 if r.date_fin is None and dossier.date_fin is not None:
435 r.date_fin = min(date_fin, dossier.date_fin)
436 facteur = self.calculer_nombre_jours(
437 r.date_debut, r.date_fin,
438 date_debut, date_fin).days \
439 / float(rapport_date_delta.days)
440
441 if r.type_id in TYPE_REMUN_BSTG:
442 salaire_complement += montant * facteur
443
444 if r.type_id not in TYPE_REMUN_MAD:
445 masse_salariale += montant
446 masse_salariale_annee += montant * regime
447
448 if r.type_id in TYPE_REMUN_BASE:
449 salaire_base += montant * facteur
450
451 if r.type_id in TYPE_REMUN_FONC_RESP:
452 indemnites['fonc_resp'] += montant * facteur
453
454 if r.type_id in TYPE_REMUN_EXPAT:
455 indemnites['expat'] += montant * facteur
456
457 if r.type_id in TYPE_REMUN_SCOLARITE:
458 indemnites['scolarite'] += montant * facteur
459
460 if r.type_id in TYPE_REMUN_LOGEMENT:
461 indemnites['logement'] += montant * facteur
462
463 if r.type_id in TYPE_REMUN_TRANSP:
464 indemnites['transp'] += montant * facteur
465
466 if r.type_id in TYPE_REMUN_13E:
467 indemnites['13e'] += montant * facteur
468
469 if r.type_id in TYPE_PRIME_INTERIM:
470 indemnites['interim'] += montant * facteur
471
472 if r.type_id not in TYPE_REMUN_ALL_INDEMNITES \
473 and r.type.nature_remuneration == TYPE_NATURE_INDEMN \
474 and r.type_id != TYPE_REMUN_MAD:
475 indemnites['autre_recurr'] += montant * facteur
476
477 if r.type_id in TYPE_PRIME_INSTALLATION:
478 primes['installation'] += montant * facteur
479
480 if r.type_id in TYPE_PRIME_DEMENAG:
481 primes['demenagement'] += montant * facteur
482
483 if r.type_id in TYPE_PRIME_AVION:
484 primes['avion'] += montant * facteur
485
486 if r.type_id not in TYPE_PRIME_ALL and \
487 r.type.nature_remuneration == TYPE_NATURE_PAIEMENT:
488 primes['autre'] += montant * facteur
489
490 if r.type_id in TYPE_CHARGE_PATRONALE:
491 charges['patronale'] += montant * facteur
492
493 if r.type_id not in TYPE_CHARGE_ALL and \
494 r.type.nature_remuneration == TYPE_NATURE_CHARGES:
495 charges['autre'] += montant * facteur
496
497 if r.type.nature_remuneration == TYPE_NATURE_INDEMN or \
498 r.type.id in (7,):
499 total_remun_annee['indemnite'] += montant * facteur
500
501 if r.type.nature_remuneration == TYPE_NATURE_PAIEMENT:
502 total_remun_annee['accessoire'] += montant * facteur
503
504 if r.type.nature_remuneration == TYPE_NATURE_CHARGES:
505 total_remun_annee['charges'] += montant * facteur
506
507 if (r.type.nature_remuneration == TYPE_NATURE_TRAITEMENT and
508 r.type.id not in (TYPE_REMUN_MAD, 7)
509 ) or r.type_id == TYPE_REMUN_BSTG:
510 total_remun_annee['traitement'] += montant * facteur
511
512 total_indemnites = sum(indemnites.values())
513
514 masse_salariale_euro = rh.Remuneration(
515 montant=masse_salariale_annee, devise=devise)
516 self.convertir(masse_salariale_euro)
517
518 if dossier.classement and dossier.classement.coefficient:
519 coefficient = dossier.classement.coefficient
520 else:
521 coefficient = ""
522
523 #todo valeur du point si pas présent
524 valeur_point = valeurs_point_par_imp.get(
525 dossier.poste.implantation_id
526 )
527 if valeur_point:
528 valeur_point_label = "%s %s" % (valeur_point.valeur,
529 valeur_point.devise.code)
530 else:
531 valeur_point_label = ""
532
533 salaire_theorique = (
534 round(valeur_point.valeur * int(coefficient) * regime, 2) \
535 if valeur_point and coefficient and regime else None)
536
537 item_rapport = {
538 'bureau': dossier.poste.implantation.region.code,
539 'pays': unicode(pays),
540 'implantation': dossier.poste.implantation.nom_court,
541 'type_implantation': dossier.poste.implantation.type,
542 #'imputation': None,
543 'valeur_point': valeur_point_label,
544 'numero_employe': dossier.employe_id,
545 'nom': dossier.employe.nom.upper(),
546 'prenom': dossier.employe.prenom,
547 'type_de_poste': dossier.poste.type_poste.nom,
548 'intitule_de_poste': dossier.poste.nom_feminin
549 if dossier.employe.genre == "F" else
550 dossier.poste.nom,
551 'niveau': dossier.classement,
552 'point': coefficient,
553 'regime_de_travail': "%s %%" % int(regime * 100),
554 'local_expatrie': statut,
555 'statut': dossier.statut.code,
556 'date_fin_contrat': dossier.date_fin or "",
557 'date_debut': d_date_debut or "",
558 'date_fin': d_date_fin or "",
559 'nb_jours': dossier_date_delta.days,
560 'devise': devise.code,
561 'salaire_bstg_annuel': bstg_remun.montant \
562 if bstg_remun else "",
563 'salaire_bstg_euro': bstg_remun_euro.montant \
564 if bstg_remun else "",
565 'organisme_bstg': dossier.organisme_bstg or "",
566 'salaire_theorique': salaire_theorique,
567 'salaire_base_brut': \
568 salaire_base * regime,
569 'salaire_complementaire': \
570 salaire_complement * regime,
571 #'salaire_total': None
572 'indemnite_fonctions': indemnites['fonc_resp'] * \
573 regime,
574 'indemnite_expat': indemnites['expat'] * regime,
575 'indemnite_scolarite': indemnites['scolarite'] * \
576 regime,
577 'indemnite_logement': indemnites['logement'] * \
578 regime,
579 'indemnite_transp': indemnites['transp'] * regime,
580 'indemnite_13e': indemnites['13e'] * regime,
581 'prime_interim': indemnites['interim'] * regime,
582 'indemnite_autre': indemnites['autre_recurr'] * \
583 regime,
584 'indemnite_sous_total': total_indemnites * regime,
585 'total_brut': (
586 total_indemnites + salaire_base +
587 salaire_complement
588 ) * regime,
589 'prime_installation': primes['installation'] * regime,
590 'prime_demenagement': primes['demenagement'] * regime,
591 'prime_avion': primes['avion'] * regime,
592 'prime_autre': primes['autre'] * regime,
593 'prime_sous_total': sum(primes.values()) * regime,
594 'charges_patronales': charges['patronale'] * regime,
595 'charges_autre': charges['autre'] * regime,
596 'charges_sous_total': sum(charges.values()) * regime,
597 'sous_total_traitement_annee': \
598 total_remun_annee['traitement'] * regime,
599 'sous_total_indemnite_annee': \
600 total_remun_annee['indemnite'] * regime,
601 'sous_total_accessoire_annee': \
602 total_remun_annee['accessoire'] * regime,
603 'sous_total_charges_annee': \
604 total_remun_annee['charges'] * regime,
605 'masse_salariale': masse_salariale * regime,
606 'masse_salariale_annee': masse_salariale_annee * regime,
607 'masse_salariale_annee_euro': \
608 masse_salariale_euro.montant * regime,
609 'sep': ods.Separator(),
610 }
611
612 grand_total_euro += round(masse_salariale_euro.montant * regime
613 * (
614 dossier_date_delta.days / rapport_date_delta.days
615 ), 2)
616
617 self.rapport.append(item_rapport)
618
619 self.rapport = sorted(self.rapport, key=lambda r: r['nom'])
620
621 self.grand_totaux = (grand_total, grand_total_euro)
622
623 def calculer_nombre_jours(self, debut, fin, debut_limite, fin_limite):
624 """Calcul le nombre de jours entre fin et debut, sans dépasser
625 les limites. Si debut ou fin set null, on prend debut_limite/fin_limi
626 """
627
628 if not debut:
629 debut = debut_limite
630 if not fin:
631 fin = fin_limite
632
633 if debut < debut_limite:
634 debut = debut_limite
635 if fin > fin_limite:
636 fin = fin_limite
637
638 fin += datetime.timedelta(days=1)
639 return fin - debut
640
641 def build_qs(self, prefix, date_debut, date_fin):
642 date_debut_null = \
643 Q(**{"%s%s__isnull" % (prefix, KEY_DATE_DEBUT): True})
644 date_fin_null = \
645 Q(**{"%s%s__isnull" % (prefix, KEY_DATE_FIN): True})
646 date_debut_superieure_ou_egale_a_borne_gauche = \
647 Q(**{"%s%s__gte" % (prefix, KEY_DATE_DEBUT): date_debut})
648 date_debut_inferieure_ou_egale_a_borne_gauche = \
649 Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT): date_debut})
650 date_fin_superieure_ou_egale_a_borne_gauche = \
651 Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN): date_debut})
652 date_fin_inferieure_ou_egale_a_borne_droite = \
653 Q(**{"%s%s__lte" % (prefix, KEY_DATE_FIN): date_fin})
654 date_debut_inferieure_ou_egale_a_borne_droite = \
655 Q(**{"%s%s__lte" % (prefix, KEY_DATE_DEBUT): date_fin})
656 date_fin_superieure_ou_egale_a_borne_droite = \
657 Q(**{"%s%s__gte" % (prefix, KEY_DATE_FIN): date_fin})
658
659 q_range = \
660 (
661 date_debut_null & date_fin_null
662 ) | (
663 date_fin_superieure_ou_egale_a_borne_gauche &
664 date_fin_inferieure_ou_egale_a_borne_droite &
665 (
666 date_debut_inferieure_ou_egale_a_borne_gauche |
667 date_debut_null
668 )
669 ) | (
670 date_debut_superieure_ou_egale_a_borne_gauche &
671 date_debut_inferieure_ou_egale_a_borne_droite &
672 (
673 date_fin_superieure_ou_egale_a_borne_droite |
674 date_fin_null
675 )
676 ) | (
677 date_debut_inferieure_ou_egale_a_borne_gauche &
678 date_fin_superieure_ou_egale_a_borne_droite
679 ) | (
680 date_debut_null &
681 date_fin_superieure_ou_egale_a_borne_droite
682 ) | (
683 date_debut_inferieure_ou_egale_a_borne_gauche &
684 date_fin_null
685 )
686 return q_range
687
688 def convertir(self, remuneration):
689 if remuneration.devise != self.devise_base:
690 try:
691 remuneration.montant = float(remuneration.montant) * \
692 self.trouver_taux(remuneration.devise).taux
693 remuneration.devise = self.devise_base
694 except AttributeError:
695 pass
696
697 def trouver_taux(self, devise):
698 if devise.code not in self.taux_change:
699 try:
700 t = rh.TauxChange.objects.filter(
701 devise=devise, annee=self.annee
702 )[0]
703 except IndexError:
704 return None
705 self.taux_change[devise.code] = t
706 return self.taux_change[devise.code]
707
708 def csv(self):
709 self.csv_handle = StringIO.StringIO()
710 csv_writer = csv.writer(self.csv_handle, delimiter=",",
711 doublequote=False, escapechar="\\", quoting=csv.QUOTE_ALL,
712 )
713 header = [v[0] for v in self.rapport[0]]
714 csv_writer.writerow(header)
715 for row in self.rapport:
716 values = [v[1] for v in row]
717 csv_writer.writerow(
718 [unicode(r).encode('utf-8') for r in values]
719 )
720
721 def ods(self):
722 self.doc = ods.OpenDocumentSpreadsheet()
723
724 nom = u"Masse salariale %s" % self.annee
725 if self.region:
726 nom += u" %s" % self.region
727 elif self.implantation:
728 nom += u" %s" % self.implantation
729
730 table = self.doc.add_table(name=nom)
731
732 for h in self.headers:
733 if len(h) > 2:
734 table.add_column(**h[2])
735
736 table.add_row([h[1] for h in self.headers], rowheight='2cm')
737
738 for r in self.rapport:
739 table.add_row([r[h[0]] for h in self.headers])
740
741 #a.doc.write('hello_world.ods')