book: hierarchie + test

book: Feld 'start_balance' entfernt
This commit is contained in:
Frederik Jaeckel 2022-09-15 23:49:54 +02:00
parent e10616e847
commit a2e7f192f8
16 changed files with 319 additions and 168 deletions

140
book.py
View file

@ -3,7 +3,7 @@
# The COPYRIGHT file at the top level of this repository contains the # The COPYRIGHT file at the top level of this repository contains the
# full copyright notices and license terms. # 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.pyson import Eval, Or, Bool, Id
from trytond.exceptions import UserError from trytond.exceptions import UserError
from trytond.i18n import gettext from trytond.i18n import gettext
@ -20,6 +20,16 @@ STATES = {
} }
DEPENDS=['state'] 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 = [ sel_state_book = [
('open', 'Open'), ('open', 'Open'),
('closed', 'Closed'), ('closed', 'Closed'),
@ -27,7 +37,7 @@ sel_state_book = [
] ]
class Book(Workflow, ModelSQL, ModelView): class Book(tree(separator='/'), Workflow, ModelSQL, ModelView):
'Cashbook' 'Cashbook'
__name__ = 'cashbook.book' __name__ = 'cashbook.book'
@ -35,9 +45,15 @@ class Book(Workflow, ModelSQL, ModelView):
required=True, ondelete="RESTRICT") required=True, ondelete="RESTRICT")
name = fields.Char(string='Name', required=True, name = fields.Char(string='Name', required=True,
states=STATES, depends=DEPENDS) 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', 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, owner = fields.Many2One(string='Owner', required=True, select=True,
model_name='res.user', ondelete='SET NULL', model_name='res.user', ondelete='SET NULL',
states=STATES, depends=DEPENDS) states=STATES, depends=DEPENDS)
@ -54,8 +70,8 @@ class Book(Workflow, ModelSQL, ModelView):
states=STATES, depends=DEPENDS) states=STATES, depends=DEPENDS)
reconciliations = fields.One2Many(string='Reconciliations', reconciliations = fields.One2Many(string='Reconciliations',
field='cashbook', model_name='cashbook.recon', field='cashbook', model_name='cashbook.recon',
states=STATES, depends=DEPENDS) states=STATES2, depends=DEPENDS2)
number_sequ = fields.Many2One(string='Line numbering', required=True, number_sequ = fields.Many2One(string='Line numbering',
help='Number sequence for numbering of the cash book lines.', help='Number sequence for numbering of the cash book lines.',
model_name='ir.sequence', model_name='ir.sequence',
domain=[ domain=[
@ -65,41 +81,56 @@ class Book(Workflow, ModelSQL, ModelView):
('company', '=', Eval('company', -1)), ('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'", 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.") 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, states=STATES2, depends=DEPENDS2)
start_date = fields.Date(string='Initial Date',
states={ states={
'readonly': Or( 'readonly': Or(
STATES['readonly'], STATES2['readonly'],
Bool(Eval('lines')), Bool(Eval('lines')),
), ),
}, depends=DEPENDS+['lines']) 'invisible': STATES2['invisible'],
start_balance = fields.Numeric(string='Initial Amount', required=True, 'required': Bool(Eval('btype')),
digits=(16, Eval('currency_digits', 2)), }, depends=DEPENDS2+['lines'])
states={
'readonly': Or(
STATES['readonly'],
Bool(Eval('lines')),
),
}, depends=DEPENDS+['lines', 'currency_digits'])
balance = fields.Function(fields.Numeric(string='Balance', readonly=True, balance = fields.Function(fields.Numeric(string='Balance', readonly=True,
digits=(16, Eval('currency_digits', 2)), digits=(16, Eval('currency_digits', 2)),
depends=['currency_digits']), 'on_change_with_balance') depends=['currency_digits']), 'on_change_with_balance')
currency = fields.Many2One(string='Currency', required=True, currency = fields.Many2One(string='Currency',
model_name='currency.currency', model_name='currency.currency',
states={ states={
'readonly': Or( 'readonly': Or(
STATES['readonly'], STATES2['readonly'],
Bool(Eval('lines', [])), 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', currency_digits = fields.Function(fields.Integer(string='Currency Digits',
readonly=True), 'on_change_with_currency_digits') readonly=True), 'on_change_with_currency_digits')
state = fields.Selection(string='State', required=True, state = fields.Selection(string='State', required=True,
readonly=True, selection=sel_state_book) readonly=True, selection=sel_state_book)
state_string = state.translated('state') 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 @classmethod
def __setup__(cls): def __setup__(cls):
super(Book, cls).__setup__() 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 @classmethod
def default_number_atcheck(cls): def default_number_atcheck(cls):
return True return True
@classmethod
def default_start_balance(cls):
""" zero
"""
return Decimal('0.0')
@classmethod @classmethod
def default_currency(cls): def default_currency(cls):
""" currency of company """ currency of company
@ -193,12 +226,15 @@ class Book(Workflow, ModelSQL, ModelView):
def get_rec_name(self, name): def get_rec_name(self, name):
""" name, balance, state """ name, balance, state
""" """
return '%(name)s | %(balance)s %(symbol)s | %(state)s' % { recname = super(Book, self).get_rec_name(name)
'name': self.name or '-', if self.btype:
'balance': Report.format_number(self.balance or 0.0, None), return '%(name)s | %(balance)s %(symbol)s | %(state)s' % {
'symbol': getattr(self.currency, 'symbol', '-'), 'name': recname or '-',
'state': self.state_string, 'balance': Report.format_number(self.balance or 0.0, None),
} 'symbol': getattr(self.currency, 'symbol', '-'),
'state': self.state_string,
}
return recname
@fields.depends('currency') @fields.depends('currency')
def on_change_with_currency_digits(self, name=None): def on_change_with_currency_digits(self, name=None):
@ -209,28 +245,36 @@ class Book(Workflow, ModelSQL, ModelView):
else: else:
return 2 return 2
@fields.depends('id', 'start_balance') @fields.depends('id')
def on_change_with_balance(self, name=None): def on_change_with_balance(self, name=None):
""" compute balance """ compute balance
""" """
Line = Pool().get('cashbook.line') pool = Pool()
Book2 = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
tab_line = Line.__table__() tab_line = Line.__table__()
cursor = Transaction().connection.cursor() 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'), 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.id:
if self.start_balance is not None: balance = Decimal('0.0')
balance = self.start_balance cursor.execute(*query)
cursor.execute(*query) result = cursor.fetchone()
result = cursor.fetchone() if result:
if result: if result[0] is not None:
balance += result[0] balance += result[0]
return balance return balance
@classmethod @classmethod
@ModelView.button @ModelView.button
@ -263,12 +307,6 @@ class Book(Workflow, ModelSQL, ModelView):
actions = iter(args) actions = iter(args)
for books, values in zip(actions, actions): for books, values in zip(actions, actions):
for book in books: 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': if book.state != 'open':
# allow state-update, if its the only action # allow state-update, if its the only action
if not (('state' in values.keys()) and (len(values.keys()) == 1)): if not (('state' in values.keys()) and (len(values.keys()) == 1)):

View file

@ -12,6 +12,13 @@ full copyright notices and license terms. -->
<field name="priority" eval="10"/> <field name="priority" eval="10"/>
<field name="name">book_list</field> <field name="name">book_list</field>
</record> </record>
<record model="ir.ui.view" id="book_view_tree">
<field name="model">cashbook.book</field>
<field name="type">tree</field>
<field name="priority" eval="10"/>
<field name="field_childs">childs</field>
<field name="name">book_tree</field>
</record>
<record model="ir.ui.view" id="book_view_form"> <record model="ir.ui.view" id="book_view_form">
<field name="model">cashbook.book</field> <field name="model">cashbook.book</field>
<field name="type">form</field> <field name="type">form</field>
@ -19,7 +26,7 @@ full copyright notices and license terms. -->
<field name="name">book_form</field> <field name="name">book_form</field>
</record> </record>
<!-- action view--> <!-- action view - list -->
<record model="ir.action.act_window" id="act_book_view"> <record model="ir.action.act_window" id="act_book_view">
<field name="name">Cashbook</field> <field name="name">Cashbook</field>
<field name="res_model">cashbook.book</field> <field name="res_model">cashbook.book</field>
@ -35,6 +42,23 @@ full copyright notices and license terms. -->
<field name="act_window" ref="act_book_view"/> <field name="act_window" ref="act_book_view"/>
</record> </record>
<!-- action view - tree -->
<record model="ir.action.act_window" id="act_book_tree">
<field name="name">Cashbook</field>
<field name="res_model">cashbook.book</field>
<field name="domain" eval="[('parent', '=', None)]" pyson="1"/>
</record>
<record model="ir.action.act_window.view" id="act_book_tree-1">
<field name="sequence" eval="10"/>
<field name="view" ref="book_view_tree"/>
<field name="act_window" ref="act_book_tree"/>
</record>
<record model="ir.action.act_window.view" id="act_book_tree-2">
<field name="sequence" eval="20"/>
<field name="view" ref="book_view_form"/>
<field name="act_window" ref="act_book_tree"/>
</record>
<!-- permission --> <!-- permission -->
<!-- anon: deny all --> <!-- anon: deny all -->
<record model="ir.model.access" id="access_book-anon"> <record model="ir.model.access" id="access_book-anon">
@ -251,18 +275,24 @@ full copyright notices and license terms. -->
<field name="perm_read" eval="False"/> <field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/> <field name="perm_write" eval="False"/>
</record> </record>
<record model="ir.model.field.access" id="fa_book-start_balance-anon">
<field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'start_balance')]"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
</record>
<record model="ir.model.field.access" id="fa_book-currency-anon"> <record model="ir.model.field.access" id="fa_book-currency-anon">
<field name="field" <field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/> search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/>
<field name="perm_read" eval="False"/> <field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/> <field name="perm_write" eval="False"/>
</record> </record>
<record model="ir.model.field.access" id="fa_book-parent-anon">
<field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'parent')]"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
</record>
<record model="ir.model.field.access" id="fa_book-childs-anon">
<field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'childs')]"/>
<field name="perm_read" eval="False"/>
<field name="perm_write" eval="False"/>
</record>
<!-- field-access - group_cashbook_admin --> <!-- field-access - group_cashbook_admin -->
<record model="ir.model.field.access" id="fa_book-company-group_cashbook_admin"> <record model="ir.model.field.access" id="fa_book-company-group_cashbook_admin">
@ -335,16 +365,23 @@ full copyright notices and license terms. -->
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
</record> </record>
<record model="ir.model.field.access" id="fa_book-start_balance-group_cashbook_admin"> <record model="ir.model.field.access" id="fa_book-currency-group_cashbook_admin">
<field name="field" <field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'start_balance')]"/> search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/>
<field name="group" ref="group_cashbook_admin"/> <field name="group" ref="group_cashbook_admin"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
</record> </record>
<record model="ir.model.field.access" id="fa_book-currency-group_cashbook_admin"> <record model="ir.model.field.access" id="fa_book-parent-group_cashbook_admin">
<field name="field" <field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/> search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'parent')]"/>
<field name="group" ref="group_cashbook_admin"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/>
</record>
<record model="ir.model.field.access" id="fa_book-childs-group_cashbook_admin">
<field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'childs')]"/>
<field name="group" ref="group_cashbook_admin"/> <field name="group" ref="group_cashbook_admin"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="True"/> <field name="perm_write" eval="True"/>
@ -421,16 +458,23 @@ full copyright notices and license terms. -->
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/> <field name="perm_write" eval="False"/>
</record> </record>
<record model="ir.model.field.access" id="fa_book-start_balance-group_cashbook"> <record model="ir.model.field.access" id="fa_book-currency-group_cashbook">
<field name="field" <field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'start_balance')]"/> search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/>
<field name="group" ref="group_cashbook"/> <field name="group" ref="group_cashbook"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/> <field name="perm_write" eval="False"/>
</record> </record>
<record model="ir.model.field.access" id="fa_book-currency-group_cashbook"> <record model="ir.model.field.access" id="fa_book-parent-group_cashbook">
<field name="field" <field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'currency')]"/> search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'parent')]"/>
<field name="group" ref="group_cashbook"/>
<field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/>
</record>
<record model="ir.model.field.access" id="fa_book-childs-group_cashbook">
<field name="field"
search="[('model.model', '=', 'cashbook.book'), ('name', '=', 'childs')]"/>
<field name="group" ref="group_cashbook"/> <field name="group" ref="group_cashbook"/>
<field name="perm_read" eval="True"/> <field name="perm_read" eval="True"/>
<field name="perm_write" eval="False"/> <field name="perm_write" eval="False"/>

