diff --git a/book.py b/book.py index a9a2add..38fa020 100644 --- a/book.py +++ b/book.py @@ -45,6 +45,8 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): required=True, ondelete="RESTRICT") name = fields.Char(string='Name', required=True, states=STATES, depends=DEPENDS) + description = fields.Text(string='Description', + states=STATES, depends=DEPENDS) 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', @@ -307,6 +309,15 @@ class Book(tree(separator='/'), Workflow, ModelSQL, ModelView): actions = iter(args) for books, values in zip(actions, actions): 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': # allow state-update, if its the only action if not (('state' in values.keys()) and (len(values.keys()) == 1)): diff --git a/configuration.py b/configuration.py index e8de3b6..2dc986e 100644 --- a/configuration.py +++ b/configuration.py @@ -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.') field_defbook = fields.Many2One(string='Default Cashbook', 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): diff --git a/line.py b/line.py index 59b778f..db402e9 100644 --- a/line.py +++ b/line.py @@ -931,6 +931,7 @@ class LineContext(ModelView): cashbook = fields.Many2One(string='Cashbook', required=True, model_name='cashbook.book', + domain=[('btype', '!=', None)], states={ 'readonly': Eval('num_cashbook', 0) < 2, }, depends=['num_cashbook']) @@ -993,7 +994,7 @@ class LineContext(ModelView): with Transaction().set_context({ '_check_access': True, }): - return CashBook.search_count([]) + return CashBook.search_count([('btype', '!=', None)]) @classmethod def default_done(cls): diff --git a/locale/de.po b/locale/de.po index 6075046..ba68660 100644 --- a/locale/de.po +++ b/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'." 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 # @@ -434,10 +438,18 @@ msgctxt "view:cashbook.book:" msgid "Reconciliations" msgstr "Abstimmungen" +msgctxt "view:cashbook.book:" +msgid "Description" +msgstr "Beschreibung" + msgctxt "field:cashbook.book,name:" msgid "Name" msgstr "Name" +msgctxt "field:cashbook.book,description:" +msgid "Description" +msgstr "Beschreibung" + msgctxt "field:cashbook.book,btype:" msgid "Type" msgstr "Typ" diff --git a/locale/en.po b/locale/en.po index 912ff90..5d58001 100644 --- a/locale/en.po +++ b/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'." 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" msgid "Cashbook" msgstr "Cashbook" @@ -398,10 +402,18 @@ msgctxt "view:cashbook.book:" msgid "Reconciliations" msgstr "Reconciliations" +msgctxt "view:cashbook.book:" +msgid "Description" +msgstr "Description" + msgctxt "field:cashbook.book,name:" msgid "Name" msgstr "Name" +msgctxt "field:cashbook.book,description:" +msgid "Description" +msgstr "Description" + msgctxt "field:cashbook.book,btype:" msgid "Type" msgstr "Type" diff --git a/message.xml b/message.xml index 3946a30..14bc840 100644 --- a/message.xml +++ b/message.xml @@ -110,6 +110,9 @@ full copyright notices and license terms. --> The category of the booking line '%(recname)s' does not match the posting type '%(booktype)s'. + + The type cannot be deleted on the cash book '%(cbname)s' because it still contains %(numlines)s lines. + diff --git a/tests/test_book.py b/tests/test_book.py index c5be24e..67fea04 100644 --- a/tests/test_book.py +++ b/tests/test_book.py @@ -117,6 +117,62 @@ class BookTestCase(ModuleTestCase): Book.delete, [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() def test_book_deny_delete_closed(self): """ create cashbook, add lines, try to delete in state 'closed' diff --git a/view/book_form.xml b/view/book_form.xml index c47d19a..d2ffaad 100644 --- a/view/book_form.xml +++ b/view/book_form.xml @@ -24,6 +24,9 @@ full copyright notices and license terms. --> + + +