Commit 7ed495cb authored by Nicolas Chauvat's avatar Nicolas Chauvat

refactor and split views/primary into several smaller files

parent 43cd07a9b86b
......@@ -76,6 +76,15 @@ class Period(AnyEntity):
fetch_attrs, fetch_order = fetch_config(['name', 'start', 'stop'])
def entries(self):
rset = self._cw.execute('Any E ORDERBY D WHERE E is DoubleEntry, E diem D, '
'P eid %(x)s, P start <= D, P stop >= D',
{'x':self.eid})
return rset
# class AccountingYear(Period):
# __regid__ = 'AccountingYear'
class PeriodITreeAdapter(adapters.ITreeAdapter):
__select__ = is_instance('Period','AccountingYear')
tree_relation = 'sub_periods'
......
......@@ -6,6 +6,9 @@ msgstr ""
"Generated-By: pygettext.py 1.5\n"
"Plural-Forms: nplurals=2; plural=(n > 1);\n"
msgid "A year of accounting"
msgstr ""
# schema pot file, generated on 2010-07-29 17:31:30
#
# singular and plural forms for each entity type
......@@ -27,9 +30,6 @@ msgstr ""
msgid "AccountingYear_plural"
msgstr ""
msgid "Division du temps"
msgstr ""
msgid "DoubleEntry"
msgstr ""
......@@ -46,9 +46,6 @@ msgstr ""
msgid "DoubleEntry_plural"
msgstr ""
msgid "Exercice comptable"
msgstr ""
msgid "New Account"
msgstr ""
......@@ -73,9 +70,6 @@ msgstr ""
msgid "Period_plural"
msgstr ""
msgid "Plan comptable"
msgstr ""
msgid "Sub periods"
msgstr ""
......@@ -97,6 +91,15 @@ msgstr ""
msgid "This Period"
msgstr ""
msgid "Totals"
msgstr ""
msgid "accounting views"
msgstr ""
msgid "accountingyear_plural"
msgstr ""
msgid "add DoubleEntry has_parts DoubleEntryPart subject"
msgstr ""
......@@ -116,6 +119,21 @@ msgstr ""
msgid "balance"
msgstr ""
msgid "balancesheet"
msgstr ""
msgid "boxes_accountingyear_box"
msgstr ""
msgid "boxes_accountingyear_box_description"
msgstr ""
msgid "boxes_doubleentry_views_box"
msgstr ""
msgid "boxes_doubleentry_views_box_description"
msgstr ""
msgid "boxes_subperiods_box"
msgstr ""
......@@ -187,6 +205,12 @@ msgstr ""
msgid "has_parts_object"
msgstr ""
msgid "income"
msgstr ""
msgid "journal"
msgstr ""
msgid "ledger"
msgstr ""
......@@ -308,3 +332,6 @@ msgstr ""
msgctxt "AccountHierarchy"
msgid "title"
msgstr ""
msgid "vat"
msgstr ""
......@@ -5,13 +5,16 @@
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"PO-Revision-Date: 2010-07-29 18:45+0200\n"
"PO-Revision-Date: 2010-07-30 00:45+0200\n"
"Last-Translator: Nicolas Chauvat <nicolas.chauvat@logilab.fr>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "A year of accounting"
msgstr "Exercice comptable"
msgid "Account"
msgstr "Compte"
......@@ -30,9 +33,6 @@ msgstr "Exercice"
msgid "AccountingYear_plural"
msgstr "Exercices"
msgid "Division du temps"
msgstr ""
msgid "DoubleEntry"
msgstr "Écriture"
......@@ -49,9 +49,6 @@ msgstr ""
msgid "DoubleEntry_plural"
msgstr "Écritures"
msgid "Exercice comptable"
msgstr ""
msgid "New Account"
msgstr "Nouveau compte"
......@@ -76,9 +73,6 @@ msgstr "Période"
msgid "Period_plural"
msgstr "Périodes"
msgid "Plan comptable"
msgstr ""
msgid "Sub periods"
msgstr ""
......@@ -100,6 +94,15 @@ msgstr ""
msgid "This Period"
msgstr "Cette période"
msgid "Totals"
msgstr "Totaux"
msgid "accounting views"
msgstr "Vues comptables"
msgid "accountingyear_plural"
msgstr "Exercices"
msgid "add DoubleEntry has_parts DoubleEntryPart subject"
msgstr ""
......@@ -119,6 +122,21 @@ msgstr ""
msgid "balance"
msgstr ""
msgid "balancesheet"
msgstr "bilan"
msgid "boxes_accountingyear_box"
msgstr ""
msgid "boxes_accountingyear_box_description"
msgstr ""
msgid "boxes_doubleentry_views_box"
msgstr ""
msgid "boxes_doubleentry_views_box_description"
msgstr ""
msgid "boxes_subperiods_box"
msgstr ""
......@@ -190,6 +208,12 @@ msgstr ""
msgid "has_parts_object"
msgstr ""
msgid "income"
msgstr "compte résultat"
msgid "journal"
msgstr ""
msgid "ledger"
msgstr "livre"
......@@ -209,7 +233,7 @@ msgid "name"
msgstr ""
msgid "number"
msgstr ""
msgstr "nombre"
msgctxt "Account"
msgid "number"
......@@ -311,3 +335,6 @@ msgstr ""
msgctxt "AccountHierarchy"
msgid "title"
msgstr ""
msgid "vat"
msgstr ""
......@@ -2,17 +2,15 @@ from yams.buildobjs import (EntityType, SubjectRelation, RelationDefinition,
Date, String, Int, Boolean)
class Period(EntityType):
"""Division du temps"""
name = String(required=True, fulltextindexed=True, indexed=True, maxsize=128)
start = Date()
stop = Date()
sub_periods = SubjectRelation('Period', cardinality='*?')
class AccountingYear(Period):
"Exercice comptable"
class AccountingYear(Period): # exercice comptable
__specializes_schema__ = True
class AccountHierarchy(EntityType):
"Plan comptable"
class AccountHierarchy(EntityType): # plan comptable
title = String()
for_year = SubjectRelation('AccountingYear', cardinality='*?')
sub_accounts = SubjectRelation('Account', cardinality='*?')
......
from cubicweb.web import uicfg
uicfg.autoform_section.tag_subject_of(('*', 'has_parts', 'DoubleEntryPart'), formtype="main", section="inlined")
uicfg.primaryview_section.tag_subject_of(('Account', 'sub_accounts', '*'), 'hidden')
uicfg.primaryview_section.tag_subject_of(('AccountHierarchy', 'sub_accounts', '*'), 'hidden')
def format_montant(montant, empty_if_zero=True, separator='&nbsp;'):
assert isinstance(montant, int) or isinstance(montant, long) or isinstance(montant, str), type(montant)
if empty_if_zero and not int(montant):
return ''
if isinstance(montant, int) or isinstance(montant,long):
montant = '%i' % montant
if len(montant) == 1:
return '0,0%s' % montant
elif len(montant) == 2:
return '0,%s' % montant
else:
r = ','+montant[-2:]
montant = montant[:-2]
sep = ''
while montant:
r = montant[-3:] + sep + r
montant = montant[:-3]
sep = separator
return r
def entries2accounts(self, rset):
accounts = {}
for row in xrange(len(rset)):
ecr = self.cw_rset.get_entity(row,0)
for part in ecr.has_parts:
acc = part.on_account[0]
credits, debits = accounts.setdefault(acc.eid, ([],[]))
if part.credit:
credits.append(part)
else:
debits.append(part)
return accounts
# -*- encoding: utf-8 -*-
from logilab.mtconverter import xml_escape
from cubicweb.selectors import is_instance
from cubicweb.view import EntityView
from cubicweb.web.views import baseviews
from cubes.accounting.views import format_montant, entries2accounts
from cubes.accounting.views import reports
class BalanceSheet(baseviews.EntityView):
__regid__ = 'balancesheet'
title = _('balancesheet')
__select__ = is_instance('DoubleEntry')
def call(self, **kwargs):
self.w(u'<h2>Balance sheet</h2>')
......@@ -12,30 +12,7 @@ from cubicweb.selectors import is_instance, has_related_entities
from cubicweb.web import box
from cubicweb.web.htmlwidgets import BoxWidget, BoxHtml
class SubperiodsBox(box.BoxTemplate):
__regid__ = 'subperiods_box'
__select__ = box.BoxTemplate.__select__ & is_instance('Period', 'AccountingYear')
visible = True
title = _('Sub periods')
order = 50
def call(self, **kwargs):
entity = self.cw_rset.get_entity(0,0)
try:
rset = entity.related('sub_periods', role='subject')
except Unauthorized:
# can't access to something in the query, forget this box
return
if len(rset) == 0:
return
box = BoxWidget(self._cw._(self.title), self.__regid__)
for i, eid in enumerate(rset):
entity = rset.get_entity(i, 0)
box.append(self.mk_action(entity.dc_title(), entity.absolute_url()))
box.render(w=self.w)
class PossibleViewsBox(box.BoxTemplate):
class DEntryPossibleViewsBox(box.BoxTemplate):
"""display a box containing links to all possible views"""
__regid__ = 'doubleentry_views_box'
__select__ = box.BoxTemplate.__select__ & is_instance('DoubleEntry')
......@@ -44,7 +21,7 @@ class PossibleViewsBox(box.BoxTemplate):
def call(self, **kwargs):
box = BoxWidget(self._cw._(self.title), self.__regid__)
for name in ['journal', 'ledger', 'balance', 'vat']:
for name in ['journal', 'ledger', 'balance', 'income', 'balancesheet', 'vat']:
view = self._cw.vreg['views'].select(name, req=self._cw, rset=self.cw_rset)
box.append(self.box_action(view))
box.render(self.w)
......
......@@ -10,134 +10,7 @@ from cubicweb.selectors import is_instance
from cubicweb.web.facet import RangeFacet
from cubicweb.web.views import primary, baseviews, tabs
uicfg.autoform_section.tag_subject_of(('*', 'has_parts', 'DoubleEntryPart'), formtype="main", section="inlined")
uicfg.primaryview_section.tag_subject_of(('Account', 'sub_accounts', '*'), 'hidden')
uicfg.primaryview_section.tag_subject_of(('AccountHierarchy', 'sub_accounts', '*'), 'hidden')
# class AccountNumberFacet(RangeFacet):
# __regid__ = 'acc-num-facet'
# __select__ = RangeFacet.__select__ & is_instance('Account')
# rtype = 'number'
def format_montant(montant, empty_if_zero=True, separator='&nbsp;'):
assert isinstance(montant, int) or isinstance(montant, long) or isinstance(montant, str), type(montant)
if empty_if_zero and not int(montant):
return ''
if isinstance(montant, int) or isinstance(montant,long):
montant = '%i' % montant
if len(montant) == 1:
return '0,0%s' % montant
elif len(montant) == 2:
return '0,%s' % montant
else:
r = ','+montant[-2:]
montant = montant[:-2]
sep = ''
while montant:
r = montant[-3:] + sep + r
montant = montant[:-3]
sep = separator
return r
class PeriodPrimaryView(tabs.TabbedPrimaryView):
__select__ = is_instance('Period', 'AccountingYear')
tabs = [_('period_journal'), _('period_ledger'), _('period_balance')]
default_tab = 'period_journal'
class PeriodJournalView(EntityView):
__select__ = is_instance('Period','AccountingYear')
__regid__ = 'period_journal'
def cell_call(self, row, col):
entity = self.cw_rset.get_entity(row, col)
rset = self._cw.execute('Any E WHERE E is DoubleEntry, E diem D, P eid %(x)s, P start <= D, P stop >= D', {'x':entity.eid})
self.paginate()
self.wview('journal', rset, 'null')
class PeriodLedgerView(EntityView):
__select__ = is_instance('Period','AccountingYear')
__regid__ = 'period_ledger'
def cell_call(self, row, col):
entity = self.cw_rset.get_entity(row, col)
rset = self._cw.execute('Any E WHERE E is DoubleEntry, E diem D, P eid %(x)s, P start <= D, P stop >= D', {'x':entity.eid})
self.wview('ledger', rset, 'null')
class PeriodBalanceView(EntityView):
__select__ = is_instance('Period','AccountingYear')
__regid__ = 'period_balance'
def cell_call(self, row, col):
entity = self.cw_rset.get_entity(row, col)
rset = self._cw.execute('DISTINCT Any A,P WHERE X on_account A, P eid %(x)s', {'x':entity.eid})
self.wview('cumul', rset, 'null')
#rset = self._cw.execute('Any E WHERE E is DoubleEntry, E diem D, P eid %(x)s, P start <= D, P stop >= D', {'x':entity.eid})
#self.wview('balance', rset, 'null')
class AccountHierarchyPrimaryView(primary.PrimaryView):
__select__ = is_instance('AccountHierarchy')
def render_entity_attributes(self, entity):
pass
def render_entity_relations(self, entity):
rset = entity.related('sub_accounts')
self.wview('treeview', rset, 'null')
class AccountPrimaryView(primary.PrimaryView):
__select__ = is_instance('Account')
def cell_call(self, row, col, **kwargs):
entity = self.cw_rset.get_entity(row, col)
self.w(u'<h1 class="titleUnderline">%s</h1>' % xml_escape(entity.dc_long_title()))
subs = entity.related('sub_accounts')
entries = entity.entries()
if subs and entries:
self.w(u'<blink>Ne devrait pas avoir sous-compte ET écritures</blink>')
if subs:
self.wview('treeview', subs, 'null')
if entries:
credits, debits = entity.cumul()
self.w(u'<table border="0" width="100%%" align="center">')
if debits > credits:
self.w(u'<tr><td colspan="6" align="left"><b>COMPTE DEBITEUR %s</b></td></tr>'
% format_montant(debits-credits))
elif debits < credits:
self.w(u'<tr><td colspan="6" align="right"><b>COMPTE CREDITEUR %s</b></td></tr>'
% format_montant(credits-debits))
else:
self.w(u'<tr><td colspan="6" align="center"><b>COMPTE EQUILIBRE</b></td></tr>')
self.w(u'<tr align="right"><td colspan="2">Total débits</td><td>%s</td>'
u'<td colspan="2">Total crédits</td><td>%s</td></tr>'
% (format_montant(debits), format_montant(credits)))
self.w(u'<tr><td width="7%">Date</td><td width="35%">Libellé</td>'
u'<td width="8%">Débit</td><td width="7%">Date</td>'
u'<td width="35%">Libellé</td><td width="8%">Crédit</td></tr>')
parts = entity.reverse_on_account
credits = [part for part in parts if part.credit] + [None]*len(parts)
debits = [part for part in parts if not part.credit] + [None]*len(parts)
for credit, debit in zip(credits, debits):
if credit is None and debit is None:
break
self.w(u'<tr valign="top">')
if debit is None:
self.w(u'<td colspan="3"> </td>')
else:
ecr = debit.reverse_has_parts[0]
self.w(u'<td>%s</td><td><a href="%s">%s</a></td><td align="right">%s</td>' %
(ecr.diem, ecr.absolute_url(), xml_escape(ecr.title), format_montant(debit.amount)))
if credit is None:
self.w(u'<td colspan="3"> </td>')
else:
ecr = credit.reverse_has_parts[0]
self.w(u'<td>%s</td><td>%s</td><td align="right">%s</td>' %
(ecr.diem, xml_escape(ecr.title), format_montant(credit.amount)))
self.w(u'</tr>\n')
self.w(u'</table>')
from cubes.accounting.views import format_montant, entries2accounts
class DoubleEntryPrimaryView(primary.EntityView):
__regid__ = 'journal'
......@@ -257,6 +130,7 @@ class PeriodCumul(baseviews.AnyRsetView):
})
#XXX cette version restreinte de ledger est-elle vraiment utile
class Balance(baseviews.EntityView):
__regid__ = 'balance'
title = _('balance')
......@@ -288,19 +162,6 @@ class Balance(baseviews.EntityView):
% (self._cw._('Totals'), format_montant(total_debit), format_montant(total_credit)))
self.w(u'</tbody></table>')
def entries2accounts(self, rset):
accounts = {}
for row in xrange(len(rset)):
ecr = self.cw_rset.get_entity(row,0)
for part in ecr.has_parts:
acc = part.on_account[0]
credits, debits = accounts.setdefault(acc.eid, ([],[]))
if part.credit:
credits.append(part)
else:
debits.append(part)
return accounts
class Ledger(baseviews.EntityView):
__regid__ = 'ledger'
title = _('ledger')
......
# -*- encoding: utf-8 -*-
from logilab.mtconverter import xml_escape
from cubicweb.selectors import is_instance
from cubicweb.view import EntityView
from cubicweb.web.views import baseviews
from cubes.accounting.views import format_montant, entries2accounts
from cubes.accounting.views import reports
class IncomeStatement(baseviews.EntityView):
__regid__ = 'income'
title = _('income')
__select__ = is_instance('DoubleEntry')
def call(self, **kwargs):
total_charges, total_charges_prev = 0, 0
self.w(u'<table class="pycompta" width="100%" align="center" cellpadding="3">'
u'<tbody><tr><td></td><th>N</th><th>N-1</th></tr>')
self.w(u'<tr><td align="right"><b>Total des charges</b></td>'
u'<td align="right"><b>%s</b></td>'
u'<td align="right"><b>%s</b></td></tr>' %
(format_montant(total_charges), format_montant(total_charges)))
self.w(u'</tbody></table>')
def depth_style(depth, msg, has_child=True):
if depth == 0 and has_child:
return u'''<b>%s</b>''' % msg
elif depth == 1 and has_child:
return u'''<i>%s</i>''' % msg
else:
return u'''%s''' % msg
def write_decompte_poste(out, poste, planc):
out.write(u'''<a onclick="javascript:toggleVisible('%s')">%s</a>''' \
% (poste.get('id'), poste.get('nom')))
out.write(u'''<div align="right" style="display: none" id="%s">
<table border="1" style="border-collapse: collapse">''' % poste.get('id'))
out.write(u'''<thead><tr><th>compte</th><th>crédit</th><th>débit</th></tr></thead>''')
for compte in poste.findall('compte'):
out.write(u'''<tr><td>%s - %s</td><td>%s</td><td>%s</td></tr>''' % (
compte.get('num'), planc.get(compte.get('num'),'error'),
fmc(compte, 'credit'), fmc(compte, 'debit')))
out.write(u'''</table></div>''')
def write_poste_resultat(out, poste, planc, postes_prec, depth=0):
out.write(TRHIGHLIGHT)
out.write(u'''<td valign="top">''')
nom = poste.get('nom')
ident = poste.get('id')
out.write(u'&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;' * depth)
subpostes = poste.findall('poste')
if depth == 0:
out.write(u'<b>%s</b>' % nom)
elif subpostes:
out.write(u'<i>%s</i>' % nom)
else:
write_decompte_poste(out, poste, planc)
out.write(u'''</td><td valign="top" align="right">''')
if subpostes:
out.write('&#xA0;')
elif depth == 0:
out.write(u'''<b>%s</b>''' % fmc(poste, 'montant', False))
else:
out.write(fmc(poste,'montant', False))
out.write(u'''</td><td valign="top" align="right">''')
contenu = subpostes and '&#xA0;' or fmc(postes_prec[ident], 'montant', False)
if depth == 0:
out.write(u'''<b>%s</b>''' % contenu)
else:
out.write(contenu)
out.write(u'</td></tr>')
for sposte in subpostes:
write_poste_resultat(out, sposte, planc, postes_prec, depth+1)
if subpostes:
out.write(u'''<tr><td align="right">''')
txt = u'&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;&#xA0;'*depth + u'Total %s' % poste.get('nom')
out.write(depth_style(depth, txt))
out.write(depth_style(depth, fmc(poste, 'montant', False)))
out.write(u'''</td> <td align="right">''')
out.write(depth_style(depth, fmc(postes_prec[ident], 'montant', False)))
out.write(u'</td></tr>')
# FIXME: move lightgray below to css
def write_compte_resultat_total(out, title, new, old):
out.write(u'''<tr bgcolor="lightgray">
<td><b>%s</b></td>
<td align="right"><b>%s</b></td>
<td align="right"><b>%s</b></td>
</tr>''' % (title, format_montant(new), format_montant(old)))
def write_compte_resultat(out, resultat, planc, cr_precedent):
crnode = resultat.getroot()
crnode_prec = cr_precedent.getroot()
out.write(DOCTYPE)
title = u'Comptabilité %s -- Compte résulat du %s au %s' % (
SOCIETE, crnode.get('debut'), crnode.get('fin'))
out.write(HEADER % {'title':title,'breadcrumbs': breadcrumbs(crnode,u'compte de résultat')})
out.write(u'''<table class="pycompta" width="100%" align="center" cellpadding="3">
<tr><td></td><th>N</th><th>N-1</th></tr>''')
postes_prec = {}
# produits
pnode = crnode.find('produits')
produits = dict([ (poste.get('id'), poste) for poste in pnode.findall('poste') ])
pnode_prec = crnode_prec.find('produits')
postes_prec.update( dict([ (poste.get('id'), poste) for poste in pnode_prec.findall('.//poste') ]) )
# charges
cnode = crnode.find('charges')
charges = dict([ (poste.get('id'), poste) for poste in cnode.findall('poste') ])
cnode_prec = crnode_prec.find('charges')
postes_prec.update( dict([ (poste.get('id'), poste) for poste in cnode_prec.findall('.//poste') ]) )
# resultat exploitation