diff --git a/book.py b/book.py
index 2adfb4f..a9a2add 100644
--- a/book.py
+++ b/book.py
@@ -3,7 +3,7 @@
# The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms.
-from trytond.model import Workflow, ModelView, ModelSQL, fields, Check
+from trytond.model import Workflow, ModelView, ModelSQL, fields, Check, tree
from trytond.pyson import Eval, Or, Bool, Id
from trytond.exceptions import UserError
from trytond.i18n import gettext
@@ -20,6 +20,16 @@ STATES = {
}
DEPENDS=['state']
+# states in case of 'btype'!=None
+STATES2 = {
+ 'readonly': Or(
+ Eval('state', '') != 'open',
+ ~Bool(Eval('btype')),
+ ),
+ 'invisible': ~Bool(Eval('btype')),
+ }
+DEPENDS2 = ['state', 'btype']
+
sel_state_book = [
('open', 'Open'),
('closed', 'Closed'),
@@ -27,7 +37,7 @@ sel_state_book = [
]
-class Book(Workflow, ModelSQL, ModelView):
+class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
'Cashbook'
__name__ = 'cashbook.book'
@@ -35,9 +45,15 @@ class Book(Workflow, ModelSQL, ModelView):
required=True, ondelete="RESTRICT")
name = fields.Char(string='Name', required=True,
states=STATES, depends=DEPENDS)
- btype = fields.Many2One(string='Type', required=True,
+ btype = fields.Many2One(string='Type',
+ help='A cash book with type can contain postings. Without type is a view.',
model_name='cashbook.type', ondelete='RESTRICT',
- states=STATES, depends=DEPENDS)
+ states={
+ 'readonly': Or(
+ STATES['readonly'],
+ Bool(Eval('lines')),
+ ),
+ }, depends=DEPENDS+['lines'])
owner = fields.Many2One(string='Owner', required=True, select=True,
model_name='res.user', ondelete='SET NULL',
states=STATES, depends=DEPENDS)
@@ -54,8 +70,8 @@ class Book(Workflow, ModelSQL, ModelView):
states=STATES, depends=DEPENDS)
reconciliations = fields.One2Many(string='Reconciliations',
field='cashbook', model_name='cashbook.recon',
- states=STATES, depends=DEPENDS)
- number_sequ = fields.Many2One(string='Line numbering', required=True,
+ states=STATES2, depends=DEPENDS2)
+ number_sequ = fields.Many2One(string='Line numbering',
help='Number sequence for numbering of the cash book lines.',
model_name='ir.sequence',
domain=[
@@ -65,41 +81,56 @@ class Book(Workflow, ModelSQL, ModelView):
('company', '=', Eval('company', -1)),
],
],
- states=STATES, depends=DEPENDS+['company'])
+ states={
+ 'readonly': STATES2['readonly'],
+ 'required': Bool(Eval('btype')),
+ }, depends=DEPENDS2+['company'])
number_atcheck = fields.Boolean(string="number when 'Checking'",
- help="The numbering of the lines is done in the step Check. If the check mark is inactive, this happens with Done.")
- start_date = fields.Date(string='Initial Date', required=True,
+ help="The numbering of the lines is done in the step Check. If the check mark is inactive, this happens with Done.",
+ states=STATES2, depends=DEPENDS2)
+ start_date = fields.Date(string='Initial Date',
states={
'readonly': Or(
- STATES['readonly'],
+ STATES2['readonly'],
Bool(Eval('lines')),
),
- }, depends=DEPENDS+['lines'])
- start_balance = fields.Numeric(string='Initial Amount', required=True,
- digits=(16, Eval('currency_digits', 2)),
- states={
- 'readonly': Or(
- STATES['readonly'],
- Bool(Eval('lines')),
- ),
- }, depends=DEPENDS+['lines', 'currency_digits'])
+ 'invisible': STATES2['invisible'],
+ 'required': Bool(Eval('btype')),
+ }, depends=DEPENDS2+['lines'])
balance = fields.Function(fields.Numeric(string='Balance', readonly=True,
digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']), 'on_change_with_balance')
- currency = fields.Many2One(string='Currency', required=True,
+ currency = fields.Many2One(string='Currency',
model_name='currency.currency',
states={
'readonly': Or(
- STATES['readonly'],
+ STATES2['readonly'],
Bool(Eval('lines', [])),
),
- }, depends=DEPENDS+['lines'])
+ 'invisible': STATES2['invisible'],
+ 'required': Bool(Eval('btype')),
+ }, depends=DEPENDS2+['lines'])
currency_digits = fields.Function(fields.Integer(string='Currency Digits',
readonly=True), 'on_change_with_currency_digits')
state = fields.Selection(string='State', required=True,
readonly=True, selection=sel_state_book)
state_string = state.translated('state')
+ parent = fields.Many2One(string="Parent",
+ model_name='cashbook.book', ondelete='RESTRICT',
+ left='left', right='right')
+ childs = fields.One2Many(string='Children', field='parent',
+ model_name='cashbook.book')
+ left = fields.Integer(string='Left', required=True, select=True)
+ right = fields.Integer(string='Right', required=True, select=True)
+
+ @classmethod
+ def __register__(cls, module_name):
+ super(Book, cls).__register__(module_name)
+
+ table = cls.__table_handler__(module_name)
+ table.drop_column('start_balance')
+
@classmethod
def __setup__(cls):
super(Book, cls).__setup__()
@@ -131,16 +162,18 @@ class Book(Workflow, ModelSQL, ModelView):
},
})
+ @staticmethod
+ def default_left():
+ return 0
+
+ @staticmethod
+ def default_right():
+ return 0
+
@classmethod
def default_number_atcheck(cls):
return True
- @classmethod
- def default_start_balance(cls):
- """ zero
- """
- return Decimal('0.0')
-
@classmethod
def default_currency(cls):
""" currency of company
@@ -193,12 +226,15 @@ class Book(Workflow, ModelSQL, ModelView):
def get_rec_name(self, name):
""" name, balance, state
"""
- return '%(name)s | %(balance)s %(symbol)s | %(state)s' % {
- 'name': self.name or '-',
- 'balance': Report.format_number(self.balance or 0.0, None),
- 'symbol': getattr(self.currency, 'symbol', '-'),
- 'state': self.state_string,
- }
+ recname = super(Book, self).get_rec_name(name)
+ if self.btype:
+ return '%(name)s | %(balance)s %(symbol)s | %(state)s' % {
+ 'name': recname or '-',
+ 'balance': Report.format_number(self.balance or 0.0, None),
+ 'symbol': getattr(self.currency, 'symbol', '-'),
+ 'state': self.state_string,
+ }
+ return recname
@fields.depends('currency')
def on_change_with_currency_digits(self, name=None):
@@ -209,28 +245,36 @@ class Book(Workflow, ModelSQL, ModelView):
else:
return 2
- @fields.depends('id', 'start_balance')
+ @fields.depends('id')
def on_change_with_balance(self, name=None):
""" compute balance
"""
- Line = Pool().get('cashbook.line')
+ pool = Pool()
+ Book2 = pool.get('cashbook.book')
+ Line = pool.get('cashbook.line')
tab_line = Line.__table__()
cursor = Transaction().connection.cursor()
- query = tab_line.select(
+ line_query = Line.search([
+ ('cashbook.id', 'in', Book2.search([
+ ('parent', 'child_of', [self.id]),
+ ], query=True)),
+ ], query=True)
+
+ query = line_query.join(tab_line,
+ condition=tab_line.id==line_query.id,
+ ).select(
Sum(tab_line.credit - tab_line.debit).as_('balance'),
- group_by=[tab_line.cashbook],
- where=tab_line.cashbook == self.id
)
if self.id:
- if self.start_balance is not None:
- balance = self.start_balance
- cursor.execute(*query)
- result = cursor.fetchone()
- if result:
+ balance = Decimal('0.0')
+ cursor.execute(*query)
+ result = cursor.fetchone()
+ if result:
+ if result[0] is not None:
balance += result[0]
- return balance
+ return balance
@classmethod
@ModelView.button
@@ -263,12 +307,6 @@ class Book(Workflow, ModelSQL, ModelView):
actions = iter(args)
for books, values in zip(actions, actions):
for book in books:
- if 'start_balance' in values.keys():
- if len(book.lines) > 0:
- raise UserError(gettext(
- 'cashbook.msg_book_err_startamount_with_lines',
- bookname = book.rec_name,
- ))
if book.state != 'open':
# allow state-update, if its the only action
if not (('state' in values.keys()) and (len(values.keys()) == 1)):
diff --git a/book.xml b/book.xml
index 465bcaf..810cf96 100644
--- a/book.xml
+++ b/book.xml
@@ -12,6 +12,13 @@ full copyright notices and license terms. -->
book_list
+
+ cashbook.book
+ tree
+
+ childs
+ book_tree
+
cashbook.book
form
@@ -19,7 +26,7 @@ full copyright notices and license terms. -->
book_form
-
+
Cashbook
cashbook.book
@@ -35,6 +42,23 @@ full copyright notices and license terms. -->
+
+
+ Cashbook
+ cashbook.book
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -251,18 +275,24 @@ full copyright notices and license terms. -->
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
@@ -335,16 +365,23 @@ full copyright notices and license terms. -->
-
+
+ search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/>
-
+
+ search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'parent')]"/>
+
+
+
+
+
+
@@ -421,16 +458,23 @@ full copyright notices and license terms. -->
-
+
+ search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/>
-
+
+ search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'parent')]"/>
+
+
+
+
+
+
diff --git a/line.py b/line.py
index b708a5d..59b778f 100644
--- a/line.py
+++ b/line.py
@@ -51,7 +51,8 @@ class Line(Workflow, ModelSQL, ModelView):
__name__ = 'cashbook.line'
cashbook = fields.Many2One(string='Cashbook', required=True, select=True,
- model_name='cashbook.book', ondelete='CASCADE', readonly=True)
+ model_name='cashbook.book', ondelete='CASCADE', readonly=True,
+ domain=[('btype', '!=', None)])
date = fields.Date(string='Date', required=True, select=True,
states=STATES, depends=DEPENDS)
month = fields.Function(fields.Integer(string='Month', readonly=True),
@@ -607,8 +608,8 @@ class Line(Workflow, ModelSQL, ModelView):
return 2
@fields.depends('id', 'date', 'cashbook', \
- '_parent_cashbook.start_balance', '_parent_cashbook.id',\
- 'reconciliation', '_parent_reconciliation.start_amount',
+ '_parent_cashbook.id', 'reconciliation', \
+ '_parent_reconciliation.start_amount',\
'_parent_reconciliation.state')
def on_change_with_balance(self, name=None):
""" compute balance until current line, with current sort order,
@@ -646,7 +647,7 @@ class Line(Workflow, ModelSQL, ModelView):
query = [
('cashbook.id', '=', self.cashbook.id),
]
- balance = self.cashbook.start_balance
+ balance = Decimal('0.0')
# get existing reconciliation, starting before current line
# this will speed up calculation of by-line-balance
diff --git a/locale/de.po b/locale/de.po
index d1be018..6075046 100644
--- a/locale/de.po
+++ b/locale/de.po
@@ -70,10 +70,6 @@ msgctxt "model:ir.message,text:msg_category_type_not_like_parent"
msgid "The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'."
msgstr "Der Typ der aktuellen Kategorie '%(catname)s' muß gleich dem Typ der übergeordneten Kategorie '%(parentname)s' sein."
-msgctxt "model:ir.message,text:msg_book_err_startamount_with_lines"
-msgid "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
-msgstr "Der Anfangsbetrag des Kassenbuchs '%(bookname)s' kann nicht geändert werden, da es bereits Buchungen enthält."
-
msgctxt "model:ir.message,text:msg_line_deny_recon_by_state"
msgid "For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'."
msgstr "Für die Abstimmung muss die Zeile '%(recname)s' im Status 'Prüfen' oder 'Fertig' sein."
@@ -258,6 +254,10 @@ msgctxt "model:ir.ui.menu,name:menu_booklist"
msgid "Cashbook"
msgstr "Kassenbuch"
+msgctxt "model:ir.ui.menu,name:menu_booktree"
+msgid "Cashbook"
+msgstr "Kassenbuch"
+
msgctxt "model:ir.ui.menu,name:menu_open_lines"
msgid "Open Cashbook"
msgstr "Kassenbuch öffnen"
@@ -286,6 +286,10 @@ msgctxt "model:ir.action,name:act_book_view"
msgid "Cashbook"
msgstr "Kassenbuch"
+msgctxt "model:ir.action,name:act_book_tree"
+msgid "Cashbook"
+msgstr "Kassenbuch"
+
msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type"
msgstr "Kassenbuchtyp"
@@ -418,6 +422,10 @@ msgctxt "view:cashbook.book:"
msgid "General Information"
msgstr "Allgemein"
+msgctxt "view:cashbook.book:"
+msgid "Amount and Numbering"
+msgstr "Betrag und Nummerierung"
+
msgctxt "view:cashbook.book:"
msgid "Balance"
msgstr "Saldo"
@@ -434,6 +442,10 @@ msgctxt "field:cashbook.book,btype:"
msgid "Type"
msgstr "Typ"
+msgctxt "help:cashbook.book,btype:"
+msgid "A cash book with type can contain postings. Without type is a view."
+msgstr "Ein Kassenbuch mit Typ kann Buchungen enthalten. Ohne Typ ist eine Sicht."
+
msgctxt "field:cashbook.book,state:"
msgid "State"
msgstr "Status"
@@ -482,10 +494,6 @@ msgctxt "field:cashbook.book,currency_digits:"
msgid "Currency Digits"
msgstr "Nachkommastellen Währung"
-msgctxt "field:cashbook.book,start_balance:"
-msgid "Initial Amount"
-msgstr "Anfangsbetrag"
-
msgctxt "field:cashbook.book,start_date:"
msgid "Initial Date"
msgstr "Anfangsdatum"
@@ -518,6 +526,22 @@ msgctxt "help:cashbook.book,number_atcheck:"
msgid "The numbering of the lines is done in the step Check. If the check mark is inactive, this happens with Done."
msgstr "Die Nummerierung der Zeilen wird beim Schritt 'Prüfen' erledigt. Bei inaktivem Häkchen passiert dies erst bei 'Fertig'."
+msgctxt "field:cashbook.book,parent:"
+msgid "Parent"
+msgstr "Übergeordnet"
+
+msgctxt "field:cashbook.book,childs:"
+msgid "Children"
+msgstr "Untergeordnet"
+
+msgctxt "field:cashbook.book,left:"
+msgid "Left"
+msgstr "Links"
+
+msgctxt "field:cashbook.book,right:"
+msgid "Right"
+msgstr "Rechts"
+
##################
# cashbook.split #
diff --git a/locale/en.po b/locale/en.po
index fdab81a..912ff90 100644
--- a/locale/en.po
+++ b/locale/en.po
@@ -66,10 +66,6 @@ msgctxt "model:ir.message,text:msg_category_type_not_like_parent"
msgid "The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'."
msgstr "The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'."
-msgctxt "model:ir.message,text:msg_book_err_startamount_with_lines"
-msgid "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
-msgstr "The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings."
-
msgctxt "model:ir.message,text:msg_line_deny_recon_by_state"
msgid "For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'."
msgstr "For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'."
@@ -242,6 +238,10 @@ msgctxt "model:ir.ui.menu,name:menu_booklist"
msgid "Cashbook"
msgstr "Cashbook"
+msgctxt "model:ir.ui.menu,name:menu_booktree"
+msgid "Cashbook"
+msgstr "Cashbook"
+
msgctxt "model:ir.ui.menu,name:menu_open_lines"
msgid "Open Cashbook"
msgstr "Open Cashbook"
@@ -266,6 +266,10 @@ msgctxt "model:ir.action,name:act_book_view"
msgid "Cashbook"
msgstr "Cashbook"
+msgctxt "model:ir.action,name:act_book_tree"
+msgid "Cashbook"
+msgstr "Cashbook"
+
msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type"
msgstr "Cashbook Type"
@@ -382,6 +386,10 @@ msgctxt "view:cashbook.book:"
msgid "General Information"
msgstr "General Information"
+msgctxt "view:cashbook.book:"
+msgid "Amount and Numbering"
+msgstr "Amount and Numbering"
+
msgctxt "view:cashbook.book:"
msgid "Balance"
msgstr "Balance"
@@ -398,6 +406,10 @@ msgctxt "field:cashbook.book,btype:"
msgid "Type"
msgstr "Type"
+msgctxt "help:cashbook.book,btype:"
+msgid "A cash book with type can contain postings. Without type is a view."
+msgstr "A cash book with type can contain postings. Without type is a view."
+
msgctxt "field:cashbook.book,state:"
msgid "State"
msgstr "State"
@@ -446,10 +458,6 @@ msgctxt "field:cashbook.book,currency_digits:"
msgid "Currency Digits"
msgstr "Currency Digits"
-msgctxt "field:cashbook.book,start_balance:"
-msgid "Initial Amount"
-msgstr "Initial Amount"
-
msgctxt "field:cashbook.book,start_date:"
msgid "Initial Date"
msgstr "Initial Date"
@@ -482,6 +490,22 @@ msgctxt "help:cashbook.book,number_atcheck:"
msgid "The numbering of the lines is done in the step Check. If the check mark is inactive, this happens with Done."
msgstr "The numbering of the lines is done in the step Check. If the check mark is inactive, this happens with Done."
+msgctxt "field:cashbook.book,parent:"
+msgid "Parent"
+msgstr "Parent"
+
+msgctxt "field:cashbook.book,childs:"
+msgid "Children"
+msgstr "Children"
+
+msgctxt "field:cashbook.book,left:"
+msgid "Left"
+msgstr "Left"
+
+msgctxt "field:cashbook.book,right:"
+msgid "Right"
+msgstr "Right"
+
msgctxt "model:cashbook.split,name:"
msgid "Split booking line"
msgstr "Split booking line"
diff --git a/menu.xml b/menu.xml
index d4c8a1b..f05d611 100644
--- a/menu.xml
+++ b/menu.xml
@@ -83,9 +83,21 @@ full copyright notices and license terms. -->
+
+
+
+
+ parent="menu_booktree" sequence="10"/>