book: start-saldo + sperre bei lines>0, saldo, rec_name + tests
This commit is contained in:
parent
8fd6e0d339
commit
ae5303658e
9 changed files with 218 additions and 40 deletions
75
book.py
75
book.py
|
@ -9,6 +9,10 @@ from trytond.exceptions import UserError
|
|||
from trytond.i18n import gettext
|
||||
from trytond.transaction import Transaction
|
||||
from trytond.pool import Pool
|
||||
from trytond.report import Report
|
||||
from decimal import Decimal
|
||||
from sql.aggregate import Sum
|
||||
from sql.conditionals import Case
|
||||
|
||||
|
||||
STATES = {
|
||||
|
@ -48,6 +52,15 @@ class Book(Workflow, ModelSQL, ModelView):
|
|||
account = fields.Many2One(string='Account', select=True,
|
||||
model_name='account.account', ondelete='RESTRICT',
|
||||
states=STATES, depends=DEPENDS)
|
||||
start_balance = fields.Numeric(string='Initial Amount', required=True,
|
||||
states={
|
||||
'readonly': Or(
|
||||
STATES['readonly'],
|
||||
Bool(Eval('lines')),
|
||||
),
|
||||
}, depends=DEPENDS+['lines'])
|
||||
balance = fields.Function(fields.Numeric(string='Balance', readonly=True),
|
||||
'on_change_with_balance')
|
||||
currency = fields.Many2One(string='Currency', required=True,
|
||||
model_name='currency.currency',
|
||||
states={
|
||||
|
@ -64,6 +77,7 @@ class Book(Workflow, ModelSQL, ModelView):
|
|||
def __setup__(cls):
|
||||
super(Book, cls).__setup__()
|
||||
cls._order.insert(0, ('name', 'ASC'))
|
||||
cls._order.insert(0, ('state', 'ASC'))
|
||||
t = cls.__table__()
|
||||
cls._sql_constraints.extend([
|
||||
('state_val',
|
||||
|
@ -90,6 +104,12 @@ class Book(Workflow, ModelSQL, ModelView):
|
|||
},
|
||||
})
|
||||
|
||||
@classmethod
|
||||
def default_start_balance(cls):
|
||||
""" zero
|
||||
"""
|
||||
return Decimal('0.0')
|
||||
|
||||
@classmethod
|
||||
def default_currency(cls):
|
||||
""" currency of company
|
||||
|
@ -116,6 +136,55 @@ class Book(Workflow, ModelSQL, ModelView):
|
|||
"""
|
||||
return Transaction().user
|
||||
|
||||
@staticmethod
|
||||
def order_state(tables):
|
||||
""" edit = 0, check/done = 1
|
||||
"""
|
||||
Book2 = Pool().get('cashbook.book')
|
||||
tab_book = Book2.__table__()
|
||||
table, _ = tables[None]
|
||||
|
||||
query = tab_book.select(
|
||||
Case(
|
||||
(tab_book.state == 'open', 0),
|
||||
else_ = 1),
|
||||
where=tab_book.id==table.id
|
||||
)
|
||||
return [query]
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
@fields.depends('id', 'start_balance')
|
||||
def on_change_with_balance(self, name=None):
|
||||
""" compute balance
|
||||
"""
|
||||
Line = Pool().get('cashbook.line')
|
||||
tab_line = Line.__table__()
|
||||
cursor = Transaction().connection.cursor()
|
||||
|
||||
query = tab_line.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 += result[0]
|
||||
return balance
|
||||
|
||||
@classmethod
|
||||
@ModelView.button
|
||||
@Workflow.transition('open')
|
||||
|
@ -147,6 +216,12 @@ 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)):
|
||||
|
|
73
line.py
73
line.py
|
@ -77,6 +77,13 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
required=True, readonly=True, depends=['currency_digits'])
|
||||
credit = fields.Numeric(string='Credit', digits=(16, Eval('currency_digits', 2)),
|
||||
required=True, readonly=True, depends=['currency_digits'])
|
||||
|
||||
balance = fields.Function(fields.Numeric(string='Balance',
|
||||
digits=(16, Eval('currency_digits', 2)),
|
||||
help='Balance of the cash book up to the current line, if the default sorting applies.',
|
||||
readonly=True, depends=['currency_digits']),
|
||||
'on_change_with_balance')
|
||||
|
||||
currency = fields.Function(fields.Many2One(model_name='currency.currency',
|
||||
string="Currency"), 'on_change_with_currency')
|
||||
currency_digits = fields.Function(fields.Integer(string='Currency Digits'),
|
||||
|
@ -94,8 +101,8 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
@classmethod
|
||||
def __setup__(cls):
|
||||
super(Line, cls).__setup__()
|
||||
cls._order.insert(0, ('state', 'ASC'))
|
||||
cls._order.insert(0, ('date', 'ASC'))
|
||||
cls._order.insert(0, ('state', 'ASC'))
|
||||
t = cls.__table__()
|
||||
cls._sql_constraints.extend([
|
||||
('state_val',
|
||||
|
@ -146,20 +153,6 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
"""
|
||||
pass
|
||||
|
||||
@fields.depends('bookingtype', 'category')
|
||||
def on_change_bookingtype(self):
|
||||
""" clear category if not valid type
|
||||
"""
|
||||
types = {
|
||||
'in': ['in', 'mvin'],
|
||||
'out': ['out', 'mvout'],
|
||||
}
|
||||
|
||||
if self.bookingtype:
|
||||
if self.category:
|
||||
if not self.bookingtype in types.get(self.category.cattype, ''):
|
||||
self.category = None
|
||||
|
||||
@classmethod
|
||||
def default_state(cls):
|
||||
""" default: edit
|
||||
|
@ -180,6 +173,12 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
context = Transaction().context
|
||||
return context.get('cashbook', None)
|
||||
|
||||
@classmethod
|
||||
def search_rec_name(cls, name, clause):
|
||||
""" search in description +...
|
||||
"""
|
||||
return [('description',) + tuple(clause[1:])]
|
||||
|
||||
def get_rec_name(self, name):
|
||||
""" short + name
|
||||
"""
|
||||
|
@ -219,12 +218,6 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
|
||||
return [tab2]
|
||||
|
||||
@classmethod
|
||||
def search_category_view(cls, name, clause):
|
||||
""" search in category
|
||||
"""
|
||||
return [('category.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@fields.depends('category')
|
||||
def on_change_with_category_view(self, name=None):
|
||||
""" show optimizef form of category for list-view
|
||||
|
@ -239,10 +232,10 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
return self.category.get_long_recname(self.category.name)
|
||||
|
||||
@classmethod
|
||||
def search_rec_name(cls, name, clause):
|
||||
""" search in description +...
|
||||
def search_category_view(cls, name, clause):
|
||||
""" search in category
|
||||
"""
|
||||
return [('description',) + tuple(clause[1:])]
|
||||
return [('category.rec_name',) + tuple(clause[1:])]
|
||||
|
||||
@fields.depends('date')
|
||||
def on_change_with_month(self, name=None):
|
||||
|
@ -286,6 +279,20 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
"""
|
||||
return [('cashbook.state',) + tuple(clause[1:])]
|
||||
|
||||
@fields.depends('bookingtype', 'category')
|
||||
def on_change_bookingtype(self):
|
||||
""" clear category if not valid type
|
||||
"""
|
||||
types = {
|
||||
'in': ['in', 'mvin'],
|
||||
'out': ['out', 'mvout'],
|
||||
}
|
||||
|
||||
if self.bookingtype:
|
||||
if self.category:
|
||||
if not self.bookingtype in types.get(self.category.cattype, ''):
|
||||
self.category = None
|
||||
|
||||
@fields.depends('cashbook', '_parent_cashbook.currency')
|
||||
def on_change_with_currency(self, name=None):
|
||||
""" currency of cashbook
|
||||
|
@ -302,6 +309,23 @@ class Line(Workflow, ModelSQL, ModelView):
|
|||
else:
|
||||
return 2
|
||||
|
||||
@fields.depends('id', 'cashbook', '_parent_cashbook.start_balance', '_parent_cashbook.id')
|
||||
def on_change_with_balance(self, name=None):
|
||||
""" compute balance until current line, with current sort order
|
||||
"""
|
||||
Line = Pool().get('cashbook.line')
|
||||
|
||||
if self.cashbook:
|
||||
balance = self.cashbook.start_balance
|
||||
lines = Line.search([
|
||||
('cashbook.id', '=', self.cashbook.id),
|
||||
])
|
||||
for line in lines:
|
||||
balance += line.credit - line.debit
|
||||
if line.id == self.id:
|
||||
break
|
||||
return balance
|
||||
|
||||
@classmethod
|
||||
def get_debit_credit(cls, values):
|
||||
""" compute debit/credit from amount
|
||||
|
@ -470,6 +494,7 @@ class LineContext(ModelView):
|
|||
""" get number of accessible cashbooks,
|
||||
depends on user-permissions
|
||||
"""
|
||||
print('-- on_change_with_num_cashbook:', Transaction().context)
|
||||
LineContext = Pool().get('cashbook.line.context')
|
||||
return LineContext.default_num_cashbook()
|
||||
|
||||
|
|
16
locale/de.po
16
locale/de.po
|
@ -54,6 +54,10 @@ 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."
|
||||
|
||||
|
||||
#############
|
||||
# res.group #
|
||||
|
@ -274,6 +278,10 @@ msgctxt "field:cashbook.book,currency:"
|
|||
msgid "Currency"
|
||||
msgstr "Währung"
|
||||
|
||||
msgctxt "field:cashbook.book,start_balance:"
|
||||
msgid "Initial Amount"
|
||||
msgstr "Anfangsbetrag"
|
||||
|
||||
|
||||
#################
|
||||
# cashbook.line #
|
||||
|
@ -390,6 +398,14 @@ msgctxt "field:cashbook.line,currency_digits:"
|
|||
msgid "Currency Digits"
|
||||
msgstr "Nachkommastellen Währung"
|
||||
|
||||
msgctxt "field:cashbook.line,balance:"
|
||||
msgid "Balance"
|
||||
msgstr "Saldo"
|
||||
|
||||
msgctxt "help:cashbook.line,balance:"
|
||||
msgid "Balance of the cash book up to the current line, if the default sorting applies."
|
||||
msgstr "Saldo des Kassenbuchs bis zur aktuellen Zeile, sofern die Standardsortierung gilt."
|
||||
|
||||
|
||||
#################
|
||||
# cashbook.type #
|
||||
|
|
|
@ -41,6 +41,9 @@ full copyright notices and license terms. -->
|
|||
<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>
|
||||
</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>
|
||||
|
||||
</data>
|
||||
</tryton>
|
||||
|
|
|
@ -62,7 +62,7 @@ class BookTestCase(ModuleTestCase):
|
|||
self.assertEqual(book.state, 'open')
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
"The cashbook 'Book 1' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.",
|
||||
"The cashbook 'Book 1 | 1.00 usd | Open' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.",
|
||||
Book.delete,
|
||||
[book])
|
||||
|
||||
|
@ -95,7 +95,7 @@ class BookTestCase(ModuleTestCase):
|
|||
self.assertEqual(book.state, 'closed')
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
"The cashbook 'Book 1' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.",
|
||||
"The cashbook 'Book 1 | 1.00 usd | Closed' cannot be deleted because it contains 1 lines and is not in the status 'Archive'.",
|
||||
Book.delete,
|
||||
[book])
|
||||
|
||||
|
@ -160,7 +160,7 @@ class BookTestCase(ModuleTestCase):
|
|||
self.assertEqual(book.state, 'closed')
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
"The cash book 'Book 1a' is 'Closed' and cannot be changed.",
|
||||
"The cash book 'Book 1a | 1.00 usd | Closed' is 'Closed' and cannot be changed.",
|
||||
Book.write,
|
||||
*[
|
||||
[book],
|
||||
|
@ -183,7 +183,7 @@ class BookTestCase(ModuleTestCase):
|
|||
Book.wfarchive([book])
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
"The cash book 'Book 1c' is 'Archive' and cannot be changed.",
|
||||
"The cash book 'Book 1c | 0.00 usd | Archive' is 'Archive' and cannot be changed.",
|
||||
Book.write,
|
||||
*[
|
||||
[book],
|
||||
|
@ -192,6 +192,59 @@ 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')
|
||||
book, = Book.create([{
|
||||
'name': 'Book 1',
|
||||
'btype': types.id,
|
||||
'company': company.id,
|
||||
'currency': company.currency.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',
|
||||
}])],
|
||||
}])
|
||||
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()
|
||||
def test_book_permission_owner(self):
|
||||
""" create book + 2x users, add users to group, check access
|
||||
|
@ -228,7 +281,7 @@ class BookTestCase(ModuleTestCase):
|
|||
'company': company.id,
|
||||
'currency': company.currency.id,
|
||||
}])
|
||||
self.assertEqual(book.rec_name, 'Fridas book'),
|
||||
self.assertEqual(book.rec_name, 'Fridas book | 0.00 usd | Open'),
|
||||
self.assertEqual(book.owner.rec_name, 'Frida'),
|
||||
|
||||
with Transaction().set_context({
|
||||
|
@ -243,7 +296,7 @@ class BookTestCase(ModuleTestCase):
|
|||
with Transaction().set_user(usr_lst[0].id):
|
||||
books = Book.search([])
|
||||
self.assertEqual(len(books), 1)
|
||||
self.assertEqual(books[0].rec_name, 'Fridas book')
|
||||
self.assertEqual(books[0].rec_name, 'Fridas book | 0.00 usd | Open')
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
'You are not allowed to access "Cashbook".',
|
||||
|
@ -298,7 +351,7 @@ class BookTestCase(ModuleTestCase):
|
|||
'currency': company.currency.id,
|
||||
'btype': types.id,
|
||||
}])
|
||||
self.assertEqual(book.rec_name, 'Fridas book'),
|
||||
self.assertEqual(book.rec_name, 'Fridas book | 0.00 usd | Open'),
|
||||
self.assertEqual(book.owner.rec_name, 'Frida'),
|
||||
|
||||
with Transaction().set_context({
|
||||
|
@ -315,7 +368,7 @@ class BookTestCase(ModuleTestCase):
|
|||
with Transaction().set_user(usr_lst[0].id):
|
||||
books = Book.search([])
|
||||
self.assertEqual(len(books), 1)
|
||||
self.assertEqual(books[0].rec_name, 'Fridas book')
|
||||
self.assertEqual(books[0].rec_name, 'Fridas book | 0.00 usd | Open')
|
||||
|
||||
@with_transaction()
|
||||
def test_book_permission_observer(self):
|
||||
|
@ -360,7 +413,7 @@ class BookTestCase(ModuleTestCase):
|
|||
'currency': company.currency.id,
|
||||
'btype': types.id,
|
||||
}])
|
||||
self.assertEqual(book.rec_name, 'Fridas book'),
|
||||
self.assertEqual(book.rec_name, 'Fridas book | 0.00 usd | Open'),
|
||||
self.assertEqual(book.owner.rec_name, 'Frida'),
|
||||
|
||||
with Transaction().set_context({
|
||||
|
@ -377,6 +430,6 @@ class BookTestCase(ModuleTestCase):
|
|||
with Transaction().set_user(usr_lst[0].id):
|
||||
books = Book.search([])
|
||||
self.assertEqual(len(books), 1)
|
||||
self.assertEqual(books[0].rec_name, 'Fridas book')
|
||||
self.assertEqual(books[0].rec_name, 'Fridas book | 0.00 usd | Open')
|
||||
|
||||
# end BookTestCase
|
||||
|
|
|
@ -131,7 +131,7 @@ class LineTestCase(ModuleTestCase):
|
|||
self.assertEqual(book.state, 'closed')
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
"The cash book 'Book 1' is 'Closed' and cannot be changed.",
|
||||
"The cash book 'Book | 2.00 usd | Closed' is 'Closed' and cannot be changed.",
|
||||
Line.write,
|
||||
*[
|
||||
[book.lines[0]],
|
||||
|
@ -478,7 +478,7 @@ class LineTestCase(ModuleTestCase):
|
|||
self.assertEqual(book.state, 'closed')
|
||||
|
||||
self.assertRaisesRegex(UserError,
|
||||
"The cashbook line '05/01/2022 Text 1' cannot be deleted because the Cashbook 'Book 1' is in state 'Closed'.",
|
||||
"The cashbook line '05/01/2022 Text 1' cannot be deleted because the Cashbook 'Book | 2.00 usd | Closed' is in state 'Closed'.",
|
||||
Lines.delete,
|
||||
[book.lines[0]])
|
||||
|
||||
|
@ -570,7 +570,7 @@ class LineTestCase(ModuleTestCase):
|
|||
'amount': Decimal('1.0'),
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(book.rec_name, 'Fridas book'),
|
||||
self.assertEqual(book.rec_name, 'Fridas book | 1.00 usd | Open'),
|
||||
self.assertEqual(book.owner.rec_name, 'Frida'),
|
||||
|
||||
with Transaction().set_context({
|
||||
|
@ -585,7 +585,7 @@ class LineTestCase(ModuleTestCase):
|
|||
with Transaction().set_user(usr_lst[0].id):
|
||||
lines = Line.search([])
|
||||
self.assertEqual(len(lines), 1)
|
||||
self.assertEqual(lines[0].cashbook.rec_name, 'Fridas book')
|
||||
self.assertEqual(lines[0].cashbook.rec_name, 'Fridas book | 1.00 usd | Open')
|
||||
self.assertEqual(lines[0].rec_name, '05/01/2022 Test 1')
|
||||
|
||||
Line.write(*[
|
||||
|
@ -647,7 +647,7 @@ class LineTestCase(ModuleTestCase):
|
|||
'amount': Decimal('1.0'),
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(book.rec_name, 'Fridas book'),
|
||||
self.assertEqual(book.rec_name, 'Fridas book | 1.00 usd | Open'),
|
||||
self.assertEqual(book.owner.rec_name, 'Frida'),
|
||||
|
||||
with Transaction().set_context({
|
||||
|
@ -731,7 +731,7 @@ class LineTestCase(ModuleTestCase):
|
|||
'amount': Decimal('1.0'),
|
||||
}])],
|
||||
}])
|
||||
self.assertEqual(book.rec_name, 'Fridas book'),
|
||||
self.assertEqual(book.rec_name, 'Fridas book | 1.00 usd | Open'),
|
||||
self.assertEqual(book.owner.rec_name, 'Frida'),
|
||||
|
||||
with Transaction().set_context({
|
||||
|
|
|
@ -13,6 +13,10 @@ full copyright notices and license terms. -->
|
|||
<label name="account"/>
|
||||
<field name="account"/>
|
||||
|
||||
<label name="start_balance"/>
|
||||
<field name="start_balance"/>
|
||||
<newline/>
|
||||
|
||||
<label name="state"/>
|
||||
<field name="state"/>
|
||||
<group id="grpstate" col="3" colspan="2">
|
||||
|
|
|
@ -5,6 +5,7 @@ full copyright notices and license terms. -->
|
|||
<tree>
|
||||
<field name="name"/>
|
||||
<field name="btype"/>
|
||||
<field name="start_balance"/>
|
||||
<field name="currency"/>
|
||||
<field name="account"/>
|
||||
<field name="owner"/>
|
||||
|
|
|
@ -9,6 +9,7 @@ full copyright notices and license terms. -->
|
|||
<field name="description" expand="1"/>
|
||||
<field name="credit" sum="Credit"/>
|
||||
<field name="debit" sum="Debit"/>
|
||||
<field name="balance"/>
|
||||
<field name="currency"/>
|
||||
<field name="state"/>
|
||||
<button name="wfedit"/>
|
||||
|
|
Loading…
Reference in a new issue