View file

@ -51,7 +51,8 @@ class Line(Workflow, ModelSQL, ModelView):
__name__ = 'cashbook.line' __name__ = 'cashbook.line'
cashbook = fields.Many2One(string='Cashbook', required=True, select=True, 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, date = fields.Date(string='Date', required=True, select=True,
states=STATES, depends=DEPENDS) states=STATES, depends=DEPENDS)
month = fields.Function(fields.Integer(string='Month', readonly=True), month = fields.Function(fields.Integer(string='Month', readonly=True),
@ -607,8 +608,8 @@ class Line(Workflow, ModelSQL, ModelView):
return 2 return 2
@fields.depends('id', 'date', 'cashbook', \ @fields.depends('id', 'date', 'cashbook', \
'_parent_cashbook.start_balance', '_parent_cashbook.id',\ '_parent_cashbook.id', 'reconciliation', \
'reconciliation', '_parent_reconciliation.start_amount', '_parent_reconciliation.start_amount',\
'_parent_reconciliation.state') '_parent_reconciliation.state')
def on_change_with_balance(self, name=None): def on_change_with_balance(self, name=None):
""" compute balance until current line, with current sort order, """ compute balance until current line, with current sort order,
@ -646,7 +647,7 @@ class Line(Workflow, ModelSQL, ModelView):
query = [ query = [
('cashbook.id', '=', self.cashbook.id), ('cashbook.id', '=', self.cashbook.id),
] ]
balance = self.cashbook.start_balance balance = Decimal('0.0')
# get existing reconciliation, starting before current line # get existing reconciliation, starting before current line
# this will speed up calculation of by-line-balance # this will speed up calculation of by-line-balance

View file

@ -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'." 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." 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" 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'." 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." 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" msgid "Cashbook"
msgstr "Kassenbuch" msgstr "Kassenbuch"
msgctxt "model:ir.ui.menu,name:menu_booktree"
msgid "Cashbook"
msgstr "Kassenbuch"
msgctxt "model:ir.ui.menu,name:menu_open_lines" msgctxt "model:ir.ui.menu,name:menu_open_lines"
msgid "Open Cashbook" msgid "Open Cashbook"
msgstr "Kassenbuch öffnen" msgstr "Kassenbuch öffnen"
@ -286,6 +286,10 @@ msgctxt "model:ir.action,name:act_book_view"
msgid "Cashbook" msgid "Cashbook"
msgstr "Kassenbuch" msgstr "Kassenbuch"
msgctxt "model:ir.action,name:act_book_tree"
msgid "Cashbook"
msgstr "Kassenbuch"
msgctxt "model:ir.action,name:act_type_view" msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type" msgid "Cashbook Type"
msgstr "Kassenbuchtyp" msgstr "Kassenbuchtyp"
@ -418,6 +422,10 @@ msgctxt "view:cashbook.book:"
msgid "General Information" msgid "General Information"
msgstr "Allgemein" msgstr "Allgemein"
msgctxt "view:cashbook.book:"
msgid "Amount and Numbering"
msgstr "Betrag und Nummerierung"
msgctxt "view:cashbook.book:" msgctxt "view:cashbook.book:"
msgid "Balance" msgid "Balance"
msgstr "Saldo" msgstr "Saldo"
@ -434,6 +442,10 @@ msgctxt "field:cashbook.book,btype:"
msgid "Type" msgid "Type"
msgstr "Typ" 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:" msgctxt "field:cashbook.book,state:"
msgid "State" msgid "State"
msgstr "Status" msgstr "Status"
@ -482,10 +494,6 @@ msgctxt "field:cashbook.book,currency_digits:"
msgid "Currency Digits" msgid "Currency Digits"
msgstr "Nachkommastellen Währung" msgstr "Nachkommastellen Währung"
msgctxt "field:cashbook.book,start_balance:"
msgid "Initial Amount"
msgstr "Anfangsbetrag"
msgctxt "field:cashbook.book,start_date:" msgctxt "field:cashbook.book,start_date:"
msgid "Initial Date" msgid "Initial Date"
msgstr "Anfangsdatum" 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." 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'." 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 # # cashbook.split #

View file

@ -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'." 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'." 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" 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'." 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'." 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" msgid "Cashbook"
msgstr "Cashbook" msgstr "Cashbook"
msgctxt "model:ir.ui.menu,name:menu_booktree"
msgid "Cashbook"
msgstr "Cashbook"
msgctxt "model:ir.ui.menu,name:menu_open_lines" msgctxt "model:ir.ui.menu,name:menu_open_lines"
msgid "Open Cashbook" msgid "Open Cashbook"
msgstr "Open Cashbook" msgstr "Open Cashbook"
@ -266,6 +266,10 @@ msgctxt "model:ir.action,name:act_book_view"
msgid "Cashbook" msgid "Cashbook"
msgstr "Cashbook" msgstr "Cashbook"
msgctxt "model:ir.action,name:act_book_tree"
msgid "Cashbook"
msgstr "Cashbook"
msgctxt "model:ir.action,name:act_type_view" msgctxt "model:ir.action,name:act_type_view"
msgid "Cashbook Type" msgid "Cashbook Type"
msgstr "Cashbook Type" msgstr "Cashbook Type"
@ -382,6 +386,10 @@ msgctxt "view:cashbook.book:"
msgid "General Information" msgid "General Information"
msgstr "General Information" msgstr "General Information"
msgctxt "view:cashbook.book:"
msgid "Amount and Numbering"
msgstr "Amount and Numbering"
msgctxt "view:cashbook.book:" msgctxt "view:cashbook.book:"
msgid "Balance" msgid "Balance"
msgstr "Balance" msgstr "Balance"
@ -398,6 +406,10 @@ msgctxt "field:cashbook.book,btype:"
msgid "Type" msgid "Type"
msgstr "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:" msgctxt "field:cashbook.book,state:"
msgid "State" msgid "State"
msgstr "State" msgstr "State"
@ -446,10 +458,6 @@ msgctxt "field:cashbook.book,currency_digits:"
msgid "Currency Digits" msgid "Currency Digits"
msgstr "Currency Digits" msgstr "Currency Digits"
msgctxt "field:cashbook.book,start_balance:"
msgid "Initial Amount"
msgstr "Initial Amount"
msgctxt "field:cashbook.book,start_date:" msgctxt "field:cashbook.book,start_date:"
msgid "Initial Date" msgid "Initial Date"
msgstr "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." 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." 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:" msgctxt "model:cashbook.split,name:"
msgid "Split booking line" msgid "Split booking line"
msgstr "Split booking line" msgstr "Split booking line"

View file

@ -83,9 +83,21 @@ full copyright notices and license terms. -->
</record> </record>
<!-- menu: /Cashbook/Cashbook --> <!-- menu: /Cashbook/Cashbook -->
<menuitem id="menu_booktree" action="act_book_tree"
icon="tryton-tree"
parent="menu_cashbook" sequence="40"/>
<record model="ir.ui.menu-res.group" id="menu_booktree-group_cashbook">
<field name="menu" ref="menu_booktree"/>
<field name="group" ref="group_cashbook"/>
</record>
<record model="ir.ui.menu-res.group" id="menu_booktree-group_cashbook_admin">
<field name="menu" ref="menu_booktree"/>
<field name="group" ref="group_cashbook_admin"/>
</record>
<!-- menu: /Cashbook/Cashbook/Cashbook -->
<menuitem id="menu_booklist" action="act_book_view" <menuitem id="menu_booklist" action="act_book_view"
icon="tryton-list" icon="tryton-list"
parent="menu_cashbook" sequence="40"/> parent="menu_booktree" sequence="10"/>
<record model="ir.ui.menu-res.group" id="menu_booklist-group_cashbook"> <record model="ir.ui.menu-res.group" id="menu_booklist-group_cashbook">
<field name="menu" ref="menu_booklist"/> <field name="menu" ref="menu_booklist"/>
<field name="group" ref="group_cashbook"/> <field name="group" ref="group_cashbook"/>

View file

@ -53,9 +53,6 @@ full copyright notices and license terms. -->
<record model="ir.message" id="msg_category_type_not_like_parent"> <record model="ir.message" id="msg_category_type_not_like_parent">
<field name="text">The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'.</field> <field name="text">The type of the current category '%(catname)s' must be equal to the type of the parent category '%(parentname)s'.</field>
</record> </record>
<record model="ir.message" id="msg_book_err_startamount_with_lines">
<field name="text">The initial amount of the cash book '%(bookname)s' cannot be changed because it already contains bookings.</field>
</record>
<record model="ir.message" id="msg_line_deny_recon_by_state"> <record model="ir.message" id="msg_line_deny_recon_by_state">
<field name="text">For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'.</field> <field name="text">For reconciliation, the line '%(recname)s' must be in the status 'Check' or 'Done'.</field>
</record> </record>

View file

@ -226,7 +226,7 @@ class Reconciliation(Workflow, ModelSQL, ModelView):
)) ))
values['start_amount'] = reconciliation.predecessor.end_amount values['start_amount'] = reconciliation.predecessor.end_amount
else : else :
values['start_amount'] = reconciliation.cashbook.start_balance values['start_amount'] = Decimal('0.0')
values['end_amount'] = values['start_amount'] values['end_amount'] = values['start_amount']
# add 'checked'-lines to reconciliation # add 'checked'-lines to reconciliation

