book: neues Feld 'description', sperrt btype-->None mit Zeilen + Test
line: verwendet nur kassenbücher mit typ
This commit is contained in:
parent
a2e7f192f8
commit
2acdc55efb
12 changed files with 107 additions and 11 deletions
11
book.py
11
book.py
|
@ -45,6 +45,8 @@ class Book(tree(separator='/'), 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)
|
||||||
|
description = fields.Text(string='Description',
|
||||||
|
states=STATES, depends=DEPENDS)
|
||||||
btype = fields.Many2One(string='Type',
|
btype = fields.Many2One(string='Type',
|
||||||
help='A cash book with type can contain postings. Without type is a view.',
|
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',
|
||||||
|
@ -307,6 +309,15 @@ class Book(tree(separator='/'), 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:
|
||||||
|
# deny btype-->None if lines not empty
|
||||||
|
if 'btype' in values.keys():
|
||||||
|
if (values['btype'] is None) and (len(book.lines) > 0):
|
||||||
|
raise UserError(gettext(
|
||||||
|
'cashbook.msg_book_btype_with_lines',
|
||||||
|
cbname = book.rec_name,
|
||||||
|
numlines = len(book.lines),
|
||||||
|
))
|
||||||
|
|
||||||
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)):
|
||||||
|
|
|
@ -17,7 +17,8 @@ field_catnamelong = fields.Boolean(string='Category: Show long name',
|
||||||
help='Shows the long name of the category in the Category field of a cash book line.')
|
help='Shows the long name of the category in the Category field of a cash book line.')
|
||||||
field_defbook = fields.Many2One(string='Default Cashbook',
|
field_defbook = fields.Many2One(string='Default Cashbook',
|
||||||
help='The default cashbook is selected when you open the booking wizard.',
|
help='The default cashbook is selected when you open the booking wizard.',
|
||||||
model_name='cashbook.book', ondelete='SET NULL')
|
model_name='cashbook.book', ondelete='SET NULL',
|
||||||
|
domain=[('btype', '!=', None)])
|
||||||
|
|
||||||
|
|
||||||
class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
|
class Configuration(ModelSingleton, ModelSQL, ModelView, UserMultiValueMixin):
|
||||||
|
|
3
line.py
3
line.py
|
@ -931,6 +931,7 @@ class LineContext(ModelView):
|
||||||
|
|
||||||
cashbook = fields.Many2One(string='Cashbook', required=True,
|
cashbook = fields.Many2One(string='Cashbook', required=True,
|
||||||
model_name='cashbook.book',
|
model_name='cashbook.book',
|
||||||
|
domain=[('btype', '!=', None)],
|
||||||
states={
|
states={
|
||||||
'readonly': Eval('num_cashbook', 0) < 2,
|
'readonly': Eval('num_cashbook', 0) < 2,
|
||||||
}, depends=['num_cashbook'])
|
}, depends=['num_cashbook'])
|
||||||
|
@ -993,7 +994,7 @@ class LineContext(ModelView):
|
||||||
with Transaction().set_context({
|
with Transaction().set_context({
|
||||||
'_check_access': True,
|
'_check_access': True,
|
||||||
}):
|
}):
|
||||||
return CashBook.search_count([])
|
return CashBook.search_count([('btype', '!=', None)])
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def default_done(cls):
|
def default_done(cls):
|
||||||
|
|
12
locale/de.po
12
locale/de.po
|
@ -146,6 +146,10 @@ msgctxt "model:ir.message,text:msg_line_invalid_category"
|
||||||
msgid "The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'."
|
msgid "The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'."
|
||||||
msgstr "Die Kategorie der Buchungszeile '%(recname)s' paßt nicht zum Buchungstyp '%(booktype)s'."
|
msgstr "Die Kategorie der Buchungszeile '%(recname)s' paßt nicht zum Buchungstyp '%(booktype)s'."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_book_btype_with_lines"
|
||||||
|
msgid "The type cannot be deleted on the cash book '%(cbname)s' because it still contains %(numlines)s lines."
|
||||||
|
msgstr "Der Typ kann am Kassenbuch '%(cbname)s' nicht gelöscht werden, da es noch %(numlines)s Zeilen enthält."
|
||||||
|
|
||||||
|
|
||||||
#############
|
#############
|
||||||
# res.group #
|
# res.group #
|
||||||
|
@ -434,10 +438,18 @@ msgctxt "view:cashbook.book:"
|
||||||
msgid "Reconciliations"
|
msgid "Reconciliations"
|
||||||
msgstr "Abstimmungen"
|
msgstr "Abstimmungen"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.book:"
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
msgctxt "field:cashbook.book,name:"
|
msgctxt "field:cashbook.book,name:"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,description:"
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Beschreibung"
|
||||||
|
|
||||||
msgctxt "field:cashbook.book,btype:"
|
msgctxt "field:cashbook.book,btype:"
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Typ"
|
msgstr "Typ"
|
||||||
|
|
12
locale/en.po
12
locale/en.po
|
@ -142,6 +142,10 @@ msgctxt "model:ir.message,text:msg_line_invalid_category"
|
||||||
msgid "The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'."
|
msgid "The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'."
|
||||||
msgstr "The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'."
|
msgstr "The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'."
|
||||||
|
|
||||||
|
msgctxt "model:ir.message,text:msg_book_btype_with_lines"
|
||||||
|
msgid "The type cannot be deleted on the cash book '%(cbname)s' because it still contains %(numlines)s lines."
|
||||||
|
msgstr "The type cannot be deleted on the cash book '%(cbname)s' because it still contains %(numlines)s lines."
|
||||||
|
|
||||||
msgctxt "model:res.group,name:group_cashbook"
|
msgctxt "model:res.group,name:group_cashbook"
|
||||||
msgid "Cashbook"
|
msgid "Cashbook"
|
||||||
msgstr "Cashbook"
|
msgstr "Cashbook"
|
||||||
|
@ -398,10 +402,18 @@ msgctxt "view:cashbook.book:"
|
||||||
msgid "Reconciliations"
|
msgid "Reconciliations"
|
||||||
msgstr "Reconciliations"
|
msgstr "Reconciliations"
|
||||||
|
|
||||||
|
msgctxt "view:cashbook.book:"
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Description"
|
||||||
|
|
||||||
msgctxt "field:cashbook.book,name:"
|
msgctxt "field:cashbook.book,name:"
|
||||||
msgid "Name"
|
msgid "Name"
|
||||||
msgstr "Name"
|
msgstr "Name"
|
||||||
|
|
||||||
|
msgctxt "field:cashbook.book,description:"
|
||||||
|
msgid "Description"
|
||||||
|
msgstr "Description"
|
||||||
|
|
||||||
msgctxt "field:cashbook.book,btype:"
|
msgctxt "field:cashbook.book,btype:"
|
||||||
msgid "Type"
|
msgid "Type"
|
||||||
msgstr "Type"
|
msgstr "Type"
|
||||||
|
|
|
@ -110,6 +110,9 @@ full copyright notices and license terms. -->
|
||||||
<record model="ir.message" id="msg_line_invalid_category">
|
<record model="ir.message" id="msg_line_invalid_category">
|
||||||
<field name="text">The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'.</field>
|
<field name="text">The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'.</field>
|
||||||
</record>
|
</record>
|
||||||
|
<record model="ir.message" id="msg_book_btype_with_lines">
|
||||||
|
<field name="text">The type cannot be deleted on the cash book '%(cbname)s' because it still contains %(numlines)s lines.</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</tryton>
|
</tryton>
|
||||||
|
|
|
@ -117,6 +117,62 @@ class BookTestCase(ModuleTestCase):
|
||||||
Book.delete,
|
Book.delete,
|
||||||
[book])
|
[book])
|
||||||
|
|
||||||
|
@with_transaction()
|
||||||
|
def test_book_deny_btype_set_none(self):
|
||||||
|
""" create cashbook, add lines,
|
||||||
|
try to set btype to None with lines
|
||||||
|
"""
|
||||||
|
pool = Pool()
|
||||||
|
Book = pool.get('cashbook.book')
|
||||||
|
|
||||||
|
types = self.prep_type()
|
||||||
|
category = self.prep_category(cattype='in')
|
||||||
|
company = self.prep_company()
|
||||||
|
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,
|
||||||
|
'start_date': date(2022, 5, 1),
|
||||||
|
'lines': [('create', [{
|
||||||
|
'date': date(2022, 5, 1),
|
||||||
|
'description': 'test 1',
|
||||||
|
'category': category.id,
|
||||||
|
'bookingtype': 'in',
|
||||||
|
'amount': Decimal('1.0'),
|
||||||
|
'party': party.id,
|
||||||
|
}])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.name, 'Book 1')
|
||||||
|
self.assertEqual(book.btype.rec_name, 'CAS - Cash')
|
||||||
|
|
||||||
|
self.assertRaisesRegex(UserError,
|
||||||
|
"The type cannot be deleted on the cash book 'Book 1 | 1.00 usd | Open' because it still contains 1 lines.",
|
||||||
|
Book.write,
|
||||||
|
*[
|
||||||
|
[book],
|
||||||
|
{
|
||||||
|
'btype': None,
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
Book.write(*[
|
||||||
|
[book],
|
||||||
|
{
|
||||||
|
'lines': [('delete', [book.lines[0].id])],
|
||||||
|
}])
|
||||||
|
self.assertEqual(len(book.lines), 0)
|
||||||
|
self.assertEqual(book.btype.rec_name, 'CAS - Cash')
|
||||||
|
|
||||||
|
Book.write(*[
|
||||||
|
[book],
|
||||||
|
{
|
||||||
|
'btype': None,
|
||||||
|
}])
|
||||||
|
self.assertEqual(book.btype, None)
|
||||||
|
|
||||||
@with_transaction()
|
@with_transaction()
|
||||||
def test_book_deny_delete_closed(self):
|
def test_book_deny_delete_closed(self):
|
||||||
""" create cashbook, add lines, try to delete in state 'closed'
|
""" create cashbook, add lines, try to delete in state 'closed'
|
||||||
|
|
|
@ -24,6 +24,9 @@ full copyright notices and license terms. -->
|
||||||
<page name="reconciliations" string="Reconciliations" col="1">
|
<page name="reconciliations" string="Reconciliations" col="1">
|
||||||
<field name="reconciliations"/>
|
<field name="reconciliations"/>
|
||||||
</page>
|
</page>
|
||||||
|
<page name="description" string="Description" col="1">
|
||||||
|
<field name="description"/>
|
||||||
|
</page>
|
||||||
<page id="pggeneral" string="General Information" col="4">
|
<page id="pggeneral" string="General Information" col="4">
|
||||||
<label name="company"/>
|
<label name="company"/>
|
||||||
<field name="company"/>
|
<field name="company"/>
|
||||||
|
|
|
@ -3,10 +3,8 @@
|
||||||
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. -->
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="rec_name"/>
|
||||||
<field name="btype"/>
|
|
||||||
<field name="balance" sum="Balance"/>
|
<field name="balance" sum="Balance"/>
|
||||||
<field name="currency"/>
|
<field name="currency"/>
|
||||||
<field name="owner"/>
|
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
|
@ -4,10 +4,8 @@ The COPYRIGHT file at the top level of this repository contains the
|
||||||
full copyright notices and license terms. -->
|
full copyright notices and license terms. -->
|
||||||
<tree>
|
<tree>
|
||||||
<field name="name"/>
|
<field name="name"/>
|
||||||
<field name="btype"/>
|
|
||||||
<field name="balance" sum="Balance"/>
|
<field name="balance" sum="Balance"/>
|
||||||
<field name="currency"/>
|
<field name="currency"/>
|
||||||
<field name="owner"/>
|
|
||||||
<field name="state"/>
|
<field name="state"/>
|
||||||
<field name="parent" tree_invisible="1"/>
|
<field name="parent" tree_invisible="1"/>
|
||||||
<field name="childs" tree_invisible="1"/>
|
<field name="childs" tree_invisible="1"/>
|
||||||
|
|
|
@ -20,7 +20,7 @@ class EnterBookingStart(ModelView):
|
||||||
__name__ = 'cashbook.enterbooking.start'
|
__name__ = 'cashbook.enterbooking.start'
|
||||||
|
|
||||||
cashbook = fields.Many2One(string='Cashbook', model_name='cashbook.book',
|
cashbook = fields.Many2One(string='Cashbook', model_name='cashbook.book',
|
||||||
domain=[('id', 'in', Eval('cashbooks', []))],
|
domain=[('id', 'in', Eval('cashbooks', [])), ('btype', '!=', None)],
|
||||||
depends=['cashbooks'], required=True)
|
depends=['cashbooks'], required=True)
|
||||||
cashbooks = fields.One2Many(string='Cashbooks', field=None,
|
cashbooks = fields.One2Many(string='Cashbooks', field=None,
|
||||||
model_name='cashbook.book', readonly=True,
|
model_name='cashbook.book', readonly=True,
|
||||||
|
@ -137,6 +137,7 @@ class EnterBookingWizard(Wizard):
|
||||||
result = {
|
result = {
|
||||||
'cashbooks': [x.id for x in Cashbook.search([
|
'cashbooks': [x.id for x in Cashbook.search([
|
||||||
('state', '=', 'open'),
|
('state', '=', 'open'),
|
||||||
|
('btype', '!=', None),
|
||||||
('owner.id', '=', Transaction().user),
|
('owner.id', '=', Transaction().user),
|
||||||
])],
|
])],
|
||||||
'bookingtype': getattr(self.start, 'bookingtype', 'out'),
|
'bookingtype': getattr(self.start, 'bookingtype', 'out'),
|
||||||
|
|
|
@ -16,7 +16,7 @@ class OpenCashBookStart(ModelView):
|
||||||
__name__ = 'cashbook.open_lines.start'
|
__name__ = 'cashbook.open_lines.start'
|
||||||
|
|
||||||
cashbook = fields.Many2One(string='Cashbook', model_name='cashbook.book',
|
cashbook = fields.Many2One(string='Cashbook', model_name='cashbook.book',
|
||||||
required=True)
|
required=True, domain=[('btype', '!=', None)])
|
||||||
checked = fields.Boolean(string='Checked', help="Show cashbook lines in Checked-state.")
|
checked = fields.Boolean(string='Checked', help="Show cashbook lines in Checked-state.")
|
||||||
done = fields.Boolean(string='Done', help="Show cashbook lines in Done-state")
|
done = fields.Boolean(string='Done', help="Show cashbook lines in Done-state")
|
||||||
date_from = fields.Date(string='Start Date')
|
date_from = fields.Date(string='Start Date')
|
||||||
|
@ -54,7 +54,7 @@ class OpenCashBook(Wizard):
|
||||||
with Transaction().set_context({
|
with Transaction().set_context({
|
||||||
'_check_access': True,
|
'_check_access': True,
|
||||||
}):
|
}):
|
||||||
books = Book.search([])
|
books = Book.search([('btype', '!=', None)])
|
||||||
if len(books) == 1:
|
if len(books) == 1:
|
||||||
return 'open_'
|
return 'open_'
|
||||||
return 'askuser'
|
return 'askuser'
|
||||||
|
@ -90,7 +90,7 @@ class OpenCashBook(Wizard):
|
||||||
with Transaction().set_context({
|
with Transaction().set_context({
|
||||||
'_check_access': True,
|
'_check_access': True,
|
||||||
}):
|
}):
|
||||||
books = Book.search([])
|
books = Book.search([('btype', '!=', None)])
|
||||||
if len(books) > 0:
|
if len(books) > 0:
|
||||||
book = books[0]
|
book = books[0]
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue