pycompta-render to html, json or csv

authorNicolas Chauvat <nicolas.chauvat@logilab.fr>
changesetac729fbcbc9f
branchdefault
phasedraft
hiddenyes
parent revision#1148b0d1d6b6 rapports configurables (closes #257021)
child revision#1a2664c8bb11 [comptas] permet plusieurs sous-comptas
files modified by this revision
bin/pycompta-render
lib/render_csv.py
lib/render_html.py
lib/render_json.py
main.py
# HG changeset patch
# User Nicolas Chauvat <nicolas.chauvat@logilab.fr>
# Date 1406801582 -7200
# Thu Jul 31 12:13:02 2014 +0200
# Node ID ac729fbcbc9fc87b9d5d90664e23d53b25977806
# Parent 1148b0d1d6b68733749a98419e5b7216406517e6
pycompta-render to html, json or csv

diff --git a/bin/pycompta-render b/bin/pycompta-render
@@ -1,44 +1,18 @@
1  #!/usr/bin/python
2 
3  import sys
4  import os.path as osp
5 -import glob
6 -import codecs
7 -import xml.etree.ElementTree as ET
8 -from pycompta.lib import render_html
9 -
10 -target = sys.argv[1]
11 -if not target.endswith('/'): target += '/'
12 -basedir = osp.dirname(target)
13 -planc = {}
14 -for compte in ET.parse(osp.join(target, 'plan-comptable.xml')).findall('.//compte'):
15 -    planc[compte.get('numero')] = compte.get('nom')
16 -cr_precedent = ET.parse(osp.join(target, 'compte-resultat_precedent.xml'))
17 -bilan_precedent = ET.parse(osp.join(target, 'bilan_precedent.xml'))
18 +from pycompta.main import render_mod
19 
20 -for path in glob.glob(osp.join(basedir,'*.xml')):
21 -    basename = osp.basename(path)
22 -    if basename.startswith('journal'):
23 -        dest = path.replace('journal', 'journal2').replace('.xml', '.html')
24 -        print 'rendering journal', dest
25 -        render_html.write_journal(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
26 -    elif basename.startswith('grand-livre'):
27 -        dest = path.replace('grand-livre', 'balance2').replace('.xml', '.html')
28 -        print 'rendering balance', dest, 'from', path
29 -        render_html.write_balance(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
30 -        dest = path.replace('grand-livre', 'grand-livre2').replace('.xml', '.html')
31 -        print 'rendering grand-livre', dest, 'from', path
32 -        render_html.write_grandlivre(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
33 -        dest = path.replace('grand-livre', 'tva2').replace('.xml', '.html')
34 -        print 'rendering tva', dest, 'from', path
35 -        render_html.write_tva(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
36 -    elif basename.startswith('compte-resultat'):
37 -        dest = path.replace('compte-resultat', 'compte-resultat2').replace('.xml', '.html')
38 -        print 'rendering compte-resultat', dest, 'from', path
39 -        render_html.write_compte_resultat(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, cr_precedent)
40 -    elif basename.startswith('bilan-immo'):
41 -        pass
42 -    elif basename.startswith('bilan') and not basename.startswith('bilan_precedent'):
43 -        dest = path.replace('bilan', 'bilan2').replace('.xml', '.html')
44 -        print 'rendering bilan', dest, 'from', path
45 -        render_html.write_bilan(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, bilan_precedent)
46 +if __name__ == '__main__':
47 +    format = sys.argv[1]
48 +    target = sys.argv[2]
49 +    if not target.endswith('/'): target += '/'
50 +    basedir = osp.dirname(target)
51 +
52 +    if format == 'html':
53 +        from pycompta.lib import render_html as mod
54 +    if format == 'json':
55 +        from pycompta.lib import render_json as mod
56 +
57 +    render_mod(mod, basedir)
diff --git a/lib/render_csv.py b/lib/render_csv.py
@@ -0,0 +1,43 @@
58 +# -*- coding: utf-8 -*-
59 +
60 +from pycompta.lib.render_html import fmc, format_montant
61 +
62 +SOCIETE = 'SOCIETE'
63 +EXTENSION = '.csv'
64 +
65 +def div100(string):
66 +    if len(string) > 2:
67 +        return string[:-2]+','+string[-2:]
68 +    return string
69 +
70 +def write_journal(output, journal, planc):
71 +    pass
72 +
73 +def write_grandlivre(*args):
74 +    pass
75 +
76 +def write_balance(out, glivre, planc):
77 +    glnode = glivre.getroot()
78 +    out.write('# Comptabilite %s -- Balance du %s au %s\r\n' % (
79 +            SOCIETE, glnode.get('debut'), glnode.get('fin')))
80 +    out.write('compte;libelle;report-debit;report-credit;var-debit;var-credit;debit;credit\r\n')
81 +    for compte in glivre.findall('compte'):
82 +        total = int(compte.get('solde'))
83 +        out.write(';'.join([compte.get('num'), planc.get(compte.get('num'), u''),
84 +                            div100(compte.get('report-debit')),
85 +                            div100(compte.get('report-credit')),
86 +                            div100(compte.get('var-debit')),
87 +                            div100(compte.get('var-credit')),
88 +                            div100(str(max(-total,0))),
89 +                            div100(str(max(total,0)))
90 +                            ]))
91 +        out.write('\r\n')
92 +
93 +def write_compte_resultat(*args):
94 +    pass
95 +
96 +def write_bilan(*args):
97 +    pass
98 +
99 +def write_tva(*args):
100 +    pass
diff --git a/lib/render_html.py b/lib/render_html.py
@@ -22,10 +22,12 @@
101  TRHIGHLIGHT = u'''<tr onmouseover="addElementClass(this, 'highlighted');" ''' \
102      '''onmouseout="removeElementClass(this, 'highlighted')">'''
103 
104  SOCIETE = '%soc%'
105 
106 +EXTENSION = '.html'
107 +
108  # FIXME: move javascript to lib ?
109 
110  def fmc(compte, key, empty_if_zero=True):
111      return format_montant(compte.get(key), empty_if_zero)
112 
diff --git a/lib/render_json.py b/lib/render_json.py
@@ -0,0 +1,60 @@
113 +# -*- coding: utf-8 -*-
114 +
115 +import json
116 +
117 +SOCIETE = 'SOCIETE'
118 +EXTENSION = '.json'
119 +
120 +def write_journal(output, journal, planc):
121 +    jnl = journal.getroot()
122 +    out = {}
123 +    out['title'] = u'Comptabilité %s -- Journal du %s au %s' % (SOCIETE, jnl.get('debut'), jnl.get('fin'))
124 +    out['debit'] = jnl.get('debit')
125 +    out['credit'] = jnl.get('credit')
126 +    out['ecritures'] = []
127 +    ecritures = sorted((ecr for ecr in journal.findall('ecriture')), key=lambda x: x.get('date'))
128 +    for ecr in ecritures:
129 +        item = {'num': ecr.get('e_num'),
130 +                'date': ecr.get('date'),
131 +                'label': ecr.findtext('libelle'),
132 +                'debits': [],
133 +                'credits': [],
134 +                }
135 +        for part in sorted((deb for deb in ecr.findall('debit')), key=lambda x: x.get('compte')):
136 +            item['debits'].append({'compte':part.get('compte'), 'montant': part.get('montant')})
137 +        for part in sorted((deb for deb in ecr.findall('credit')), key=lambda x: x.get('compte')):
138 +            item['credits'].append({'compte':part.get('compte'), 'montant': part.get('montant')})
139 +        txt = u''
140 +        if ecr.get('ref'):
141 +            txt += u'Cf %s <br />' % ecr.get('ref')
142 +        for reg in ecr.findall('reglement'):
143 +            txt += u'Réglement %s %s <br />' % (reg.get('type',''), reg.text)
144 +        out['ecritures'].append(item)
145 +    json.dump(out, output)
146 +
147 +def write_grandlivre(*args):
148 +    pass
149 +
150 +def write_balance(*args):
151 +    pass
152 +
153 +def write_compte_resultat(output, resultat, planc, crprecedent):
154 +    crnode = resultat.getroot()
155 +    out = {}
156 +    out['title'] = u'Comptabilité %s -- Compte résulat du %s au %s' % (
157 +        SOCIETE, crnode.get('debut'), crnode.get('fin'))
158 +    out['debut'] = crnode.get('debut')
159 +    out['fin'] = crnode.get('fin')
160 +    pnode = crnode.find('produits')
161 +    #out['produits'] = dict([ (poste.get('id'), poste) for poste in pnode.findall('poste') ])
162 +    cnode = crnode.find('charges')
163 +    #out['charges'] = dict([ (poste.get('id'), poste) for poste in cnode.findall('poste') ])
164 +    print out
165 +    json.dump(out, output)
166 +    return
167 +
168 +def write_bilan(*args):
169 +    pass
170 +
171 +def write_tva(*args):
172 +    pass
diff --git a/main.py b/main.py
@@ -10,10 +10,13 @@
173  import sys
174  import os
175  import os.path as osp
176  import optparse
177  import logging
178 +import glob
179 +import codecs
180 +import xml.etree.ElementTree as ET
181 
182  if sys.stdout.isatty():
183      from logilab.common.logging_ext import set_color_formatter
184      set_color_formatter()
185 
@@ -131,10 +134,54 @@
186      else :
187          compta_prev = None
188 
189      return compta, compta_prev
190 
191 +def planc2dict(filename):
192 +    planc = {}
193 +    for compte in ET.parse(filename).findall('.//compte'):
194 +        planc[compte.get('numero')] = compte.get('nom')
195 +    return planc
196 +
197 +def render_mod(mod, basedir, filenames=None):
198 +    planc = planc2dict(osp.join(basedir, 'plan-comptable.xml'))
199 +    #societe = ET.parse(osp.join(basedir, 'societe.xml'))
200 +
201 +    ext = mod.EXTENSION
202 +
203 +    if filenames is None:
204 +        filenames = glob.glob(osp.join(basedir,'*.xml'))
205 +
206 +    for path in filenames:
207 +        basename = osp.basename(path)
208 +        if basename.startswith('journal'):
209 +            dest = path.replace('.xml', ext)
210 +            logging.debug('rendering journal', dest)
211 +            mod.write_journal(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
212 +        elif basename.startswith('grand-livre'):
213 +            dest = path.replace('grand-livre', 'balance').replace('.xml', ext)
214 +            logging.debug('rendering balance', dest, 'from', path)
215 +            mod.write_balance(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
216 +            dest = path.replace('.xml', ext)
217 +            logging.debug('rendering grand-livre', dest, 'from', path)
218 +            mod.write_grandlivre(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
219 +            dest = path.replace('grand-livre', 'tva').replace('.xml', ext)
220 +            logging.debug('rendering tva', dest, 'from', path)
221 +            mod.write_tva(codecs.open(dest,'w','utf-8'), ET.parse(path), planc)
222 +        elif basename.startswith('compte-resultat'):
223 +            cr_precedent = ET.parse(osp.join(basedir, 'compte-resultat_precedent.xml'))
224 +            dest = path.replace('.xml', ext)
225 +            logging.debug('rendering compte-resultat', dest, 'from', path)
226 +            mod.write_compte_resultat(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, cr_precedent)
227 +        elif basename.startswith('bilan-immo'):
228 +            pass
229 +        elif basename.startswith('bilan') and not basename.startswith('bilan_precedent'):
230 +            bilan_precedent = ET.parse(osp.join(basedir, 'bilan_precedent.xml'))
231 +            dest = path.replace('.xml', ext)
232 +            logging.debug('rendering bilan', dest, 'from', path)
233 +            mod.write_bilan(codecs.open(dest,'w','utf-8'), ET.parse(path), planc, bilan_precedent)
234 +
235  ## MAIN ########################################################################
236 
237  def mk_optparser():
238      usage = "usage: %prog [options] <source> <target>"
239      parser = optparse.OptionParser(usage)
@@ -178,10 +225,11 @@
240      if options.fo or (options.pdf and options.fo is None) :
241          render.render_fo(*job)
242      if options.pdf :
243          render.render_pdf(*job)
244 
245 +# XXX move to bin/pycompta
246  def run(args):
247      """
248      Programme principal
249      """
250      # parse command-line
@@ -236,6 +284,5 @@
251          stats = stats.load('stones.prof')
252          stats.sort_stats('time', 'calls')
253          stats.print_stats(30)
254      else:
255          return run_standard(config, debut, fin, prev, options)
256 -