View file

@ -51,10 +51,37 @@ class BookTestCase(ModuleTestCase):
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
}]) }])
self.assertEqual(book.name, 'Book 1') self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.rec_name, 'Book 1 | 0.00 usd | Open')
self.assertEqual(book.btype.rec_name, 'CAS - Cash') self.assertEqual(book.btype.rec_name, 'CAS - Cash')
self.assertEqual(book.state, 'open') self.assertEqual(book.state, 'open')
self.assertEqual(book.state_string, 'Open') self.assertEqual(book.state_string, 'Open')
@with_transaction()
def test_book_create_hierarchy(self):
""" create cashbook, hierarchical
"""
pool = Pool()
Book = pool.get('cashbook.book')
types = self.prep_type()
company = self.prep_company()
book, = Book.create([{
'name': 'Level 1',
'btype': None,
'company': company.id,
'childs': [('create', [{
'name': 'Level 2',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
}])],
}])
self.assertEqual(book.name, 'Level 1')
self.assertEqual(book.rec_name, 'Level 1')
self.assertEqual(len(book.childs), 1)
self.assertEqual(book.childs[0].rec_name, 'Level 1/Level 2 | 0.00 usd | Open')
@with_transaction() @with_transaction()
def test_book_deny_delete_open(self): def test_book_deny_delete_open(self):
""" create cashbook, add lines, try to delete in state 'open' """ create cashbook, add lines, try to delete in state 'open'
@ -225,62 +252,6 @@ class BookTestCase(ModuleTestCase):
}, },
]) ])
@with_transaction()
def test_book_deny_update_start_amount(self):
""" create cashbook, add lines, update start-amount
"""
pool = Pool()
Book = pool.get('cashbook.book')
types = self.prep_type()
company = self.prep_company()
category = self.prep_category(cattype='in')
party = self.prep_party()
book, = Book.create([{
'name': 'Book 1',
'btype': types.id,
'company': company.id,
'currency': company.currency.id,
'number_sequ': self.prep_sequence().id,
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.start_balance, Decimal('0.0'))
self.assertEqual(book.rec_name, 'Book 1 | 0.00 usd | Open')
Book.write(*[
[book],
{
'start_balance': Decimal('1.0'),
}])
self.assertEqual(book.start_balance, Decimal('1.0'))
self.assertEqual(book.balance, Decimal('1.0'))
Book.write(*[
[book],
{
'lines': [('create', [{
'amount': Decimal('2.0'),
'description': 'Test',
'category': category.id,
'bookingtype': 'in',
'party': party.id,
}])],
}])
self.assertEqual(book.start_balance, Decimal('1.0'))
self.assertEqual(book.balance, Decimal('3.0'))
self.assertEqual(len(book.lines), 1)
self.assertEqual(book.lines[0].balance, Decimal('3.0'))
self.assertRaisesRegex(UserError,
"The initial amount of the cash book 'Fridas book | 3.00 usd | Open' cannot be changed because it already contains bookings.",
Book.write,
*[
[book],
{
'start_balance': Decimal('1.5'),
},
])
@with_transaction() @with_transaction()
def test_book_permission_owner(self): def test_book_permission_owner(self):
""" create book + 2x users, add users to group, check access """ create book + 2x users, add users to group, check access

View file

@ -39,7 +39,6 @@ class BookingWizardTestCase(ModuleTestCase):
'currency': company.currency.id, 'currency': company.currency.id,
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 1, 1), 'start_date': date(2022, 1, 1),
'start_balance': Decimal('0.0'),
}]) }])
party, = Party.create([{ party, = Party.create([{
@ -114,7 +113,6 @@ class BookingWizardTestCase(ModuleTestCase):
'currency': company.currency.id, 'currency': company.currency.id,
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 1, 1), 'start_date': date(2022, 1, 1),
'start_balance': Decimal('0.0'),
}, { }, {
'name': 'Bank', 'name': 'Bank',
'btype': types.id, 'btype': types.id,
@ -122,7 +120,6 @@ class BookingWizardTestCase(ModuleTestCase):
'currency': company.currency.id, 'currency': company.currency.id,
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'start_date': date(2022, 1, 1), 'start_date': date(2022, 1, 1),
'start_balance': Decimal('0.0'),
}]) }])
party, = Party.create([{ party, = Party.create([{

View file

@ -574,6 +574,36 @@ class LineTestCase(ModuleTestCase):
self.assertEqual(book.lines[2].rec_name, '05/01/2022|Rev/Sp|1.00 usd|Text 3 [-]') self.assertEqual(book.lines[2].rec_name, '05/01/2022|Rev/Sp|1.00 usd|Text 3 [-]')
self.assertEqual(book.lines[2].state, 'edit') self.assertEqual(book.lines[2].state, 'edit')
@with_transaction()
def test_line_to_non_type_book(self):
""" create cashbook w/o type
"""
pool = Pool()
Book = pool.get('cashbook.book')
Line = pool.get('cashbook.line')
category = self.prep_category(cattype='in')
company = self.prep_company()
party = self.prep_party()
book, = Book.create([{
'name': 'Book 1',
'btype': None,
'company': company.id,
}])
self.assertEqual(book.name, 'Book 1')
self.assertEqual(book.state, 'open')
self.assertRaisesRegex(UserError,
'The value for field "Cashbook" in "Cashbook Line" is not valid according to its domain.',
Line.create,
[{
'cashbook': book.id,
'date': date(2022, 5, 1),
'category': category.id,
'bookingtype': 'in',
'amount': Decimal('0.0'),
}])
@with_transaction() @with_transaction()
def test_line_create_check_deny_write(self): def test_line_create_check_deny_write(self):
""" create cashbook + line, 'close' book, write to line """ create cashbook + line, 'close' book, write to line

View file

@ -221,7 +221,6 @@ class ReconTestCase(ModuleTestCase):
'btype': types.id, 'btype': types.id,
'company': company.id, 'company': company.id,
'currency': company.currency.id, 'currency': company.currency.id,
'start_balance': Decimal('12.50'),
'start_date': date(2022, 5, 1), 'start_date': date(2022, 5, 1),
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'reconciliations': [('create', [{ 'reconciliations': [('create', [{
@ -234,7 +233,7 @@ class ReconTestCase(ModuleTestCase):
self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]') self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
Reconciliation.wfcheck(list(book.reconciliations)) Reconciliation.wfcheck(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 12.50 usd - 12.50 usd [0]') self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 0.00 usd [0]')
@with_transaction() @with_transaction()
def test_recon_set_start_amount_by_predecessor(self): def test_recon_set_start_amount_by_predecessor(self):
@ -254,7 +253,6 @@ class ReconTestCase(ModuleTestCase):
'btype': types.id, 'btype': types.id,
'company': company.id, 'company': company.id,
'currency': company.currency.id, 'currency': company.currency.id,
'start_balance': Decimal('12.50'),
'start_date': date(2022, 5, 1), 'start_date': date(2022, 5, 1),
'number_sequ': self.prep_sequence().id, 'number_sequ': self.prep_sequence().id,
'reconciliations': [('create', [{ 'reconciliations': [('create', [{
@ -287,7 +285,7 @@ class ReconTestCase(ModuleTestCase):
Reconciliation.wfcheck(list(book.reconciliations)) Reconciliation.wfcheck(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].state, 'check') self.assertEqual(book.reconciliations[0].state, 'check')
self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 12.50 usd - 24.50 usd [2]') self.assertEqual(book.reconciliations[0].rec_name, '05/01/2022 - 05/31/2022 | 0.00 usd - 12.00 usd [2]')
Reconciliation.wfdone(list(book.reconciliations)) Reconciliation.wfdone(list(book.reconciliations))
self.assertEqual(book.reconciliations[0].state, 'done') self.assertEqual(book.reconciliations[0].state, 'done')
@ -298,7 +296,7 @@ class ReconTestCase(ModuleTestCase):
}]) }])
self.assertEqual(recons[0].rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]') self.assertEqual(recons[0].rec_name, '05/31/2022 - 06/30/2022 | 0.00 usd - 0.00 usd [0]')
Reconciliation.wfcheck(recons) Reconciliation.wfcheck(recons)
self.assertEqual(recons[0].rec_name, '05/31/2022 - 06/30/2022 | 24.50 usd - 24.50 usd [0]') self.assertEqual(recons[0].rec_name, '05/31/2022 - 06/30/2022 | 12.00 usd - 12.00 usd [0]')
@with_transaction() @with_transaction()
def test_recon_predecessor_done(self): def test_recon_predecessor_done(self):

View file

@ -15,29 +15,32 @@ full copyright notices and license terms. -->
</group> </group>
</group> </group>
<label name="btype"/>
<field name="btype"/>
<label name="balance"/> <label name="balance"/>
<field name="balance"/> <field name="balance"/>
<newline/>
<notebook colspan="4"> <notebook colspan="4">
<page id="pgrecon" string="Reconciliations" col="1"> <page name="reconciliations" string="Reconciliations" col="1">
<field name="reconciliations"/> <field name="reconciliations"/>
</page> </page>
<page id="pggeneral" string="General Information" col="4"> <page id="pggeneral" string="General Information" col="4">
<label name="company"/>
<field name="company"/>
<label name="parent"/>
<field name="parent"/>
<field name="childs" colspan="4"/>
</page>
<page name="number_sequ" string="Amount and Numbering" col="4">
<label name="number_sequ"/> <label name="number_sequ"/>
<field name="number_sequ"/> <field name="number_sequ"/>
<label name="number_atcheck"/> <label name="number_atcheck"/>
<field name="number_atcheck"/> <field name="number_atcheck"/>
<label name="btype"/>
<field name="btype"/>
<label name="currency"/>
<field name="currency"/>
<label name="start_balance"/>
<field name="start_balance"/>
<label name="start_date"/> <label name="start_date"/>
<field name="start_date"/> <field name="start_date"/>
<label name="currency"/>
<field name="currency"/>
<label id="phaccount" colspan="2" string=" "/> <label id="phaccount" colspan="2" string=" "/>
</page> </page>

View file

@ -5,7 +5,6 @@ full copyright notices and license terms. -->
<tree> <tree>
<field name="name"/> <field name="name"/>
<field name="btype"/> <field name="btype"/>
<field name="start_balance"/>
<field name="balance" sum="Balance"/> <field name="balance" sum="Balance"/>
<field name="currency"/> <field name="currency"/>
<field name="owner"/> <field name="owner"/>

14
view/book_tree.xml Normal file
View file

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- This file is part of the cashbook-module from m-ds for Tryton.
The COPYRIGHT file at the top level of this repository contains the
full copyright notices and license terms. -->
<tree>
<field name="name"/>
<field name="btype"/>
<field name="balance" sum="Balance"/>
<field name="currency"/>
<field name="owner"/>
<field name="state"/>
<field name="parent" tree_invisible="1"/>
<field name="childs" tree_invisible="1"/>
</tree>

View file

@ -13,13 +13,12 @@ full copyright notices and license terms. -->
<field name="description" colspan="5"/> <field name="description" colspan="5"/>
<notebook colspan="6"> <notebook colspan="6">
<page string="General Information" id="general" col="6"> <page string="General Information" id="general" col="4">
<label name="company"/> <label name="company"/>
<field name="company"/> <field name="company"/>
<label name="parent"/> <label name="parent"/>
<field name="parent"/> <field name="parent"/>
<newline/> <field name="childs" colspan="4"/>
<field name="childs" colspan="6"/>
</page> </page>
</notebook> </notebook>